504 lines
23 KiB
C
504 lines
23 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 "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 path from the DVX folder; 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. The default set lives in
|
|
// AUDIO_SOUNDS_DIR.
|
|
|
|
#define AUDIO_SOUNDS_SECTION "sounds"
|
|
#define AUDIO_SOUNDS_DIR "config/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);
|
|
|
|
// ============================================================
|
|
// BASIC (src/include/basic/sound.bas)
|
|
// ============================================================
|
|
|
|
// Each BASIC program gets its own context on first use. Volumes are in
|
|
// percent (0..100); a voice number is 0 on failure (SoundError$ says why).
|
|
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 PlayWave(const char *fileName);
|
|
int32_t PlayWaveLoop(const char *fileName);
|
|
const char *SoundError(void);
|
|
const char *SoundInfo(void);
|
|
int32_t SoundPlaying(int32_t voice);
|
|
void SoundVolume(int32_t volume);
|
|
void StopAllSounds(void);
|
|
void StopWave(int32_t voice);
|
|
|
|
#endif // AUDIO_LIB_H
|