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>
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
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));
|
||||
Reference in New Issue
Block a user