diff --git a/src/audio/backend.rs b/src/audio/backend.rs index 583edf6..236807d 100644 --- a/src/audio/backend.rs +++ b/src/audio/backend.rs @@ -1,125 +1,30 @@ use librespot::playback::audio_backend::{Sink, SinkAsBytes, SinkResult}; use librespot::playback::convert::Converter; use librespot::playback::decoder::AudioPacket; -use log::{error, trace}; use std::io::Write; -use std::sync::{Arc, Mutex}; -use std::thread::JoinHandle; -use std::time::Duration; use crate::ipc; use crate::ipc::packet::IpcPacket; pub struct StdoutSink { client: ipc::Client, - buffer: Arc>>, - is_stopped: Arc>, - handle: Option>, } -const BUFFER_SIZE: usize = 7680; - impl StdoutSink { - pub fn start_writer(&mut self) { - // With 48khz, 32-bit float, 2 channels, 1 second of audio is 384000 bytes - // 384000 / 50 = 7680 bytes per 20ms - - let buffer = self.buffer.clone(); - let is_stopped = self.is_stopped.clone(); - let client = self.client.clone(); - - let handle = std::thread::spawn(move || { - let mut output = std::io::stdout(); - let mut act_buffer = [0u8; BUFFER_SIZE]; - - // Use closure to make sure lock is released as fast as possible - let is_stopped = || { - let is_stopped = is_stopped.lock().unwrap(); - *is_stopped - }; - - // Start songbird's playback - client.send(IpcPacket::StartPlayback).unwrap(); - - loop { - if is_stopped() { - break; - } - - std::thread::sleep(Duration::from_millis(15)); - - let mut buffer = buffer.lock().unwrap(); - let to_drain: usize; - - if buffer.len() < BUFFER_SIZE { - // Copy the buffer into the action buffer - // Fill remaining length with zeroes - act_buffer[..buffer.len()].copy_from_slice(&buffer[..]); - act_buffer[buffer.len()..].fill(0); - - to_drain = buffer.len(); - } else { - act_buffer.copy_from_slice(&buffer[..BUFFER_SIZE]); - to_drain = BUFFER_SIZE; - } - - output.write_all(&act_buffer).unwrap_or(()); - buffer.drain(..to_drain); - } - }); - - self.handle = Some(handle); - } - - pub fn stop_writer(&mut self) -> std::thread::Result<()> { - // Use closure to avoid deadlocking the mutex - let set_stopped = |value| { - let mut is_stopped = self.is_stopped.lock().unwrap(); - *is_stopped = value; - }; - - // Notify thread to stop - set_stopped(true); - - // Wait for thread to stop - let result = match self.handle.take() { - Some(handle) => handle.join(), - None => Ok(()), - }; - - // Reset stopped value - set_stopped(false); - - result - } - pub fn new(client: ipc::Client) -> Self { - StdoutSink { - client, - is_stopped: Arc::new(Mutex::new(false)), - buffer: Arc::new(Mutex::new(Vec::new())), - handle: None, - } + StdoutSink { client } } } impl Sink for StdoutSink { fn start(&mut self) -> SinkResult<()> { - self.start_writer(); + // TODO: Handle error + self.client.send(IpcPacket::StartPlayback).unwrap(); Ok(()) } fn stop(&mut self) -> SinkResult<()> { - // Stop the writer thread - // This is done before pausing songbird, because else the writer thread - // might hang on writing to stdout - if let Err(why) = self.stop_writer() { - error!("Failed to stop stdout writer: {:?}", why); - } else { - trace!("Stopped stdout writer"); - } - // Stop songbird's playback self.client.send(IpcPacket::StopPlayback).unwrap(); @@ -140,7 +45,11 @@ impl Sink for StdoutSink { &samples_f32, ) .unwrap(); - self.write_bytes(resampled.as_bytes())?; + + let samples_i16 = + &converter.f64_to_s16(&resampled.iter().map(|v| *v as f64).collect::>()); + + self.write_bytes(samples_i16.as_bytes())?; } Ok(()) @@ -149,18 +58,7 @@ impl Sink for StdoutSink { impl SinkAsBytes for StdoutSink { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> { - let get_buffer_len = || { - let buffer = self.buffer.lock().unwrap(); - buffer.len() - }; - - while get_buffer_len() > BUFFER_SIZE * 2 { - std::thread::sleep(Duration::from_millis(15)); - } - - let mut buffer = self.buffer.lock().unwrap(); - - buffer.extend_from_slice(data); + std::io::stdout().write_all(data).unwrap(); Ok(()) } diff --git a/src/session/mod.rs b/src/session/mod.rs index dbc96bb..915c931 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -19,7 +19,7 @@ use serenity::{ }; use songbird::{ create_player, - input::{children_to_reader, Input}, + input::{children_to_reader, Codec, Container, Input}, tracks::TrackHandle, Call, Event, EventContext, EventHandler, }; @@ -137,7 +137,8 @@ impl SpoticordSession { let reader = children_to_reader::(vec![child]); // Create track (paused, fixes audio glitches) - let (mut track, track_handle) = create_player(Input::float_pcm(true, reader)); + let (mut track, track_handle) = + create_player(Input::new(true, reader, Codec::Pcm, Container::Raw, None)); track.pause(); // Set call audio to track @@ -549,6 +550,9 @@ impl SpoticordSession { loop { timer.tick().await; + // Make sure this task has not been aborted, if it has this will automatically stop execution. + tokio::task::yield_now().await; + let is_playing = { let pbi = pbi.read().await;