-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocal-engine.js
More file actions
91 lines (81 loc) · 2.91 KB
/
Copy pathlocal-engine.js
File metadata and controls
91 lines (81 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/* global transformers */
const { pipeline, env, read_audio } = window.transformers;
env.allowRemoteModels = true;
env.allowLocalModels = true;
env.backends.onnx.wasm.wasmPaths = chrome.runtime.getURL('vendor/transformers/');
// Offscreen pages are not crossOriginIsolated; force single-threaded WASM.
env.backends.onnx.wasm.numThreads = 1;
const pipelines = new Map();
const SAMPLE_RATE = 16000;
async function getPipeline(model, progressCallback) {
if (pipelines.has(model)) return pipelines.get(model);
const asr = await pipeline('automatic-speech-recognition', model, {
progress_callback: progressCallback || null
});
pipelines.set(model, asr);
return asr;
}
function base64ToBlob(base64, mimeType) {
const binaryStr = atob(base64);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
return new Blob([bytes], { type: mimeType || 'audio/webm' });
}
async function decodeAudio(base64, mimeType) {
const blob = base64ToBlob(base64, mimeType);
const url = URL.createObjectURL(blob);
try {
return await read_audio(url, SAMPLE_RATE);
} finally {
URL.revokeObjectURL(url);
}
}
function notifyProgress(model, payload) {
chrome.runtime.sendMessage({
type: 'LOCAL_PROGRESS',
model,
payload
});
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (!message || message._target !== 'offscreen') return;
if (message.type === 'OFFSCREEN_PING') {
sendResponse({ success: true, ready: true });
return;
}
if (message.type === 'LOCAL_PRELOAD') {
(async () => {
try {
await getPipeline(message.model, (payload) => notifyProgress(message.model, payload));
sendResponse({ success: true });
} catch (error) {
const msg = (error && error.message) ? error.message : 'Local preload failed';
sendResponse({ success: false, error: msg });
}
})();
return true;
}
if (message.type === 'LOCAL_TRANSCRIBE') {
(async () => {
try {
const asr = await getPipeline(message.model, (payload) => notifyProgress(message.model, payload));
const audio = await decodeAudio(message.audioData, message.mimeType);
const result = await asr(audio);
const text = (result && result.text) || (typeof result === 'string' ? result : '');
sendResponse({ success: true, data: { text } });
} catch (error) {
const msg = (error && error.message) ? error.message : 'Local transcription failed';
let hint = '';
if (/offset is out of bounds/i.test(msg)) {
hint = ' (Try a smaller local model like Xenova/whisper-small.)';
} else if (/unsupported model type/i.test(msg)) {
hint = ' (Model not supported by the local runtime. Try Xenova/whisper-small.)';
}
sendResponse({ success: false, error: msg + hint });
}
})();
return true;
}
});