PC音频控制最佳实践
1. API使用指导
系统提供了多样化的API,根据不同的开发语言以及不同的音频使用场景,主要介绍以下三种API:
- AudioRenderer:用于音频输出的ArkTS/JS API,仅支持PCM格式,需要应用持续写入音频数据进行工作。应用可以在输入前添加数据预处理,如设定音频文件的采样率、位宽等。
- OHAudio:用于音频输出的Native API,此API在设计上实现归一,同时支持普通音频通路和低时延通路。仅支持PCM格式,适用于依赖Native层实现音频输出功能的场景。
- AVPlayer:用于音频播放的ArkTS/JS API,集成了流媒体和本地资源解析、媒体资源解封装、音频解码和音频输出功能。可用于直接播放mp3、m4a等格式的音频文件,不支持直接播放PCM格式文件。
参考链接:音频播放开发概述。
2.音频控制及示例
2.1选择合适的音频流
音频流类型是定义音频数据播放和录制方式的关键属性。对于音频流,其类型由StreamUsage确定。
下表中列举常用的播放音频流类型,由StreamUsage定义:
音频流使用类型(StreamUsage) |
适用场景 |
---|---|
STREAM_USAGE_MOVIE |
适用于播放短视频、电影、电视剧等各类视频内容。 |
STREAM_USAGE_AUDIOBOOK |
适用于播放有声读物、新闻、播客等。 |
STREAM_USAGE_GAME |
适用于游戏内配乐、配音,游戏类型音频流播放时,后台播放的音乐不会被打断;游戏内语音,建议使用STREAM_USAGE_VOICE_COMMUNICATION。 |
STREAM_USAGE_NAVIGATION |
适用于导航场景的语音播报功能。 |
STREAM_USAGE_VOICE_MESSAGE |
适用于播放语音短消息。 |
STREAM_USAGE_VOICE_COMMUNICATION |
适用于VoIP语音通话。 |
STREAM_USAGE_ALARM |
适用于播放闹铃。 |
STREAM_USAGE_RINGTONE |
适用于VoIP来电响铃等。 |
STREAM_USAGE_NOTIFICATION |
适用于播放通知音、提示音。 |
参考链接:播放音频流类型。
2.1.1ArkTS侧应用适配, 使用AudioRenderer开发音频播放功能:
可以在调用createAudioRenderer获取音频渲染器时,传入对应的StreamUsage。
createAudioRenderer的参数options类型为AudioRendererOptions,包含AudioRendererInfo渲染器信息,使用AudioRendererInfo.usage可指定StreamUsage音频流类型。
2.1.2C侧应用适配,使用OHAudio开发音频播放功能:
可以在调用OH_AudioStreamBuilder_SetRendererInfo()接口时,传入对应的OH_AudioStream_Usage指定音频流类型。
2.1.3ArkTS侧应用,使用AVPlayer开发全量的音频播放功能:
可以通过设置AVPlayer的属性audioRendererInfo来实现。AVPlayer.audioRendererInfo的类型为audio.AudioRendererInfo。使用AudioRendererInfo.usage可指定StreamUsage音频流类型。
2.1.4C侧应用,使用AVPlayer开发全量的音频播放功能:
可以在调用OH_AVPlayer_SetAudioRendererInfo接口时,传入对应的OH_AudioStream_Usage指定音频流类型。
参考链接:设置音频流类型。
2.2音量控制
播放流类型(StreamUsage)决定了音频流所属的音量类型(AudioVolumeType),各类音量类型(如媒体、铃声、闹钟、通话等)拥有独立的音量值,在用户界面上可独立调节,相互之间不会影响。
常见的播放流类型与音量类型的对应关系为:
音频流使用类型(StreamUsage) |
音量类型(AudioVolumeType) |
---|---|
MUSIC、MOVIE、AUDIOBOOK、GAME |
媒体音量(MEDIA) |
RINGTONE、NOTIFICATION |
铃声音量(RINGTONE) |
VOICE_COMMUNICATION |
通话音量(VOICE_CALL) |
ALARM |
闹钟音量(ALARM) |
2.2.1应用获取音量大小
2.2.1.1TS侧应用获取当前音频流的音量
获取当前音量值,同步返回音量大小,音量范围[0.0-1.0],默认值为1.0,非系统音量。
import { BusinessError } from '@kit.BasicServicesKit';
try {
let value: number = audioRenderer.getVolume();
console.info(`Indicate that the volume is obtained ${value}.`);
} catch (err) {
let error = err as BusinessError;
console.error(`Failed to obtain the volume, error ${error}.`);
}
参考API:getVolume。
2.2.1.2C侧应用获取当前音量
获取当前音频流音量值,其中volume为指向一个获取当前音频流音量值的指针,音量值的范围是[0.0, 1.0]。
OH_AudioStream_Result OH_AudioRenderer_GetVolume(OH_AudioRenderer *renderer, float *volume)
参考API:OH_AudioRenderer_GetVolume()。
2.2.2应用调整应用音量大小
2.2.2.1使用AVPlayer设置音频流音量的示例代码如下:
let volume = 1.0; // 指定的音量大小,取值范围为[0.00-1.00],1表示最大音量 avPlayer.setVolume(volume);
2.2.2.2TS侧应用设置音频流音量的示例代码:
import { BusinessError } from '@kit.BasicServicesKit';
audioRenderer.setVolume(0.5).then(() => { // 音量范围为[0.0-1.0]。
console.info('Invoke setVolume succeeded.');
}).catch((err: BusinessError) => {
console.error(`Invoke setVolume failed, code is ${err.code}, message is ${err.message}`);
});
参考文档:音频流音量。
2.2.2.3C侧应用设置音频流音量的示例代码 :
OH_AudioStream_Result OH_AudioRenderer_SetVolume (OH_AudioRenderer *renderer, float volume)
参数:
renderer |
指向OH_AudioStreamBuilder_GenerateRenderer创建的音频流实例。 |
volume |
设置当前音频流音量,音量值的范围是[0.0, 1.0]。 |
返回:
参考API:OH_AudioRenderer_SetVolume()。
2.3音频焦点策略
系统预设了默认的音频焦点策略,根据音频流的类型及启动的先后顺序,对所有播放和录制音频流进行统一管理。
在启动播放或录制功能前,应用需要先申请音频焦点;而在播放或录制结束后,应适时释放音频焦点。在播放或录制的过程中,可能会因其他音频流的介入而失去焦点,此时,应用需要采取相应措施处理音频焦点变化。
- 在启动播放或录制操作前,应根据音频的具体用途,选择并使用合适的音频流类型,即准确设置StreamUsage或SourceType。
- 在播放或录制的过程中,需监听音频焦点事件,并在接收到音频焦点中断事件(InterruptEvent)时,采取相应的处理措施。
- 若应用程序有意主动管理音频焦点,可使用音频会话(AudioSession)相关的接口进行操作。
参考文档:音频焦点和音频会话介绍。
2.3.1TS侧应用处理音频焦点变化的指导
使用AudioRenderer开发音频播放功能,可以调用on('audioInterrupt')接口,监听音频焦点事件InterruptEvent。
audioRenderer.on('audioInterrupt', (interruptEvent: audio.InterruptEvent) => {
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_PAUSE: // 音频流已被暂停,临时失去焦点,应用更新UX界面状态
break;
case audio.InterruptHint.INTERRUPT_HINT_STOP: // 音频流已被停止,永久失去焦点,若想恢复渲染,需用户主动触发
break;
case audio.InterruptHint.INTERRUPT_HINT_RESUME: // 音频已经恢复,应用更新UX界面状态等
break;
case audio.InterruptHint.INTERRUPT_HINT_DUCK:
break;
case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
break;
}
}
参考API:on('audioInterrupt')。
2.3.2C侧应用处理音频焦点变化的指导
使用OHAudio开发音频播放功能(C/C++),可以调用OH_AudioStreamBuilder_SetRendererCallback接口,监听音频焦点事件OH_AudioRenderer_OnInterruptEvent。
int32_t MyOnInterruptEvent(OH_AudioRenderer* renderer, void* userData, OH_AudioInterrupt_ForceType type,OH_AudioInterrupt_Hint hint) {
switch(hint) {
case AUDIOSTREAM_INTERRUPT_HINT_RESUME: // 音频已经恢复,应用更新UX界面状态等
break;
case AUDIOSTREAM_INTERRUPT_HINT_PAUSE: // 音频流已被停止,永久失去焦点,若想恢复渲染,需用户主动触发
break;
case AUDIOSTREAM_INTERRUPT_HINT_STOP: // 音频流已被暂停,临时失去焦点,应用更新UX界面状态
break;
case AUDIOSTREAM_INTERRUPT_HINT_DUCK:
break;
case AUDIOSTREAM_INTERRUPT_HINT_UNDUCK:
break;
}
}
2.4音频设备变更
系统会根据音频流的类型,指定默认的输出设备,典型的场景如下:
- 音乐(Music)类型音频流的默认输出设备为扬声器。
- 语音通话(VoiceCommunication)类型音频流的默认输入设备为麦克风,默认输出设备为听筒。
- 闹铃(Alarm)类型音频流的默认输出设备为扬声器。若先连接蓝牙耳机,再开始播放Alarm音频,则扬声器和蓝牙耳机会同时播放。
2.4.1TS侧应用 处理音频设备变更的指导
2.4.1.1指定默认发声设备
import { BusinessError } from '@kit.BasicServicesKit';
// 可注明下本接口主要服务于语音消息和语音通话场景,允许在AudioRenderer创建以后的任何时间被调用。
// 未播放时调用,系统会记录应用设置的默认本机内置发声设备,当应用启动播放时从设置的默认本机内置发声设备发声。
// 正在播放时调用,在没有外接设备如蓝牙耳机/有线耳机,系统会立即切换到设置的默认本机内置发声设备发声;否则系统会先记录应用设置的默认本机内置发声设备,等外接设备移除后再切换到设置的默认本机内置发声设备发声。
audioRenderer.setDefaultOutputDevice(audio.DeviceType.SPEAKER).then(() => {
console.info('setDefaultOutputDevice Success!');
}).catch((err: BusinessError) => {
console.error(`setDefaultOutputDevice Fail: ${err}`);
});
参考API:setDefaultOutputDevice。
2.4.1.2处理音频设备变更
(audioRenderer as audio.AudioRenderer).on('outputDeviceChangeWithInfo',
async (deviceChangeInfo: audio.AudioStreamDeviceChangeInfo) => {
switch (deviceChangeInfo.changeReason) {
case audio.AudioStreamDeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE:
// 响应设备不可用事件,如果应用处于播放状态,应暂停播放,更新UX界面。
// await audioRenderer.pause();
break;
case audio.AudioStreamDeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE:
// 应用根据业务情况响应设备可用事件。
break;
case audio.AudioStreamDeviceChangeReason.REASON_OVERRODE:
// 应用根据业务情况响应设备强选事件。
break;
case audio.AudioStreamDeviceChangeReason.REASON_UNKNOWN:
// 应用根据业务情况响应未知原因事件。
break;
}
});
- REASON_NEW_DEVICE_AVAILABLE:新设备可用。
触发场景:普通蓝牙设备(耳机、眼镜、音箱、车机等)连接、支持佩戴检测的蓝牙设备(耳机、眼镜等)佩戴、有线设备(3.5mm耳机、Type-C耳机、USB耳机、USB音箱等)插入、分布式设备上线等。
- REASON_OLD_DEVICE_UNAVAILABLE:旧设备不可用。
触发场景:普通蓝牙设备(耳机、眼镜、音箱、车机等)断开、支持佩戴检测的蓝牙耳机双耳摘下、支持佩戴检测的蓝牙眼镜摘下、有线设备(3.5mm耳机、Type-C耳机、USB耳机、音箱等)拔出、分布式设备下线等。
- REASON_OVERRODE:用户强制选择设备。
触发场景:用户从界面选择切换音频流输出设备、从外设选择接听蜂窝或VoIP来电。
参考文档:音频流输出设备信息。
2.4.2C侧应用 处理音频设备变更的指导
2.4.2.1指定默认发声设备
OH_AudioRenderer_SetDefaultOutputDevice() 接口仅适用于音频流为语音消息、VoIP语音通话或者VoIP视频通话的场景使用,可选默认发声设备类型为听筒、扬声器和系统默认设备。
在应用启动播放时,若有外接设备如蓝牙耳机/有线耳机接入,系统优先从外接设备发声;否则系统遵循应用设置的默认本机内置发声设备发声。
OH_AudioStream_Result OH_AudioRenderer_SetDefaultOutputDevice(OH_AudioRenderer* renderer, OH_AudioDevice_Type deviceType)
2.4.2.2处理音频设备变更
OH_AudioStreamBuilder_SetRendererOutputDeviceChangeCallback() 设置输出音频流设备变更的回调。
//设置输出音频流设备变更的回调
OH_AudioStream_Result OH_AudioStreamBuilder_SetRendererOutputDeviceChangeCallback(OH_AudioStreamBuilder * builder, OH_AudioRenderer_OutputDeviceChangeCallback callback, void * userData)
//输出音频流设备变更的回调函数
//OH_AudioRenderer_OutputDeviceChangeCallback,流设备变更原因,枚举常量
void (*OH_AudioRenderer_OutputDeviceChangeCallback)(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_DeviceChangeReason reason) {
switch (reason) {
case REASON_OLD_DEVICE_UNAVAILABLE: // 旧设备不可用。当报告此原因时,应用程序应考虑暂停音频播放。
break;
case REASON_NEW_DEVICE_AVAILABLE: // 新设备可用。
break;
case REASON_OVERRODE: // 用户或系统强制选择切换。
break;
case REASON_UNKNOWN: //未知原因
break;
}
}
3.规范标准
焦点示例标准:
incomming当前来的音频流 |
|||||
inproc 预先的音频流 |
音频类型 |
VOICE_COMMUNICATION(VOIP通话) |
SYSTEM(锁屏、按键) |
MUSIC(音频) |
ALARM(闹钟) |
VOICE_COMMUNICATION |
pause inproc |
duck incoming |
stop inproc |
duck incoming |
|
SYSTEM |
play both |
play both |
play both |
play both |
|
MUSIC |
pause inproc |
play both |
play both |
pause inproc |
|
ALARM |
stop inproc |
play both |
duck incoming |
stop inproc |
4.适配文档
TS侧应用完整demo参考基于AudioRenderer的音频播控和多场景交互指导,在audio-interaction-master\entry\src\main\module.json5路径下,修改deviceTypes为2in1,即可在PC端运行demo。
4.1音频焦点控制代码示例及视频演示
private interruptCallback: (interruptEvent: audio.InterruptEvent) => void = (interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
this.updateIsPlay(false);
break;
case audio.InterruptHint.INTERRUPT_HINT_STOP:
this.updateIsPlay(false);
this.pause();
break;
case audio.InterruptHint.INTERRUPT_HINT_DUCK:
break;
case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
break;
default:
break;
}
} else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) {
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_RESUME:
this.start();
break;
default:
break;
}
}
}
下述视频展示了音频播放过程中闹钟响起时,暂停音乐播放;闹钟结束后,恢复音乐播放的场景:
4.2音频设备变更代码示例及视频演示
private outputDeviceChangeCallback: (deviceChangeInfo: audio.AudioStreamDeviceChangeInfo) => void = (deviceChangeInfo: audio.AudioStreamDeviceChangeInfo) => {
Logger.info(TAG, `DeviceInfo id: ${deviceChangeInfo.devices[0].id}`);
Logger.info(TAG, `DeviceInfo name: ${deviceChangeInfo.devices[0].name}`);
Logger.info(TAG, `DeviceInfo address: ${deviceChangeInfo.devices[0].address}`);
Logger.info(TAG, `Device change reason: ${deviceChangeInfo.changeReason}`);
if (deviceChangeInfo.changeReason === audio.AudioStreamDeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) {
Logger.info(TAG, `Device change reason: ${deviceChangeInfo.changeReason}`);
this.pause();
}
}
下述视频展示了音频播放过程中插入耳机,音乐自动切换播放;拔出耳机,音乐暂停播放的场景:
更多推荐
所有评论(0)