diff --git a/engine/Cargo.lock b/engine/Cargo.lock index c6890260..4329c415 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -233,6 +233,7 @@ dependencies = [ name = "event_scheduler" version = "0.1.0" dependencies = [ + "common", "float-ord", "heapless", ] @@ -510,6 +511,7 @@ dependencies = [ name = "midi_quantizer" version = "0.1.0" dependencies = [ + "common", "dsp", ] @@ -546,6 +548,7 @@ dependencies = [ name = "multiband_diode_ladder_distortion" version = "0.1.0" dependencies = [ + "common", "dsp", ] @@ -634,6 +637,7 @@ name = "oscilloscope" version = "0.1.0" dependencies = [ "canvas_utils", + "common", "dsp", ] @@ -760,6 +764,7 @@ dependencies = [ name = "quantizer" version = "0.1.0" dependencies = [ + "common", "dsp", ] diff --git a/engine/common/src/lib.rs b/engine/common/src/lib.rs index 5c2fae8d..6d7685bd 100644 --- a/engine/common/src/lib.rs +++ b/engine/common/src/lib.rs @@ -16,7 +16,7 @@ static mut RNG: Pcg32 = unsafe { std::mem::transmute([5573589319906701683u64, 1442695040888963407u64]) }; #[inline(always)] -pub fn rng() -> &'static mut Pcg32 { unsafe { &mut RNG } } +pub fn rng() -> &'static mut Pcg32 { ref_static_mut!(RNG) } pub fn uuid_v4() -> Uuid { let entropy: (u64, i64) = rng().gen(); @@ -34,3 +34,12 @@ pub fn set_raw_panic_hook(log_err: unsafe extern "C" fn(ptr: *const u8, len: usi std::panic::set_hook(Box::new(hook)) } + +/// Implements `&mut *std::ptr::addr_of_mut!(x)` to work around the annoying new Rust rules on +/// referencing static muts +#[macro_export] +macro_rules! ref_static_mut { + ($x:expr) => { + unsafe { &mut *std::ptr::addr_of_mut!($x) } + }; +} diff --git a/engine/compressor/src/lib.rs b/engine/compressor/src/lib.rs index e20993b0..9920a770 100644 --- a/engine/compressor/src/lib.rs +++ b/engine/compressor/src/lib.rs @@ -197,7 +197,7 @@ fn detect_level_peak( buf: &CircularBuffer, lookahead_samples: isize, sample_ix_in_frame: usize, - old_max: f32, + _old_max: f32, ) -> f32 { // Try to fast-path. If the old max hasn't been removed from the lookahead buffer yet and it's // still the max, then we can just return it. @@ -278,7 +278,7 @@ impl Compressor { top_threshold_db: f32, bottom_ratio: f32, top_ratio: f32, - knee: f32, + _knee: f32, sensing_method: SensingMethod, post_gain: f32, ) -> f32 { diff --git a/engine/event_scheduler/Cargo.toml b/engine/event_scheduler/Cargo.toml index 62c2c253..586b9bb9 100644 --- a/engine/event_scheduler/Cargo.toml +++ b/engine/event_scheduler/Cargo.toml @@ -10,3 +10,4 @@ crate-type = ["cdylib", "rlib"] [dependencies] heapless = { version = "0.7", default-features = false } float-ord = "0.3" +common = { path = "../common" } diff --git a/engine/event_scheduler/src/lib.rs b/engine/event_scheduler/src/lib.rs index b9c026fb..c14b8963 100644 --- a/engine/event_scheduler/src/lib.rs +++ b/engine/event_scheduler/src/lib.rs @@ -1,5 +1,6 @@ use std::cmp::Reverse; +use common::ref_static_mut; use float_ord::FloatOrd; use heapless::binary_heap::{BinaryHeap, Min}; @@ -127,7 +128,7 @@ fn handle_event(evt: ScheduledEvent) { #[no_mangle] pub extern "C" fn run(raw_cur_time: f64, cur_beats: f64) { - let scheduled_events = unsafe { &mut SCHEDULED_EVENTS }; + let scheduled_events = ref_static_mut!(SCHEDULED_EVENTS); loop { match scheduled_events.peek() { None => break, @@ -139,7 +140,7 @@ pub extern "C" fn run(raw_cur_time: f64, cur_beats: f64) { handle_event(evt); } - let scheduled_beat_events = unsafe { &mut SCHEDULED_BEAT_EVENTS }; + let scheduled_beat_events = ref_static_mut!(SCHEDULED_BEAT_EVENTS); loop { match scheduled_beat_events.peek() { None => break, @@ -172,8 +173,8 @@ pub unsafe extern "C" fn alloc_ids_buffer(count: usize) -> *mut i32 { #[no_mangle] pub extern "C" fn cancel_events_by_ids() -> usize { let ids = unsafe { &*IDS_BUFFER }.as_slice(); - let scheduled_events = unsafe { &mut SCHEDULED_EVENTS }; - let scheduled_beat_events = unsafe { &mut SCHEDULED_BEAT_EVENTS }; + let scheduled_events = ref_static_mut!(SCHEDULED_EVENTS); + let scheduled_beat_events = ref_static_mut!(SCHEDULED_BEAT_EVENTS); let mut actually_cancelled_evt_count = 0; diff --git a/engine/granular/src/lib.rs b/engine/granular/src/lib.rs index af5f2391..c9f93e21 100644 --- a/engine/granular/src/lib.rs +++ b/engine/granular/src/lib.rs @@ -5,6 +5,7 @@ //! Several aspects of this design draw significant inspiration from the Clouds eurorack module made //! by Mutable Instruments and associated code which is available on Github: https://github.com/pichenettes/eurorack +use common::ref_static_mut; use dsp::{clamp, filters::butterworth::ButterworthFilter, mix, read_interpolated, smooth}; use rand::prelude::*; @@ -28,7 +29,7 @@ impl Default for ReverseState { } static mut SCRATCH: [(f32, f32); 8192] = [(0.0, 0.0); 8192]; -fn scratch() -> &'static mut [(f32, f32); 8192] { unsafe { &mut SCRATCH } } +fn scratch() -> &'static mut [(f32, f32); 8192] { ref_static_mut!(SCRATCH) } #[derive(Clone)] pub struct GranularVoice { diff --git a/engine/midi_quantizer/Cargo.toml b/engine/midi_quantizer/Cargo.toml index 79b9070a..f20a151b 100644 --- a/engine/midi_quantizer/Cargo.toml +++ b/engine/midi_quantizer/Cargo.toml @@ -9,3 +9,4 @@ crate-type = ["cdylib", "rlib"] [dependencies] dsp = { path = "../dsp" } +common = { path = "../common" } diff --git a/engine/midi_quantizer/src/lib.rs b/engine/midi_quantizer/src/lib.rs index 6118c9c8..4244effe 100644 --- a/engine/midi_quantizer/src/lib.rs +++ b/engine/midi_quantizer/src/lib.rs @@ -1,3 +1,5 @@ +use common::ref_static_mut; + extern "C" { fn play_note(note: usize); @@ -37,7 +39,7 @@ impl MIDIQuantizerState { } static mut STATE: MIDIQuantizerState = MIDIQuantizerState::default(); -fn state() -> &'static mut MIDIQuantizerState { unsafe { &mut STATE } } +fn state() -> &'static mut MIDIQuantizerState { ref_static_mut!(STATE) } #[no_mangle] pub extern "C" fn set_octave_range(low: isize, high: isize) { diff --git a/engine/multiband_diode_ladder_distortion/Cargo.toml b/engine/multiband_diode_ladder_distortion/Cargo.toml index 5251b6e5..78b712c2 100644 --- a/engine/multiband_diode_ladder_distortion/Cargo.toml +++ b/engine/multiband_diode_ladder_distortion/Cargo.toml @@ -9,3 +9,4 @@ crate-type = ["cdylib", "rlib"] [dependencies] dsp = { path = "../dsp" } +common = { path = "../common" } diff --git a/engine/multiband_diode_ladder_distortion/src/lib.rs b/engine/multiband_diode_ladder_distortion/src/lib.rs index 36c2d91c..03cd7962 100644 --- a/engine/multiband_diode_ladder_distortion/src/lib.rs +++ b/engine/multiband_diode_ladder_distortion/src/lib.rs @@ -1,3 +1,4 @@ +use common::ref_static_mut; use dsp::{band_splitter::BandSplitter, FRAME_SIZE}; static mut INPUT_BUFFER: [f32; FRAME_SIZE] = [0.0; FRAME_SIZE]; @@ -15,33 +16,32 @@ pub extern "C" fn init() { #[no_mangle] pub extern "C" fn get_input_buf_ptr() -> *mut f32 { - unsafe { &mut INPUT_BUFFER as *mut [f32; FRAME_SIZE] as *mut f32 } + unsafe { std::ptr::addr_of_mut!(INPUT_BUFFER) as *mut f32 } } #[no_mangle] pub extern "C" fn get_low_output_buf_ptr() -> *mut f32 { - unsafe { &mut LOW_BAND_OUTPUT_BUFFER as *mut [f32; FRAME_SIZE] as *mut f32 } + unsafe { std::ptr::addr_of_mut!(LOW_BAND_OUTPUT_BUFFER) as *mut f32 } } #[no_mangle] pub extern "C" fn get_mid_output_buf_ptr() -> *mut f32 { - unsafe { &mut MID_BAND_OUTPUT_BUFFER as *mut [f32; FRAME_SIZE] as *mut f32 } + unsafe { std::ptr::addr_of_mut!(MID_BAND_OUTPUT_BUFFER) as *mut f32 } } #[no_mangle] pub extern "C" fn get_high_output_buf_ptr() -> *mut f32 { - unsafe { &mut HIGH_BAND_OUTPUT_BUFFER as *mut [f32; FRAME_SIZE] as *mut f32 } + unsafe { std::ptr::addr_of_mut!(HIGH_BAND_OUTPUT_BUFFER) as *mut f32 } } #[no_mangle] pub extern "C" fn process() { let splitter = unsafe { &mut *BAND_SPLITTER }; - unsafe { - splitter.apply_frame( - &INPUT_BUFFER, - &mut LOW_BAND_OUTPUT_BUFFER, - &mut MID_BAND_OUTPUT_BUFFER, - &mut HIGH_BAND_OUTPUT_BUFFER, - ); - } + + splitter.apply_frame( + ref_static_mut!(INPUT_BUFFER), + ref_static_mut!(LOW_BAND_OUTPUT_BUFFER), + ref_static_mut!(MID_BAND_OUTPUT_BUFFER), + ref_static_mut!(HIGH_BAND_OUTPUT_BUFFER), + ); } diff --git a/engine/noise_gen/src/lib.rs b/engine/noise_gen/src/lib.rs index ba951759..53d33b17 100644 --- a/engine/noise_gen/src/lib.rs +++ b/engine/noise_gen/src/lib.rs @@ -1,4 +1,4 @@ -use common::rng; +use common::{ref_static_mut, rng}; use rand::prelude::*; #[derive(Clone, Copy)] @@ -87,14 +87,18 @@ pub unsafe extern "C" fn generate() -> *const f32 { NoiseType::Pink => todo!(), NoiseType::Brown => todo!(), }; - for out in &mut OUTPUT { + for out in ref_static_mut!(OUTPUT) { let sample = generator() * GAIN; if QUANTIZATION_FACTOR > 0 { LAST_VAL = dsp::quantize(-1., 1., QUANTIZATION_FACTOR as f32, sample); } else { if SMOOTHING_COEFFICIENT != 0. { - dsp::one_pole(&mut LAST_VAL, sample, 1. - SMOOTHING_COEFFICIENT); + dsp::one_pole( + ref_static_mut!(LAST_VAL), + sample, + 1. - SMOOTHING_COEFFICIENT, + ); } else { LAST_VAL = sample; } diff --git a/engine/oscilloscope/Cargo.toml b/engine/oscilloscope/Cargo.toml index 3ada9c69..87babc38 100644 --- a/engine/oscilloscope/Cargo.toml +++ b/engine/oscilloscope/Cargo.toml @@ -10,3 +10,4 @@ crate-type = ["cdylib", "rlib"] [dependencies] dsp = { path = "../dsp" } canvas_utils = { path = "../canvas_utils" } +common = { path = "../common" } diff --git a/engine/oscilloscope/src/lib.rs b/engine/oscilloscope/src/lib.rs index 01cce0a7..9ee632c8 100644 --- a/engine/oscilloscope/src/lib.rs +++ b/engine/oscilloscope/src/lib.rs @@ -1,5 +1,6 @@ use self::oscilloscope::{PreviousWindow, Viz, WindowLength}; use canvas_utils::VizView; +use common::ref_static_mut; use f0_estimation::YinCtx; pub(crate) mod conf; @@ -84,13 +85,13 @@ pub extern "C" fn oscilloscope_renderer_set_view( ) { maybe_set_panic_hook(); - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.set_view(cur_bpm, VizView { width, height, dpr }); } #[no_mangle] pub extern "C" fn oscilloscope_renderer_set_window(window_mode: u8, window_length: f32) { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); let window = WindowLength::from_parts(window_mode, window_length); viz.set_window(window); } @@ -98,7 +99,7 @@ pub extern "C" fn oscilloscope_renderer_set_window(window_mode: u8, window_lengt /// Process all samples in `FRAME_DATA_BUFFER` and update the viz #[no_mangle] pub extern "C" fn oscilloscope_renderer_process(cur_bpm: f32, cur_beat: f32, cur_time: f32) { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.process(cur_bpm, cur_beat, cur_time); } @@ -109,44 +110,44 @@ pub extern "C" fn oscilloscope_renderer_get_frame_data_ptr() -> *const f32 { #[no_mangle] pub extern "C" fn oscilloscope_renderer_commit_samples() { - let viz = unsafe { &mut VIZ }; - let frame_data = unsafe { &FRAME_DATA_BUFFER }; + let viz = ref_static_mut!(VIZ); + let frame_data = ref_static_mut!(FRAME_DATA_BUFFER); viz.commit_samples(frame_data); } #[no_mangle] pub extern "C" fn oscilloscope_get_image_data_buf_ptr() -> *const u8 { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.get_image_data().as_ptr() } #[no_mangle] pub extern "C" fn oscilloscope_get_image_data_buf_len() -> usize { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.get_image_data().len() } #[no_mangle] pub extern "C" fn oscilloscope_renderer_set_frozen(frozen: bool) { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.set_frozen(frozen); } #[no_mangle] pub extern "C" fn oscilloscope_renderer_set_frame_by_frame(frame_by_frame: bool) { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.set_frame_by_frame(frame_by_frame); } /// Returns pointer to zero-terminated string #[no_mangle] pub extern "C" fn oscilloscope_renderer_get_detected_f0_display() -> *const u8 { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.yin_ctx.get_detected_f0_display() } #[no_mangle] pub extern "C" fn oscilloscope_renderer_set_snap_f0_to_midi(snap_f0_to_midi: bool) { - let viz = unsafe { &mut VIZ }; + let viz = ref_static_mut!(VIZ); viz.snap_f0_to_midi = snap_f0_to_midi; } diff --git a/engine/polysynth/src/lib.rs b/engine/polysynth/src/lib.rs index 857af2df..0cde3c02 100644 --- a/engine/polysynth/src/lib.rs +++ b/engine/polysynth/src/lib.rs @@ -1,13 +1,12 @@ //! Synth state management. Handles keeping track of what each voice of each polyphonic synth //! is playing and passing the correct commands through to the WebAudio synths. -#[cfg(feature = "wasm-bindgen")] -#[macro_use] -extern crate wasm_bindgen; #[cfg(feature = "wasm-bindgen")] extern crate common; #[cfg(feature = "wasm-bindgen")] extern crate js_sys; +#[cfg(feature = "wasm-bindgen")] +extern crate wasm_bindgen; #[macro_use] extern crate log; diff --git a/engine/quantizer/Cargo.toml b/engine/quantizer/Cargo.toml index cec91a52..b3f1d038 100644 --- a/engine/quantizer/Cargo.toml +++ b/engine/quantizer/Cargo.toml @@ -9,3 +9,4 @@ crate-type = ["cdylib", "rlib"] [dependencies] dsp = { path = "../dsp" } +common = { path = "../common" } diff --git a/engine/quantizer/src/lib.rs b/engine/quantizer/src/lib.rs index f67b9c50..597ecb7c 100644 --- a/engine/quantizer/src/lib.rs +++ b/engine/quantizer/src/lib.rs @@ -1,3 +1,4 @@ +use common::ref_static_mut; use dsp::FRAME_SIZE; #[repr(u8)] @@ -53,7 +54,7 @@ static mut STATE: QuantizerState = QuantizerState { quantization_interval: 1., mode: QuantizationMode::Round, }; -fn state() -> &'static mut QuantizerState { unsafe { &mut STATE } } +fn state() -> &'static mut QuantizerState { ref_static_mut!(STATE) } #[no_mangle] pub extern "C" fn set_quantization_state(quantization_interval: f32, mode: u8) { @@ -63,7 +64,7 @@ pub extern "C" fn set_quantization_state(quantization_interval: f32, mode: u8) { } static mut IO_BUFFER: [f32; FRAME_SIZE] = [0.; FRAME_SIZE]; -fn io_buf() -> &'static mut [f32; FRAME_SIZE] { unsafe { &mut IO_BUFFER } } +fn io_buf() -> &'static mut [f32; FRAME_SIZE] { ref_static_mut!(IO_BUFFER) } #[no_mangle] pub extern "C" fn get_io_buf_ptr() -> *mut f32 { io_buf().as_mut_ptr() } diff --git a/engine/wavegen/src/bindings.rs b/engine/wavegen/src/bindings.rs index 9ab99a83..78e90bfb 100644 --- a/engine/wavegen/src/bindings.rs +++ b/engine/wavegen/src/bindings.rs @@ -1,5 +1,6 @@ use std::f32::consts::PI; +use common::ref_static_mut; use waveform_renderer::WaveformRendererCtx; extern "C" { @@ -81,7 +82,7 @@ fn get_waveform_renderer_ctx() -> &'static mut WaveformRendererCtx { pub extern "C" fn wavegen_render_waveform() -> *const u8 { common::set_raw_panic_hook(log_err); - let state = unsafe { &mut ENCODED_STATE_BUF }; + let state = ref_static_mut!(ENCODED_STATE_BUF); let magnitudes = &mut state[..HARMONIC_COUNT]; // normalize magnitudes let max_magnitude = magnitudes.iter().fold(0.0f32, |acc, x| acc.max(*x)); diff --git a/engine/wavetable/src/fm/filter/mod.rs b/engine/wavetable/src/fm/filter/mod.rs index 60ac3d6b..159f4872 100644 --- a/engine/wavetable/src/fm/filter/mod.rs +++ b/engine/wavetable/src/fm/filter/mod.rs @@ -223,7 +223,6 @@ pub(crate) struct FilterModule { pub filter_state: FilterState, pub q: ParamSource, pub cutoff_freq: ParamSource, - pub last_cutoff_freq: f32, pub gain: ParamSource, pub rendered_q: [f32; FRAME_SIZE], pub rendered_cutoff_freq: [f32; FRAME_SIZE], @@ -238,7 +237,6 @@ impl Default for FilterModule { filter_state: FilterState::new_simple_biquad(FilterMode::Lowpass), q: ParamSource::new_constant(1.), cutoff_freq: ParamSource::new_constant(440.), - last_cutoff_freq: 440., gain: ParamSource::new_constant(0.), rendered_q: [0.; FRAME_SIZE], rendered_cutoff_freq: [0.; FRAME_SIZE], diff --git a/engine/wavetable/src/lib.rs b/engine/wavetable/src/lib.rs index e32cee81..88490567 100644 --- a/engine/wavetable/src/lib.rs +++ b/engine/wavetable/src/lib.rs @@ -41,7 +41,7 @@ fn mix(mix_factor: f32, low: f32, high: f32) -> f32 { ((1.0 - mix_factor) * low) + (mix_factor * high) } -const SMOOTH_TAIL_LEN_SAMPLES: usize = 16; +// const SMOOTH_TAIL_LEN_SAMPLES: usize = 16; impl WaveTable { pub fn new(settings: WaveTableSettings) -> Self { @@ -95,17 +95,17 @@ impl WaveTable { let base_sample = mix(sample_mix, low_sample, high_sample); // We mix the final `SMOOTH_TAIL_LEN_SAMPLES` samples with the first sample of the waveform // to avoid audio artifacts caused by discontinuities produced by wrapping around - let samples_from_the_end = self.settings.waveform_length - sample_low_ix; + // let samples_from_the_end = self.settings.waveform_length - sample_low_ix; // if samples_from_the_end > SMOOTH_TAIL_LEN_SAMPLES { return base_sample; // } - let first_sample = self.samples[waveform_offset_samples]; - mix( - (SMOOTH_TAIL_LEN_SAMPLES - samples_from_the_end) as f32 / SMOOTH_TAIL_LEN_SAMPLES as f32, - base_sample, - first_sample, - ) + // let first_sample = self.samples[waveform_offset_samples]; + // mix( + // (SMOOTH_TAIL_LEN_SAMPLES - samples_from_the_end) as f32 / SMOOTH_TAIL_LEN_SAMPLES as f32, + // base_sample, + // first_sample, + // ) } fn sample_dimension(&self, dimension_ix: usize, waveform_ix: f32, sample_ix: f32) -> f32 {