Search code examples
javajava-native-interfacejna

How can I call function with PointerByReference arguments for a Float[]?


I have a library that uses some c++ compiled code and I would like to use this and other functions to try it out but they all use Pointer and PointerByReference as arguments instead of normal types.

fun writeSampleFloat(length: Int,
                         leftOut: PointerByReference /*Float[]*/,
                         rightOut: PointerByReference/*Float[]*/)

I see it uses the JNA library but I cannot find how to get Pointer by reference of Float[] or Pointer of FloatArray or Pointer of ShortArray.

Ok So I am using instance of synth loaded in library project that uses jna, I would like record sound from it by using oboe example that I have in other library project but that uses jni. So basically everything works I can use synth and also oboe example can render sound from microphone and my idea to record that synth is to pass that instance of synth as native reference to that oboe cpp code so I can use render callback there to write data from synth. I can see how I can write data correctly but I don't know how to pass there instance. My first question was different becasue first idea was that I will call java form oboe rendering callback but because that is some realtime audio stuff this is no possible and worng approach I believe. so I would like to pass that synth I already have headers of synth in oboe included ...

So I have JNA val handle: PointerByReference in my kotlin/java project that references synth instance. I have also working jni access to oboe code like this :

 external fun create(): Boolean

that is defined in cpp like this:

Java_com_sheraz_oboerecorder_AudioEngine_create(JNIEnv *env, jobject obj)

can I pass that synth handle somehow so I can cast that handle of synth in cpp to actual type that i included in cpp and call that rendering function on it ?

I know probably best would be to write it all in jna but this cpp world is difficult for me. I just need to find a way to call that synth instance I see example how to write data from synth:

onAudioReady(AudioStream *stream, void *audioData, int32_t numFrames)
    {
    if(stream->getFormat() == AudioFormat::Float)
            {
                synth_write_float(dev->synth, numFrames, static_cast<float *>(audioData), 0, 2, static_cast<float *>(audioData), 1, 2);
            }
            else
            {
                synth_write_s16(dev->synth, numFrames, static_cast<short *>(audioData), 0, 2, static_cast<short *>(audioData), 1, 2);
            }
 return DataCallbackResult::Continue;
    }

This function I have it renders nicely audio from microphone and saves to file already. Now I have it like this

oboe::DataCallbackResult
RecordingCallback::onAudioReady(oboe::AudioStream *audioStream, void *audioData,
                                int32_t numFrames) {
   mSoundRecording->write(audioData, numFrames);
return oboe::DataCallbackResult::Continue;
}

I hope I can just pass there my JNA handle somehow and call that function on synth form audio rendering callback. This question starts to sound too complicated maybe it's another one.


Solution

  • Since you don't include more of the API for a specific call I can't directly answer your implementation question, but I will address the portion of the question that asks about the types.

    In C (and C++), arrays are stored in contiguous native memory. If you know the type (float in this case) you can simply offset from the pointer to get the appropriate element, e.g., foo[0] is at the pointer location to the array, foo[1] would be at that pointer location plus an offset equal to the type byte size (4 bytes for a float) and so on.

    The Pointer and PointerByReference are JNA types. PointerByReference is a pointer that points to a Pointer; you can call the getValue() function on the PointerByReference() to retrieve this pointer.

    Based on the way I read this API, that Pointer is actually the beginning of the float array, so you'd just use that Pointer and retrieve the array from its location.

    So this is likely what you need to do:

    length len = 123; // I assume you know this length
    
    // call the function with your length
    
    Pointer p = leftOut.getValue();
    float[] leftArray = p.getFloatArray(0, len);
    
    Pointer q = rightOut.getValue();
    float[] rightArray = q.getFloatArray(0, len);
    

    There may be a bit more to it, depending on whether the native float array memory is allocated by the API (in which case look for a function to release that memory) or whether you are required to allocate the native memory yourself, in which case you would do this:

    // allocate memory for the float array of size len
    Memory m = new Memory(4 * len);
    // Memory extends pointer so just pass this to the PBR constructor
    PointerByReference leftOut = new PointerByReference(m);
    // now pass leftOut to the method
    

    When using only JNA, you rarely need to deal with the actual native value of the pointer. However, in your edited question you seem to be wanting to interact with JNI code. In this case you'll want the pointer value to pass to those functions. It can be obtained with

    long peer = Pointer.nativeValue(p); // or m or whatever the pointer is