superpoweredSDK / Low-Latency-Android-iOS-Linux-Windows-tvOS-macOS-Interactive-Audio-Platform

🇸Superpowered Audio, Networking and Cryptographics SDKs. High performance and cross platform on Android, iOS, macOS, tvOS, Linux, Windows and modern web browsers.
https://superpowered.com
1.33k stars 285 forks source link

Not getting audioprocessing callback from SuperpoweredAndroidAudioIO.cpp #744

Closed nprapchat closed 1 year ago

nprapchat commented 1 year ago

Hi Team, I am facing issue on recording audio on some the devices, in some of the devices I am not getting audioprocessing callback from SuperpoweredAndroidAudioIO.cpp, can you guys please help me to find the cause?

SuperpoweredAndroidAudioIO_OutputCallback() in this method getting callback false

if (!internals->callback(internals->clientdata, output, internals->buffersize,
                                     internals->samplerate)) {
                silence = true;
                internals->silenceFrames += internals->buffersize;
            } 

Thank you.

balazsbanto commented 1 year ago

Hey, thanks for reaching out.

internals->callback is callback provided by the integrator, so I would need more info on how your callback looks. For e.g. in our SuperpoweredPlayer sample app (https://github.com/superpoweredSDK/Low-Latency-Android-iOS-Linux-Windows-tvOS-macOS-Interactive-Audio-Platform/blob/master/Examples_Android/SuperpoweredPlayer/app/src/main/cpp/PlayerExample.cpp) it looks like this:

// This is the internals->callback,  which is called periodically by the audio engine.
static bool audioProcessing (
        void * __unused clientdata, // custom pointer
        short int *audio,           // output buffer
        int numberOfFrames,         // number of frames to process
        int samplerate              // current sample rate in Hz
) {
    player->outputSamplerate = (unsigned int)samplerate;
    float playerOutput[numberOfFrames * 2];

    if (player->processStereo(playerOutput, false, (unsigned int)numberOfFrames)) {
        Superpowered::FloatToShortInt(playerOutput, audio, (unsigned int)numberOfFrames);
        return true;
    } else return false;
}
nprapchat commented 1 year ago

thank you for reply @balazsbanto

my callback looks like this

`static bool audioProcessing(void *clientdata, short int *audioIO, int numberOfSamples,
                               int __unused samplerate) {
    bool isSuccess = true;
    if (nullptr != clientdata && ((Recorder *) clientdata)->isRecorderRunning()) {
        isSuccess = ((Recorder *) clientdata)->process(audioIO, (unsigned int) numberOfSamples, true);
    }
    return isSuccess;
}`

and the process function looks like this

`bool Recorder::process(short int *buffer, unsigned int numberOfSamples, bool isRecorded) {
    bool isSuccess = false;
    try {
        if (isRecorderRunning() && nullptr != buffer) {

            double v = 0;
            for (int i = 0; i < numberOfSamples; i++) {
                v += buffer[i] * buffer[i];
            }

            double amplitude = v / (double) numberOfSamples;
            double volume = 0;
            if (amplitude > 0) {
                volume = 10 * log10(amplitude);
            }
            WaveformPoints::GetInstance()->SetNextPoint(volume);
            if (nullptr != recordingBuffer) {
                Superpowered::ShortIntToFloat(buffer, recordingBuffer, (unsigned int) numberOfSamples);
                audioRecorder->recordInterleaved(recordingBuffer, numberOfSamples);
                noOfSamples++;
                isSuccess = true;
            } else {

              isSuccess = false;
            }
        }
    } 

    return isSuccess;
}`
balazsbanto commented 1 year ago

You should investigate the cases where isSuccess becomes false.

nprapchat commented 1 year ago

for me it's not coming in this callback, so not able to check process method

``static bool audioProcessing(void *clientdata, short int *audioIO, int numberOfSamples,
                               int __unused samplerate) {
    bool isSuccess = true;
    if (nullptr != clientdata && ((Recorder *) clientdata)->isRecorderRunning()) {
        isSuccess = ((Recorder *) clientdata)->process(audioIO, (unsigned int) numberOfSamples, true);
    }
    return isSuccess;
}`` 
balazsbanto commented 1 year ago

Previously you mentioned that the callback method returns false. If it returns false than it must enter somehow. Maybe you initialise the SuperpoweredAndroidAudioIO class with another callback.

So basically when you create the SuperpoweredAndroidAudioIO instance

  audioIO = new SuperpoweredAndroidAudioIO (
            samplerate,                     // device native sampling rate
            buffersize,                     // device native buffer size
            true,                          // enableInput
            true,                           // enableOutput
            audioProcessing,                // process callback function
            NULL,                           // clientData
            -1,                             // inputStreamType (-1 = default)
            SL_ANDROID_STREAM_MEDIA         // outputStreamType (-1 = default)
    );

you pass in the audioProcessing YOUR callback, which is assigned to internals->callback, and that is called in the code that you mentioned prevously:

if (!internals->callback(internals->clientdata, output, internals->buffersize,
                                     internals->samplerate)) {
                silence = true;
                internals->silenceFrames += internals->buffersize;
            } 
nprapchat commented 1 year ago

getting internals->hasOutput true for some of the devices while recording

`SuperpoweredAndroidAudioIO_InputCallback(SLAndroidSimpleBufferQueueItf caller, void *pContext) {
    SuperpoweredAndroidAudioIOInternals *internals = (SuperpoweredAndroidAudioIOInternals *) pContext;
    internals->inputFifo.incWrite(internals->numBuffers);
    if (!internals->hasOutput &&
        internals->inputFifo.hasAudio()) { // When there is no audio output configured and we have enough audio input available.

        internals->callback(internals->clientdata, internals->inputFifo.buffer +
                                                   internals->inputFifo.readIndex *
                                                   internals->bufferStep, internals->buffersize,
                            internals->samplerate);
        internals->inputFifo.incRead(internals->numBuffers);
    }

    (*caller)->Enqueue(caller, internals->inputFifo.buffer +
                               internals->inputFifo.writeIndex * internals->bufferStep,
                       (SLuint32) internals->buffersize * NUM_CHANNELS * 2);
}`
balazsbanto commented 1 year ago

I don't really understand what you are trying to say. internals->hasOutput is initialised with the value you pass to the SuperpoweredAndroidAudioIO constructor. For e.g.:

 audioIO = new SuperpoweredAndroidAudioIO (
            samplerate,                  
            buffersize,                  
            true,                         
            true,                           // THIS WILL BE THE VALUE OF internals->hasOutput
            audioProcessing,               
            NULL,                        
            -1,                           
            SL_ANDROID_STREAM_MEDIA     
    );