Skip to content

Audio Callbacks

To ensure compatability across different audio hardware and operating systems, our SDK is designed to be platform-agnostic. Instead of handling audio output directly, the SDK outputs raw PCM audio samples through a set of callback functions defined in the struct splayer_audio_api_t. We provide complete implementations in the SDK package's src/ directory for Darwin (macOS/iOS) and ALSA (Linux), which you're welcome to use in your project. The reference app uses them. If you're targeting a different operating system or audio backend, you'll need to implement your own callback functions allowing you to integrate the SDK with your own audio pipeline. To help you get started, we've included a placeholder implementation below that serves as a basic skeleton for custom integration. For more information please refer to the API reference documentation in splayer_audio_api-3.h.

Handle samples as whole frames

The samples are interleaved 16-bit signed PCM. A frame is one sample per channel, so with two channels the buffer reads left, right, left, right, ... The SDK's audio pipeline expects every sample of a frame to be consumed together. For audio_data this means:

  • sample_count is always a whole number of frames.
  • The return value, the number of samples consumed, must be a multiple of the channel count. Return 0 when not even one frame fits.
  • samples_buffered must be a multiple of the channel count.

Backends that work in frames, such as ALSA's snd_pcm_writei, keep this alignment by themselves; the ALSA example converts sample_count to frames and the written frames back to samples. The rule is easy to break when a custom queue or ring buffer sits between audio_data and the backend: when the buffer has room for only part of the chunk, round down to a frame boundary rather than accepting the samples that fit. A partial frame, for example 3 samples consumed from a stereo chunk, leaves a lone sample behind. The player cannot recover from a partial frame, so it stops with a fatal error when audio_data returns one.

audio_output_dummy.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "splayerapi/splayer_audio_api-3.h"
struct dummy_audio_impl {
  struct splayer_audio_api api;
};
static void dummy_flush(struct splayer_audio_api* ctx) {}

static void dummy_init(struct splayer_audio_api* ctx, const int output_sample_channels, const int output_sample_rate) {}

static void dummy_pause(struct splayer_audio_api* ctx, int pause) {}

static size_t dummy_play_audio(struct splayer_audio_api* ctx, const int16_t* samples, size_t sample_count,
                               uint32_t* samples_buffered) {
  return 0; // Return the number of samples consumed, always a whole number of frames.
}

static void dummy_set_volume(struct splayer_audio_api* ctx, const int volume) {}

static void dummy_shutdown(struct splayer_audio_api* ctx) {}

struct splayer_audio_api* audio_api_allocate() {
  struct dummy_audio_impl* api = malloc(sizeof(struct dummy_audio_impl));
  memset(api, 0, sizeof(struct dummy_audio_impl));
  splayer_audio_api_t e = {dummy_init, dummy_shutdown, dummy_flush, dummy_play_audio, dummy_pause, dummy_set_volume};
  api->api = e;

  return &api->api;
}