From 9878689cfac2909b6d0080b09237f182eac520d2 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 10 Aug 2026 13:33:23 +0200 Subject: [PATCH] Effetti sonori sci-fi: sweep, modulazione, beacon e arpeggi via aevalsrc --- extensions/index.ts | 49 ++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/extensions/index.ts b/extensions/index.ts index d2661ba..bac5319 100644 --- a/extensions/index.ts +++ b/extensions/index.ts @@ -422,39 +422,52 @@ function cancelRecording(): Promise { } // --------------------------------------------------------------------------- -// Feedback sonori (bip) per le interazioni con la registrazione +// Feedback sonori sci-fi (sintesi procedurale ffmpeg) // --------------------------------------------------------------------------- type SoundType = "start" | "stop" | "cancel" | "timeout" | "done"; -const SOUNDS: Record = { - start: { freq: 880, dur: 0.15 }, // bip acuto breve (inizio) - stop: { freq: 660, dur: 0.25 }, // conferma - cancel: { freq: 220, dur: 0.3 }, // tono basso (annullamento) - timeout: { freq: 440, dur: 0.4, repeat: 2 }, // doppio bip di avviso - done: { freq: 990, dur: 0.2 }, // completamento (successo) +// Espressioni aevalsrc per effetti fantascientifici +const SOUNDS: Record = { + // Power up: sweep ascendente 440→1760Hz + start: { expr: "sin(2*PI*(440+1320*t/0.15)*t)*(1-t/0.15)", dur: "0.15" }, + // Deactivate: sweep discendente 1200→300Hz + stop: { expr: "sin(2*PI*(1200-900*t/0.18)*t)*(1-t/0.18)", dur: "0.18" }, + // Error/Abort: modulato discendente a bassa freq + cancel: { expr: "sin(2*PI*(350-200*t/0.35)*t)*exp(-2*t)*(1+0.3*sin(2*PI*8*t))", dur: "0.35" }, + // Warning beacon: pulsazione bi-tono (radar) + timeout: { expr: "if(lt(mod(t,0.5),0.15),sin(2*PI*700*t),if(lt(mod(t,0.5),0.35),0,sin(2*PI*500*t)))*0.8", dur: "1.2" }, + // Success chime: arpeggio Do-Mi-Sol acuto + done: { expr: "if(lt(t,0.08),sin(2*PI*523*t),if(lt(t,0.16),sin(2*PI*659*t),sin(2*PI*784*t)))*(1-0.3*t)", dur: "0.28" }, }; function playSound(type: SoundType) { const cfg = SOUNDS[type]; if (!cfg) return; const wav = path.join(os.tmpdir(), `agy-sound-${type}-${Date.now()}.wav`); - const { freq, dur, repeat = 1 } = cfg; - // genera il tono con ffmpeg (silenzioso) e riproduce con paplay/aplay/ffplay + // genera il tono sci-fi con ffmpeg (silenzioso) e riproduce con paplay/aplay/ffplay try { execFileAsync( "ffmpeg", - ["-hide_banner", "-loglevel", "error", "-y", "-f", "lavfi", "-i", `sine=frequency=${freq}:duration=${dur}`, wav], + [ + "-hide_banner", + "-loglevel", + "error", + "-y", + "-f", + "lavfi", + "-i", + `aevalsrc='${cfg.expr}':d=${cfg.dur}:s=44100`, + wav, + ], { timeout: 10_000 }, ) .then(() => { - for (let i = 0; i < repeat; i++) { - execFileAsync("paplay", [wav], { timeout: 5000 }).catch(() => { - // fallback su aplay/ffplay se paplay non disponibile - execFileAsync("aplay", ["-q", wav], { timeout: 5000 }).catch(() => { - execFileAsync("ffplay", ["-nodisp", "-autoexit", wav], { timeout: 5000 }).catch(() => {}); - }); + execFileAsync("paplay", [wav], { timeout: 5000 }).catch(() => { + // fallback su aplay/ffplay se paplay non disponibile + execFileAsync("aplay", ["-q", wav], { timeout: 5000 }).catch(() => { + execFileAsync("ffplay", ["-nodisp", "-autoexit", wav], { timeout: 5000 }).catch(() => {}); }); - } + }); // pulizia setTimeout(() => { try { @@ -462,7 +475,7 @@ function playSound(type: SoundType) { } catch { /* ignora */ } - }, repeat * 1500); + }, 3000); }) .catch(() => {}); } catch {