@timur00kh/whisper.wasm 0.0.7 → 0.1.0-beta.whisper.cpp-v1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +160 -24
- package/dist/audio/AudioConverter.d.ts +31 -0
- package/dist/audio/AudioConverter.d.ts.map +1 -0
- package/dist/audio/index.d.ts +10 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/types.d.ts +93 -0
- package/dist/audio/types.d.ts.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +596 -244
- package/dist/index.umd.js +12 -12
- package/dist/libmain-CKnKHQiQ.js +13 -0
- package/dist/libmain-Dz7N7fgx.mjs +2198 -0
- package/dist/utils/timeoutError.d.ts +5 -0
- package/dist/utils/timeoutError.d.ts.map +1 -0
- package/dist/whisper/TranscriptionSession.d.ts +6 -0
- package/dist/whisper/TranscriptionSession.d.ts.map +1 -1
- package/dist/whisper/WhisperWasmService.d.ts +3 -1
- package/dist/whisper/WhisperWasmService.d.ts.map +1 -1
- package/package.json +8 -5
- package/dist/libmain-D50HCaHR.js +0 -13
- package/dist/libmain-DyRJqz-4.mjs +0 -2198
package/dist/index.es.js
CHANGED
|
@@ -1,190 +1,226 @@
|
|
|
1
|
-
const
|
|
2
|
-
constructor(
|
|
3
|
-
this.level =
|
|
1
|
+
const M = class M {
|
|
2
|
+
constructor(t = M.levels.INFO, e = "") {
|
|
3
|
+
this.level = t, this.prefix = e;
|
|
4
4
|
}
|
|
5
|
-
debug(...
|
|
6
|
-
this.level <=
|
|
5
|
+
debug(...t) {
|
|
6
|
+
this.level <= M.levels.DEBUG && console.debug(`[${this.prefix}] [DEBUG]`, ...t);
|
|
7
7
|
}
|
|
8
|
-
info(...
|
|
9
|
-
this.level <=
|
|
8
|
+
info(...t) {
|
|
9
|
+
this.level <= M.levels.INFO && console.info(`[${this.prefix}] [INFO]`, ...t);
|
|
10
10
|
}
|
|
11
|
-
warn(...
|
|
12
|
-
this.level <=
|
|
11
|
+
warn(...t) {
|
|
12
|
+
this.level <= M.levels.WARN && console.warn(`[${this.prefix}] [WARN]`, ...t);
|
|
13
13
|
}
|
|
14
|
-
error(...
|
|
15
|
-
this.level <=
|
|
14
|
+
error(...t) {
|
|
15
|
+
this.level <= M.levels.ERROR && console.error(`[${this.prefix}] [ERROR]`, ...t);
|
|
16
16
|
}
|
|
17
|
-
setLevel(
|
|
18
|
-
this.level =
|
|
17
|
+
setLevel(t) {
|
|
18
|
+
this.level = t;
|
|
19
19
|
}
|
|
20
20
|
getLevel() {
|
|
21
21
|
return this.level;
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
M.levels = {
|
|
25
25
|
DEBUG: 0,
|
|
26
26
|
INFO: 1,
|
|
27
27
|
WARN: 2,
|
|
28
28
|
ERROR: 3
|
|
29
29
|
};
|
|
30
|
-
let
|
|
31
|
-
const
|
|
30
|
+
let y = M;
|
|
31
|
+
const F = async () => WebAssembly.validate(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11])), L = {
|
|
32
32
|
language: "auto",
|
|
33
33
|
threads: 4,
|
|
34
34
|
translate: !1
|
|
35
35
|
};
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
let r = 0,
|
|
40
|
-
if (
|
|
41
|
-
[r,
|
|
42
|
-
else if (
|
|
43
|
-
[
|
|
36
|
+
function q(n) {
|
|
37
|
+
const t = String(n).trim().replace(",", "."), e = t.split(":").map(Number);
|
|
38
|
+
if (e.some(Number.isNaN)) throw new Error(`Bad time: "${n}"`);
|
|
39
|
+
let r = 0, o = 0, i = 0;
|
|
40
|
+
if (e.length === 3)
|
|
41
|
+
[r, o] = e, i = parseFloat(t.split(":").pop() || "0");
|
|
42
|
+
else if (e.length === 2)
|
|
43
|
+
[o] = e, i = parseFloat(t.split(":").pop() || "0");
|
|
44
44
|
else
|
|
45
|
-
throw new Error(`Bad time format: "${
|
|
46
|
-
return Math.floor(((r * 60 +
|
|
45
|
+
throw new Error(`Bad time format: "${n}"`);
|
|
46
|
+
return Math.floor(((r * 60 + o) * 60 + i) * 1e3);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
51
|
-
throw new Error("Line does not match VTT-like pattern: " +
|
|
52
|
-
const r =
|
|
53
|
-
if (
|
|
48
|
+
function W(n) {
|
|
49
|
+
const e = /^\s*\[?\s*([0-9]{1,2}:[0-9]{2}:(?:[0-9]{2}[.,][0-9]{1,3})|[0-9]{1,2}:[0-9]{2}[.,][0-9]{1,3})\s*-->\s*([0-9]{1,2}:[0-9]{2}:(?:[0-9]{2}[.,][0-9]{1,3})|[0-9]{1,2}:[0-9]{2}[.,][0-9]{1,3})\s*\]?\s*(.*)\s*$/.exec(n);
|
|
50
|
+
if (!e)
|
|
51
|
+
throw new Error("Line does not match VTT-like pattern: " + n);
|
|
52
|
+
const r = e[1], o = e[2], i = e[3] || "", s = q(r), a = q(o);
|
|
53
|
+
if (a < s)
|
|
54
54
|
throw new Error("End time is before start time");
|
|
55
55
|
return {
|
|
56
|
-
startMs:
|
|
57
|
-
endMs:
|
|
56
|
+
startMs: s,
|
|
57
|
+
endMs: a,
|
|
58
58
|
start: r,
|
|
59
|
-
end:
|
|
59
|
+
end: o,
|
|
60
60
|
text: i
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
function
|
|
64
|
-
return new Promise((
|
|
63
|
+
function O(n) {
|
|
64
|
+
return new Promise((t) => setTimeout(t, n));
|
|
65
65
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
function D(n, t) {
|
|
67
|
+
let e = null, r = !1, o = null, i = null;
|
|
68
|
+
return { timeoutError: () => new Promise((u, h) => {
|
|
69
|
+
i = u, o = h, e = setTimeout(() => {
|
|
70
|
+
!r && o && (r = !0, o(new Error(t)));
|
|
71
|
+
}, n);
|
|
72
|
+
}), clear: () => {
|
|
73
|
+
e && (clearTimeout(e), e = null), i && (i(), i = null), r = !0, o = null;
|
|
74
|
+
} };
|
|
71
75
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
function I(n, t = 16e3 * 100) {
|
|
77
|
+
const e = [];
|
|
78
|
+
for (let r = 0; r < n.length; r += t)
|
|
79
|
+
e.push(n.subarray(r, r + t));
|
|
80
|
+
return e;
|
|
81
|
+
}
|
|
82
|
+
class $ {
|
|
83
|
+
constructor(t, e) {
|
|
84
|
+
this.whisperService = t, this.logger = new y((e == null ? void 0 : e.logLevel) || y.levels.ERROR, "TranscriptionSession");
|
|
85
|
+
}
|
|
86
|
+
async *streaming(t, e = {}) {
|
|
87
|
+
const { timeoutMs: r = 3e4 } = e, o = I(t);
|
|
88
|
+
let i = 0;
|
|
89
|
+
for await (const s of o) {
|
|
90
|
+
const a = [];
|
|
91
|
+
let u = null, h = !1, d, l = 0;
|
|
92
|
+
const { timeoutError: m, clear: c } = D(r, "Transcribe timeout"), w = () => this.whisperService.transcribe(
|
|
93
|
+
s,
|
|
94
|
+
(g) => {
|
|
95
|
+
l = g.timeEnd, g.timeStart += i, g.timeEnd += i, this.logger.debug("Transcription segment in session:", g), u ? (u(g), u = null) : a.push(g), c();
|
|
86
96
|
},
|
|
87
|
-
|
|
97
|
+
e
|
|
88
98
|
).then(() => {
|
|
89
|
-
|
|
90
|
-
}).catch((
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
this.logger.debug("Transcription done in session then"), h = !0, i += l, c(), u == null || u(void 0);
|
|
100
|
+
}).catch((g) => {
|
|
101
|
+
this.logger.debug("Transcription error in session catch:", g), d = g, c(), u == null || u(void 0);
|
|
102
|
+
});
|
|
103
|
+
for (w(); ; ) {
|
|
104
|
+
if (d) {
|
|
105
|
+
if (e.restartModelOnError) {
|
|
106
|
+
this.whisperService.restartModel(), w();
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
throw d;
|
|
100
110
|
}
|
|
111
|
+
if (h) break;
|
|
112
|
+
if (a.length)
|
|
113
|
+
yield a.shift();
|
|
114
|
+
else
|
|
115
|
+
try {
|
|
116
|
+
const g = await Promise.race([
|
|
117
|
+
new Promise(
|
|
118
|
+
(f) => u = f
|
|
119
|
+
),
|
|
120
|
+
m()
|
|
121
|
+
]);
|
|
122
|
+
g && (yield g);
|
|
123
|
+
} catch (g) {
|
|
124
|
+
d = g;
|
|
125
|
+
}
|
|
101
126
|
}
|
|
102
|
-
|
|
127
|
+
e.sleepMsBetweenChunks && await O(e.sleepMsBetweenChunks);
|
|
103
128
|
}
|
|
104
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* @deprecated Use `streaming()` instead.
|
|
132
|
+
*/
|
|
133
|
+
async *streamimg(t, e = {}) {
|
|
134
|
+
yield* this.streaming(t, e);
|
|
135
|
+
}
|
|
105
136
|
}
|
|
106
|
-
class
|
|
107
|
-
on(
|
|
108
|
-
return this.addEventListener(
|
|
137
|
+
class U extends EventTarget {
|
|
138
|
+
on(t, e) {
|
|
139
|
+
return this.addEventListener(t, e), () => this.removeEventListener(t, e);
|
|
109
140
|
}
|
|
110
|
-
emit(
|
|
111
|
-
this.dispatchEvent(new CustomEvent(
|
|
141
|
+
emit(t, e) {
|
|
142
|
+
this.dispatchEvent(new CustomEvent(t, { detail: e }));
|
|
112
143
|
}
|
|
113
144
|
}
|
|
114
|
-
class
|
|
115
|
-
constructor(
|
|
116
|
-
this.wasmModule = null, this.instance = null, this.modelFileName = "whisper.bin", this.isTranscribing = !1, this.bus = new
|
|
145
|
+
class Z {
|
|
146
|
+
constructor(t) {
|
|
147
|
+
this.wasmModule = null, this.instance = null, this.modelFileName = "whisper.bin", this.isTranscribing = !1, this.bus = new U(), this.modelData = null, this.logger = new y((t == null ? void 0 : t.logLevel) ?? y.levels.ERROR, "WhisperWasmService"), t != null && t.init && this.loadWasmScript();
|
|
117
148
|
}
|
|
118
149
|
async checkWasmSupport() {
|
|
119
|
-
return await
|
|
150
|
+
return await F();
|
|
120
151
|
}
|
|
121
152
|
async loadWasmScript() {
|
|
122
|
-
this.wasmModule = await (await import("./libmain-
|
|
123
|
-
print: (
|
|
124
|
-
this.logger.debug(
|
|
153
|
+
this.wasmModule = await (await import("./libmain-Dz7N7fgx.mjs")).default({
|
|
154
|
+
print: (t, ...e) => {
|
|
155
|
+
e.length > 0 && this.logger.debug(e), t.startsWith("[") ? (this.logger.info(t), this.bus.emit("transcribe", t)) : (this.logger.debug(t), this.bus.emit("system_info", t));
|
|
125
156
|
},
|
|
126
|
-
printErr: (
|
|
127
|
-
this.logger.debug(
|
|
157
|
+
printErr: (t, ...e) => {
|
|
158
|
+
e.length > 0 && this.logger.debug(e), this.logger.warn(t), this.bus.emit("transcribeError", t);
|
|
128
159
|
}
|
|
129
160
|
});
|
|
130
161
|
}
|
|
131
|
-
async
|
|
162
|
+
async initModel(t) {
|
|
132
163
|
if (!await this.checkWasmSupport())
|
|
133
164
|
throw new Error("WASM is not supported");
|
|
134
|
-
return this.wasmModule && (this.wasmModule.FS_unlink(this.modelFileName), this.wasmModule.free()), await this.loadWasmScript(), await
|
|
165
|
+
return this.modelData = t, this.wasmModule && (this.wasmModule.FS_unlink(this.modelFileName), this.wasmModule.free()), await this.loadWasmScript(), await O(100), this.storeFS(this.modelFileName, t), this.instance = this.wasmModule.init(this.modelFileName), Promise.resolve();
|
|
166
|
+
}
|
|
167
|
+
restartModel() {
|
|
168
|
+
if (!this.modelData)
|
|
169
|
+
throw new Error("Model not loaded");
|
|
170
|
+
return this.initModel(this.modelData);
|
|
135
171
|
}
|
|
136
|
-
storeFS(
|
|
172
|
+
storeFS(t, e) {
|
|
137
173
|
if (!this.wasmModule)
|
|
138
174
|
throw new Error("WASM module not loaded");
|
|
139
175
|
try {
|
|
140
|
-
this.wasmModule.FS_unlink(
|
|
176
|
+
this.wasmModule.FS_unlink(t);
|
|
141
177
|
} catch {
|
|
142
178
|
}
|
|
143
|
-
this.wasmModule.FS_createDataFile("/",
|
|
179
|
+
this.wasmModule.FS_createDataFile("/", t, e, !0, !0, !0);
|
|
144
180
|
}
|
|
145
|
-
async transcribe(
|
|
181
|
+
async transcribe(t, e, r = {}) {
|
|
146
182
|
if (this.isTranscribing)
|
|
147
183
|
throw new Error("Already transcribing");
|
|
148
184
|
if (!this.wasmModule)
|
|
149
185
|
throw new Error("WASM module not loaded");
|
|
150
186
|
if (!this.instance)
|
|
151
187
|
throw new Error("WASM instance not loaded");
|
|
152
|
-
const
|
|
153
|
-
|
|
188
|
+
const o = 120;
|
|
189
|
+
t.length > 16e3 * o && this.logger.warn(
|
|
154
190
|
"It's not recommended to transcribe audio data that is longer than 120 seconds"
|
|
155
191
|
), this.isTranscribing = !0;
|
|
156
192
|
const {
|
|
157
193
|
language: i = "auto",
|
|
158
|
-
threads:
|
|
159
|
-
translate:
|
|
194
|
+
threads: s = 4,
|
|
195
|
+
translate: a = !1
|
|
160
196
|
} = {
|
|
161
|
-
...
|
|
197
|
+
...L,
|
|
162
198
|
...r
|
|
163
|
-
},
|
|
164
|
-
return this.wasmModule.full_default(this.instance,
|
|
165
|
-
const
|
|
166
|
-
const { startMs:
|
|
167
|
-
timeStart:
|
|
168
|
-
timeEnd:
|
|
169
|
-
text:
|
|
199
|
+
}, u = [], h = Date.now();
|
|
200
|
+
return this.wasmModule.full_default(this.instance, t, i, s, a), await new Promise((d, l) => {
|
|
201
|
+
const m = this.bus.on("transcribe", (g) => {
|
|
202
|
+
const { startMs: f, endMs: v, text: B } = W(g.detail), T = {
|
|
203
|
+
timeStart: f,
|
|
204
|
+
timeEnd: v,
|
|
205
|
+
text: B,
|
|
170
206
|
raw: g.detail
|
|
171
207
|
};
|
|
172
|
-
|
|
173
|
-
}),
|
|
208
|
+
u.push(T), e == null || e(T);
|
|
209
|
+
}), c = setTimeout(
|
|
174
210
|
() => {
|
|
175
|
-
this.isTranscribing = !1,
|
|
211
|
+
this.isTranscribing = !1, m(), w(), this.logger.error("Transcribe timeout"), l(new Error("Transcribe timeout")), this.bus.emit("transcribeError", "Transcribe timeout");
|
|
176
212
|
},
|
|
177
|
-
|
|
178
|
-
),
|
|
179
|
-
this.isTranscribing = !1,
|
|
213
|
+
o * 2 * 1e3
|
|
214
|
+
), w = this.bus.on("transcribeError", (g) => {
|
|
215
|
+
this.isTranscribing = !1, m(), w(), clearTimeout(c), this.logger.debug("Transcribe error", g.detail), d({ segments: u, transcribeDurationMs: Date.now() - h });
|
|
180
216
|
});
|
|
181
217
|
});
|
|
182
218
|
}
|
|
183
219
|
createSession() {
|
|
184
|
-
return new
|
|
220
|
+
return new $(this, { logLevel: this.logger.getLevel() });
|
|
185
221
|
}
|
|
186
222
|
}
|
|
187
|
-
const
|
|
223
|
+
const _ = {
|
|
188
224
|
"tiny.en": {
|
|
189
225
|
id: "tiny.en",
|
|
190
226
|
name: "Tiny English",
|
|
@@ -306,131 +342,131 @@ const S = {
|
|
|
306
342
|
url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-q5_0.bin"
|
|
307
343
|
}
|
|
308
344
|
};
|
|
309
|
-
function
|
|
310
|
-
return Object.values(
|
|
345
|
+
function N() {
|
|
346
|
+
return Object.values(_).map(({ url: n, ...t }) => t);
|
|
311
347
|
}
|
|
312
|
-
function
|
|
313
|
-
return
|
|
348
|
+
function x(n) {
|
|
349
|
+
return _[n];
|
|
314
350
|
}
|
|
315
|
-
class
|
|
316
|
-
constructor(
|
|
317
|
-
this.cacheEnabled = !0, this.models =
|
|
351
|
+
class b {
|
|
352
|
+
constructor(t = { logLevel: y.levels.ERROR }) {
|
|
353
|
+
this.cacheEnabled = !0, this.models = N(), this.logger = new y(t.logLevel, "ModelManager");
|
|
318
354
|
}
|
|
319
355
|
/**
|
|
320
356
|
* Loads model by name
|
|
321
357
|
*/
|
|
322
|
-
async loadModel(
|
|
323
|
-
var
|
|
324
|
-
const
|
|
325
|
-
if (!
|
|
326
|
-
throw new Error(`Model ${
|
|
327
|
-
if (this.cacheEnabled &&
|
|
328
|
-
const g = await this.getCachedModel(
|
|
358
|
+
async loadModel(t, e = !0, r) {
|
|
359
|
+
var w;
|
|
360
|
+
const o = x(t);
|
|
361
|
+
if (!o)
|
|
362
|
+
throw new Error(`Model ${t} not found in config`);
|
|
363
|
+
if (this.cacheEnabled && e) {
|
|
364
|
+
const g = await this.getCachedModel(t);
|
|
329
365
|
if (g)
|
|
330
|
-
return this.logger.info(`Model ${
|
|
366
|
+
return this.logger.info(`Model ${t} loaded from cache`), r && r(100), g;
|
|
331
367
|
}
|
|
332
|
-
this.logger.info(`Loading model ${
|
|
333
|
-
const i = await fetch(
|
|
368
|
+
this.logger.info(`Loading model ${t} from ${o.url}`);
|
|
369
|
+
const i = await fetch(o.url);
|
|
334
370
|
if (!i.ok)
|
|
335
371
|
throw new Error(`Failed to load model: ${i.statusText}`);
|
|
336
|
-
const
|
|
337
|
-
let
|
|
338
|
-
const h = (
|
|
372
|
+
const s = i.headers.get("content-length"), a = s ? parseInt(s, 10) : 0;
|
|
373
|
+
let u = 0;
|
|
374
|
+
const h = (w = i.body) == null ? void 0 : w.getReader();
|
|
339
375
|
if (!h)
|
|
340
376
|
throw new Error("Response body is not readable");
|
|
341
377
|
const d = [];
|
|
342
378
|
try {
|
|
343
379
|
let g = !1;
|
|
344
380
|
for (; !g; ) {
|
|
345
|
-
const
|
|
346
|
-
if (g =
|
|
347
|
-
const
|
|
348
|
-
r(
|
|
381
|
+
const f = await h.read();
|
|
382
|
+
if (g = f.done, !g && f.value && (d.push(f.value), u += f.value.length, r && a > 0)) {
|
|
383
|
+
const v = Math.round(u / a * 100);
|
|
384
|
+
r(v);
|
|
349
385
|
}
|
|
350
386
|
}
|
|
351
387
|
} finally {
|
|
352
388
|
h.releaseLock();
|
|
353
389
|
}
|
|
354
|
-
const
|
|
355
|
-
let
|
|
390
|
+
const l = d.reduce((g, f) => g + f.length, 0), m = new Uint8Array(l);
|
|
391
|
+
let c = 0;
|
|
356
392
|
for (const g of d)
|
|
357
|
-
|
|
358
|
-
return this.cacheEnabled &&
|
|
393
|
+
m.set(g, c), c += g.length;
|
|
394
|
+
return this.cacheEnabled && e && await this.saveModelToCache(t, m), r && r(100), m;
|
|
359
395
|
}
|
|
360
396
|
/**
|
|
361
397
|
* Loads WASM model by URL and saves it to IndexedDB using the URL itself as key.
|
|
362
398
|
*/
|
|
363
|
-
async loadModelByUrl(
|
|
399
|
+
async loadModelByUrl(t, e) {
|
|
364
400
|
var r;
|
|
365
401
|
try {
|
|
366
402
|
if (this.cacheEnabled) {
|
|
367
|
-
const
|
|
368
|
-
if (
|
|
369
|
-
return this.logger.info(`WASM module loaded from cache by URL: ${
|
|
403
|
+
const c = await this.getCachedModelByUrl(t);
|
|
404
|
+
if (c)
|
|
405
|
+
return this.logger.info(`WASM module loaded from cache by URL: ${t}`), e && e(100), c;
|
|
370
406
|
}
|
|
371
|
-
this.logger.info(`Loading WASM module from URL: ${
|
|
372
|
-
const
|
|
373
|
-
if (!
|
|
374
|
-
throw new Error(`Failed to load WASM module: ${
|
|
375
|
-
const i =
|
|
376
|
-
let
|
|
377
|
-
const
|
|
378
|
-
if (!
|
|
407
|
+
this.logger.info(`Loading WASM module from URL: ${t}`);
|
|
408
|
+
const o = await fetch(t);
|
|
409
|
+
if (!o.ok)
|
|
410
|
+
throw new Error(`Failed to load WASM module: ${o.statusText}`);
|
|
411
|
+
const i = o.headers.get("content-length"), s = i ? parseInt(i, 10) : 0;
|
|
412
|
+
let a = 0;
|
|
413
|
+
const u = (r = o.body) == null ? void 0 : r.getReader();
|
|
414
|
+
if (!u)
|
|
379
415
|
throw new Error("Response body is not readable");
|
|
380
416
|
const h = [];
|
|
381
417
|
try {
|
|
382
|
-
let
|
|
383
|
-
for (; !
|
|
384
|
-
const
|
|
385
|
-
if (
|
|
386
|
-
const g = Math.round(
|
|
387
|
-
|
|
418
|
+
let c = !1;
|
|
419
|
+
for (; !c; ) {
|
|
420
|
+
const w = await u.read();
|
|
421
|
+
if (c = w.done, !c && w.value && (h.push(w.value), a += w.value.length, e && s > 0)) {
|
|
422
|
+
const g = Math.round(a / s * 100);
|
|
423
|
+
e(g);
|
|
388
424
|
}
|
|
389
425
|
}
|
|
390
426
|
} finally {
|
|
391
|
-
|
|
427
|
+
u.releaseLock();
|
|
392
428
|
}
|
|
393
|
-
const d = h.reduce((
|
|
394
|
-
let
|
|
395
|
-
for (const
|
|
396
|
-
|
|
397
|
-
return this.cacheEnabled && await this.saveModelToCacheByUrl(
|
|
398
|
-
} catch (
|
|
399
|
-
throw this.logger.error(
|
|
429
|
+
const d = h.reduce((c, w) => c + w.length, 0), l = new Uint8Array(d);
|
|
430
|
+
let m = 0;
|
|
431
|
+
for (const c of h)
|
|
432
|
+
l.set(c, m), m += c.length;
|
|
433
|
+
return this.cacheEnabled && await this.saveModelToCacheByUrl(t, l), e && e(100), l;
|
|
434
|
+
} catch (o) {
|
|
435
|
+
throw this.logger.error(o), new Error("Failed to load WASM module");
|
|
400
436
|
}
|
|
401
437
|
}
|
|
402
438
|
/**
|
|
403
439
|
* Get model from IndexedDB by URL (key is the URL itself)
|
|
404
440
|
*/
|
|
405
|
-
async getCachedModelByUrl(
|
|
441
|
+
async getCachedModelByUrl(t) {
|
|
406
442
|
try {
|
|
407
|
-
const
|
|
408
|
-
return new Promise((i,
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
},
|
|
443
|
+
const o = (await this.openIndexedDB()).transaction(["modelsByUrl"], "readonly").objectStore("modelsByUrl");
|
|
444
|
+
return new Promise((i, s) => {
|
|
445
|
+
const a = o.get(t);
|
|
446
|
+
a.onsuccess = () => {
|
|
447
|
+
const u = a.result;
|
|
448
|
+
u && u.data ? i(u.data) : i(null);
|
|
449
|
+
}, a.onerror = () => s(a.error);
|
|
414
450
|
});
|
|
415
|
-
} catch (
|
|
416
|
-
return this.logger.error("Error reading model from cache by URL:",
|
|
451
|
+
} catch (e) {
|
|
452
|
+
return this.logger.error("Error reading model from cache by URL:", e), null;
|
|
417
453
|
}
|
|
418
454
|
}
|
|
419
455
|
/**
|
|
420
456
|
* Saves model to IndexedDB by URL (key is the URL itself)
|
|
421
457
|
*/
|
|
422
|
-
async saveModelToCacheByUrl(
|
|
458
|
+
async saveModelToCacheByUrl(t, e) {
|
|
423
459
|
try {
|
|
424
460
|
const i = (await this.openIndexedDB()).transaction(["modelsByUrl"], "readwrite").objectStore("modelsByUrl");
|
|
425
|
-
await new Promise((
|
|
426
|
-
const
|
|
427
|
-
url:
|
|
428
|
-
data:
|
|
461
|
+
await new Promise((s, a) => {
|
|
462
|
+
const u = i.put({
|
|
463
|
+
url: t,
|
|
464
|
+
data: e,
|
|
429
465
|
timestamp: Date.now(),
|
|
430
|
-
size:
|
|
466
|
+
size: e.length
|
|
431
467
|
});
|
|
432
|
-
|
|
433
|
-
}), this.logger.info(`Model saved to cache by URL: ${
|
|
468
|
+
u.onsuccess = () => s(), u.onerror = () => a(u.error);
|
|
469
|
+
}), this.logger.info(`Model saved to cache by URL: ${t}`);
|
|
434
470
|
} catch (r) {
|
|
435
471
|
this.logger.error("Error saving model to cache by URL:", r);
|
|
436
472
|
}
|
|
@@ -439,17 +475,17 @@ class F {
|
|
|
439
475
|
* Gets list of available models with cache information
|
|
440
476
|
*/
|
|
441
477
|
async getAvailableModels() {
|
|
442
|
-
const
|
|
478
|
+
const t = [...this.models];
|
|
443
479
|
if (!this.cacheEnabled)
|
|
444
|
-
return
|
|
480
|
+
return t;
|
|
445
481
|
try {
|
|
446
|
-
const
|
|
447
|
-
return
|
|
482
|
+
const e = await this.getCachedModelNames();
|
|
483
|
+
return t.map((r) => ({
|
|
448
484
|
...r,
|
|
449
|
-
cached:
|
|
485
|
+
cached: e.includes(r.id)
|
|
450
486
|
}));
|
|
451
|
-
} catch (
|
|
452
|
-
return this.logger.error("Error checking cache status:",
|
|
487
|
+
} catch (e) {
|
|
488
|
+
return this.logger.error("Error checking cache status:", e), t;
|
|
453
489
|
}
|
|
454
490
|
}
|
|
455
491
|
/**
|
|
@@ -461,24 +497,24 @@ class F {
|
|
|
461
497
|
/**
|
|
462
498
|
* Gets model by name from config
|
|
463
499
|
*/
|
|
464
|
-
getModelConfig(
|
|
465
|
-
return
|
|
500
|
+
getModelConfig(t) {
|
|
501
|
+
return x(t);
|
|
466
502
|
}
|
|
467
503
|
/**
|
|
468
504
|
* Saves model to IndexedDB
|
|
469
505
|
*/
|
|
470
|
-
async saveModelToCache(
|
|
506
|
+
async saveModelToCache(t, e) {
|
|
471
507
|
try {
|
|
472
508
|
const i = (await this.openIndexedDB()).transaction(["models"], "readwrite").objectStore("models");
|
|
473
|
-
await new Promise((
|
|
474
|
-
const
|
|
475
|
-
name:
|
|
476
|
-
data:
|
|
509
|
+
await new Promise((s, a) => {
|
|
510
|
+
const u = i.put({
|
|
511
|
+
name: t,
|
|
512
|
+
data: e,
|
|
477
513
|
timestamp: Date.now(),
|
|
478
|
-
size:
|
|
514
|
+
size: e.length
|
|
479
515
|
});
|
|
480
|
-
|
|
481
|
-
}), this.logger.info(`Model ${
|
|
516
|
+
u.onsuccess = () => s(), u.onerror = () => a(u.error);
|
|
517
|
+
}), this.logger.info(`Model ${t} saved to cache`);
|
|
482
518
|
} catch (r) {
|
|
483
519
|
this.logger.error("Error saving model to cache:", r);
|
|
484
520
|
}
|
|
@@ -486,18 +522,18 @@ class F {
|
|
|
486
522
|
/**
|
|
487
523
|
* Gets model from IndexedDB cache
|
|
488
524
|
*/
|
|
489
|
-
async getCachedModel(
|
|
525
|
+
async getCachedModel(t) {
|
|
490
526
|
try {
|
|
491
|
-
const
|
|
492
|
-
return new Promise((i,
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
},
|
|
527
|
+
const o = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
|
|
528
|
+
return new Promise((i, s) => {
|
|
529
|
+
const a = o.get(t);
|
|
530
|
+
a.onsuccess = () => {
|
|
531
|
+
const u = a.result;
|
|
532
|
+
u && u.data ? i(u.data) : i(null);
|
|
533
|
+
}, a.onerror = () => s(a.error);
|
|
498
534
|
});
|
|
499
|
-
} catch (
|
|
500
|
-
return this.logger.error("Error getting cached model:",
|
|
535
|
+
} catch (e) {
|
|
536
|
+
return this.logger.error("Error getting cached model:", e), null;
|
|
501
537
|
}
|
|
502
538
|
}
|
|
503
539
|
/**
|
|
@@ -506,32 +542,32 @@ class F {
|
|
|
506
542
|
async getCachedModelNames() {
|
|
507
543
|
try {
|
|
508
544
|
const r = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
|
|
509
|
-
return new Promise((
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
a
|
|
514
|
-
},
|
|
545
|
+
return new Promise((o, i) => {
|
|
546
|
+
const s = r.getAllKeys();
|
|
547
|
+
s.onsuccess = () => {
|
|
548
|
+
const a = s.result;
|
|
549
|
+
o(a);
|
|
550
|
+
}, s.onerror = () => i(s.error);
|
|
515
551
|
});
|
|
516
|
-
} catch (
|
|
517
|
-
return this.logger.error("Error getting cached model names:",
|
|
552
|
+
} catch (t) {
|
|
553
|
+
return this.logger.error("Error getting cached model names:", t), [];
|
|
518
554
|
}
|
|
519
555
|
}
|
|
520
556
|
/**
|
|
521
557
|
* Opens IndexedDB for model caching
|
|
522
558
|
*/
|
|
523
559
|
async openIndexedDB() {
|
|
524
|
-
return new Promise((
|
|
560
|
+
return new Promise((t, e) => {
|
|
525
561
|
const r = indexedDB.open("WhisperModels", 2);
|
|
526
|
-
r.onerror = () =>
|
|
527
|
-
const i =
|
|
562
|
+
r.onerror = () => e(r.error), r.onsuccess = () => t(r.result), r.onupgradeneeded = (o) => {
|
|
563
|
+
const i = o.target.result;
|
|
528
564
|
if (!i.objectStoreNames.contains("models")) {
|
|
529
|
-
const
|
|
530
|
-
|
|
565
|
+
const s = i.createObjectStore("models", { keyPath: "name" });
|
|
566
|
+
s.createIndex("timestamp", "timestamp", { unique: !1 }), s.createIndex("size", "size", { unique: !1 });
|
|
531
567
|
}
|
|
532
568
|
if (!i.objectStoreNames.contains("modelsByUrl")) {
|
|
533
|
-
const
|
|
534
|
-
|
|
569
|
+
const s = i.createObjectStore("modelsByUrl", { keyPath: "url" });
|
|
570
|
+
s.createIndex("timestamp", "timestamp", { unique: !1 }), s.createIndex("size", "size", { unique: !1 });
|
|
535
571
|
}
|
|
536
572
|
};
|
|
537
573
|
});
|
|
@@ -541,18 +577,18 @@ class F {
|
|
|
541
577
|
*/
|
|
542
578
|
async clearCache() {
|
|
543
579
|
try {
|
|
544
|
-
const
|
|
545
|
-
await new Promise((i,
|
|
546
|
-
const
|
|
547
|
-
|
|
580
|
+
const e = (await this.openIndexedDB()).transaction(["models", "modelsByUrl"], "readwrite"), r = e.objectStore("models");
|
|
581
|
+
await new Promise((i, s) => {
|
|
582
|
+
const a = r.clear();
|
|
583
|
+
a.onsuccess = () => i(), a.onerror = () => s(a.error);
|
|
548
584
|
});
|
|
549
|
-
const
|
|
550
|
-
await new Promise((i,
|
|
551
|
-
const
|
|
552
|
-
|
|
585
|
+
const o = e.objectStore("modelsByUrl");
|
|
586
|
+
await new Promise((i, s) => {
|
|
587
|
+
const a = o.clear();
|
|
588
|
+
a.onsuccess = () => i(), a.onerror = () => s(a.error);
|
|
553
589
|
}), this.logger.info("Model cache cleared");
|
|
554
|
-
} catch (
|
|
555
|
-
this.logger.error("Error clearing cache:",
|
|
590
|
+
} catch (t) {
|
|
591
|
+
this.logger.error("Error clearing cache:", t);
|
|
556
592
|
}
|
|
557
593
|
}
|
|
558
594
|
/**
|
|
@@ -561,20 +597,336 @@ class F {
|
|
|
561
597
|
async getCacheInfo() {
|
|
562
598
|
try {
|
|
563
599
|
const r = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
|
|
564
|
-
return new Promise((
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
},
|
|
600
|
+
return new Promise((o, i) => {
|
|
601
|
+
const s = r.getAll();
|
|
602
|
+
s.onsuccess = () => {
|
|
603
|
+
const a = s.result, u = a.reduce((h, d) => h + (d.size || 0), 0);
|
|
604
|
+
o({ count: a.length, totalSize: u });
|
|
605
|
+
}, s.onerror = () => i(s.error);
|
|
570
606
|
});
|
|
571
|
-
} catch (
|
|
572
|
-
return this.logger.error("Error getting cache info:",
|
|
607
|
+
} catch (t) {
|
|
608
|
+
return this.logger.error("Error getting cache info:", t), { count: 0, totalSize: 0 };
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
var p = /* @__PURE__ */ ((n) => (n.MP3 = "mp3", n.WAV = "wav", n.OGG = "ogg", n.M4A = "m4a", n.AAC = "aac", n.FLAC = "flac", n.MP4 = "mp4", n.WEBM = "webm", n.AVI = "avi", n.MOV = "mov", n.MKV = "mkv", n.RAW_PCM = "raw_pcm", n.MICROPHONE = "microphone", n.AUDIO_ELEMENT = "audio_element", n))(p || {});
|
|
613
|
+
const A = {
|
|
614
|
+
targetSampleRate: 16e3,
|
|
615
|
+
// Whisper требует 16kHz
|
|
616
|
+
targetChannels: 1,
|
|
617
|
+
// Моно аудио
|
|
618
|
+
inputSampleRate: 16e3,
|
|
619
|
+
normalize: !0,
|
|
620
|
+
noiseReduction: !1,
|
|
621
|
+
logLevel: y.levels.ERROR,
|
|
622
|
+
signal: void 0,
|
|
623
|
+
recordingDurationMs: 1e4
|
|
624
|
+
};
|
|
625
|
+
function j(n) {
|
|
626
|
+
return typeof n == "number" ? n : n ? y.levels[n] : y.levels.ERROR;
|
|
627
|
+
}
|
|
628
|
+
function S(n) {
|
|
629
|
+
return new y(j(n.logLevel), "AudioConverter");
|
|
630
|
+
}
|
|
631
|
+
function E(n) {
|
|
632
|
+
if (n != null && n.aborted)
|
|
633
|
+
throw new DOMException("Aborted", "AbortError");
|
|
634
|
+
}
|
|
635
|
+
function R() {
|
|
636
|
+
return typeof window > "u" ? !1 : !!(window.AudioContext || window.webkitAudioContext || window.OfflineAudioContext || window.webkitOfflineAudioContext);
|
|
637
|
+
}
|
|
638
|
+
function k() {
|
|
639
|
+
return [
|
|
640
|
+
p.MP3,
|
|
641
|
+
p.WAV,
|
|
642
|
+
p.OGG,
|
|
643
|
+
p.M4A,
|
|
644
|
+
p.AAC,
|
|
645
|
+
p.FLAC,
|
|
646
|
+
p.MP4,
|
|
647
|
+
p.WEBM,
|
|
648
|
+
p.AVI,
|
|
649
|
+
p.MOV,
|
|
650
|
+
p.MKV,
|
|
651
|
+
p.RAW_PCM,
|
|
652
|
+
p.MICROPHONE,
|
|
653
|
+
p.AUDIO_ELEMENT
|
|
654
|
+
];
|
|
655
|
+
}
|
|
656
|
+
async function ee(n, t = {}, e = {}) {
|
|
657
|
+
var s, a, u, h, d;
|
|
658
|
+
if (!R())
|
|
659
|
+
throw new Error("Web Audio API is not supported in this browser");
|
|
660
|
+
const r = { ...A, ...t }, o = S(r), i = [];
|
|
661
|
+
try {
|
|
662
|
+
E(r.signal), o.info(`Converting file: ${n.name}`), (s = e.onProgress) == null || s.call(e, 0, `Loading file: ${n.name}`);
|
|
663
|
+
const l = await H(n);
|
|
664
|
+
(a = e.onProgress) == null || a.call(e, 20, "File loaded, decoding..."), E(r.signal);
|
|
665
|
+
const m = await P(l, r, e, o);
|
|
666
|
+
(u = e.onProgress) == null || u.call(e, 40, "Audio decoded, processing...");
|
|
667
|
+
const c = await C(m, r, e, o, i);
|
|
668
|
+
return (h = e.onProgress) == null || h.call(e, 100, "Conversion completed"), o.info("File conversion completed successfully"), c;
|
|
669
|
+
} catch (l) {
|
|
670
|
+
throw o.error("File conversion failed:", l), (d = e.onError) == null || d.call(e, l), l;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async function z(n, t = {}, e = {}) {
|
|
674
|
+
var s, a, u, h, d;
|
|
675
|
+
if (!R())
|
|
676
|
+
throw new Error("Web Audio API is not supported in this browser");
|
|
677
|
+
const r = { ...A, ...t }, o = S(r), i = [];
|
|
678
|
+
try {
|
|
679
|
+
E(r.signal), o.info("Converting from MediaStream"), (s = e.onProgress) == null || s.call(e, 0, "Starting recording...");
|
|
680
|
+
const l = await X(n, r, e, o);
|
|
681
|
+
(a = e.onProgress) == null || a.call(e, 50, "Recording completed, decoding...");
|
|
682
|
+
const m = await l.arrayBuffer(), c = await P(m, r, e, o);
|
|
683
|
+
(u = e.onProgress) == null || u.call(e, 70, "Audio decoded, processing...");
|
|
684
|
+
const w = await C(c, r, e, o, i);
|
|
685
|
+
return (h = e.onProgress) == null || h.call(e, 100, "Conversion completed"), w;
|
|
686
|
+
} catch (l) {
|
|
687
|
+
throw o.error("MediaStream conversion failed:", l), (d = e.onError) == null || d.call(e, l), l;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
async function te(n, t = {}, e = {}) {
|
|
691
|
+
var s, a, u, h, d, l;
|
|
692
|
+
if (!R())
|
|
693
|
+
throw new Error("Web Audio API is not supported in this browser");
|
|
694
|
+
const r = { ...A, ...t }, o = S(r), i = [];
|
|
695
|
+
try {
|
|
696
|
+
E(r.signal), o.info("Converting from HTMLAudioElement"), (s = e.onProgress) == null || s.call(e, 0, "Capturing audio from element...");
|
|
697
|
+
const m = n.srcObject;
|
|
698
|
+
if (m && m instanceof MediaStream) {
|
|
699
|
+
i.push("Using HTMLAudioElement.srcObject MediaStream");
|
|
700
|
+
const f = await z(m, r, e);
|
|
701
|
+
return {
|
|
702
|
+
...f,
|
|
703
|
+
warnings: [...i, ...f.warnings ?? []]
|
|
704
|
+
};
|
|
573
705
|
}
|
|
706
|
+
const c = n.currentSrc || n.src;
|
|
707
|
+
if (c)
|
|
708
|
+
try {
|
|
709
|
+
(a = e.onProgress) == null || a.call(e, 10, "Fetching audio source...");
|
|
710
|
+
const f = await Y(c, r.signal);
|
|
711
|
+
(u = e.onProgress) == null || u.call(e, 30, "Fetched, decoding...");
|
|
712
|
+
const v = await P(f, r, e, o);
|
|
713
|
+
(h = e.onProgress) == null || h.call(e, 60, "Decoded, processing...");
|
|
714
|
+
const B = await C(v, r, e, o, i);
|
|
715
|
+
return (d = e.onProgress) == null || d.call(e, 100, "Conversion completed"), B;
|
|
716
|
+
} catch (f) {
|
|
717
|
+
if ((f == null ? void 0 : f.name) === "AbortError")
|
|
718
|
+
throw f;
|
|
719
|
+
i.push(
|
|
720
|
+
`Failed to fetch element src (CORS?) – falling back to captureStream: ${f.message}`
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
const w = n.captureStream || n.mozCaptureStream;
|
|
724
|
+
if (typeof w != "function")
|
|
725
|
+
throw new Error(
|
|
726
|
+
"Unable to capture audio from HTMLAudioElement: no srcObject, fetch failed, and captureStream() is not supported"
|
|
727
|
+
);
|
|
728
|
+
i.push("Using HTMLAudioElement.captureStream() fallback");
|
|
729
|
+
const g = w.call(n);
|
|
730
|
+
try {
|
|
731
|
+
const f = await z(g, r, e);
|
|
732
|
+
return {
|
|
733
|
+
...f,
|
|
734
|
+
warnings: [...i, ...f.warnings ?? []]
|
|
735
|
+
};
|
|
736
|
+
} finally {
|
|
737
|
+
g.getTracks().forEach((f) => f.stop());
|
|
738
|
+
}
|
|
739
|
+
} catch (m) {
|
|
740
|
+
throw o.error("HTMLAudioElement conversion failed:", m), (l = e.onError) == null || l.call(e, m), m;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
async function re(n, t = {}, e = {}) {
|
|
744
|
+
var i, s, a, u;
|
|
745
|
+
if (!R())
|
|
746
|
+
throw new Error("Web Audio API is not supported in this browser");
|
|
747
|
+
const r = { ...A, ...t }, o = S(r);
|
|
748
|
+
try {
|
|
749
|
+
E(r.signal), o.info("Converting from Float32Array"), (i = e.onProgress) == null || i.call(e, 0, "Processing Float32Array...");
|
|
750
|
+
const h = window.AudioContext || window.webkitAudioContext, d = r.inputSampleRate ?? r.targetSampleRate, l = new h({ sampleRate: d });
|
|
751
|
+
try {
|
|
752
|
+
const m = l.createBuffer(1, n.length, l.sampleRate);
|
|
753
|
+
m.getChannelData(0).set(n), (s = e.onProgress) == null || s.call(e, 30, "AudioBuffer created, processing...");
|
|
754
|
+
const w = [];
|
|
755
|
+
d !== r.targetSampleRate && w.push(
|
|
756
|
+
`Float32Array sample rate (${d}Hz) will be converted to ${r.targetSampleRate}Hz`
|
|
757
|
+
);
|
|
758
|
+
const g = await C(m, r, e, o, w);
|
|
759
|
+
return (a = e.onProgress) == null || a.call(e, 100, "Conversion completed"), o.info("Float32Array conversion completed successfully"), g;
|
|
760
|
+
} finally {
|
|
761
|
+
try {
|
|
762
|
+
await l.close();
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} catch (h) {
|
|
767
|
+
throw o.error("Float32Array conversion failed:", h), (u = e.onError) == null || u.call(e, h), h;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function ne(n, t = {}, e = {}) {
|
|
771
|
+
var s, a, u, h;
|
|
772
|
+
if (!R())
|
|
773
|
+
throw new Error("Web Audio API is not supported in this browser");
|
|
774
|
+
const r = { ...A, ...t }, o = S(r), i = [];
|
|
775
|
+
try {
|
|
776
|
+
E(r.signal), o.info("Converting from ArrayBuffer"), (s = e.onProgress) == null || s.call(e, 0, "Processing ArrayBuffer...");
|
|
777
|
+
const d = await P(n, r, e, o);
|
|
778
|
+
(a = e.onProgress) == null || a.call(e, 40, "Audio decoded, processing...");
|
|
779
|
+
const l = await C(d, r, e, o, i);
|
|
780
|
+
return (u = e.onProgress) == null || u.call(e, 100, "Conversion completed"), o.info("ArrayBuffer conversion completed successfully"), l;
|
|
781
|
+
} catch (d) {
|
|
782
|
+
throw o.error("ArrayBuffer conversion failed:", d), (h = e.onError) == null || h.call(e, d), d;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
async function H(n) {
|
|
786
|
+
return new Promise((t, e) => {
|
|
787
|
+
const r = new FileReader();
|
|
788
|
+
r.onload = (o) => {
|
|
789
|
+
var i;
|
|
790
|
+
return t((i = o.target) == null ? void 0 : i.result);
|
|
791
|
+
}, r.onerror = (o) => e(o), r.readAsArrayBuffer(n);
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
async function P(n, t, e, r) {
|
|
795
|
+
var s;
|
|
796
|
+
(s = e.onProgress) == null || s.call(e, 15, "Decoding audio data");
|
|
797
|
+
const o = window.AudioContext || window.webkitAudioContext, i = new o({
|
|
798
|
+
sampleRate: t.targetSampleRate
|
|
799
|
+
});
|
|
800
|
+
try {
|
|
801
|
+
return await i.decodeAudioData(n);
|
|
802
|
+
} catch (a) {
|
|
803
|
+
throw r.error("Audio decoding failed:", a), new Error(`Failed to decode audio: ${a.message}`);
|
|
804
|
+
} finally {
|
|
805
|
+
try {
|
|
806
|
+
await i.close();
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
async function C(n, t, e, r, o) {
|
|
812
|
+
var a, u, h, d;
|
|
813
|
+
(a = e.onProgress) == null || a.call(e, 50, "Converting audio format..."), J(n, t, o);
|
|
814
|
+
const i = await Q(n, t, e);
|
|
815
|
+
(u = e.onProgress) == null || u.call(e, 70, "Converting to Float32Array...");
|
|
816
|
+
const s = V(i);
|
|
817
|
+
return (h = e.onProgress) == null || h.call(e, 80, "Applying effects..."), t.normalize && G(s), t.noiseReduction && K(s), (d = e.onProgress) == null || d.call(e, 90, "Finalizing..."), {
|
|
818
|
+
audioData: s,
|
|
819
|
+
audioInfo: {
|
|
820
|
+
sampleRate: i.sampleRate,
|
|
821
|
+
duration: i.duration,
|
|
822
|
+
channels: i.numberOfChannels,
|
|
823
|
+
bitDepth: 32,
|
|
824
|
+
// Float32
|
|
825
|
+
format: "float32"
|
|
826
|
+
},
|
|
827
|
+
warnings: o.length > 0 ? o : void 0
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
async function Q(n, t, e) {
|
|
831
|
+
var s;
|
|
832
|
+
(s = e.onProgress) == null || s.call(e, 60, "Converting audio format...");
|
|
833
|
+
const r = window.OfflineAudioContext || window.webkitOfflineAudioContext, o = new r(
|
|
834
|
+
t.targetChannels,
|
|
835
|
+
Math.floor(n.length * t.targetSampleRate / n.sampleRate),
|
|
836
|
+
t.targetSampleRate
|
|
837
|
+
), i = o.createBufferSource();
|
|
838
|
+
return i.buffer = n, i.connect(o.destination), i.start(0), await o.startRendering();
|
|
839
|
+
}
|
|
840
|
+
function V(n) {
|
|
841
|
+
if (n.numberOfChannels === 1)
|
|
842
|
+
return n.getChannelData(0);
|
|
843
|
+
{
|
|
844
|
+
const t = n.getChannelData(0), e = n.getChannelData(1), r = new Float32Array(t.length);
|
|
845
|
+
for (let o = 0; o < t.length; o++)
|
|
846
|
+
r[o] = (t[o] + e[o]) / 2;
|
|
847
|
+
return r;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
function G(n) {
|
|
851
|
+
let t = 0;
|
|
852
|
+
for (let e = 0; e < n.length; e++)
|
|
853
|
+
t = Math.max(t, Math.abs(n[e]));
|
|
854
|
+
if (t > 0) {
|
|
855
|
+
const e = 0.95 / t;
|
|
856
|
+
for (let r = 0; r < n.length; r++)
|
|
857
|
+
n[r] *= e;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function K(n) {
|
|
861
|
+
const t = new Float32Array(n.length), e = 3;
|
|
862
|
+
for (let r = 0; r < n.length; r++) {
|
|
863
|
+
let o = 0, i = 0;
|
|
864
|
+
for (let s = Math.max(0, r - e); s <= Math.min(n.length - 1, r + e); s++)
|
|
865
|
+
o += n[s], i++;
|
|
866
|
+
t[r] = o / i;
|
|
867
|
+
}
|
|
868
|
+
n.set(t);
|
|
869
|
+
}
|
|
870
|
+
function J(n, t, e) {
|
|
871
|
+
n.numberOfChannels > 2 && e.push(`Audio has ${n.numberOfChannels} channels, will be mixed to mono`), n.sampleRate !== t.targetSampleRate && e.push(
|
|
872
|
+
`Audio sample rate (${n.sampleRate}Hz) will be converted to ${t.targetSampleRate}Hz`
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
async function X(n, t, e, r) {
|
|
876
|
+
var w;
|
|
877
|
+
if (typeof window > "u")
|
|
878
|
+
throw new Error("MediaStream recording is only supported in browser environments");
|
|
879
|
+
if (!window.MediaRecorder)
|
|
880
|
+
throw new Error("MediaRecorder is not supported in this browser");
|
|
881
|
+
const o = window.MediaRecorder, s = [
|
|
882
|
+
"audio/webm;codecs=opus",
|
|
883
|
+
"audio/webm",
|
|
884
|
+
"audio/ogg;codecs=opus",
|
|
885
|
+
"audio/ogg"
|
|
886
|
+
].find((g) => o.isTypeSupported(g)), a = new o(n, s ? { mimeType: s } : void 0), u = [], h = new Promise((g, f) => {
|
|
887
|
+
a.ondataavailable = (v) => {
|
|
888
|
+
v.data && v.data.size > 0 && u.push(v.data);
|
|
889
|
+
}, a.onerror = () => f(new Error("MediaRecorder error")), a.onstop = () => {
|
|
890
|
+
const v = s || a.mimeType || "application/octet-stream";
|
|
891
|
+
g(new Blob(u, { type: v }));
|
|
892
|
+
};
|
|
893
|
+
}), d = t.signal, l = () => {
|
|
894
|
+
try {
|
|
895
|
+
a.state !== "inactive" && a.stop();
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
d == null || d.addEventListener("abort", l, { once: !0 });
|
|
900
|
+
const m = t.recordingDurationMs ?? 1e4, c = setTimeout(() => {
|
|
901
|
+
try {
|
|
902
|
+
a.state !== "inactive" && a.stop();
|
|
903
|
+
} catch {
|
|
904
|
+
}
|
|
905
|
+
}, m);
|
|
906
|
+
(w = e.onProgress) == null || w.call(e, 20, "Recording audio..."), r.debug("Starting MediaRecorder", { mimeType: s ?? a.mimeType, durationMs: m }), a.start(250);
|
|
907
|
+
try {
|
|
908
|
+
return await h;
|
|
909
|
+
} finally {
|
|
910
|
+
clearTimeout(c), d == null || d.removeEventListener("abort", l);
|
|
574
911
|
}
|
|
575
912
|
}
|
|
913
|
+
async function Y(n, t) {
|
|
914
|
+
E(t);
|
|
915
|
+
const e = await fetch(n, { signal: t });
|
|
916
|
+
if (!e.ok)
|
|
917
|
+
throw new Error(`Failed to fetch (${e.status}): ${e.statusText}`);
|
|
918
|
+
return await e.arrayBuffer();
|
|
919
|
+
}
|
|
576
920
|
export {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
921
|
+
p as AudioFormat,
|
|
922
|
+
b as ModelManager,
|
|
923
|
+
Z as WhisperWasmService,
|
|
924
|
+
ne as convertFromArrayBuffer,
|
|
925
|
+
te as convertFromAudioElement,
|
|
926
|
+
ee as convertFromFile,
|
|
927
|
+
re as convertFromFloat32Array,
|
|
928
|
+
z as convertFromMediaStream,
|
|
929
|
+
N as getAllModels,
|
|
930
|
+
k as getSupportedFormats,
|
|
931
|
+
R as isWebAudioSupported
|
|
580
932
|
};
|