/* Model the cut rules with the SHIPPED constants and drive them with synthetic speech patterns. The question is: how long does a segment get, per style? */ const fs=require('fs'); const src=fs.readFileSync('engine.js','utf8'); const g=(n)=>Number(src.match(new RegExp('const '+n+'\\s*=\\s*Math\\.round\\((\\d+) / FRAME_MS\\)'))[1]); const FRAME=Number(src.match(/const FRAME\s*=\s*(\d+)/)[1]); const SAMPLE_RATE=Number(src.match(/const SAMPLE_RATE\s*=\s*(\d+)/)[1]); const FRAME_MS=(FRAME/SAMPLE_RATE)*1000; const F=(n)=>Math.round(g(n)/FRAME_MS); // source stores ms; the loop counts frames const C={SOFT_CUT:F('SOFT_CUT'),SOFT_DIP:F('SOFT_DIP'),HARD_CUT:F('HARD_CUT'), HARD_CUT_MAX:F('HARD_CUT_MAX'),EARLY_CLOSE:F('EARLY_CLOSE'), EARLY_MIN_SEG:F('EARLY_MIN_SEG'),SILENCE_TO_END:F('SILENCE_TO_END')}; console.log('FRAME_MS='+FRAME_MS+' constants (ms): '+ Object.entries(C).map(([k,v])=>k+'='+Math.round(v*FRAME_MS)).join(' ')+'\n'); /* pattern: fn(frameIndex) -> {speech:bool, dipish:bool, sentenceEnded:bool} */ function run(name, pattern, maxFrames){ let len=0,silence=0,dip=0,cuts=[]; for (let i=0;i=C.HARD_CUT_MAX) why='ceiling'; else if (silence>=C.SILENCE_TO_END) why='silence'; else if (len>=C.SOFT_CUT && dip>=C.SOFT_DIP) why=(len>=C.HARD_CUT?'hard':'soft'); else if (p.sentenceEnded && len>=C.EARLY_MIN_SEG && silence>=C.EARLY_CLOSE) why='early'; else if (p.sentenceEnded && len>=C.SOFT_CUT) why='sentence'; if (why){ cuts.push({ms:len*FRAME_MS,why}); len=0;silence=0;dip=0; } } const avg=cuts.length?Math.round(cuts.reduce((a,c)=>a+c.ms,0)/cuts.length):0; const worst=cuts.length?Math.max(...cuts.map(c=>c.ms)):0; console.log(name); console.log(' segments: '+cuts.length+' avg '+avg+'ms worst '+worst+'ms'); console.log(' reasons : '+JSON.stringify(cuts.reduce((a,c)=>(a[c.why]=(a[c.why]||0)+1,a),{}))); return {avg,worst}; } const S=(ms)=>Math.round(ms/FRAME_MS); /* A fast preacher: continuous speech, a sentence boundary every ~4s, and no pause anywhere long enough for the old rules to notice. */ run('FAST preacher — no pauses, sentence every 4s', (i)=>({ speech:true, dipish:false, sentenceEnded: i%S(4000)===S(3999) }), S(60000)); /* Same speaker but Whisper never reports a sentence end (worst case). */ run('FAST preacher — no pauses, no punctuation at all', (i)=>({ speech:true, dipish:false, sentenceEnded:false }), S(60000)); /* A normal speaker: 150ms micro-gaps between phrases every 2s. */ run('NORMAL speaker — 150ms gap every 2s', (i)=>{ const inGap = (i%S(2000)) >= S(1850); return { speech:!inGap, dipish:inGap, sentenceEnded:false }; }, S(60000)); /* Someone who stops properly at the end of each sentence. */ run('DELIBERATE speaker — 600ms full stop every 5s', (i)=>{ const inGap = (i%S(5000)) >= S(4400); return { speech:!inGap, dipish:inGap, sentenceEnded:false }; }, S(60000));