1# 实现音频低时延耳返 2<!--Kit: Audio Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @songshenke--> 5<!--Designer: @caixuejiang; @hao-liangfei; @zhanganxiang--> 6<!--Tester: @Filger--> 7<!--Adviser: @zengyawen--> 8 9从API20开始支持音频低时延耳返。 10 11AudioLoopback是音频返听器,可将音频以更低时延的方式实时传输到耳机中,让用户可以实时听到自己或者其他的相关声音。 12 13常用于K歌类应用,将录制的人声和背景音乐实时传送到耳机中,使用户通过反馈即时进行调整,获得更好的使用体验。 14 15当启用音频返听时,系统会创建低时延渲染器与低时延采集器,实现低时延耳返功能。采集的音频直接通过内部路由返回到渲染器。对于渲染器,其音频焦点策略与[STREAM_USAGE_MUSIC](../../reference/apis-audio-kit/arkts-apis-audio-e.md#streamusage)相匹配。对于采集器,其音频焦点策略与[SOURCE_TYPE_MIC](../../reference/apis-audio-kit/arkts-apis-audio-e.md#sourcetype8)相匹配。 16 17输入\输出设备由系统自动选择。如果当前输入\输出不支持低时延,则音频返听无法启用。在运行过程中,如果音频焦点被另一个音频流抢占,输入\输出设备切换到不支持低时延的设备,系统会自动禁用音频返听。 18 19## 使用前提 20 21- 当前仅支持通过有线耳机实现低时延返听功能,音频由有线耳机进行采集并播放。 22 23- 低功耗渲染器和低时延渲染器在API version 20不能实现并发。若要启用渲染器,建议采用[STREAM_USAGE_UNKNOWN](../../reference/apis-audio-kit/arkts-apis-audio-e.md#streamusage);系统内决策采用[STREAM_USAGE_MUSIC](../../reference/apis-audio-kit/arkts-apis-audio-e.md#streamusage)创建普通渲染器。 24 25## 开发指导 26 27使用AudioLoopback音频返听涉及到[isAudioLoopbackSupported](../../reference/apis-audio-kit/arkts-apis-audio-AudioStreamManager.md#isaudioloopbacksupported20)返听能力查询、AudioLoopback实例创建、返听音量设置、返听状态监听与返听启用禁用等。本开发指导将以一次启用返听的过程为例,向开发者讲解如何使用AudioLoopback进行音频返听,建议搭配[AudioLoopback](../../reference/apis-audio-kit/arkts-apis-audio-AudioLoopback.md)的API说明阅读。 28 29下图展示了AudioLoopback的状态变化,在创建实例后,调用对应的方法可以进入指定的状态实现对应行为。 30 31需要注意的是在确定的状态执行不合适的方法可能导致AudioLoopback发生错误,建议开发者在调用状态转换的方法前进行状态检查,避免程序运行产生预期以外的结果。 32 33**AudioLoopback状态变化示意图** 34 35 36 37使用[on('statusChange')](../../reference/apis-audio-kit/arkts-apis-audio-AudioLoopback.md#onstatuschange20)方法可以监听AudioLoopback的状态变化,每个状态对应值与说明见[AudioLoopbackStatus](../../reference/apis-audio-kit/arkts-apis-audio-e.md#audioloopbackstatus20)。 38 39### 开发步骤及注意事项 40 411. 查询返听能力并创建AudioLoopback实例,音频返听模式可以查看[AudioLoopbackMode](../../reference/apis-audio-kit/arkts-apis-audio-e.md#audioloopbackmode20)。 42 43 > **说明:** 44 > 返听需要申请麦克风权限ohos.permission.MICROPHONE,申请方式参考:[向用户申请授权](../../security/AccessToken/request-user-authorization.md)。 45 46 ```ts 47 import { audio } from '@kit.AudioKit'; 48 import { BusinessError } from '@kit.BasicServicesKit'; 49 50 let mode: audio.AudioLoopbackMode = audio.AudioLoopbackMode.HARDWARE; 51 let audioLoopback: audio.AudioLoopback; 52 let isSupported = audio.getAudioManager().getStreamManager().isAudioLoopbackSupported(mode); 53 if (isSupported) { 54 audio.createAudioLoopback(mode).then((loopback) => { 55 audioLoopback = loopback; 56 console.info('Invoke createAudioLoopback succeeded.'); 57 }).catch((err: BusinessError) => { 58 console.error(`Invoke createAudioLoopback failed, code is ${err.code}, message is ${err.message}.`); 59 }); 60 } 61 ``` 62 632. 调用[getStatus](../../reference/apis-audio-kit/arkts-apis-audio-AudioLoopback.md#getstatus20)方法,查询当前返听状态。 64 65 > **注意:** 66 > 音频返听状态受音频焦点、低时延管控、采集与播放设备等因素影响。 67 68 ```ts 69 import { BusinessError } from '@kit.BasicServicesKit'; 70 71 audioLoopback.getStatus().then((status: audio.AudioLoopbackStatus) => { 72 console.info(`getStatus success, status is ${status}.`); 73 }).catch((err: BusinessError) => { 74 console.error(`getStatus failed, code is ${err.code}, message is ${err.message}.`); 75 }) 76 ``` 77 783. 调用[setVolume](../../reference/apis-audio-kit/arkts-apis-audio-AudioLoopback.md#setvolume20)方法,设置音频返听音量。 79 80 > **注意:** 81 > - 在启用返听前设置音量,音量将在启用返听成功后生效。 82 > - 在启用返听后设置音量,音量将立即生效。 83 > - 启用返听前未设置音量,启用返听时将采用默认音量0.5。 84 85 ```ts 86 import { BusinessError } from '@kit.BasicServicesKit'; 87 88 audioLoopback.setVolume(0.5).then(() => { 89 console.info('setVolume success.'); 90 }).catch((err: BusinessError) => { 91 console.error(`setVolume failed, code is ${err.code}, message is ${err.message}.`); 92 }); 93 ``` 94 954. 调用[enable](../../reference/apis-audio-kit/arkts-apis-audio-AudioLoopback.md#enable20)方法,启用或禁用音频返听功能。 96 97 ```ts 98 import { BusinessError } from '@kit.BasicServicesKit'; 99 100 audioLoopback.enable(true).then((isSuccess) => { 101 if (isSuccess) { 102 console.info('enable success.'); 103 } else { 104 console.info('enable failed.'); 105 } 106 }).catch((err: BusinessError) => { 107 console.error(`enable failed, code is ${err.code}, message is ${err.message}.`); 108 }); 109 110 audioLoopback.enable(false).then((isSuccess) => { 111 if (isSuccess) { 112 console.info('disable success.'); 113 } else { 114 console.info('disable failed.'); 115 } 116 }).catch((err: BusinessError) => { 117 console.error(`disable failed, code is ${err.code}, message is ${err.message}.`); 118 }); 119 ``` 120 121### 完整示例 122 123使用AudioLoopback启用音频低时延返听示例代码如下所示。 124 125```ts 126import { audio } from '@kit.AudioKit'; 127import { BusinessError } from '@kit.BasicServicesKit'; 128import { common } from '@kit.AbilityKit'; 129 130const TAG = 'AudioLoopbackDemo'; 131 132let mode: audio.AudioLoopbackMode = audio.AudioLoopbackMode.HARDWARE; 133let audioLoopback: audio.AudioLoopback | undefined = undefined; 134 135let statusChangeCallback = (status: audio.AudioLoopbackStatus) => { 136 if (status == audio.AudioLoopbackStatus.UNAVAILABLE_DEVICE) { 137 console.info('Audio loopback status is: UNAVAILABLE_DEVICE'); 138 } else if (status == audio.AudioLoopbackStatus.UNAVAILABLE_SCENE) { 139 console.info('Audio loopback status is: UNAVAILABLE_SCENE'); 140 } else if (status == audio.AudioLoopbackStatus.AVAILABLE_IDLE) { 141 console.info('Audio loopback status is: AVAILABLE_IDLE'); 142 } else if (status == audio.AudioLoopbackStatus.AVAILABLE_RUNNING) { 143 console.info('Audio loopback status is: AVAILABLE_RUNNING'); 144 } 145}; 146 147// 查询能力,创建实例。 148function init() { 149 let isSupported = audio.getAudioManager().getStreamManager().isAudioLoopbackSupported(mode); 150 if (isSupported) { 151 audio.createAudioLoopback(mode).then((loopback) => { 152 console.info('Invoke createAudioLoopback succeeded.'); 153 audioLoopback = loopback; 154 }).catch((err: BusinessError) => { 155 console.error(`Invoke createAudioLoopback failed, code is ${err.code}, message is ${err.message}.`); 156 }); 157 } else { 158 console.error('Audio loopback is unsupported.'); 159 } 160} 161 162// 设置音频返听音量。 163async function setVolume(volume: number) { 164 if (audioLoopback !== undefined) { 165 try { 166 await audioLoopback.setVolume(volume); 167 console.info(`Invoke setVolume ${volume} succeeded.`); 168 } catch (err) { 169 console.error(`Invoke setVolume failed, code is ${err.code}, message is ${err.message}.`); 170 } 171 } else { 172 console.error('Audio loopback not created.'); 173 } 174} 175 176// 设置监听事件,启用音频返听。 177async function enable() { 178 if (audioLoopback !== undefined) { 179 try { 180 let status = await audioLoopback.getStatus(); 181 if (status == audio.AudioLoopbackStatus.AVAILABLE_IDLE) { 182 // 注册监听。 183 audioLoopback.on('statusChange', statusChangeCallback); 184 // 启动返听。 185 let success = await audioLoopback.enable(true); 186 if (success) { 187 console.info('Invoke enable succeeded'); 188 } else { 189 status = await audioLoopback.getStatus(); 190 statusChangeCallback(status); 191 } 192 } else { 193 statusChangeCallback(status); 194 } 195 } catch (err) { 196 console.error(`Invoke enable failed, code is ${err.code}, message is ${err.message}.`); 197 } 198 } else { 199 console.error('Audio loopback not created.'); 200 } 201} 202 203// 禁用音频返听,关闭监听事件。 204async function disable() { 205 if (audioLoopback !== undefined) { 206 try { 207 let status = await audioLoopback.getStatus(); 208 if (status == audio.AudioLoopbackStatus.AVAILABLE_RUNNING) { 209 // 禁用返听。 210 let success = await audioLoopback.enable(false); 211 if (success) { 212 console.info('Invoke disable succeeded'); 213 // 关闭监听。 214 audioLoopback.off('statusChange', statusChangeCallback); 215 } else { 216 status = await audioLoopback.getStatus(); 217 statusChangeCallback(status); 218 } 219 } else { 220 statusChangeCallback(status); 221 } 222 } catch (err) { 223 console.error(`Invoke disable failed, code is ${err.code}, message is ${err.message}.`); 224 } 225 } else { 226 console.error('Audio loopback not created.'); 227 } 228} 229``` 230 231### 音频低时延返听示例 232可参考[使用AudioLoopback启用音频低时延返听的示例](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/Audio)。