drain method
Copy out everything readable and advance the read cursor.
writeCursor MUST come from an atomic (acquire) load by the caller — the
samples it advertises are only guaranteed visible through that load.
Implementation
MirrorDrain drain(int writeCursor, {int? maxFrames}) {
var n = available(writeCursor);
if (maxFrames != null && maxFrames >= 0 && n > maxFrames) n = maxFrames;
final totalOverrun = header[MirrorHeader.overrunFrames];
final newOverrun = (totalOverrun - _lastOverrun) & 0xFFFFFFFF;
_lastOverrun = totalOverrun;
if (n == 0) return MirrorDrain(Float32List(0), newOverrun);
final r = header[MirrorHeader.readCursor];
final off = r % capacityFrames;
final first = (off + n > capacityFrames) ? capacityFrames - off : n;
final out = Float32List(n * channels);
out.setRange(0, first * channels, samples, off * channels);
if (first < n) {
out.setRange(first * channels, n * channels, samples, 0);
}
header[MirrorHeader.readCursor] = (r + n) & 0xFFFFFFFF;
return MirrorDrain(out, newOverrun);
}