spoticord/src/audio/stream.rs

90 lines
1.9 KiB
Rust

use std::{
io::{Read, Seek, Write},
sync::{Arc, Condvar, Mutex},
};
use songbird::input::reader::MediaSource;
/// The lower the value, the less latency
///
/// Too low of a value results in unpredictable audio
const MAX_SIZE: usize = 32 * 1024;
#[derive(Clone)]
pub struct Stream {
inner: Arc<(Mutex<Vec<u8>>, Condvar)>,
}
impl Stream {
pub fn new() -> Self {
Self {
inner: Arc::new((Mutex::new(Vec::new()), Condvar::new())),
}
}
}
impl Read for Stream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let (mutex, condvar) = &*self.inner;
let mut buffer = mutex.lock().expect("Mutex was poisoned");
// Prevent Discord jitter by filling buffer with zeroes if we don't have any audio
// (i.e. when you skip too far ahead in a song which hasn't been downloaded yet)
if buffer.is_empty() {
buf.fill(0);
condvar.notify_all();
return Ok(buf.len());
}
let max_read = usize::min(buf.len(), buffer.len());
buf[0..max_read].copy_from_slice(&buffer[0..max_read]);
buffer.drain(0..max_read);
condvar.notify_all();
Ok(max_read)
}
}
impl Write for Stream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let (mutex, condvar) = &*self.inner;
let mut buffer = mutex.lock().expect("Mutex was poisoned");
while buffer.len() + buf.len() > MAX_SIZE {
buffer = condvar.wait(buffer).expect("Mutex was poisoned");
}
buffer.extend_from_slice(buf);
condvar.notify_all();
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
let (mutex, condvar) = &*self.inner;
let mut buffer = mutex.lock().expect("Mutex was poisoned");
buffer.clear();
condvar.notify_all();
Ok(())
}
}
impl Seek for Stream {
fn seek(&mut self, _: std::io::SeekFrom) -> std::io::Result<u64> {
Ok(0)
}
}
impl MediaSource for Stream {
fn byte_len(&self) -> Option<u64> {
None
}
fn is_seekable(&self) -> bool {
false
}
}