std1.0.0-beta-7 SafeC docs

Source audio/audio_backend.h

View in the repository

// SafeC Standard Library — audio: the interface between audio.sc and the
// per-API backends (audio_<api>[_<os>].sc). std-private.
//
// Each backend is one AudioBackendVt_ (a table of functions) audio.sc picks
// by API id. A backend fills an AudioHost_'s device list in probe(), and
// opens/starts/stops an AudioStreamState_ that audio.sc allocated and filled
// with the request. On its real-time path it only calls
// audio_stream_process_() (convert + callback + convert, allocation- and
// lock-free) and the atomic flag helpers below.
#pragma once
#include <std/audio/audio.h>

#define AUDIO_DIR_OUT 0
#define AUDIO_DIR_IN  1
#define AUDIO_DEVICE_KEY_MAX 256

namespace std {

// A backend's private state, opaque here: each backend keeps its own struct
// (AlsaStream_, JackStream_, WStream_, ...) and converts its handle to and
// from these in its open()/close() and its one accessor.
struct AudioHostImpl_   { char opaque_; };
struct AudioStreamImpl_ { char opaque_; };

// A device a probe found: the public info plus what the backend needs to
// open it again (an ALSA PCM name, a JACK client name, a Core Audio
// AudioDeviceID, a WASAPI endpoint id).
struct AudioDeviceRec_ {
    struct AudioDeviceInfo info;
    char      key[AUDIO_DEVICE_KEY_MAX];
    long long native;
};

// A device key a probe has seen and the id it got (ids stay stable across
// rescans: a device that comes back gets its old id).
struct AudioKnownId_ {
    char key[AUDIO_DEVICE_KEY_MAX];
    unsigned int id;
};

struct AudioHost_ {
    int api;
    struct AudioDeviceRec_* devs;
    unsigned int count;
    unsigned int cap;
    struct AudioKnownId_* known;
    unsigned int known_count;
    unsigned int known_cap;
    unsigned int next_id;        // ids handed out so far
    unsigned int default_out;    // device ids, AUDIO_DEFAULT_DEVICE if none
    unsigned int default_in;
    ?&heap AudioHostImpl_ backend;   // backend-private, released by host_close
    struct AudioError error;
};

// One stream. Fields marked (req) are the request, set by audio.sc before
// the backend's open(); (be) are the backend's to set in open(); (rt) are
// touched on the real-time path, atomically.
struct AudioStreamState_ {
    int api;
    ?&static struct AudioBackendVt_ vt;   // the backend
    int mode_out;                     // (req) output direction requested
    int mode_in;                      // (req) input direction requested
    unsigned int user_format;         // (req) AUDIO_FORMAT_*
    int user_interleaved;             // (req)
    int user_channels[2];             // (req)
    int first_channel[2];             // (req) device channel offset
    unsigned int device_id[2];        // (req) resolved ids (0 if device_name used)
    char device_key[2][AUDIO_DEVICE_KEY_MAX]; // (req) the device records' keys
    long long device_native[2];       // (req)
    unsigned int sample_rate;         // (req) requested -> (be) actual
    unsigned int buffer_frames;       // (req) requested (0 = default) -> (be) actual
    unsigned int max_frames;          // (be) the largest period process() may get (>= buffer_frames)
    unsigned int buffer_count;        // (req) -> (be)
    unsigned int flags;               // (req) AUDIO_FLAG_*
    int priority;                     // (req)
    char stream_name[AUDIO_STREAM_NAME_MAX];   // (req)
    char device_name[AUDIO_DEVICE_NAME_MAX];   // (req) options.device_name

    unsigned int device_format[2];    // (be) the device's sample format
    int device_channels[2];           // (be) channels the device was opened with
    int device_interleaved[2];        // (be)
    long latency[2];                  // (be) frames

    int convert[2];                   // set after open: user != device layout
    // Each max_frames long (heap; empty for an unused direction).
    []unsigned char user_buf[2];      // what the callback sees
    []unsigned char device_buf[2];    // what the backend reads/writes (the same memory as user_buf if !convert)

    AudioCallbackRaw_ callback;       // the user's fn int(&AudioIo, ?&T), erased
    ?&Erased user;

    int state;                        // (rt) AUDIO_STREAM_*
    int stop_request;                 // (rt) 0, AUDIO_CB_STOP or AUDIO_CB_ABORT (from control or callback)
    unsigned int pending_status;      // (rt) AUDIO_STATUS_* the backend observed
    long long frames_done;            // (rt) frames processed
    long long frames_offset;          // set_stream_time()
    int rt_error_set;                 // (rt) 1 once rt_error holds an error
    struct AudioError rt_error;       // written once by the stream thread before rt_error_set
    unsigned long long thread;        // a stream thread std started (ALSA, WASAPI, dummy)
    int thread_live;                  // it hasn't been joined yet
    ?fn void(&struct AudioStreamState_) thread_body;   // what it runs
    ?&heap AudioStreamImpl_ backend;  // backend-private
    struct AudioError error;          // control-thread errors (open/start/stop)
};

// What a backend implements. Each returns AUDIO_OK or an AUDIO_ERR_* code,
// with the message in host.error / st.error.
struct AudioBackendVt_ {
    int api;
    fn int() available;                                   // can this API work here at all (lib/server present)?
    fn int(&AudioHost_) probe;                            // (re)fill the device list
    fn void(&AudioHost_) host_close;
    fn int(&AudioHost_, &AudioStreamState_) open;         // open the device(s); set the (be) fields
    fn int(&AudioStreamState_) start;
    fn int(&AudioStreamState_) stop;                      // drain, then stop
    fn int(&AudioStreamState_) abort;                     // stop now
    fn void(&AudioStreamState_) close;                    // release (stream is stopped)
};

// The backends compiled into this build (defined by each backend file).
&static AudioBackendVt_ audio_dummy_backend_();
#if defined(__linux__)
&static AudioBackendVt_ audio_alsa_backend_();
#endif
#if defined(__linux__) || defined(__APPLE__)
&static AudioBackendVt_ audio_jack_backend_();
#endif
#if defined(__APPLE__)
&static AudioBackendVt_ audio_coreaudio_backend_();
#endif
#if defined(_WIN32)
&static AudioBackendVt_ audio_wasapi_backend_();
#endif
#if defined(__wasm__)
&static AudioBackendVt_ audio_webaudio_backend_();
#endif

// ── for backends' probe() ────────────────────────────────────────────────
// Empties the list (ids are kept per key for the next adds).
void audio_host_clear_(&AudioHost_ h);
// Appends a device (the caller filled info except id); returns its record,
// null on OOM. A key seen in an earlier probe gets its old id back.
?&AudioDeviceRec_ audio_host_add_(&AudioHost_ h, &AudioDeviceInfo info, const &char key, long long native);
// A blank AudioDeviceInfo (no channels, no rates).
struct AudioDeviceInfo audio_device_info_blank_();
// Adds 'rate' to info's rate list (kept ascending, no duplicates).
void audio_device_add_rate_(&AudioDeviceInfo info, unsigned int rate);
// The standard rates std probes: 8000 .. 192000.
int audio_standard_rate_count_();
unsigned int audio_standard_rate_(int i);

// ── for backends' real-time path ─────────────────────────────────────────
// Runs one period of 'frames' (<= max_frames): device_buf[IN] -> user_buf[IN]
// conversion, the callback, user_buf[OUT] -> device_buf[OUT]. Returns the
// callback's AUDIO_CB_* (a stop/abort is also latched into stop_request).
int audio_stream_process_(&AudioStreamState_ st, unsigned int frames);
// Zeroes device_buf[OUT] for 'frames' frames (an underrun's silence).
void audio_stream_silence_(&AudioStreamState_ st, unsigned int frames);
// ORs AUDIO_STATUS_* into the next callback's status.
void audio_stream_flag_status_(&AudioStreamState_ st, unsigned int status);
// Latest stop request (0, AUDIO_CB_STOP or AUDIO_CB_ABORT).
int  audio_stream_stop_requested_(&AudioStreamState_ st);
void audio_stream_request_stop_(&AudioStreamState_ st, int how);
// The stream ended on its own (callback stop, device error): state ->
// STOPPED. 'err' may be null.
void audio_stream_mark_stopped_(&AudioStreamState_ st, ?&AudioError err);
// Records an error raised on the stream thread (first one wins).
void audio_stream_set_rt_error_(&AudioStreamState_ st, int code, const &char message);
// Bytes per frame of device_buf[dir].
unsigned long audio_stream_device_frame_bytes_(&AudioStreamState_ st, int dir);

// ── stream threads (ALSA, WASAPI, dummy) ─────────────────────────────────
// Starts body(st) on a new thread (made real-time when the stream asked);
// joins a previous, finished one first.
int  audio_stream_thread_start_(&AudioStreamState_ st, fn void(&AudioStreamState_) body);
// Joins the stream thread if one is live. Never from the stream thread.
void audio_stream_thread_join_(&AudioStreamState_ st);

} // namespace std