tools/voci.mjs (make voci) genera un mp3 per ogni battuta con la voce del personaggio scelta in tools/voci.json, solo per i testi nuovi o cambiati; tools/voci_xtts.py divide le frasi e toglie punti finali e caporali, che XTTS in italiano leggeva («Buongiorno, punto»). Nel gioco il fumetto dura quanto la voce, la musica si abbassa mentre si parla, il muto spegne anche le voci. Il leone ora parla e i personaggi parlano dalla posizione che la scena dichiara (speakers), che prima era ignorata. Tutte le voci verificate con Whisper; test: ogni battuta ha la sua voce, il fumetto non finisce prima della voce, la musica si abbassa (provati con errori iniettati). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdeKd62opy9PKgpQXtVctF
44 lines
2.1 KiB
JavaScript
44 lines
2.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { existsSync } from 'node:fs';
|
|
import { trackFor, Music } from '../src/engine/music.js';
|
|
import { story } from '../src/story/story.js';
|
|
|
|
test('il brano giusto per titolo, fine e scena', () => {
|
|
const s = { music: { title: 't.mp3', game: 'g.mp3', ending: 'e.mp3' }, scenes: { a: {}, b: { music: 'b.mp3' } } };
|
|
assert.equal(trackFor(s, { screen: 'title', scene: 'a' }), 't.mp3');
|
|
assert.equal(trackFor(s, { screen: 'intro', scene: 'a' }), 't.mp3');
|
|
assert.equal(trackFor(s, { screen: 'ending', scene: 'a' }), 'e.mp3');
|
|
assert.equal(trackFor(s, { screen: 'game', scene: 'a' }), 'g.mp3');
|
|
assert.equal(trackFor(s, { screen: 'game', scene: 'b' }), 'b.mp3');
|
|
assert.equal(trackFor({ scenes: {} }, { screen: 'game', scene: 'x' }), null);
|
|
});
|
|
|
|
test('volume e muto', () => {
|
|
const m = new Music({ volume: 0.5, muted: true });
|
|
assert.equal(m.setVolume(1.4), 1);
|
|
assert.equal(m.muted, false, 'alzare il volume toglie il muto');
|
|
assert.equal(m.toggle(), true);
|
|
assert.equal(m.setVolume(-1), 0);
|
|
assert.deepEqual(m.settings(), { volume: 0, muted: true });
|
|
});
|
|
|
|
test('i brani della storia esistono', () => {
|
|
const files = [...Object.values(story.music ?? {}), ...Object.values(story.scenes).map(s => s.music).filter(Boolean)];
|
|
assert.ok(files.length);
|
|
for (const f of files) assert.ok(existsSync(new URL('../' + f, import.meta.url)), `manca ${f}`);
|
|
});
|
|
|
|
test('mentre parla qualcuno la musica si abbassa, poi risale', () => {
|
|
globalThis.Audio ??= class { constructor(src) { this.src = src; this.volume = 0; this.paused = true; } play() { this.paused = false; return Promise.resolve(); } pause() { this.paused = true; } };
|
|
const m = new Music({ volume: 0.6 });
|
|
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
|
|
assert.equal(m.current.audio.volume, 0.6);
|
|
m.duck = 0.45;
|
|
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
|
|
assert.ok(Math.abs(m.current.audio.volume - 0.27) < 1e-9, 'abbassata sotto la voce');
|
|
m.duck = 1;
|
|
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
|
|
assert.equal(m.current.audio.volume, 0.6);
|
|
});
|