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>
where
T: 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 mutably
pub fn sample_rate_mut(&mut self) -> &mut f64 {
&mut self.sample_rate
}
#[allow(dead_code)]
/// Get the most recent sample values
pub fn sample(&self) -> (f32, f32) {
(
self.trackable.left_channel.sample(),
self.trackable.right_channel.sample(),
)
}
#[allow(dead_code)]
/// Get the most recent RMS values
pub 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 track
node.set_sample_rate(track.sample_rate);
Self {
node,
settings,
trackable: track.trackable.clone(),
}