diff --git a/engine/src/daw/daw_core/audiograph.rs b/engine/src/daw/daw_core/audiograph.rs index 00837f5..b0be210 100644 --- a/engine/src/daw/daw_core/audiograph.rs +++ b/engine/src/daw/daw_core/audiograph.rs @@ -365,6 +365,25 @@ impl Drop for AudioNode { } } +impl std::fmt::Display for AudioNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "AudioNode {{ + id: {}, + start_offset (ms): {}ms, + track_number: {}, + sample_path: {}, + channels: {}, + duration (ms): {}ms, +}}", + self.id, + self.start_offset.as_millis(), + self.track_number, + self.sample_path, + self.channels, + self.duration().as_millis()) + } +} + pub struct Track { pub name: String, pub idx: usize, @@ -690,7 +709,7 @@ impl AudioGraph<'static> { start_offset: Duration, track_number: u32 ) { - let node = self.get_mut_node(id).unwrap(); + let node = self.get_mut_node_by_id(id).unwrap(); node.start_offset = start_offset; node.track_number = track_number; @@ -761,6 +780,11 @@ impl AudioGraph<'static> { let res = self.nodes.len(); res } + + pub fn count_nodes(&self) -> usize { + let res = self.nodes.len(); + res + } // adjust all node start times to match tempo pub fn fit_nodes_to_tempo( @@ -779,10 +803,14 @@ impl AudioGraph<'static> { } } - pub fn get_mut_node(&mut self, id: u64) -> Option<&mut AudioNode> { + pub fn get_mut_node_by_id(&mut self, id: u64) -> Option<&mut AudioNode> { self.nodes.iter_mut().find(|e| { e.id == id }) } + pub fn get_node_by_id(&self, id: u64) -> Option<&AudioNode> { + self.nodes.iter().find(|e| { e.id == id }) + } + // get the id of all nodes in a given playlist track pub fn get_node_ids_in_playlist_track( &mut self, @@ -830,11 +858,11 @@ impl AudioGraph<'static> { self.tempo, subdivision); - let nearest_offset_millis = util::math::round_to_nearest_unsigned_multiple( - offset.as_millis(), - interval.as_millis()); + let nearest_offset_micros = util::math::round_to_nearest_multiple( + offset.as_micros(), + interval.as_micros()); - Duration::from_millis(nearest_offset_millis.try_into().unwrap()) + Duration::from_nanos(nearest_offset_micros.try_into().unwrap()) } // return the unique audio tracks numbers in the graph @@ -850,8 +878,20 @@ impl AudioGraph<'static> { counted } + // count number of tracks + pub fn track_count(&self) -> u32 { + match self.track_numbers().len() { + x if x > 1 => { + x as u32 + } + _ => { + daw::defaults::NUM_OF_TRACKS + } + } + } + // get a vector of AudioNodes with the specified track number - pub fn track(&self, track_number: u32) -> Vec<&AudioNode> { + pub fn get_track_by_track_number(&self, track_number: u32) -> Vec<&AudioNode> { let mut track_nodes: Vec<&AudioNode> = Vec::new(); for node in &self.nodes { @@ -868,7 +908,7 @@ impl AudioGraph<'static> { let mut tracks: Vec> = Vec::new(); for track_num in self.track_numbers() { - let track = self.track(track_num); + let track = self.get_track_by_track_number(track_num); tracks.push(track) } @@ -880,7 +920,7 @@ impl AudioGraph<'static> { #[cfg(test)] mod tests { use super::*; - use crate::daw; + use crate::daw::{self}; use futures_test::{self}; #[test] @@ -976,7 +1016,7 @@ mod tests { assert_ne!(now_elapsed.as_millis(), later.elapsed().as_millis()); let diff = now_elapsed - runtime; - let result = diff.as_nanos() >= 0; + let result = diff.as_millis() < 10; assert_eq!(result, true); } @@ -1068,7 +1108,7 @@ mod tests { #[futures_test::test] async fn test_audiograph_node_functions() { - let id = 1; + let id = 100; let sample_path = daw::METRONOME_TICK_PATH.to_string(); let start_offset = Duration::from_millis(350); let sample_rate = 44_100; @@ -1087,45 +1127,46 @@ mod tests { let buf = node.buffer.clone(); audiograph.add_node(node); - audiograph.construct_and_add_node( + let id2 = audiograph.construct_and_add_node( "assets/assets_66-bd-01.wav".to_string(), Duration::from_millis(100), 1); - audiograph.construct_and_add_node_with_snap( + let id3 = audiograph.construct_and_add_node_with_snap( "assets/assets_66-sd-01.wav".to_string(), Duration::from_millis(600), - 1, + 2, daw::timing::QuarterNote::new()); assert_eq!(audiograph.len(), 3); + assert_eq!(audiograph.track_count(), 3); + assert_eq!(audiograph.get_node_by_id(id2).unwrap().start_offset.as_millis(), 100); + assert_eq!(audiograph.get_node_by_id(id3).unwrap().start_offset.as_millis(), 0); - let target_id = audiograph.get_mut_node(1).unwrap().id; + let target = audiograph.get_node_by_id(id).unwrap(); + + assert_eq!(target.id, id); + assert_eq!(target.track_number, 0); + assert_eq!(target.start_offset.as_millis(), start_offset.as_millis()); + audiograph.move_node( - target_id, + id, Duration::from_millis(0), 2); let target = &audiograph.nodes[0]; - assert_eq!(target.id, target_id); + assert_eq!(target.id, id); assert_eq!(target.track_number, 2); assert_eq!(target.start_offset.as_millis(), 0); - let runtime = Duration::from_secs(1); - - let (_controller, mixer) = audiograph.buffer_slice(runtime).unwrap(); - - let mut nonzero_samples = Vec::::new(); - mixer.for_each(|x| { - if x != 0f32 { - nonzero_samples.push(x); - } - }); + audiograph.move_node_with_snap( + id, + Duration::from_millis(12), + 1, + daw::timing::HalfNote::new()); + let target = &audiograph.nodes[0]; - let mut node_samples = Vec::::new(); - buf.convert_samples().for_each(|x| { - if x != 0f32 { - node_samples.push(x); - } - }); + assert_eq!(target.id, id); + assert_eq!(target.track_number, 1); + assert_eq!(target.start_offset.as_millis(), 0); } } diff --git a/engine/src/main.rs b/engine/src/main.rs index 421c5b7..f04ecf8 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -336,7 +336,7 @@ fn get_node_data( .audiograph .lock() .unwrap(); - let node = audiograph.get_mut_node(id).unwrap(); + let node = audiograph.get_mut_node_by_id(id).unwrap(); let waveform = node.get_waveform().clone(); let dur = node.duration().as_secs_f32(); let ratio = dur / audiograph.max_beats() as f32; @@ -392,14 +392,7 @@ fn get_playlist_data( .lock() .unwrap() .max_beats_displayed; - let track_count = match audiograph.tracks() { - x if x.len() > 1 => { - x.len() as u32 - } - _ => { - daw::defaults::NUM_OF_TRACKS - } - }; + let track_count = audiograph.track_count(); let max_playlist_duration = audiograph .duration_max() .as_secs_f32(); diff --git a/engine/src/util/math.rs b/engine/src/util/math.rs index 4dc7f74..6c23c5e 100644 --- a/engine/src/util/math.rs +++ b/engine/src/util/math.rs @@ -17,7 +17,8 @@ use num_traits::{ PrimInt, Signed, Unsigned, - Float, ToPrimitive, + Float, + ToPrimitive, }; use rodio::cpal::Sample; use std::iter::{ @@ -270,83 +271,32 @@ pub fn interpolate_to< Some((xns, yns)) } -pub fn round_to_nearest_signed_multiple< +pub fn round_to_nearest_multiple< T: + // PrimInt + Num + NumOps + - Signed + - PartialOrd + - From + - Copy, - U: - PrimInt + - Unsigned ->( - n: T, - multiple: U, -) -> T { - let remainder = n.abs() % multiple.into(); - let ceil = n + multiple.into() - remainder; - let floor = n - multiple.into() + remainder; - - if multiple.is_zero() { - return n; - } - - if remainder.is_zero() { - return n; - } - - // if n.is_negative() { - // return -(n.abs() - remainder); - // } - - let ceil_diff = (n - ceil).abs(); - let floor_diff = (n - floor).abs(); - - if ceil_diff <= floor_diff { ceil } else { floor } -} - -pub fn round_to_nearest_unsigned_multiple< - T: - PrimInt + - Num + - NumOps + - Unsigned + PartialOrd + From + + From + + Into + Copy + std::fmt::Display, U: PrimInt + - Unsigned + + Num + + NumOps + + PartialOrd + From + + Into + + Copy + std::fmt::Display >( n: T, multiple: U, ) -> T { - println!("rounding to nearest unsigned mult: {}, {}", n, multiple); - let remainder = n % multiple.into(); - let mult: T = multiple.into(); - println!("calculatd remainder: {}", remainder); - let ceil = n + multiple.into() - remainder; - let floor = n - multiple.into() + (mult - remainder); - - if multiple.is_zero() { - return n; - } - - if remainder.is_zero() { - return n; - } - - let ceil_diff = (n + n) - ceil; - let floor_diff = (n + n) - floor; - - println!("ceil: {}, floor: {}", ceil, floor); - - if ceil_diff <= floor_diff { ceil } else { floor } + let denom: u8 = 2; + ((n + multiple.into() / denom.into()) / multiple.into()) * multiple.into() } pub fn vec_itof32 < @@ -541,3 +491,79 @@ pub fn sample_to_n_elements< _ => None } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_round_to_nearest_multiple() { + let val: u32 = round_to_nearest_multiple(12 as u32, 5 as u32); + assert_eq!(val, 10); + + let val: u32 = round_to_nearest_multiple(13 as u32, 5 as u32); + assert_eq!(val, 15); + + let val: u32 = round_to_nearest_multiple(149 as u32, 10 as u32); + assert_eq!(val, 150); + + let val: i32 = round_to_nearest_multiple::(201, 10); + assert_eq!(val, 200); + + let val: i32 = round_to_nearest_multiple::(143, 12); + assert_eq!(val, 144); + + // todo: make multiple unsigned + let val: i32 = round_to_nearest_multiple::(-1001, -100); + assert_eq!(val, -1000); + + let val: u8 = round_to_nearest_multiple::(127, 8); + assert_eq!(val, 128); + + let val: i16 = round_to_nearest_multiple::(127, 6); + assert_eq!(val, 126); + + // // todo: handle floats + // let val: f32 = round_to_nearest_multiple::(99.9, 10); + // assert_eq!(val, 126); + } + + #[test] + fn test_gaussian_1d() { + let xs: Vec = vec![12.4, -3.321, 3.143222, 593.098]; + let radius = 1.; + + let gauss = gaussian_1d(&xs, radius, false); + assert_eq!(gauss.unwrap(), [0.019367829, -0.0051928335, 0.004914897, 0.06218239]); + + let radius = 0.001; + let gauss = gaussian_1d(&xs, radius, false); + assert_eq!(gauss.unwrap(), [0.0, -8.281204e-37, 5.3503775e-33, 0.0]); + } + + #[test] + fn test_sample_to_n_elements() { + let xs: Vec = vec![4.32, 522.345, -9.284, 1234.542, 1.023, -2.003, 3.1423, 2.]; + + let interp = sample_to_n_elements(&xs, 4); + assert_eq!(interp.unwrap().len(), 4); + + let interp = sample_to_n_elements(&xs, 3); + assert_eq!(interp.unwrap().len(), 3); + } + + #[test] + fn test_f_normalize() { + let xs: Vec = vec![4.32, 522.345, -9.284, 1234.542, 1.023, -2.003, 3.1423, 2.]; + let normalized = f_normalize(xs); + + let mut greater_than_one = Vec::::new(); + for x in normalized { + if x.abs() > 1. { + greater_than_one.push(x); + } + } + + assert_eq!(greater_than_one.len(), 0); + } +}