Tryst::SDL::AudioCapture::RingState
Shared state between the audio-thread callback (the producer, #push) and the drain fiber (the consumer, #pop). A struct, not a Crystal object, reached through a raw pointer - same reasoning as ever: the audio thread must never touch a Crystal object or dispatch a method on one.
Every mutable field here is an Atomic, and every mutation goes
through a method call on pointer.value (@produced.add(...)
and friends) rather than a field assignment. That distinction
matters and is not obvious: pointer.value.field = x and
pointer.value.field += x both silently write to a throwaway
copy and never reach the pointee - Crystal's op-assign desugars
to evaluating pointer.value once into a local temporary and
never writing it back. A plain method call on that same
receiver does not have this problem - Crystal passes self for
a struct method by the address the receiver dereferenced from,
so pointer.value.push(...) mutates the real memory in place,
including under genuine concurrent multi-thread stress. Every
mutator on this struct is therefore a method, never a bare
field write, and that rule must not be broken by future edits
here.
produced is written only by the audio thread, consumed only
by the drain fiber - the single-writer-per-counter property that
makes this lock-free. Both are monotonic counts of samples ever
produced/consumed, not ring positions - % capacity turns one
into the other where needed.
Constructors
Instance methods
Drain-side. Copies whatever is newly available into dest
(which must be at least #capacity floats) and returns
{samples copied, samples dropped since the last #pop}.
Audio-thread side. samples counts individual floats
(interleaved channels), matching how SDL_mixer's postmix
callback itself counts them.
Tells the audio thread to stop accepting samples. Call only while the postmix callback cannot be running concurrently (the mixer's own audio lock, held by AudioCapture#stop) - otherwise a push already past its #active? check could still land after the drain fiber has taken this as the last word and exited.