1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
< html lang ="en ">
3
3
< head >
4
4
< script src ="node_modules/@picovoice/web-voice-processor/dist/iife/index.js "> </ script >
5
5
< script src ="node_modules/@picovoice/koala-web/dist/iife/index.js "> </ script >
6
6
< script src ="koala_params.js "> </ script >
7
- < script type ="application/javascript ">
8
- let koala = null ;
9
-
10
- let originalBuffer ;
11
- let enhancedBuffer ;
12
- let outputFrames ;
13
-
14
- window . onload = function ( ) {
15
- const audioContext = new ( window . AudioContext || window . webKitAudioContext ) (
16
- { sampleRate : 16000 }
17
- ) ;
18
-
19
- const originalAudioGain = audioContext . createGain ( ) ;
20
- originalAudioGain . gain . value = 0 ;
21
-
22
- const enhancedAudioGain = audioContext . createGain ( ) ;
23
- enhancedAudioGain . gain . value = 1 ;
24
-
25
- originalAudioGain . connect ( audioContext . destination ) ;
26
- enhancedAudioGain . connect ( audioContext . destination ) ;
27
-
28
- let originalAudioSource ;
29
- let enhancedAudioSource ;
30
- let isPlaying = false ;
31
-
32
- function readAudioFile ( selectedFile , callback ) {
33
- let reader = new FileReader ( ) ;
34
- reader . onload = function ( ev ) {
35
- let wavBytes = reader . result ;
36
- audioContext . decodeAudioData ( wavBytes , callback ) ;
37
- } ;
38
- reader . readAsArrayBuffer ( selectedFile ) ;
39
- }
40
-
41
- const fileSelector = document . getElementById ( "audioFile" ) ;
42
- fileSelector . addEventListener ( "change" , async ( event ) => {
43
- outputFrames = [ ] ;
44
- resultBox . style . display = "none" ;
45
-
46
- originalAudioSource ?. stop ( ) ;
47
- enhancedAudioSource ?. stop ( ) ;
48
-
49
- writeMessage ( "Loading audio file..." ) ;
50
- const fileList = event . target . files ;
51
- readAudioFile ( fileList [ 0 ] , async ( audioBuffer ) => {
52
- const f32PCM = audioBuffer . getChannelData ( 0 ) ;
53
- const i16PCM = new Int16Array ( f32PCM . length ) ;
54
-
55
- const INT16_MAX = 32767 ;
56
- const INT16_MIN = - 32768 ;
57
- i16PCM . set (
58
- f32PCM . map ( ( f ) => {
59
- let i = Math . trunc ( f * INT16_MAX ) ;
60
- if ( f > INT16_MAX ) i = INT16_MAX ;
61
- if ( f < INT16_MIN ) i = INT16_MIN ;
62
- return i ;
63
- } )
64
- ) ;
65
-
66
- writeMessage ( "Processing audio file..." ) ;
67
- const splitPcm = [ ] ;
68
- await koala . reset ( ) ;
69
- for ( let i = 0 ; i < ( i16PCM . length - koala . frameLength + 1 ) ; i += koala . frameLength ) {
70
- const split = i16PCM . slice ( i , i + koala . frameLength ) ;
71
- splitPcm . push ( split ) ;
72
- await koala . process ( split ) ;
73
- }
74
-
75
- writeMessage ( "Waiting for Koala engine to finish processing audio file..." ) ;
76
- await waitForProcess ( splitPcm , outputFrames ) ;
77
-
78
- originalBuffer = createBuffer ( i16PCM ) ;
79
- enhancedBuffer = createBuffer ( mergeFrames ( outputFrames , koala . delaySample ) ) ;
80
-
81
- writeMessage ( "Press 'Play' to listen to recording. Move the slider to play around with noise." ) ;
82
- resultBox . style . display = "block" ;
83
- } ) ;
84
- } ) ;
85
-
86
- const displayTimer = document . getElementById ( "displayTimer" ) ;
87
- const recordButton = document . getElementById ( "recordAudio" ) ;
88
- const stopRecord = document . getElementById ( "stopRecord" ) ;
89
- const resultBox = document . getElementById ( "result" ) ;
90
- const volumeControl = document . getElementById ( "volumeControl" ) ;
91
- const playAudio = document . getElementById ( "playAudio" ) ;
92
-
93
- let timer = null ;
94
- let currentTimer = 0.0 ;
95
- let audioData = [ ] ;
96
- const recorderEngine = {
97
- onmessage : ( event ) => {
98
- switch ( event . data . command ) {
99
- case "process" :
100
- audioData . push ( event . data . inputFrame ) ;
101
- break ;
102
- }
103
- }
104
- }
105
-
106
- recordButton . addEventListener ( "click" , async ( ) => {
107
- displayTimer . style . display = "inline" ;
108
- stopRecord . style . display = "inline" ;
109
- recordButton . style . display = "none" ;
110
- resultBox . style . display = "none" ;
111
-
112
- originalAudioSource ?. stop ( ) ;
113
- enhancedAudioSource ?. stop ( ) ;
114
-
115
- currentTimer = 0.0 ;
116
- audioData = [ ] ;
117
- outputFrames = [ ] ;
118
-
119
- try {
120
- writeMessage ( "Recording audio..." ) ;
121
- window . WebVoiceProcessor . WebVoiceProcessor . setOptions ( {
122
- frameLength : koala . frameLength
123
- } ) ;
124
- await window . WebVoiceProcessor . WebVoiceProcessor . subscribe ( [ recorderEngine , koala ] ) ;
125
- timer = setInterval ( ( ) => {
126
- currentTimer += 0.1 ;
127
- displayTimer . innerText = `${ currentTimer . toFixed ( 1 ) } / 120` ;
128
- if ( currentTimer === 120 ) {
129
- stopRecord . click ( ) ;
130
- }
131
- } , 100 ) ;
132
- } catch ( e ) {
133
- writeMessage ( e ) ;
134
- }
135
- } ) ;
136
-
137
- stopRecord . addEventListener ( "click" , async ( ) => {
138
- displayTimer . style . display = "none" ;
139
- stopRecord . style . display = "none" ;
140
- recordButton . style . display = "inline" ;
141
-
142
- await window . WebVoiceProcessor . WebVoiceProcessor . unsubscribe ( [ recorderEngine , koala ] ) ;
143
- clearInterval ( timer ) ;
144
-
145
- writeMessage ( "Waiting for Koala engine to finish processing..." )
146
- await waitForProcess ( audioData , outputFrames ) ;
147
-
148
- originalBuffer = createBuffer ( mergeFrames ( audioData ) ) ;
149
- enhancedBuffer = createBuffer ( mergeFrames ( outputFrames , koala . delaySample ) ) ;
150
-
151
- writeMessage ( "Press 'Play' to listen to recording. Move the slider to play around with noise." ) ;
152
- resultBox . style . display = "block" ;
153
- } ) ;
154
-
155
- volumeControl . addEventListener ( "input" , ( e ) => {
156
- originalAudioGain . gain . value = 1 - e . target . value ;
157
- enhancedAudioGain . gain . value = e . target . value ;
158
- } ) ;
159
-
160
- playAudio . addEventListener ( "click" , ( ) => {
161
- if ( ! isPlaying ) {
162
- isPlaying = true ;
163
- const current_time = audioContext . currentTime ;
164
-
165
- originalAudioSource = audioContext . createBufferSource ( ) ;
166
- enhancedAudioSource = audioContext . createBufferSource ( ) ;
167
-
168
- originalAudioSource . buffer = originalBuffer ;
169
- originalAudioSource . loop = true ;
170
- originalAudioSource . connect ( originalAudioGain ) ;
171
- originalAudioSource . start ( current_time + 0.2 ) ;
172
-
173
- enhancedAudioSource . buffer = enhancedBuffer ;
174
- enhancedAudioSource . loop = true ;
175
- enhancedAudioSource . connect ( enhancedAudioGain ) ;
176
- enhancedAudioSource . start ( current_time + 0.2 ) ;
177
-
178
- playAudio . innerHTML = "Stop"
179
- } else {
180
- isPlaying = false ;
181
-
182
- originalAudioSource . stop ( ) ;
183
- enhancedAudioSource . stop ( ) ;
184
-
185
- playAudio . innerHTML = "Play"
186
- }
187
- } ) ;
188
-
189
- function mergeFrames ( data , delaySample = 0 ) {
190
- let delay = 0 ;
191
-
192
- const pcm = new Int16Array ( data . length * koala . frameLength ) ;
193
- for ( let i = 0 ; i < data . length ; i ++ ) {
194
- if ( i * koala . frameLength < delaySample ) {
195
- delay += 1 ;
196
- } else {
197
- pcm . set ( data [ i ] , ( i - delay ) * koala . frameLength ) ;
198
- }
199
- }
200
- return pcm ;
201
- }
202
-
203
- function createBuffer ( data ) {
204
- const buffer = audioContext . createBuffer ( 1 , data . length , koala . sampleRate ) ;
205
- const source = new Float32Array ( data . length ) ;
206
- for ( let i = 0 ; i < data . length ; i ++ ) {
207
- source [ i ] = data [ i ] < 0 ? data [ i ] / 32768 : data [ i ] / 32767 ;
208
- }
209
- buffer . copyToChannel ( source , 0 ) ;
210
- return buffer ;
211
- }
212
-
213
- async function waitForProcess ( input , output ) {
214
- return new Promise ( resolve => {
215
- setInterval ( ( ) => {
216
- if ( input . length === output . length ) {
217
- resolve ( ) ;
218
- }
219
- } , 100 )
220
- } ) ;
221
- }
222
- }
223
-
224
- function writeMessage ( message ) {
225
- console . log ( message ) ;
226
- document . getElementById ( "status" ) . innerHTML = message ;
227
- }
228
-
229
- function processErrorCallback ( error ) {
230
- writeMessage ( error ) ;
231
- }
232
-
233
- function processCallback ( enhancedPcm ) {
234
- outputFrames . push ( enhancedPcm ) ;
235
- }
236
-
237
- async function startKoala ( accessKey ) {
238
- writeMessage ( "Koala is loading. Please wait..." ) ;
239
- try {
240
- koala = await KoalaWeb . KoalaWorker . create (
241
- accessKey ,
242
- processCallback ,
243
- { base64 : modelParams } ,
244
- { processErrorCallback : processErrorCallback }
245
- ) ;
246
-
247
- writeMessage ( "Koala worker ready!" ) ;
248
-
249
- writeMessage (
250
- "WebVoiceProcessor initializing. Microphone permissions requested ..."
251
- ) ;
252
- window . WebVoiceProcessor . WebVoiceProcessor . setOptions ( {
253
- frameLength : koala . frameLength
254
- } ) ;
255
- document . getElementById ( "control" ) . style . display = "block" ;
256
- writeMessage ( "Koala worker is ready!" ) ;
257
- } catch ( err ) {
258
- processErrorCallback ( err ) ;
259
- }
260
- }
261
- </ script >
7
+ < script type ="application/javascript " src ="scripts/koala.js "> </ script >
262
8
</ head >
263
9
< body >
264
10
< h1 > Koala Web Demo</ h1 >
265
11
< p > This demo uses Koala for Web and the WebVoiceProcessor to:</ p >
266
12
< ol >
13
+ < li > Create an instance of Koala with the model file provided.</ li >
267
14
< li >
268
- Create an instance of Koala with the model file provided.
15
+ Select an audio file or acquire microphone (& ask permission) data
16
+ stream and convert to voice processing format (16kHz 16-bit linear PCM).
17
+ The downsampled audio is forwarded to the Koala engine. The audio
18
+ < i > does not</ i > leave the browser: all processing is occurring via the
19
+ Koala WebAssembly code.
269
20
</ li >
270
21
< li >
271
- Select an audio file or acquire microphone (& ask permission) data stream and convert to voice
272
- processing format (16kHz 16-bit linear PCM). The downsampled audio is
273
- forwarded to the Koala engine. The audio < i > does not</ i > leave the
274
- browser: all processing is occurring via the Koala WebAssembly code.
275
- </ li >
276
- < li >
277
- Enhance audio real time using Koala engine. Output both original and enhanced
278
- audio.
22
+ Enhance audio real time using Koala engine. Output both original and
23
+ enhanced audio.
279
24
</ li >
280
25
</ ol >
281
26
After entering the AccessKey, click the "Start Koala" button.
@@ -291,29 +36,38 @@ <h1>Koala Web Demo</h1>
291
36
value ="Start Koala "
292
37
onclick ="startKoala(document.getElementById('accessKey').value) "
293
38
/>
294
- < hr />
39
+ < hr />
295
40
< div id ="control " style ="display: none ">
296
41
< label for ="audioFile "> Choose audio file to enhance:</ label >
297
- < input type ="file " id ="audioFile " name ="audioFile "/>
42
+ < input type ="file " id ="audioFile " name ="audioFile " />
298
43
< p > < b > OR</ b > </ p >
299
- < label for ="recordAudio "> Record audio to enhance (up to 2 minutes):</ label >
44
+ < label for ="recordAudio "
45
+ > Record audio to enhance (up to 2 minutes):</ label
46
+ >
300
47
< button id ="recordAudio "> Record Audio</ button >
301
- < span id ="displayTimer " style ="display: none; "> </ span >
302
- < button id ="stopRecord " style ="display: none; "> Stop Recording</ button >
303
- < hr />
48
+ < span id ="displayTimer " style ="display: none "> </ span >
49
+ < button id ="stopRecord " style ="display: none "> Stop Recording</ button >
50
+ < hr />
304
51
</ div >
305
52
< div id ="status "> </ div >
306
- < br >
53
+ < br / >
307
54
< div id ="result " style ="display: none ">
308
55
< label >
309
56
Original
310
- < input type ="range " id ="volumeControl " min ="0 " max ="1 " value ="1 " step ="0.01 " />
57
+ < input
58
+ type ="range "
59
+ id ="volumeControl "
60
+ min ="0 "
61
+ max ="1 "
62
+ value ="1 "
63
+ step ="0.01 "
64
+ />
311
65
Koalafied
312
- < br >
313
- < br >
66
+ < br / >
67
+ < br / >
314
68
< button id ="playAudio "> Play</ button >
315
69
</ label >
316
70
</ div >
317
- < br >
71
+ < br / >
318
72
</ body >
319
73
</ html >
0 commit comments