RIFT64
RIFT64 Banner

AUDIOBRIDGE

Real-time SID Audio Synthesis, ADSR Profiling, and Sound Effects

🖥️ V1 Protocol Beta
🏎️ SwiftLink 38400+ Baud
💾 Legacy C64/C128 & Replicas

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)

2. Real-Time Stream Commands (No-ACK)

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);