Files
church-translate/app/_pb.js
T
christianandClaude Opus 5 c9eef0672c Church Translate, as it runs at the campuses
Live sermon translation into 100+ languages, running entirely on one Mac per
campus: audio in, voice detection, Whisper on the GPU, NLLB-200 in its own
process, and a built-in web server the congregation reads from on their
phones. No audio leaves the building.

This is the source only. What used to sit beside it — 14 GB of speech and
translation models, 8 GB of voice bundles, 6 GB of natural voices, the Electron
caches, and the app data holding the operator PIN and every service transcript
— is machine state and downloaded assets, and stays in the NAS backup where it
belongs. The vendored node, cloudflared and Windows ffmpeg binaries are out too
(533 MB, re-fetched for packaging); the browser-side runtime beside them is
kept, so a checkout still serves the congregation page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:41:21 -04:00

33 lines
2.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const ort = require('onnxruntime-node');
const fs = require('fs');
const D='/private/tmp/claude-502/-Users-Studio-Documents-BandLineupDisplay/cee248f6-d861-4845-bd13-03867cf46596/scratchpad/piper/';
const cfg = JSON.parse(fs.readFileSync(D+'lessac.json','utf8'));
console.log('sample_rate', cfg.audio.sample_rate, 'num_symbols', cfg.num_symbols, 'phoneme_type', cfg.phoneme_type, 'lang', cfg.language && cfg.language.code);
console.log('inference defaults', JSON.stringify(cfg.inference));
const map = cfg.phoneme_id_map;
// crude: use the espeak IPA of the sentence is unavailable here; approximate with the raw characters that exist in the map
const IPA = "dʒiːzəs sɛd tuː ðɛm aɪ æm ðə bɹɛd ʌv laɪf huːɛvɚ kʌmz tuː miː";
const ids=[map['^'][0]];
for (const ch of IPA){ if(map[ch]){ ids.push(map[ch][0]); ids.push(map['_'][0]); } }
ids.push(map['$'][0]);
(async()=>{
const ep = process.argv[2]||'cpu';
const t=Date.now();
const sess = await ort.InferenceSession.create(D+'lessac.onnx',{executionProviders:[ep]});
console.log(`[${ep}] load ${Date.now()-t}ms inputs=${sess.inputNames.join(',')}`);
const mk=(len)=>({
input: new ort.Tensor('int64', BigInt64Array.from(ids.map(BigInt)), [1, ids.length]),
input_lengths: new ort.Tensor('int64', BigInt64Array.from([BigInt(ids.length)]), [1]),
scales: new ort.Tensor('float32', Float32Array.from([cfg.inference.noise_scale, len, cfg.inference.noise_w]), [3])
});
for (const ls of [1.0, 0.85, 0.7]) {
const feeds = mk(ls);
let o = await sess.run(feeds);
const k=sess.outputNames[0];
const frames=o[k].data.length;
let tot=0; for(let i=0;i<8;i++){const a=Date.now(); await sess.run(feeds); tot+=Date.now()-a;}
const avg=tot/8/1000, dur=frames/cfg.audio.sample_rate;
console.log(`[${ep}] length_scale=${ls} audio ${dur.toFixed(2)}s infer ${avg.toFixed(3)}s RTF ${(avg/dur).toFixed(4)} rss ${Math.round(process.memoryUsage().rss/1048576)}MB`);
}
})().catch(e=>console.log(`[${process.argv[2]}] ERR`, e.message));