use std::{
sync::{Arc, atomic::Ordering},
time::{Duration, Instant},
};
use cpal::Sample;
use crate::{
CallbackInfo, Controls, PrefetchState, SharedData, Source,
err::Result,
operate_samples,
sample_buffer::{SampleBufferMut, write_silence},
silence_sbuf, slice_sbuf,
source::{DeviceConfig, VolumeIterator},
};
#[derive(Debug)]
pub(super) struct Mixer {
shared: Arc<SharedData>,
volume: VolumeIterator,
last_play: Option<bool>,
last_sound: bool,
info: DeviceConfig,
}
impl Mixer {
pub(super) fn new(shared: Arc<SharedData>, info: DeviceConfig) -> Self {
Self {
shared,
volume: VolumeIterator::default(),
last_play: None,
last_sound: false,
info,
}
}
pub(super) fn mix<'a, 'b: 'a>(
&mut self,
data: &'a mut SampleBufferMut<'b>,
play_time: Instant,
) {
if let Err(e) = self.try_mix(data, play_time) {
silence_sbuf!(data);
_ = self.shared.invoke_err_callback(e);
}
}
fn try_mix<'a, 'b: 'a>(
&mut self,
data: &'a mut SampleBufferMut<'b>,
play_time: Instant,
) -> Result<()> {
let controls = { self.shared.controls()?.clone() };
let lp = self.last_play.unwrap_or(controls.play);
self.last_play = Some(controls.play);
self.volume.set_volume(controls.volume, lp);
if controls.play {
self.last_sound = true;
if !lp {
if self.volume.until_target().is_none() {
self.volume.set_volume(0., lp);
}
self.volume.to_linear_time_rate(
controls.volume,
self.info.sample_rate,
controls.fade_duration,
self.info.channel_count as usize,
);
}
self.play(data, controls)?;
} else {
if lp {
self.volume.to_linear_time_rate(
0.,
self.info.sample_rate,
controls.fade_duration,
self.info.channel_count as usize,
);
}
let len = (self.volume.until_target().unwrap_or(0)
* self.info.channel_count as usize)
.min(data.len());
if len != 0 {
self.play(&mut slice_sbuf!(data, 0..len), controls)?;
self.last_sound = true;
}
let data_len = data.len();
silence_sbuf!(slice_sbuf!(data, len..data_len));
if len == 0 && self.last_sound {
if let Err(e) = self
.shared
.invoke_callback(CallbackInfo::PauseEnds(play_time))
{
_ = self.shared.invoke_err_callback(e);
};
self.last_sound = false;
}
}
Ok(())
}
fn play(
&mut self,
data: &mut SampleBufferMut,
controls: Controls,
) -> Result<()> {
let shared = self.shared.clone();
let mut src = shared.source()?;
let cnt = self.play_source(&mut src, data, &controls)?;
let mut data = slice_sbuf!(data, cnt..);
if data.is_empty() {
return self.check_prefetch_callback(&src, &controls, None);
}
{
let mut psrc = self.shared.prefech_src()?;
let Some(src2) = psrc.as_mut() else {
silence_sbuf!(data);
return if src.is_none() {
self.shared.invoke_callback(CallbackInfo::NoSource)
} else {
*src = None;
self.shared.invoke_callback(CallbackInfo::SourceEnded(
PrefetchState::NoPrefetch,
))
};
};
let cfg = src2.preferred_config();
if cfg.is_some() && cfg.as_ref() != Some(&self.info) {
return self.shared.invoke_callback(
CallbackInfo::SourceEnded(PrefetchState::PrefetchFailed),
);
}
src2.init(&self.info)?;
*src = psrc.take();
}
self.shared.prefetch_notify.store(true, Ordering::Relaxed);
let cnt = self.play_source(&mut src, &mut data, &controls)?;
let data = slice_sbuf!(data, cnt..);
if !data.is_empty() {
silence_sbuf!(data);
*src = None;
self.shared.invoke_callback(CallbackInfo::SourceEnded(
PrefetchState::PrefetchSuccessful,
))?;
self.shared.invoke_callback(CallbackInfo::SourceEnded(
PrefetchState::NoPrefetch,
))
} else {
self.check_prefetch_callback(
&src,
&controls,
Some(CallbackInfo::SourceEnded(
PrefetchState::PrefetchSuccessful,
)),
)
}
}
fn play_source(
&mut self,
src: &mut Option<Box<dyn Source>>,
data: &mut SampleBufferMut,
controls: &Controls,
) -> Result<usize> {
match src.as_mut() {
Some(s) => self.play_source_inner(s, data, controls),
None => Ok(0),
}
}
fn play_source_inner(
&mut self,
src: &mut Box<dyn Source>,
data: &mut SampleBufferMut,
controls: &Controls,
) -> Result<usize> {
let supports_volume = src.volume(self.volume);
let (cnt, e) = src.read(data);
if let Err(e) = e {
_ = self.shared.invoke_err_callback(e.into());
}
if supports_volume {
self.volume.skip_vol(cnt);
}
operate_samples!(data, d, {
if !supports_volume {
if controls.volume != 1. {
#[allow(clippy::useless_conversion)]
for s in d.iter_mut() {
*s = (*s).mul_amp(self.volume.next_vol().into());
}
} else if controls.volume == 0. {
write_silence(&mut d[..cnt]);
}
}
Ok(cnt)
})
}
fn check_prefetch_callback(
&mut self,
src: &Option<Box<dyn Source>>,
controls: &Controls,
qcb: Option<CallbackInfo>,
) -> Result<()> {
let cb = (controls.prefetch != Duration::ZERO
&& self.shared.prefetch_notify.load(Ordering::Relaxed))
.then(|| {
src.as_ref()
.and_then(|t| t.get_time())
.map(|ts| ts.total - ts.current)
.and_then(|t| (t <= controls.prefetch).then_some(t))
})
.flatten();
if let Some(cb) = qcb {
self.shared.invoke_callback(cb)?;
}
if let Some(t) = cb {
self.shared.prefetch_notify.store(false, Ordering::Relaxed);
self.shared.invoke_callback(CallbackInfo::PrefetchTime(t))
} else {
Ok(())
}
}
}