1# Audio Rendering Development 2 3## When to Use 4 5**AudioRenderer** provides APIs for rendering audio files and controlling playback. It also supports audio interruption. You can use the APIs provided by **AudioRenderer** to play audio files in output devices and manage playback tasks. 6 7### Audio Interruption 8 9When an audio stream with a higher priority needs to be played, the audio renderer interrupts the stream with a lower priority. For example, if a call comes in when the user is listening to music, the music playback, which is the lower priority stream, is paused. For details, see [How to Develop](#how-to-develop). 10 11### State Check 12 13During application development, you are advised to use **on('stateChange')** to subscribe to state changes of the **AudioRenderer** instance. This is because some operations can be performed only when the audio renderer is in a given state. If the application performs an operation when the audio renderer is not in the given state, the system may throw an exception or generate other undefined behavior. 14 15**Figure 1** Audio renderer state 16 17 18 19### Asynchronous Operations 20 21To ensure that the UI thread is not blocked, most **AudioRenderer** calls are asynchronous. Each API provides the callback and promise functions. The following examples use the promise functions. For more information, see [AudioRenderer in Audio Management](../reference/apis/js-apis-audio.md#audiorenderer8). 22 23 24 25## How to Develop 26 271. Use **createAudioRenderer()** to create an **AudioRenderer** instance. 28 Set parameters of the audio renderer in **audioCapturerOptions**. This instance is used to render audio, control and obtain the rendering status, and register a callback for notification. 29 30 ```js 31 var audioStreamInfo = { 32 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 33 channels: audio.AudioChannel.CHANNEL_1, 34 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 35 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 36 } 37 38 var audioRendererInfo = { 39 content: audio.ContentType.CONTENT_TYPE_SPEECH, 40 usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION, 41 rendererFlags: 1 42 } 43 44 var audioRendererOptions = { 45 streamInfo: audioStreamInfo, 46 rendererInfo: audioRendererInfo 47 } 48 49 let audioRenderer = await audio.createAudioRenderer(audioRendererOptions); 50 ``` 51 522. Use **on('interrupt')** to subscribe to audio interruption events. 53 54 Stream-A is interrupted when Stream-B with a higher or equal priority requests to become active and use the output device. 55 56 In some cases, the audio renderer performs forcible operations such as pausing and ducking, and notifies the application through **InterruptEvent**. In other cases, the application can choose to act on the **InterruptEvent** or ignore it. 57 58 In the case of audio interruption, the application may encounter write failures. To avoid such failures, interruption unaware applications can use **audioRenderer.state** to check the renderer state before writing audio data. The applications can obtain more details by subscribing to the audio interruption events. For details, see [InterruptEvent](../reference/apis/js-apis-audio.md#interruptevent9). 59 60 ```js 61 audioRenderer.on('interrupt', (interruptEvent) => { 62 console.info('InterruptEvent Received'); 63 console.info('InterruptType: ' + interruptEvent.eventType); 64 console.info('InterruptForceType: ' + interruptEvent.forceType); 65 console.info('AInterruptHint: ' + interruptEvent.hintType); 66 67 if (interruptEvent.forceType == audio.InterruptForceType.INTERRUPT_FORCE) { 68 switch (interruptEvent.hintType) { 69 // Force Pause: Action was taken by framework. 70 // Halt the write calls to avoid data loss. 71 case audio.InterruptHint.INTERRUPT_HINT_PAUSE: 72 isPlay = false; 73 break; 74 // Force Stop: Action was taken by framework. 75 // Halt the write calls to avoid data loss. 76 case audio.InterruptHint.INTERRUPT_HINT_STOP: 77 isPlay = false; 78 break; 79 // Force Duck: Action was taken by framework, 80 // just notifying the app that volume has been reduced. 81 case audio.InterruptHint.INTERRUPT_HINT_DUCK: 82 break; 83 // Force Unduck: Action was taken by framework, 84 // just notifying the app that volume has been restored. 85 case audio.InterruptHint.INTERRUPT_HINT_UNDUCK: 86 break; 87 } 88 } else if (interruptEvent.forceType == audio.InterruptForceType.INTERRUPT_SHARE) { 89 switch (interruptEvent.hintType) { 90 // Share Resume: Action is to be taken by App. 91 // Resume the force paused stream if required. 92 case audio.InterruptHint.INTERRUPT_HINT_RESUME: 93 startRenderer(); 94 break; 95 // Share Pause: Stream has been interrupted, 96 // It can choose to pause or play concurrently. 97 case audio.InterruptHint.INTERRUPT_HINT_PAUSE: 98 isPlay = false; 99 pauseRenderer(); 100 break; 101 } 102 } 103 }); 104 ``` 105 1063. Use **start()** to start audio rendering. 107 108 The renderer state will be **STATE_RUNNING** once the audio renderer is started. The application can then begin reading buffers. 109 110 ```js 111 async function startRenderer() { 112 var state = audioRenderer.state; 113 // The state should be prepared, paused, or stopped. 114 if (state != audio.AudioState.STATE_PREPARED || state != audio.AudioState.STATE_PAUSED || 115 state != audio.AudioState.STATE_STOPPED) { 116 console.info('Renderer is not in a correct state to start'); 117 return; 118 } 119 120 await audioRenderer.start(); 121 122 state = audioRenderer.state; 123 if (state == audio.AudioState.STATE_RUNNING) { 124 console.info('Renderer started'); 125 } else { 126 console.error('Renderer start failed'); 127 } 128 } 129 ``` 130 1314. Call **write()** to write data to the buffer. 132 133 Read the audio data to be played to the buffer. Call **write()** repeatedly to write the data to the buffer. 134 135 ```js 136 async function writeBuffer(buf) { 137 var state = audioRenderer.state; 138 if (state != audio.AudioState.STATE_RUNNING) { 139 console.error('Renderer is not running, do not write'); 140 isPlay = false; 141 return; 142 } 143 let writtenbytes = await audioRenderer.write(buf); 144 145 console.info('Actual written bytes: ' + writtenbytes); 146 if (writtenbytes < 0) { 147 console.error('Write buffer failed. check the state of renderer'); 148 } 149 } 150 151 // Reasonable minimum buffer size for renderer. However, the renderer can accept other read sizes as well. 152 const bufferSize = await audioRenderer.getBufferSize(); 153 const path = '/data/file_example_WAV_2MG.wav'; 154 let ss = fileio.createStreamSync(path, 'r'); 155 const totalSize = 2146166; // file_example_WAV_2MG.wav 156 let rlen = 0; 157 let discardHeader = new ArrayBuffer(44); 158 ss.readSync(discardHeader); 159 rlen += 44; 160 161 var id = setInterval(() => { 162 if (isPlay || isRelease) { 163 if (rlen >= totalSize || isRelease) { 164 ss.closeSync(); 165 stopRenderer(); 166 clearInterval(id); 167 } 168 let buf = new ArrayBuffer(bufferSize); 169 rlen += ss.readSync(buf); 170 console.info('Total bytes read from file: ' + rlen); 171 writeBuffer(buf); 172 } else { 173 console.info('check after next interval'); 174 } 175 } , 30); // interval to be set based on audio file format 176 ``` 177 1785. (Optional) Call **pause()** or **stop()** to pause or stop rendering. 179 180 ```js 181 async function pauseRenderer() { 182 var state = audioRenderer.state; 183 if (state != audio.AudioState.STATE_RUNNING) { 184 console.info('Renderer is not running'); 185 return; 186 } 187 188 await audioRenderer.pause(); 189 190 state = audioRenderer.state; 191 if (state == audio.AudioState.STATE_PAUSED) { 192 console.info('Renderer paused'); 193 } else { 194 console.error('Renderer pause failed'); 195 } 196 } 197 198 async function stopRenderer() { 199 var state = audioRenderer.state; 200 if (state != audio.AudioState.STATE_RUNNING || state != audio.AudioState.STATE_PAUSED) { 201 console.info('Renderer is not running or paused'); 202 return; 203 } 204 205 await audioRenderer.stop(); 206 207 state = audioRenderer.state; 208 if (state == audio.AudioState.STATE_STOPPED) { 209 console.info('Renderer stopped'); 210 } else { 211 console.error('Renderer stop failed'); 212 } 213 } 214 ``` 215 2166. After the task is complete, call **release()** to release related resources. 217 218 **AudioRenderer** uses a large number of system resources. Therefore, ensure that the resources are released after the task is complete. 219 220 ```js 221 async function releaseRenderer() { 222 if (state_ == RELEASED || state_ == NEW) { 223 console.info('Resourced already released'); 224 return; 225 } 226 227 await audioRenderer.release(); 228 229 state = audioRenderer.state; 230 if (state == STATE_RELEASED) { 231 console.info('Renderer released'); 232 } else { 233 console.info('Renderer release failed'); 234 } 235 236 } 237 ``` 238 239 240