NativeRingAudioSink
A sink that owns its device callback in C, and therefore owns the ring that callback reads.
Why this exists
Every sink used to be handed an AudioRenderCallback and called it from whatever thread its device used. On macOS that made the device's real-time thread a Kotlin mutator the garbage collector has to stop at a safepoint, and the measured worst stop-the-world pause on the development machine was 63 to 256 microseconds against a 10.67 millisecond period. The fix is a callback that never leaves C, and a callback that never leaves C cannot be handed a Kotlin lambda. So a sink that has one says so by implementing this interface, and gets a ring instead.
Why it is a capability and not a fork
The choice belongs to the sink, not to the platform, because the sink is what owns the device callback. A native sink with no C callback simply does not implement this interface and keeps working exactly as before, through AudioSink.open and a Kotlin ring. Nothing in the engine branches on the operating system, and there is one code path for policy: everything about backpressure, clock anchoring, flush ordering and buffer sizing stays in commonMain.
Ownership, which is the part that bites
The ring belongs to the sink for its whole life, and AudioSink.close releases it. That is not a convention that could have gone the other way. Clearing or freeing a ring requires the device callback to be provably out of its render path, and the only code that can promise that is the code that stopped the device. So the engine writes into the ring and never frees it.
What a caller must not do
AudioSink.open is not the entry point for a sink that implements this. Calling it must fail loudly rather than open a device whose callback ignores the lambda it was given, because a device that plays silence while the caller believes its callback is being called is the worst of the three possible behaviours.
Properties
Whether stopping or pausing this device cuts the sound at whatever sample it reached (#486).
The device's own buffer size in sample frames. Sizes the engine's ring.
Device loss, underrun, format change. The sink reports; the engine decides what to do.
How far latencyNanos can be trusted.
A platform handle for effects that attach to this device's stream, or null when the platform has no such concept.
Functions
Nanoseconds of audio handed over but not yet audible, including everything inside the OS and the hardware.
Opens the device.
Opens the device and the ring behind it together, and hands back both.
What the sound the next open plays is, for the platform's own sound processing (#446). The engine calls this before every open with the item's AudioContent, never AudioContent.Automatic, which it has already resolved. It takes effect at that open: a device already open keeps what it was opened with. A platform with no such setting ignores it, which is the default.