DVX_GUI/src/libs/kpunch/audio/audio.h
2026-10-14 17:37:03 -05:00

594 lines
27 KiB
C

// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// audio.h -- sound library (audio.lib)
//
// Sound for DVX programs through whatever sound hardware the computer
// has. Every program opens its own audio context; any number of
// programs play at the same time through one mixer, and everything a
// program opened is stopped and released when it ends, however it ends.
//
// Samples a sound loaded from a WAV or VOC file (or memory, or raw
// PCM) and played on voices: volume, pan, pitch, looping,
// an end notification.
// Tones square, triangle and noise tones of a frequency and length,
// made by the mixer (on the PC speaker by its tone generator).
// Streams a program's own sound (music, speech) at any rate: it
// writes samples, or the library asks for them.
// Notes a queue of tones and rests played back to back, each to the
// exact sample (melodies; QBasic's PLAY and SOUND).
// Volume one master volume for the whole system and one per
// context.
// FM the FM synthesizer chip (OPL2 / OPL3), when there is one:
// two-operator voices shared by every program (tones use
// them too), or the whole chip for one program at a time.
// MIDI the MIDI port (MPU-401), for one program at a time.
// Desktop the sounds DVX plays for its own events (starting, message
// sounds boxes, ...), chosen in DVX.INI [sounds].
//
// The mixer works in 16-bit integers at the rate the sound device
// achieved (22,050 Hz unless DVX.INI [audio] rate says otherwise),
// resamples every source with linear interpolation and clips the sum.
// It mixes from the device's own interrupt (or the timer service for
// devices without one), so sound keeps playing however busy the
// programs are. At most the configured voice limit plays at once;
// starting one more drops the quietest.
//
// Square and triangle tones play on a free FM voice when the computer
// has an FM chip (DVX.INI [audio] fmTones), else - or when every FM voice
// is taken - the mixer makes them.
//
// Devices come from driver modules (audioDrv.h) found at start-up:
// the one named by DVX.INI [audio] driver, else the Sound Blaster
// family, the PC speaker and finally the silent null driver, which
// keeps programs working on a computer without sound.
//
// Functions returning int32_t return AUDIO_OK or a voice id (> 0) on
// success and a negative AUDIO_ERR_* code on failure (audioErrorText
// says what it means). Nothing here may be called from an interrupt.
#ifndef AUDIO_LIB_H
#define AUDIO_LIB_H
#include "dvxApp.h"
#include "dvxPrefs.h"
#include "dvxTypes.h"
#include <stdbool.h>
#include <stdint.h>
// Volume of a voice, a context or the master: 0 (silent) .. AUDIO_VOLUME_MAX
#define AUDIO_VOLUME_MAX 256
// Pan of a voice: AUDIO_PAN_LEFT .. AUDIO_PAN_RIGHT (centre plays at full
// volume on both sides; panning turns the other side down)
#define AUDIO_PAN_LEFT (-256)
#define AUDIO_PAN_CENTER 0
#define AUDIO_PAN_RIGHT 256
// Pitch of a voice as a speed ratio in 1/256ths: AUDIO_PITCH_NORMAL plays
// at the recorded speed, twice that an octave higher.
#define AUDIO_PITCH_NORMAL 256
#define AUDIO_PITCH_MIN 64
#define AUDIO_PITCH_MAX 1024
// Sample rates a sound, a stream or the device may have (Hz)
#define AUDIO_RATE_MIN 4000
#define AUDIO_RATE_MAX 48000
#define AUDIO_RATE_DEFAULT 22050
// Tone frequencies (Hz)
#define AUDIO_TONE_MIN_HZ 20
#define AUDIO_TONE_MAX_HZ 20000
// Voices: the most that can exist, and the default limit of voices mixed
// at once (DVX.INI [audio] voices, 1..AUDIO_VOICES_MAX)
#define AUDIO_VOICES_MAX 32
#define AUDIO_VOICES_DEFAULT 16
// Note queues: the most entries (tones and rests) waiting to start, and
// the unit of their frequencies (1/1000 Hz; 0 is a rest)
#define AUDIO_NOTES_MAX 256
#define AUDIO_MILLIHZ_PER_HZ 1000
// Stream buffer length (milliseconds of sound)
#define AUDIO_STREAM_MS_MIN 20
#define AUDIO_STREAM_MS_DEFAULT 250
#define AUDIO_STREAM_MS_MAX 2000
// Text sizes in AudioInfoT (with the terminating zero)
#define AUDIO_NAME_MAX 16
#define AUDIO_DESC_MAX 64
// Results
#define AUDIO_OK 0
#define AUDIO_ERR_ARG (-1) // a bad argument
#define AUDIO_ERR_NO_MEMORY (-2) // out of memory
#define AUDIO_ERR_OPEN (-3) // the file could not be read
#define AUDIO_ERR_FORMAT (-4) // not a WAV or VOC file
#define AUDIO_ERR_UNSUPPORTED (-5) // compressed or unusual sound data
#define AUDIO_ERR_CORRUPT (-6) // damaged or incomplete file
#define AUDIO_ERR_NO_VOICE (-7) // voice limit reached, and this sound is the quietest
#define AUDIO_ERR_NO_DEVICE (-8) // no sound driver could be started
#define AUDIO_ERR_VOICE (-9) // no such voice in this context (ended, or never was)
#define AUDIO_ERR_BUSY (-10) // the synthesizer is in use by another program, or not ready
#define AUDIO_ERR_NO_SYNTH (-11) // no such synthesizer (no FM chip, no MIDI port)
#define AUDIO_ERR_COUNT 12 // AUDIO_OK and the eleven errors
// Opaque handles
typedef struct AudioCtxT AudioCtxT;
typedef struct AudioNotesT AudioNotesT;
typedef struct AudioSampleT AudioSampleT;
typedef struct AudioStreamT AudioStreamT;
// Tone waveforms
typedef enum {
AUDIO_WAVE_SQUARE = 0,
AUDIO_WAVE_TRIANGLE = 1,
AUDIO_WAVE_NOISE = 2
} AudioWaveE;
// Why a voice ended
typedef enum {
AUDIO_END_FINISHED = 0, // played to its end (or its tone length)
AUDIO_END_STOPPED = 1, // audioVoiceStop, audioStopAll, its sample or stream closed
AUDIO_END_DROPPED = 2 // the voice limit dropped it for a louder sound
} AudioEndE;
// PCM layout: rate in Hz, 8 (unsigned) or 16 (signed, little-endian)
// bits, 1 or 2 channels (stereo interleaved left, right).
typedef struct {
uint32_t rate;
uint32_t bits;
uint32_t channels;
} AudioFormatT;
// The sound device in use (audioGetInfo).
typedef struct {
char driver[AUDIO_NAME_MAX]; // driver name ("sb", "speaker", "null"); "" when none runs
char description[AUDIO_DESC_MAX]; // for people, e.g. "Sound Blaster 16 (DSP 4.05)"
uint32_t rate; // mixing rate the device achieved (Hz)
uint32_t bits; // output bits
uint32_t channels; // output channels
uint32_t voiceLimit; // voices mixed at once
bool running; // a device is playing (some context is open)
bool hasDac; // plays sampled sound (else tones only, or nothing)
bool hasSynth; // has a hardware music synthesizer
bool hasTone; // plays tones on a hardware tone generator
} AudioInfoT;
// Mixer counters since the device started (audioGetStats).
typedef struct {
uint32_t renders; // blocks the device asked the mixer for
uint32_t frames; // frames mixed
uint32_t underruns; // stream frames that were due but not yet written
uint32_t clipped; // output samples clipped to the 16-bit range
uint32_t dropped; // voices dropped (or refused) by the voice limit
uint32_t playing; // voices playing now
} AudioStatsT;
// A voice of ctx ended. Called from audioPump or the shell's idle time,
// never from an interrupt, as the program that owns ctx.
typedef void (*AudioEndFnT)(AudioCtxT *ctx, int32_t voice, AudioEndE reason, void *userData);
// A pull stream wants up to frames frames in its own format in buf.
// Return how many were written (fewer, even 0, is fine: the rest is asked
// for later). Called from audioPump or the shell's idle time.
typedef uint32_t (*AudioFillFnT)(AudioStreamT *stream, void *buf, uint32_t frames, void *userData);
// ============================================================
// Contexts, device and volume
// ============================================================
// Open an audio context for the calling program; the first one opened
// starts the sound device. NULL when out of memory or when no driver
// could be started. Every context is closed for its program when the
// program ends.
AudioCtxT *audioOpen(void);
// Stop every voice of ctx, free its samples and streams and close it; the
// last one closed stops the device. NULL is ignored.
void audioClose(AudioCtxT *ctx);
// Text for an AUDIO_OK / AUDIO_ERR_* code.
const char *audioErrorText(int32_t err);
// The device in use (running false and driver "" while none is open).
void audioGetInfo(AudioInfoT *info);
// Mixer counters since the device started.
void audioGetStats(AudioStatsT *stats);
// Master volume of all sound (0..AUDIO_VOLUME_MAX; DVX.INI [audio]
// volume, in percent, sets it when the device starts).
int32_t audioGetMasterVolume(void);
void audioSetMasterVolume(int32_t volume);
// Volume of everything ctx plays (0..AUDIO_VOLUME_MAX, default the
// maximum).
int32_t audioGetVolume(const AudioCtxT *ctx);
void audioSetVolume(AudioCtxT *ctx, int32_t volume);
// Call fn when a voice of ctx ends (NULL: no calls).
void audioSetEndFn(AudioCtxT *ctx, AudioEndFnT fn, void *userData);
// Deliver end notifications and fill pull streams now. The shell does
// this in its idle time; a program that keeps the desktop busy every
// frame should call it once a frame.
void audioPump(void);
// Stop every voice of ctx.
void audioStopAll(AudioCtxT *ctx);
// ============================================================
// Samples and voices
// ============================================================
// Load a WAV (PCM, 8 or 16 bits, mono or stereo, any other chunks
// skipped) or VOC (8-bit, 16-bit, silence and extended blocks) sound from
// a file or from memory.
int32_t audioSampleLoad(AudioCtxT *ctx, const char *path, AudioSampleT **out);
int32_t audioSampleLoadMem(AudioCtxT *ctx, const void *data, uint32_t size, AudioSampleT **out);
// A sample from raw PCM frames in format fmt.
int32_t audioSampleCreate(AudioCtxT *ctx, const void *pcm, uint32_t frames, const AudioFormatT *fmt, AudioSampleT **out);
// Stop the voices playing it and free it. NULL is ignored.
void audioSampleFree(AudioSampleT *sample);
// The sample's recorded format and length in frames.
void audioSampleInfo(const AudioSampleT *sample, AudioFormatT *fmt, uint32_t *frames);
// Play sample on a new voice at volume and pan, from its start, once or
// looping until stopped. Returns the voice id.
int32_t audioPlay(AudioSampleT *sample, int32_t volume, int32_t pan, bool loop);
// Load a sound file and play it; the sample is freed when the voice ends.
int32_t audioPlayFile(AudioCtxT *ctx, const char *path, int32_t volume, int32_t pan, bool loop);
// Play a tone of hz for ms milliseconds (0: until stopped).
int32_t audioTone(AudioCtxT *ctx, AudioWaveE wave, uint32_t hz, int32_t volume, uint32_t ms);
// Change or stop a voice of ctx; AUDIO_ERR_VOICE when it is not one
// (any more). Pitch changes a sample's or stream's speed (and a tone's
// frequency); SetFrequency sets a tone's frequency.
int32_t audioVoiceSetFrequency(AudioCtxT *ctx, int32_t voice, uint32_t hz);
int32_t audioVoiceSetPan(AudioCtxT *ctx, int32_t voice, int32_t pan);
int32_t audioVoiceSetPitch(AudioCtxT *ctx, int32_t voice, int32_t pitch);
int32_t audioVoiceSetVolume(AudioCtxT *ctx, int32_t voice, int32_t volume);
int32_t audioVoiceStop(AudioCtxT *ctx, int32_t voice);
// True while the voice of ctx is still playing.
bool audioVoiceIsPlaying(const AudioCtxT *ctx, int32_t voice);
// ============================================================
// Streams
// ============================================================
// Open a stream of format fmt with a buffer of bufferMs milliseconds (0:
// AUDIO_STREAM_MS_DEFAULT). It starts playing at once on its own voice
// (silence until samples arrive). With fill, the library asks for
// samples whenever the buffer has room; without, the program writes them
// with audioStreamWrite.
int32_t audioStreamOpen(AudioCtxT *ctx, const AudioFormatT *fmt, uint32_t bufferMs, AudioFillFnT fill, void *userData, AudioStreamT **out);
// Stop and free the stream. NULL is ignored.
void audioStreamClose(AudioStreamT *stream);
// Frames buffered and not yet played, and frames of room left.
uint32_t audioStreamQueued(const AudioStreamT *stream);
uint32_t audioStreamSpace(const AudioStreamT *stream);
// The stream's voice id (for volume, pan and pitch).
int32_t audioStreamVoice(const AudioStreamT *stream);
// Append up to frames frames in the stream's format; returns how many fit.
uint32_t audioStreamWrite(AudioStreamT *stream, const void *pcm, uint32_t frames);
// ============================================================
// Note queues
// ============================================================
//
// Tones and rests played one after the other, each lasting exactly as
// long as asked (to the output frame, with no drift over a long tune) as
// long as the next is queued before the one before it ends. A queue
// plays on its own voice, in the mixer or on the PC speaker's tone
// generator (never on the FM chip), and the voice limit never drops it;
// it is silent while empty.
// A queue of wave tones (square, triangle or noise) at volume
// (0..AUDIO_VOLUME_MAX; audioVoiceSetVolume on audioNotesVoice changes it).
int32_t audioNotesOpen(AudioCtxT *ctx, AudioWaveE wave, int32_t volume, AudioNotesT **out);
// Stop and free the queue. NULL is ignored.
void audioNotesClose(AudioNotesT *notes);
// Append a tone of milliHz thousandths of a hertz (AUDIO_TONE_MIN_HZ ..
// AUDIO_TONE_MAX_HZ, or 0 for a rest) lasting us microseconds.
// AUDIO_ERR_BUSY when AUDIO_NOTES_MAX entries are already waiting.
int32_t audioNotesAdd(AudioNotesT *notes, uint32_t milliHz, uint32_t us);
// Silence the queue now and forget everything in it.
void audioNotesClear(AudioNotesT *notes);
// Entries not yet finished (playing or waiting; 0 when the queue has gone
// quiet); tones, when not NULL, gets how many of them are tones rather
// than rests.
uint32_t audioNotesQueued(const AudioNotesT *notes, uint32_t *tones);
// Entries that can still be added.
uint32_t audioNotesSpace(const AudioNotesT *notes);
// The queue's voice id (for volume and pan).
int32_t audioNotesVoice(const AudioNotesT *notes);
// ============================================================
// FM synthesizer
// ============================================================
//
// The FM chip's two-operator voices are shared: a program takes one with
// audioFmAlloc, programs it with audioFmWrite and gives it back with
// audioFmFree (closing the context gives back all of its voices). Voices
// are handed out least recently used first, so a released note's tail
// is cut last. audioFmWrite takes the registers of the voice's own
// channel, numbered as for the first channel of an OPL2 (AUDIO_FM_*
// below); the library adds the channel's offsets. On an OPL3 a
// AUDIO_FM_FEEDBACK value without either output bit plays on both sides.
//
// A program may instead take the whole chip (audioFmRawAcquire): every
// voice of the other programs is silenced and taken back (their tones
// go on in the mixer), new voices are refused until the chip is given
// back, and a second program asking for the chip gets AUDIO_ERR_BUSY.
//
// audioFmAlloc, audioFmFree, audioFmWrite and audioFmRawWrite may also be
// called from a timer callback (platformTimerAdd), as music playback does.
// Most voices an FM chip has (OPL3; an OPL2 has 9)
#define AUDIO_FM_VOICES_MAX 18
// The chip's own sample rate (Hz), which frequency numbers are based on:
// hz = fnum * AUDIO_FM_CHIP_HZ * 2^block / 2^20
#define AUDIO_FM_CHIP_HZ 49716
#define AUDIO_FM_FNUM_MAX 1023
#define AUDIO_FM_BLOCK_MAX 7
#define AUDIO_FM_FNUM_HI_SHIFT 8 // fnum bits 8-9 go in AUDIO_FM_KEY_BLOCK bits 0-1
#define AUDIO_FM_BLOCK_SHIFT 2 // block in AUDIO_FM_KEY_BLOCK bits 2-4
// Channel registers for audioFmWrite (modulator / carrier operator)
#define AUDIO_FM_MOD_CHAR 0x20 // tremolo, vibrato, sustaining, key scale rate, multiple
#define AUDIO_FM_CAR_CHAR 0x23
#define AUDIO_FM_MOD_LEVEL 0x40 // key scale level (bits 6-7), attenuation (bits 0-5, 0.75 dB)
#define AUDIO_FM_CAR_LEVEL 0x43
#define AUDIO_FM_MOD_ATTACK 0x60 // attack rate (bits 4-7), decay rate (bits 0-3)
#define AUDIO_FM_CAR_ATTACK 0x63
#define AUDIO_FM_MOD_SUSTAIN 0x80 // sustain level (bits 4-7), release rate (bits 0-3)
#define AUDIO_FM_CAR_SUSTAIN 0x83
#define AUDIO_FM_FNUM_LOW 0xA0 // frequency number bits 0-7
#define AUDIO_FM_KEY_BLOCK 0xB0 // key on, block, frequency number bits 8-9
#define AUDIO_FM_FEEDBACK 0xC0 // outputs, feedback (bits 1-3), additive (bit 0)
#define AUDIO_FM_MOD_WAVE 0xE0 // waveform
#define AUDIO_FM_CAR_WAVE 0xE3
// Bits of those registers
#define AUDIO_FM_KEY_ON 0x20 // AUDIO_FM_KEY_BLOCK: sounding
#define AUDIO_FM_OUT_LEFT 0x10 // AUDIO_FM_FEEDBACK: left output (OPL3)
#define AUDIO_FM_OUT_RIGHT 0x20 // AUDIO_FM_FEEDBACK: right output (OPL3)
#define AUDIO_FM_OUT_BOTH (AUDIO_FM_OUT_LEFT | AUDIO_FM_OUT_RIGHT)
#define AUDIO_FM_ADDITIVE 0x01 // AUDIO_FM_FEEDBACK: both operators heard
// The FM chip (audioFmGetInfo); voices 0 when there is none.
typedef struct {
char description[AUDIO_DESC_MAX]; // e.g. "OPL3 FM synthesizer at 388h"
uint32_t voices; // two-operator voices
uint32_t freeVoices; // not taken by any program
bool stereo; // voices can be panned (OPL3)
bool rawOwned; // one program has the whole chip
} AudioFmInfoT;
// Take a free FM voice for ctx. Returns its id (> 0), AUDIO_ERR_NO_VOICE
// when all are taken, AUDIO_ERR_BUSY while a program has the whole chip,
// AUDIO_ERR_NO_SYNTH without an FM chip.
int32_t audioFmAlloc(AudioCtxT *ctx);
// Silence the voice (key off) and give it back.
int32_t audioFmFree(AudioCtxT *ctx, int32_t voice);
// The FM chip in use.
void audioFmGetInfo(AudioFmInfoT *info);
// Set a register of the voice's channel (AUDIO_FM_*).
int32_t audioFmWrite(AudioCtxT *ctx, int32_t voice, uint8_t reg, uint8_t value);
// The whole chip for ctx alone; audioFmRawWrite sets any register
// (bank * AUDIO_FM_BANK_REGS + index, bank 1 on an OPL3 only). Release
// silences the chip and gives its voices back to everyone.
int32_t audioFmRawAcquire(AudioCtxT *ctx);
int32_t audioFmRawRelease(AudioCtxT *ctx);
int32_t audioFmRawWrite(AudioCtxT *ctx, uint32_t reg, uint8_t value);
// ============================================================
// MIDI port
// ============================================================
//
// The MIDI port (an MPU-401 in UART mode, used only when DVX.INI [audio]
// midi = mpu401) belongs to one program at a time. audioMidiPut may
// also be called from a timer callback; it returns AUDIO_ERR_BUSY when
// the port cannot take a byte yet (send it again later).
// The MIDI port (audioMidiGetInfo).
typedef struct {
char description[AUDIO_DESC_MAX]; // e.g. "MPU-401 MIDI port at 330h"; "" when none
bool present;
bool owned; // a program is using it
} AudioMidiInfoT;
int32_t audioMidiAcquire(AudioCtxT *ctx);
void audioMidiGetInfo(AudioMidiInfoT *info);
int32_t audioMidiPut(AudioCtxT *ctx, uint8_t byte);
int32_t audioMidiRelease(AudioCtxT *ctx);
// ============================================================
// Desktop sounds
// ============================================================
//
// The sounds DVX plays for its events (dvxPlaySound, DvxSoundE in
// dvxApp.h). Each event's sound is a WAV or VOC file named in DVX.INI
// [sounds] under the event's key: a name alone is a file in the sound
// folders (the user's SOUNDS, then SHARED\SOUNDS: platformKindFind), a
// name with a folder in it a path (empty or missing: none). The library
// connects itself to DVX when it loads and plays them on a context of its
// own, so they belong to no program; with no sound hardware they are
// silent.
#define AUDIO_SOUNDS_SECTION "sounds"
// The DVX.INI key of an event ("startup", "error", ...), and its name for
// people ("Start DVX", "Error", ...); NULL for an event out of range.
const char *audioDeskKey(DvxSoundE event);
const char *audioDeskLabel(DvxSoundE event);
// Play the event's sound (the DvxSoundHookT that DVX calls); with wait,
// return once it has played (at most a few seconds).
void audioDeskPlay(DvxSoundE event, bool wait);
// Play a sound file the way desktop sounds play (to try one out).
// Returns the voice id or an AUDIO_ERR_* code.
int32_t audioDeskPlayFile(const char *path);
// DVX.INI [sounds] has changed: read it again before the next sound.
void audioDeskReload(void);
// ============================================================
// Drivers
// ============================================================
// The short names of the sound drivers installed (DVX.INI [audio]
// driver), the one tried first first; returns how many were put in names
// (at most max).
int32_t audioDriverNames(char names[][AUDIO_NAME_MAX], int32_t max);
// The most drivers audioDriverNames is asked for by DVX's own callers
#define AUDIO_DRIVERS_MAX 16
// ============================================================
// Settings (DVX.INI, the Control Panel's Sounds page)
// ============================================================
//
// What the Sounds page and the BASIC functions read and write; prefs is
// a copy of DVX.INI the caller loads and saves. The device reads the
// same keys when it starts (driver, volume, midi: audioDrv.h).
// [audio] midi: where MIDI music plays, in dropdown order (label for
// people, iniName stored; value unused). Default AUDIO_MIDI_AUTO.
#define AUDIO_MIDI_CHOICE_COUNT 4
extern const PrefsChoiceT audioMidiChoices[AUDIO_MIDI_CHOICE_COUNT];
// The sound file of an event in [sounds] ("" for none or an event out of
// range), and setting it ("" or NULL: none).
const char *audioDeskFile(PrefsHandleT *prefs, DvxSoundE event);
void audioDeskSetFile(PrefsHandleT *prefs, DvxSoundE event, const char *path);
// A volume in percent (clamped to 0..100) as 0..AUDIO_VOLUME_MAX.
int32_t audioPercentToVolume(int32_t percent);
// [audio] driver (AUDIO_DRIVER_AUTO when missing), midi (the index in
// audioMidiChoices; auto when missing or unknown) and volume (percent,
// 0..100, 100 when missing).
const char *audioSettingDriver(PrefsHandleT *prefs);
int32_t audioSettingMidi(PrefsHandleT *prefs);
int32_t audioSettingVolume(PrefsHandleT *prefs);
// Store [audio] driver, midi (an audioMidiChoices index; out of range:
// auto) and volume (percent, clamped).
void audioSettingsStore(PrefsHandleT *prefs, const char *driver, int32_t midi, int32_t volume);
// ============================================================
// BASIC (src/include/basic/sound.bas)
// ============================================================
// Each BASIC program gets its own context on first use. Volumes, pans
// and pitches are in percent (volume 0..100, pan -100..100, pitch
// 25..400); a voice, sound or stream number is 0 on failure (SoundError$
// says why). Sounds and streams are numbered per program and freed with
// its context. SoundCreate and SoundStreamWrite take BINARY STRING PCM
// (bytes and byte count). The Midi... functions are declared in
// music.bas.
int32_t FmAlloc(void);
void FmFree(int32_t voice);
const char *FmInfo(const char *field);
int32_t FmRawAcquire(void);
void FmRawRelease(void);
int32_t FmRawWrite(int32_t reg, int32_t value);
int32_t FmWrite(int32_t voice, int32_t reg, int32_t value);
int32_t MidiAcquire(void);
const char *MidiPortInfo(const char *field);
int32_t MidiPut(int32_t byteValue);
void MidiRelease(void);
int32_t PlayNoise(int32_t frequency, int32_t milliseconds, int32_t volume);
int32_t PlayTone(int32_t frequency, int32_t milliseconds, int32_t volume);
int32_t PlayTriangle(int32_t frequency, int32_t milliseconds, int32_t volume);
int32_t PlayWave(const char *fileName);
int32_t PlayWaveLoop(const char *fileName);
int32_t SoundCaps(const char *field);
int32_t SoundCreate(const uint8_t *pcm, int32_t len, int32_t rate, int32_t bits, int32_t channels);
const char *SoundDriverAuto(void);
int32_t SoundDriverCount(void);
const char *SoundDriverName(int32_t index);
const char *SoundError(void);
int32_t SoundEventCount(void);
const char *SoundEventFile(int32_t prefs, int32_t eventIdx);
const char *SoundEventKey(int32_t eventIdx);
const char *SoundEventLabel(int32_t eventIdx);
void SoundEventsReload(void);
const char *SoundFolder(void);
void SoundFree(int32_t sound);
int32_t SoundGetVolume(void);
const char *SoundInfo(void);
int32_t SoundLength(int32_t sound);
int32_t SoundLoad(const char *fileName);
int32_t SoundMasterVolume(void);
int32_t SoundMidiCount(void);
const char *SoundMidiLabel(int32_t index);
int32_t SoundPlay(int32_t sound, int32_t volume, int32_t pan, int32_t loopIt);
void SoundPlayEvent(int32_t eventIdx, int32_t wait);
int32_t SoundPlaying(int32_t voice);
void SoundSetEventFile(int32_t prefs, int32_t eventIdx, const char *fileName);
void SoundSetFrequency(int32_t voice, int32_t frequency);
void SoundSetMasterVolume(int32_t percent);
void SoundSetPan(int32_t voice, int32_t pan);
void SoundSetPitch(int32_t voice, int32_t percent);
const char *SoundSettingDriver(int32_t prefs);
int32_t SoundSettingMidi(int32_t prefs);
void SoundSettingsStore(int32_t prefs, const char *driver, int32_t midi, int32_t volume);
int32_t SoundSettingVolume(int32_t prefs);
void SoundSetVolume(int32_t voice, int32_t volume);
void SoundStreamClose(int32_t stream);
int32_t SoundStreamOpen(int32_t rate, int32_t bits, int32_t channels, int32_t bufferMs);
int32_t SoundStreamQueued(int32_t stream);
int32_t SoundStreamSpace(int32_t stream);
int32_t SoundStreamVoice(int32_t stream);
int32_t SoundStreamWrite(int32_t stream, const uint8_t *pcm, int32_t len);
int32_t SoundTestFile(const char *fileName);
void SoundVolume(int32_t volume);
void StopAllSounds(void);
void StopWave(int32_t voice);
#endif // AUDIO_LIB_H