You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:07:29