electronstudio / jaylib

Java JNI bindings for Raylib
Other
111 stars 19 forks source link

How do you use function callbacks? #48

Closed deter0 closed 2 months ago

deter0 commented 3 months ago

Specifically I am trying to use AttachAudioStreamProcessor, but I'm not sure how to use/ construct AudioCallback

electronstudio commented 3 months ago

I don't know; you would have to ask the authors of JavaCPP or find an example of a callback in another JavaCPP library. What happens if you extend AudioCallback? e.g.

class MyAudioCallback extends AudioCallback{
    public void call(Pointer var1, int var2){

    }
}
electronstudio commented 3 months ago

I just tried that and it seems to work.

electronstudio commented 3 months ago

I converted https://github.com/raysan5/raylib/blob/master/examples/audio/audio_raw_stream.c

to https://github.com/electronstudio/jaylib-maven-example-project/blob/master/src/main/java/examples/AudioRawStream.java

It doesn't quite work - maybe because I didn't convert the unsigned int maths - but the callback runs and gets data.

electronstudio commented 3 months ago

It works now.

deter0 commented 3 months ago

Hmm that helps but how do I read a pointer of array. For example this C code:

static void callback(void *bufferData, unsigned int frames)
{
    // https://cdecl.org/?q=float+%28*fs%29%5B2%5D
    float (*fs)[2] = bufferData;

    for (size_t i = 0; i < frames; ++i) {
        fft_push(fs[i][0]);
    }
}
electronstudio commented 3 months ago

That code is just taking every second float from the buffer:

public void call(Pointer p, int frames) {
    FloatPointer fp = new FloatPointer(p);

    for (int i = 0; i < frames; i+=2) {
        fft_push(fp.get(i));
    }
}

If you do require the floats to be in arrays of 2 elements (one left channel and one right channel I guess) you could do it like this, but I don't think it's necessary:

public void call(Pointer p, int frames) {
    FloatPointer fp = new FloatPointer(p);

    for (int i = 0; i < frames; i+=2) {
        float[] fa = new float[2];
        fp.get(fa, i, i+1);
    }
}
deter0 commented 3 months ago

That makes a lot of sense but for some reason this is making my program crash with a weird error message, probably by some kind of memory corruption or something.

The error:

Error getting method ID of java/lang/RuntimeException/<init>
terminate called after throwing an instance of 'JavaCPP_exception'
  what():  java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java...

Code: https://gist.github.com/deter0/412e83e521f51218b9b35eb7b7225995

Screencast from 2024-06-07 09-11-36.webm

electronstudio commented 3 months ago

The callback is getting garbage collected. Stuff like that is a problem when you mix a language that expects you to manage your own memory with one that does it automatically. Try this

static AudioCallback audioCallback =  new AudioCallback() {
    @Override
    public void call(Pointer p, int frames) {
        FloatPointer fp = new FloatPointer(p);

        for (int i = 0; i < frames; i+=2) {
            float[] fa = new float[2];
            fp.get(fa, i, i+1);
        }
    }
};

void SetPlayingSong(Music song) {
    if (playingSong != null) {
        DetachAudioStreamProcessor(playingSong.stream(), null);
    }
    if (IsMusicReady(song)) {
        AttachAudioStreamProcessor(song.stream(), audioCallback);
        playingSong = song;
    }
}
deter0 commented 2 months ago

That worked, thank you so much!