AudioRenderer提供了一种比AVPlayer更灵活的方式,允许开发者在音频数据输入渲染器之前进行预处理,满足更复杂的音频播放需求。
一、AudioRenderer工作原理
AudioRenderer 是一个音频渲染器,用于播放PCM(脉冲编码调制)音频数据。它允许开发者在音频数据输入渲染器之前进行预处理,从而实现更灵活的音频播放功能,例如:
- 添加音频特效,例如混响、均衡器等。
- 对音频数据进行格式转换,例如从立体声转换为单声道。
- 对音频数据进行压缩或解压缩。
AudioRenderer有以下几种状态:
- prepared: 已创建实例,但尚未开始渲染。
- running: 正在渲染音频数据。
- paused: 已暂停渲染,但可以恢复播放。
- stopped: 已停止渲染,不能恢复播放。
- released: 已释放资源,不能再进行任何操作。
二、开发步骤及注意事项
使用AudioRenderer开发音频播放功能,需要以下步骤:
1、配置音频渲染参数并创建AudioRenderer实例
- 首先,需要使用
AudioRendererOptions
结构体配置音频渲染参数,包括:- streamInfo: 音频流信息,包括采样率、声道、采样格式、编码格式等。
- rendererInfo: 音频渲染器信息,包括媒体类型、音频流使用类型、渲染器标志等。
- 然后,使用
audio.createAudioRenderer()
方法创建AudioRenderer实例。
2、开始渲染音频
- 使用
start()
方法进入running
状态,开始渲染音频。
3、写入音频数据
- 使用
write()
方法向缓冲区持续写入音频数据,进行渲染播放。 - 在写入之前,可以进行音频数据的预处理,例如添加特效、格式转换等。
4、停止渲染
- 使用
stop()
方法停止渲染。
5、释放资源
- 使用
release()
方法销毁实例,释放所有占用的资源。
注意事项:
- 为了避免UI线程阻塞,大部分AudioRenderer调用都是异步的。你可以使用回调函数或者Promise来处理异步操作。
- 每个API都有特定的状态要求,开发者需要在调用状态转换的方法前进行状态检查,避免程序运行产生预期以外的结果。
- 订阅AudioRenderer的
stateChange
事件,以便在状态转换时及时做出响应。 - 了解
markReach
事件,用于在渲染的帧数达到特定值时触发回调。
三、示例代码
以下是一个使用AudioRenderer渲染音频文件的完整示例代码:
import audio from '@ohos.multimedia.audio';
import fs from '@ohos.file.fs';
const TAG = 'AudioRendererDemo';
export default class AudioRendererDemo {
private renderModel = undefined;
private audioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
channels: audio.AudioChannel.CHANNEL_2, // 通道
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
}
private audioRendererInfo = {
content: audio.ContentType.CONTENT_TYPE_MUSIC, // 媒体类型
usage: audio.StreamUsage.STREAM_USAGE_MEDIA, // 音频流使用类型
rendererFlags: 0 // 音频渲染器标志
}
private audioRendererOptions = {
streamInfo: this.audioStreamInfo,
rendererInfo: this.audioRendererInfo
}
// 初始化,创建实例,设置监听事件
init() {
audio.createAudioRenderer(this.audioRendererOptions, (err, renderer) => { // 创建AudioRenderer实例
if (!err) {
console.info(`${TAG}: creating AudioRenderer success`);
this.renderModel = renderer;
this.renderModel.on('stateChange', (state) => { // 设置监听事件,当转换到指定的状态时触发回调
if (state == 2) {
console.info('audio renderer state is: STATE_RUNNING');
}
});
this.renderModel.on('markReach', 1000, (position) => { // 订阅markReach事件,当渲染的帧数达到1000帧时触发回调
if (position == 1000) {
console.info('ON Triggered successfully');
}
});
} else {
console.info(`${TAG}: creating AudioRenderer failed, error: ${err.message}`);
}
});
}
// 开始一次音频渲染
async start() {
let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
if (stateGroup.indexOf(this.renderModel.state) === -1) { // 当且仅当状态为prepared、paused和stopped之一时才能启动渲染
console.error(TAG + 'start failed');
return;
}
await this.renderModel.start(); // 启动渲染
const bufferSize = await this.renderModel.getBufferSize();
let context = getContext(this);
let path = context.filesDir;
const filePath = path + '/test.wav'; // 使用沙箱路径获取文件,实际路径为/data/storage/el2/base/haps/entry/files/test.wav
let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
let stat = await fs.stat(filePath);
let buf = new ArrayBuffer(bufferSize);
let len = stat.size % bufferSize === 0 ? Math.floor(stat.size / bufferSize) : Math.floor(stat.size / bufferSize + 1);
for (let i = 0; i < len; i++) {
let options = {
offset: i * bufferSize,
length: bufferSize
};
let readsize = await fs.read(file.fd, buf, options);
// buf是要写入缓冲区的音频数据,在调用AudioRenderer.write()方法前可以进行音频数据的预处理,实现个性化的音频播放功能,AudioRenderer会读出写入缓冲区的音频数据进行渲染
let writeSize = await new Promise((resolve, reject) => {
this.renderModel.write(buf, (err, writeSize) => {
if (err) {
reject(err);
} else {
resolve(writeSize);
}
});
});
if (this.renderModel.state === audio.AudioState.STATE_RELEASED) { // 如果渲染器状态为released,停止渲染
fs.close(file);
await this.renderModel.stop();
}
if (this.renderModel.state === audio.AudioState.STATE_RUNNING) {
if (i === len - 1) { // 如果音频文件已经被读取完,停止渲染
fs.close(file);
await this.renderModel.stop();
}
}
}
}
// 暂停渲染
async pause() {
// 只有渲染器状态为running的时候才能暂停
if (this.renderModel.state !== audio.AudioState.STATE_RUNNING) {
console.info('Renderer is not running');
return;
}
await this.renderModel.pause(); // 暂停渲染
if (this.renderModel.state === audio.AudioState.STATE_PAUSED) {
console.info('Renderer is paused.');
} else {
console.error('Pausing renderer failed.');
}
}
// 停止渲染
async stop() {
// 只有渲染器状态为running或paused的时候才可以停止
if (this.renderModel.state !== audio.AudioState.STATE_RUNNING && this.renderModel.state !== audio.AudioState.STATE_PAUSED) {
console.info('Renderer is not running or paused.');
return;
}
await this.renderModel.stop(); // 停止渲染
if (this.renderModel.state === audio.AudioState.STATE_STOPPED) {
console.info('Renderer stopped.');
} else {
console.error('Stopping renderer failed.');
}
}
// 销毁实例,释放资源
async release() {
// 渲染器状态不是released状态,才能release
if (this.renderModel.state === audio.AudioState.STATE_RELEASED) {
console.info('Renderer already released');
return;
}
await this.renderModel.release(); // 释放资源
if (this.renderModel.state === audio.AudioState.STATE_RELEASED) {
console.info('Renderer released');
} else {
console.error('Renderer release failed.');
}
}
}
四、总结
AudioRenderer为开发者提供了更加灵活的音频播放控制,允许开发者在音频数据流入渲染器之前进行预处理,从而实现更加个性化的音频播放体验。开发者可以通过配置音频渲染参数、控制渲染状态以及对音频数据进行预处理,来满足各种音频播放需求。