Updated 7 November 2026

Plugin audio

A plugin produces sound by queueing 16-bit samples with audio_queue. The system mixes them into the console's audio output.

Contents

How it works

Property Value
Format Signed 16-bit, one mono stream
Sample rate 22050 Hz on both platforms; there is no resampling, so generate 22050 Hz
Capacity 16 383 frames (about 743 ms)
Units audio_queue and audio_get_free count frames, never int16_t elements
  • Stereo input is mixed down: each (L, R) frame is stored as (L + R) / 2, and the mono result plays on both speakers.
  • Overflow: audio_queue stores only the frames that fit and drops the rest. It never blocks. Check audio_get_free() first.
  • Underflow: when the queue runs empty, the output is silent until new frames arrive.
  • Volume: Lua sets the plugin volume with c8b.sound.setPluginGain(gain) (0.0 to 2.0). The console default is about 0.82; the emulator plays plugin audio at 1.0.
  • Mixing: plugin audio goes through the music bus together with tracker, MIDI and MP3 music.
  • Lifecycle: the queue is emptied at plugin.init() and plugin.cleanup(), so sound never carries over from one plugin to the next.
  • Threads: call audio_queue only from your plugin's entry points, never from an interrupt.

Keeping the queue filled

Keep a target amount of audio queued (around 300 ms) and top it up once per plugin_update:

enum { RATE = 22050, TARGET = RATE * 300 / 1000 };   /* 6615 frames */

size_t free_frames = api->audio_get_free();
size_t queued      = 16383 - free_frames;
if (queued < TARGET) {
    size_t n = TARGET - queued;
    render_mix(buf, n);                               /* your mixer */
    api->audio_queue(buf, n, /*stereo=*/false);
}

A plugin can also leave music to Lua (for example the MIDI player) and use the plugin audio queue only for its own sound.