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:
@@ -0,0 +1,35 @@
|
||||
/* Load and speak with every Chinese bundle the picker offers, plus a Piper
|
||||
voice to prove the espeak path did not regress. */
|
||||
const path=require('path'), os=require('os');
|
||||
const voices=require('./voices.js');
|
||||
const sherpa=require('sherpa-onnx-node');
|
||||
const DIR=path.join(os.homedir(),'Library/Application Support/Church Translate Voices');
|
||||
const C=require('./voice-catalog.js');
|
||||
|
||||
const TEXT='耶稣说,我就是道路、真理、生命。';
|
||||
const cases=C.forLanguage('zho_Hans').filter(v=>v.engine==='builtin')
|
||||
.map(v=>({bundle:v.bundle,model:v.model,text:TEXT}));
|
||||
cases.push({bundle:'vits-piper-de_DE-thorsten-medium',model:'de_DE-thorsten-medium.onnx',
|
||||
text:'Jesus sprach: Ich bin der Weg.'});
|
||||
|
||||
let ok=0,bad=0;
|
||||
for (const c of cases) {
|
||||
const dir=path.join(DIR,c.bundle);
|
||||
try {
|
||||
const cfg=voices.ttsConfig(dir,{model:c.model});
|
||||
const which=cfg.model.vits.lexicon?'lexicon':'espeak';
|
||||
const t=new sherpa.OfflineTts(cfg);
|
||||
const a=t.generate({text:c.text,sid:0,speed:1.0});
|
||||
const secs=a.samples.length/a.sampleRate;
|
||||
/* Silence means it "worked" but produced nothing — that is still a failure. */
|
||||
let peak=0; for(let i=0;i<a.samples.length;i++){const v=Math.abs(a.samples[i]); if(v>peak)peak=v;}
|
||||
const dead=peak<0.01;
|
||||
console.log((dead?' SILENT ':' OK ')+c.bundle.padEnd(38)+which.padEnd(9)+
|
||||
secs.toFixed(1)+'s peak '+peak.toFixed(3)+' @'+a.sampleRate);
|
||||
dead?bad++:ok++;
|
||||
} catch(e) {
|
||||
console.log(' FAIL '+c.bundle.padEnd(38)+String(e.message).split('\n')[0].slice(0,70));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
console.log('\n'+ok+' usable, '+bad+' not');
|
||||
Reference in New Issue
Block a user