From ca613b8f5a9d15f083e2edd1eeee16ce8aa1e1db Mon Sep 17 00:00:00 2001 From: This_Is_NPC Date: Mon, 21 Sep 2026 04:48:06 -0300 Subject: [PATCH] refactor(effs): share the audio ring through // @src audio_open/write/close C copied the same IO_RING block so each file compiled alone. eff_src already dedupes a path; a // @src line lets that seen-set include the ring, so a program that imports more than one effect still emits one copy. --- bend2/comp.ts | 4 +- bend2/effs/audio_close.c | 163 +------------------------------------- bend2/effs/audio_open.c | 163 +------------------------------------- bend2/effs/audio_ring.c | 166 +++++++++++++++++++++++++++++++++++++++ bend2/effs/audio_write.c | 163 +------------------------------------- 5 files changed, 172 insertions(+), 487 deletions(-) create mode 100644 bend2/effs/audio_ring.c diff --git a/bend2/comp.ts b/bend2/comp.ts index f2c29dad0..e298a480c 100644 --- a/bend2/comp.ts +++ b/bend2/comp.ts @@ -1235,7 +1235,9 @@ function eff_src(path: string, seen: Set): string { return ""; } seen.add(path); - return fs.readFileSync(path, "utf8"); + const dir = path.slice(0, path.lastIndexOf("/")); + return fs.readFileSync(path, "utf8").replace(/^\/\/ @src ([a-z0-9_]+\.c)$/gm, + (_: string, name: string) => eff_src(dir + "/" + name, seen)); } // Io diff --git a/bend2/effs/audio_close.c b/bend2/effs/audio_close.c index a3642f1d7..c10b3e9ff 100644 --- a/bend2/effs/audio_close.c +++ b/bend2/effs/audio_close.c @@ -1,168 +1,7 @@ // Audio // ===== -// The ring: 4096 float32 stereo frames. The effects fill it on the -// evaluator's thread; the device's callback drains it and pads the -// rest of its buffer with silence. The same block sits in -// audio_write.c and audio_close.c under this guard, so that any one -// of the three effects compiles alone. -#ifndef IO_RING -#define IO_RING 4096u - -#ifdef __OBJC__ -#import -#elif defined(__linux__) -#include -#endif - -typedef struct { - _Atomic(u64) read, written; - float pcm[IO_RING * 2]; -#ifdef __OBJC__ - AudioUnit unit; -#elif defined(__linux__) - snd_pcm_t* unit; - pthread_t pump; - _Atomic(u32) done; -#endif -} IoRing; - -// n frames queued after the write; a write past the ring's room is -// dropped and the queue answered as it is. -static u64 io_ring_write(IoRing* p, const float* pcm, u32 n) { - u64 w = atomic_load_explicit(&p->written, memory_order_relaxed); - u64 r = atomic_load_explicit(&p->read, memory_order_acquire); - if (w - r + n <= IO_RING) { - u32 at = (u32)(w % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(p->pcm + at * 2, pcm, first * 8); - memcpy(p->pcm, pcm + first * 2, (n - first) * 8); - atomic_store_explicit(&p->written, w + n, memory_order_release); - w += n; - } - return w - r; -} - -static void io_ring_pull(IoRing* p, float* out, u32 frames) { - u64 r = atomic_load_explicit(&p->read, memory_order_relaxed); - u64 w = atomic_load_explicit(&p->written, memory_order_acquire); - u32 n = (u32)(w - r < frames ? w - r : frames); - u32 at = (u32)(r % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(out, p->pcm + at * 2, first * 8); - memcpy(out + first * 2, p->pcm, (n - first) * 8); - memset(out + n * 2, 0, (frames - n) * 8); - atomic_store_explicit(&p->read, r + n, memory_order_release); -} - -#ifdef __OBJC__ - -static OSStatus io_ring_pump(void* ctx, AudioUnitRenderActionFlags* flags, - const AudioTimeStamp* when, UInt32 bus, UInt32 frames, - AudioBufferList* bl) { - if (bl->mNumberBuffers != 1 || bl->mBuffers[0].mNumberChannels != 2 - || bl->mBuffers[0].mDataByteSize < frames * 8) { - return kAudio_ParamError; - } - io_ring_pull(ctx, bl->mBuffers[0].mData, frames); - return noErr; -} - -// The default output unit fed float32 stereo at the asked rate (the -// unit converts to the device's own). -static u32 io_ring_start(IoRing* p, u32 rate) { - AudioComponentDescription desc = { kAudioUnitType_Output, - kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple, 0, 0 }; - AudioComponent comp = AudioComponentFindNext(NULL, &desc); - if (comp == NULL || AudioComponentInstanceNew(comp, &p->unit) != noErr) { - return ENODEV; - } - AudioStreamBasicDescription fmt = { 0 }; - fmt.mSampleRate = rate; - fmt.mFormatID = kAudioFormatLinearPCM; - fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; - fmt.mBytesPerPacket = 8; - fmt.mFramesPerPacket = 1; - fmt.mBytesPerFrame = 8; - fmt.mChannelsPerFrame = 2; - fmt.mBitsPerChannel = 32; - AURenderCallbackStruct cb = { io_ring_pump, p }; - bool ok = AudioUnitSetProperty(p->unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &fmt, sizeof fmt) == noErr - && AudioUnitSetProperty(p->unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &cb, sizeof cb) == noErr - && AudioUnitInitialize(p->unit) == noErr - && AudioOutputUnitStart(p->unit) == noErr; - return ok ? 0 : ENODEV; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - AudioOutputUnitStop(p->unit); - AudioUnitUninitialize(p->unit); - AudioComponentInstanceDispose(p->unit); - } - free(p); -} - -#elif defined(__linux__) - -// A thread feeds the default ALSA device 256 frames at a time (a -// write blocks until the device has room, so the ring drains at the -// device's clock). -static void* io_ring_pump(void* ctx) { - IoRing* p = ctx; - float out[256 * 2]; - while (atomic_load_explicit(&p->done, memory_order_relaxed) == 0) { - io_ring_pull(p, out, 256); - snd_pcm_sframes_t n = snd_pcm_writei(p->unit, out, 256); - if (n < 0) { - snd_pcm_recover(p->unit, (int)n, 1); - } - } - return NULL; -} - -static void io_ring_hush(const char* file, int line, const char* fn, int err, - const char* fmt, ...) { -} - -static u32 io_ring_start(IoRing* p, u32 rate) { - snd_lib_error_set_handler(io_ring_hush); - if (snd_pcm_open(&p->unit, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { - return ENODEV; - } - if (snd_pcm_set_params(p->unit, SND_PCM_FORMAT_FLOAT_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 1, 20000) < 0 - || pthread_create(&p->pump, NULL, io_ring_pump, p) != 0) { - return ENODEV; - } - return 0; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - atomic_store_explicit(&p->done, 1, memory_order_relaxed); - if (p->pump != 0) { - pthread_join(p->pump, NULL); - } - snd_pcm_close(p->unit); - } - free(p); -} - -#else - -static u32 io_ring_start(IoRing* p, u32 rate) { - return ENOTSUP; -} - -static void io_ring_free(IoRing* p) { - free(p); -} - -#endif -#endif +// @src audio_ring.c Term audio_close_run(Env e, Term* f, IoWork* w) { io_ring_free((IoRing*)(uintptr_t)io_hand_v(f[0])); diff --git a/bend2/effs/audio_open.c b/bend2/effs/audio_open.c index db2806c6b..ca650532b 100644 --- a/bend2/effs/audio_open.c +++ b/bend2/effs/audio_open.c @@ -1,168 +1,7 @@ // Audio // ===== -// The ring: 4096 float32 stereo frames. The effects fill it on the -// evaluator's thread; the device's callback drains it and pads the -// rest of its buffer with silence. The same block sits in -// audio_write.c and audio_close.c under this guard, so that any one -// of the three effects compiles alone. -#ifndef IO_RING -#define IO_RING 4096u - -#ifdef __OBJC__ -#import -#elif defined(__linux__) -#include -#endif - -typedef struct { - _Atomic(u64) read, written; - float pcm[IO_RING * 2]; -#ifdef __OBJC__ - AudioUnit unit; -#elif defined(__linux__) - snd_pcm_t* unit; - pthread_t pump; - _Atomic(u32) done; -#endif -} IoRing; - -// n frames queued after the write; a write past the ring's room is -// dropped and the queue answered as it is. -static u64 io_ring_write(IoRing* p, const float* pcm, u32 n) { - u64 w = atomic_load_explicit(&p->written, memory_order_relaxed); - u64 r = atomic_load_explicit(&p->read, memory_order_acquire); - if (w - r + n <= IO_RING) { - u32 at = (u32)(w % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(p->pcm + at * 2, pcm, first * 8); - memcpy(p->pcm, pcm + first * 2, (n - first) * 8); - atomic_store_explicit(&p->written, w + n, memory_order_release); - w += n; - } - return w - r; -} - -static void io_ring_pull(IoRing* p, float* out, u32 frames) { - u64 r = atomic_load_explicit(&p->read, memory_order_relaxed); - u64 w = atomic_load_explicit(&p->written, memory_order_acquire); - u32 n = (u32)(w - r < frames ? w - r : frames); - u32 at = (u32)(r % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(out, p->pcm + at * 2, first * 8); - memcpy(out + first * 2, p->pcm, (n - first) * 8); - memset(out + n * 2, 0, (frames - n) * 8); - atomic_store_explicit(&p->read, r + n, memory_order_release); -} - -#ifdef __OBJC__ - -static OSStatus io_ring_pump(void* ctx, AudioUnitRenderActionFlags* flags, - const AudioTimeStamp* when, UInt32 bus, UInt32 frames, - AudioBufferList* bl) { - if (bl->mNumberBuffers != 1 || bl->mBuffers[0].mNumberChannels != 2 - || bl->mBuffers[0].mDataByteSize < frames * 8) { - return kAudio_ParamError; - } - io_ring_pull(ctx, bl->mBuffers[0].mData, frames); - return noErr; -} - -// The default output unit fed float32 stereo at the asked rate (the -// unit converts to the device's own). -static u32 io_ring_start(IoRing* p, u32 rate) { - AudioComponentDescription desc = { kAudioUnitType_Output, - kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple, 0, 0 }; - AudioComponent comp = AudioComponentFindNext(NULL, &desc); - if (comp == NULL || AudioComponentInstanceNew(comp, &p->unit) != noErr) { - return ENODEV; - } - AudioStreamBasicDescription fmt = { 0 }; - fmt.mSampleRate = rate; - fmt.mFormatID = kAudioFormatLinearPCM; - fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; - fmt.mBytesPerPacket = 8; - fmt.mFramesPerPacket = 1; - fmt.mBytesPerFrame = 8; - fmt.mChannelsPerFrame = 2; - fmt.mBitsPerChannel = 32; - AURenderCallbackStruct cb = { io_ring_pump, p }; - bool ok = AudioUnitSetProperty(p->unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &fmt, sizeof fmt) == noErr - && AudioUnitSetProperty(p->unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &cb, sizeof cb) == noErr - && AudioUnitInitialize(p->unit) == noErr - && AudioOutputUnitStart(p->unit) == noErr; - return ok ? 0 : ENODEV; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - AudioOutputUnitStop(p->unit); - AudioUnitUninitialize(p->unit); - AudioComponentInstanceDispose(p->unit); - } - free(p); -} - -#elif defined(__linux__) - -// A thread feeds the default ALSA device 256 frames at a time (a -// write blocks until the device has room, so the ring drains at the -// device's clock). -static void* io_ring_pump(void* ctx) { - IoRing* p = ctx; - float out[256 * 2]; - while (atomic_load_explicit(&p->done, memory_order_relaxed) == 0) { - io_ring_pull(p, out, 256); - snd_pcm_sframes_t n = snd_pcm_writei(p->unit, out, 256); - if (n < 0) { - snd_pcm_recover(p->unit, (int)n, 1); - } - } - return NULL; -} - -static void io_ring_hush(const char* file, int line, const char* fn, int err, - const char* fmt, ...) { -} - -static u32 io_ring_start(IoRing* p, u32 rate) { - snd_lib_error_set_handler(io_ring_hush); - if (snd_pcm_open(&p->unit, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { - return ENODEV; - } - if (snd_pcm_set_params(p->unit, SND_PCM_FORMAT_FLOAT_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 1, 20000) < 0 - || pthread_create(&p->pump, NULL, io_ring_pump, p) != 0) { - return ENODEV; - } - return 0; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - atomic_store_explicit(&p->done, 1, memory_order_relaxed); - if (p->pump != 0) { - pthread_join(p->pump, NULL); - } - snd_pcm_close(p->unit); - } - free(p); -} - -#else - -static u32 io_ring_start(IoRing* p, u32 rate) { - return ENOTSUP; -} - -static void io_ring_free(IoRing* p) { - free(p); -} - -#endif -#endif +// @src audio_ring.c Term audio_open_run(Env e, Term* f, IoWork* w) { u32 rate = (u32)f[0]; diff --git a/bend2/effs/audio_ring.c b/bend2/effs/audio_ring.c new file mode 100644 index 000000000..f450a1808 --- /dev/null +++ b/bend2/effs/audio_ring.c @@ -0,0 +1,166 @@ +// Audio +// ===== + +// The ring: 4096 float32 stereo frames. The effects fill it on the +// evaluator's thread; the device's callback drains it and pads the +// rest of its buffer with silence. audio_open.c, audio_write.c and +// audio_close.c pull this file through // @src so any one of the three +// still compiles alone, and a program that imports more than one +// still sees a single copy. +#ifndef IO_RING +#define IO_RING 4096u + +#ifdef __OBJC__ +#import +#elif defined(__linux__) +#include +#endif + +typedef struct { + _Atomic(u64) read, written; + float pcm[IO_RING * 2]; +#ifdef __OBJC__ + AudioUnit unit; +#elif defined(__linux__) + snd_pcm_t* unit; + pthread_t pump; + _Atomic(u32) done; +#endif +} IoRing; + +// n frames queued after the write; a write past the ring's room is +// dropped and the queue answered as it is. +static u64 io_ring_write(IoRing* p, const float* pcm, u32 n) { + u64 w = atomic_load_explicit(&p->written, memory_order_relaxed); + u64 r = atomic_load_explicit(&p->read, memory_order_acquire); + if (w - r + n <= IO_RING) { + u32 at = (u32)(w % IO_RING); + u32 first = n < IO_RING - at ? n : IO_RING - at; + memcpy(p->pcm + at * 2, pcm, first * 8); + memcpy(p->pcm, pcm + first * 2, (n - first) * 8); + atomic_store_explicit(&p->written, w + n, memory_order_release); + w += n; + } + return w - r; +} + +static void io_ring_pull(IoRing* p, float* out, u32 frames) { + u64 r = atomic_load_explicit(&p->read, memory_order_relaxed); + u64 w = atomic_load_explicit(&p->written, memory_order_acquire); + u32 n = (u32)(w - r < frames ? w - r : frames); + u32 at = (u32)(r % IO_RING); + u32 first = n < IO_RING - at ? n : IO_RING - at; + memcpy(out, p->pcm + at * 2, first * 8); + memcpy(out + first * 2, p->pcm, (n - first) * 8); + memset(out + n * 2, 0, (frames - n) * 8); + atomic_store_explicit(&p->read, r + n, memory_order_release); +} + +#ifdef __OBJC__ + +static OSStatus io_ring_pump(void* ctx, AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* when, UInt32 bus, UInt32 frames, + AudioBufferList* bl) { + if (bl->mNumberBuffers != 1 || bl->mBuffers[0].mNumberChannels != 2 + || bl->mBuffers[0].mDataByteSize < frames * 8) { + return kAudio_ParamError; + } + io_ring_pull(ctx, bl->mBuffers[0].mData, frames); + return noErr; +} + +// The default output unit fed float32 stereo at the asked rate (the +// unit converts to the device's own). +static u32 io_ring_start(IoRing* p, u32 rate) { + AudioComponentDescription desc = { kAudioUnitType_Output, + kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple, 0, 0 }; + AudioComponent comp = AudioComponentFindNext(NULL, &desc); + if (comp == NULL || AudioComponentInstanceNew(comp, &p->unit) != noErr) { + return ENODEV; + } + AudioStreamBasicDescription fmt = { 0 }; + fmt.mSampleRate = rate; + fmt.mFormatID = kAudioFormatLinearPCM; + fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; + fmt.mBytesPerPacket = 8; + fmt.mFramesPerPacket = 1; + fmt.mBytesPerFrame = 8; + fmt.mChannelsPerFrame = 2; + fmt.mBitsPerChannel = 32; + AURenderCallbackStruct cb = { io_ring_pump, p }; + bool ok = AudioUnitSetProperty(p->unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &fmt, sizeof fmt) == noErr + && AudioUnitSetProperty(p->unit, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &cb, sizeof cb) == noErr + && AudioUnitInitialize(p->unit) == noErr + && AudioOutputUnitStart(p->unit) == noErr; + return ok ? 0 : ENODEV; +} + +static void io_ring_free(IoRing* p) { + if (p->unit != NULL) { + AudioOutputUnitStop(p->unit); + AudioUnitUninitialize(p->unit); + AudioComponentInstanceDispose(p->unit); + } + free(p); +} + +#elif defined(__linux__) + +// A thread feeds the default ALSA device 256 frames at a time (a +// write blocks until the device has room, so the ring drains at the +// device's clock). +static void* io_ring_pump(void* ctx) { + IoRing* p = ctx; + float out[256 * 2]; + while (atomic_load_explicit(&p->done, memory_order_relaxed) == 0) { + io_ring_pull(p, out, 256); + snd_pcm_sframes_t n = snd_pcm_writei(p->unit, out, 256); + if (n < 0) { + snd_pcm_recover(p->unit, (int)n, 1); + } + } + return NULL; +} + +static void io_ring_hush(const char* file, int line, const char* fn, int err, + const char* fmt, ...) { +} + +static u32 io_ring_start(IoRing* p, u32 rate) { + snd_lib_error_set_handler(io_ring_hush); + if (snd_pcm_open(&p->unit, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { + return ENODEV; + } + if (snd_pcm_set_params(p->unit, SND_PCM_FORMAT_FLOAT_LE, + SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 1, 20000) < 0 + || pthread_create(&p->pump, NULL, io_ring_pump, p) != 0) { + return ENODEV; + } + return 0; +} + +static void io_ring_free(IoRing* p) { + if (p->unit != NULL) { + atomic_store_explicit(&p->done, 1, memory_order_relaxed); + if (p->pump != 0) { + pthread_join(p->pump, NULL); + } + snd_pcm_close(p->unit); + } + free(p); +} + +#else + +static u32 io_ring_start(IoRing* p, u32 rate) { + return ENOTSUP; +} + +static void io_ring_free(IoRing* p) { + free(p); +} + +#endif +#endif diff --git a/bend2/effs/audio_write.c b/bend2/effs/audio_write.c index 8727c0563..0939a3906 100644 --- a/bend2/effs/audio_write.c +++ b/bend2/effs/audio_write.c @@ -1,168 +1,7 @@ // Audio // ===== -// The ring: 4096 float32 stereo frames. The effects fill it on the -// evaluator's thread; the device's callback drains it and pads the -// rest of its buffer with silence. The same block sits in -// audio_write.c and audio_close.c under this guard, so that any one -// of the three effects compiles alone. -#ifndef IO_RING -#define IO_RING 4096u - -#ifdef __OBJC__ -#import -#elif defined(__linux__) -#include -#endif - -typedef struct { - _Atomic(u64) read, written; - float pcm[IO_RING * 2]; -#ifdef __OBJC__ - AudioUnit unit; -#elif defined(__linux__) - snd_pcm_t* unit; - pthread_t pump; - _Atomic(u32) done; -#endif -} IoRing; - -// n frames queued after the write; a write past the ring's room is -// dropped and the queue answered as it is. -static u64 io_ring_write(IoRing* p, const float* pcm, u32 n) { - u64 w = atomic_load_explicit(&p->written, memory_order_relaxed); - u64 r = atomic_load_explicit(&p->read, memory_order_acquire); - if (w - r + n <= IO_RING) { - u32 at = (u32)(w % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(p->pcm + at * 2, pcm, first * 8); - memcpy(p->pcm, pcm + first * 2, (n - first) * 8); - atomic_store_explicit(&p->written, w + n, memory_order_release); - w += n; - } - return w - r; -} - -static void io_ring_pull(IoRing* p, float* out, u32 frames) { - u64 r = atomic_load_explicit(&p->read, memory_order_relaxed); - u64 w = atomic_load_explicit(&p->written, memory_order_acquire); - u32 n = (u32)(w - r < frames ? w - r : frames); - u32 at = (u32)(r % IO_RING); - u32 first = n < IO_RING - at ? n : IO_RING - at; - memcpy(out, p->pcm + at * 2, first * 8); - memcpy(out + first * 2, p->pcm, (n - first) * 8); - memset(out + n * 2, 0, (frames - n) * 8); - atomic_store_explicit(&p->read, r + n, memory_order_release); -} - -#ifdef __OBJC__ - -static OSStatus io_ring_pump(void* ctx, AudioUnitRenderActionFlags* flags, - const AudioTimeStamp* when, UInt32 bus, UInt32 frames, - AudioBufferList* bl) { - if (bl->mNumberBuffers != 1 || bl->mBuffers[0].mNumberChannels != 2 - || bl->mBuffers[0].mDataByteSize < frames * 8) { - return kAudio_ParamError; - } - io_ring_pull(ctx, bl->mBuffers[0].mData, frames); - return noErr; -} - -// The default output unit fed float32 stereo at the asked rate (the -// unit converts to the device's own). -static u32 io_ring_start(IoRing* p, u32 rate) { - AudioComponentDescription desc = { kAudioUnitType_Output, - kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple, 0, 0 }; - AudioComponent comp = AudioComponentFindNext(NULL, &desc); - if (comp == NULL || AudioComponentInstanceNew(comp, &p->unit) != noErr) { - return ENODEV; - } - AudioStreamBasicDescription fmt = { 0 }; - fmt.mSampleRate = rate; - fmt.mFormatID = kAudioFormatLinearPCM; - fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; - fmt.mBytesPerPacket = 8; - fmt.mFramesPerPacket = 1; - fmt.mBytesPerFrame = 8; - fmt.mChannelsPerFrame = 2; - fmt.mBitsPerChannel = 32; - AURenderCallbackStruct cb = { io_ring_pump, p }; - bool ok = AudioUnitSetProperty(p->unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &fmt, sizeof fmt) == noErr - && AudioUnitSetProperty(p->unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &cb, sizeof cb) == noErr - && AudioUnitInitialize(p->unit) == noErr - && AudioOutputUnitStart(p->unit) == noErr; - return ok ? 0 : ENODEV; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - AudioOutputUnitStop(p->unit); - AudioUnitUninitialize(p->unit); - AudioComponentInstanceDispose(p->unit); - } - free(p); -} - -#elif defined(__linux__) - -// A thread feeds the default ALSA device 256 frames at a time (a -// write blocks until the device has room, so the ring drains at the -// device's clock). -static void* io_ring_pump(void* ctx) { - IoRing* p = ctx; - float out[256 * 2]; - while (atomic_load_explicit(&p->done, memory_order_relaxed) == 0) { - io_ring_pull(p, out, 256); - snd_pcm_sframes_t n = snd_pcm_writei(p->unit, out, 256); - if (n < 0) { - snd_pcm_recover(p->unit, (int)n, 1); - } - } - return NULL; -} - -static void io_ring_hush(const char* file, int line, const char* fn, int err, - const char* fmt, ...) { -} - -static u32 io_ring_start(IoRing* p, u32 rate) { - snd_lib_error_set_handler(io_ring_hush); - if (snd_pcm_open(&p->unit, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { - return ENODEV; - } - if (snd_pcm_set_params(p->unit, SND_PCM_FORMAT_FLOAT_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 1, 20000) < 0 - || pthread_create(&p->pump, NULL, io_ring_pump, p) != 0) { - return ENODEV; - } - return 0; -} - -static void io_ring_free(IoRing* p) { - if (p->unit != NULL) { - atomic_store_explicit(&p->done, 1, memory_order_relaxed); - if (p->pump != 0) { - pthread_join(p->pump, NULL); - } - snd_pcm_close(p->unit); - } - free(p); -} - -#else - -static u32 io_ring_start(IoRing* p, u32 rate) { - return ENOTSUP; -} - -static void io_ring_free(IoRing* p) { - free(p); -} - -#endif -#endif +// @src audio_ring.c // The samples (interleaved L R ...) into the ring; the frames queued // after the write. Past the ring's room, the samples are dropped and