struct

Tryst::SDL::AudioCapture::RingState

Inherits Struct < Value < Object

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

new(capacity : Int32)
Source

Instance methods

active?
Source
capacity
Source
pop(dest : Pointer(Float32)) : Tuple(Int32, Int64)

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}.

Source
push(pcm : Pointer(Float32), samples : Int32) : Nil

Audio-thread side. samples counts individual floats (interleaved channels), matching how SDL_mixer's postmix callback itself counts them.

Source
stop

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.

Source