Audio Processing Unit (APU)

APU Overview

The Audio Processing Unit (APU) handles audio playback on the Sandpiper platform. It supports stereo 16-bit PCM audio at various sample rates (44.1 KHz, 22.05 KHz, 11.025 KHz) and uses DMA to stream audio data from shared memory to the audio hardware.

The APU works with a double-buffered system where the hardware toggles between two buffer halves. Applications can use APUFrame() to determine which buffer is currently being played and APUWaitSync() to synchronize audio updates with buffer swaps.

Data Structures

EAPUSampleRate

enum EAPUSampleRate
{
    ASR_44_100_Hz = 0,  // 44.1000 KHz
    ASR_22_050_Hz = 1,  // 22.0500 KHz
    ASR_11_025_Hz = 2,  // 11.0250 KHz
    ASR_Halt = 3,       // Halt audio playback
};

Defines the supported audio sample rates.

EAPUBufferSize

enum EAPUBufferSize
{
    ABS_128Bytes  = 0,  //   32 16bit stereo samples
    ABS_256Bytes  = 1,  //   64 16bit stereo samples
    ABS_512Bytes  = 2,  //  128 16bit stereo samples
    ABS_1024Bytes = 3,  //  256 16bit stereo samples
    ABS_2048Bytes = 4,  //  512 16bit stereo samples
    ABS_4096Bytes = 5,  // 1024 16bit stereo samples
};

Defines the supported audio buffer sizes. Each sample consists of two 16-bit values (left and right channels).

API Documentation

Initialization / Shutdown

int APUInitAudio(struct EAudioContext* _context, struct SPPlatform* _platform);

Initializes the audio context. This must be called before using any other APU functions. The sample rate is initially set to ASR_Halt (audio stopped). Returns 0 on success.

void APUShutdownAudio(struct EAudioContext* _context);

Shuts down the audio context. If audio is still playing, it will be halted automatically.

Configuration

void APUSetBufferSize(struct EAudioContext* _context, enum EAPUBufferSize _bufferSize);

Sets the audio buffer size. The buffer size determines how many samples are played before the APU switches to the next buffer half. Larger buffers provide more time to fill the next buffer but introduce more latency.

void APUSetSampleRate(struct EAudioContext* _context, enum EAPUSampleRate _sampleRate);

Sets the audio sample rate. Use ASR_Halt to stop audio playback.

void APUSwapChannels(struct EAudioContext* _context, uint32_t _swap);

Swaps the left and right audio channels. Pass a non-zero value to swap channels, or zero to keep the original order.

Playback Control

void APUStartDMA(struct EAudioContext* _context, uint32_t _audioBufferAddress16byteAligned);

Queues the next set of audio samples to be transferred to the APU's internal buffer. The buffer address must be 16-byte aligned and should be a physical address obtained from SPAllocateBuffer().

Synchronization

void APUSync(struct EAudioContext* _context);

Sends a no-operation command to the APU. This can be used to ensure previous commands have been processed.

uint32_t APUFrame(struct EAudioContext* _context);

Returns the current frame status (0 or 1). This indicates which half of the double buffer is currently being played. Applications should fill the inactive buffer half while the other is being played.

uint32_t APUGetWordCount(struct EAudioContext* _context);

Returns the number of words currently in the APU buffer. This can be used to monitor buffer fill level.

void APUWaitSync(struct EAudioContext *_context);

Blocks until the APU switches to the next buffer. This is useful for synchronizing audio data updates with buffer swaps.

Example Usage

// Initialize platform and audio
struct SPPlatform* platform = SPInitPlatform();
struct EAudioContext audioCtx;
APUInitAudio(&audioCtx, platform);

// Allocate audio buffer (must be 16-byte aligned)
struct SPSizeAlloc audioBuffer;
audioBuffer.size = 4096;  // Double buffer: 2x 2048 bytes
SPAllocateBuffer(platform, &audioBuffer);

// Configure audio
APUSetBufferSize(&audioCtx, ABS_2048Bytes);
APUSetSampleRate(&audioCtx, ASR_44_100_Hz);

// Start playback
APUStartDMA(&audioCtx, (uint32_t)audioBuffer.dmaAddress);

// Audio loop
while (playing) {
    uint32_t frame = APUFrame(&audioCtx);
    // Fill the inactive buffer half
    int16_t* samples = (int16_t*)(audioBuffer.cpuAddress + (frame ? 0 : 2048));
    // ... fill samples ...
    
    APUWaitSync(&audioCtx);  // Wait for buffer swap
}

// Cleanup
APUShutdownAudio(&audioCtx);
SPFreeBuffer(platform, &audioBuffer);
SPShutdownPlatform(platform);