orbe-launcher/tools/gen-sounds.mjs
2026-10-05 17:06:13 +02:00

49 lines
2.4 KiB
JavaScript

// Génère les sons de navigation du thème Orbe (WAV 16 bits mono, 44,1 kHz) : sobres, courts, sans dépendance.
// Usage : node tools/gen-sounds.mjs
import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
const RATE = 44100
const out = join(import.meta.dirname, '..', 'themes', 'orbe', 'sounds')
/* voix : fréquence qui glisse de f0 à f1, enveloppe attaque/déclin, timbre sinus + un peu d'harmonique */
function tone(dur, f0, f1, { gain = 0.5, attack = 0.004, harm = 0.15, delay = 0 } = {}) {
const n = Math.round((dur + delay) * RATE), s = new Float32Array(n)
let phase = 0
for (let i = Math.round(delay * RATE); i < n; i++) {
const t = i / RATE - delay, p = t / dur
const f = f0 * Math.pow(f1 / f0, p)
phase += (2 * Math.PI * f) / RATE
const env = Math.min(1, t / attack) * Math.pow(1 - p, 2.2)
s[i] = gain * env * (Math.sin(phase) + harm * Math.sin(2 * phase))
}
return s
}
const mix = (...parts) => {
const n = Math.max(...parts.map((p) => p.length)), s = new Float32Array(n)
for (const p of parts) for (let i = 0; i < p.length; i++) s[i] += p[i]
return s
}
function wav(samples) {
const b = Buffer.alloc(44 + samples.length * 2)
b.write('RIFF', 0); b.writeUInt32LE(36 + samples.length * 2, 4); b.write('WAVE', 8)
b.write('fmt ', 12); b.writeUInt32LE(16, 16); b.writeUInt16LE(1, 20); b.writeUInt16LE(1, 22)
b.writeUInt32LE(RATE, 24); b.writeUInt32LE(RATE * 2, 28); b.writeUInt16LE(2, 32); b.writeUInt16LE(16, 34)
b.write('data', 36); b.writeUInt32LE(samples.length * 2, 40)
samples.forEach((v, i) => b.writeInt16LE(Math.round(Math.max(-1, Math.min(1, v)) * 32767), 44 + i * 2))
return b
}
const sounds = {
move: tone(0.035, 2400, 2200, { gain: 0.18, harm: 0 }), // petit tic
select: mix(tone(0.07, 660, 680, { gain: 0.32 }), tone(0.09, 990, 1000, { gain: 0.28, delay: 0.045 })), // deux notes qui montent
back: mix(tone(0.07, 740, 720, { gain: 0.28 }), tone(0.09, 494, 480, { gain: 0.26, delay: 0.045 })), // deux notes qui descendent
error: mix(tone(0.08, 180, 170, { gain: 0.35, harm: 0.4 }), tone(0.08, 180, 170, { gain: 0.35, harm: 0.4, delay: 0.11 })),
launch: mix(tone(0.45, 330, 1320, { gain: 0.3, attack: 0.02 }), tone(0.5, 495, 1980, { gain: 0.12, attack: 0.05, delay: 0.06 }))
}
mkdirSync(out, { recursive: true })
for (const [name, s] of Object.entries(sounds)) writeFileSync(join(out, name + '.wav'), wav(s))
console.log('sons écrits dans', out)