如何基于Superpowered API实现多声音同时播放(鼓组场景)
嘿,我一眼就看出问题所在了——你现在只能播放单个鼓音的核心原因是每个音频播放器的输出直接覆盖了主缓冲区,没有做混音叠加。咱们来一步步修复这个问题:
问题分析
你当前的audioProcessing循环里,每个SuperpoweredAdvancedAudioPlayer调用process时,都是直接把音频数据写入self->stereoBuffer。这样一来,后面的播放器会完全覆盖前面播放器的输出,最后只能听到最后一个触发的鼓音。
解决方案:实现混音叠加
要让多个声音同时播放,我们需要把每个播放器的输出叠加到主缓冲区里,而不是替换。具体步骤如下:
- 添加临时缓冲区:用来存放单个播放器的输出,再把它叠加到主缓冲区
- 清空主缓冲区:每次处理音频前,先清空主缓冲区,避免残留上一帧的音频
- 逐个叠加播放器输出:遍历每个播放器,若它正在播放,就把它的输出叠加到主缓冲区
- 添加限幅器:混音后音量可能超过上限导致失真,用限幅器控制音量峰值
修改后的完整代码
首先,在你的类初始化时添加临时缓冲区(记得在dealloc时释放):
@interface Superpowered () { SuperpoweredIOSAudioIO *output; SuperpoweredAdvancedAudioPlayer *players[10]; float *stereoBuffer; float *tempBuffer; // 新增临时缓冲区 } @end @implementation Superpowered - (instancetype)init { self = [super init]; if (self) { // 初始化其他变量... // 分配临时缓冲区(按常见的最大帧数4096分配,可根据你的AudioIO设置调整) tempBuffer = (float *)malloc(sizeof(float) * 2 * 4096); } return self; } - (void)dealloc { free(tempBuffer); // 释放其他资源... }
然后修改audioProcessing函数:
static bool audioProcessing(SuperpoweredIOSAudioIO *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) { Superpowered *self = (Superpowered *)clientdata; bool overallSilence = true; // 1. 清空主立体声缓冲区,准备叠加混音 memset(self->stereoBuffer, 0, sizeof(float) * 2 * numberOfSamples); for(int i = 0; i < 10; i++) { SuperpoweredAdvancedAudioPlayer *player = self->players[i]; // 跳过未播放的播放器,节省性能 if (!player->playing) continue; // 2. 将当前播放器的输出写入临时缓冲区 bool silence = !player->process(self->tempBuffer, false, numberOfSamples); if (!silence) { overallSilence = false; // 3. 叠加临时缓冲区到主缓冲区(推荐用Superpowered提供的高效混音函数) SuperpoweredMix(self->stereoBuffer, self->tempBuffer, numberOfSamples * 2, 1.0f, 1.0f); // 也可以手动叠加,效果相同但效率稍低: // for (unsigned int j = 0; j < numberOfSamples * 2; j++) { // self->stereoBuffer[j] += self->tempBuffer[j]; // } } } if (!overallSilence) { // 4. 添加限幅器,防止混音后音量过大导致削波失真 SuperpoweredLimiter(self->stereoBuffer, numberOfSamples * 2, 0.9f, 0.01f); // 将处理好的立体声数据写入输出缓冲区 SuperpoweredDeInterleave(self->stereoBuffer, buffers[0], buffers[1], numberOfSamples); } return !overallSilence; }
额外注意事项
- 确保每个
SuperpoweredAdvancedAudioPlayer都正确加载了对应的鼓音文件(调用open方法并验证加载成功) - 你的
playWithPlayerAtIndex方法是正确的:seek(0)确保每次触发都从音频开头播放,play(false)表示不循环,符合鼓组的单次触发需求 - 如果发现音量异常,可以调整限幅器参数,或者给单个播放器设置增益(比如
player->volume = 0.7f)
这样修改后,你就能同时触发多个鼓音,实现鼓组的多声音播放了!
内容的提问来源于stack exchange,提问作者user3620372
相关产品推荐
相关产品推荐

