基于Superpowered Audio Kit的音频切换爆音问题排查求助
解决Superpowered音频切换时的爆音问题
你遇到的切换曲目时的尖锐爆音,大概率是音频资源销毁与新实例创建的时序冲突,或是旧音频线程未完全终止就启动新播放导致的。结合你的代码和Superpowered的工作机制,我整理了几个核心修复点:
一、核心问题拆解
- 资源释放不彻底:你直接删除
audioSystem(SuperpoweredAndroidAudioIO实例),但Superpowered的音频线程不会立即停止,此时立刻创建新实例,旧线程可能还在向输出缓冲区写数据,和新线程的输出叠加产生爆音。 - 缓冲区残留旧数据:新实例的
stereoBuffer没有初始化清零,残留的旧音频数据会在新曲目启动时先被输出,引发爆音。 - 切换时序不同步:Java端调用销毁方法后立刻启动新播放,没有给旧线程留出足够的终止时间。
二、具体修复方案
1. 优化资源销毁逻辑,确保线程完全停止
修改setDestroyAllThreads方法,先停止音频IO再释放资源,给线程留足终止时间:
void Eve::setDestroyAllThreads(){ // 先停止音频IO,等待线程终止 if (audioSystem) { audioSystem->stop(); usleep(10000); // 等待10ms,确保线程处理完当前缓冲区 } // 释放资源并置空指针,避免野指针 delete playerBass; playerBass = nullptr; delete playerB; playerB = nullptr; delete spatializer; spatializer = nullptr; delete filterb; filterb = nullptr; delete audioSystem; audioSystem = nullptr; // 重置状态变量 finishedPlaying = false; playingNow = false; pausedNow = false; processed = false; hd = false; }
2. 初始化时清空音频缓冲区
在Eve构造函数中,给新创建的stereoBuffer清零,避免残留旧数据:
Eve::Eve(const char *path,unsigned int samplerate,unsigned int buffersize) : activeFx(0), crossValue(0.0f), volBass(1.0f * headroom), volB(1.0f * headroom) { stereoBuffer = (float *)memalign(16,(buffersize + 16) * sizeof(float) * 2); // 初始化缓冲区为静音 memset(stereoBuffer, 0, (buffersize + 16) * sizeof(float) * 2); playerBass = new SuperpoweredAdvancedAudioPlayer(&playerBass , playerEventCallbackA, samplerate, 0); playerB = new SuperpoweredAdvancedAudioPlayer(&playerB, playerEventCallbackB, samplerate, 0); filterb = new SuperpoweredFilter(SuperpoweredFilter_Resonant_Lowpass, samplerate*0.98); spatializer = new SuperpoweredSpatializer(samplerate); playerBass->open(path); playerB->open(path); finishedPlaying = false; playerBass->syncMode = playerB->syncMode = SuperpoweredAdvancedAudioPlayerSyncMode_TempoAndBeat; audioSystem = new SuperpoweredAndroidAudioIO(samplerate, buffersize, false, true, audioProcessing, this, -1, SL_ANDROID_STREAM_MEDIA, buffersize * 2); }
3. 调整Java端切换时序,增加等待
在Java端调用销毁方法后,等待一小段时间再启动新播放:
// 修改Java端切换逻辑 PlayerClass.setDestroyAllThreads(); // 等待旧线程完全终止 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } PlayerClass.open("new_path", sampleRate, bufferSize); PlayerClass.play();
4. 在process方法中处理初始静音
确保新曲目启动前输出静音,避免异常数据:
bool Eve::process(short int *output, unsigned int numberOfSamples) { // 未播放时直接输出静音 if (!playingNow || (!playerBass->isPlaying && !playerB->isPlaying)) { memset(output, 0, numberOfSamples * 2 * sizeof(short)); return false; } bool masterIsA = (crossValue <= 0.5f); double masterBpm = masterIsA ? playerBass->currentBpm : playerB->currentBpm; double msElapsedSinceLastBeatA = playerBass->msElapsedSinceLastBeat; bool silence = !playerBass->process(stereoBuffer, false, numberOfSamples*1, volBass, masterBpm, playerB->msElapsedSinceLastBeat); processed = true; filterb->process(stereoBuffer, stereoBuffer, numberOfSamples); if (playerB->process(stereoBuffer, !silence, numberOfSamples, volB, masterBpm, msElapsedSinceLastBeatA)) silence = false; spatializer->process(stereoBuffer, stereoBuffer, (float*)output, (float*)output, numberOfSamples, true); SuperpoweredFloatToShortInt(stereoBuffer, output, numberOfSamples); return !silence; }
5. 修复全局指针的空指针问题
在JNI销毁方法中,销毁eve实例并置空:
extern "C" JNIEXPORT void Java_com_EvePlayer_setDestroyAllThreads(JNIEnv *javaEnvironment, jobject self) { if (eve) { eve->setDestroyAllThreads(); delete eve; eve = nullptr; } }
内容的提问来源于stack exchange,提问作者Isaac Oyewole
相关产品推荐
相关产品推荐

