1# 音频采集开发指导 2 3## 简介 4 5AudioCapturer提供了用于获取原始音频文件的方法。开发者可以通过本指导了解应用如何通过AudioCapturer接口的调用实现音频数据的采集。 6 7- **状态检查**:在进行应用开发的过程中,建议开发者通过on('stateChange')方法订阅AudioCapturer的状态变更。因为针对AudioCapturer的某些操作,仅在音频采集器在固定状态时才能执行。如果应用在音频采集器处于错误状态时执行操作,系统可能会抛出异常或生成其他未定义的行为。 8 9## 运作机制 10 11该模块提供了音频采集模块的状态变化示意图。 12 13**图1** 音频采集状态变化示意图 14 15 16 17**PREPARED状态:** 通过调用create()方法进入到该状态。<br> 18**RUNNING状态:** 正在进行音频数据播放,可以在prepared状态通过调用start()方法进入此状态,也可以在stopped状态通过调用start()方法进入此状态。<br> 19**STOPPED状态:** 在running状态可以通过stop()方法停止音频数据的播放。<br> 20**RELEASED状态:** 在prepared和stop状态,用户均可通过release()方法释放掉所有占用的硬件和软件资源,并且不会再进入到其他的任何一种状态了。<br> 21 22## 约束与限制 23 24开发者在进行音频数据采集功能开发前,需要先对所开发的应用配置麦克风权限(ohos.permission.MICROPHONE),配置方式请参见[访问控制授权申请](../security/accesstoken-guidelines.md#配置文件权限声明)。 25 26## 开发指导 27 28详细API含义可参考:[音频管理API文档AudioCapturer](../reference/apis/js-apis-audio.md#audiocapturer8) 29 301. 使用createAudioCapturer()创建一个全局的AudioCapturer实例。 31 32 在audioCapturerOptions中设置音频采集器的相关参数。该实例可用于音频采集、控制和获取采集状态,以及注册通知回调。 33 34 ```js 35 import audio from '@ohos.multimedia.audio'; 36 import fs from '@ohos.file.fs'; //便于步骤3 read函数调用 37 38 //音频渲染相关接口自测试 39 @Entry 40 @Component 41 struct AudioRenderer { 42 @State message: string = 'Hello World' 43 private audioCapturer : audio.AudioCapturer; //供全局调用 44 45 async initAudioCapturer(){ 46 let audioStreamInfo = { 47 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 48 channels: audio.AudioChannel.CHANNEL_1, 49 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 50 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 51 } 52 53 let audioCapturerInfo = { 54 source: audio.SourceType.SOURCE_TYPE_MIC, 55 capturerFlags: 0 // 0是音频采集器的扩展标志位,默认为0 56 } 57 58 let audioCapturerOptions = { 59 streamInfo: audioStreamInfo, 60 capturerInfo: audioCapturerInfo 61 } 62 63 this.audioCapturer = await audio.createAudioCapturer(audioCapturerOptions); 64 console.log('AudioRecLog: Create audio capturer success.'); 65 } 66 ``` 67 682. 调用start()方法来启动/恢复采集任务。 69 70 启动完成后,采集器状态将变更为STATE_RUNNING,然后应用可以开始读取缓冲区。 71 72 ```js 73 async startCapturer() { 74 let state = this.audioCapturer.state; 75 // Capturer start时的状态应该是STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一. 76 if (state == audio.AudioState.STATE_PREPARED || state == audio.AudioState.STATE_PAUSED || 77 state == audio.AudioState.STATE_STOPPED) { 78 await this.audioCapturer.start(); 79 state = this.audioCapturer.state; 80 if (state == audio.AudioState.STATE_RUNNING) { 81 console.info('AudioRecLog: Capturer started'); 82 } else { 83 console.error('AudioRecLog: Capturer start failed'); 84 } 85 } 86 } 87 ``` 88 893. 读取采集器的音频数据并将其转换为字节流。重复调用read()方法读取数据,直到应用准备停止采集。 90 91 参考以下示例,将采集到的数据写入文件。 92 93 ```js 94 async readData(){ 95 let state = this.audioCapturer.state; 96 // 只有状态为STATE_RUNNING的时候才可以read. 97 if (state != audio.AudioState.STATE_RUNNING) { 98 console.info('Capturer is not in a correct state to read'); 99 return; 100 } 101 const path = '/data/data/.pulse_dir/capture_js.wav'; // 采集到的音频文件存储路径 102 let file = fs.openSync(path, 0o2); 103 let fd = file.fd; 104 if (file !== null) { 105 console.info('AudioRecLog: file created'); 106 } else { 107 console.info('AudioRecLog: file create : FAILED'); 108 return; 109 } 110 if (fd !== null) { 111 console.info('AudioRecLog: file fd opened in append mode'); 112 } 113 let numBuffersToCapture = 150; // 循环写入150次 114 let count = 0; 115 while (numBuffersToCapture) { 116 this.bufferSize = await this.audioCapturer.getBufferSize(); 117 let buffer = await this.audioCapturer.read(this.bufferSize, true); 118 let options = { 119 offset: count * this.bufferSize, 120 length: this.bufferSize 121 } 122 if (typeof(buffer) == undefined) { 123 console.info('AudioRecLog: read buffer failed'); 124 } else { 125 let number = fs.writeSync(fd, buffer, options); 126 console.info(`AudioRecLog: data written: ${number}`); 127 } 128 numBuffersToCapture--; 129 count++; 130 } 131 } 132 ``` 133 1344. 采集完成后,调用stop方法,停止录制。 135 136 ```js 137 async StopCapturer() { 138 let state = this.audioCapturer.state; 139 // 只有采集器状态为STATE_RUNNING或STATE_PAUSED的时候才可以停止 140 if (state != audio.AudioState.STATE_RUNNING && state != audio.AudioState.STATE_PAUSED) { 141 console.info('AudioRecLog: Capturer is not running or paused'); 142 return; 143 } 144 145 await this.audioCapturer.stop(); 146 147 state = this.audioCapturer.state; 148 if (state == audio.AudioState.STATE_STOPPED) { 149 console.info('AudioRecLog: Capturer stopped'); 150 } else { 151 console.error('AudioRecLog: Capturer stop failed'); 152 } 153 } 154 ``` 155 1565. 任务结束,调用release()方法释放相关资源。 157 158 ```js 159 async releaseCapturer() { 160 let state = this.audioCapturer.state; 161 // 采集器状态不是STATE_RELEASED或STATE_NEW状态,才能release 162 if (state == audio.AudioState.STATE_RELEASED || state == audio.AudioState.STATE_NEW) { 163 console.info('AudioRecLog: Capturer already released'); 164 return; 165 } 166 167 await this.audioCapturer.release(); 168 169 state = this.audioCapturer.state; 170 if (state == audio.AudioState.STATE_RELEASED) { 171 console.info('AudioRecLog: Capturer released'); 172 } else { 173 console.info('AudioRecLog: Capturer release failed'); 174 } 175 } 176 ``` 177 1786. (可选)获取采集器相关信息 179 180 通过以下代码,可以获取采集器的相关信息。 181 182 ```js 183 async getAudioCapturerInfo(){ 184 // 获取当前采集器状态 185 let state = this.audioCapturer.state; 186 // 获取采集器信息 187 let audioCapturerInfo : audio.AudioCapturerInfo = await this.audioCapturer.getCapturerInfo(); 188 // 获取音频流信息 189 let audioStreamInfo : audio.AudioStreamInfo = await this.audioCapturer.getStreamInfo(); 190 // 获取音频流ID 191 let audioStreamId : number = await this.audioCapturer.getAudioStreamId(); 192 // 获取纳秒形式的Unix时间戳 193 let audioTime : number = await this.audioCapturer.getAudioTime(); 194 // 获取合理的最小缓冲区大小 195 let bufferSize : number = await this.audioCapturer.getBufferSize(); 196 } 197 ``` 198 1997. (可选)使用on('markReach')方法订阅采集器标记到达事件,使用off('markReach')取消订阅事件。 200 201 注册markReach监听后,当采集器采集的帧数到达设定值时,会触发回调并返回设定的值。 202 203 ```js 204 async markReach(){ 205 this.audioCapturer.on('markReach', 10, (reachNumber) => { 206 console.info('Mark reach event Received'); 207 console.info(`The Capturer reached frame: ${reachNumber}`); 208 }); 209 this.audioCapturer.off('markReach'); // 取消markReach事件的订阅,后续将无法监听到“标记到达”事件 210 } 211 ``` 212 2138. (可选)使用on('periodReach')方法订阅采集器区间标记到达事件,使用off('periodReach')取消订阅事件。 214 215 注册periodReach监听后,**每当**采集器采集的帧数到达设定值时,会触发回调并返回设定的值。 216 217 ```js 218 async periodReach(){ 219 this.audioCapturer.on('periodReach', 10, (reachNumber) => { 220 console.info('Period reach event Received'); 221 console.info(`In this period, the Capturer reached frame: ${reachNumber}`); 222 }); 223 this.audioCapturer.off('periodReach'); // 取消periodReach事件的订阅,后续将无法监听到“区间标记到达”事件 224 } 225 ``` 226 2279. 如果应用需要在采集器状态更新时进行一些操作,可以订阅该事件,当采集器状态更新时,会受到一个包含有事件类型的回调。 228 229 ```js 230 async stateChange(){ 231 this.audioCapturer.on('stateChange', (state) => { 232 console.info(`AudioCapturerLog: Changed State to : ${state}`) 233 switch (state) { 234 case audio.AudioState.STATE_PREPARED: 235 console.info('--------CHANGE IN AUDIO STATE----------PREPARED--------------'); 236 console.info('Audio State is : Prepared'); 237 break; 238 case audio.AudioState.STATE_RUNNING: 239 console.info('--------CHANGE IN AUDIO STATE----------RUNNING--------------'); 240 console.info('Audio State is : Running'); 241 break; 242 case audio.AudioState.STATE_STOPPED: 243 console.info('--------CHANGE IN AUDIO STATE----------STOPPED--------------'); 244 console.info('Audio State is : stopped'); 245 break; 246 case audio.AudioState.STATE_RELEASED: 247 console.info('--------CHANGE IN AUDIO STATE----------RELEASED--------------'); 248 console.info('Audio State is : released'); 249 break; 250 default: 251 console.info('--------CHANGE IN AUDIO STATE----------INVALID--------------'); 252 console.info('Audio State is : invalid'); 253 break; 254 } 255 }); 256 } 257 ```