use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc;
use crate::core::resample::STREAM_SINC_MAX_HALF_TAPS;
#[derive(Debug)]
pub(crate) struct TrackAnchor {
generation: AtomicU64,
ring_frame: AtomicU64,
track_frame: AtomicU64,
}
impl TrackAnchor {
fn new() -> Self {
Self {
generation: AtomicU64::new(0),
ring_frame: AtomicU64::new(0),
track_frame: AtomicU64::new(0),
}
}
pub(crate) fn store(&self, ring_frame: u64, track_frame: u64) {
let generation = self.generation.load(Ordering::Relaxed);
self.generation.store(generation + 1, Ordering::Release);
self.ring_frame.store(ring_frame, Ordering::Relaxed);
self.track_frame.store(track_frame, Ordering::Relaxed);
self.generation.store(generation + 2, Ordering::Release);
}
pub(crate) fn load(&self) -> Option<(u64, u64)> {
let g1 = self.generation.load(Ordering::Acquire);
if g1 % 2 != 0 {
return None;
}
let ring = self.ring_frame.load(Ordering::Relaxed);
let track = self.track_frame.load(Ordering::Relaxed);
let g2 = self.generation.load(Ordering::Acquire);
if g1 == g2 {
Some((ring, track))
} else {
None
}
}
}
#[derive(Debug)]
pub(crate) struct SourceRing {
slots: Box<[AtomicU32]>,
head: AtomicU64,
tail: AtomicU64,
channels: usize,
pub(crate) anchor: TrackAnchor,
}
impl SourceRing {
pub(crate) fn new(capacity_frames: usize, channels: usize) -> Arc<Self> {
let capacity = capacity_frames * channels;
let slots: Vec<AtomicU32> = (0..capacity).map(|_| AtomicU32::new(0)).collect();
Arc::new(Self {
slots: slots.into_boxed_slice(),
head: AtomicU64::new(0),
tail: AtomicU64::new(0),
channels,
anchor: TrackAnchor::new(),
})
}
#[inline]
pub(crate) fn head_frames(&self) -> u64 {
self.head.load(Ordering::Relaxed) / self.channels as u64
}
#[inline]
fn capacity(&self) -> usize {
self.slots.len()
}
#[inline]
pub(crate) fn capacity_samples(&self) -> usize {
self.slots.len()
}
#[inline]
pub(crate) fn occupied(&self) -> usize {
let tail = self.tail.load(Ordering::Acquire);
let head = self.head.load(Ordering::Acquire);
tail.saturating_sub(head) as usize
}
pub(crate) fn push_slice(&self, input: &[f32]) -> usize {
let tail = self.tail.load(Ordering::Relaxed);
let head = self.head.load(Ordering::Acquire);
let free = self.capacity() - tail.saturating_sub(head) as usize;
let count = input.len().min(free);
for (i, &sample) in input.iter().take(count).enumerate() {
let idx = ((tail + i as u64) % self.capacity() as u64) as usize;
self.slots[idx].store(sample.to_bits(), Ordering::Relaxed);
}
self.tail.store(tail + count as u64, Ordering::Release);
count
}
#[inline]
pub(crate) fn free(&self) -> usize {
let tail = self.tail.load(Ordering::Relaxed);
let head = self.head.load(Ordering::Acquire);
self.capacity() - tail.saturating_sub(head) as usize
}
pub(crate) fn pop_slice(&self, out: &mut [f32]) -> usize {
let head = self.head.load(Ordering::Relaxed);
let tail = self.tail.load(Ordering::Acquire);
let count = out.len().min(tail.saturating_sub(head) as usize);
for (i, sample) in out.iter_mut().take(count).enumerate() {
let idx = ((head + i as u64) % self.capacity() as u64) as usize;
*sample = f32::from_bits(self.slots[idx].load(Ordering::Relaxed));
}
self.head.store(head + count as u64, Ordering::Release);
count
}
#[inline]
pub(crate) fn channels(&self) -> usize {
self.channels
}
}
#[derive(Debug)]
pub struct SourceProducer {
ring: Arc<SourceRing>,
pushed_frames: u64,
}
impl SourceProducer {
pub(crate) fn new(ring: Arc<SourceRing>) -> Self {
Self {
ring,
pushed_frames: 0,
}
}
pub fn push(&mut self, interleaved: &[f32]) -> usize {
let channels = self.ring.channels();
debug_assert_eq!(interleaved.len() % channels, 0);
let free_frames = self.ring.free() / channels;
let frames = (interleaved.len() / channels).min(free_frames);
let pushed = self.ring.push_slice(&interleaved[..frames * channels]);
debug_assert_eq!(pushed, frames * channels);
self.pushed_frames += frames as u64;
frames
}
pub fn occupied_frames(&self) -> usize {
self.ring.occupied() / self.ring.channels()
}
pub fn free_frames(&self) -> usize {
self.ring.slots.len() / self.ring.channels() - self.occupied_frames()
}
pub fn pushed_frames(&self) -> u64 {
self.pushed_frames
}
pub fn demand_hint(&self, out_frames: usize, max_rate: f64) -> usize {
let rate = max_rate.clamp(
super::control::MIN_TEMPO_RATE,
super::control::MAX_TEMPO_RATE,
);
(out_frames as f64 * rate).ceil() as usize + STREAM_SINC_MAX_HALF_TAPS + 2
}
pub fn set_track_position(&mut self, track_frame: u64) {
self.ring.anchor.store(self.pushed_frames, track_frame);
}
pub fn finish(&mut self) -> bool {
let channels = self.ring.channels();
let padding = [0.0f32; 64];
let mut remaining_frames = STREAM_SINC_MAX_HALF_TAPS + 2;
while remaining_frames > 0 {
let batch_frames = remaining_frames.min(padding.len() / channels);
let accepted = self.push(&padding[..batch_frames * channels]);
if accepted == 0 {
return false;
}
remaining_frames -= accepted;
}
true
}
}
#[derive(Debug, Clone, Copy, Default)]
struct TimelineEntry {
output_abs: u64,
source_pos: f64,
rate: f64,
}
#[derive(Debug)]
pub(crate) struct TimelineMap {
entries: Box<[TimelineEntry]>,
head: usize,
len: usize,
}
impl TimelineMap {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
entries: vec![TimelineEntry::default(); capacity.max(2)].into_boxed_slice(),
head: 0,
len: 0,
}
}
pub(crate) fn clear(&mut self) {
self.head = 0;
self.len = 0;
}
#[inline]
fn at(&self, i: usize) -> TimelineEntry {
self.entries[(self.head + i) % self.entries.len()]
}
pub(crate) fn push(&mut self, output_abs: u64, source_pos: f64, rate: f64) {
if self.len > 0 {
let last_idx = (self.head + self.len - 1) % self.entries.len();
let last = self.entries[last_idx];
if output_abs <= last.output_abs || self.len == self.entries.len() {
self.entries[last_idx] = TimelineEntry {
output_abs: output_abs.max(last.output_abs),
source_pos: source_pos.max(last.source_pos),
rate,
};
return;
}
}
let idx = (self.head + self.len) % self.entries.len();
self.entries[idx] = TimelineEntry {
output_abs,
source_pos,
rate,
};
self.len += 1;
}
pub(crate) fn evict_before(&mut self, consumed: u64) {
while self.len >= 2 && self.at(1).output_abs <= consumed {
self.head = (self.head + 1) % self.entries.len();
self.len -= 1;
}
}
pub(crate) fn map_to_source(&self, output: f64) -> Option<f64> {
if self.len == 0 {
return None;
}
let first = self.at(0);
if output <= first.output_abs as f64 {
return Some(first.source_pos);
}
for i in 1..self.len {
let a = self.at(i - 1);
let b = self.at(i);
if output <= b.output_abs as f64 {
let span = (b.output_abs - a.output_abs) as f64;
if span <= 0.0 {
return Some(b.source_pos);
}
let t = (output - a.output_abs as f64) / span;
return Some(a.source_pos + t * (b.source_pos - a.source_pos));
}
}
Some(self.at(self.len - 1).source_pos)
}
pub(crate) fn map_to_output(&self, source: f64) -> Option<f64> {
if self.len == 0 {
return None;
}
let first = self.at(0);
if source <= first.source_pos {
return Some(first.output_abs as f64);
}
for i in 1..self.len {
let a = self.at(i - 1);
let b = self.at(i);
if source <= b.source_pos {
let span = b.source_pos - a.source_pos;
if span <= 0.0 {
return Some(b.output_abs as f64);
}
let t = (source - a.source_pos) / span;
return Some(a.output_abs as f64 + t * (b.output_abs - a.output_abs) as f64);
}
}
let last = self.at(self.len - 1);
let rate = last.rate.max(1e-6);
Some(last.output_abs as f64 + (source - last.source_pos) / rate)
}
pub(crate) fn rate_at(&self, output: f64) -> Option<f64> {
if self.len == 0 {
return None;
}
let first = self.at(0);
if output <= first.output_abs as f64 {
return Some(first.rate);
}
for i in 1..self.len {
let a = self.at(i - 1);
let b = self.at(i);
if output <= b.output_abs as f64 {
let span = (b.output_abs - a.output_abs) as f64;
if span <= 0.0 {
return Some(b.rate);
}
let t = (output - a.output_abs as f64) / span;
return Some(a.rate + t * (b.rate - a.rate));
}
}
Some(self.at(self.len - 1).rate)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ring_push_pop_wraps() {
let ring = SourceRing::new(4, 2); assert_eq!(ring.push_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), 6);
let mut out = [0.0f32; 4];
assert_eq!(ring.pop_slice(&mut out), 4);
assert_eq!(out, [1.0, 2.0, 3.0, 4.0]);
assert_eq!(ring.push_slice(&[7.0, 8.0, 9.0, 10.0, 11.0, 12.0]), 6);
let mut out8 = [0.0f32; 8];
assert_eq!(ring.pop_slice(&mut out8), 8);
assert_eq!(out8, [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]);
}
#[test]
fn ring_bounded_capacity() {
let ring = SourceRing::new(2, 1);
assert_eq!(ring.push_slice(&[1.0, 2.0, 3.0]), 2);
assert_eq!(ring.occupied(), 2);
}
#[test]
fn producer_never_splits_frames() {
let ring = SourceRing::new(3, 2); let mut producer = SourceProducer::new(Arc::clone(&ring));
assert_eq!(ring.push_slice(&[0.5]), 1);
let frames = producer.push(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(frames, 2);
assert_eq!(ring.occupied(), 5);
let mut out = [0.0f32; 5];
assert_eq!(ring.pop_slice(&mut out), 5);
assert_eq!(out, [0.5, 1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn ring_cross_thread_transfers_all_samples() {
let ring = SourceRing::new(64, 1);
let producer_ring = Arc::clone(&ring);
let total = 100_000usize;
let producer = std::thread::spawn(move || {
let mut sent = 0usize;
while sent < total {
let batch: Vec<f32> = (sent..(sent + 37).min(total)).map(|i| i as f32).collect();
let mut offset = 0;
while offset < batch.len() {
let pushed = producer_ring.push_slice(&batch[offset..]);
offset += pushed;
if pushed == 0 {
std::thread::yield_now();
}
}
sent += batch.len();
}
});
let mut received = 0usize;
let mut buf = [0.0f32; 41];
while received < total {
let n = ring.pop_slice(&mut buf);
for &sample in &buf[..n] {
assert_eq!(sample, received as f32, "out-of-order sample");
received += 1;
}
if n == 0 {
std::thread::yield_now();
}
}
producer.join().unwrap();
}
#[test]
fn timeline_map_interpolates_and_clamps() {
let mut map = TimelineMap::with_capacity(4);
assert_eq!(map.map_to_source(0.0), None);
map.push(0, 0.0, 1.0);
map.push(100, 110.0, 1.2);
map.push(200, 230.0, 1.2);
assert_eq!(map.map_to_source(0.0), Some(0.0));
assert!((map.map_to_source(50.0).unwrap() - 55.0).abs() < 1e-9);
assert!((map.map_to_source(150.0).unwrap() - 170.0).abs() < 1e-9);
assert!((map.map_to_source(500.0).unwrap() - 230.0).abs() < 1e-9);
assert!((map.rate_at(50.0).unwrap() - 1.1).abs() < 1e-9);
}
#[test]
fn timeline_map_merges_when_full_and_keeps_anchor() {
let mut map = TimelineMap::with_capacity(2);
map.push(0, 0.0, 1.0);
map.push(10, 10.0, 1.0);
map.push(20, 20.0, 1.0); assert!((map.map_to_source(20.0).unwrap() - 20.0).abs() < 1e-9);
map.evict_before(25);
assert!(map.map_to_source(25.0).is_some());
}
}