如何与超级玩家同时玩多个玩家[已解决]

时间:2019-07-06 19:18:03

标签: android superpowered

我正在尝试制作一个基本的混音器应用程序,该应用程序将同时播放多达4种声音。

这是我用来实现此目的的课程:

#define log_print __android_log_print

static SuperpoweredAndroidAudioIO *audioIO;
static SuperpoweredAdvancedAudioPlayer *playerA, *playerB, *playerC, *playerD;
static float *floatBuffer;

// This is called periodically by the audio engine.
static bool audioProcessing (
        void * __unused clientdata, // custom pointer
        short int *audio,           // buffer of interleaved samples
        int numberOfFrames,         // number of frames to process
        int __unused samplerate     // sampling rate
) {
    if(playerA->process(floatBuffer, false, (unsigned int)numberOfFrames)
       || playerB->process(floatBuffer, false, (unsigned int)numberOfFrames)
       || playerC->process(floatBuffer, false, (unsigned int)numberOfFrames)
       || playerD->process(floatBuffer, false, (unsigned int)numberOfFrames)) {
        SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int)numberOfFrames);
        return true;
    } else {
        return false;
    }
}

// Called by the playerA.
static void playerEventCallbackA (
        void * __unused clientData,
        SuperpoweredAdvancedAudioPlayerEvent event,
        void *value
) {
    switch (event) {
        case SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess:
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_LoadError:
            log_print(ANDROID_LOG_ERROR, "Player", "Open error: %s", (char *)value);
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_EOF:
            playerA->seek(0);    // loop track
            break;
        default:;
    };
}
static void playerEventCallbackB (
        void * __unused clientData,
        SuperpoweredAdvancedAudioPlayerEvent event,
        void *value
) {
    switch (event) {
        case SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess:
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_LoadError:
            log_print(ANDROID_LOG_ERROR, "Player", "Open error: %s", (char *)value);
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_EOF:
            playerB->seek(0);    // loop track
            break;
        default:;
    };
}
static void playerEventCallbackC (
        void * __unused clientData,
        SuperpoweredAdvancedAudioPlayerEvent event,
        void *value
) {
    switch (event) {
        case SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess:
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_LoadError:
            log_print(ANDROID_LOG_ERROR, "Player", "Open error: %s", (char *)value);
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_EOF:
            playerC->seek(0);    // loop track
            break;
        default:;
    };
}
static void playerEventCallbackD (
        void * __unused clientData,
        SuperpoweredAdvancedAudioPlayerEvent event,
        void *value
) {
    switch (event) {
        case SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess:
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_LoadError:
            log_print(ANDROID_LOG_ERROR, "Player", "Open error: %s", (char *)value);
            break;
        case SuperpoweredAdvancedAudioPlayerEvent_EOF:
            playerD->seek(0);    // loop track
            break;
        default:;
    };
}

// StartAudio - Start audio engine and initialize playerA.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_StartAudio (
        JNIEnv * __unused env,
        jobject  __unused obj,
        jint samplerate,
        jint buffersize
) {
    SuperpoweredInitialize(
            "ExampleLicenseKey-WillExpire-OnNextUpdate",
            false, // enableAudioAnalysis (using SuperpoweredAnalyzer, SuperpoweredLiveAnalyzer, SuperpoweredWaveform or SuperpoweredBandpassFilterbank)
            false, // enableFFTAndFrequencyDomain (using SuperpoweredFrequencyDomain, SuperpoweredFFTComplex, SuperpoweredFFTReal or SuperpoweredPolarFFT)
            false, // enableAudioTimeStretching (using SuperpoweredTimeStretching)
            false, // enableAudioEffects (using any SuperpoweredFX class)
            true, // enableAudioPlayerAndDecoder (using SuperpoweredAdvancedAudioPlayer or SuperpoweredDecoder)
            false, // enableCryptographics (using Superpowered::RSAPublicKey, Superpowered::RSAPrivateKey, Superpowered::hasher or Superpowered::AES)
            false  // enableNetworking (using Superpowered::httpRequest)
    );

    // Allocate audio buffer.
    floatBuffer = (float *)malloc(sizeof(float) * 2 * buffersize);

    // Initialize playerA and pass callback function.
    playerA = new SuperpoweredAdvancedAudioPlayer (
            NULL,                           // clientData
            playerEventCallbackA,            // callback function
            (unsigned int)samplerate,       // sampling rate
            0                               // cachedPointCount
    );
    // Initialize playerA and pass callback function.
    playerB = new SuperpoweredAdvancedAudioPlayer (
            NULL,                           // clientData
            playerEventCallbackA,            // callback function
            (unsigned int)samplerate,       // sampling rate
            0                               // cachedPointCount
    );
    // Initialize playerA and pass callback function.
    playerC = new SuperpoweredAdvancedAudioPlayer (
            NULL,                           // clientData
            playerEventCallbackA,            // callback function
            (unsigned int)samplerate,       // sampling rate
            0                               // cachedPointCount
    );
    // Initialize playerA and pass callback function.
    playerD = new SuperpoweredAdvancedAudioPlayer (
            NULL,                           // clientData
            playerEventCallbackA,            // callback function
            (unsigned int)samplerate,       // sampling rate
            0                               // cachedPointCount
    );

    // Initialize audio with audio callback function.
    audioIO = new SuperpoweredAndroidAudioIO (
            samplerate,                     // sampling rate
            buffersize,                     // buffer size
            false,                          // enableInput
            true,                           // enableOutput
            audioProcessing,                // process callback function
            NULL,                           // clientData
            -1,                             // inputStreamType (-1 = default)
            SL_ANDROID_STREAM_MEDIA         // outputStreamType (-1 = default)
    );
}

// OpenFile - Open file in playerA, specifying offset and length.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_OpenFile (
        JNIEnv *env,
        jobject __unused obj,
        jstring path,       // path to APK file
        jint channelID      // which player to use
) {
    log_print(ANDROID_LOG_INFO, "Player", "~~CHECKPOINT: %d", channelID);
    const char *str = env->GetStringUTFChars(path, 0);
    switch (channelID) {
        case 0:
            playerA->open(str);
            log_print(ANDROID_LOG_INFO, "Player", "~~Opening: %s", str);
            break;
        case 1:
            playerB->open(str);
            log_print(ANDROID_LOG_INFO, "Player", "~~Opening: %s", str);
            break;
        case 2:
            playerC->open(str);
            break;
        case 3:
            playerD->open(str);
            break;
        default: ;
    }
    env->ReleaseStringUTFChars(path, str);
}

// TogglePlayback - Toggle Play/Pause state of the playerA.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_TogglePlayback (
        JNIEnv * __unused env,
        jobject __unused obj,
        jint channelID,
        jboolean playing
) {
    switch (channelID) {
        case -1:
            if(playing){
                log_print(ANDROID_LOG_INFO, "Player", "~~PLAYING ALL: %d", channelID);
                playerA->play(true);
                playerB->play(true);
                playerC->play(true);
                playerD->play(true);
                SuperpoweredCPU::setSustainedPerformanceMode(playerA->playing);  // prevent dropouts
                SuperpoweredCPU::setSustainedPerformanceMode(playerB->playing);  // prevent dropouts
                SuperpoweredCPU::setSustainedPerformanceMode(playerC->playing);  // prevent dropouts
                SuperpoweredCPU::setSustainedPerformanceMode(playerD->playing);  // prevent dropouts
            } else {
                playerA->pause();
                    playerA->seek(0);
                playerB->pause();
                    playerB->seek(0);
                playerC->pause();
                    playerC->seek(0);
                playerD->pause();
                    playerD->seek(0);
            }
            break;
        case 0:
            if(playing){
                playerA->play(false);
                SuperpoweredCPU::setSustainedPerformanceMode(playerA->playing);  // prevent dropouts
            } else {
                playerA->pause();
                playerA->seek(0);
            }
            break;
        case 1:
            if(playing){
                playerB->play(false);
                SuperpoweredCPU::setSustainedPerformanceMode(playerB->playing);  // prevent dropouts
            } else playerB->pause();
            break;
        case 2:
            if(playing){
                playerC->play(false);
                SuperpoweredCPU::setSustainedPerformanceMode(playerC->playing);  // prevent dropouts
            } else playerC->pause();
            break;
        case 3:
            if(playing){
                playerD->play(false);
                SuperpoweredCPU::setSustainedPerformanceMode(playerD->playing);  // prevent dropouts
            } else playerD->pause();
            break;
        default:;
    }
}

// onBackground - Put audio processing to sleep.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_onBackground(
        JNIEnv *__unused env,
        jobject __unused obj
) {
    audioIO->onBackground();
}

// onForeground - Resume audio processing.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_onForeground(
        JNIEnv *__unused env,
        jobject __unused obj
) {
    audioIO->onForeground();
}

// Cleanup - Free resources.
extern "C" JNIEXPORT void
Java_com_connorschwing_CustomAudioManager_Cleanup(
        JNIEnv *__unused env,
        jobject __unused obj
) {
    delete audioIO;
    delete playerA;
    delete playerB;
    delete playerC;
    delete playerD;
    free(floatBuffer);
}

对于一首曲目效果很好。我能够录制声音,在播放器中打开它,然后播放并循环播放。但是,如果我在另一条轨道上录制并开始播放播放器B,则播放器B不会发出声音。我一次只能听到一条音轨。我知道我必须以某种方式将这些播放器组合到一个缓冲区中,但是我不确定该怎么做。现在,它只对每个背靠背调用player-> play(false)。

我应该如何更改此代码以允许多个玩家同时运行?

解决方案尝试1 我没有在每个调用中为bufferAdd值使用true / false,而是将调用本身用于其他播放器。这些对player-> process()的调用将返回一个布尔值,该布尔值将被设置为下一个调用的bufferAdd值

if((playerD->process(floatBuffer, playerC->process(floatBuffer, playerB->process(floatBuffer, playerA->process(floatBuffer, false, (unsigned int)numberOfFrames), (unsigned int)numberOfFrames), (unsigned int)numberOfFrames) , (unsigned int)numberOfFrames)))
{
    SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int)numberOfFrames);
    return true;
}

解决方案尝试2 检查每个播放器的状态,如果正在处理,则处理其他三个播放器

if(playerA->process(floatBuffer, false, (unsigned int)numberOfFrames))
{
    playerB->process(floatBuffer, true, (unsigned int)numberOfFrames);
    playerC->process(floatBuffer, true, (unsigned int)numberOfFrames);
    playerD->process(floatBuffer, true, (unsigned int)numberOfFrames);
    SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int)numberOfFrames);
    return true;
} else if(playerB->process(floatBuffer, false, (unsigned int)numberOfFrames)) {
    playerA->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerC->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerD->process(floatBuffer, true, (unsigned int) numberOfFrames);
    SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int) numberOfFrames);
    return true;
} else if(playerC->process(floatBuffer, false, (unsigned int)numberOfFrames)) {
    playerB->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerA->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerD->process(floatBuffer, true, (unsigned int) numberOfFrames);
    SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int) numberOfFrames);
    return true;
} else if(playerD->process(floatBuffer, false, (unsigned int)numberOfFrames)) {
    playerB->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerC->process(floatBuffer, true, (unsigned int) numberOfFrames);
    playerA->process(floatBuffer, true, (unsigned int) numberOfFrames);
    SuperpoweredFloatToShortInt(floatBuffer, audio, (unsigned int) numberOfFrames);
    return true;
} else return false;

3 个答案:

答案 0 :(得分:1)

  1. 在调用播放器[ABCD]-> process()的audioProcessing函数中,始终调用每个播放器的process()函数,无论其状态如何。 (在当前代码中,如果前一个返回true,||(OR)运算符不会调用下一个process()。)
  2. player-> process的第二个参数称为“ bufferAdd”。如果为true,则将其添加到缓冲区中的音频中;如果为false,则将替换缓冲区中的内容。 (在您当前的代码中为false,因此播放器不会混合在一起。)如果先前的播放器中的任何一个确实输出音频,则将“ bufferAdd”设置为true(从process()返回true)。因此,由于没有“以前的”播放器,因此只有第一个播放器的bufferAdd可以固定为false。

答案 1 :(得分:0)

更优雅的解决方案可以是:

bool silence = playerA->process(floatBuffer, false, ...);
silence |= playerB->process(floatBuffer, !silence, ...);
silence |= playerC->process(floatBuffer, !silence, ...);
silence |= playerD->process(floatBuffer, !silence, ...);

答案 2 :(得分:0)

@ConnorS 这是一个对我有用的片段,它有 4 个播放器和一个 StereoMixer:

bool CrossExample::process(short int *output, unsigned int numberOfFrames, unsigned int samplerate) {


player1->outputSamplerate = player2->outputSamplerate = player3->outputSamplerate = player4->outputSamplerate;


// Get audio from the players into a buffer on the stack.
float outputBuffer[numberOfFrames * 2];
bool silence = player1->processStereo(outputBuffer, false, numberOfFrames, volA);
if (player2->processStereo(outputBuffer, !silence, numberOfFrames, volA)) silence = false;
if (player3->processStereo(outputBuffer, !silence, numberOfFrames, volA)) silence = false;
if (player4->processStereo(outputBuffer, !silence, numberOfFrames, volA)) silence = false;

    mixer->process(outputBuffer,
            NULL,
            NULL,
            NULL,
            outputBuffer,
            numberOfFrames);
};

// The output buffer is ready now, let's write the finished audio into the requested buffer.
if (!silence) Superpowered::FloatToShortInt(outputBuffer, output, numberOfFrames);
return !silence; //this equals true

}