use {
super::*,
fundsp::{
MAX_BUFFER_SIZE,
audionode::AudioNode,
audiounit::AudioUnit,
buffer::{BufferRef, BufferVec},
combinator::An,
prelude::U0,
realseq::SequencerBackend,
sequencer::{Fade, Sequencer},
wave::{Wave, WavePlayer},
},
rubato::FftFixedIn,
};
pub(crate) struct Tap {
backend: Arc<Mutex<Backend>>,
done: Duration,
format: SoundFormat,
muted: Arc<AtomicBool>,
paused: Arc<AtomicBool>,
sequencer: Sequencer,
stream: Option<Stream>,
time: Duration,
}
impl Tap {
pub(crate) const CHANNELS: u16 = 2;
pub(crate) fn drain(&mut self) -> Sound {
self.drain_exact(None).unwrap()
}
pub(crate) fn drain_exact(&mut self, count: Option<usize>) -> Option<Sound> {
let mut backend = self.backend.lock().unwrap();
if let Some(count) = count
&& backend.samples.len() < count
{
return None;
}
let end = count.unwrap_or(backend.samples.len());
let samples = backend.samples.drain(..end).collect();
let sound = Sound::new(self.format, samples);
self.time += sound.duration();
Some(sound)
}
pub(crate) fn format(&self) -> SoundFormat {
self.format
}
pub(crate) fn is_done(&self) -> bool {
self.time >= self.done
}
pub(crate) fn load_wave(&self, path: &Utf8Path) -> Result<Arc<Wave>> {
let mut input = Wave::load(path).context(error::WaveLoad)?;
if input.is_empty() {
return Ok(Arc::new(Wave::new(1, self.format.sample_rate as f64)));
}
while input.channels() > Self::CHANNELS.into_usize() {
input.remove_channel(input.channels() - 1);
}
let sample_rate = input.sample_rate();
if sample_rate.fract() != 0.0 {
return Err(error::WaveSampleRate { sample_rate }.build());
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let sample_rate = sample_rate as usize;
if sample_rate == self.format.sample_rate.into_usize() {
return Ok(Arc::new(input));
}
let start = Instant::now();
let mut resampler = FftFixedIn::<f32>::new(
sample_rate,
self.format.sample_rate.into_usize(),
1024,
2,
input.channels(),
)
.context(error::WaveResamplerConstruction)?;
let input_channels = (0..input.channels())
.map(|channel| input.channel(channel).as_slice())
.collect::<Vec<&[f32]>>();
let output_channels = resampler
.process_all(
&input_channels,
None,
self.format.sample_rate as f64 / sample_rate as f64,
)
.context(error::WaveResample)?;
log::info!("resampled {path} in {:.2}s", start.elapsed().as_secs_f32());
let mut output = Wave::new(0, self.format.sample_rate as f64);
for channel in output_channels {
output.push_channel(&channel);
}
Ok(Arc::new(output))
}
pub(crate) fn new(options: &Options, sample_rate: u32) -> Self {
let mut sequencer = Sequencer::new(false, Self::CHANNELS.into());
sequencer.set_sample_rate(sample_rate.into());
let sequencer_backend = sequencer.backend();
let paused = Arc::new(AtomicBool::new(false));
let muted = Arc::new(AtomicBool::new(options.mute));
Self {
backend: Arc::new(Mutex::new(Backend {
buffer: BufferVec::new(Self::CHANNELS.into()),
muted: muted.clone(),
paused: paused.clone(),
sample: 0,
samples: Vec::new(),
sequencer_backend,
})),
done: Duration::ZERO,
format: SoundFormat {
channels: Self::CHANNELS,
sample_rate,
},
muted,
paused,
sequencer,
stream: None,
time: Duration::ZERO,
}
}
pub(crate) fn pause(&self) {
self.paused.store(true, atomic::Ordering::Relaxed);
}
pub(crate) fn play(&self) {
self.paused.store(false, atomic::Ordering::Relaxed);
}
pub(crate) fn sequence<T>(&mut self, node: An<T>, duration: f64, fade_in: f64, fade_out: f64)
where
T: AudioNode<Inputs = U0> + IntoStereo<T::Outputs> + 'static,
{
self.done = self.done.max(self.time + Duration::from_secs_f64(duration));
self.sequencer.push_relative(
0.0,
duration,
Fade::default(),
fade_in,
fade_out,
node.0.into_stereo(),
);
}
pub(crate) fn sequence_wave(&mut self, wave: &Arc<Wave>, fade_in: f64, fade_out: f64) {
if wave.channels() == 0 {
return;
}
let duration = wave.duration();
if wave.channels() == 1 {
let mono = WavePlayer::new(wave, 0, 0, wave.len(), None);
self.sequence(An(mono), duration, fade_in, fade_out);
} else {
let left = WavePlayer::new(wave, 0, 0, wave.len(), None);
let right = WavePlayer::new(wave, 1, 0, wave.len(), None);
self.sequence(An(left) | An(right), duration, fade_in, fade_out);
}
}
pub(crate) fn stream(
&mut self,
output_device: &cpal::Device,
stream_config: &StreamConfig,
) -> Result {
let backend = self.backend.clone();
self.stream = None;
let stream = output_device
.build_output_stream(
stream_config,
move |data: &mut [f32], _info| {
backend.lock().unwrap().write(data);
},
|err| eprintln!("output stream error: {err}"),
None,
)
.context(error::AudioBuildOutputStream)?;
self.stream = Some(stream);
log::info!(
"output stream opened: {}",
StreamConfigDisplay(stream_config),
);
Ok(())
}
pub(crate) fn toggle_muted(&self) {
self.muted.fetch_xor(true, atomic::Ordering::Relaxed);
}
pub(crate) fn write(&self, buffer: &mut [f32]) {
self.backend.lock().unwrap().write(buffer);
}
}
struct Backend {
buffer: BufferVec,
muted: Arc<AtomicBool>,
paused: Arc<AtomicBool>,
sample: u64,
samples: Vec<f32>,
sequencer_backend: SequencerBackend,
}
impl Backend {
fn write(&mut self, buffer: &mut [f32]) {
if self.paused.load(atomic::Ordering::Relaxed) {
buffer.fill(0.0);
return;
}
let muted = self.muted.load(atomic::Ordering::Relaxed);
for slot in buffer {
if self
.sample
.is_multiple_of(MAX_BUFFER_SIZE.into_u64() * u64::from(Tap::CHANNELS))
{
self.sequencer_backend.process(
MAX_BUFFER_SIZE,
&BufferRef::empty(),
&mut self.buffer.buffer_mut(),
);
}
let sample = self.buffer.at_f32(
self.sample.into_usize() % Tap::CHANNELS.into_usize(),
(self.sample.into_usize() / Tap::CHANNELS.into_usize()) % MAX_BUFFER_SIZE,
);
*slot = if muted { 0.0 } else { sample };
self.samples.push(sample);
self.sample += 1;
}
}
}