• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using AudioCapturer for Audio Recording
2
3The AudioCapturer is used to record Pulse Code Modulation (PCM) audio data. It is suitable if you have extensive audio development experience and want to implement more flexible recording features.
4
5## Development Guidelines
6
7The full recording process involves creating an AudioCapturer instance, configuring audio recording parameters, starting and stopping recording, and releasing the instance. In this topic, you will learn how to use the AudioCapturer to record audio data. Before the development, you are advised to read [AudioCapturer](../../reference/apis-audio-kit/arkts-apis-audio-AudioCapturer.md) for the API reference.
8
9The figure below shows the state changes of the AudioCapturer. After an AudioCapturer instance is created, different APIs can be called to switch the AudioCapturer to different states and trigger the required behavior. If an API is called when the AudioCapturer is not in the given state, the system may throw an exception or generate other undefined behavior. Therefore, you are advised to check the AudioCapturer state before triggering state transition.
10
11**Figure 1** AudioCapturer state transition
12
13![AudioCapturer state change](figures/audiocapturer-status-change.png)
14
15You can call **on('stateChange')** to listen for state changes of the AudioCapturer. For details about each state, see [AudioState](../../reference/apis-audio-kit/arkts-apis-audio-e.md#audiostate8).
16
17### How to Develop
18
191. Set audio recording parameters and create an AudioCapturer instance. For details about the parameters, see [AudioCapturerOptions](../../reference/apis-audio-kit/arkts-apis-audio-i.md#audiocaptureroptions8).
20
21   > **NOTE**
22   >
23   > When the microphone audio source is set ([SourceType](../../reference/apis-audio-kit/arkts-apis-audio-e.md#sourcetype8) is set to **SOURCE_TYPE_MIC**, **SOURCE_TYPE_VOICE_RECOGNITION**, **SOURCE_TYPE_VOICE_COMMUNICATION**, or **SOURCE_TYPE_VOICE_MESSAGE**), the permission ohos.permission.MICROPHONE is required. For details about how to apply for the permission, see [Requesting User Authorization](../../security/AccessToken/request-user-authorization.md).
24
25   ```ts
26    import { audio } from '@kit.AudioKit';
27
28    let audioStreamInfo: audio.AudioStreamInfo = {
29      samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // Sampling rate.
30      channels: audio.AudioChannel.CHANNEL_2, // Channel.
31      sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // Sampling format.
32      encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // Encoding format.
33    };
34
35    let audioCapturerInfo: audio.AudioCapturerInfo = {
36      source: audio.SourceType.SOURCE_TYPE_MIC, // Audio source type: microphone. Set this parameter based on the service scenario.
37      capturerFlags: 0 // Flag indicating an AudioCapturer.
38    };
39
40    let audioCapturerOptions: audio.AudioCapturerOptions = {
41      streamInfo: audioStreamInfo,
42      capturerInfo: audioCapturerInfo
43    };
44
45    audio.createAudioCapturer(audioCapturerOptions, (err, data) => {
46      if (err) {
47        console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`);
48      } else {
49        console.info('Invoke createAudioCapturer succeeded.');
50        let audioCapturer = data;
51      }
52    });
53   ```
54
552. Call **on('readData')** to subscribe to the audio data read callback.
56    > **NOTE**
57    > - **Thread management**: You are advised not to use multiple threads for data reading. If multithreading is necessary for data reading, ensure proper thread management.
58    > - **Thread performance**: Do not execute time-consuming tasks in the thread where the **readData** API resides. Failing to do so may delay the data processing thread's response to callbacks, potentially causing issues like missing audio data, lag, and noise.
59
60   ```ts
61    import { BusinessError } from '@kit.BasicServicesKit';
62    import { fileIo as fs } from '@kit.CoreFileKit';
63    import { common } from '@kit.AbilityKit';
64
65    class Options {
66      offset?: number;
67      length?: number;
68    }
69
70    let bufferSize: number = 0;
71    // Obtain the context from the component and ensure that the return value of this.getUIContext().getHostContext() is UIAbilityContext.
72    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
73    let path = context.cacheDir;
74    let filePath = path + '/StarWars10s-2C-48000-4SW.pcm';
75    let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
76    let readDataCallback = (buffer: ArrayBuffer) => {
77      let options: Options = {
78        offset: bufferSize,
79        length: buffer.byteLength
80      }
81      fs.writeSync(file.fd, buffer, options);
82      bufferSize += buffer.byteLength;
83    };
84
85    audioCapturer.on('readData', readDataCallback);
86   ```
87
883. Call **start()** to switch the AudioCapturer to the **running** state and start recording.
89
90   ```ts
91    import { BusinessError } from '@kit.BasicServicesKit';
92
93    audioCapturer.start((err: BusinessError) => {
94      if (err) {
95        console.error(`Capturer start failed, code is ${err.code}, message is ${err.message}`);
96      } else {
97        console.info('Capturer start success.');
98      }
99    });
100   ```
101
1024. Call **stop()** to stop recording.
103
104   ```ts
105    import { BusinessError } from '@kit.BasicServicesKit';
106
107    audioCapturer.stop((err: BusinessError) => {
108      if (err) {
109        console.error(`Capturer stop failed, code is ${err.code}, message is ${err.message}`);
110      } else {
111        console.info('Capturer stopped.');
112      }
113    });
114   ```
115
1165. Call **release()** to release the instance.
117
118   ```ts
119    import { BusinessError } from '@kit.BasicServicesKit';
120
121    audioCapturer.release((err: BusinessError) => {
122      if (err) {
123        console.error(`capturer release failed, code is ${err.code}, message is ${err.message}`);
124      } else {
125        console.info('capturer released.');
126      }
127    });
128   ```
129
130### Sample Code
131
132Refer to the sample code below to record audio using AudioCapturer.
133
134```ts
135import { audio } from '@kit.AudioKit';
136import { BusinessError } from '@kit.BasicServicesKit';
137import { fileIo as fs } from '@kit.CoreFileKit';
138import { common } from '@kit.AbilityKit';
139
140const TAG = 'AudioCapturerDemo';
141
142class Options {
143  offset?: number;
144  length?: number;
145}
146
147let bufferSize: number = 0;
148let audioCapturer: audio.AudioCapturer | undefined = undefined;
149let audioStreamInfo: audio.AudioStreamInfo = {
150  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // Sampling rate.
151  channels: audio.AudioChannel.CHANNEL_2, // Channel.
152  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // Sampling format.
153  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // Encoding format.
154};
155let audioCapturerInfo: audio.AudioCapturerInfo = {
156  source: audio.SourceType.SOURCE_TYPE_MIC, // Audio source type: microphone. Set this parameter based on the service scenario.
157  capturerFlags: 0 // Flag indicating an AudioCapturer.
158};
159let audioCapturerOptions: audio.AudioCapturerOptions = {
160  streamInfo: audioStreamInfo,
161  capturerInfo: audioCapturerInfo
162};
163// Obtain the context from the component and ensure that the return value of this.getUIContext().getHostContext() is UIAbilityContext.
164let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
165let path = context.cacheDir;
166let filePath = path + '/StarWars10s-2C-48000-4SW.pcm';
167let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
168let readDataCallback = (buffer: ArrayBuffer) => {
169   let options: Options = {
170      offset: bufferSize,
171      length: buffer.byteLength
172   }
173   fs.writeSync(file.fd, buffer, options);
174   bufferSize += buffer.byteLength;
175};
176
177// Create an AudioCapturer instance, and set the events to listen for.
178function init() {
179  audio.createAudioCapturer(audioCapturerOptions, (err, capturer) => { // Create an AudioCapturer instance.
180    if (err) {
181      console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`);
182      return;
183    }
184    console.info(`${TAG}: create AudioCapturer success`);
185    audioCapturer = capturer;
186    if (audioCapturer !== undefined) {
187       (audioCapturer as audio.AudioCapturer).on('readData', readDataCallback);
188    }
189  });
190}
191
192// Start audio recording.
193function start() {
194  if (audioCapturer !== undefined) {
195    let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
196    if (stateGroup.indexOf((audioCapturer as audio.AudioCapturer).state.valueOf()) === -1) { // Recording can be started only when the AudioCapturer is in the STATE_PREPARED, STATE_PAUSED, or STATE_STOPPED state.
197      console.error(`${TAG}: start failed`);
198      return;
199    }
200
201    // Start recording.
202    (audioCapturer as audio.AudioCapturer).start((err: BusinessError) => {
203      if (err) {
204        console.error('Capturer start failed.');
205      } else {
206        console.info('Capturer start success.');
207      }
208    });
209  }
210}
211
212// Stop recording.
213function stop() {
214  if (audioCapturer !== undefined) {
215    // The AudioCapturer can be stopped only when it is in the STATE_RUNNING or STATE_PAUSED state.
216    if ((audioCapturer as audio.AudioCapturer).state.valueOf() !== audio.AudioState.STATE_RUNNING && (audioCapturer as audio.AudioCapturer).state.valueOf() !== audio.AudioState.STATE_PAUSED) {
217      console.info('Capturer is not running or paused');
218      return;
219    }
220
221    // Stop recording.
222    (audioCapturer as audio.AudioCapturer).stop((err: BusinessError) => {
223      if (err) {
224        console.error('Capturer stop failed.');
225      } else {
226        fs.close(file);
227        console.info('Capturer stop success.');
228      }
229    });
230  }
231}
232
233// Release the instance.
234function release() {
235  if (audioCapturer !== undefined) {
236    // The AudioCapturer can be released only when it is not in the STATE_RELEASED or STATE_NEW state.
237    if ((audioCapturer as audio.AudioCapturer).state.valueOf() === audio.AudioState.STATE_RELEASED || (audioCapturer as audio.AudioCapturer).state.valueOf() === audio.AudioState.STATE_NEW) {
238      console.info('Capturer already released');
239      return;
240    }
241
242    // Release the resources.
243    (audioCapturer as audio.AudioCapturer).release((err: BusinessError) => {
244      if (err) {
245        console.error('Capturer release failed.');
246      } else {
247        console.info('Capturer release success.');
248      }
249    });
250  }
251}
252```
253
254### Setting the Mute Interruption Mode
255
256To ensure that the recording is not interrupted by the system's focus concurrency rules, a feature is introduced to change the interruption strategy from stopping the recording to simply muting it. You can control this behavior by calling [setWillMuteWhenInterrupted](../../reference/apis-audio-kit/arkts-apis-audio-AudioCapturer.md#setwillmutewheninterrupted20) when creating an AudioCapturer instance. By default, this mode is disabled, and the audio focus strategy manages the order of concurrent audio streams. When enabled, if the recording is interrupted by another application, it will go into a muted state instead of stopping or pausing. In this state, the audio captured is silent.
257