Skip to main content
The PSL1GHT audio library provides a simple interface to play raw PCM audio by filling a circular buffer. Audio samples are 32-bit floating point values ranging from -1 to 1.

Overview

The audio system uses a producer-consumer model where your application fills audio blocks in a circular buffer, and the hardware consumes them for playback. Events notify you when blocks are ready to be filled. Key concepts:
  • Audio ports: Logical audio output channels (2-channel stereo or 8-channel)
  • Blocks: Fixed-size buffers (256 samples) that you fill with audio data
  • Event queue: Notifies when the next block is ready to be written

Initialization

1

Initialize the audio subsystem

Call audioInit() to initialize the audio system for your application.
audio/audio.h
#include <audio/audio.h>

s32 ret = audioInit();
if (ret != 0) {
    printf("Failed to initialize audio: %d\n", ret);
    return -1;
}
2

Configure audio port parameters

Set up the port configuration with channel count, buffer blocks, and initial volume.
samples/audio/audiotest/source/main.c
audioPortParam params;
params.numChannels = AUDIO_PORT_2CH;  // 2 or 8 channels
params.numBlocks = AUDIO_BLOCK_8;     // 8, 16, or 32 blocks
params.attrib = 0;                    // or AUDIO_PORT_INITLEVEL
params.level = 1.0f;                  // volume (0.0 to 1.0)
Use AUDIO_PORT_INITLEVEL in the attrib field to set an initial volume level. Otherwise, set attrib to 0.
3

Open the audio port

Open an audio port with your parameters. This returns a unique port number.
samples/audio/audiotest/source/main.c
u32 portNum;
s32 ret = audioPortOpen(&params, &portNum);
if (ret != 0) {
    printf("Failed to open audio port: %d\n", ret);
    audioQuit();
    return -1;
}
4

Get the port configuration

Retrieve the actual configuration including buffer addresses.
samples/audio/audiotest/source/main.c
audioPortConfig config;
ret = audioGetPortConfig(portNum, &config);
printf("Audio buffer start: 0x%X\n", config.audioDataStart);
printf("Read index: 0x%X\n", config.readIndex);
printf("Blocks: %ld\n", config.numBlocks);

Playing Audio

Basic Playback (Polling)

The simplest approach polls the hardware read index and writes ahead of it:
samples/audio/audiotest/source/main.c
void fillBuffer(float *buf) {
    static float pos = 0;
    
    for (unsigned int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
        // Generate stereo sine wave
        buf[i*2 + 0] = sin(pos);      // Left channel
        buf[i*2 + 1] = sin(pos * 2);  // Right channel
        pos += 0.01f;
        if (pos > M_PI)
            pos -= 2 * M_PI;
    }
}

u32 playOneBlock(u64 *readIndex, float *audioDataStart) {
    static u32 audio_block_index = 1;
    
    // Get current hardware position
    u64 current_block = *readIndex;
    
    // Don't overwrite block being played
    if (audio_block_index == current_block)
        return 0;
    
    // Calculate block address
    float *buf = audioDataStart + 2 * AUDIO_BLOCK_SAMPLES * audio_block_index;
    fillBuffer(buf);
    
    // Advance to next block
    audio_block_index = (audio_block_index + 1) % AUDIO_BLOCK_8;
    return 1;
}

// Start playback
audioPortStart(portNum);

// Play audio blocks
while (playing) {
    playOneBlock(
        (u64*)(u64)config.readIndex,
        (float*)(u64)config.audioDataStart
    );
}
Never write to the block currently being read by the hardware (readIndex). Always stay at least one block ahead.

Event-Driven Playback

For more precise timing, use event queues to know exactly when blocks are ready:
samples/audio/audiosync/source/main.c
#include <audio/audio.h>
#include <sys/event_queue.h>

sys_event_queue_t snd_queue;
sys_ipc_key_t snd_key;

// Create event queue for audio notifications
ret = audioCreateNotifyEventQueue(&snd_queue, &snd_key);
ret = audioSetNotifyEventQueue(snd_key);
ret = sysEventQueueDrain(snd_queue);  // Clear initial events

// Start playback
audioPortStart(portNum);

// Wait for events and fill blocks
void playOneBlock(audioPortConfig *config) {
    sys_event_t event;
    
    // Wait for block ready event (20ms timeout)
    s32 ret = sysEventQueueReceive(snd_queue, &event, 20*1000);
    if (ret != 0) return;
    
    u64 current_block = *(u64*)((u64)config->readIndex);
    f32 *dataStart = (f32*)((u64)config->audioDataStart);
    u32 audio_block_index = (current_block + 1) % config->numBlocks;
    
    // Fill the next block
    f32 *buf = dataStart + config->channelCount * AUDIO_BLOCK_SAMPLES * audio_block_index;
    fillBuffer(buf);
}

Audio Format

Sample Format

  • Type: 32-bit floating point (float)
  • Range: -1.0 to 1.0
  • Samples per block: 256 (AUDIO_BLOCK_SAMPLES)

Channel Layout

Samples are interleaved by channel:
// For each sample index i:
buf[i*2 + 0] = left_sample;   // Left channel
buf[i*2 + 1] = right_sample;  // Right channel

Block Size Calculation

// Size of one block in bytes
size_t block_size = numChannels * AUDIO_BLOCK_SAMPLES * sizeof(float);

// Address of block N
float *block_addr = audioDataStart + N * numChannels * AUDIO_BLOCK_SAMPLES;

Shutdown

Always clean up audio resources in reverse order:
1

Stop the audio port

audioPortStop(portNum);
2

Remove event queue (if used)

samples/audio/audiosync/source/main.c
audioRemoveNotifyEventQueue(snd_key);
sysEventQueueDestroy(snd_queue, 0);
3

Close the port

audioPortClose(portNum);
4

Quit the audio subsystem

audioQuit();

Port Status

The audioPortConfig::status field indicates the current port state:
ConstantValueDescription
AUDIO_STATUS_READY1Port is ready to play
AUDIO_STATUS_RUN2Port is currently playing
AUDIO_STATUS_CLOSE0x1010Port has been closed

API Reference

See audio/audio.h for the complete audio API:
  • audioInit() - Initialize audio subsystem
  • audioQuit() - Shut down audio subsystem
  • audioPortOpen() - Open an audio port
  • audioPortStart() - Start playback
  • audioPortStop() - Stop playback
  • audioPortClose() - Close an audio port
  • audioGetPortConfig() - Get port configuration
  • audioCreateNotifyEventQueue() - Create event queue
  • audioSetNotifyEventQueue() - Set current event queue
  • audioRemoveNotifyEventQueue() - Disconnect event queue

Examples

  • Basic playback: samples/audio/audiotest/ - Simple polling-based audio playback
  • Event-driven: samples/audio/audiosync/ - Audio-video synchronization using events

Build docs developers (and LLMs) love