• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用AudioCapturer开发音频录制功能
2<!--Kit: Audio Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @songshenke-->
5<!--Designer: @caixuejiang; @hao-liangfei; @zhanganxiang-->
6<!--Tester: @Filger-->
7<!--Adviser: @zengyawen-->
8
9AudioCapturer是音频采集器,用于录制PCM(Pulse Code Modulation)音频数据,适合有音频开发经验的开发者实现更灵活的录制功能。
10
11## 开发指导
12
13使用AudioCapturer录制音频涉及到AudioCapturer实例的创建、音频采集参数的配置、采集的开始与停止、资源的释放等。本开发指导将以一次录制音频数据的过程为例,向开发者讲解如何使用AudioCapturer进行音频录制,建议搭配[AudioCapturer的API说明](../../reference/apis-audio-kit/arkts-apis-audio-AudioCapturer.md)阅读。
14
15下图展示了AudioCapturer的状态变化,在创建实例后,调用对应的方法可以进入指定的状态实现对应的行为。需要注意的是在确定的状态执行不合适的方法可能导致AudioCapturer发生错误,建议开发者在调用状态转换的方法前进行状态检查,避免程序运行产生预期以外的结果。
16
17**图1** AudioCapturer状态变化示意图
18
19![AudioCapturer status change](figures/audiocapturer-status-change.png)
20
21使用on('stateChange')方法可以监听AudioCapturer的状态变化,每个状态对应值与说明见[AudioState](../../reference/apis-audio-kit/arkts-apis-audio-e.md#audiostate8)。
22
23### 开发步骤及注意事项
24
251. 配置音频采集参数并创建AudioCapturer实例,音频采集参数的详细信息可以查看[AudioCapturerOptions](../../reference/apis-audio-kit/arkts-apis-audio-i.md#audiocaptureroptions8)。
26
27   > **说明:**
28   > 当设置Mic音频源(即[SourceType](../../reference/apis-audio-kit/arkts-apis-audio-e.md#sourcetype8)为SOURCE_TYPE_MIC、SOURCE_TYPE_VOICE_RECOGNITION、SOURCE_TYPE_VOICE_COMMUNICATION、SOURCE_TYPE_VOICE_MESSAGE)时,需要申请麦克风权限ohos.permission.MICROPHONE,申请方式参考:[向用户申请授权](../../security/AccessToken/request-user-authorization.md)。
29
30   ```ts
31    import { audio } from '@kit.AudioKit';
32
33    let audioStreamInfo: audio.AudioStreamInfo = {
34      samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率。
35      channels: audio.AudioChannel.CHANNEL_2, // 通道。
36      sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式。
37      encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式。
38    };
39
40    let audioCapturerInfo: audio.AudioCapturerInfo = {
41      source: audio.SourceType.SOURCE_TYPE_MIC, // 音源类型:Mic音频源。根据业务场景配置,参考SourceType。
42      capturerFlags: 0 // 音频采集器标志。
43    };
44
45    let audioCapturerOptions: audio.AudioCapturerOptions = {
46      streamInfo: audioStreamInfo,
47      capturerInfo: audioCapturerInfo
48    };
49
50    audio.createAudioCapturer(audioCapturerOptions, (err, data) => {
51      if (err) {
52        console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`);
53      } else {
54        console.info('Invoke createAudioCapturer succeeded.');
55        let audioCapturer = data;
56      }
57    });
58   ```
59
602. 调用on('readData')方法,订阅监听音频数据读入回调。
61    > **注意:**
62    > - **线程管理**:不建议使用多线程来处理数据读取。若需使用多线程读取数据,需要做好线程管理。
63    > - **线程耗时**:`readData` 方法所在的线程中,不建议执行耗时任务。否则可能会导致数据处理线程响应回调延迟,进而引发录音数据缺失、卡顿、杂音等音频效果问题。
64
65   ```ts
66    import { BusinessError } from '@kit.BasicServicesKit';
67    import { fileIo as fs } from '@kit.CoreFileKit';
68    import { common } from '@kit.AbilityKit';
69
70    class Options {
71      offset?: number;
72      length?: number;
73    }
74
75    let bufferSize: number = 0;
76    // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext。
77    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
78    let path = context.cacheDir;
79    let filePath = path + '/StarWars10s-2C-48000-4SW.pcm';
80    let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
81    let readDataCallback = (buffer: ArrayBuffer) => {
82      let options: Options = {
83        offset: bufferSize,
84        length: buffer.byteLength
85      }
86      fs.writeSync(file.fd, buffer, options);
87      bufferSize += buffer.byteLength;
88    };
89
90    audioCapturer.on('readData', readDataCallback);
91   ```
92
933. 调用start()方法进入running状态,开始录制音频。
94
95   ```ts
96    import { BusinessError } from '@kit.BasicServicesKit';
97
98    audioCapturer.start((err: BusinessError) => {
99      if (err) {
100        console.error(`Capturer start failed, code is ${err.code}, message is ${err.message}`);
101      } else {
102        console.info('Capturer start success.');
103      }
104    });
105   ```
106
1074. 调用stop()方法停止录制。
108
109   ```ts
110    import { BusinessError } from '@kit.BasicServicesKit';
111
112    audioCapturer.stop((err: BusinessError) => {
113      if (err) {
114        console.error(`Capturer stop failed, code is ${err.code}, message is ${err.message}`);
115      } else {
116        console.info('Capturer stopped.');
117      }
118    });
119   ```
120
1215. 调用release()方法销毁实例,释放资源。
122
123   ```ts
124    import { BusinessError } from '@kit.BasicServicesKit';
125
126    audioCapturer.release((err: BusinessError) => {
127      if (err) {
128        console.error(`capturer release failed, code is ${err.code}, message is ${err.message}`);
129      } else {
130        console.info('capturer released.');
131      }
132    });
133   ```
134
135### 完整示例
136
137下面展示了使用AudioCapturer录制音频的完整示例代码。
138
139```ts
140import { audio } from '@kit.AudioKit';
141import { BusinessError } from '@kit.BasicServicesKit';
142import { fileIo as fs } from '@kit.CoreFileKit';
143import { common } from '@kit.AbilityKit';
144
145const TAG = 'AudioCapturerDemo';
146
147class Options {
148  offset?: number;
149  length?: number;
150}
151
152let bufferSize: number = 0;
153let audioCapturer: audio.AudioCapturer | undefined = undefined;
154let audioStreamInfo: audio.AudioStreamInfo = {
155  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率。
156  channels: audio.AudioChannel.CHANNEL_2, // 通道。
157  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式。
158  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式。
159};
160let audioCapturerInfo: audio.AudioCapturerInfo = {
161  source: audio.SourceType.SOURCE_TYPE_MIC, // 音源类型:Mic音频源。根据业务场景配置,参考SourceType。
162  capturerFlags: 0 // 音频采集器标志。
163};
164let audioCapturerOptions: audio.AudioCapturerOptions = {
165  streamInfo: audioStreamInfo,
166  capturerInfo: audioCapturerInfo
167};
168let file: fs.File;
169let readDataCallback: Callback<ArrayBuffer>;
170
171async function initArguments(context: common.UIAbilityContext) {
172  let path = context.cacheDir;
173  // 确保该沙箱路径下存在该资源。
174  let filePath = path + '/StarWars10s-2C-48000-4SW.pcm';
175  file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
176  readDataCallback = (buffer: ArrayBuffer) => {
177    let options: Options = {
178      offset: bufferSize,
179      length: buffer.byteLength
180    }
181    fs.writeSync(file.fd, buffer, options);
182    bufferSize += buffer.byteLength;
183  };
184}
185
186// 初始化,创建实例,设置监听事件。
187async function init() {
188  audio.createAudioCapturer(audioCapturerOptions, (err, capturer) => { // 创建AudioCapturer实例。
189    if (err) {
190      console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`);
191      return;
192    }
193    console.info(`${TAG}: create AudioCapturer success`);
194    audioCapturer = capturer;
195    if (audioCapturer !== undefined) {
196      audioCapturer.on('readData', readDataCallback);
197    }
198  });
199}
200
201// 开始一次音频采集。
202async function start() {
203  if (audioCapturer !== undefined) {
204    let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
205    if (stateGroup.indexOf(audioCapturer.state.valueOf()) === -1) { // 当且仅当状态为STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一时才能启动采集。
206      console.error(`${TAG}: start failed`);
207      return;
208    }
209
210    // 启动采集。
211    audioCapturer.start((err: BusinessError) => {
212      if (err) {
213        console.error('Capturer start failed.');
214      } else {
215        console.info('Capturer start success.');
216      }
217    });
218  }
219}
220
221// 停止采集。
222async function stop() {
223  if (audioCapturer !== undefined) {
224    // 只有采集器状态为STATE_RUNNING或STATE_PAUSED的时候才可以停止。
225    if (audioCapturer.state.valueOf() !== audio.AudioState.STATE_RUNNING && audioCapturer.state.valueOf() !== audio.AudioState.STATE_PAUSED) {
226      console.info('Capturer is not running or paused');
227      return;
228    }
229
230    // 停止采集。
231    audioCapturer.stop((err: BusinessError) => {
232      if (err) {
233        console.error('Capturer stop failed.');
234      } else {
235        fs.close(file);
236        console.info('Capturer stop success.');
237      }
238    });
239  }
240}
241
242// 销毁实例,释放资源。
243async function release() {
244  if (audioCapturer !== undefined) {
245    // 采集器状态不是STATE_RELEASED或STATE_NEW状态,才能release。
246    if (audioCapturer.state.valueOf() === audio.AudioState.STATE_RELEASED || audioCapturer.state.valueOf() === audio.AudioState.STATE_NEW) {
247      console.info('Capturer already released');
248      return;
249    }
250
251    // 释放资源。
252    audioCapturer.release((err: BusinessError) => {
253      if (err) {
254        console.error('Capturer release failed.');
255      } else {
256        console.info('Capturer release success.');
257      }
258    });
259  }
260}
261
262@Entry
263@Component
264struct Index {
265  build() {
266    Scroll() {
267      Column() {
268        Row() {
269          Column() {
270            Text('初始化').fontColor(Color.Black).fontSize(16).margin({ top: 12 });
271          }
272          .backgroundColor(Color.White)
273          .borderRadius(30)
274          .width('45%')
275          .height('25%')
276          .margin({ right: 12, bottom: 12 })
277          .onClick(async () => {
278            let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
279            initArguments(context);
280            init();
281          });
282
283          Column() {
284            Text('开始录制').fontColor(Color.Black).fontSize(16).margin({ top: 12 });
285          }
286          .backgroundColor(Color.White)
287          .borderRadius(30)
288          .width('45%')
289          .height('25%')
290          .margin({ bottom: 12 })
291          .onClick(async () => {
292            start();
293          });
294        }
295
296        Row() {
297          Column() {
298            Text('停止录制').fontSize(16).margin({ top: 12 });
299          }
300          .id('audio_effect_manager_card')
301          .backgroundColor(Color.White)
302          .borderRadius(30)
303          .width('45%')
304          .height('25%')
305          .margin({ right: 12, bottom: 12 })
306          .onClick(async () => {
307            stop();
308          });
309
310          Column() {
311            Text('释放资源').fontColor(Color.Black).fontSize(16).margin({ top: 12 });
312          }
313          .backgroundColor(Color.White)
314          .borderRadius(30)
315          .width('45%')
316          .height('25%')
317          .margin({ bottom: 12 })
318          .onClick(async () => {
319            release();
320          });
321        }
322        .padding(12)
323      }
324      .height('100%')
325      .width('100%')
326      .backgroundColor('#F1F3F5');
327    }
328  }
329}
330```
331
332### 设置静音打断模式
333
334如果需要实现录音全程不被系统基于焦点并发规则打断的效果,提供将打断策略从停止录音切换为静音录制的功能,录音过程中也不影响其他应用启动录音。开发者在创建AudioCapturer实例时,调用[setWillMuteWhenInterrupted](../../reference/apis-audio-kit/arkts-apis-audio-AudioCapturer.md#setwillmutewheninterrupted20)接口设置是否开启静音打断模式。默认不开启,此时由音频焦点策略管理并发音频流的执行顺序。开启后,被其他应用打断导致停止或暂停录制时会进入静音录制状态,在此状态下录制的音频没有声音。