1370 lines
42 KiB
Markdown
1370 lines
42 KiB
Markdown
# 音频引擎使用指南
|
||
|
||
## 目录
|
||
|
||
- [音频引擎使用指南](#音频引擎使用指南)
|
||
- [目录](#目录)
|
||
- [概述](#概述)
|
||
- [音频配置与格式](#音频配置与格式)
|
||
- [音频格式](#音频格式)
|
||
- [音频配置](#音频配置)
|
||
- [常见配置场景](#常见配置场景)
|
||
- [音频缓冲区管理](#音频缓冲区管理)
|
||
- [创建音频缓冲区](#创建音频缓冲区)
|
||
- [内存对齐与SIMD优化](#内存对齐与simd优化)
|
||
- [交错与非交错格式](#交错与非交错格式)
|
||
- [格式转换](#格式转换)
|
||
- [实时音频处理](#实时音频处理)
|
||
- [处理回调设计](#处理回调设计)
|
||
- [延迟考量](#延迟考量)
|
||
- [环形缓冲区](#环形缓冲区)
|
||
- [高级功能](#高级功能)
|
||
- [多声道处理](#多声道处理)
|
||
- [采样率转换](#采样率转换)
|
||
- [音频混合](#音频混合)
|
||
- [性能优化](#性能优化)
|
||
- [缓冲区大小选择](#缓冲区大小选择)
|
||
- [SIMD加速](#simd加速)
|
||
- [内存管理](#内存管理)
|
||
- [完整示例](#完整示例)
|
||
- [音频播放器](#音频播放器)
|
||
- [实时处理器](#实时处理器)
|
||
- [格式转换器](#格式转换器)
|
||
- [跨平台考虑](#跨平台考虑)
|
||
- [Windows 平台](#windows-平台)
|
||
- [Linux 平台](#linux-平台)
|
||
- [macOS 平台](#macos-平台)
|
||
|
||
## 概述
|
||
|
||
音频引擎是Audio Backend系统的核心组件,提供了高性能、低延迟的音频处理功能。音频引擎的主要目标是提供统一的音频缓冲区管理、音频格式转换和实时音频处理能力,同时通过SIMD指令集优化提供卓越的性能。
|
||
|
||
核心功能:
|
||
|
||
- **高性能音频缓冲区**:支持多种格式、对齐内存和零拷贝操作
|
||
- **格式转换**:在不同音频格式之间无缝转换
|
||
- **实时处理**:低延迟音频处理,适用于实时应用
|
||
- **多声道支持**:灵活的声道配置,支持交错和非交错格式
|
||
- **SIMD优化**:利用现代CPU的SIMD指令集加速音频处理
|
||
|
||
音频引擎架构:
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ 应用程序 │
|
||
└───────────┬─────────┘
|
||
│
|
||
┌───────────▼─────────┐
|
||
│ 音频引擎接口 │
|
||
└───────────┬─────────┘
|
||
│
|
||
┌───────────▼─────────┐
|
||
│ 音频缓冲区管理 │◄────┐
|
||
└───────────┬─────────┘ │
|
||
│ │
|
||
┌───────────▼─────────┐ │
|
||
│ 格式转换 │ │
|
||
└───────────┬─────────┘ │
|
||
│ │
|
||
┌───────────▼─────────┐ │
|
||
│ 实时处理 │ │
|
||
└───────────┬─────────┘ │
|
||
│ │
|
||
┌───────────▼─────────┐ │
|
||
│ SIMD优化 │─────┘
|
||
└─────────────────────┘
|
||
```
|
||
|
||
## 音频配置与格式
|
||
|
||
### 音频格式
|
||
|
||
音频引擎支持多种常见的音频格式,适用于不同的场景和需求:
|
||
|
||
```cpp
|
||
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] | 极高 | 精确计算、主音频总线 |
|
||
|
||
获取格式字节大小:
|
||
|
||
```cpp
|
||
// 获取音频格式的字节大小
|
||
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`结构包含了所有音频处理所需的基本配置参数:
|
||
|
||
```cpp
|
||
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;
|
||
};
|
||
```
|
||
|
||
### 常见配置场景
|
||
|
||
以下是几种常见的音频配置场景:
|
||
|
||
1. **低延迟实时处理**:
|
||
```cpp
|
||
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
|
||
```
|
||
|
||
2. **高质量音频处理**:
|
||
```cpp
|
||
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
|
||
```
|
||
|
||
3. **多声道环绕声**:
|
||
```cpp
|
||
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
|
||
```
|
||
|
||
4. **网络流传输**:
|
||
```cpp
|
||
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`类是音频引擎的核心,提供了灵活的音频数据管理:
|
||
|
||
```cpp
|
||
// 创建音频缓冲区
|
||
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); // 交错格式
|
||
```
|
||
|
||
基本操作:
|
||
|
||
```cpp
|
||
// 清空缓冲区(填充零)
|
||
buffer.clear();
|
||
|
||
// 重新分配缓冲区
|
||
buffer.allocate(1024, 2, AudioFormat::FLOAT32, false);
|
||
|
||
// 释放内存
|
||
buffer.release();
|
||
```
|
||
|
||
### 内存对齐与SIMD优化
|
||
|
||
音频引擎使用内存对齐技术确保SIMD指令能高效执行:
|
||
|
||
```cpp
|
||
// 检查缓冲区是否对齐
|
||
if (buffer.is_aligned()) {
|
||
// 可以安全地使用SIMD指令
|
||
// ...
|
||
} else {
|
||
// 回退到非SIMD版本
|
||
// ...
|
||
}
|
||
```
|
||
|
||
AudioBuffer内部使用AlignedBuffer确保内存按SIMD要求对齐:
|
||
|
||
```cpp
|
||
// AudioBuffer内部实现片段
|
||
simd::AlignedBuffer<uint8_t, simd::ALIGNMENT_AVX> data_;
|
||
```
|
||
|
||
### 交错与非交错格式
|
||
|
||
音频引擎支持两种主要的数据布局:
|
||
|
||
1. **交错格式**(LRLRLR...):
|
||
- 优点:缓存友好,适合顺序访问
|
||
- 缺点:单声道处理不方便
|
||
- 适用:音频文件IO,硬件接口
|
||
|
||
2. **非交错格式**(LLLL...RRRR...):
|
||
- 优点:单声道处理方便
|
||
- 缺点:多声道同时处理效率较低
|
||
- 适用:单声道效果处理,某些DSP算法
|
||
|
||
示例:
|
||
|
||
```cpp
|
||
// 创建交错格式缓冲区
|
||
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; // 右声道第一个样本
|
||
```
|
||
|
||
格式转换:
|
||
|
||
```cpp
|
||
// 交错格式转换为非交错格式
|
||
AudioBuffer non_interleaved = interleaved_buffer.to_non_interleaved();
|
||
|
||
// 非交错格式转换为交错格式
|
||
AudioBuffer interleaved = non_interleaved_buffer.to_interleaved();
|
||
```
|
||
|
||
### 格式转换
|
||
|
||
音频引擎支持在不同格式之间无缝转换:
|
||
|
||
```cpp
|
||
// 从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);
|
||
```
|
||
|
||
## 实时音频处理
|
||
|
||
### 处理回调设计
|
||
|
||
实时音频处理通常基于回调函数,以下是推荐的回调设计模式:
|
||
|
||
```cpp
|
||
// 音频处理回调函数类型
|
||
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);
|
||
```
|
||
|
||
重要的回调设计原则:
|
||
|
||
1. **效率**:回调必须高效执行,避免阻塞
|
||
2. **确定性**:处理时间应该可预测
|
||
3. **无分配**:不应在回调中分配/释放内存
|
||
4. **无锁**:避免使用互斥锁等阻塞操作
|
||
5. **无IO**:避免文件、网络或其他IO操作
|
||
|
||
### 延迟考量
|
||
|
||
音频引擎中的延迟主要来源:
|
||
|
||
1. **缓冲区大小**:较大的缓冲区增加延迟
|
||
```cpp
|
||
// 计算缓冲区引入的延迟
|
||
double latency_ms = config.get_latency_ms();
|
||
```
|
||
|
||
2. **处理链延迟**:效果和处理算法可能引入延迟
|
||
|
||
3. **硬件延迟**:音频设备本身的延迟
|
||
|
||
延迟预算管理:
|
||
|
||
```cpp
|
||
// 低延迟场景(游戏、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`类为音频流提供线程安全的缓冲能力:
|
||
|
||
```cpp
|
||
// 创建环形缓冲区
|
||
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 ...
|
||
}
|
||
```
|
||
|
||
环形缓冲区关键操作:
|
||
|
||
```cpp
|
||
// 检查可用数据量
|
||
size_t available = ring_buffer.available();
|
||
|
||
// 检查可写空间
|
||
size_t space = ring_buffer.space();
|
||
|
||
// 清空缓冲区
|
||
ring_buffer.clear();
|
||
|
||
// 调整大小(会清空现有数据)
|
||
ring_buffer.resize(96000);
|
||
```
|
||
|
||
## 高级功能
|
||
|
||
### 多声道处理
|
||
|
||
处理多声道音频的技术:
|
||
|
||
1. **声道遍历**:
|
||
```cpp
|
||
// 遍历所有声道
|
||
for (uint16_t ch = 0; ch < buffer.channels(); ++ch) {
|
||
float* channel_data = buffer.channel_data<float>(ch);
|
||
// 处理单个声道...
|
||
}
|
||
```
|
||
|
||
2. **声道操作**:
|
||
```cpp
|
||
// 不同声道处理
|
||
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; // 右声道增益
|
||
}
|
||
```
|
||
|
||
3. **声道路由**:
|
||
```cpp
|
||
// 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;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 采样率转换
|
||
|
||
基本采样率转换:
|
||
|
||
```cpp
|
||
// 将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)
|
||
```
|
||
|
||
注意:
|
||
1. 内置的resample方法使用简单的线性插值
|
||
2. 对于高质量转换,应考虑使用专业的重采样库
|
||
3. 降采样时,应先进行抗混叠滤波
|
||
|
||
### 音频混合
|
||
|
||
混合多个音频源:
|
||
|
||
```cpp
|
||
// 混合两个缓冲区
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
使用内置混合方法:
|
||
|
||
```cpp
|
||
// 假设已有两个缓冲区
|
||
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+ | 很低 | 批量处理,非实时应用 |
|
||
|
||
选择策略:
|
||
|
||
```cpp
|
||
// 根据应用场景选择缓冲区大小
|
||
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优化,自动选择最佳实现:
|
||
|
||
```cpp
|
||
// 使用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`
|
||
|
||
### 内存管理
|
||
|
||
音频处理中的内存管理最佳实践:
|
||
|
||
1. **预分配缓冲区**:
|
||
```cpp
|
||
// 在初始化时预分配所有缓冲区
|
||
AudioBuffer input_buffer(config);
|
||
AudioBuffer output_buffer(config);
|
||
AudioBuffer temp_buffer(config);
|
||
```
|
||
|
||
2. **重用缓冲区**:
|
||
```cpp
|
||
// 重用同一缓冲区进行多次处理
|
||
void process_chain(AudioBuffer& buffer) {
|
||
// 所有处理原地进行,避免分配新内存
|
||
apply_gain(buffer);
|
||
apply_filter(buffer);
|
||
apply_effect(buffer);
|
||
}
|
||
```
|
||
|
||
3. **避免频繁分配/释放**:
|
||
```cpp
|
||
// 错误示例:每次调用都创建临时缓冲区
|
||
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());
|
||
}
|
||
// ...处理...
|
||
}
|
||
};
|
||
```
|
||
|
||
4. **对齐内存**:
|
||
```cpp
|
||
// AudioBuffer已经内部使用对齐内存
|
||
// 如果需要自定义分配,使用AlignedBuffer
|
||
simd::AlignedBuffer<float, simd::ALIGNMENT_AVX> aligned_data(1024);
|
||
```
|
||
|
||
## 完整示例
|
||
|
||
### 音频播放器
|
||
|
||
以下是一个简单音频播放器的示例:
|
||
|
||
```cpp
|
||
#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;
|
||
}
|
||
```
|
||
|
||
### 实时处理器
|
||
|
||
以下是一个实时音频处理器示例:
|
||
|
||
```cpp
|
||
#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;
|
||
}
|
||
```
|
||
|
||
### 格式转换器
|
||
|
||
以下是一个音频格式转换器示例:
|
||
|
||
```cpp
|
||
#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有差异
|
||
|
||
```cpp
|
||
#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内联汇编
|
||
|
||
```cpp
|
||
#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默认编译器,苹果特定扩展
|
||
|
||
```cpp
|
||
#ifdef __APPLE__
|
||
// macOS特定代码
|
||
#include <TargetConditionals.h>
|
||
|
||
#if TARGET_CPU_ARM64
|
||
// Apple Silicon(ARM)代码
|
||
// 使用NEON指令
|
||
#else
|
||
// Intel Mac代码
|
||
// 使用SSE/AVX指令
|
||
#endif
|
||
#endif
|
||
```
|
||
|
||
跨平台构建策略:
|
||
|
||
1. **统一接口**:AudioBuffer提供统一接口,隐藏平台差异
|
||
2. **运行时检测**:使用CPU特性检测选择适当实现
|
||
3. **条件编译**:为特定平台提供优化实现
|
||
4. **内存管理抽象**:使用AlignedBuffer隐藏平台特定内存管理
|
||
|
||
```cpp
|
||
// 跨平台音频处理示例
|
||
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()
|
||
);
|
||
}
|
||
} |