# Combined audio for effect recipes

[Effect browser](../analysis/interface/effects/index.html) ·
[Audio manifest](../analysis/audio/recipes/manifest.json)

All 21 visual recipe entry points now have combined sound auditions. They share
**15 distinct sound sequences**, rendered once each and mapped through
`recipe_to_clip`. WAV, Ogg and sample-indexed register traces live together in
`analysis/audio/recipes/`. Identical sound layers do not create duplicate files.

Each layer retains its decoded FM patch, pitch parameter, level and duration.
The original `39A9`, `1121` and `10F4` paths program a separate voice on one
emulated OPL chip. This lets the chip combine the voices, rather than adding
independently normalised WAVs. There are **39 native voice checks** across the
15 sequences, covering patch registers, pitch, carrier attenuation, key-on and
independent key-off times.

## Controlled audition conditions

- The clock is assumed to run at 500 Hz: each timer tick is 96 output samples
  at 48 kHz. All layers are scheduled at recipe onset; their individual duration
  fields determine key-off, not the visual emitters' delayed starts.
- Voices are assigned fixed channels in recipe record order. Live allocation,
  distance admission, stereo placement, Doppler, music and voice stealing are
  absent. The temporary voice flags select the original nonspatial path with
  key-on/retrigger state; exported source flags remain preserved in metadata.
- Rendering uses the same pinned, vendored Nuked OPL3 core as the earlier sound
  auditions, in OPL2-compatible mode, with waveform selection enabled.
- Each shared clip extends through its longest member recipe, followed by two
  seconds of release padding. Long visual effects may therefore have substantial
  silence after their early sounds; later emitter starts do not imply another
  sound trigger.
- No loudness normalisation, gain correction or DC blocker is applied to PCM.
  FM waveforms can be asymmetric while sounding. Per-channel means and half-second
  means are retained for inspection; this is not the earlier MIDI-rendering path.

## Audio verification

All rendered WAVs contain nonzero PCM and no samples at either signed 16-bit
limit. Peaks, clipped-sample counts, uncompressed PCM hashes, channel means and
half-second means are saved beside each register trace. The Ogg files are also
decoded and checked for gross mean offsets, with decoded peaks recorded.
These checks do not claim sample identity between lossy Ogg and WAV, nor analog
output filtering identical to a physical Sound Blaster card.

## Browser playback

The existing Play/Pause, timeline and Reset controls now operate sound and the
composite visual together. With sound enabled, the visual timeline follows
`HTMLAudioElement.currentTime`, avoiding accumulation of an independent browser
animation clock. Seeking changes both. Disabling sound returns to the inspection
clock. Changing recipe or resetting pauses the previous audio; related recipes
reuse the same clip. Audio is optional, starts only on user interaction, and has
a listening-volume control plus direct Ogg/WAV/trace links.

The timeline includes the clip's release padding. This is extra audition time,
not a change to the original emitter lifetimes. The per-layer patch links in
the table remain baseline reference tones, distinct from the combined audition.
Browser checks verify audio progression, time alignment, pause, seek and reset,
alongside the existing recipe, phase and projection checks.

```sh
PYTHONPATH=/tmp/darker-python python3 tools/render_recipe_audio.py
PYTHONPATH=/tmp/darker-python python3 tools/extract_effect_recipes.py
NODE_PATH=/tmp/riotfire-browser/node_modules node tools/verify_effect_recipes.js
```

The first command requires the previously exported `recipes.json`; the second
embeds the new audio manifest into the browser. Original DOS files are unchanged.
