Issue with Superpowered Equalizer - Audio Muted Intermittently

I’m encountering an intermittent issue while using the Superpowered library to process audio in my Android app. I’m implementing an 8-band equalizer for both the left and right channels, along with other effects like reverb. However, sometimes when I activate the equalizer, the audio simply stops playing (it becomes muted).

Here’s a summary of what’s happening:

The audio player works fine without the equalizer. When I enable the equalizer (on both left and right channels), sometimes the audio works perfectly, but other times it goes completely silent. The equalizer is properly set up, and the initial gains for the bands are set to 0dB during initialization. Audio processing is done inside the audioProcessing function, where I apply the equalizer and other effects.

Here’s a snippet of the audio processing code:

static SuperpoweredAndroidAudioIO *audioIO;
static Superpowered::AdvancedAudioPlayer *player;
static SuperpoweredNBandEQ *leftEQ;
static SuperpoweredNBandEQ *rightEQ;
static Superpowered::Waveform *waveform;
static Superpowered::Reverb *reverb;

static const int numberOfBands = 8;
static bool  isProcessStereo = false;

static bool audioProcessing (
        void * __unused clientdata,
        short int *audio,
        int numberOfFrames,
        int samplerate
) {

    player->outputSamplerate = (unsigned int)samplerate;
    float playerOutput[numberOfFrames * 2];  // Buffer intercalado de áudio estéreo

    if (player->processStereo(playerOutput, false, (unsigned int)numberOfFrames)) {

        reverb->samplerate = samplerate;
        reverb->process(playerOutput, playerOutput, numberOfFrames);

        float leftChannel[numberOfFrames];
        float rightChannel[numberOfFrames];

        for (int i = 0; i < numberOfFrames; i++) {
            leftChannel[i] = playerOutput[i * 2];      // Canal esquerdo
            rightChannel[i] = playerOutput[i * 2 + 1]; // Canal direito
        }

        // Processar os canais com o equalizador
        leftEQ->process(leftChannel, leftChannel, (unsigned int)numberOfFrames);

        rightEQ->process(rightChannel, rightChannel, (unsigned int)numberOfFrames);

        // Recombinar os canais processados
        for (int i = 0; i < numberOfFrames; i++) {
            playerOutput[i * 2] = leftChannel[i];
            playerOutput[i * 2 + 1] = rightChannel[i];
        }

        waveform->process(playerOutput, numberOfFrames, -1);
        Superpowered::FloatToShortIntInterleave(leftChannel, rightChannel, audio, (unsigned int)numberOfFrames);
        isProcessStereo = true;
    } else {
        isProcessStereo = false;
    }

    return  isProcessStereo;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_expert_mixer_core_superpowered_SuperpoweredPlayerManager_NativeInit(JNIEnv *env, jobject thiz, jint samplerate,
                                                                             jint buffersize, jstring tempPath) {
    Superpowered::Initialize("ExampleLicenseKey-WillExpire-OnNextUpdate");

    const char *str = env->GetStringUTFChars(tempPath, nullptr);
    Superpowered::AdvancedAudioPlayer::setTempFolder(str);
    env->ReleaseStringUTFChars(tempPath, str);

    player = new Superpowered::AdvancedAudioPlayer((unsigned int)samplerate, 0);
    waveform = new Superpowered::Waveform(
            48000,
            0
    );

    float frequencies[numberOfBands] = {60.0f, 170.0f, 310.0f, 600.0f, 1000.0f, 3000.0f, 6000.0f, 12000.0f};

    leftEQ = new SuperpoweredNBandEQ((unsigned int)48000, frequencies);
    rightEQ = new SuperpoweredNBandEQ((unsigned int)48000, frequencies);

    reverb = new Superpowered::Reverb(48000, 48000);

    for (int i = 0; i < numberOfBands; i++) {
        leftEQ->setGainDb(i, 0.0f);
        rightEQ->setGainDb(i, 0.0f);
    }

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

extern "C"
JNIEXPORT void JNICALL
Java_com_expert_mixer_core_superpowered_SuperpoweredPlayerManager_ToggleEqualizer(JNIEnv *env, jobject thiz, jboolean value) {
    leftEQ->enabled = value;
    rightEQ->enabled = value;
}

I’ve already tried the following:

Resetting the equalizer gains every time Superpowered is initialized. Ensuring the equalizer is properly enabled before processing audio. Has anyone encountered this issue or has suggestions on where I might be going wrong?

It seems like you’ve encountered an issue with the audio becoming muted when you enable the equalizer. This is often caused by either:

  1. Incorrect handling of audio buffers after the equalizer is processed.
  2. Overflow or underflow in the audio buffer values, which could be causing silence when the gain processing is applied.
  3. Audio gain issues within the equalizer bands, where improper configuration might lead to a “mute-like” effect.

Here are some steps and suggestions to troubleshoot and resolve the issue:


1. Check the Input and Output Buffers:

Ensure that the audio buffer values are not getting corrupted during processing. When you’re splitting the stereo channels into leftChannel and rightChannel, and then recombining them after the equalizer processing, there could be an issue during this recombination.

What to check:

  • Ensure that both leftChannel and rightChannel arrays are correctly populated and recombined into playerOutput before converting them back to the final audio array.

To do this, you could add some logging (or breakpoints if debugging) to inspect the audio values before and after the equalizer processing, like:

for (int i = 0; i < numberOfFrames; i++) {
    // Log or check audio buffer values before and after EQ
    printf("Left: %f, Right: %f", leftChannel[i], rightChannel[i]);
}

2. Check the Output Levels of the Equalizer:

Ensure that the equalizer’s output is not too low, which might cause the audio to be inaudible. You may want to temporarily increase the gain on the EQ bands for testing purposes.

For example, try setting the gains to a non-zero value initially (e.g., +6dB) to check if the audio becomes audible again:

for (int i = 0; i < numberOfBands; i++) {
    leftEQ->setGainDb(i, 6.0f);   // Increase gain for testing
    rightEQ->setGainDb(i, 6.0f);  // Increase gain for testing
}

If the audio plays after increasing the gain, it suggests that the issue may be with the EQ settings or gain values. You can adjust these accordingly once the issue is identified.


3. Check the Sample Rate:

Make sure that the sample rates are being handled consistently across your processing pipeline. Since you’re initializing the EQ, player, and reverb with specific sample rates, any mismatch might cause unintended audio effects (like muting).

Ensure that the player->outputSamplerate, leftEQ->samplerate, rightEQ->samplerate, and reverb->samplerate are all consistent.

If necessary, re-verify the sample rate initialization and check if the sample rate changes dynamically during playback.


4. Normalize Audio Levels After Processing:

It’s possible that the output after processing (especially with the reverb and equalizer) is being reduced too much, causing the silence. Try adding a normalization step after processing the EQ and reverb to bring audio levels back to a reasonable range:

for (int i = 0; i < numberOfFrames; i++) {
    playerOutput[i * 2] *= 0.9f;      // Normalize left channel
    playerOutput[i * 2 + 1] *= 0.9f;  // Normalize right channel
}

This ensures that the final audio output isn’t too quiet after effects processing.


5. Ensure Proper Initialization and Enabling of EQ:

Even though you have set gains to 0dB, check if the leftEQ->enabled and rightEQ->enabled are properly set before processing. Additionally, ensure the equalizer isn’t unintentionally being disabled after it has been enabled.

You can add a conditional check within the audioProcessing function to verify if the equalizer is enabled during processing:

if (leftEQ->enabled && rightEQ->enabled) {
    // Proceed with equalizer processing
    leftEQ->process(leftChannel, leftChannel, (unsigned int)numberOfFrames);
    rightEQ->process(rightChannel, rightChannel, (unsigned int)numberOfFrames);
} else {
    // EQ is disabled, bypass processing
}

6. Test in Isolation:

Try commenting out the reverb processing temporarily to isolate whether the issue is with the reverb or equalizer. This helps to identify which effect might be causing the issue.

For example:

// Comment this out for testing
// reverb->process(playerOutput, playerOutput, numberOfFrames);

Test with just the equalizer to see if the issue persists. If removing reverb processing resolves the issue, it may be a problem with the interaction between the two effects (EQ and reverb).


7. Check for Edge Cases in Audio Buffers:

Ensure that you’re handling cases where numberOfFrames might vary, and ensure your audio buffers are of appropriate size. Any mismatch or buffer overflow could lead to silence or unexpected behavior.


Conclusion:

  • Check the audio buffers for correct handling.
  • Test with different EQ gain levels to ensure audio isn’t too quiet.
  • Normalize the output after applying EQ and reverb.
  • Isolate the issue by disabling certain effects and testing the behavior.
  • Ensure proper initialization of sample rates and EQ settings.

Let me know if the problem persists after applying these troubleshooting steps!