基于Android Making Waves教程实现AAudio立体声DSP及PAN效果
基于AAudio实现立体声DSP与声像(PAN)效果
一、先解决立体声失真问题
你把AAudioStreamBuilder_setChannelCount设为2后出现失真,核心原因是数据回调函数没有按照立体声的缓冲区格式处理数据。AAudio的立体声PCM缓冲区采用交错存储格式:每个音频帧包含左声道样本、右声道样本,依次循环(即[左, 右, 左, 右,...])。原来的单声道代码只处理单个样本,直接复用会导致样本错位,引发失真。
二、立体声流配置调整
首先确保流配置正确,除了声道数设为2,其他参数保持低延迟要求:
AAudioStreamBuilder *streamBuilder; AAudio_createStreamBuilder(&streamBuilder); AAudioStreamBuilder_setFormat(streamBuilder, AAUDIO_FORMAT_PCM_FLOAT); AAudioStreamBuilder_setChannelCount(streamBuilder, 2); // 改为立体声 AAudioStreamBuilder_setPerformanceMode(streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); AAudioStreamBuilder_setDataCallback(streamBuilder, ::dataCallback, &oscillator_); AAudioStreamBuilder_setErrorCallback(streamBuilder, ::errorCallback, this); // 打开流等后续代码保持不变
三、实现声像(PAN)效果的核心逻辑
声像控制的关键是给左右声道分配不同的幅度,常用正弦余弦法保证总音量稳定(避免pan到中间时音量翻倍):
- 定义pan参数范围:
-1.0f(完全左声道)到1.0f(完全右声道),0为居中 - 计算幅度:
- 左声道幅度:
cos(pan * M_PI_4) - 右声道幅度:
sin(pan * M_PI_4)
(M_PI_4是π/4,对应45度,保证左右声道幅度平方和为1,总能量不变)
- 左声道幅度:
四、修改数据回调函数
假设你的Oscillator类需要新增pan_成员变量来控制声像,修改后的回调函数如下:
aaudio_data_callback_result_t dataCallback( AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) { Oscillator *oscillator = static_cast<Oscillator*>(userData); float *outputBuffer = static_cast<float*>(audioData); // 遍历每个音频帧,填充左右声道样本 for (int32_t i = 0; i < numFrames; ++i) { // 生成单声道基础波形样本 float monoSample = oscillator->nextSample(); // 计算声像对应的左右声道幅度 float pan = oscillator->getPan(); float leftAmplitude = cosf(pan * M_PI_4); float rightAmplitude = sinf(pan * M_PI_4); // 填充到交错缓冲区:左声道在前,右声道在后 outputBuffer[i * 2] = monoSample * leftAmplitude; outputBuffer[i * 2 + 1] = monoSample * rightAmplitude; } return AAUDIO_CALLBACK_RESULT_CONTINUE; }
同时,在Oscillator类中添加pan的get/set方法:
class Oscillator { private: float pan_ = 0.0f; // 默认居中 // 其他原有成员变量... public: void setPan(float pan) { // 限制pan范围在-1到1之间 pan_ = std::clamp(pan, -1.0f, 1.0f); } float getPan() const { return pan_; } // 原有nextSample()等方法... };
五、关键注意事项
- 确保缓冲区类型正确:因为你设置了
AAUDIO_FORMAT_PCM_FLOAT,所以要把audioData转为float*处理,不要用整数格式 - 样本数量计算:
numFrames是音频帧的数量,每个帧包含2个样本(左右声道),所以总样本数是numFrames * 2 - 幅度计算用
cosf/sinf(浮点版本),避免整数运算精度问题
内容的提问来源于stack exchange,提问作者Massimo Vantaggio
相关产品推荐
相关产品推荐

