Introduction to the AudioBridge
The MOS Technology 6581/8580 Sound Interface Device (SID) is arguably the most famous and loved sound chip of the 8-bit era. RIFT64 features the AudioBridge, an optimized multi-mode audio synthesis engine (SoundBridge) running directly inside the Commodore 64 client's background interrupt loop.
By linking your high-level server application with the AudioBridge, you can trigger complex, native 3-voice sound effects, dynamically define ADSR instruments, play chordal and classical arpeggios, and stream notes with absolute real-time precision over the serial bridge.
Audio Engine Modes (AM)
To provide safe, glitch-free concurrent execution, the AudioBridge operates under four primary coordination modes, configured via the AM protocol command:
| ID | Mode | Description |
|---|---|---|
| 00 | PLAYER_ONLY | Traditional tracker background music player (Cadaver's MiniPlayer2) has complete control of all 3 SID voices. Direct soundbridge notes are disabled. |
| 01 | SOUNDBRIDGE_ONLY | Tracker player is paused. SoundBridge has complete control of all 3 SID voices for keyboard synthesis, SFX trigger, and multi-voice tracks. |
| 02 | MIXED_PLAYER_PLUS_SFX | Tracker player owns Voice 1 and Voice 2 to play background music. Voice 3 is isolated and locked exclusively for SoundBridge sound effects and plucks. |
| 03 | DIRECT_SID_MANUAL | Background interrupt updates are suspended. SID chip registers ($D400–$D418) are modified directly via the AF, AQ, etc., manual stream commands. |
Protocol Command Reference
The AudioBridge commands are categorized into Control Commands (requiring protocol handshake ACK/NAK) and Real-Time Stream Commands (instant, unframed no-ACK packets).
1. Control Commands (ACK/NAK)
- AR (Audio Reset): Silences all 25 SID registers, clears shadow registers, terminates any active sound effects, and stops voice ownership.
- AV (Audio Volume): Sets the master global volume register ($D418, low 4-bits) while preserving filter cutoffs.
- AM (Audio Mode): Coordinates mode changes (0..3) with safe register wipe cleanups.
- AI (Define Instrument): Stores a five-byte instrument definition record
[PL, PH, AD, SR, CT]into C64 memory for quick-access voice note triggering. - AB (SFX Base): Relocates the 16-slot SFX bytecode bank to a new page-aligned base (the byte is the high address byte; each slot is 64 bytes). Defaults to
$C000on the client until changed. - AS (SFX Play): Triggers a custom bytecode sound effect script in Slot ID 0..15 on Voice 3, respecting priority-based preemption.
- AX (SFX Stop): Immediately halts active bytecode sound effects on Voice 3 and silences the channel.
- AZ (Stop All): Instantly cuts gates and silences all three voices.
2. Real-Time Stream Commands (No-ACK)
- AN (Note On): Plays a note on
voice(0..2) at 16-bit pitchfreq, loading the ADSR, Waveform, and Pulse Width from the specifiedinstrumentId. Active voice effects persist across Note Ons (so e.g. an armed Slide glides from the previous note); disarm them explicitly by sendingAEwith effect type Off. - AO (Note Off): Clears the Gate bit of
voiceto start the release envelope decay phase. - AF (Full Voice Setup): Performs a low-level, raw write of all 7 voice parameters (FL, FH, PL, PH, AD, SR, CT).
- AQ (Set Frequency): Writes a new 16-bit frequency directly to
voice, updating the reference pitch anchor. - AP (Set Pulse Width): Writes a 12-bit pulse width duty cycle register.
- AD (Set ADSR): Modifies Attack/Decay and Sustain/Release registers.
- AW (Set Control): Direct write to Waveform/Control register (gate, wave, sync, ring).
- AE (Set Voice Effect): Sets local note effect parameters (Vibrato, Slide, PWM, Arpeggio) executed in the C64 background interrupt loop.
Local Note Effects Engine (AE)
Modulating notes at 50Hz/60Hz over the serial link is highly bandwidth-intensive. The SoundBridge core offloads this work to the C64 IRQ using the **Local Effects Engine (AE Command)**. Effects are applied locally using highly optimized, multiplier-free 6502 assembly:
1. Local Vibrato (FX=01)
Periodically modulates the active oscillator pitch above and below a reference base pitch using a 16-step signed triangle oscillation lookup table. Uses high-performance 16-bit shift-and-add logic instead of generic multiplier loops to preserve critical IRQ cycles.
2. Local Pitch Slide / Portamento (FX=02)
Implements portamento: once armed, each new Note On on that voice glides from the previous note's pitch to the new note's pitch instead of jumping. The engine ramps the live frequency toward the target by depth SID units per gated step, while speed gates how often a step occurs (higher speed = faster glide; $00 freezes the glide). Direction (up or down) is chosen automatically from the sign of (target − current), and the ramp snaps exactly onto the target to avoid overshoot. The first note after arming plays normally, since there is no previous pitch to glide from.
3. Pulse-Width Modulation (FX=03)
Sweeps the duty cycle of pulse waves dynamically, creating rich, chorus-like thick analog leads. To prevent complete silences on extreme pulse values, the sweep is strictly restricted to the high-efficiency musical range ($0080–$0F80).
4. Arpeggios (FX=04)
Allows classic C64 "chords" to be rendered on a single voice by cycling rapidly between the root note, 3rd, and 5th intervals, creating the illusion of polyphony on a monophonic channel.
C# SDK Developer Examples
The RIFT64 modern C# SDK wraps the AudioBridge commands into highly readable, asynchronous, thread-safe methods:
The SoundBridge API is fully strongly typed: use the
SoundBridgeVoice (Voice1..Voice3),
SoundBridgeAudioMode (PlayerOnly, SoundBridgeOnly, MixedPlayerPlusSfx, DirectSidManual),
SoundBridgeEffect (Off, Vibrato, Slide, Pwm, Arpeggio) and the
[Flags] SidWaveform (Triangle, Sawtooth, Pulse, Noise, Gate, Sync, RingMod, Test) enums
instead of raw bytes. Plain byte overloads remain available for advanced use.
1. Basic Synthesiser Setup
// Reset Audio Engine and set Volume to maximum
await client.SoundBridgeResetAsync(cancellationToken);
await client.SoundBridgeSetVolumeAsync(15, cancellationToken);
// Define a Pulse Lead instrument (Pulse wave + gate, Pulse width $0400, snappy Attack)
await client.SoundBridgeDefineInstrumentAsync(
id: 3,
pulseWidth: 0x0400,
attackDecay: 0x01,
sustainRelease: 0xF8,
control: SidWaveform.Pulse | SidWaveform.Gate,
cancellationToken);
2. Triggering Notes & Applying Effects
// Play C-4 (Freq $1167) on Voice 3 using Instrument 3
await client.SoundBridgeNoteOnAsync(SoundBridgeVoice.Voice3, 0x1167, instrumentId: 3, cancellationToken);
// Immediately apply deep, fast Vibrato locally on V3 (Speed $30, Depth $20)
await client.SoundBridgeSetEffectAsync(
SoundBridgeVoice.Voice3,
SoundBridgeEffect.Vibrato, // Off, Vibrato, Slide, Pwm, Arpeggio
speed: 0x30,
depth: 0x20,
cancellationToken);
// Let it sing for 2 seconds
await Task.Delay(2000, cancellationToken);
// Release the note
await client.SoundBridgeNoteOffAsync(SoundBridgeVoice.Voice3, cancellationToken);
3. Portamento Glides (Slide)
// Arm portamento on Voice 3: each new note glides from the previous one.
// Speed gates the ramp rate; Depth is the step size in SID units.
await client.SoundBridgeSetEffectAsync(SoundBridgeVoice.Voice3, SoundBridgeEffect.Slide, speed: 0xC0, depth: 0x10, cancellationToken);
// First note plays normally (no previous pitch to glide from).
await client.SoundBridgeNoteOnAsync(SoundBridgeVoice.Voice3, 0x1167, instrumentId: 3, cancellationToken); // C-4
await Task.Delay(400, cancellationToken);
// Each following note glides from the last note's pitch to the new one.
await client.SoundBridgeNoteOnAsync(SoundBridgeVoice.Voice3, 0x22CD, instrumentId: 3, cancellationToken); // glides up to C-5
await Task.Delay(600, cancellationToken);
await client.SoundBridgeNoteOnAsync(SoundBridgeVoice.Voice3, 0x1167, instrumentId: 3, cancellationToken); // glides back down to C-4
await Task.Delay(400, cancellationToken);
await client.SoundBridgeNoteOffAsync(SoundBridgeVoice.Voice3, cancellationToken);
4. Arpeggiated Chords (Arp)
// Hold a root note, then arm an A-minor arpeggio on Voice 3.
// holdFrames = frames per tone; minor:true = (0,+3,+7), false = (0,+4,+7).
await client.SoundBridgeNoteOnAsync(SoundBridgeVoice.Voice3, 0x0EA2, instrumentId: 3, cancellationToken); // A-3 root
await client.SoundBridgeSetArpeggioAsync(SoundBridgeVoice.Voice3, holdFrames: 2, minor: true, cancellationToken);
await Task.Delay(1500, cancellationToken);
// Disarm the effect (Off) and release the note.
await client.SoundBridgeSetEffectAsync(SoundBridgeVoice.Voice3, SoundBridgeEffect.Off, speed: 0, depth: 0, cancellationToken);
await client.SoundBridgeNoteOffAsync(SoundBridgeVoice.Voice3, cancellationToken);