use crate::audio::AudioBuffer;
use crossbeam::queue::SegQueue;
use std::sync::Arc;
use std::time::{Duration, Instant};
pub struct AudioScheduler {
incoming: Arc<SegQueue<AudioBuffer>>,
sorted: Arc<parking_lot::Mutex<Vec<AudioBuffer>>>,
}
impl AudioScheduler {
pub fn new() -> Self {
Self {
incoming: Arc::new(SegQueue::new()),
sorted: Arc::new(parking_lot::Mutex::new(Vec::new())),
}
}
pub fn schedule(&self, buffer: AudioBuffer) {
self.incoming.push(buffer);
}
pub fn is_empty(&self) -> bool {
self.incoming.is_empty() && self.sorted.lock().is_empty()
}
pub fn clear(&self) {
let mut sorted = self.sorted.lock();
while self.incoming.pop().is_some() {}
sorted.clear();
}
pub fn next_ready(&self) -> Option<AudioBuffer> {
self.next_ready_with_latency(Duration::ZERO)
}
pub fn next_ready_with_latency(&self, output_latency: Duration) -> Option<AudioBuffer> {
let mut sorted = self.sorted.lock();
while let Some(buf) = self.incoming.pop() {
let pos = sorted
.binary_search_by_key(&buf.timestamp, |b| b.timestamp)
.unwrap_or_else(|e| e);
sorted.insert(pos, buf);
}
let now = Instant::now();
let early_ok = Duration::from_micros(1000);
if let Some(buf) = sorted.first() {
if buf.play_at <= now + early_ok + output_latency {
return Some(sorted.remove(0));
}
}
None
}
}
impl Default for AudioScheduler {
fn default() -> Self {
Self::new()
}
}