Spaces:
Sleeping
Sleeping
Yurii Paniv
commited on
Commit
•
773a50a
1
Parent(s):
ea0a199
Delete telegram bot and web version
Browse files- Dockerfile +0 -9
- heroku.yml +0 -3
- webapp/.dockerignore +0 -7
- webapp/app.ini +0 -8
- webapp/client.py +0 -43
- webapp/main.py +0 -96
- webapp/requirements.txt +0 -6
- webapp/static/main.js +0 -78
- webapp/static/recorder.js +0 -395
- webapp/templates/hello.html +0 -47
Dockerfile
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
FROM python:3.7
|
2 |
-
COPY ./webapp /app
|
3 |
-
WORKDIR /app
|
4 |
-
RUN apt-get update
|
5 |
-
RUN apt-get install -y ffmpeg
|
6 |
-
RUN wget https://github.com/robinhad/voice-recognition-ua/releases/download/v0.4/uk.tflite
|
7 |
-
# RUN wget https://github.com/robinhad/voice-recognition-ua/releases/download/v0.4/kenlm.scorer
|
8 |
-
RUN pip install -r requirements.txt
|
9 |
-
CMD uwsgi app.ini --http 0.0.0.0:$PORT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
heroku.yml
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
build:
|
2 |
-
docker:
|
3 |
-
web: Dockerfile
|
|
|
|
|
|
|
|
webapp/.dockerignore
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
__pycache__/
|
2 |
-
.venv/
|
3 |
-
.env
|
4 |
-
.git
|
5 |
-
LICENSE
|
6 |
-
README.md
|
7 |
-
.github/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/app.ini
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
[uwsgi]
|
2 |
-
wsgi-file = main.py
|
3 |
-
callable = app
|
4 |
-
http = :5000
|
5 |
-
processes = 1
|
6 |
-
master = true
|
7 |
-
vacuum = true
|
8 |
-
die-on-term = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/client.py
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python
|
2 |
-
# -*- coding: utf-8 -*-
|
3 |
-
from __future__ import absolute_import, division, print_function
|
4 |
-
|
5 |
-
import numpy as np
|
6 |
-
import sys
|
7 |
-
import wave
|
8 |
-
|
9 |
-
from deepspeech import Model, version
|
10 |
-
from timeit import default_timer as timer
|
11 |
-
|
12 |
-
|
13 |
-
def client(audio_file, lang="uk"):
|
14 |
-
model_load_start = timer()
|
15 |
-
# sphinx-doc: python_ref_model_start
|
16 |
-
model = "./uk.tflite"
|
17 |
-
|
18 |
-
ds = Model(model)
|
19 |
-
# ds.enableExternalScorer("kenlm.scorer")
|
20 |
-
# sphinx-doc: python_ref_model_stop
|
21 |
-
model_load_end = timer() - model_load_start
|
22 |
-
print('Loaded model in {:.3}s.'.format(model_load_end), file=sys.stderr)
|
23 |
-
|
24 |
-
desired_sample_rate = ds.sampleRate()
|
25 |
-
|
26 |
-
fin = wave.open(audio_file, 'rb')
|
27 |
-
fs_orig = fin.getframerate()
|
28 |
-
audio = np.frombuffer(fin.readframes(fin.getnframes()), np.int16)
|
29 |
-
|
30 |
-
audio_length = fin.getnframes() * (1/fs_orig)
|
31 |
-
fin.close()
|
32 |
-
|
33 |
-
print('Running inference.', file=sys.stderr)
|
34 |
-
inference_start = timer()
|
35 |
-
# sphinx-doc: python_ref_inference_start
|
36 |
-
|
37 |
-
result = ds.stt(audio)
|
38 |
-
print(result)
|
39 |
-
# sphinx-doc: python_ref_inference_stop
|
40 |
-
inference_end = timer() - inference_start
|
41 |
-
print('Inference took %0.3fs for %0.3fs audio file.' %
|
42 |
-
(inference_end, audio_length), file=sys.stderr)
|
43 |
-
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/main.py
DELETED
@@ -1,96 +0,0 @@
|
|
1 |
-
from flask import Flask, render_template, request
|
2 |
-
from io import BytesIO
|
3 |
-
from client import client
|
4 |
-
import telebot
|
5 |
-
import logging
|
6 |
-
import os
|
7 |
-
import warnings
|
8 |
-
from client import client
|
9 |
-
from io import BytesIO
|
10 |
-
import pydub
|
11 |
-
|
12 |
-
warnings.simplefilter('ignore')
|
13 |
-
TOKEN = os.environ['TOKEN']
|
14 |
-
|
15 |
-
if not TOKEN:
|
16 |
-
print('You must set the TOKEN environment variable')
|
17 |
-
exit(1)
|
18 |
-
|
19 |
-
START_MSG = '''Вітання!
|
20 |
-
Цей бот створений для тестування перекладу українських аудіозаписів в текст.
|
21 |
-
Група для обговорення: https://t.me/speech_recognition_uk'''
|
22 |
-
|
23 |
-
FIRST_STEP = '''Використовувати бота просто: надішліть аудіоповідомлення і чекайте відповіді'''
|
24 |
-
|
25 |
-
|
26 |
-
bot = telebot.TeleBot(TOKEN, parse_mode=None, threaded=False)
|
27 |
-
app = Flask(__name__,)
|
28 |
-
|
29 |
-
|
30 |
-
@app.route('/')
|
31 |
-
def index():
|
32 |
-
bot.remove_webhook()
|
33 |
-
bot.set_webhook(
|
34 |
-
url='https://voice-recognition-ua.herokuapp.com/' + TOKEN)
|
35 |
-
return render_template('hello.html')
|
36 |
-
|
37 |
-
|
38 |
-
@app.route('/recognize', methods=["POST"])
|
39 |
-
def recognize():
|
40 |
-
file = request.files['file']
|
41 |
-
lang = request.form["lang"]
|
42 |
-
audio = BytesIO()
|
43 |
-
file.save(audio)
|
44 |
-
audio.seek(0)
|
45 |
-
result = client(audio, lang)
|
46 |
-
return result
|
47 |
-
|
48 |
-
|
49 |
-
@app.route('/' + TOKEN, methods=['POST'])
|
50 |
-
def getMessage():
|
51 |
-
bot.process_new_updates(
|
52 |
-
[telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
|
53 |
-
return "!", 200
|
54 |
-
|
55 |
-
|
56 |
-
@bot.message_handler(commands=['start', 'help'])
|
57 |
-
def send_welcome(message):
|
58 |
-
bot.reply_to(message, START_MSG)
|
59 |
-
bot.reply_to(message, FIRST_STEP)
|
60 |
-
|
61 |
-
|
62 |
-
@bot.message_handler(content_types=['voice'])
|
63 |
-
def process_voice_message(message):
|
64 |
-
# download the recording
|
65 |
-
file_info = bot.get_file(message.voice.file_id)
|
66 |
-
downloaded_file = bot.download_file(file_info.file_path)
|
67 |
-
# create in-memory representation of files
|
68 |
-
source_audio = BytesIO()
|
69 |
-
source_audio.write(downloaded_file)
|
70 |
-
source_audio.seek(0)
|
71 |
-
output_audio = BytesIO()
|
72 |
-
ogg_file = pydub.AudioSegment.from_ogg(
|
73 |
-
source_audio)
|
74 |
-
|
75 |
-
# convert ogg to wav
|
76 |
-
ogg_file.set_frame_rate(16000).set_channels(
|
77 |
-
1).export(output_audio, "wav", codec="pcm_s16le")
|
78 |
-
output_audio.seek(0)
|
79 |
-
|
80 |
-
# do the recognition
|
81 |
-
# get the recognized text
|
82 |
-
try:
|
83 |
-
text = client(output_audio)
|
84 |
-
# no results
|
85 |
-
if not text:
|
86 |
-
bot.reply_to(message, 'Я не зміг розпізнати 😢')
|
87 |
-
else:
|
88 |
-
# send the recognized text
|
89 |
-
bot.reply_to(message, text)
|
90 |
-
except Exception as e:
|
91 |
-
logging.log(logging.ERROR, str(e))
|
92 |
-
bot.reply_to(message, 'Трапилась помилка 😢')
|
93 |
-
|
94 |
-
|
95 |
-
if __name__ == '__main__':
|
96 |
-
app.run(host='0.0.0.0')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/requirements.txt
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
Flask==1.1.2
|
2 |
-
deepspeech-tflite==0.9.3
|
3 |
-
numpy==1.17.0
|
4 |
-
uwsgi==2.0.19.1
|
5 |
-
pytelegrambotapi==3.7.6
|
6 |
-
pydub==0.24.1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/static/main.js
DELETED
@@ -1,78 +0,0 @@
|
|
1 |
-
// part of script inspired by https://github.com/addpipe/simple-recorderjs-demo
|
2 |
-
var gumStream; //stream from getUserMedia()
|
3 |
-
var rec; //Recorder.js object
|
4 |
-
var input; //MediaStreamAudioSourceNode we'll be recording
|
5 |
-
|
6 |
-
// shim for AudioContext when it's not avb.
|
7 |
-
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
8 |
-
var audioContext; //audio context to help us record
|
9 |
-
const resultNode = document.getElementById('result');
|
10 |
-
const actionButton = document.getElementById('action');
|
11 |
-
const langSelector = document.getElementById('lang');
|
12 |
-
|
13 |
-
function resultProcess(data) {
|
14 |
-
resultNode.textContent = `Довжина тексту: ${data.length} \n
|
15 |
-
Текст: ${data}
|
16 |
-
`;
|
17 |
-
actionButton.textContent = "Почати запис (3 сек)";
|
18 |
-
actionButton.disabled = false;
|
19 |
-
}
|
20 |
-
|
21 |
-
function exportWAV(blob) {
|
22 |
-
actionButton.textContent = "Обробляється..."
|
23 |
-
var data = new FormData()
|
24 |
-
data.append('file', blob);
|
25 |
-
data.append("lang", langSelector.value);
|
26 |
-
fetch(`./recognize`, { method: "POST", body: data })
|
27 |
-
.then(response => response.text())
|
28 |
-
.then(resultProcess);
|
29 |
-
}
|
30 |
-
function record() {
|
31 |
-
|
32 |
-
var constraints = { audio: true, video: false }
|
33 |
-
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
|
34 |
-
actionButton.textContent = "Запис...";
|
35 |
-
resultNode.textContent = "";
|
36 |
-
actionButton.disabled = true;
|
37 |
-
/*
|
38 |
-
create an audio context after getUserMedia is called
|
39 |
-
sampleRate might change after getUserMedia is called, like it does on macOS when recording through AirPods
|
40 |
-
the sampleRate defaults to the one set in your OS for your playback device
|
41 |
-
*/
|
42 |
-
audioContext = new AudioContext();
|
43 |
-
|
44 |
-
/* assign to gumStream for later use */
|
45 |
-
gumStream = stream;
|
46 |
-
|
47 |
-
/* use the stream */
|
48 |
-
input = audioContext.createMediaStreamSource(stream);
|
49 |
-
|
50 |
-
/*
|
51 |
-
Create the Recorder object and configure to record mono sound (1 channel)
|
52 |
-
Recording 2 channels will double the file size
|
53 |
-
*/
|
54 |
-
rec = new Recorder(input, { numChannels: 1 })
|
55 |
-
|
56 |
-
//start the recording process
|
57 |
-
rec.record()
|
58 |
-
sleep(3000).then(stop);
|
59 |
-
})
|
60 |
-
}
|
61 |
-
|
62 |
-
|
63 |
-
function stop() {
|
64 |
-
rec.stop();
|
65 |
-
|
66 |
-
//stop microphone access
|
67 |
-
gumStream.getAudioTracks()[0].stop();
|
68 |
-
|
69 |
-
//create the wav blob and pass it on to createDownloadLink
|
70 |
-
rec.exportWAV(exportWAV);
|
71 |
-
}
|
72 |
-
|
73 |
-
|
74 |
-
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
|
75 |
-
|
76 |
-
async function handleAction() {
|
77 |
-
record();
|
78 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/static/recorder.js
DELETED
@@ -1,395 +0,0 @@
|
|
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 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/templates/hello.html
DELETED
@@ -1,47 +0,0 @@
|
|
1 |
-
<!DOCTYPE html>
|
2 |
-
<html lang="en">
|
3 |
-
|
4 |
-
<head>
|
5 |
-
<meta charset="UTF-8">
|
6 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
-
<title>Розпізнавання української мови</title>
|
8 |
-
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
9 |
-
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
10 |
-
</head>
|
11 |
-
|
12 |
-
<body>
|
13 |
-
<div class="container">
|
14 |
-
<div class="col-12 col-md-8 col-sm-12 col-xl-6 mx-auto text-center">
|
15 |
-
<h1>Демо розпізнавання української мови</h1>
|
16 |
-
<p>Говоріть 3 секунди після натискання на кнопку, тоді отримаєте результат</p>
|
17 |
-
<div class="row no-gutters">
|
18 |
-
<div class="col-1 col-sm-3"> </div>
|
19 |
-
<div class="col-6">
|
20 |
-
<button class="btn btn-primary" id="action" onclick="handleAction()">Почати запис (3 сек)</button>
|
21 |
-
</div>
|
22 |
-
<div class="col-5 col-sm-3">
|
23 |
-
<select id="lang" class="browser-default custom-select" style="width: 100%">
|
24 |
-
<option selected value="uk">Українська</option>
|
25 |
-
</select>
|
26 |
-
</div>
|
27 |
-
</div>
|
28 |
-
<div id="result"></div>
|
29 |
-
<br>
|
30 |
-
<a href="https://commonvoice.mozilla.org/uk" target="_blank" class="btn btn-success" id="action">Долучитись
|
31 |
-
до створення відкритого
|
32 |
-
набору<br> записів української мови</a>
|
33 |
-
<br>
|
34 |
-
<div style="margin-top: 10px" class="alert alert-warning" role="alert">
|
35 |
-
Дисклеймер: через малу кількість даних для навчання нейромережі
|
36 |
-
результат може бути неточний:
|
37 |
-
<hr style="border-top-color:black">
|
38 |
-
сів метелик на травичку -> сі вметелик на требичко <br>
|
39 |
-
better late than never -> better waits and never
|
40 |
-
</div>
|
41 |
-
</div>
|
42 |
-
</div>
|
43 |
-
<script src="/static/recorder.js"></script>
|
44 |
-
<script src="/static/main.js"></script>
|
45 |
-
</body>
|
46 |
-
|
47 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|