Gade SDL

March 14, 2026 · View on GitHub

Ada 2022 Mentioned in Awesome Ada

Gade SDL

Gade SDL on macOS

A SDL front end in Ada for Gade, intended as a minimal, easily portable reference implementation.

Usage

Build

This setup has been tested on MacOS 12 (Intel) and MacOS 26 (Apple Silicon) so far.

Build using Alire:

alr build

This should install the required Gade and SDLAda dependencies, along with the SDL2 libraries if your system has an Alire supported package manager.

The executable is generated at bin/gade.

Run

bin/gade [options] [rom_file]

If rom_file is omitted, the app starts and waits for a ROM to be dropped on the window.

Options

  • -u, --uncapped: run without frame-rate cap.
  • -l=<level>, --log=<level>: SDL log priority (for example debug, info, warn, error).
  • -h, --help: show help.

Examples

bin/gade path/to/game.gb
bin/gade --uncapped path/to/game.gb
bin/gade --log=debug path/to/game.gb

Controls

  • Z: A
  • X: B
  • Enter: Start
  • Backspace: Select
  • Arrow keys: D-Pad
  • Space: Fast-forward (hold)

Audio Pipeline

The front end uses a form of Dynamic Rate Control to sync the audio to the video.

  1. Runtime.Main_Loop produces emulator audio in chunks (Producer_Chunk_Samples), inside Generate.
  2. It calls Audio.IO.Queue_Asynchronously to hand off each chunk.
  3. Audio.IO.Queue_Asynchronously writes raw Stereo_Sample data into Source_Ring (Buffers.Transactional_Ring.Transactional_Ring_Buffer).
  4. Audio.IO.Resampling_Task runs concurrently:
    • reads from Source_Ring,
    • computes fill error from Output_Ring level (Audio.Callback.Level),
    • applies PI-based dynamic rate control (PID without the D/derivative: Proportional_Gain, Integral_Gain, clamped by Max_Delta),
    • resamples via Audio.Resampler (cubic interpolation),
    • writes float stereo frames into Output_Ring (another transactional ring).
  5. Audio.Callback.SDL_Callback is consumer-only:
    • drains output Ring into SDL’s output buffer,
    • writes silence for any remainder (underrun protection).

So the data flow is:

flowchart LR
    A[Main Loop<br/>Generate audio chunks] -->|Integer Samples| B
    B[Source Ring<br/>Transactional Ring Buffer]
    B --> C[Resampling Task<br/>PI-based Dynamic Rate Control]
    C -->|Float Samples| D[Output Ring<br/>Transactional Ring Buffer]
    D --> E[SDL Callback]
    E --> F[SDL Audio Device]

    D -- Output Ring Fill --> C