Oboe GitHub 主页 : GitHub/Oboe
① 简单使用 : Getting Started
② Oboe 全指南 : Full Guide To Oboe
③ Oboe API 参考 : API reference
④ Android 音频框架发展 : Android audio history
在 【Android 高性能音频】Oboe 开发流程 ( 导入 Oboe 库 | 使用预构建的二进制库和头文件 | 编译 Oboe 源码 ) 博客中介绍了 如何导入 Oboe 函数库到项目中 , 本博客中在导入 Oboe 函数库的基础上 , 进行 Oboe 播放器功能开发 ;
在 【Android 高性能音频】Oboe 开发流程 ( 包含头 Oboe 头文件 | 创建音频流 | 设置音频流 | 音频流回调类 AudioStreamCallback ) 介绍了如何创建 AudioStreamBuilder , 以及 创建 AudioStreamCallback 回调 ;
在 【Android 高性能音频】Oboe 开发流程 ( 创建并设置 AudioStreamCallback 对象 | 打开 Oboe 音频流 | 日志封装 logging_macros.h ) 博客中介绍了 设置 AudioStreamCallback 对象 , 打开 Oboe 音频流 操作 , 以及 Google 官方提供的日志封装有文件 ;
在 【Android 高性能音频】Oboe 开发流程 ( 检查 Oboe 音频流属性 | 开始播放 | 停止播放 | 关闭 Oboe 音频流 | 重新配置 Oboe 音频流属性 ) 博客中介绍了 如何开始 Oboe 音频流播放 , 以及 播放完毕后的收尾工作 ;
在 【Android 高性能音频】Oboe 开发流程 ( Oboe 完整代码示例 ) 中展示了一个 完整的 Oboe 播放器案例 ;
帧 代表一个 声音单元 , 该单元中的 采样个数 是 声道数 ;
该 声音单元 ( 帧 ) 中的 采样大小 是 样本位数 与 声道数 乘积 ;
下面的代码是 【Android 高性能音频】Oboe 开发流程 ( Oboe 完整代码示例 ) 博客中的 Oboe 音频流创建时 的代码 , 设置 Oboe 音频流 的参数如下 ;
设置的 采样格式 是 oboe::AudioFormat::Float , 每个采样都是一个 float 单精度浮点数 , 4 4 4 字节 ;
设置的 声道数 是 oboe::ChannelCount::Stereo , 立体声 , 左右双声道 ;
则对应的 1 1 1 个音频帧 中包含 2 2 2 个采样 , 左声道 1 1 1 个采样 , 右声道 1 1 1 个采样 , 每个采样是 4 4 4 字节的单精度浮点类型 float 类型 ;
上述 1 1 1 个音频帧的字节大小是 2 × 4 = 8 2\times 4 = 8 2×4=8 字节 ;
// 1. 音频流构建器
oboe::AudioStreamBuilder builder = oboe::AudioStreamBuilder();
// 设置音频流方向
builder.setDirection(oboe::Direction::Output);
// 设置性能优先级
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
// 设置共享模式 , 独占
builder.setSharingMode(oboe::SharingMode::Exclusive);
// 设置音频采样格式
builder.setFormat(oboe::AudioFormat::Float);
// 设置声道数 , 单声道/立体声
builder.setChannelCount(oboe::ChannelCount::Stereo);
// 设置采样率
builder.setSampleRate(48000);
// 设置回调对象 , 注意要设置 AudioStreamCallback * 指针类型
builder.setCallback(&myCallback);
如果设置的 采样格式 是 oboe::AudioFormat::I16 , 每个采样都是一个 16 16 16 位整型 , 2 2 2 字节 , 相当于 short 类型 ;
设置的 声道数 是 oboe::ChannelCount::Stereo , 立体声 , 左右双声道 ;
则对应的 1 1 1 个音频帧 中包含 2 2 2 个采样 , 左声道 1 1 1 个采样 , 右声道 1 1 1 个采样 , 每个采样是 2 2 2 字节的 short 类型 ;
上述 1 1 1 个音频帧的字节大小是 2 × 2 = 4 2\times 2 = 4 2×2=4 字节 ;
在 Oboe 播放器回调类 oboe::AudioStreamCallback 中 , 实现的 onAudioReady 方法 ,
其中的 int32_t numFrames 就是本次需要采样的帧数 ,
注意单位是音频帧 ,
这里的音频帧就是上面所说的
采样格式 是 oboe::AudioFormat::Float , 每个采样都是一个 float 单精度浮点数 , 4 4 4 字节 ,
声道数 是 oboe::ChannelCount::Stereo , 立体声 , 左右双声道 ,
对应的 1 1 1 个音频帧 中包含 2 2 2 个采样 , 左声道 1 1 1 个采样 , 右声道 1 1 1 个采样 , 每个采样是 4 4 4 字节的单精度浮点类型 float 类型 ;
上述 1 1 1 个音频帧的字节大小是 2 × 4 = 8 2\times 4 = 8 2×4=8 字节 ;
因此在该方法中的后续采样 , 每帧都要采集 2 2 2 个样本 , 每个样本 4 4 4 字节 , 每帧采集 8 8 8 字节的样本 ,
总共 numFrames 帧需要采集 numFrames 乘以 8 8 8 字节的音频采样 ;
在 onAudioReady 方法中 , 需要 采集 8 × 8 \times 8× numFrames 字节 的音频数据样本 , 并将数据拷贝到 void *audioData 指针指向的内存中 ;
// Oboe 音频流回调类
class MyCallback : public oboe::AudioStreamCallback {
public:
oboe::DataCallbackResult
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
// 需要生成 AudioFormat::Float 类型数据 , 该缓冲区类型也是该类型
// 生产者需要检查该格式
// oboe::AudioStream *audioStream 已经转换为适当的类型
// 获取音频数据缓冲区
auto *floatData = static_cast<float *>(audioData);
// 生成正弦波数据
for (int i = 0; i < numFrames; ++i) {
float sampleValue = kAmplitude * sinf(mPhase);
for (int j = 0; j < kChannelCount; j++) {
floatData[i * kChannelCount + j] = sampleValue;
}
mPhase += mPhaseIncrement;
if (mPhase >= kTwoPi) mPhase -= kTwoPi;
}
LOGI("回调 onAudioReady");
return oboe::DataCallbackResult::Continue;
}
};