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);
}