Merge pull request #800 from gradio-app/streaming_audio

Streaming audio
This commit is contained in:
Abubakar Abid 2022-03-29 08:11:14 -07:00 committed by GitHub
commit 9afdf09075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 428 additions and 81 deletions

2
demo/streaming_stt/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pbmm
*.scorer

View File

@ -0,0 +1 @@
deepspeech==0.8.2

43
demo/streaming_stt/run.py Normal file
View File

@ -0,0 +1,43 @@
from deepspeech import Model
import gradio as gr
import scipy.io.wavfile
import numpy as np
model_file_path = "deepspeech-0.8.2-models.pbmm"
lm_file_path = "deepspeech-0.8.2-models.scorer"
beam_width = 100
lm_alpha = 0.93
lm_beta = 1.18
model = Model(model_file_path)
model.enableExternalScorer(lm_file_path)
model.setScorerAlphaBeta(lm_alpha, lm_beta)
model.setBeamWidth(beam_width)
def reformat_freq(sr, y):
if sr not in (
48000,
16000,
): # Deepspeech only supports 16k, (we convert 48k -> 16k)
raise ValueError("Unsupported rate", sr)
if sr == 48000:
y = (
((y / max(np.max(y), 1)) * 32767)
.reshape((-1, 3))
.mean(axis=1)
.astype("int16")
)
sr = 16000
return sr, y
def transcribe(speech, stream):
_, y = reformat_freq(*speech)
if stream is None:
stream = model.createStream()
stream.feedAudioContent(y)
text = stream.intermediateDecode()
return text, stream
gr.Interface(transcribe, ["microphone", "state"], ["text", "state"], live=True).launch()

View File

@ -0,0 +1,3 @@
wget https://github.com/mozilla/DeepSpeech/releases/download/v0.8.2/deepspeech-0.8.2-models.pbmm
wget https://github.com/mozilla/DeepSpeech/releases/download/v0.8.2/deepspeech-0.8.2-models.scorer
apt install libasound2-dev portaudio19-dev libportaudio2 libportaudiocpp0 ffmpeg

View File

@ -0,0 +1 @@
deepspeech==0.8.2

View File

@ -0,0 +1,43 @@
from deepspeech import Model
import gradio as gr
import scipy.io.wavfile
import numpy as np
model_file_path = "deepspeech-0.8.2-models.pbmm"
lm_file_path = "deepspeech-0.8.2-models.scorer"
beam_width = 100
lm_alpha = 0.93
lm_beta = 1.18
model = Model(model_file_path)
model.enableExternalScorer(lm_file_path)
model.setScorerAlphaBeta(lm_alpha, lm_beta)
model.setBeamWidth(beam_width)
def reformat_freq(sr, y):
if sr not in (
48000,
16000,
): # Deepspeech only supports 16k, (we convert 48k -> 16k)
raise ValueError("Unsupported rate", sr)
if sr == 48000:
y = (
((y / max(np.max(y), 1)) * 32767)
.reshape((-1, 3))
.mean(axis=1)
.astype("int16")
)
sr = 16000
return sr, y
def transcribe(speech, stream):
_, y = reformat_freq(*speech)
if stream is None:
stream = model.createStream()
stream.feedAudioContent(y)
text = stream.intermediateDecode()
return text, stream
gr.Interface(transcribe, ["microphone", "state"], ["text", "state"], live=True).launch()

View File

@ -72,6 +72,7 @@ class PredictBody(BaseModel):
data: List[Any]
state: Optional[Any]
fn_index: Optional[int]
cleared: Optional[bool]
class FlagData(BaseModel):
@ -257,10 +258,13 @@ def api_docs(request: Request):
async def predict(body: PredictBody, username: str = Depends(get_current_user)):
if app.launchable.stateful:
session_hash = body.session_hash
state = app.state_holder.get(
(session_hash, "state"), app.launchable.state_default
)
body.state = state
if body.cleared:
body.state = None
else:
state = app.state_holder.get(
(session_hash, "state"), app.launchable.state_default
)
body.state = state
try:
output = await run_in_threadpool(app.launchable.process_api, body, username)
if app.launchable.stateful:

215
ui/package-lock.json generated
View File

@ -13,9 +13,54 @@
"prettier-plugin-svelte": "^2.6.0",
"svelte": "^3.46.3",
"svelte-check": "^2.4.1",
"svelte-i18n": "^3.3.13",
"vitest": "^0.3.2"
}
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.3.tgz",
"integrity": "sha512-kP/Buv5vVFMAYLHNvvUzr0lwRTU0u2WTy44Tqwku1X3C3lJ5dKqDCYVqA8wL+Y19Bq+MwHgxqd5FZJRCIsLRyQ==",
"dependencies": {
"@formatjs/intl-localematcher": "0.2.24",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
"integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.0.18.tgz",
"integrity": "sha512-vquIzsAJJmZ5jWVH8dEgUKcbG4yu3KqtyPet+q35SW5reLOvblkfeCXTRW2TpIwNXzdVqsJBwjbTiRiSU9JxwQ==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.3",
"@formatjs/icu-skeleton-parser": "1.3.5",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.5.tgz",
"integrity": "sha512-Nhyo2/6kG7ZfgeEfo02sxviOuBcvtzH6SYUharj3DLCDJH3A/4OxkKcmx/2PWGX4bc6iSieh+FA94CsKDxnZBQ==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.3",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.2.24",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.24.tgz",
"integrity": "sha512-K/HRGo6EMnCbhpth/y3u4rW4aXkmQNqRe1L2G+Y5jNr3v0gYhvaucV8WixNju/INAMbPBlbsRBRo/nfjnoOnxQ==",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -215,6 +260,14 @@
"node": ">=0.12"
}
},
"node_modules/deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -546,6 +599,11 @@
"node": ">=12"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@ -641,6 +699,16 @@
"node": ">= 6"
}
},
"node_modules/globalyzer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="
},
"node_modules/globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
},
"node_modules/graceful-fs": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
@ -686,6 +754,17 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/intl-messageformat": {
"version": "9.11.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.11.4.tgz",
"integrity": "sha512-77TSkNubIy/hsapz6LQpyR6OADcxhWdhSaboPb5flMaALCVkPvAIxr48AlPqaMl4r1anNcvR9rpLWVdwUY1IKg==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.3",
"@formatjs/fast-memoize": "1.2.1",
"@formatjs/icu-messageformat-parser": "2.0.18",
"tslib": "^2.1.0"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -1156,6 +1235,27 @@
"svelte": "^3.24.0"
}
},
"node_modules/svelte-i18n": {
"version": "3.3.13",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.3.13.tgz",
"integrity": "sha512-RQM+ys4+Y9ztH//tX22H1UL2cniLNmIR+N4xmYygV6QpQ6EyQvloZiENRew8XrVzfvJ8HaE8NU6/yurLkl7z3g==",
"dependencies": {
"deepmerge": "^4.2.2",
"estree-walker": "^2.0.1",
"intl-messageformat": "^9.3.15",
"sade": "^1.7.4",
"tiny-glob": "^0.2.6"
},
"bin": {
"svelte-i18n": "dist/cli.js"
},
"engines": {
"node": ">= 11.15.0"
},
"peerDependencies": {
"svelte": "^3.25.1"
}
},
"node_modules/svelte-preprocess": {
"version": "4.10.3",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.3.tgz",
@ -1221,6 +1321,15 @@
}
}
},
"node_modules/tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
"dependencies": {
"globalyzer": "0.1.0",
"globrex": "^0.1.2"
}
},
"node_modules/tinypool": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.1.2.tgz",
@ -1248,6 +1357,11 @@
"node": ">=8.0"
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@ -1354,6 +1468,50 @@
}
},
"dependencies": {
"@formatjs/ecma402-abstract": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.3.tgz",
"integrity": "sha512-kP/Buv5vVFMAYLHNvvUzr0lwRTU0u2WTy44Tqwku1X3C3lJ5dKqDCYVqA8wL+Y19Bq+MwHgxqd5FZJRCIsLRyQ==",
"requires": {
"@formatjs/intl-localematcher": "0.2.24",
"tslib": "^2.1.0"
}
},
"@formatjs/fast-memoize": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
"integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
"requires": {
"tslib": "^2.1.0"
}
},
"@formatjs/icu-messageformat-parser": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.0.18.tgz",
"integrity": "sha512-vquIzsAJJmZ5jWVH8dEgUKcbG4yu3KqtyPet+q35SW5reLOvblkfeCXTRW2TpIwNXzdVqsJBwjbTiRiSU9JxwQ==",
"requires": {
"@formatjs/ecma402-abstract": "1.11.3",
"@formatjs/icu-skeleton-parser": "1.3.5",
"tslib": "^2.1.0"
}
},
"@formatjs/icu-skeleton-parser": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.5.tgz",
"integrity": "sha512-Nhyo2/6kG7ZfgeEfo02sxviOuBcvtzH6SYUharj3DLCDJH3A/4OxkKcmx/2PWGX4bc6iSieh+FA94CsKDxnZBQ==",
"requires": {
"@formatjs/ecma402-abstract": "1.11.3",
"tslib": "^2.1.0"
}
},
"@formatjs/intl-localematcher": {
"version": "0.2.24",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.24.tgz",
"integrity": "sha512-K/HRGo6EMnCbhpth/y3u4rW4aXkmQNqRe1L2G+Y5jNr3v0gYhvaucV8WixNju/INAMbPBlbsRBRo/nfjnoOnxQ==",
"requires": {
"tslib": "^2.1.0"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1506,6 +1664,11 @@
"type-detect": "^4.0.0"
}
},
"deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
},
"detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -1656,6 +1819,11 @@
"integrity": "sha512-8Sbo0zpzgwWrwjQYLmHF78f7E2xg5Ve63bjB2ng3V2aManilnnTGaliq2snYg+NOX60+hEvJHRdVnuIAHW0lVw==",
"optional": true
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@ -1726,6 +1894,16 @@
"is-glob": "^4.0.1"
}
},
"globalyzer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="
},
"globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
},
"graceful-fs": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
@ -1762,6 +1940,17 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"intl-messageformat": {
"version": "9.11.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.11.4.tgz",
"integrity": "sha512-77TSkNubIy/hsapz6LQpyR6OADcxhWdhSaboPb5flMaALCVkPvAIxr48AlPqaMl4r1anNcvR9rpLWVdwUY1IKg==",
"requires": {
"@formatjs/ecma402-abstract": "1.11.3",
"@formatjs/fast-memoize": "1.2.1",
"@formatjs/icu-messageformat-parser": "2.0.18",
"tslib": "^2.1.0"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -2070,6 +2259,18 @@
"typescript": "*"
}
},
"svelte-i18n": {
"version": "3.3.13",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.3.13.tgz",
"integrity": "sha512-RQM+ys4+Y9ztH//tX22H1UL2cniLNmIR+N4xmYygV6QpQ6EyQvloZiENRew8XrVzfvJ8HaE8NU6/yurLkl7z3g==",
"requires": {
"deepmerge": "^4.2.2",
"estree-walker": "^2.0.1",
"intl-messageformat": "^9.3.15",
"sade": "^1.7.4",
"tiny-glob": "^0.2.6"
}
},
"svelte-preprocess": {
"version": "4.10.3",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.3.tgz",
@ -2083,6 +2284,15 @@
"strip-indent": "^3.0.0"
}
},
"tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
"requires": {
"globalyzer": "0.1.0",
"globrex": "^0.1.2"
}
},
"tinypool": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.1.2.tgz",
@ -2101,6 +2311,11 @@
"is-number": "^7.0.0"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",

View File

@ -36,6 +36,7 @@
let queue_index: number | null = null;
let initial_queue_index: number | null = null;
let just_flagged: boolean = false;
let cleared_since_last_submit = false;
const default_inputs: Array<unknown> = input_components.map((component) =>
"default" in component ? component.default : null
@ -52,12 +53,12 @@
let expected_duration: number | null = null;
let example_id: number | null = null;
const setValues = (index: number, value: unknown) => {
const setValues = async (index: number, value: unknown) => {
example_id = null;
has_changed = true;
input_values[index] = value;
if (live && state !== "PENDING") {
submit();
await submit();
}
};
@ -90,7 +91,7 @@
clearInterval(timer);
};
const submit = () => {
const submit = async () => {
if (state === "PENDING") {
return;
}
@ -108,64 +109,67 @@
has_changed = false;
let submission_count_at_click = submission_count;
startTimer();
fn(
"predict",
{ data: input_values, example_id: example_id },
queue,
queueCallback
)
.then((output) => {
if (
state !== "PENDING" ||
submission_count_at_click !== submission_count
) {
return;
}
stopTimer();
output_values = output["data"];
for (let [i, value] of output_values.entries()) {
if (output_components[i].name === "state") {
for (let [j, input_component] of input_components.entries()) {
if (input_component.name === "state") {
input_values[j] = value;
}
}
let output: any;
try {
output = await fn(
"predict",
{
data: input_values,
cleared: cleared_since_last_submit,
example_id: example_id
},
queue,
queueCallback
);
} catch (e) {
if (
state !== "PENDING" ||
submission_count_at_click !== submission_count
) {
return;
}
stopTimer();
console.error(e);
state = "ERROR";
output_values = deepCopy(default_outputs);
}
if (state !== "PENDING" || submission_count_at_click !== submission_count) {
return;
}
stopTimer();
output_values = output["data"];
cleared_since_last_submit = false;
for (let [i, value] of output_values.entries()) {
if (output_components[i].name === "state") {
for (let [j, input_component] of input_components.entries()) {
if (input_component.name === "state") {
input_values[j] = value;
}
}
if ("durations" in output) {
last_duration = output["durations"][0];
}
if ("avg_durations" in output) {
avg_duration = output["avg_durations"][0];
if (queue && initial_queue_index) {
expected_duration = avg_duration * (initial_queue_index + 1);
} else {
expected_duration = avg_duration;
}
}
state = "COMPLETE";
if (live && has_changed) {
submit();
}
})
.catch((e) => {
if (
state !== "PENDING" ||
submission_count_at_click !== submission_count
) {
return;
}
stopTimer();
console.error(e);
state = "ERROR";
output_values = deepCopy(default_outputs);
});
}
}
if ("durations" in output) {
last_duration = output["durations"][0];
}
if ("avg_durations" in output) {
avg_duration = output["avg_durations"][0];
if (queue && initial_queue_index) {
expected_duration = avg_duration * (initial_queue_index + 1);
} else {
expected_duration = avg_duration;
}
}
state = "COMPLETE";
if (live && has_changed) {
await submit();
}
};
const clear = () => {
input_values = deepCopy(default_inputs);
output_values = deepCopy(default_outputs);
interpret_mode = false;
state = "START";
cleared_since_last_submit = true;
stopTimer();
};
const flag = (flag_option: string | null) => {
@ -231,6 +235,7 @@
{...input_component}
{theme}
{static_src}
{live}
value={input_values[i]}
interpretation={interpret_mode
? interpretation_values[i]
@ -248,19 +253,21 @@
>
{$_("interface.clear")}
</button>
<button
class="panel-button submit bg-gray-50 dark:bg-gray-700 flex-1 p-3 rounded transition font-semibold focus:outline-none"
on:click={submit}
>
{$_("interface.submit")}
</button>
{#if !live}
<button
class="panel-button submit bg-gray-50 dark:bg-gray-700 flex-1 p-3 rounded transition font-semibold focus:outline-none"
on:click={submit}
>
{$_("interface.submit")}
</button>
{/if}
</div>
</div>
<div class="panel flex-1">
<div
class="component-set p-2 rounded flex flex-col flex-1 gap-2 relative"
style="min-height: 36px"
class:opacity-50={state === "PENDING"}
class:opacity-50={state === "PENDING" && !live}
>
{#if state !== "START"}
<div class="state absolute right-2 flex items-center gap-0.5 text-xs">

View File

@ -9,6 +9,7 @@
import { _ } from "svelte-i18n";
export let value: null | Value;
export let live: boolean;
export let setValue: (val: typeof value) => typeof value;
export let theme: string;
export let name: string;
@ -20,37 +21,54 @@
let recorder: MediaRecorder;
let mode = "";
let audio_chunks: Array<Blob> = [];
let chunks_at_submit: number = 0;
let audio_blob;
let player;
let inited = false;
let crop_values = [0, 100];
let submitting_data = false;
let record_interval;
function blob_to_data_url(blob: Blob): Promise<string> {
return new Promise((fulfill, reject) => {
let reader = new FileReader();
reader.onerror = reject;
reader.onload = (e) => fulfill(reader.result as string);
reader.readAsDataURL(blob);
});
async function generate_data(): Promise<{
data: string;
name: string;
is_example: boolean;
}> {
function blob_to_data_url(blob: Blob): Promise<string> {
return new Promise((fulfill, reject) => {
let reader = new FileReader();
reader.onerror = reject;
reader.onload = (e) => fulfill(reader.result as string);
reader.readAsDataURL(blob);
});
}
audio_blob = new Blob(audio_chunks, { type: "audio/wav" });
return {
data: await blob_to_data_url(audio_blob),
name,
is_example
};
}
async function prepare_audio() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
recorder = new MediaRecorder(stream);
recorder.addEventListener("dataavailable", (event) => {
recorder.addEventListener("dataavailable", async (event) => {
audio_chunks.push(event.data);
if (live && !submitting_data) {
submitting_data = true;
chunks_at_submit = audio_chunks.length;
await setValue(await generate_data());
submitting_data = false;
audio_chunks = audio_chunks.slice(chunks_at_submit);
}
});
recorder.addEventListener("stop", async () => {
recording = false;
audio_blob = new Blob(audio_chunks, { type: "audio/wav" });
setValue({
data: await blob_to_data_url(audio_blob),
name,
is_example
});
if (!live) {
setValue(await generate_data());
}
});
}
@ -61,6 +79,12 @@
if (!inited) await prepare_audio();
recorder.start();
if (live) {
record_interval = setInterval(() => {
recorder.stop();
recorder.start();
}, 1000);
}
}
onDestroy(() => {
@ -70,7 +94,11 @@
});
const stop = () => {
recording = false;
recorder.stop();
if (live) {
clearInterval(record_interval);
}
};
function clear() {
@ -117,7 +145,7 @@
</script>
<div class="input-audio">
{#if value === null}
{#if value === null || (source === "microphone" && live)}
{#if source === "microphone"}
{#if recording}
<button