use crate::audio::{Audio, AudioBus, AudioSpec};
use crate::math;
use crate::slices;
use anyhow::{bail, Result};
use std::cmp::{Ord, Ordering};
use std::collections::HashMap;
use std::sync::atomic::{self, AtomicBool};
use std::sync::Arc;
use std::time::{Duration, Instant};
const STATUS_REPORT_INTERVAL: Duration = Duration::from_secs(1);
#[derive(Debug, Copy, Clone)]
pub struct Keyframe {
sample_pos: usize,
val: f32,
}
impl PartialEq for Keyframe {
fn eq(&self, other: &Self) -> bool {
self.sample_pos == other.sample_pos && (self.val - other.val).abs() < 0.001
}
}
impl Eq for Keyframe {}
impl PartialOrd for Keyframe {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Keyframe {
fn cmp(&self, other: &Self) -> Ordering {
self.sample_pos.cmp(&other.sample_pos)
}
}
struct Layer {
bus: AudioBus,
amp_keyframes: Vec<Keyframe>,
total_samples_played: usize,
buffer: Audio,
buffer_pos: usize,
shutdown_when_finished: bool,
last_status_report_instant: Instant,
}
impl Layer {
fn new(bus: AudioBus, shutdown_when_finished: bool) -> Self {
Layer {
buffer: Audio::from_spec(&bus.spec),
bus,
shutdown_when_finished,
amp_keyframes: vec![],
total_samples_played: 0,
buffer_pos: 0,
last_status_report_instant: Instant::now(),
}
}
fn load_next_chunk(&mut self) -> Result<()> {
self.prune_keyframes();
self.log_status();
let mut chunk = self.bus.collect_chunk()?;
for index in 0..chunk.data[0].len() {
let amp = self.current_amp();
for channel in chunk.data.iter_mut() {
channel[index] *= amp;
}
self.total_samples_played += 1;
}
self.buffer = chunk;
self.buffer_pos = 0;
Ok(())
}
fn log_status(&mut self) {
if self.last_status_report_instant.elapsed() < STATUS_REPORT_INTERVAL {
return;
}
let played_dur = Duration::from_secs_f32(
self.total_samples_played as f32 / self.bus.spec.sample_rate as f32,
);
match self.bus.expected_total_samples {
Some(expected_total_samples) => {
let total_dur = Duration::from_secs_f32(
expected_total_samples as f32 / self.bus.spec.sample_rate as f32,
);
let percent_played =
((played_dur.as_secs_f32() / total_dur.as_secs_f32()) * 100.0) as u16;
info!(
"Played {}s of {}s ~ {}%",
played_dur.as_secs(),
total_dur.as_secs(),
percent_played
);
}
None => {
info!("Played {}s", played_dur.as_secs());
}
}
self.last_status_report_instant = Instant::now();
}
#[inline]
fn prune_keyframes(&mut self) {
loop {
if self.amp_keyframes.len() > 1 {
if self.amp_keyframes[self.amp_keyframes.len() - 2].sample_pos
< self.total_samples_played
{
self.amp_keyframes.pop();
continue;
}
}
break;
}
}
fn sort_keyframes(&mut self) {
self.amp_keyframes.sort();
self.amp_keyframes.reverse();
}
fn clear_keyframes_after(&mut self, sample_pos: usize) {
self.amp_keyframes.retain(|k| k.sample_pos <= sample_pos);
}
#[inline]
fn current_amp(&mut self) -> f32 {
let keyframe_len = self.amp_keyframes.len();
if keyframe_len == 0 {
1.0
} else if keyframe_len == 1 {
self.amp_keyframes[0].val
} else {
let prev = self.amp_keyframes[keyframe_len - 1];
let next = self.amp_keyframes[keyframe_len - 2];
let progress = (self.total_samples_played - prev.sample_pos) as f32
/ (next.sample_pos - prev.sample_pos) as f32;
math::sqrt_interp(prev.val, next.val, progress)
}
}
#[inline]
pub fn dur_to_sample(&self, dur: Duration) -> usize {
(dur.as_secs_f32() * self.bus.spec.sample_rate as f32) as usize
}
pub fn fade_from_now(&mut self, to: f32, dur: Duration) {
let current_amp = self.current_amp();
self.amp_keyframes.push(Keyframe {
sample_pos: self.total_samples_played,
val: current_amp,
});
self.amp_keyframes.push(Keyframe {
sample_pos: self.total_samples_played + self.dur_to_sample(dur),
val: to,
});
self.sort_keyframes();
}
pub fn fade(&mut self, start: Duration, start_val: f32, dur: Duration, end_val: f32) {
self.amp_keyframes.push(Keyframe {
sample_pos: self.dur_to_sample(start),
val: start_val,
});
self.amp_keyframes.push(Keyframe {
sample_pos: self.dur_to_sample(start + dur),
val: end_val,
});
self.sort_keyframes();
}
pub fn fade_in_out(&mut self, fade_in_dur: Option<Duration>, fade_out_dur: Option<Duration>) {
if fade_in_dur.is_some() {
self.fade(Duration::from_secs(0), 0.0, fade_in_dur.unwrap(), 1.0);
}
if fade_out_dur.is_some() && self.bus.expected_total_samples.is_some() {
let total_dur = Duration::from_secs_f32(
self.bus.expected_total_samples.unwrap() as f32 / self.bus.spec.sample_rate as f32,
);
let fade_start = total_dur - fade_out_dur.unwrap();
self.fade(fade_start, 1.0, fade_out_dur.unwrap(), 0.0);
}
}
}
pub struct Mixer {
pub spec: AudioSpec,
pub finished_flag: Arc<AtomicBool>,
layers: HashMap<u32, Layer>,
}
impl Mixer {
pub fn new(spec: &AudioSpec) -> Self {
Mixer {
finished_flag: Arc::new(AtomicBool::from(false)),
spec: *spec,
layers: HashMap::new(),
}
}
pub fn fill_buffer(&mut self, out_buf: &mut [f32]) {
slices::zero_slice(out_buf);
for buffer_interleaved_samples in out_buf.chunks_mut(self.spec.channels as usize) {
let mut closed_layer_ids: Vec<u32> = Vec::with_capacity(0);
for (layer_id, layer) in self.layers.iter_mut() {
if layer.buffer_pos >= layer.buffer.data[0].len() {
if layer.load_next_chunk().is_err() {
if layer.shutdown_when_finished {
info!("Layer finished and requested mixer shutdown; setting flag.");
self.finished_flag.store(true, atomic::Ordering::Relaxed);
}
closed_layer_ids.push(*layer_id);
continue;
};
}
for (channel_idx, out_sample_channel) in
buffer_interleaved_samples.iter_mut().enumerate()
{
*out_sample_channel += layer.buffer.data[channel_idx][layer.buffer_pos];
}
layer.buffer_pos += 1;
}
if !closed_layer_ids.is_empty() {
for layer_id in closed_layer_ids.into_iter() {
self.layers.remove(&layer_id);
}
}
}
}
pub fn insert_layer(
&mut self,
id: u32,
bus: AudioBus,
shutdown_when_finished: bool,
) -> Result<()> {
let layer = Layer::new(bus, shutdown_when_finished);
self.layers.insert(id, layer);
Ok(())
}
pub fn fade_out_all_layers(&mut self, dur: Duration) {
for layer in self.layers.values_mut() {
layer.fade_from_now(0.0, dur);
layer.clear_keyframes_after(layer.total_samples_played + layer.dur_to_sample(dur));
}
}
pub fn fade_from_now(&mut self, id: u32, to: f32, dur: Duration) -> Result<()> {
match self.layers.get_mut(&id) {
Some(layer) => Ok(layer.fade_from_now(to, dur)),
None => bail!("Layer not found"),
}
}
pub fn fade(
&mut self,
id: u32,
start: Duration,
start_val: f32,
dur: Duration,
end_val: f32,
) -> Result<()> {
match self.layers.get_mut(&id) {
Some(layer) => Ok(layer.fade(start, start_val, dur, end_val)),
None => bail!("Layer not found"),
}
}
pub fn fade_in_out(
&mut self,
id: u32,
fade_in_dur: Option<Duration>,
fade_out_dur: Option<Duration>,
) -> Result<()> {
match self.layers.get_mut(&id) {
Some(layer) => Ok(layer.fade_in_out(fade_in_dur, fade_out_dur)),
None => bail!("Layer not found"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_utils::*;
use crossbeam_channel::unbounded;
#[test]
fn prune_keyframes() {
let mut layer = basic_layer();
assert!(layer.amp_keyframes.is_empty());
layer.amp_keyframes = vec![
basic_keyframe(4000),
basic_keyframe(1500),
basic_keyframe(1000),
];
layer.total_samples_played = 900;
layer.prune_keyframes();
assert_eq!(layer.amp_keyframes.len(), 3);
layer.total_samples_played = 1200;
layer.prune_keyframes();
assert_eq!(layer.amp_keyframes.len(), 3);
layer.total_samples_played = 2000;
layer.prune_keyframes();
assert_eq!(layer.amp_keyframes.len(), 2);
assert_eq!(layer.amp_keyframes[0].sample_pos, 4000);
assert_eq!(layer.amp_keyframes[1].sample_pos, 1500);
layer.total_samples_played = 5000;
layer.prune_keyframes();
assert_eq!(layer.amp_keyframes.len(), 1);
assert_eq!(layer.amp_keyframes[0].sample_pos, 4000);
}
#[test]
fn clear_keyframes_after_with_no_keyframes() {
let mut layer = basic_layer();
layer.clear_keyframes_after(0);
assert!(layer.amp_keyframes.is_empty());
}
#[test]
fn clear_keyframes_after_with_keyframes_before_and_after() {
let mut layer = basic_layer();
layer.fade(Duration::from_secs(0), 0.5, Duration::from_secs(2), 1.0);
layer.fade(Duration::from_secs(5), 0.3, Duration::from_secs(6), 0.9);
assert_eq!(layer.amp_keyframes.len(), 4);
layer.clear_keyframes_after(layer.dur_to_sample(Duration::from_secs(4)));
assert_eq!(layer.amp_keyframes.len(), 2);
assert_almost_eq(layer.amp_keyframes[0].val, 1.0);
assert_almost_eq(layer.amp_keyframes[1].val, 0.5);
}
fn basic_layer() -> Layer {
let (_, rx) = unbounded();
let spec = AudioSpec {
channels: 2,
sample_rate: 44100,
};
let bus = AudioBus {
spec,
channels: vec![rx],
expected_total_samples: None,
};
Layer::new(bus, false)
}
fn basic_keyframe(sample_pos: usize) -> Keyframe {
Keyframe {
sample_pos,
val: 1.0,
}
}
}