use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use crossbeam_queue::ArrayQueue;
use crate::{
error::Error,
player::PlaybackId,
source::{
amplified::AmplifiedSourceMessage,
measured::{CpuLoad, SharedCpuLoadState},
mixed::MixerMessage,
panned::PannedSourceMessage,
playback::PlaybackMessageQueue,
},
SynthPlaybackMessage,
};
#[derive(Clone)]
pub struct SynthPlaybackHandle {
is_playing: Arc<AtomicBool>,
playback_id: PlaybackId,
playback_message_queue: PlaybackMessageQueue,
mixer_event_queue: Arc<ArrayQueue<MixerMessage>>,
measurement_state: Option<SharedCpuLoadState>,
}
impl SynthPlaybackHandle {
pub(crate) fn new(
is_playing: Arc<AtomicBool>,
playback_id: PlaybackId,
playback_message_queue: PlaybackMessageQueue,
mixer_event_queue: Arc<ArrayQueue<MixerMessage>>,
measurement_state: Option<SharedCpuLoadState>,
) -> Self {
Self {
is_playing,
playback_id,
playback_message_queue,
mixer_event_queue,
measurement_state,
}
}
pub fn id(&self) -> PlaybackId {
self.playback_id
}
pub fn is_playing(&self) -> bool {
self.is_playing.load(Ordering::Relaxed)
}
pub fn cpu_load(&self) -> Option<CpuLoad> {
self.measurement_state
.as_ref()
.and_then(|state| state.try_lock().map(|state| state.cpu_load()).ok())
}
pub fn stop<T: Into<Option<u64>>>(&self, stop_time: T) -> Result<(), Error> {
if !self.is_playing() {
return Err(Error::SourceNotPlaying);
}
let stop_time = stop_time.into();
if let Some(sample_time) = stop_time {
if self
.mixer_event_queue
.force_push(MixerMessage::StopSource {
playback_id: self.playback_id,
sample_time,
})
.is_some()
{
log::warn!("Mixer's event queue is full.");
log::warn!("Increase the mixer event queue to prevent this from happening...");
}
} else {
if let PlaybackMessageQueue::Synth { playback, .. } = &self.playback_message_queue {
if playback.force_push(SynthPlaybackMessage::Stop).is_some() {
return Err(Self::synth_playback_queue_error("stop"));
}
} else {
unreachable!("Expecting a synth message queue for a synth playback handle");
}
}
Ok(())
}
pub fn set_volume<T: Into<Option<u64>>>(
&self,
volume: f32,
sample_time: T,
) -> Result<(), Error> {
if !self.is_playing() {
return Err(Error::SourceNotPlaying);
}
let sample_time = sample_time.into();
if let Some(sample_time) = sample_time {
if self
.mixer_event_queue
.push(MixerMessage::SetSourceVolume {
playback_id: self.playback_id,
volume,
sample_time,
})
.is_err()
{
return Err(Self::mixer_event_queue_error("set_volume"));
}
} else {
if self
.playback_message_queue
.volume()
.force_push(AmplifiedSourceMessage::SetVolume(volume))
.is_some()
{
}
}
Ok(())
}
pub fn set_panning<T: Into<Option<u64>>>(
&self,
panning: f32,
sample_time: T,
) -> Result<(), Error> {
if !self.is_playing() {
return Err(Error::SourceNotPlaying);
}
let sample_time = sample_time.into();
if let Some(sample_time) = sample_time {
if self
.mixer_event_queue
.push(MixerMessage::SetSourcePanning {
playback_id: self.playback_id,
panning,
sample_time,
})
.is_err()
{
return Err(Self::mixer_event_queue_error("set_panning"));
}
} else {
if self
.playback_message_queue
.panning()
.force_push(PannedSourceMessage::SetPanning(panning))
.is_some()
{
}
}
Ok(())
}
fn mixer_event_queue_error(event_name: &str) -> Error {
log::warn!("Mixer's event queue is full. Failed to send a {event_name} event.");
log::warn!("Increase the mixer event queue to prevent this from happening...");
Error::SendError("Mixer queue is full".to_string())
}
fn synth_playback_queue_error(event_name: &str) -> Error {
log::warn!("Synth playback event queue is full. Failed to send a {event_name} event.");
Error::SendError("Synth playback queue is full".to_string())
}
}