1# Developing Audio Call 2 3During an audio call, audio output (playing the peer voice) and audio input (recording the local voice) are carried out simultaneously. You can use the AudioRenderer to implement audio output and the AudioCapturer to implement audio input. 4 5Before starting or stopping using the audio call service, the application needs to check the [audio scene](audio-call-overview.md#audio-scene) and [ringer mode](audio-call-overview.md#ringer-mode) to adopt proper audio management and prompt policies. 6 7The sample code below demonstrates the basic process of using the AudioRenderer and AudioCapturer to implement the audio call service, without the process of call data transmission. In actual development, the peer call data transmitted over the network needs to be decoded and played, and the sample code uses the process of reading an audio file instead; the local call data needs to be encoded and packed and then sent to the peer over the network, and the sample code uses the process of writing an audio file instead. 8 9## Using AudioRenderer to Play the Peer Voice 10 11This process is similar to the process of [using AudioRenderer to develop audio playback](using-audiorenderer-for-playback.md). The key differences lie in the **audioRendererInfo** parameter and audio data source. In the **audioRendererInfo** parameter used for audio calling, **content** must be set to **CONTENT_TYPE_SPEECH**, and **usage** must be set to **STREAM_USAGE_VOICE_COMMUNICATION**. 12 13```ts 14import { audio } from '@kit.AudioKit'; 15import { fileIo as fs } from '@kit.CoreFileKit'; 16import { BusinessError } from '@kit.BasicServicesKit'; 17 18const TAG = 'VoiceCallDemoForAudioRenderer'; 19// The process is similar to the process of using AudioRenderer to develop audio playback. The key differences lie in the audioRendererInfo parameter and audio data source. 20class Options { 21 offset?: number; 22 length?: number; 23} 24 25let bufferSize: number = 0; 26let renderModel: audio.AudioRenderer | undefined = undefined; 27let audioStreamInfo: audio.AudioStreamInfo = { 28 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // Sampling rate. 29 channels: audio.AudioChannel.CHANNEL_2, // Channel. 30 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // Sampling format. 31 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // Encoding format. 32}; 33let audioRendererInfo: audio.AudioRendererInfo = { 34 // Set the parameters related to the call scenario. 35 usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION, // Audio stream usage type: VoIP call. 36 rendererFlags: 0 // AudioRenderer flag. The default value is 0. 37}; 38let audioRendererOptions: audio.AudioRendererOptions = { 39 streamInfo: audioStreamInfo, 40 rendererInfo: audioRendererInfo 41}; 42let path = getContext().cacheDir; 43// Ensure that the resource exists in the sandbox path. 44let filePath = path + '/StarWars10s-2C-48000-4SW.wav'; 45let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_ONLY); 46let writeDataCallback = (buffer: ArrayBuffer) => { 47 let options: Options = { 48 offset: bufferSize, 49 length: buffer.byteLength 50 }; 51 fs.readSync(file.fd, buffer, options); 52 bufferSize += buffer.byteLength; 53}; 54 55// Create an AudioRenderer instance, and set the events to listen for. 56audio.createAudioRenderer(audioRendererOptions, (err: BusinessError, renderer: audio.AudioRenderer) => { // Create an AudioRenderer instance. 57 if (!err) { 58 console.info(`${TAG}: creating AudioRenderer success`); 59 renderModel = renderer; 60 if (renderModel !== undefined) { 61 renderModel.on('stateChange', (state: audio.AudioState) => { // Set the events to listen for. A callback is invoked when the AudioRenderer is switched to the specified state. 62 if (state == 1) { 63 console.info('audio renderer state is: STATE_PREPARED'); 64 } 65 if (state == 2) { 66 console.info('audio renderer state is: STATE_RUNNING'); 67 } 68 }); 69 renderModel.on('markReach', 1000, (position: number) => { // Subscribe to the markReach event. A callback is triggered when the number of rendered frames reaches 1000. 70 if (position == 1000) { 71 console.info('ON Triggered successfully'); 72 } 73 }); 74 renderModel.on('writeData', writeDataCallback); 75 } 76 } else { 77 console.info(`${TAG}: creating AudioRenderer failed, error: ${err.message}`); 78 } 79}); 80 81// Start audio rendering. 82async function start() { 83 if (renderModel !== undefined) { 84 let stateGroup: number[] = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED]; 85 if (stateGroup.indexOf(renderModel.state.valueOf()) === -1) { // Rendering can be started only when the AudioRenderer is in the STATE_PREPARED, STATE_PAUSED, or STATE_STOPPED state. 86 console.error(TAG + 'start failed'); 87 return; 88 } 89 renderModel.start((err: BusinessError) => { 90 if (err) { 91 console.error('Renderer start failed.'); 92 } else { 93 console.info('Renderer start success.'); 94 } 95 }); 96 } 97} 98 99// Pause the rendering. 100async function pause() { 101 if (renderModel !== undefined) { 102 // Rendering can be paused only when the AudioRenderer is in the STATE_RUNNING state. 103 if (renderModel.state.valueOf() !== audio.AudioState.STATE_RUNNING) { 104 console.info('Renderer is not running'); 105 return; 106 } 107 await renderModel.pause(); // Pause rendering. 108 if (renderModel.state.valueOf() === audio.AudioState.STATE_PAUSED) { 109 console.info('Renderer is paused.'); 110 } else { 111 console.error('Pausing renderer failed.'); 112 } 113 } 114} 115 116// Stop rendering. 117async function stop() { 118 if (renderModel !== undefined) { 119 // The AudioRenderer can be stopped only when it is in the STATE_RUNNING or STATE_PAUSED state. 120 if (renderModel.state.valueOf() !== audio.AudioState.STATE_RUNNING && renderModel.state.valueOf() !== audio.AudioState.STATE_PAUSED) { 121 console.info('Renderer is not running or paused.'); 122 return; 123 } 124 await renderModel.stop(); // Stop rendering. 125 if (renderModel.state.valueOf() === audio.AudioState.STATE_STOPPED) { 126 console.info('Renderer stopped.'); 127 } else { 128 console.error('Stopping renderer failed.'); 129 } 130 } 131} 132 133// Release the instance. 134async function release() { 135 if (renderModel !== undefined) { 136 // The AudioRenderer can be released only when it is not in the STATE_RELEASED state. 137 if (renderModel.state.valueOf() === audio.AudioState.STATE_RELEASED) { 138 console.info('Renderer already released'); 139 return; 140 } 141 await renderModel.release(); // Release the instance. 142 if (renderModel.state.valueOf() === audio.AudioState.STATE_RELEASED) { 143 console.info('Renderer released'); 144 } else { 145 console.error('Renderer release failed.'); 146 } 147 } 148} 149``` 150 151## Using AudioCapturer to Record the Local Voice 152 153This process is similar to the process of [using AudioCapturer to develop audio recording](using-audiocapturer-for-recording.md). The key differences lie in the **audioCapturerInfo** parameter and audio data stream direction. In the **audioCapturerInfo** parameter used for audio calling, **source** must be set to **SOURCE_TYPE_VOICE_COMMUNICATION**. 154 155You must request the **ohos.permission.MICROPHONE** permission for all recording tasks. For details, see [Requesting User Authorization](../../security/AccessToken/request-user-authorization.md). 156 157```ts 158import { audio } from '@kit.AudioKit'; 159import { fileIo as fs } from '@kit.CoreFileKit'; 160import { BusinessError } from '@kit.BasicServicesKit'; 161 162const TAG = 'VoiceCallDemoForAudioCapturer'; 163class Options { 164 offset?: number; 165 length?: number; 166} 167 168// The process is similar to the process of using AudioCapturer to develop audio recording. The key differences lie in the audioCapturerInfo parameter and audio data stream direction. 169let bufferSize: number = 0; 170let audioCapturer: audio.AudioCapturer | undefined = undefined; 171let audioStreamInfo: audio.AudioStreamInfo = { 172 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // Sampling rate. 173 channels: audio.AudioChannel.CHANNEL_2, // Channel. 174 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // Sampling format. 175 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // Encoding format. 176}; 177let audioCapturerInfo: audio.AudioCapturerInfo = { 178 // Set the parameters related to the call scenario. 179 source: audio.SourceType.SOURCE_TYPE_VOICE_COMMUNICATION, // Audio source type: voice communication. 180 capturerFlags: 0 // AudioCapturer flag. The default value is 0. 181}; 182let audioCapturerOptions: audio.AudioCapturerOptions = { 183 streamInfo: audioStreamInfo, 184 capturerInfo: audioCapturerInfo 185}; 186let path = getContext().cacheDir; 187let filePath = path + '/StarWars10s-2C-48000-4SW.wav'; 188let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 189let readDataCallback = (buffer: ArrayBuffer) => { 190 let options: Options = { 191 offset: bufferSize, 192 length: buffer.byteLength 193 }; 194 fs.writeSync(file.fd, buffer, options); 195 bufferSize += buffer.byteLength; 196}; 197 198// Create an AudioRenderer instance, and set the events to listen for. 199async function init() { 200 audio.createAudioCapturer(audioCapturerOptions, (err: BusinessError, capturer: audio.AudioCapturer) => { // Create an AudioCapturer instance. 201 if (err) { 202 console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`); 203 return; 204 } 205 console.info(`${TAG}: create AudioCapturer success`); 206 audioCapturer = capturer; 207 if (audioCapturer !== undefined) { 208 audioCapturer.on('markReach', 1000, (position: number) => { // Subscribe to the markReach event. A callback is triggered when the number of captured frames reaches 1000. 209 if (position === 1000) { 210 console.info('ON Triggered successfully'); 211 } 212 }); 213 audioCapturer.on('periodReach', 2000, (position: number) => { // Subscribe to the periodReach event. A callback is triggered each time when the number of captured frames reaches 2000. 214 if (position === 2000) { 215 console.info('ON Triggered successfully'); 216 } 217 }); 218 audioCapturer.on('readData', readDataCallback); 219 } 220 }); 221} 222 223// Start audio recording. 224async function start() { 225 if (audioCapturer !== undefined) { 226 let stateGroup: number[] = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED]; 227 if (stateGroup.indexOf(audioCapturer.state.valueOf()) === -1) { // Recording can be started only when the AudioCapturer is in the STATE_PREPARED, STATE_PAUSED, or STATE_STOPPED state. 228 console.error(`${TAG}: start failed`); 229 return; 230 } 231 audioCapturer.start((err: BusinessError) => { 232 if (err) { 233 console.error('Capturer start failed.'); 234 } else { 235 console.info('Capturer start success.'); 236 } 237 }); 238 } 239} 240 241// Stop recording. 242async function stop() { 243 if (audioCapturer !== undefined) { 244 // The AudioCapturer can be stopped only when it is in STATE_RUNNING or STATE_PAUSED state. 245 if (audioCapturer.state.valueOf() !== audio.AudioState.STATE_RUNNING && audioCapturer.state.valueOf() !== audio.AudioState.STATE_PAUSED) { 246 console.info('Capturer is not running or paused'); 247 return; 248 } 249 await audioCapturer.stop(); // Stop recording. 250 if (audioCapturer.state.valueOf() === audio.AudioState.STATE_STOPPED) { 251 console.info('Capturer stopped'); 252 } else { 253 console.error('Capturer stop failed'); 254 } 255 } 256} 257 258// Release the instance. 259async function release() { 260 if (audioCapturer !== undefined) { 261 // The AudioCapturer can be released only when it is not in the STATE_RELEASED or STATE_NEW state. 262 if (audioCapturer.state.valueOf() === audio.AudioState.STATE_RELEASED || audioCapturer.state.valueOf() === audio.AudioState.STATE_NEW) { 263 console.info('Capturer already released'); 264 return; 265 } 266 await audioCapturer.release(); // Release the instance. 267 if (audioCapturer.state.valueOf() === audio.AudioState.STATE_RELEASED) { 268 console.info('Capturer released'); 269 } else { 270 console.error('Capturer release failed'); 271 } 272 } 273} 274``` 275