Yurii Paniv commited on
Commit
9a0bda1
1 Parent(s): 593d0e2

Fix sample_rate bug

Browse files
Files changed (4) hide show
  1. .gitignore +2 -1
  2. Dockerfile +0 -2
  3. static/recorder.js +395 -0
  4. templates/hello.html +1 -1
.gitignore CHANGED
@@ -128,4 +128,5 @@ dmypy.json
128
  # Pyre type checker
129
  .pyre/
130
 
131
- *.tflite
 
 
128
  # Pyre type checker
129
  .pyre/
130
 
131
+ *.tflite
132
+ .DS_Store
Dockerfile CHANGED
@@ -1,5 +1,3 @@
1
- # make sure to download https://github.com/robinhad/voice-recognition-ua/releases/download/0.1/uk.tflite
2
- # before build
3
  FROM python:3.7
4
  COPY . /app
5
  WORKDIR /app
 
 
 
1
  FROM python:3.7
2
  COPY . /app
3
  WORKDIR /app
static/recorder.js ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //code taken from https://github.com/mattdiamond/Recorderjs/blob/master/src/recorder.js
2
+ (function (f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f() } else if (typeof define === "function" && define.amd) { define([], f) } else { var g; if (typeof window !== "undefined") { g = window } else if (typeof global !== "undefined") { g = global } else if (typeof self !== "undefined") { g = self } else { g = this } g.Recorder = f() } })(function () {
3
+ var define, module, exports; return (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++)s(r[o]); return s })({
4
+ 1: [function (require, module, exports) {
5
+ "use strict";
6
+
7
+ module.exports = require("./recorder").Recorder;
8
+
9
+ }, { "./recorder": 2 }], 2: [function (require, module, exports) {
10
+ 'use strict';
11
+
12
+ var _createClass = (function () {
13
+ function defineProperties(target, props) {
14
+ for (var i = 0; i < props.length; i++) {
15
+ var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor);
16
+ }
17
+ } return function (Constructor, protoProps, staticProps) {
18
+ if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor;
19
+ };
20
+ })();
21
+
22
+ Object.defineProperty(exports, "__esModule", {
23
+ value: true
24
+ });
25
+ exports.Recorder = undefined;
26
+
27
+ var _inlineWorker = require('inline-worker');
28
+
29
+ var _inlineWorker2 = _interopRequireDefault(_inlineWorker);
30
+
31
+ function _interopRequireDefault(obj) {
32
+ return obj && obj.__esModule ? obj : { default: obj };
33
+ }
34
+
35
+ function _classCallCheck(instance, Constructor) {
36
+ if (!(instance instanceof Constructor)) {
37
+ throw new TypeError("Cannot call a class as a function");
38
+ }
39
+ }
40
+
41
+ var Recorder = exports.Recorder = (function () {
42
+ function Recorder(source, cfg) {
43
+ var _this = this;
44
+
45
+ _classCallCheck(this, Recorder);
46
+
47
+ this.config = {
48
+ bufferLen: 4096,
49
+ numChannels: 2,
50
+ mimeType: 'audio/wav'
51
+ };
52
+ this.recording = false;
53
+ this.callbacks = {
54
+ getBuffer: [],
55
+ exportWAV: []
56
+ };
57
+
58
+ Object.assign(this.config, cfg);
59
+ this.context = source.context;
60
+ this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels);
61
+
62
+ this.node.onaudioprocess = function (e) {
63
+ if (!_this.recording) return;
64
+
65
+ var buffer = [];
66
+ for (var channel = 0; channel < _this.config.numChannels; channel++) {
67
+ buffer.push(e.inputBuffer.getChannelData(channel));
68
+ }
69
+ _this.worker.postMessage({
70
+ command: 'record',
71
+ buffer: buffer
72
+ });
73
+ };
74
+
75
+ source.connect(this.node);
76
+ this.node.connect(this.context.destination); //this should not be necessary
77
+
78
+ var self = {};
79
+ this.worker = new _inlineWorker2.default(function () {
80
+ var recLength = 0,
81
+ recBuffers = [],
82
+ sampleRate = undefined,
83
+ numChannels = undefined;
84
+
85
+ self.onmessage = function (e) {
86
+ switch (e.data.command) {
87
+ case 'init':
88
+ init(e.data.config);
89
+ break;
90
+ case 'record':
91
+ record(e.data.buffer);
92
+ break;
93
+ case 'exportWAV':
94
+ exportWAV(e.data.type);
95
+ break;
96
+ case 'getBuffer':
97
+ getBuffer();
98
+ break;
99
+ case 'clear':
100
+ clear();
101
+ break;
102
+ }
103
+ };
104
+
105
+ function init(config) {
106
+ sampleRate = config.sampleRate;
107
+ numChannels = config.numChannels;
108
+ initBuffers();
109
+ }
110
+
111
+ function record(inputBuffer) {
112
+ for (var channel = 0; channel < numChannels; channel++) {
113
+ recBuffers[channel].push(inputBuffer[channel]);
114
+ }
115
+ recLength += inputBuffer[0].length;
116
+ }
117
+
118
+ function exportWAV(type) {
119
+ var buffers = [];
120
+ for (var channel = 0; channel < numChannels; channel++) {
121
+ buffers.push(mergeBuffers(recBuffers[channel], recLength));
122
+ }
123
+ var interleaved = undefined;
124
+ if (numChannels === 2) {
125
+ interleaved = interleave(buffers[0], buffers[1]);
126
+ } else {
127
+ interleaved = buffers[0];
128
+ }
129
+ var downsampledBuffer = downsampleBuffer(interleaved, 16000);
130
+ var dataview = encodeWAV(downsampledBuffer);
131
+ var audioBlob = new Blob([dataview], { type: type });
132
+
133
+ self.postMessage({ command: 'exportWAV', data: audioBlob });
134
+ }
135
+
136
+ function getBuffer() {
137
+ var buffers = [];
138
+ for (var channel = 0; channel < numChannels; channel++) {
139
+ buffers.push(mergeBuffers(recBuffers[channel], recLength));
140
+ }
141
+ self.postMessage({ command: 'getBuffer', data: buffers });
142
+ }
143
+
144
+ function clear() {
145
+ recLength = 0;
146
+ recBuffers = [];
147
+ initBuffers();
148
+ }
149
+
150
+ function initBuffers() {
151
+ for (var channel = 0; channel < numChannels; channel++) {
152
+ recBuffers[channel] = [];
153
+ }
154
+ }
155
+
156
+ function mergeBuffers(recBuffers, recLength) {
157
+ var result = new Float32Array(recLength);
158
+ var offset = 0;
159
+ for (var i = 0; i < recBuffers.length; i++) {
160
+ result.set(recBuffers[i], offset);
161
+ offset += recBuffers[i].length;
162
+ }
163
+ return result;
164
+ }
165
+
166
+ function downsampleBuffer(buffer, rate) {
167
+ if (rate == sampleRate) {
168
+ return buffer;
169
+ }
170
+ if (rate > sampleRate) {
171
+ throw "downsampling rate show be smaller than original sample rate";
172
+ }
173
+ var sampleRateRatio = sampleRate / rate;
174
+ var newLength = Math.round(buffer.length / sampleRateRatio);
175
+ var result = new Float32Array(newLength);
176
+ var offsetResult = 0;
177
+ var offsetBuffer = 0;
178
+ while (offsetResult < result.length) {
179
+ var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
180
+ // Use average value of skipped samples
181
+ var accum = 0, count = 0;
182
+ for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
183
+ accum += buffer[i];
184
+ count++;
185
+ }
186
+ result[offsetResult] = accum / count;
187
+ // Or you can simply get rid of the skipped samples:
188
+ // result[offsetResult] = buffer[nextOffsetBuffer];
189
+ offsetResult++;
190
+ offsetBuffer = nextOffsetBuffer;
191
+ }
192
+ return result;
193
+ }
194
+
195
+ function interleave(inputL, inputR) {
196
+ var length = inputL.length + inputR.length;
197
+ var result = new Float32Array(length);
198
+
199
+ var index = 0,
200
+ inputIndex = 0;
201
+
202
+ while (index < length) {
203
+ result[index++] = inputL[inputIndex];
204
+ result[index++] = inputR[inputIndex];
205
+ inputIndex++;
206
+ }
207
+ return result;
208
+ }
209
+
210
+ function floatTo16BitPCM(output, offset, input) {
211
+ for (var i = 0; i < input.length; i++, offset += 2) {
212
+ var s = Math.max(-1, Math.min(1, input[i]));
213
+ output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
214
+ }
215
+ }
216
+
217
+ function writeString(view, offset, string) {
218
+ for (var i = 0; i < string.length; i++) {
219
+ view.setUint8(offset + i, string.charCodeAt(i));
220
+ }
221
+ }
222
+
223
+ function encodeWAV(samples) {
224
+ var buffer = new ArrayBuffer(44 + samples.length * 2);
225
+ var view = new DataView(buffer);
226
+
227
+ /* RIFF identifier */
228
+ writeString(view, 0, 'RIFF');
229
+ /* RIFF chunk length */
230
+ view.setUint32(4, 36 + samples.length * 2, true);
231
+ /* RIFF type */
232
+ writeString(view, 8, 'WAVE');
233
+ /* format chunk identifier */
234
+ writeString(view, 12, 'fmt ');
235
+ /* format chunk length */
236
+ view.setUint32(16, 16, true);
237
+ /* sample format (raw) */
238
+ view.setUint16(20, 1, true);
239
+ /* channel count */
240
+ view.setUint16(22, numChannels, true);
241
+ /* sample rate */
242
+ view.setUint32(24, 16000, true);
243
+ /* byte rate (sample rate * block align) */
244
+ view.setUint32(28, 16000 * 4, true);
245
+ /* sample rate */
246
+ //view.setUint32(24, sampleRate, true);
247
+ /* byte rate (sample rate * block align) */
248
+ //view.setUint32(28, sampleRate * 4, true);
249
+ /* block align (channel count * bytes per sample) */
250
+ view.setUint16(32, numChannels * 2, true);
251
+ /* bits per sample */
252
+ view.setUint16(34, 16, true);
253
+ /* data chunk identifier */
254
+ writeString(view, 36, 'data');
255
+ /* data chunk length */
256
+ view.setUint32(40, samples.length * 2, true);
257
+
258
+ floatTo16BitPCM(view, 44, samples);
259
+
260
+ return view;
261
+ }
262
+ }, self);
263
+
264
+ this.worker.postMessage({
265
+ command: 'init',
266
+ config: {
267
+ sampleRate: this.context.sampleRate,
268
+ numChannels: this.config.numChannels
269
+ }
270
+ });
271
+
272
+ this.worker.onmessage = function (e) {
273
+ var cb = _this.callbacks[e.data.command].pop();
274
+ if (typeof cb == 'function') {
275
+ cb(e.data.data);
276
+ }
277
+ };
278
+ }
279
+
280
+ _createClass(Recorder, [{
281
+ key: 'record',
282
+ value: function record() {
283
+ this.recording = true;
284
+ }
285
+ }, {
286
+ key: 'stop',
287
+ value: function stop() {
288
+ this.recording = false;
289
+ }
290
+ }, {
291
+ key: 'clear',
292
+ value: function clear() {
293
+ this.worker.postMessage({ command: 'clear' });
294
+ }
295
+ }, {
296
+ key: 'getBuffer',
297
+ value: function getBuffer(cb) {
298
+ cb = cb || this.config.callback;
299
+ if (!cb) throw new Error('Callback not set');
300
+
301
+ this.callbacks.getBuffer.push(cb);
302
+
303
+ this.worker.postMessage({ command: 'getBuffer' });
304
+ }
305
+ }, {
306
+ key: 'exportWAV',
307
+ value: function exportWAV(cb, mimeType) {
308
+ mimeType = mimeType || this.config.mimeType;
309
+ cb = cb || this.config.callback;
310
+ if (!cb) throw new Error('Callback not set');
311
+
312
+ this.callbacks.exportWAV.push(cb);
313
+
314
+ this.worker.postMessage({
315
+ command: 'exportWAV',
316
+ type: mimeType
317
+ });
318
+ }
319
+ }], [{
320
+ key: 'forceDownload',
321
+ value: function forceDownload(blob, filename) {
322
+ var url = (window.URL || window.webkitURL).createObjectURL(blob);
323
+ var link = window.document.createElement('a');
324
+ link.href = url;
325
+ link.download = filename || 'output.wav';
326
+ var click = document.createEvent("Event");
327
+ click.initEvent("click", true, true);
328
+ link.dispatchEvent(click);
329
+ }
330
+ }]);
331
+
332
+ return Recorder;
333
+ })();
334
+
335
+ exports.default = Recorder;
336
+
337
+ }, { "inline-worker": 3 }], 3: [function (require, module, exports) {
338
+ "use strict";
339
+
340
+ module.exports = require("./inline-worker");
341
+ }, { "./inline-worker": 4 }], 4: [function (require, module, exports) {
342
+ (function (global) {
343
+ "use strict";
344
+
345
+ var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
346
+
347
+ var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
348
+
349
+ var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);
350
+
351
+ var InlineWorker = (function () {
352
+ function InlineWorker(func, self) {
353
+ var _this = this;
354
+
355
+ _classCallCheck(this, InlineWorker);
356
+
357
+ if (WORKER_ENABLED) {
358
+ var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1];
359
+ var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" }));
360
+
361
+ return new global.Worker(url);
362
+ }
363
+
364
+ this.self = self;
365
+ this.self.postMessage = function (data) {
366
+ setTimeout(function () {
367
+ _this.onmessage({ data: data });
368
+ }, 0);
369
+ };
370
+
371
+ setTimeout(function () {
372
+ func.call(self);
373
+ }, 0);
374
+ }
375
+
376
+ _createClass(InlineWorker, {
377
+ postMessage: {
378
+ value: function postMessage(data) {
379
+ var _this = this;
380
+
381
+ setTimeout(function () {
382
+ _this.self.onmessage({ data: data });
383
+ }, 0);
384
+ }
385
+ }
386
+ });
387
+
388
+ return InlineWorker;
389
+ })();
390
+
391
+ module.exports = InlineWorker;
392
+ }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
393
+ }, {}]
394
+ }, {}, [1])(1)
395
+ });
templates/hello.html CHANGED
@@ -29,7 +29,7 @@
29
  <div id="result"></div>
30
  </div>
31
  </div>
32
- <script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
33
  <script src="/static/main.js"></script>
34
  </body>
35
 
 
29
  <div id="result"></div>
30
  </div>
31
  </div>
32
+ <script src="/static/recorder.js"></script>
33
  <script src="/static/main.js"></script>
34
  </body>
35