use ballast_core::{Engine, Grid, Segment, Settings, Source}; use std::{ alloc::{GlobalAlloc, Layout, System}, cell::Cell, }; struct CheckedAllocator; thread_local! { static CHECK:Cell=const {Cell::new(false)}; static OPERATIONS:Cell=const {Cell::new(0)}; } fn record() { if CHECK.try_with(Cell::get).unwrap_or(false) { let _ = OPERATIONS.try_with(|n| n.set(n.get() + 1)); } } unsafe impl GlobalAlloc for CheckedAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { record(); unsafe { System.alloc(layout) } } unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) { record(); unsafe { System.dealloc(pointer, layout) } } unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, size: usize) -> *mut u8 { record(); unsafe { System.realloc(pointer, layout, size) } } } #[global_allocator] static ALLOCATOR: CheckedAllocator = CheckedAllocator; #[test] fn processing_notes_settings_transport_and_analysis_do_not_allocate_or_free() { let mut engine = Engine::new(48000., 2).unwrap(); let mut settings = Settings::default(); engine.set_settings(settings).unwrap(); CHECK.with(|v| v.set(true)); for i in 0..96000 { if i % 512 == 0 { let playing = (i / 24000) % 2 == 0; engine.set_transport( Some(140.), playing.then_some(i as f64 / 48000. * 140. / 60.), ); } if i % 4800 == 0 { let step = i / 4800; settings.segment = if step % 2 == 0 { Segment::Grid } else { Segment::Notes }; settings.grid = Grid::ALL[step % Grid::ALL.len()]; settings.source = if step % 4 < 2 { Source::Audio } else { Source::Midi }; settings.from = 20. + 30. * (step % 5) as f32; settings.bypass = step % 7 == 6; engine.set_settings(settings).unwrap(); engine.note_on(33 + (step % 12) as u8); if step % 3 == 0 { engine.learn(); } } if i % 4800 == 2400 { engine.note_off(33 + (i / 4800 % 12) as u8); } let t = i as f32 / 48000.; let x = if (i / 2400) % 2 == 0 { (std::f32::consts::TAU * 55. * t).sin() * 0.5 } else { 0. }; std::hint::black_box(engine.process_sample([x, -x])); if i % 1600 == 0 { std::hint::black_box(engine.take_analyzer()); } } engine.reset(); CHECK.with(|v| v.set(false)); assert_eq!(OPERATIONS.with(Cell::get), 0); }