mod audio; mod gui; use std::{ sync::{atomic::Ordering, Arc}, thread, }; const FLOAT_MAX: f64 = i16::max_value() as f64; fn main() { // Initialize state shared between audio and GUI threads. // The struct itself is Sync and therefore safe to share between // threads, but an Arc pointer is used to workaround Rust's // ownership rules. Calling `clone` on the pointer increments its // internal reference count allowing for safe memory management // between threads. This should not cause a runtime penalty inside // the audio callback. let global_state = Arc::new(audio::AudioState::new()); let audio_state = global_state.clone(); let gui_state = global_state.clone(); // Spawn a thread for the audio engine: thread::spawn(move || { audio::start(&audio_state).unwrap(); }); // Setup GUI: gui::init().unwrap(); let the_gui = gui::Gui::new("VUMeter-Rust"); // Setup a timer on the main thread to poll the audio state and // update the GUI. It takes a second `clone`d copy of the Arc pointer. gui::add_timeout(50, move || { // Atomically swap the `gui_up_to_date` value: let result = gui_state.gui_up_to_date.compare_exchange( false, true, Ordering::SeqCst, Ordering::SeqCst, ); if let Ok(false) = result { // if the maximum value has changed, load it into a local // variable and convert it to range 0..1.0. // Then as we are on the main thread we can update the // GUI safely. let val = gui_state.max_value.load(Ordering::SeqCst); the_gui.set_level((val as f64) / FLOAT_MAX); } }); gui::run(); }