Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
45 lines
2.6 KiB
JavaScript
45 lines
2.6 KiB
JavaScript
// Riascolta le voci del gioco (make ascolta): ogni mp3 di assets/voices/ passa da `mbot ascolta` (faster-whisper sulla
|
|
// GPU, skill «trascrittore») e si confronta col testo letto dalla voce. Elenca quelle che dicono altro: si rifanno
|
|
// con `node tools/voci.mjs --rifai 'pezzo di testo'`, oppure si aggiunge una riga a tools/pronuncia.json.
|
|
// node tools/ascolta.mjs [--min 0.8] [testo...] (con un testo: solo le battute che lo contengono)
|
|
import { execFileSync } from 'node:child_process';
|
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { spoken } from './pronuncia.mjs';
|
|
|
|
const ROOT = new URL('..', import.meta.url).pathname;
|
|
const index = JSON.parse(readFileSync(join(ROOT, 'assets/voices/index.json'), 'utf8'));
|
|
const args = process.argv.slice(2), at = args.indexOf('--min');
|
|
const min = at >= 0 ? args.splice(at, 2)[1] : '0.8';
|
|
const only = args;
|
|
// I numeri Whisper li scrive in cifre («Nove.» → «9.»): per il confronto le cifre tornano parole.
|
|
const NUMERI = ['zero', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove', 'dieci', 'undici', 'dodici'];
|
|
const plain = t => t.toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^a-z0-9]+/g, ' ').trim();
|
|
|
|
const jobs = Object.entries(index)
|
|
.map(([key, e]) => ({ who: key.split('|')[0], text: key.slice(key.indexOf('|') + 1), file: join(ROOT, 'assets/voices', e.file) }))
|
|
.filter(j => !only.length || only.some(o => j.text.includes(o)))
|
|
.map(j => ({ ...j, expect: spoken(j.text) }));
|
|
const dir = join(tmpdir(), 'leggenda-regolo-ascolto');
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, 'ascolto.json'), JSON.stringify(jobs.map(({ file, expect }) => ({ file, expect }))));
|
|
|
|
let out = '';
|
|
try {
|
|
out = execFileSync('mbot', ['ascolta', '--jobs', join(dir, 'ascolto.json'), '--json', '--min', min],
|
|
{ encoding: 'utf8', maxBuffer: 64 << 20, stdio: ['ignore', 'pipe', 'inherit'] });
|
|
} catch (e) { out = e.stdout ?? ''; }
|
|
|
|
const byFile = new Map(jobs.map(j => [j.file, j]));
|
|
const bad = [];
|
|
for (const line of out.split('\n').filter(l => l.startsWith('{'))) {
|
|
const r = JSON.parse(line);
|
|
if (r.ok !== false) continue;
|
|
const j = byFile.get(r.file), heard = r.text ?? r.heard ?? '';
|
|
if (j && plain(heard.replace(/\b(\d{1,2})\b/g, (m, n) => NUMERI[+n] ?? m)) === plain(j.expect)) continue;
|
|
bad.push({ ...j, heard, score: r.score ?? r.similarity });
|
|
}
|
|
for (const b of bad) console.log(`${b.score ?? '?'} ${b.who}: «${b.text}»\n sentito: «${b.heard}»`);
|
|
console.log(`${jobs.length} voci ascoltate, ${bad.length} da guardare`);
|