fraxy commited on
Commit
1cc4227
1 Parent(s): 081422e

vanilla command import

Browse files
Files changed (4) hide show
  1. command.js +0 -0
  2. helpers.js +193 -0
  3. index.html +413 -0
  4. libcommand.worker.js +1 -0
command.js ADDED
The diff for this file is too large to render. See raw diff
 
helpers.js ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Common Javascript functions used by the examples
2
+
3
+ function convertTypedArray(src, type) {
4
+ var buffer = new ArrayBuffer(src.byteLength);
5
+ var baseView = new src.constructor(buffer).set(src);
6
+ return new type(buffer);
7
+ }
8
+
9
+ var printTextarea = (function() {
10
+ var element = document.getElementById('output');
11
+ if (element) element.value = ''; // clear browser cache
12
+ return function(text) {
13
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
14
+ console.log(text);
15
+ if (element) {
16
+ element.value += text + "\n";
17
+ element.scrollTop = element.scrollHeight; // focus on bottom
18
+ }
19
+ };
20
+ })();
21
+
22
+ async function clearCache() {
23
+ if (confirm('Are you sure you want to clear the cache?\nAll the models will be downloaded again.')) {
24
+ indexedDB.deleteDatabase(dbName);
25
+ }
26
+ }
27
+
28
+ // fetch a remote file from remote URL using the Fetch API
29
+ async function fetchRemote(url, cbProgress, cbPrint) {
30
+ cbPrint('fetchRemote: downloading with fetch()...');
31
+
32
+ const response = await fetch(
33
+ url,
34
+ {
35
+ method: 'GET',
36
+ headers: {
37
+ 'Content-Type': 'application/octet-stream',
38
+ },
39
+ }
40
+ );
41
+
42
+ if (!response.ok) {
43
+ cbPrint('fetchRemote: failed to fetch ' + url);
44
+ return;
45
+ }
46
+
47
+ const contentLength = response.headers.get('content-length');
48
+ const total = parseInt(contentLength, 10);
49
+ const reader = response.body.getReader();
50
+
51
+ var chunks = [];
52
+ var receivedLength = 0;
53
+ var progressLast = -1;
54
+
55
+ while (true) {
56
+ const { done, value } = await reader.read();
57
+
58
+ if (done) {
59
+ break;
60
+ }
61
+
62
+ chunks.push(value);
63
+ receivedLength += value.length;
64
+
65
+ if (contentLength) {
66
+ cbProgress(receivedLength/total);
67
+
68
+ var progressCur = Math.round((receivedLength / total) * 10);
69
+ if (progressCur != progressLast) {
70
+ cbPrint('fetchRemote: fetching ' + 10*progressCur + '% ...');
71
+ progressLast = progressCur;
72
+ }
73
+ }
74
+ }
75
+
76
+ var position = 0;
77
+ var chunksAll = new Uint8Array(receivedLength);
78
+
79
+ for (var chunk of chunks) {
80
+ chunksAll.set(chunk, position);
81
+ position += chunk.length;
82
+ }
83
+
84
+ return chunksAll;
85
+ }
86
+
87
+ // load remote data
88
+ // - check if the data is already in the IndexedDB
89
+ // - if not, fetch it from the remote URL and store it in the IndexedDB
90
+ function loadRemote(url, dst, size_mb, cbProgress, cbReady, cbCancel, cbPrint) {
91
+ if (!navigator.storage || !navigator.storage.estimate) {
92
+ cbPrint('loadRemote: navigator.storage.estimate() is not supported');
93
+ } else {
94
+ // query the storage quota and print it
95
+ navigator.storage.estimate().then(function (estimate) {
96
+ cbPrint('loadRemote: storage quota: ' + estimate.quota + ' bytes');
97
+ cbPrint('loadRemote: storage usage: ' + estimate.usage + ' bytes');
98
+ });
99
+ }
100
+
101
+ // check if the data is already in the IndexedDB
102
+ var rq = indexedDB.open(dbName, dbVersion);
103
+
104
+ rq.onupgradeneeded = function (event) {
105
+ var db = event.target.result;
106
+ if (db.version == 1) {
107
+ var os = db.createObjectStore('models', { autoIncrement: false });
108
+ cbPrint('loadRemote: created IndexedDB ' + db.name + ' version ' + db.version);
109
+ } else {
110
+ // clear the database
111
+ var os = event.currentTarget.transaction.objectStore('models');
112
+ os.clear();
113
+ cbPrint('loadRemote: cleared IndexedDB ' + db.name + ' version ' + db.version);
114
+ }
115
+ };
116
+
117
+ rq.onsuccess = function (event) {
118
+ var db = event.target.result;
119
+ var tx = db.transaction(['models'], 'readonly');
120
+ var os = tx.objectStore('models');
121
+ var rq = os.get(url);
122
+
123
+ rq.onsuccess = function (event) {
124
+ if (rq.result) {
125
+ cbPrint('loadRemote: "' + url + '" is already in the IndexedDB');
126
+ cbReady(dst, rq.result);
127
+ } else {
128
+ // data is not in the IndexedDB
129
+ cbPrint('loadRemote: "' + url + '" is not in the IndexedDB');
130
+
131
+ // alert and ask the user to confirm
132
+ if (!confirm(
133
+ 'You are about to download ' + size_mb + ' MB of data.\n' +
134
+ 'The model data will be cached in the browser for future use.\n\n' +
135
+ 'Press OK to continue.')) {
136
+ cbCancel();
137
+ return;
138
+ }
139
+
140
+ fetchRemote(url, cbProgress, cbPrint).then(function (data) {
141
+ if (data) {
142
+ // store the data in the IndexedDB
143
+ var rq = indexedDB.open(dbName, dbVersion);
144
+ rq.onsuccess = function (event) {
145
+ var db = event.target.result;
146
+ var tx = db.transaction(['models'], 'readwrite');
147
+ var os = tx.objectStore('models');
148
+
149
+ var rq = null;
150
+ try {
151
+ var rq = os.put(data, url);
152
+ } catch (e) {
153
+ cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB: \n' + e);
154
+ cbCancel();
155
+ return;
156
+ }
157
+
158
+ rq.onsuccess = function (event) {
159
+ cbPrint('loadRemote: "' + url + '" stored in the IndexedDB');
160
+ cbReady(dst, data);
161
+ };
162
+
163
+ rq.onerror = function (event) {
164
+ cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB');
165
+ cbCancel();
166
+ };
167
+ };
168
+ }
169
+ });
170
+ }
171
+ };
172
+
173
+ rq.onerror = function (event) {
174
+ cbPrint('loadRemote: failed to get data from the IndexedDB');
175
+ cbCancel();
176
+ };
177
+ };
178
+
179
+ rq.onerror = function (event) {
180
+ cbPrint('loadRemote: failed to open IndexedDB');
181
+ cbCancel();
182
+ };
183
+
184
+ rq.onblocked = function (event) {
185
+ cbPrint('loadRemote: failed to open IndexedDB: blocked');
186
+ cbCancel();
187
+ };
188
+
189
+ rq.onabort = function (event) {
190
+ cbPrint('loadRemote: failed to open IndexedDB: abort');
191
+ cbCancel();
192
+ };
193
+ }
index.html ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en-us">
3
+ <head>
4
+ <title>command : Voice assistant example using Whisper + WebAssembly</title>
5
+
6
+ <style>
7
+ #output {
8
+ width: 100%;
9
+ height: 100%;
10
+ margin: 0 auto;
11
+ margin-top: 10px;
12
+ border-left: 0px;
13
+ border-right: 0px;
14
+ padding-left: 0px;
15
+ padding-right: 0px;
16
+ display: block;
17
+ background-color: black;
18
+ color: white;
19
+ font-size: 10px;
20
+ font-family: 'Lucida Console', Monaco, monospace;
21
+ outline: none;
22
+ white-space: pre;
23
+ overflow-wrap: normal;
24
+ overflow-x: scroll;
25
+ }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <div id="main-container">
30
+ <b>command : Voice assistant example using Whisper + WebAssembly</b>
31
+
32
+ <br><br>
33
+
34
+ You can find more about this project on <a href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/command.wasm">GitHub</a>.
35
+
36
+ <br><br>
37
+
38
+ <b>More examples:</b>
39
+ <a href="https://whisper.ggerganov.com/">main</a> |
40
+ <a href="https://whisper.ggerganov.com/bench">bench</a> |
41
+ <a href="https://whisper.ggerganov.com/stream">stream</a> |
42
+ <a href="https://whisper.ggerganov.com/command">command</a> |
43
+ <a href="https://whisper.ggerganov.com/talk">talk</a> |
44
+
45
+ <br><br>
46
+
47
+ <hr>
48
+
49
+ Select the model you would like to use, click the "Start" button and follow the instructions.
50
+
51
+ <br><br>
52
+
53
+ <div id="model-whisper">
54
+ Whisper model: <span id="model-whisper-status"></span>
55
+ <button id="fetch-whisper-tiny-en" onclick="loadWhisper('tiny.en')">tiny.en (75 MB)</button>
56
+ <button id="fetch-whisper-base-en" onclick="loadWhisper('base.en')">base.en (142 MB)</button>
57
+ <br><br>
58
+ Quantized models:<br><br>
59
+ <button id="fetch-whisper-tiny-en-q5_1" onclick="loadWhisper('tiny-en-q5_1')">tiny.en (Q5_1, 31 MB)</button>
60
+ <button id="fetch-whisper-base-en-q5_1" onclick="loadWhisper('base-en-q5_1')">base.en (Q5_1, 57 MB)</button>
61
+ <span id="fetch-whisper-progress"></span>
62
+
63
+ <!--
64
+ <input type="file" id="file" name="file" onchange="loadFile(event, 'whisper.bin')" />
65
+ -->
66
+ </div>
67
+
68
+ <br>
69
+
70
+ <div id="input">
71
+ <button id="start" onclick="onStart()" disabled>Start</button>
72
+ <button id="stop" onclick="onStop()" disabled>Stop</button>
73
+ <button id="clear" onclick="clearCache()">Clear Cache</button>
74
+ </div>
75
+
76
+ <br>
77
+
78
+ <div id="state">
79
+ Status: <b><span id="state-status">not started</span></b>
80
+
81
+ <pre id="state-transcribed">[The recognized voice commands will be displayed here]</pre>
82
+ </div>
83
+
84
+ <hr>
85
+
86
+ Debug output:
87
+ <textarea id="output" rows="20"></textarea>
88
+
89
+ <br>
90
+
91
+ <b>Troubleshooting</b>
92
+
93
+ <br><br>
94
+
95
+ The page does some heavy computations, so make sure:
96
+
97
+ <ul>
98
+ <li>To use a modern web browser (e.g. Chrome, Firefox)</li>
99
+ <li>To use a fast desktop or laptop computer (i.e. not a mobile phone)</li>
100
+ <li>Your browser supports WASM <a href="https://webassembly.org/roadmap/">Fixed-width SIMD</a></li>
101
+ </ul>
102
+
103
+ <div class="cell-version">
104
+ <span>
105
+ |
106
+ Build time: <span class="nav-link">Fri Nov 17 10:42:04 2023</span> |
107
+ Commit hash: <a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/commit/e2f0eba2">e2f0eba2</a> |
108
+ Commit subject: <span class="nav-link">ios : sync submodule</span> |
109
+ <a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/command.wasm">Source Code</a> |
110
+ </span>
111
+ </div>
112
+ </div>
113
+
114
+ <script type="text/javascript" src="helpers.js"></script>
115
+ <script type='text/javascript'>
116
+ // web audio context
117
+ var context = null;
118
+
119
+ // audio data
120
+ var audio = null;
121
+ var audio0 = null;
122
+
123
+ // the command instance
124
+ var instance = null;
125
+
126
+ // model name
127
+ var model_whisper = null;
128
+
129
+ var Module = {
130
+ print: printTextarea,
131
+ printErr: printTextarea,
132
+ setStatus: function(text) {
133
+ printTextarea('js: ' + text);
134
+ },
135
+ monitorRunDependencies: function(left) {
136
+ },
137
+ preRun: function() {
138
+ printTextarea('js: Preparing ...');
139
+ },
140
+ postRun: function() {
141
+ printTextarea('js: Initialized successfully!');
142
+ }
143
+ };
144
+
145
+ //
146
+ // fetch models
147
+ //
148
+
149
+ let dbVersion = 1
150
+ let dbName = 'whisper.ggerganov.com';
151
+ let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
152
+
153
+ function storeFS(fname, buf) {
154
+ // write to WASM file using FS_createDataFile
155
+ // if the file exists, delete it
156
+ try {
157
+ Module.FS_unlink(fname);
158
+ } catch (e) {
159
+ // ignore
160
+ }
161
+
162
+ Module.FS_createDataFile("/", fname, buf, true, true);
163
+
164
+ printTextarea('storeFS: stored model: ' + fname + ' size: ' + buf.length);
165
+
166
+ document.getElementById('model-whisper-status').innerHTML = 'loaded "' + model_whisper + '"!';
167
+
168
+ if (model_whisper != null) {
169
+ document.getElementById('start').disabled = false;
170
+ document.getElementById('stop' ).disabled = true;
171
+ }
172
+ }
173
+
174
+ function loadWhisper(model) {
175
+ let urls = {
176
+ 'tiny.en': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en.bin',
177
+ 'base.en': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en.bin',
178
+
179
+ 'tiny-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en-q5_1.bin',
180
+ 'base-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en-q5_1.bin',
181
+ };
182
+
183
+ let sizes = {
184
+ 'tiny.en': 75,
185
+ 'base.en': 142,
186
+
187
+ 'tiny-en-q5_1': 31,
188
+ 'base-en-q5_1': 57,
189
+ };
190
+
191
+ let url = urls[model];
192
+ let dst = 'whisper.bin';
193
+ let size_mb = sizes[model];
194
+
195
+ model_whisper = model;
196
+
197
+ document.getElementById('fetch-whisper-tiny-en').style.display = 'none';
198
+ document.getElementById('fetch-whisper-base-en').style.display = 'none';
199
+
200
+ document.getElementById('fetch-whisper-tiny-en-q5_1').style.display = 'none';
201
+ document.getElementById('fetch-whisper-base-en-q5_1').style.display = 'none';
202
+
203
+ document.getElementById('model-whisper-status').innerHTML = 'loading "' + model + '" ... ';
204
+
205
+ cbProgress = function(p) {
206
+ let el = document.getElementById('fetch-whisper-progress');
207
+ el.innerHTML = Math.round(100*p) + '%';
208
+ };
209
+
210
+ cbCancel = function() {
211
+ var el;
212
+ el = document.getElementById('fetch-whisper-tiny-en'); if (el) el.style.display = 'inline-block';
213
+ el = document.getElementById('fetch-whisper-base-en'); if (el) el.style.display = 'inline-block';
214
+
215
+ el = document.getElementById('fetch-whisper-tiny-en-q5_1'); if (el) el.style.display = 'inline-block';
216
+ el = document.getElementById('fetch-whisper-base-en-q5_1'); if (el) el.style.display = 'inline-block';
217
+
218
+ el = document.getElementById('model-whisper-status'); if (el) el.innerHTML = '';
219
+ };
220
+
221
+ loadRemote(url, dst, size_mb, cbProgress, storeFS, cbCancel, printTextarea);
222
+ }
223
+
224
+ //
225
+ // microphone
226
+ //
227
+
228
+ const kSampleRate = 16000;
229
+ const kRestartRecording_s = 120;
230
+ const kIntervalAudio_ms = 250; // pass the recorded audio to the C++ instance at this rate
231
+
232
+ var mediaRecorder = null;
233
+ var doRecording = false;
234
+ var startTime = 0;
235
+
236
+ window.AudioContext = window.AudioContext || window.webkitAudioContext;
237
+ window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
238
+
239
+ function stopRecording() {
240
+ Module.set_status("paused");
241
+ doRecording = false;
242
+ audio0 = null;
243
+ audio = null;
244
+ context = null;
245
+ }
246
+
247
+ function startRecording() {
248
+ if (!context) {
249
+ context = new AudioContext({
250
+ sampleRate: kSampleRate,
251
+ channelCount: 1,
252
+ echoCancellation: false,
253
+ autoGainControl: true,
254
+ noiseSuppression: true,
255
+ });
256
+ }
257
+
258
+ Module.set_status("");
259
+
260
+ document.getElementById('start').disabled = true;
261
+ document.getElementById('stop').disabled = false;
262
+
263
+ doRecording = true;
264
+ startTime = Date.now();
265
+
266
+ var chunks = [];
267
+ var stream = null;
268
+
269
+ navigator.mediaDevices.getUserMedia({audio: true, video: false})
270
+ .then(function(s) {
271
+ stream = s;
272
+ mediaRecorder = new MediaRecorder(stream);
273
+ mediaRecorder.ondataavailable = function(e) {
274
+ chunks.push(e.data);
275
+
276
+ var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
277
+ var reader = new FileReader();
278
+
279
+ reader.onload = function(event) {
280
+ var buf = new Uint8Array(reader.result);
281
+
282
+ if (!context) {
283
+ return;
284
+ }
285
+ context.decodeAudioData(buf.buffer, function(audioBuffer) {
286
+ var offlineContext = new OfflineAudioContext(audioBuffer.numberOfChannels, audioBuffer.length, audioBuffer.sampleRate);
287
+ var source = offlineContext.createBufferSource();
288
+ source.buffer = audioBuffer;
289
+ source.connect(offlineContext.destination);
290
+ source.start(0);
291
+
292
+ offlineContext.startRendering().then(function(renderedBuffer) {
293
+ audio = renderedBuffer.getChannelData(0);
294
+
295
+ //printTextarea('js: audio recorded, size: ' + audio.length + ', old size: ' + (audio0 == null ? 0 : audio0.length));
296
+
297
+ var audioAll = new Float32Array(audio0 == null ? audio.length : audio0.length + audio.length);
298
+ if (audio0 != null) {
299
+ audioAll.set(audio0, 0);
300
+ }
301
+ audioAll.set(audio, audio0 == null ? 0 : audio0.length);
302
+
303
+ if (instance) {
304
+ Module.set_audio(instance, audioAll);
305
+ }
306
+ });
307
+ }, function(e) {
308
+ audio = null;
309
+ });
310
+ }
311
+
312
+ reader.readAsArrayBuffer(blob);
313
+ };
314
+
315
+ mediaRecorder.onstop = function(e) {
316
+ if (doRecording) {
317
+ setTimeout(function() {
318
+ startRecording();
319
+ });
320
+ }
321
+ };
322
+
323
+ mediaRecorder.start(kIntervalAudio_ms);
324
+ })
325
+ .catch(function(err) {
326
+ printTextarea('js: error getting audio stream: ' + err);
327
+ });
328
+
329
+ var interval = setInterval(function() {
330
+ if (!doRecording) {
331
+ clearInterval(interval);
332
+ mediaRecorder.stop();
333
+ stream.getTracks().forEach(function(track) {
334
+ track.stop();
335
+ });
336
+
337
+ document.getElementById('start').disabled = false;
338
+ document.getElementById('stop').disabled = true;
339
+
340
+ mediaRecorder = null;
341
+ }
342
+
343
+ // if audio length is more than kRestartRecording_s seconds, restart recording
344
+ if (audio != null && audio.length > kSampleRate*kRestartRecording_s) {
345
+ if (doRecording) {
346
+ //printTextarea('js: restarting recording');
347
+
348
+ clearInterval(interval);
349
+ audio0 = audio;
350
+ audio = null;
351
+ mediaRecorder.stop();
352
+ stream.getTracks().forEach(function(track) {
353
+ track.stop();
354
+ });
355
+ }
356
+ }
357
+ }, 100);
358
+ }
359
+
360
+ //
361
+ // main
362
+ //
363
+
364
+ var nLines = 0;
365
+ var intervalUpdate = null;
366
+ var transcribedAll = '';
367
+
368
+ function onStart() {
369
+ if (!instance) {
370
+ instance = Module.init('whisper.bin');
371
+
372
+ if (instance) {
373
+ printTextarea("js: whisper initialized, instance: " + instance);
374
+ }
375
+ }
376
+
377
+ if (!instance) {
378
+ printTextarea("js: failed to initialize whisper");
379
+ return;
380
+ }
381
+
382
+ startRecording();
383
+
384
+ intervalUpdate = setInterval(function() {
385
+ var transcribed = Module.get_transcribed();
386
+
387
+ if (transcribed != null && transcribed.length > 1) {
388
+ transcribedAll += transcribed + '<br>';
389
+ nLines++;
390
+
391
+ // if more than 10 lines, remove the first line
392
+ if (nLines > 10) {
393
+ var i = transcribedAll.indexOf('<br>');
394
+ if (i > 0) {
395
+ transcribedAll = transcribedAll.substring(i + 4);
396
+ nLines--;
397
+ }
398
+ }
399
+ }
400
+
401
+ document.getElementById('state-status').innerHTML = Module.get_status();
402
+ document.getElementById('state-transcribed').innerHTML = transcribedAll;
403
+ }, 100);
404
+ }
405
+
406
+ function onStop() {
407
+ stopRecording();
408
+ }
409
+
410
+ </script>
411
+ <script type="text/javascript" src="command.js"></script>
412
+ </body>
413
+ </html>
libcommand.worker.js ADDED
@@ -0,0 +1 @@
 
 
1
+ "use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:function(f){(0,eval)(fs.readFileSync(f,"utf8"))},postMessage:function(msg){parentPort.postMessage(msg)},performance:global.performance||{now:function(){return Date.now()}}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=((info,receiveInstance)=>{var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports});self.onmessage=(e=>{try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0,1);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["__emscripten_thread_exit"](result)}}catch(ex){if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["__emscripten_thread_exit"](ex.status)}}else{throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else if(e.data.cmd==="processProxyingQueue"){if(Module["_pthread_self"]()){Module["_emscripten_proxy_execute_queue"](e.data.queue)}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}});