Voci: anche le risposte del motore e le scelte dei dialoghi hanno la voce; make ascolta le riascolta con Whisper; rifatte quelle storpiate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// 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`);
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"_nota": "Parole che XTTS legge male in italiano: a sinistra come sono scritte nella storia (e nel fumetto), a destra come vanno lette dalla voce. Si cerca la parola intera, senza badare alle maiuscole. Cambiare una riga rigenera solo le battute che la contengono (make voci). Esempio: «celo» (ce l'ho, delle figurine) XTTS lo legge «cielo».",
|
||||
"celo": "ce l'ho"
|
||||
"celo": "ce l'ho",
|
||||
"Cosimo I": "Cosimo primo",
|
||||
"padel": "pàdel"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user