add app, fix ble, use flash
This commit is contained in:
+82
@@ -0,0 +1,82 @@
|
||||
// wav.js — decode any browser-supported audio file and re-encode it as a
|
||||
// conformant mono PCM WAV the firmware can parse: 8 or 16-bit, 8/16/32 kHz.
|
||||
|
||||
export const RATES = [8000, 16000, 32000];
|
||||
export const BITS = [8, 16];
|
||||
|
||||
// Decode -> resample to `rate` mono -> Float32 samples in [-1, 1].
|
||||
async function decodeMono(arrayBuffer, rate) {
|
||||
const AC = window.AudioContext || window.webkitAudioContext;
|
||||
const tmp = new AC();
|
||||
let decoded;
|
||||
try {
|
||||
decoded = await tmp.decodeAudioData(arrayBuffer.slice(0));
|
||||
} finally {
|
||||
tmp.close();
|
||||
}
|
||||
|
||||
const frames = Math.max(1, Math.ceil(decoded.duration * rate));
|
||||
const OAC = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
||||
// 1-channel destination auto-downmixes any number of source channels to mono.
|
||||
const off = new OAC(1, frames, rate);
|
||||
const src = off.createBufferSource();
|
||||
src.buffer = decoded;
|
||||
src.connect(off.destination);
|
||||
src.start();
|
||||
const rendered = await off.startRendering();
|
||||
return { samples: rendered.getChannelData(0), rate, srcRate: decoded.sampleRate };
|
||||
}
|
||||
|
||||
function floatToPcm(samples, bits) {
|
||||
const n = samples.length;
|
||||
if (bits === 16) {
|
||||
const out = new Uint8Array(n * 2);
|
||||
const dv = new DataView(out.buffer);
|
||||
for (let i = 0; i < n; i++) {
|
||||
let s = Math.max(-1, Math.min(1, samples[i]));
|
||||
dv.setInt16(i * 2, Math.round(s * 32767), true); // signed LE
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// 8-bit unsigned PCM, center 128
|
||||
const out = new Uint8Array(n);
|
||||
for (let i = 0; i < n; i++) {
|
||||
let s = Math.max(-1, Math.min(1, samples[i]));
|
||||
out[i] = Math.max(0, Math.min(255, Math.round(s * 127) + 128));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildWav(pcm, rate, bits) {
|
||||
const blockAlign = bits >> 3; // mono
|
||||
const byteRate = rate * blockAlign;
|
||||
const buf = new ArrayBuffer(44 + pcm.byteLength);
|
||||
const dv = new DataView(buf);
|
||||
const u8 = new Uint8Array(buf);
|
||||
const ws = (off, str) => { for (let i = 0; i < str.length; i++) dv.setUint8(off + i, str.charCodeAt(i)); };
|
||||
|
||||
ws(0, 'RIFF');
|
||||
dv.setUint32(4, 36 + pcm.byteLength, true);
|
||||
ws(8, 'WAVE');
|
||||
ws(12, 'fmt ');
|
||||
dv.setUint32(16, 16, true); // fmt chunk size
|
||||
dv.setUint16(20, 1, true); // PCM
|
||||
dv.setUint16(22, 1, true); // mono
|
||||
dv.setUint32(24, rate, true);
|
||||
dv.setUint32(28, byteRate, true);
|
||||
dv.setUint16(32, blockAlign, true);
|
||||
dv.setUint16(34, bits, true);
|
||||
ws(36, 'data');
|
||||
dv.setUint32(40, pcm.byteLength, true);
|
||||
u8.set(pcm, 44);
|
||||
return u8;
|
||||
}
|
||||
|
||||
// Returns { wav: Uint8Array, durationSec, rate, bits, srcRate }.
|
||||
export async function fileToWav(file, { rate = 8000, bits = 8 } = {}) {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const { samples, srcRate } = await decodeMono(arrayBuffer, rate);
|
||||
const pcm = floatToPcm(samples, bits);
|
||||
const wav = buildWav(pcm, rate, bits);
|
||||
return { wav, durationSec: samples.length / rate, rate, bits, srcRate };
|
||||
}
|
||||
Reference in New Issue
Block a user