Play a sound through a graph of gain, pitch and EQ effects #311

A sound could only be played as it was uploaded. A Sound FX asset is
a node graph like a shader: Input, effects, Output. Play sound has a
new input fx that sends the voice through it.

- New asset type soundfx ("Sound FX"), not placeable, with a graph
  and its own tab. The heading in the asset panel is "Sound effects",
  so it does not take the click meant for the + menu entry.
- Nodes in core/soundfx/nodes.ts, category sound, port type audio
  that only fits audio: Input, Output, Gain (dB), Pitch (semitones),
  EQ (80 Hz and 12 kHz shelves, peaks at 250 Hz, 1 kHz and 4 kHz).
- compileSoundFx walks back from the Output with the shared graph
  emitter. An input takes one edge, so the result is one line: a
  pitch and a list of gain and filter stages. A node without input
  or a graph without Output reports a problem on the node.
- Pitch is the playback rate of the source. It applies to the whole
  sound and also changes its length, the node description says so.
- The BehaviorSystem resolves fx and hands the line to the AudioSink.
  An fx that is missing or does not compile plays the sound dry.
- SoundLibrary wires source, stages, volume, destination in that
  order. stats().last now also names pitch and stage count.
- The tab header picks one of the project's sounds and plays it
  through the graph as it stands. Stop there ends only its own voice.

Tests:
- soundfx.test.ts (new, 11), sound-library (9, +2 for the wiring),
  node-catalog (+1 for fx), graph and asset-groups adjusted
- e2e 34: ALL PASSED - 26 checks (+5: + menu opens the tab, palette,
  2 effects, tab Play and game Play through the FX at -12 st)
- Gate on melektaus-atx: typecheck, lint, prettier clean, 239 of 240
  test files green. The red one was sound-library (its fake
  AudioContext had no playbackRate), fixed afterwards, gate not
  re-run in full.
- Counter-check: without the audio rule in portsCompatible the port
  test fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-05 16:45:42 +02:00
1 parent 93c0a80b7d
commit de14f0f9e2
34 files changed
+899 -43

No files matched your search

+2
View File
@@ -34,6 +34,8 @@ export const ASSET_GROUP_LABELS: Record<AssetType, string> = {
shaderfunction: 'Shader functions',
font: 'Fonts',
sound: 'Sounds',
// Not "Sound FX": that is the `+` menu entry (see Vector art).
soundfx: 'Sound effects',
particles: 'Particles',
};
+2
View File
@@ -24,6 +24,7 @@ import scene from './icons/scene.svg?raw';
import shader from './icons/shader.svg?raw';
import shaderfunction from './icons/shaderfunction.svg?raw';
import sound from './icons/sound.svg?raw';
import soundfx from './icons/soundfx.svg?raw';
import sprite from './icons/sprite.svg?raw';
import statemachine from './icons/statemachine.svg?raw';
import struct from './icons/struct.svg?raw';
@@ -53,6 +54,7 @@ export const ASSET_ICON_SVG: Record<AssetType, string> = {
shader,
shaderfunction,
sound,
soundfx,
sprite,
statemachine,
struct,
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-8 -8 16 16" width="16" height="16">
<!-- Three faders of an equalizer: what a sound is played through. -->
<path d="M-4.5 -6.5 V6.5 M0 -6.5 V6.5 M4.5 -6.5 V6.5" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" />
<rect x="-6.5" y="0.5" width="4" height="2.5" rx="0.8" fill="currentColor" />
<rect x="-2" y="-4" width="4" height="2.5" rx="0.8" fill="currentColor" />
<rect x="2.5" y="-1" width="4" height="2.5" rx="0.8" fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 537 B