transcript_ai / wav-worker.js
Janiusx
Deploy Typhoon Video Transcriber
ff7ed83
Raw
History Blame Contribute Delete
3.05 kB
self.onmessage = function(e) {
const { channelData, sampleRate } = e.data;
const targetSampleRate = 16000;
try {
// 1. Progress: Downsampling
self.postMessage({ type: 'progress', status: 'downsampling', progress: 20 });
const downsampled = downsample(channelData, sampleRate, targetSampleRate);
// 2. Progress: Encoding
self.postMessage({ type: 'progress', status: 'encoding', progress: 50 });
const wavBuffer = encodeWAV(downsampled, targetSampleRate);
// 3. Progress: Completed
self.postMessage({ type: 'progress', status: 'completed', progress: 100 });
self.postMessage({ type: 'done', buffer: wavBuffer }, [wavBuffer]);
} catch (error) {
self.postMessage({ type: 'error', message: error.message });
}
};
// Simple average-based downsampler for speech recognition
function downsample(buffer, inputSampleRate, outputSampleRate) {
if (inputSampleRate === outputSampleRate) {
return buffer;
}
const sampleRateRatio = inputSampleRate / outputSampleRate;
const newLength = Math.round(buffer.length / sampleRateRatio);
const result = new Float32Array(newLength);
let offsetResult = 0;
let offsetBuffer = 0;
while (offsetResult < result.length) {
const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
let accum = 0;
let count = 0;
for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
accum += buffer[i];
count++;
}
result[offsetResult] = count > 0 ? accum / count : 0;
offsetResult++;
offsetBuffer = nextOffsetBuffer;
}
return result;
}
// Write standard 16-bit PCM WAV file
function encodeWAV(samples, sampleRate) {
const buffer = new ArrayBuffer(44 + samples.length * 2);
const view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* file length */
view.setUint32(4, 36 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw PCM) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, 1, true); // Mono
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 2, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * 2, true);
// Write samples
const volume = 1.0;
let offset = 44;
for (let i = 0; i < samples.length; i++, offset += 2) {
const s = Math.max(-1, Math.min(1, samples[i]));
view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
return buffer;
}
function writeString(view, offset, string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}