• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![AudioLoopback status change](figures/audioloopback-status-change.png)
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)