1- <!DOCTYPE html>
1+ <!doctype html>
22< html lang ="en ">
33 < head >
44 < script src ="node_modules/@picovoice/web-voice-processor/dist/iife/index.js "> </ script >
55 < script src ="node_modules/@picovoice/koala-web/dist/iife/index.js "> </ script >
66 < 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 >
2628 </ head >
2639 < body >
26410 < h1 > Koala Web Demo</ h1 >
26511 < p > This demo uses Koala for Web and the WebVoiceProcessor to:</ p >
26612 < ol >
13+ < li > Create an instance of Koala with the model file provided.</ li >
26714 < 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.
26920 </ li >
27021 < 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.
27924 </ li >
28025 </ ol >
28126 After entering the AccessKey, click the "Start Koala" button.
@@ -291,29 +36,38 @@ <h1>Koala Web Demo</h1>
29136 value ="Start Koala "
29237 onclick ="startKoala(document.getElementById('accessKey').value) "
29338 />
294- < hr />
39+ < hr />
29540 < div id ="control " style ="display: none ">
29641 < 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 " />
29843 < 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+ >
30047 < 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 />
30451 </ div >
30552 < div id ="status "> </ div >
306- < br >
53+ < br / >
30754 < div id ="result " style ="display: none ">
30855 < label >
30956 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+ />
31165 Koalafied
312- < br >
313- < br >
66+ < br / >
67+ < br / >
31468 < button id ="playAudio "> Play</ button >
31569 </ label >
31670 </ div >
317- < br >
71+ < br / >
31872 </ body >
31973</ html >
0 commit comments