42 KiB
音频引擎使用指南
目录
概述
音频引擎是Audio Backend系统的核心组件,提供了高性能、低延迟的音频处理功能。音频引擎的主要目标是提供统一的音频缓冲区管理、音频格式转换和实时音频处理能力,同时通过SIMD指令集优化提供卓越的性能。
核心功能:
- 高性能音频缓冲区:支持多种格式、对齐内存和零拷贝操作
- 格式转换:在不同音频格式之间无缝转换
- 实时处理:低延迟音频处理,适用于实时应用
- 多声道支持:灵活的声道配置,支持交错和非交错格式
- SIMD优化:利用现代CPU的SIMD指令集加速音频处理
音频引擎架构:
┌─────────────────────┐
│ 应用程序 │
└───────────┬─────────┘
│
┌───────────▼─────────┐
│ 音频引擎接口 │
└───────────┬─────────┘
│
┌───────────▼─────────┐
│ 音频缓冲区管理 │◄────┐
└───────────┬─────────┘ │
│ │
┌───────────▼─────────┐ │
│ 格式转换 │ │
└───────────┬─────────┘ │
│ │
┌───────────▼─────────┐ │
│ 实时处理 │ │
└───────────┬─────────┘ │
│ │
┌───────────▼─────────┐ │
│ SIMD优化 │─────┘
└─────────────────────┘
音频配置与格式
音频格式
音频引擎支持多种常见的音频格式,适用于不同的场景和需求:
enum class AudioFormat {
UNKNOWN = 0,
INT16, // 16位有符号整数 [-32768, 32767]
INT24, // 24位有符号整数(包在int32中)[-8388608, 8388607]
INT32, // 32位有符号整数
FLOAT32, // 32位浮点数 [-1.0, 1.0]
FLOAT64 // 64位浮点数 [-1.0, 1.0]
};
各格式特点和应用场景:
| 格式 | 位深 | 范围 | 精度 | 常见应用 |
|---|---|---|---|---|
| INT16 | 16位 | [-32768, 32767] | 中等 | 音频文件存储、网络传输 |
| INT24 | 24位 | [-8388608, 8388607] | 高 | 高质量音频录制 |
| INT32 | 32位 | [-2147483648, 2147483647] | 非常高 | 专业音频处理 |
| FLOAT32 | 32位 | [-1.0, 1.0] | 高 | 实时处理、音效 |
| FLOAT64 | 64位 | [-1.0, 1.0] | 极高 | 精确计算、主音频总线 |
获取格式字节大小:
// 获取音频格式的字节大小
inline size_t get_format_byte_size(AudioFormat format) {
switch (format) {
case AudioFormat::INT16: return 2;
case AudioFormat::INT24: return 3;
case AudioFormat::INT32: return 4;
case AudioFormat::FLOAT32: return 4;
case AudioFormat::FLOAT64: return 8;
default: return 0;
}
}
音频配置
AudioConfig结构包含了所有音频处理所需的基本配置参数:
struct AudioConfig {
uint32_t sample_rate = 48000; // 采样率(Hz)
uint16_t channels = 2; // 声道数
AudioFormat format = AudioFormat::FLOAT32; // 音频格式
uint32_t frames_per_buffer = 512; // 每个缓冲区的帧数
// 验证配置有效性
bool is_valid() const;
// 计算缓冲区大小(字节)
size_t get_buffer_size_bytes() const;
// 计算缓冲区大小(样本数)
size_t get_buffer_size_samples() const;
// 计算延迟(毫秒)
double get_latency_ms() const;
};
常见配置场景
以下是几种常见的音频配置场景:
-
低延迟实时处理:
AudioConfig low_latency_config; low_latency_config.sample_rate = 48000; low_latency_config.channels = 2; low_latency_config.format = AudioFormat::FLOAT32; low_latency_config.frames_per_buffer = 128; // 2.67ms @ 48kHz -
高质量音频处理:
AudioConfig high_quality_config; high_quality_config.sample_rate = 96000; high_quality_config.channels = 2; high_quality_config.format = AudioFormat::FLOAT32; high_quality_config.frames_per_buffer = 1024; // 10.67ms @ 96kHz -
多声道环绕声:
AudioConfig surround_config; surround_config.sample_rate = 48000; surround_config.channels = 8; // 7.1声道 surround_config.format = AudioFormat::FLOAT32; surround_config.frames_per_buffer = 512; // 10.67ms @ 48kHz -
网络流传输:
AudioConfig network_stream_config; network_stream_config.sample_rate = 44100; network_stream_config.channels = 2; network_stream_config.format = AudioFormat::INT16; // 节省带宽 network_stream_config.frames_per_buffer = 441; // 10ms @ 44.1kHz
音频缓冲区管理
创建音频缓冲区
AudioBuffer类是音频引擎的核心,提供了灵活的音频数据管理:
// 创建音频缓冲区
AudioConfig config;
config.sample_rate = 48000;
config.channels = 2;
config.format = AudioFormat::FLOAT32;
config.frames_per_buffer = 512;
// 方法1: 使用配置创建
AudioBuffer buffer(config);
// 方法2: 使用单独参数创建
AudioBuffer buffer2(512, // 帧数
2, // 声道数
AudioFormat::FLOAT32, // 格式
true); // 交错格式
基本操作:
// 清空缓冲区(填充零)
buffer.clear();
// 重新分配缓冲区
buffer.allocate(1024, 2, AudioFormat::FLOAT32, false);
// 释放内存
buffer.release();
内存对齐与SIMD优化
音频引擎使用内存对齐技术确保SIMD指令能高效执行:
// 检查缓冲区是否对齐
if (buffer.is_aligned()) {
// 可以安全地使用SIMD指令
// ...
} else {
// 回退到非SIMD版本
// ...
}
AudioBuffer内部使用AlignedBuffer确保内存按SIMD要求对齐:
// AudioBuffer内部实现片段
simd::AlignedBuffer<uint8_t, simd::ALIGNMENT_AVX> data_;
交错与非交错格式
音频引擎支持两种主要的数据布局:
-
交错格式(LRLRLR...):
- 优点:缓存友好,适合顺序访问
- 缺点:单声道处理不方便
- 适用:音频文件IO,硬件接口
-
非交错格式(LLLL...RRRR...):
- 优点:单声道处理方便
- 缺点:多声道同时处理效率较低
- 适用:单声道效果处理,某些DSP算法
示例:
// 创建交错格式缓冲区
AudioBuffer interleaved_buffer(config, true); // true = 交错
// 创建非交错格式缓冲区
AudioBuffer non_interleaved_buffer(config, false); // false = 非交错
// 访问交错格式数据
float* interleaved_data = interleaved_buffer.interleaved_data<float>();
interleaved_data[0] = 0.5f; // 第一个样本,左声道
interleaved_data[1] = 0.5f; // 第一个样本,右声道
// 访问非交错格式数据
float* left_channel = non_interleaved_buffer.channel_data<float>(0);
float* right_channel = non_interleaved_buffer.channel_data<float>(1);
left_channel[0] = 0.5f; // 左声道第一个样本
right_channel[0] = 0.5f; // 右声道第一个样本
格式转换:
// 交错格式转换为非交错格式
AudioBuffer non_interleaved = interleaved_buffer.to_non_interleaved();
// 非交错格式转换为交错格式
AudioBuffer interleaved = non_interleaved_buffer.to_interleaved();
格式转换
音频引擎支持在不同格式之间无缝转换:
// 从FLOAT32转换为INT16
AudioBuffer float_buffer(config);
// ... 填充float_buffer ...
AudioBuffer int16_buffer = float_buffer.convert_format(AudioFormat::INT16);
// 从INT16转换为FLOAT32
AudioConfig int16_config = config;
int16_config.format = AudioFormat::INT16;
AudioBuffer int16_buffer(int16_config);
// ... 填充int16_buffer ...
AudioBuffer float_buffer2 = int16_buffer.convert_format(AudioFormat::FLOAT32);
实时音频处理
处理回调设计
实时音频处理通常基于回调函数,以下是推荐的回调设计模式:
// 音频处理回调函数类型
using AudioProcessCallback = std::function<void(
const AudioBuffer& input_buffer,
AudioBuffer& output_buffer,
void* user_data
)>;
// 在应用中实现回调
void my_audio_callback(const AudioBuffer& input, AudioBuffer& output, void* user_data) {
// 处理每个样本
for (uint32_t i = 0; i < input.frames(); ++i) {
for (uint16_t ch = 0; ch < input.channels(); ++ch) {
// 在这里执行音频处理
// 例如:应用增益
float sample = input.channel_data<float>(ch)[i];
output.channel_data<float>(ch)[i] = sample * 0.8f;
}
}
}
// 注册回调
audio_engine.set_process_callback(my_audio_callback, user_context);
重要的回调设计原则:
- 效率:回调必须高效执行,避免阻塞
- 确定性:处理时间应该可预测
- 无分配:不应在回调中分配/释放内存
- 无锁:避免使用互斥锁等阻塞操作
- 无IO:避免文件、网络或其他IO操作
延迟考量
音频引擎中的延迟主要来源:
-
缓冲区大小:较大的缓冲区增加延迟
// 计算缓冲区引入的延迟 double latency_ms = config.get_latency_ms(); -
处理链延迟:效果和处理算法可能引入延迟
-
硬件延迟:音频设备本身的延迟
延迟预算管理:
// 低延迟场景(游戏、VoIP)
// 总延迟预算:<20ms
AudioConfig low_latency;
low_latency.sample_rate = 48000;
low_latency.frames_per_buffer = 256; // 5.33ms @ 48kHz
// 音乐制作场景
// 总延迟预算:<30ms
AudioConfig music_production;
music_production.sample_rate = 48000;
music_production.frames_per_buffer = 512; // 10.67ms @ 48kHz
// 流媒体场景
// 总延迟预算:<100ms
AudioConfig streaming;
streaming.sample_rate = 44100;
streaming.frames_per_buffer = 1024; // 23.22ms @ 44.1kHz
环形缓冲区
RingBuffer类为音频流提供线程安全的缓冲能力:
// 创建环形缓冲区
RingBuffer<float> ring_buffer(48000); // 容量为48000样本
// 生产者线程(例如,音频捕获)
void producer_thread() {
float samples[256];
// ... 填充samples ...
// 写入环形缓冲区
size_t written = ring_buffer.write(samples, 256);
if (written < 256) {
// 缓冲区已满,处理溢出...
}
}
// 消费者线程(例如,音频回放)
void consumer_thread() {
float samples[256];
// 从环形缓冲区读取
size_t read = ring_buffer.read(samples, 256);
if (read < 256) {
// 缓冲区不足,处理饥饿...
}
// ... 处理samples ...
}
环形缓冲区关键操作:
// 检查可用数据量
size_t available = ring_buffer.available();
// 检查可写空间
size_t space = ring_buffer.space();
// 清空缓冲区
ring_buffer.clear();
// 调整大小(会清空现有数据)
ring_buffer.resize(96000);
高级功能
多声道处理
处理多声道音频的技术:
-
声道遍历:
// 遍历所有声道 for (uint16_t ch = 0; ch < buffer.channels(); ++ch) { float* channel_data = buffer.channel_data<float>(ch); // 处理单个声道... } -
声道操作:
// 不同声道处理 float* left = buffer.channel_data<float>(0); float* right = buffer.channel_data<float>(1); for (uint32_t i = 0; i < buffer.frames(); ++i) { // 左右声道处理不同 left[i] *= 0.8f; // 左声道衰减 right[i] *= 1.2f; // 右声道增益 } -
声道路由:
// 2声道到5.1声道映射 void stereo_to_5point1(const AudioBuffer& stereo, AudioBuffer& surround) { float* stereo_left = stereo.channel_data<float>(0); float* stereo_right = stereo.channel_data<float>(1); float* front_left = surround.channel_data<float>(0); float* front_right = surround.channel_data<float>(1); float* center = surround.channel_data<float>(2); float* lfe = surround.channel_data<float>(3); float* rear_left = surround.channel_data<float>(4); float* rear_right = surround.channel_data<float>(5); for (uint32_t i = 0; i < stereo.frames(); ++i) { front_left[i] = stereo_left[i]; front_right[i] = stereo_right[i]; center[i] = (stereo_left[i] + stereo_right[i]) * 0.5f; lfe[i] = (stereo_left[i] + stereo_right[i]) * 0.3f; rear_left[i] = stereo_left[i] * 0.4f; rear_right[i] = stereo_right[i] * 0.4f; } }
采样率转换
基本采样率转换:
// 将48kHz转换为44.1kHz
AudioConfig src_config;
src_config.sample_rate = 48000;
src_config.channels = 2;
src_config.frames_per_buffer = 480; // 10ms @ 48kHz
AudioBuffer src_buffer(src_config);
// ... 填充src_buffer ...
// 执行转换
AudioBuffer dst_buffer = src_buffer.resample(44100);
// dst_buffer.frames() 约为 441 (10ms @ 44.1kHz)
注意:
- 内置的resample方法使用简单的线性插值
- 对于高质量转换,应考虑使用专业的重采样库
- 降采样时,应先进行抗混叠滤波
音频混合
混合多个音频源:
// 混合两个缓冲区
void mix_buffers(const AudioBuffer& buffer1,
const AudioBuffer& buffer2,
AudioBuffer& output,
float gain1 = 1.0f,
float gain2 = 1.0f) {
// 确保格式兼容
assert(buffer1.channels() == buffer2.channels());
assert(buffer1.format() == buffer2.format());
assert(buffer1.frames() == buffer2.frames());
// 准备输出缓冲区
if (output.empty() ||
output.channels() != buffer1.channels() ||
output.frames() != buffer1.frames()) {
output.allocate(buffer1.frames(), buffer1.channels(), buffer1.format());
}
// 混合每个声道
for (uint16_t ch = 0; ch < buffer1.channels(); ++ch) {
const float* in1 = buffer1.channel_data<float>(ch);
const float* in2 = buffer2.channel_data<float>(ch);
float* out = output.channel_data<float>(ch);
for (uint32_t i = 0; i < buffer1.frames(); ++i) {
out[i] = in1[i] * gain1 + in2[i] * gain2;
// 简单限制以防止削波
if (out[i] > 1.0f) out[i] = 1.0f;
if (out[i] < -1.0f) out[i] = -1.0f;
}
}
}
使用内置混合方法:
// 假设已有两个缓冲区
AudioBuffer buffer1(config);
AudioBuffer buffer2(config);
// ... 填充buffer1和buffer2 ...
// 复制第一个缓冲区到输出
AudioBuffer output = buffer1.clone();
// 混合第二个缓冲区到输出,应用0.8的增益
output.mix_from(buffer2, 0.8f);
性能优化
缓冲区大小选择
缓冲区大小是延迟和CPU使用率的权衡:
| 缓冲区大小 | 延迟 (@48kHz) | CPU使用率 | 适用场景 |
|---|---|---|---|
| 64帧 | 1.33ms | 很高 | 乐器演奏,超低延迟要求 |
| 128帧 | 2.67ms | 高 | 游戏音效,语音通话 |
| 256帧 | 5.33ms | 中高 | 实时音乐应用 |
| 512帧 | 10.67ms | 中 | 音乐制作,多轨混音 |
| 1024帧 | 21.33ms | 低 | 音频渲染,流媒体 |
| 2048帧+ | 42.67ms+ | 很低 | 批量处理,非实时应用 |
选择策略:
// 根据应用场景选择缓冲区大小
AudioConfig config;
config.sample_rate = 48000;
config.channels = 2;
config.format = AudioFormat::FLOAT32;
// 场景选择
enum class ApplicationType {
INSTRUMENT, // 实时乐器
GAME, // 游戏音效
MUSIC_CREATION, // 音乐制作
STREAMING, // 音频流
BATCH_PROCESS // 批处理
};
// 根据应用类型设置缓冲区大小
void configure_for_application(AudioConfig& config, ApplicationType type) {
switch (type) {
case ApplicationType::INSTRUMENT:
config.frames_per_buffer = 128;
break;
case ApplicationType::GAME:
config.frames_per_buffer = 256;
break;
case ApplicationType::MUSIC_CREATION:
config.frames_per_buffer = 512;
break;
case ApplicationType::STREAMING:
config.frames_per_buffer = 1024;
break;
case ApplicationType::BATCH_PROCESS:
config.frames_per_buffer = 4096;
break;
}
}
SIMD加速
音频引擎集成了SIMD优化,自动选择最佳实现:
// 使用SIMD优化的音频处理
#include "simd/audio_processing.h"
void apply_simd_processing(AudioBuffer& buffer) {
// 确保缓冲区是FLOAT32格式
if (buffer.format() != AudioFormat::FLOAT32) {
buffer = buffer.convert_format(AudioFormat::FLOAT32);
}
// 应用音量变化(使用SIMD)
for (uint16_t ch = 0; ch < buffer.channels(); ++ch) {
float* data = buffer.channel_data<float>(ch);
// 自动选择最佳SIMD实现
simd::CALL_SIMD_AUDIO_FUNCTION(
apply_gain_f32,
data, // 输入
0.8f, // 增益
data, // 输出(原地操作)
buffer.frames() // 样本数
);
}
}
SIMD优化的音频函数:
- 音频混合:
mix_audio_f32,mix_audio_multi_f32 - 音量控制:
apply_gain_f32,apply_gain_ramp_f32 - 格式转换:
convert_i16_to_f32,convert_f32_to_i16 - 向量操作:
vector_add_f32,vector_multiply_f32 - 分析函数:
calculate_rms_f32,calculate_peak_f32
内存管理
音频处理中的内存管理最佳实践:
-
预分配缓冲区:
// 在初始化时预分配所有缓冲区 AudioBuffer input_buffer(config); AudioBuffer output_buffer(config); AudioBuffer temp_buffer(config); -
重用缓冲区:
// 重用同一缓冲区进行多次处理 void process_chain(AudioBuffer& buffer) { // 所有处理原地进行,避免分配新内存 apply_gain(buffer); apply_filter(buffer); apply_effect(buffer); } -
避免频繁分配/释放:
// 错误示例:每次调用都创建临时缓冲区 void bad_process(const AudioBuffer& input) { // 每次调用都分配新内存,造成内存碎片和性能下降 AudioBuffer temp(input.frames(), input.channels(), input.format()); // ...处理... } // 正确示例:使用成员变量保存缓冲区 class AudioProcessor { private: AudioBuffer temp_buffer_; public: void process(const AudioBuffer& input) { // 只在需要时重新分配 if (temp_buffer_.empty() || temp_buffer_.frames() != input.frames() || temp_buffer_.channels() != input.channels()) { temp_buffer_.allocate(input.frames(), input.channels(), input.format()); } // ...处理... } }; -
对齐内存:
// AudioBuffer已经内部使用对齐内存 // 如果需要自定义分配,使用AlignedBuffer simd::AlignedBuffer<float, simd::ALIGNMENT_AVX> aligned_data(1024);
完整示例
音频播放器
以下是一个简单音频播放器的示例:
#include "audio_buffer.h"
#include "simd/audio_processing.h"
#include <iostream>
#include <vector>
using namespace audio_backend;
class SimplePlayer {
private:
engine::AudioBuffer buffer_;
engine::AudioConfig config_;
size_t position_;
bool playing_;
float volume_;
public:
SimplePlayer() : position_(0), playing_(false), volume_(1.0f) {
// 设置默认配置
config_.sample_rate = 44100;
config_.channels = 2;
config_.format = engine::AudioFormat::FLOAT32;
config_.frames_per_buffer = 512;
}
// 加载音频数据
bool load_audio_data(const float* data, size_t frames, uint16_t channels) {
config_.frames_per_buffer = frames;
config_.channels = channels;
// 分配缓冲区并复制数据
buffer_.allocate(config_, true); // 交错格式
std::memcpy(buffer_.interleaved_data<float>(),
data,
frames * channels * sizeof(float));
position_ = 0;
return true;
}
// 开始播放
void play() {
playing_ = true;
}
// 暂停播放
void pause() {
playing_ = false;
}
// 停止播放并重置位置
void stop() {
playing_ = false;
position_ = 0;
}
// 设置音量
void set_volume(float volume) {
volume_ = volume;
}
// 处理回调(用于音频API回调)
void process(float* output, size_t frames) {
if (!playing_ || buffer_.empty()) {
// 如果没在播放或无数据,输出静音
std::memset(output, 0, frames * config_.channels * sizeof(float));
return;
}
// 计算可用帧数
size_t available_frames = buffer_.frames() - position_;
size_t frames_to_copy = std::min(available_frames, frames);
if (frames_to_copy > 0) {
// 复制音频数据
const float* input = buffer_.interleaved_data<float>() +
position_ * config_.channels;
std::memcpy(output, input, frames_to_copy * config_.channels * sizeof(float));
// 应用音量
simd::CALL_SIMD_AUDIO_FUNCTION(
apply_gain_f32,
output, // 输入
volume_, // 增益
output, // 输出
frames_to_copy * config_.channels // 样本数
);
position_ += frames_to_copy;
}
// 如果需要,用静音填充剩余部分
if (frames_to_copy < frames) {
std::memset(
output + frames_to_copy * config_.channels,
0,
(frames - frames_to_copy) * config_.channels * sizeof(float)
);
// 如果到达末尾,循环或停止
if (position_ >= buffer_.frames()) {
// 这里实现循环播放
position_ = 0;
// 或停止播放
// playing_ = false;
}
}
}
// 获取当前状态
bool is_playing() const { return playing_; }
float get_volume() const { return volume_; }
size_t get_position() const { return position_; }
size_t get_duration() const { return buffer_.frames(); }
};
// 使用示例
int main() {
// 创建播放器
SimplePlayer player;
// 生成测试音频数据(1秒的440Hz正弦波)
const size_t sample_rate = 44100;
const size_t frames = sample_rate;
const uint16_t channels = 2;
std::vector<float> test_data(frames * channels);
for (size_t i = 0; i < frames; ++i) {
float sample = 0.5f * std::sin(2.0f * 3.14159f * 440.0f * i / sample_rate);
for (uint16_t c = 0; c < channels; ++c) {
test_data[i * channels + c] = sample;
}
}
// 加载音频数据
player.load_audio_data(test_data.data(), frames, channels);
// 设置音量并播放
player.set_volume(0.8f);
player.play();
// 在实际应用中,这里会连接到音频API的回调
// 示例:创建一个输出缓冲区并处理一些帧
const size_t buffer_size = 512;
std::vector<float> output_buffer(buffer_size * channels);
std::cout << "开始播放..." << std::endl;
// 模拟处理多个缓冲区
for (size_t i = 0; i < 10; ++i) {
player.process(output_buffer.data(), buffer_size);
// 在实际应用中,这里会将数据发送到音频设备
std::cout << "处理缓冲区 " << i << ", 位置: "
<< player.get_position() << "/" << player.get_duration() << std::endl;
}
// 暂停播放
player.pause();
std::cout << "播放已暂停" << std::endl;
// 停止并重置
player.stop();
std::cout << "播放已停止" << std::endl;
return 0;
}
实时处理器
以下是一个实时音频处理器示例:
#include "audio_buffer.h"
#include "simd/audio_processing.h"
#include <iostream>
#include <vector>
#include <array>
using namespace audio_backend;
// 简单的延迟效果处理器
class DelayProcessor {
private:
engine::AudioConfig config_;
std::vector<float> delay_buffer_;
size_t delay_length_; // 延迟长度(样本)
size_t write_pos_; // 写位置
float feedback_; // 反馈量
float dry_mix_; // 干信号混合比例
float wet_mix_; // 湿信号混合比例
public:
DelayProcessor(uint32_t sample_rate = 44100,
uint16_t channels = 2,
float delay_time_ms = 500.0f,
float feedback = 0.3f,
float dry_mix = 0.7f,
float wet_mix = 0.3f)
: delay_length_(static_cast<size_t>(sample_rate * delay_time_ms / 1000.0f)),
write_pos_(0),
feedback_(feedback),
dry_mix_(dry_mix),
wet_mix_(wet_mix) {
// 设置配置
config_.sample_rate = sample_rate;
config_.channels = channels;
config_.format = engine::AudioFormat::FLOAT32;
config_.frames_per_buffer = 512; // 默认值
// 分配延迟缓冲区(每个声道一个)
delay_buffer_.resize(delay_length_ * channels, 0.0f);
}
// 设置延迟时间
void set_delay_time(float delay_time_ms) {
size_t new_delay_length = static_cast<size_t>(
config_.sample_rate * delay_time_ms / 1000.0f);
if (new_delay_length != delay_length_) {
delay_length_ = new_delay_length;
delay_buffer_.resize(delay_length_ * config_.channels, 0.0f);
write_pos_ = 0;
}
}
// 设置反馈量
void set_feedback(float feedback) {
feedback_ = feedback;
}
// 设置混合比例
void set_mix(float dry_mix, float wet_mix) {
dry_mix_ = dry_mix;
wet_mix_ = wet_mix;
}
// 处理音频
void process(engine::AudioBuffer& buffer) {
// 确保格式正确
if (buffer.format() != engine::AudioFormat::FLOAT32) {
throw std::runtime_error("DelayProcessor只支持FLOAT32格式");
}
// 更新配置(如果必要)
if (buffer.channels() != config_.channels ||
buffer.sample_rate() != config_.sample_rate) {
config_.channels = buffer.channels();
config_.sample_rate = buffer.sample_rate();
delay_buffer_.resize(delay_length_ * config_.channels, 0.0f);
}
// 创建临时缓冲区用于原始输入
engine::AudioBuffer input_copy = buffer.clone();
// 对每一帧进行处理
for (uint32_t i = 0; i < buffer.frames(); ++i) {
for (uint16_t ch = 0; ch < buffer.channels(); ++ch) {
// 读取输入样本
float input_sample = input_copy.channel_data<float>(ch)[i];
// 计算延迟缓冲区读取位置
size_t read_pos = (write_pos_ - delay_length_ * config_.channels +
ch + delay_buffer_.size()) % delay_buffer_.size();
// 获取延迟样本
float delayed_sample = delay_buffer_[read_pos];
// 计算输出样本(干/湿混合)
float output_sample = input_sample * dry_mix_ + delayed_sample * wet_mix_;
buffer.channel_data<float>(ch)[i] = output_sample;
// 更新延迟缓冲区(输入 + 反馈)
delay_buffer_[(write_pos_ + ch) % delay_buffer_.size()] =
input_sample + delayed_sample * feedback_;
}
// 更新写入位置
write_pos_ = (write_pos_ + config_.channels) % delay_buffer_.size();
}
}
};
// 使用示例
int main() {
// 创建音频配置
engine::AudioConfig config;
config.sample_rate = 44100;
config.channels = 2;
config.format = engine::AudioFormat::FLOAT32;
config.frames_per_buffer = 512;
// 创建延迟处理器
DelayProcessor delay_processor(
config.sample_rate,
config.channels,
300.0f, // 300ms延迟
0.4f, // 40%反馈
0.6f, // 60%干信号
0.4f // 40%湿信号
);
// 创建测试音频数据
engine::AudioBuffer test_buffer(config);
// 生成测试音频(440Hz正弦波,逐渐衰减)
for (uint32_t i = 0; i < test_buffer.frames(); ++i) {
float amplitude = 0.8f * (1.0f - static_cast<float>(i) / test_buffer.frames());
float sample = amplitude * std::sin(2.0f * 3.14159f * 440.0f * i / config.sample_rate);
for (uint16_t ch = 0; ch < test_buffer.channels(); ++ch) {
test_buffer.channel_data<float>(ch)[i] = sample;
}
}
std::cout << "原始音频峰值: " <<
simd::CALL_SIMD_AUDIO_FUNCTION(
calculate_peak_f32,
test_buffer.channel_data<float>(0),
test_buffer.frames()
) << std::endl;
// 处理音频
delay_processor.process(test_buffer);
std::cout << "处理后音频峰值: " <<
simd::CALL_SIMD_AUDIO_FUNCTION(
calculate_peak_f32,
test_buffer.channel_data<float>(0),
test_buffer.frames()
) << std::endl;
// 改变处理参数再次处理
delay_processor.set_delay_time(500.0f); // 500ms延迟
delay_processor.set_feedback(0.6f); // 60%反馈
delay_processor.set_mix(0.5f, 0.5f); // 50/50混合
delay_processor.process(test_buffer);
std::cout << "再次处理后音频峰值: " <<
simd::CALL_SIMD_AUDIO_FUNCTION(
calculate_peak_f32,
test_buffer.channel_data<float>(0),
test_buffer.frames()
) << std::endl;
return 0;
}
格式转换器
以下是一个音频格式转换器示例:
#include "audio_buffer.h"
#include "simd/audio_processing.h"
#include <iostream>
#include <iomanip>
#include <chrono>
using namespace audio_backend;
// 音频格式转换器类
class FormatConverter {
public:
// 转换采样率
static engine::AudioBuffer convert_sample_rate(
const engine::AudioBuffer& input,
uint32_t new_sample_rate) {
return input.resample(new_sample_rate);
}
// 转换格式
static engine::AudioBuffer convert_format(
const engine::AudioBuffer& input,
engine::AudioFormat new_format) {
return input.convert_format(new_format);
}
// 转换声道数(简单混合/复制)
static engine::AudioBuffer convert_channels(
const engine::AudioBuffer& input,
uint16_t new_channels) {
if (input.channels() == new_channels) {
return input.clone();
}
// 创建新的缓冲区
engine::AudioConfig new_config = input.config();
new_config.channels = new_channels;
engine::AudioBuffer output(new_config, input.is_interleaved());
if (input.channels() == 1 && new_channels > 1) {
// 单声道到多声道:复制到所有声道
const float* mono = input.channel_data<float>(0);
for (uint16_t ch = 0; ch < new_channels; ++ch) {
float* out = output.channel_data<float>(ch);
std::memcpy(out, mono, input.frames() * sizeof(float));
}
}
else if (input.channels() > 1 && new_channels == 1) {
// 多声道到单声道:混合所有声道
float* mono = output.channel_data<float>(0);
std::memset(mono, 0, output.frames() * sizeof(float));
float scale = 1.0f / input.channels();
for (uint16_t ch = 0; ch < input.channels(); ++ch) {
const float* in = input.channel_data<float>(ch);
for (uint32_t i = 0; i < input.frames(); ++i) {
mono[i] += in[i] * scale;
}
}
}
else if (input.channels() > new_channels) {
// 多声道到少声道:保留前N个声道
for (uint16_t ch = 0; ch < new_channels; ++ch) {
float* out = output.channel_data<float>(ch);
const float* in = input.channel_data<float>(ch);
std::memcpy(out, in, input.frames() * sizeof(float));
}
}
else {
// 少声道到多声道:复制现有声道,其余设为0
for (uint16_t ch = 0; ch < input.channels(); ++ch) {
float* out = output.channel_data<float>(ch);
const float* in = input.channel_data<float>(ch);
std::memcpy(out, in, input.frames() * sizeof(float));
}
for (uint16_t ch = input.channels(); ch < new_channels; ++ch) {
float* out = output.channel_data<float>(ch);
std::memset(out, 0, output.frames() * sizeof(float));
}
}
return output;
}
// 完整转换
static engine::AudioBuffer convert(
const engine::AudioBuffer& input,
uint32_t new_sample_rate,
uint16_t new_channels,
engine::AudioFormat new_format) {
// 按顺序执行转换
engine::AudioBuffer temp = input;
// 先转换采样率
if (new_sample_rate != input.sample_rate()) {
temp = convert_sample_rate(temp, new_sample_rate);
}
// 再转换声道数
if (new_channels != temp.channels()) {
temp = convert_channels(temp, new_channels);
}
// 最后转换格式
if (new_format != temp.format()) {
temp = convert_format(temp, new_format);
}
return temp;
}
};
// 使用示例
int main() {
// 创建测试音频数据
engine::AudioConfig source_config;
source_config.sample_rate = 48000;
source_config.channels = 2;
source_config.format = engine::AudioFormat::FLOAT32;
source_config.frames_per_buffer = 1000;
engine::AudioBuffer source_buffer(source_config);
// 生成测试音频数据
for (uint16_t ch = 0; ch < source_buffer.channels(); ++ch) {
float* data = source_buffer.channel_data<float>(ch);
for (uint32_t i = 0; i < source_buffer.frames(); ++i) {
data[i] = 0.5f * std::sin(2.0f * 3.14159f * 440.0f * i / source_config.sample_rate);
}
}
std::cout << "源音频配置:" << std::endl;
std::cout << " 采样率: " << source_buffer.sample_rate() << " Hz" << std::endl;
std::cout << " 声道数: " << source_buffer.channels() << std::endl;
std::cout << " 格式: " << engine::get_format_name(source_buffer.format()) << std::endl;
std::cout << " 帧数: " << source_buffer.frames() << std::endl;
std::cout << " 大小: " << source_buffer.size_bytes() << " 字节" << std::endl;
// 测量格式转换性能
auto start = std::chrono::high_resolution_clock::now();
// 转换到不同格式
engine::AudioBuffer int16_buffer = FormatConverter::convert_format(
source_buffer, engine::AudioFormat::INT16);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "\n转换到INT16:" << std::endl;
std::cout << " 大小: " << int16_buffer.size_bytes() << " 字节" << std::endl;
std::cout << " 转换时间: " << duration.count() << " 微秒" << std::endl;
// 转换到单声道
start = std::chrono::high_resolution_clock::now();
engine::AudioBuffer mono_buffer = FormatConverter::convert_channels(
source_buffer, 1);
end = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "\n转换到单声道:" << std::endl;
std::cout << " 声道数: " << mono_buffer.channels() << std::endl;
std::cout << " 转换时间: " << duration.count() << " 微秒" << std::endl;
// 转换到44.1kHz
start = std::chrono::high_resolution_clock::now();
engine::AudioBuffer resampled_buffer = FormatConverter::convert_sample_rate(
source_buffer, 44100);
end = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "\n转换到44.1kHz:" << std::endl;
std::cout << " 采样率: " << resampled_buffer.sample_rate() << " Hz" << std::endl;
std::cout << " 帧数: " << resampled_buffer.frames() << std::endl;
std::cout << " 转换时间: " << duration.count() << " 微秒" << std::endl;
// 完整转换
start = std::chrono::high_resolution_clock::now();
engine::AudioBuffer converted_buffer = FormatConverter::convert(
source_buffer,
44100, // 新采样率
1, // 新声道数
engine::AudioFormat::INT16 // 新格式
);
end = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "\n完整转换:" << std::endl;
std::cout << " 采样率: " << converted_buffer.sample_rate() << " Hz" << std::endl;
std::cout << " 声道数: " << converted_buffer.channels() << std::endl;
std::cout << " 格式: " << engine::get_format_name(converted_buffer.format()) << std::endl;
std::cout << " 帧数: " << converted_buffer.frames() << std::endl;
std::cout << " 大小: " << converted_buffer.size_bytes() << " 字节" << std::endl;
std::cout << " 总转换时间: " << duration.count() << " 微秒" << std::endl;
return 0;
}
跨平台考虑
音频引擎设计为跨平台工作,但在不同平台上仍有一些考虑因素:
Windows 平台
- 内存对齐:Windows API提供
_aligned_malloc/_aligned_free - SIMD支持:广泛支持SSE/AVX系列指令集
- 编译器:MSVC对SIMD有良好支持,但语法与GCC/Clang有差异
#ifdef _WIN32
// Windows特定代码
#include <malloc.h>
void* aligned_memory = _aligned_malloc(size, alignment);
_aligned_free(aligned_memory);
#endif
Linux 平台
- 内存对齐:POSIX API提供
posix_memalign - SIMD支持:现代发行版广泛支持,但较老系统可能不支持AVX+
- 编译器:GCC/Clang支持丰富的SIMD内联汇编
#ifdef __linux__
// Linux特定代码
void* aligned_memory = nullptr;
if (posix_memalign(&aligned_memory, alignment, size) != 0) {
// 错误处理
}
free(aligned_memory);
#endif
macOS 平台
- 内存对齐:POSIX API同Linux
- SIMD支持:Intel Mac支持SSE/AVX,Apple Silicon支持NEON
- 编译器:Clang默认编译器,苹果特定扩展
#ifdef __APPLE__
// macOS特定代码
#include <TargetConditionals.h>
#if TARGET_CPU_ARM64
// Apple Silicon(ARM)代码
// 使用NEON指令
#else
// Intel Mac代码
// 使用SSE/AVX指令
#endif
#endif
跨平台构建策略:
- 统一接口:AudioBuffer提供统一接口,隐藏平台差异
- 运行时检测:使用CPU特性检测选择适当实现
- 条件编译:为特定平台提供优化实现
- 内存管理抽象:使用AlignedBuffer隐藏平台特定内存管理
// 跨平台音频处理示例
void process_audio_cross_platform(engine::AudioBuffer& buffer) {
// 使用通用接口,内部会选择最佳平台特定实现
for (uint16_t ch = 0; ch < buffer.channels(); ++ch) {
float* data = buffer.channel_data<float>(ch);
// 自动选择最佳实现(SSE/AVX/NEON/标量)
simd::CALL_SIMD_AUDIO_FUNCTION(
apply_gain_f32,
data,
0.8f,
data,
buffer.frames()
);
}
}