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 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)接口设置是否开启静音打断模式。默认不开启,此时由音频焦点策略管理并发音频流的执行顺序。开启后,被其他应用打断导致停止或暂停录制时会进入静音录制状态,在此状态下录制的音频没有声音。