use circular_buffer::CircularBuffer; use songbird::input::core::io::MediaSource; use std::io::SeekFrom; use std::{ io::{Read, Seek, Write}, sync::{Arc, Condvar, Mutex}, }; const BUFFER_SIZE: usize = 64 * 1024; #[derive(Clone, Default)] pub struct CircularBufferSource { condvar: Arc, circular_buffer: Arc>>, } impl Read for CircularBufferSource { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let mut circ_buffer = self.circular_buffer.lock().expect("Mutex was poisoned"); if circ_buffer.is_empty() { buf.fill(0); self.condvar.notify_all(); return Ok(buf.len()); } let bytes_to_read = usize::min(buf.len(), circ_buffer.len()); for (ndx, value) in circ_buffer.drain(0..bytes_to_read).enumerate() { buf[ndx] = value; } self.condvar.notify_all(); Ok(bytes_to_read) } } impl Write for CircularBufferSource { fn write(&mut self, buf: &[u8]) -> std::io::Result { let mut circ_buffer = self.circular_buffer.lock().expect("Mutex was poisoned"); while circ_buffer.len() + buf.len() > BUFFER_SIZE { circ_buffer = self.condvar.wait(circ_buffer).expect("Mutex was poisoned"); } circ_buffer.extend_from_slice(buf); self.condvar.notify_all(); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } impl Seek for CircularBufferSource { fn seek(&mut self, _pos: SeekFrom) -> std::io::Result { Err(std::io::ErrorKind::Unsupported.into()) } } impl MediaSource for CircularBufferSource { fn is_seekable(&self) -> bool { false } fn byte_len(&self) -> Option { None } }