add app, fix ble, use flash

This commit is contained in:
zyphlar
2026-07-04 03:28:14 -07:00
parent e6a2a65b82
commit 24de5d8d39
13 changed files with 1028 additions and 249 deletions
+82
View File
@@ -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 };
}