ZJ2DSL63RVPSNXIGTAZNPV6HLHOKLWC5RSK5EAAM3HIYUJ5Y3ISQC 7VX7JIJXYJBJOQDEXF66ZKSVGHFTNHN2DD2B367EA5OLKMEOZ5NQC OPLLJ6B6WHLEVGORJOLNZGJQZUW3RZY6TPPEJQLXFXLT5ZQTQP2QC LJJL6IBEQCCIVNJHDMXA7HBVI3CVROFUSFTXB3CSARCMJFM25Z5AC 7SXAXY6J4QHPDUUHCKZDEZOZ2IUMSBWODICOMIZH2KA2BGV3F54QC ASIORORAPGGVNLR42J6PZRTG7JKK34GYXBYSKSTJFXSFW6YXBJJQC 7FTW5AQKOO4W5AIHYZSAKLQR6LGVTXH7SKMSTA4ER4C4HP54VE4QC KZU3YXA22IYIPZICQTMZ4XILKCWOI5O6W4H72PDXDUTZDFVMVGVQC 66PNECCRWS2BPZS555P2355EY2PAHZWHJJEXTDU2WUBOHANRNXFQC 5Y32O2B2GTH2UHFA3NO4ZY673XNHT3W4DQADFMK4LMKLSXMZKHGQC DBHCCY3W2DG4WYMXBZ2UCNYVGZ5PS5UUUNWCXVYJ4TMXQDIZT6GAC ZQD7OQ6OQB2E6CPKKT5MILYDWSR2QDNYDPCUTG3DXGY25H6ANHZQC CAN3DHITMPDA2DJC63QMMPMIC63FMWCUAI4ZLKPLEHIYG2EWIAHAC HYU2P2RSYVVYNDSFXTOYCDOQHGHQGIMY6SFKT3SQDOTMOUNQFNFQC fundsp_output.play(&fundsp_kira::Machine::new(pink() * 0.2 * envelope(|t| exp(-0.1 * t)),// pink() * 0.5 * envelope(|t| exp(-10.0 * t)),// pink() * 0.2,));
fundsp_output.play(&fundsp_kira::Machine::new(pink() * 0.2 * envelope(|t| exp(-0.1 * t)),// pink() * 0.5 * envelope(|t| exp(-10.0 * t)),// pink() * 0.2,),&mut track,);
ui.collapsing("Audio Details", |ui| {// TODO Abstract this away into an egui widget for an *oscilloscope*use egui_plot::{Line, Plot, PlotPoints};let left_max = sample_pairs.iter().map(|(s, _)| s.abs()).max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)).unwrap_or_default();let left_wave: PlotPoints = sample_pairs.iter().enumerate().map(|(t, (s, _))| [t as f64, *s as f64]).chain((samples_stored..SAMPLES_TO_KEEP).map(|i| [i as f64, 0.0])).collect();let left_line = Line::new(left_wave).color(egui::Color32::BLUE);let right_max = sample_pairs.iter().map(|(_, s)| s.abs()).max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)).unwrap_or_default();let right_wave: PlotPoints = sample_pairs.iter().enumerate().map(|(t, (_, s))| [t as f64, *s as f64]).chain((samples_stored..SAMPLES_TO_KEEP).map(|i| [i as f64, 0.0])).collect();let right_line = Line::new(right_wave).color(egui::Color32::RED);ui.label("Main Track Oscilloscope");Plot::new("waveform").view_aspect(4.0).auto_bounds(egui::Vec2b::new(false, false)).show_grid(egui::Vec2b::new(false, true)).include_y(-left_max.max(right_max)).include_y(left_max.max(right_max)).include_x(0.0).include_x(SAMPLES_TO_KEEP as f64).show_axes(false).show(ui, |plot_ui| {plot_ui.line(left_line);plot_ui.line(right_line);});ui.label(format!("MainTrack RMS: {:?}", track.rms()));});
}}}pub trait FundspAudioApp {fn add_track<T: Resource>(&mut self) -> &mut Self;}impl FundspAudioApp for App {fn add_track<T: Resource>(&mut self) -> &mut Self {self.insert_resource(Track::<T>::default())}}#[derive(Debug, Clone)]struct ChannelDetails {rms: Arc<AtomicU32>,sample: Arc<AtomicU32>,}impl ChannelDetails {fn store_sample(&self, sample: f32) {self.sample.store(sample.to_bits(), Ordering::SeqCst);}fn store_rms(&self, rms: f32) {self.rms.store(rms.to_bits(), Ordering::SeqCst);}fn sample(&self) -> f32 {let sample_bits = self.sample.load(Ordering::SeqCst);f32::from_bits(sample_bits)}fn rms(&self) -> f32 {let rms_bits = self.rms.load(Ordering::SeqCst);f32::from_bits(rms_bits)}}impl Default for ChannelDetails {fn default() -> Self {Self {rms: Arc::new(AtomicU32::new(0.0f32.to_bits())),sample: Arc::new(AtomicU32::new(0.0f32.to_bits())),}}}#[derive(Debug)]struct Trackable<T> {should_unload: Arc<AtomicBool>,left_channel: ChannelDetails,right_channel: ChannelDetails,_marker: PhantomData<T>,}impl<T> Clone for Trackable<T>whereT: Resource,{fn clone(&self) -> Self {Self {should_unload: self.should_unload.clone(),left_channel: self.left_channel.clone(),right_channel: self.right_channel.clone(),_marker: PhantomData,}}}impl<T> Default for Trackable<T> {fn default() -> Self {Self {_marker: PhantomData,should_unload: Arc::new(AtomicBool::new(false)),left_channel: default(),right_channel: default(),}}}impl<T> Trackable<T> {/// Stops any sounds associated with this [`Trackable`]fn unload(&self) {self.should_unload.store(true, Ordering::SeqCst)}}#[derive(Resource)]pub struct Track<T> {trackable: Trackable<T>,sample_rate: f64,}impl<T> Default for Track<T> {fn default() -> Self {Self {trackable: Trackable::default(),sample_rate: DEFAULT_SR,
#[allow(dead_code)]/// Get this track's sample mutablypub fn sample_rate_mut(&mut self) -> &mut f64 {&mut self.sample_rate}#[allow(dead_code)]/// Get the most recent sample valuespub fn sample(&self) -> (f32, f32) {(self.trackable.left_channel.sample(),self.trackable.right_channel.sample(),)}#[allow(dead_code)]/// Get the most recent RMS valuespub fn rms(&self) -> (f32, f32) {(self.trackable.left_channel.rms(),self.trackable.right_channel.rms(),)}}
fn new(node: T, settings: MachinedSettings) -> Self {Self { node, settings }
fn new(mut node: N, settings: MachinedSettings, track: &Track<T>) -> Self {// Set the node sample rate from the tracknode.set_sample_rate(track.sample_rate);Self {node,settings,trackable: track.trackable.clone(),}