diff --git a/src/engine/music.js b/src/engine/music.js index bcbbbca..3bca7c7 100644 --- a/src/engine/music.js +++ b/src/engine/music.js @@ -26,12 +26,17 @@ export function trackFor(story, { screen, scene, phase, closeup, at, ok = () => } // Il volume di ogni console: pieno fino a `near` pixel, poi cala fino a `far` (non sotto un filo, `floor`: la festa -// si sente sempre). La più forte per prima. +// si sente sempre). Sotto una console (entro `near`, sfumando fino a una volta e mezzo) le altre calano fino al loro +// filo: lì si sente bene la sola musica di quella (Andrea, 31 ottobre, in cima a Via dell'Antica Posta). La più forte +// per prima. export function mix(stages, [x, y]) { - return stages.map(st => { - const d = Math.hypot(x - st.at[0], (y - st.at[1]) * 1.5), near = st.near ?? 90, far = st.far ?? 520; - const k = Math.max(0, Math.min(1, (d - near) / (far - near))); - return { src: st.music, gain: Math.max(st.floor ?? 0.08, (1 - k) ** 1.6) }; + const near = st => st.near ?? 90, floor = st => st.floor ?? 0.08; + const d = stages.map(st => Math.hypot(x - st.at[0], (y - st.at[1]) * 1.5)); + const under = stages.map((st, i) => Math.max(0, Math.min(1, (near(st) * 1.5 - d[i]) / (near(st) * 0.5)))); + return stages.map((st, i) => { + const far = st.far ?? 520, k = Math.max(0, Math.min(1, (d[i] - near(st)) / (far - near(st)))); + const others = Math.max(0, ...under.filter((_, j) => j !== i)); + return { src: st.music, gain: Math.max(floor(st), (1 - k) ** 1.6 * (1 - others)) }; }).sort((a, b) => b.gain - a.gain); } diff --git a/src/story/story.js b/src/story/story.js index 7c41987..2c78db9 100644 --- a/src/story/story.js +++ b/src/story/story.js @@ -1912,11 +1912,13 @@ export const story = { { type: 'pixelspots', phase: 'ingorgo', area: [130, 242, 490, 108], bpm: 122, count: 5, size: 20, colors: ['155,65,255', '65,110,255'] }, ], stages: [ - { when: 'ingorgo', music: 'assets/audio/ingorgo-underground.mp3', at: [565, 235] }, + // Giù a destra, sul marciapiede sotto il giardinetto, si arriva sotto la console: lì solo l'underground. + { when: 'ingorgo', music: 'assets/audio/ingorgo-underground.mp3', at: [565, 235], near: 190 }, { when: 'ingorgo', music: 'assets/audio/ingorgo-techno.mp3', at: [270, 120], far: 600 }, { when: 'ingorgo', music: 'assets/audio/ingorgo-house.mp3', at: [240, 640], far: 750 }, ], - walk: [[0, 360], [0, 322], [190, 226], [283, 222], [470, 360]], + // Col marciapiede di destra, fra le catenelle e il giardinetto, fino in fondo (Andrea, 31 ottobre). + walk: [[0, 360], [0, 322], [190, 226], [283, 222], [350, 236], [452, 272], [640, 335], [640, 360]], depth: [[222, 0.35], [360, 1.15]], entry: [240, 350], hotspots: [ diff --git a/tests/ingorgo.test.mjs b/tests/ingorgo.test.mjs index 49b9760..9f01041 100644 --- a/tests/ingorgo.test.mjs +++ b/tests/ingorgo.test.mjs @@ -6,6 +6,7 @@ import { newIncanto, updateIncanto, drawIncanto, whiteness, T } from '../src/eng import { Game, check, POSTER_READY } from '../src/engine/game.js'; import { story } from '../src/story/story.js'; import { settle } from './helpers.mjs'; +import { inside as inPolygon, findPath } from '../src/engine/geometry.js'; import { render } from '../src/engine/render.js'; const ZONE = story.zones.find(z => z.flag === 'ingorgo'); @@ -36,6 +37,18 @@ test('la miscela delle console: vicino a una si sente quella, fra due tutte e du assert.deepEqual(on.map(t => t.src), ['a.mp3', 'b.mp3']); }); +test('sotto una console si sente la sola musica di quella: in cima a Via dell\'Antica Posta, il marciapiede sotto l\'underground', () => { + const sc = story.scenes.antica_posta_alta, ok = cond => cond === 'ingorgo'; + const spot = [620, 350]; + assert.ok(inPolygon(sc.walk, spot) && findPath(sc.walk, sc.entry, spot), 'ci si arriva a piedi'); + const under = trackFor(story, { screen: 'game', scene: 'antica_posta_alta', at: spot, ok }); + assert.equal(under[0].src, 'assets/audio/ingorgo-underground.mp3'); assert.equal(under[0].gain, 1); + for (const t of under.slice(1)) assert.ok(t.gain <= 0.1, `${t.src} solo un filo: ${t.gain.toFixed(2)}`); + // A metà della strada, invece, la miscela resta. + const mid = trackFor(story, { screen: 'game', scene: 'antica_posta_alta', at: [240, 300], ok }); + assert.ok(mid.filter(t => t.gain > 0.15).length >= 2); +}); + test('la musica suona più brani insieme e li fa sfumare quando si cambia piazza', () => { 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.5 });