Manatee · Dreamcast audio & sound driver, explained
The Manatee driver

Driver architecture

Manatee is a tiny real-time kernel: one interrupt, one loop, a handful of engines and a single queue of MIDI-style events that ties them together. Everything fits in 33 KB of hand-written ARM assembly.

The engines

Host interface commands from the game status back to the game MIDI input external, port 7 Sequencer 8 song players tempo · loops · calls note-off queue Event ring 1024 events MIDI-shaped MIDI engine 128 MIDI channels 48 voices · stealing controllers · bend PCM players 8 one-shot ports 4 stream ports FX manager DSP programs · outputs AICA channels 0-47 MIDI voices 48-63 one-shots / streams AICA DSP reverb program + outputs external MIDI (FIQ) raw MIDI from the game (sdMidiSendMes) Arrows are control flow inside the driver. Everything runs on the main loop except MIDI input, which runs in the FIQ.
Figure 1. Manatee's engines. The event ring in the middle is the one place where the sequencer, raw MIDI from the game and external MIDI all meet the voice engine.
MIDI engine

Owns AICA channels 0-47. Keeps full controller state for 8 ports × 16 MIDI channels, and turns note-on events into voices using tone banks. See The MIDI engine & voices.

Sequencer

Eight song players, one per MIDI port, each with its own tempo, volume, pan, pitch and priority, feeding events into the ring. See The sequencer.

PCM players

Own AICA channels 48-63: eight one-shot ports for sound effects and voices, four stream ports for streamed music. See One-shots & PCM streams.

FX manager

Loads DSP programs cleanly, applies output mixes and positional pan. See The DSP and effects.

Who owns which channel

channels 0-47 · MIDI voices (music and sequenced effects) 48-63 · PCM slots Voice n always drives channel n. The 16 PCM slots are allocated by priority between one-shot and stream players.

Boot sequence

  1. Reset vectorSet the stack below the event ring, mask FIQ, mark the status word "initialising".
  2. SilenceMaster volume 0; key off all 64 channels with the fastest release.
  3. Clear and defaultZero the host area, event ring, port/channel/voice state and player state; give every MIDI channel power-on defaults (volume 100, expression 127, centred pan, the built-in tone bank); install the built-in banks (tone banks, drum kits, the "e-reverb" DSP program, a default output mix) in the bank table; mute and clear the DSP.
  4. Start the clockMaster volume up; drain the MIDI input; assign interrupt levels; start Timer A; enable its and MIDI-in's interrupts.
  5. GoWrite "SEGA" to the status word, unmask FIQ and enter the main loop forever.

The main loop

The loop never sleeps. On every pass it executes up to four queued events, then looks at the two flags the timer interrupt maintains:

Execute ≤ 4 events note on/off, controllers … 1 ms flag? no yes Envelope monitor sample one voice, select the next 4 ms flag? yes The 4 ms tick 1. host commands (if a batch is pending) 2. one-shot / stream players: fades, end detection 3. sequencer: advance 8 ports, emit due events 4. port fades: speed, volume, pan, pitch 5. voices: delayed key-ons, apply port changes 6. status records for the game 7. stream pitch · DSP ring buffer · FX load 8. envelope monitor no → loop
Figure 2. The main loop. Events run as fast as the loop spins; everything that needs steady timing runs on the 4 ms tick.

Because the event executor runs on every pass, a note triggered by the sequencer in step 3 of a tick typically sounds within microseconds of that tick, not 4 ms later. Parameter changes that affect already sounding voices (a port volume fade, say) are applied in step 5 of the same tick.

The event ring: one language for everything

The key design decision in Manatee is that every musical action becomes a MIDI-style event in one ring of 1024 entries. Each event is a single 32-bit word:

priority message type port 0-7 MIDI channel two data bytes Types: note off · note on · poly pressure · controller · program · channel pressure · pitch bend · system.
  • Producers: the sequencer (ports 0-7), the game directly (the "send MIDI message" host command) and the MIDI input (port 7).
  • Consumer: the main loop, four events per pass.
  • Priority travels with the event. A port's priority is stamped on every note it plays and becomes the voice's priority when it is allocated, so high-priority sounds can steal voices from music but not the other way round.
  • Overflow: the sequencer checks for a full ring and spills into its own overflow queue; the other producers do not check.

Sound RAM layout

0x00000 driver image (33 KB) stack (grows down to 0x0B000) 0x0B000 event ring 0x0C000 ports · MIDI channels · voices 0x10000 sequencer 0x11000 one-shot / stream players 0x13000 host interfacemailbox · status · bank table · log 0x18000 – 0x1FFFFF free (≈ 1.9 MB)banks · samples · stream rings · DSP buffer The first 96 KB belong to the driver. Everything above is managed by the game: the SH-4 decides where each bank goes and tells the driver through the bank table. Not to scale: the free region is ~20× the rest combined.
Figure 3. Sound RAM as the ARM sees it. The SH-4 sees the same bytes at 0x00800000 + offset.

Written in assembly

Manatee was written by hand in ARM assembly (credits in the image name Sega's Digital Media group), and it shows in ways that matter when reading it:

  • there is no calling convention: routines take values in whatever registers are convenient and sometimes return in two registers, or "return" to the instruction after the one they were called from to signal success;
  • scratch variables live inside the code image, between routines;
  • many routines share tails or fall into each other;
  • jump tables of branch instructions implement every switch.

The decompilation in this repository expresses all of that as ordinary C, preserving behaviour (including bugs), with a confidence rating per function. See Bugs, quirks & confidence.