Music mute everywhere + card-draw SFX
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 7m17s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m7s
CI/CD / Deploy - local stack (db + server + web) (push) Failing after 0s

- MusicToggle: global floating button (enable/disable music from any screen;
  hidden on the table, which has its own audio control in its HUD). Uses
  sound-store toggleMusic.
- Card sounds now use a synthesized card-draw "swish" (filtered noise burst with
  a downward sweep) for cardPlay (+ soft landing tap) and deal (a flurry),
  replacing the old beep tones.

Verified: tsc + next build pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-07 00:21:27 +03:30
parent 36600fa494
commit ed3e11b64b
3 changed files with 71 additions and 2 deletions
+31 -2
View File
@@ -115,6 +115,32 @@ class SoundManager {
notes.forEach(([freq, dur], i) => this.tone(freq, t0 + i * gap, dur, opts));
}
/** Filtered noise burst with a downward sweep — a card "swish" / draw sound. */
private swish(start: number, dur = 0.13, opts: { gain?: number; from?: number; to?: number } = {}) {
if (!this.ctx || !this.master) return;
const ctx = this.ctx;
const buf = ctx.createBuffer(1, Math.ceil(ctx.sampleRate * dur), ctx.sampleRate);
const data = buf.getChannelData(0);
for (let i = 0; i < data.length; i++) data[i] = Math.random() * 2 - 1;
const src = ctx.createBufferSource();
src.buffer = buf;
const bp = ctx.createBiquadFilter();
bp.type = "bandpass";
bp.Q.value = 0.9;
bp.frequency.setValueAtTime(opts.from ?? 3200, start);
bp.frequency.exponentialRampToValueAtTime(opts.to ?? 900, start + dur);
const g = ctx.createGain();
const peak = opts.gain ?? 0.28;
g.gain.setValueAtTime(0.0001, start);
g.gain.exponentialRampToValueAtTime(peak, start + 0.008);
g.gain.exponentialRampToValueAtTime(0.0001, start + dur);
src.connect(bp);
bp.connect(g);
g.connect(this.master);
src.start(start);
src.stop(start + dur + 0.02);
}
play(name: Sfx) {
if (!this.sfxEnabled) return;
this.init();
@@ -125,11 +151,14 @@ class SoundManager {
this.tone(520, t, 0.06, { type: "square", gain: 0.12 });
break;
case "cardPlay":
this.tone(360, t, 0.09, { type: "triangle", gain: 0.18, to: 220 });
// Draw-card swish + a soft low tap as it lands on the felt.
this.swish(t, 0.13, { gain: 0.3, from: 3200, to: 800 });
this.tone(150, t + 0.08, 0.06, { type: "sine", gain: 0.12, to: 90 });
break;
case "deal":
// A flurry of card-draw swishes (dealing).
for (let i = 0; i < 4; i++)
this.tone(320 + i * 20, t + i * 0.08, 0.07, { type: "triangle", gain: 0.14, to: 200 });
this.swish(t + i * 0.1, 0.1, { gain: 0.22, from: 3400, to: 1000 });
break;
case "trump":
this.seq([[440, 0.12], [660, 0.18]], 0.1, { type: "sine", gain: 0.25 });