Getting position from playNote (Minim)

Hi all,
I’m using the Minim Library to play music using the playNotes() function.

My question is twofold:

  1. What structure is the playNote function populating? (is it an array?)
  2. How do I get the position within this structure? (I want to know how far through the notes I am)

thanks.

1 Like

It calls a NoteManager.addEvent().

This is a NoteManager:

Thanks Jeremy, I should have thought of looking for the source.

I can see that within the NoteManager the notes are stored in a HashMap and there is also a pos variable that is the current note to play.
I’ve tried minim.NoteManager.pos to get that value but it doesn’t work as NoteManager is undefined, which I expected.

How would I get that pos value, or better yet, get access to the hashmap?
Does the fact that many of the variables in the NoteManager are private stop me for having access?

thanks

Is it possible to access the variables inside the NoteManager class? I’ve not been able to nut anything out.

Yes, by using Java reflection techniques, such as getDeclaredField():
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#getDeclaredField(java.lang.String)

However you’ve got an almost insurmountable task if your aims are upon the field events:

The reason it is so is b/c the generics datatype NoteEvent refers to a nested private interface:

So we can’t declare variables nor generics, neither use a cast operator for datatype NoteEvent!

Although we can still attempt to find some workarounds for it, the best 1 is simply coding in Python Mode instead!

I can’t find that particular field inside class NoteEvent!:

Apologies, I meant now

Before we go any further, I should be more specific.
I’m trying to get the current position of the playNote so that I can sync some visuals to it.

From what I read of getDeclaredField, it can’t return the length of an array, but could it get the value of the now variable or am I just pushing dust uphill?

Method Class::getDeclaredField() returns a Field object:

From there you can read the actual field value by using the correct get() method for its datatype.

If it happens to be an array, after you use the correct cast operator on it and store it in an appropriate variable type, you can read its length field.

Regardless, class NoteEvent doesn’t have an array field but rather a HashMap 1.

B/c field now is of datatype int, you can easily grab it via method Field::getInt() after Field::setAccessible():

I’ve tried to get access this way:
out.noteManager.getClass.getDeclaredField("now");
but it gives me
"the field AudioOutput.noteManager is not visible"
which I expected as that object is private.

So I tried to use
out.noteManager.setAccessible(true)
on it, but since it is not a field it doesn’t work and throws the same error.

So i’m guessing I might need to use something like this [stackOverflow solution] (https://stackoverflow.com/questions/2989560/how-to-get-the-fields-in-an-object-via-reflection) (code from link shown below)in order to get access to the fileds of the AudioOut and then within that, get access to the filelds of the noteManager:

Object someObject = getItSomehow();
for (Field field : someObject.getClass().getDeclaredFields()) {
    field.setAccessible(true); // You might want to set modifier to public first.
    Object value = field.get(someObject); 
    if (value != null) {
        System.out.println(field.getName() + "=" + value);
    }
}

Is this getting closer or have I gone completely haywire?

1st, get the Field instance inside a variable, then you can invoke its methods.

Field reflection examples from the previous forum:

I’ve been looking at your examples, which are so incredibly helpful and I’ve come to this point:

int i = getNow(out);

static final int getNow(final AudioOutput o) {
  try {
    final java.lang.reflect.Field f = AudioOutput.class.getDeclaredField("noteManager");
    f.setAccessible(true);

    final java.lang.reflect.Field fi = f.getClass().getDeclaredField("now");

    return fi.getInt(o);
  }

  catch (ReflectiveOperationException cause) {
    throw new RuntimeException(cause);
  }
}

I must still be a level too high or something as
getDeclaredField("now") throws:
noSuchFieldException: now

You’re getting that b/c you’re attempting to grab a field named “now” from a Field object; which obviously doesn’t have that:
fi = f.getClass().getDeclaredField("now");

Instead you need a NoteManager object 1st, only then you can attempt it.

Invoke method Field::get() over your Field f variable in order to grab the object referenced by AudioOutput::noteManager:
final NoteManager nm = (NoteManager) f.get(o);

After that you can move forward to get its field NoteManager::now as another Field object:
final java.lang.reflect.Field fi = NoteManager.class.getDeclaredField("now");

Since even before you can get ahold of NoteManager::now you 1st need to grab AudioOutput::noteManager, that’s not very performant.

How about do that once and cache those 2 objects permanently in some class.

By doing so, we just need the very last reflection step on subsequently calls!

For that I’ve created a class MinimReflectHack inside a “.java” file:

MinimReflectHack.java:

/**
 * MinimReflectHack (v1.0.3)
 * GoToLoop (2020/Jun/03)
 * Discourse.Processing.org/t/getting-position-from-playnote-minim/21360/13
 */

package ddf.minim;

import java.lang.reflect.Field;

public class MinimReflectHack {
  public static final String NOTE = "noteManager", NOW = "now";

  public NoteManager nm;
  public Field now;

  public MinimReflectHack(AudioOutput out) {
    cacheFieldNow(out);
  }

  public MinimReflectHack cacheFieldNow(AudioOutput out) {
    nm = (NoteManager) readField(getField(out, NOTE), out);
    now = getField(nm, NOW);
    return this;
  }

  public int getValueNow() {
    return readIntField(now, nm);
  }

  public MinimReflectHack setValueNow(int val) {
    writeIntField(now, nm, val);
    return this;
  }

  @Override public String toString() {
    return now.toString();
  }

  public static final Field getField(Object obj, String s) {
    return getField(obj.getClass(), s);
  }

  public static final Field getField(Class<?> c, String s) {
    try {
      Field f = c.getDeclaredField(s);
      f.setAccessible(true);
      return f;
    }

    catch (NoSuchFieldException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final Object readField(Field f, Object obj) {
    try {
      return f.get(obj);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final void writeField(Field f, Object obj, Object val) {
    try {
      f.set(obj, val);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final int readIntField(Field f, Object obj) {
    try {
      return f.getInt(obj);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final void writeIntField(Field f, Object obj, int val) {
    try {
      f.setInt(obj, val);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final float readFloatField(Field f, Object obj) {
    try {
      return f.getFloat(obj);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }

  public static final void writeFloatField(Field f, Object obj, float val) {
    try {
      f.setFloat(obj, val);
    }

    catch (IllegalAccessException cause) {
      throw new RuntimeException(cause);
    }
  }
}

There are 2 groups of methods in class MinimReflectHack.

The static 1s are general reflection helper functions, which can gets us a Field object, and then read & write to that Field, whether that’s an Object, int or float datatype.

While the non-static 1s are custom handmade to get, read and write to NoteManager::now.

Given it’s written as actual Java source code, you’re gonna need to use import in order to bring it onto your “.pde” sketch tab:
import ddf.minim.MinimReflectHack;

I’ve made my sketch example using “SequenceSound.pde” as its base:

Here’s my “.pde” test-drive sketch for class ddf.minim.MinimReflectHack:

“MinimSequenceSound.pde”:

/**
 * GitHub.com/ddf/Minim/blob/master/
 * examples/Basics/SequenceSound/SequenceSound.pde
 */

import ddf.minim.Minim;
import ddf.minim.AudioOutput;
import ddf.minim.AudioBuffer;

AudioOutput out;
AudioBuffer l, r;
int bufSz;

import ddf.minim.MinimReflectHack;

MinimReflectHack hack;

import java.util.Map;
import java.util.List;

Map<Integer, List<?>> events; // NoteManager.NoteEvent is private!

static final float AMP = 100, L_OFF = AMP, R_OFF = AMP + L_OFF;

void settings() {
  (out = new Minim(this).getLineOut()).setTempo(80);

  hack = new MinimReflectHack(out);

  events = (HashMap<Integer, List<?>>) MinimReflectHack.readField(
    MinimReflectHack.getField(hack.nm, "events"), hack.nm);

  l = out.left;
  r = out.right;
  bufSz = out.bufferSize();

  size(bufSz, round(R_OFF + AMP));

  println(width, height, bufSz, ENTER);
  println(events);
  println(hack, hack.getValueNow(), ENTER);
}

void setup() {
  stroke(#FFFF00);
  respawnNotes();
}

void draw() {
  clear();

  surface.setTitle("now: " + hack.getValueNow());

  float yl = l.get(0) * AMP + L_OFF;
  float yr = r.get(0) * AMP + R_OFF;

  int i = 0;

  while (++i < bufSz) {
    line(i - 1, yl, i, yl = l.get(i) * AMP + L_OFF);
    line(i - 1, yr, i, yr = r.get(i) * AMP + R_OFF);
  }
}

void mousePressed() {
  respawnNotes();
}

void respawnNotes() {
  out.pauseNotes();

  events.clear();
  hack.setValueNow(0);

  out.playNote(0.0, 0.9, 97.99);
  out.playNote(1.0, 0.9, 123.47);
  out.playNote(2.0, 2.9, "C3");
  out.playNote(3.0, 1.9, "E3");
  out.playNote(4.0, 0.9, "G3");
  out.playNote(5.0, "");
  out.playNote(6.0, 329.63);
  out.playNote(7.0, "G4");
  out.setNoteOffset(8.1);
  out.playNote("G5");
  out.playNote(987.77);

  out.resumeNotes();

  println(events, ENTER);
  println(hack, hack.getValueNow(), ENTER);
}
1 Like

Thanks for this. I’ll have a play and see what I can do. Your help has been amazing.
Thanks again.

I’m also posting a “Python Mode (Jython)” version, which for a long time is configured to access Java members regardless of their access level.

B/c of that, I’ve completely skipped converting class MinimReflectHack to Python.

After all we need no extra reflection code in order to access AudioOutput::noteManager.now:

"""
MinimSequenceSound (v1.0.1)
GoToLoop (2020-Jun-04)

Discourse.Processing.org/t/getting-position-from-playnote-minim/21360/14

GitHub.com/ddf/Minim/blob/master/examples/Basics/SequenceSound/SequenceSound.pde
"""

add_library('minim')

AMP = 100.0
L_OFF = AMP
R_OFF = AMP + L_OFF

def settings():
    global out, nm, events, l, r, bufSzRange

    out = Minim(this).lineOut

    nm = out.noteManager
    events = nm.events

    l = out.left
    r = out.right

    bufSz = out.bufferSize()
    bufSzRange = tuple(range(1, bufSz))

    size(bufSz, this.round(R_OFF + AMP))

    print width, height, bufSz, ENTER
    print events
    print nm.now, ENTER


def setup():
    stroke(0xffFFFF00)
    respawnNotes()


def draw(NOW = 'now: '):
    clear()

    this.surface.title = NOW + `nm.now`

    yl = l.get(0) * AMP + L_OFF
    yr = r.get(0) * AMP + R_OFF
    ii = 0

    for i in bufSzRange:
        ly = l.get(i) * AMP + L_OFF
        ry = r.get(i) * AMP + R_OFF

        line(ii, yl, i, ly)
        line(ii, yr, i, ry)

        yl = ly
        yr = ry
        ii = i


def mousePressed(): respawnNotes()

def respawnNotes():
    out.pauseNotes()

    events.clear()
    nm.now = 0

    out.playNote(0.0, 0.9, 97.99)
    out.playNote(1.0, 0.9, 123.47)
    out.playNote(2.0, 2.9, "C3")
    out.playNote(3.0, 1.9, "E3")
    out.playNote(4.0, 0.9, "G3")
    out.playNote(5.0, "")
    out.playNote(6.0, 329.63)
    out.playNote(7.0, "G4")
    out.setNoteOffset(8.1)
    out.playNote("G5")
    out.playNote(987.77)

    out.resumeNotes()

    print events, ENTER
    print nm.now, ENTER