#[macro_export]
macro_rules! endless_patch {
($PatchType:ty, $init_expr:expr) => {
struct PatchState {
instance: core::mem::MaybeUninit<$PatchType>,
initialized: bool,
error: bool,
}
#[repr(transparent)]
struct SyncPatchState(core::cell::UnsafeCell<PatchState>);
unsafe impl Sync for SyncPatchState {}
static PATCH_STATE: SyncPatchState = SyncPatchState(core::cell::UnsafeCell::new(PatchState {
instance: core::mem::MaybeUninit::uninit(),
initialized: false,
error: false,
}));
#[inline(always)]
unsafe fn state() -> &'static mut PatchState {
unsafe { &mut *PATCH_STATE.0.get() }
}
#[inline(always)]
unsafe fn patch() -> &'static mut $PatchType {
let s = unsafe { state() };
if !s.initialized {
s.instance.write($init_expr);
s.initialized = true;
}
unsafe { s.instance.assume_init_mut() }
}
#[inline(always)]
unsafe fn param_metadata(idx: i32) -> $crate::ParameterMetadata {
match $crate::ParamId::from_raw(idx) {
Some(param) => $crate::Patch::param_metadata(unsafe { patch() }, param),
None => $crate::ParameterMetadata::default(),
}
}
unsafe extern "C" fn patch_init(env: *const $crate::abi::PatchEnv) {
let supported =
!env.is_null() && unsafe { (*env).abi_version } == u32::from($crate::abi::PATCH_ABI_VERSION);
if !supported {
unsafe { state().error = true };
return;
}
$crate::Patch::init(unsafe { patch() });
}
unsafe extern "C" fn patch_agent_update_buffers(
_env: *const $crate::abi::PatchEnv,
left: *mut f32,
right: *mut f32,
samples: i32,
) -> f32 {
if left.is_null() || right.is_null() {
return 0.0;
}
let n = usize::try_from(samples).unwrap_or(0);
let left = unsafe { core::slice::from_raw_parts_mut(left, n) };
let right = unsafe { core::slice::from_raw_parts_mut(right, n) };
$crate::Patch::process_audio(unsafe { patch() }, left, right)
}
unsafe extern "C" fn patch_agent_set_buffer(_env: *const $crate::abi::PatchEnv, buffer: *mut f32) {
if buffer.is_null() {
unsafe { state().error = true };
return;
}
let buf = unsafe { core::slice::from_raw_parts_mut(buffer, $crate::WORKING_BUFFER_SIZE) };
$crate::Patch::set_working_buffer(unsafe { patch() }, buf);
}
unsafe extern "C" fn patch_agent_get_buffer_size(_env: *const $crate::abi::PatchEnv) -> i32 {
$crate::WORKING_BUFFER_SIZE as i32
}
unsafe extern "C" fn patch_agent_get_param_min(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
unsafe { param_metadata(idx) }.min
}
unsafe extern "C" fn patch_agent_get_param_max(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
unsafe { param_metadata(idx) }.max
}
unsafe extern "C" fn patch_agent_get_param_default(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
unsafe { param_metadata(idx) }.default
}
unsafe extern "C" fn patch_agent_is_param_enabled(
_env: *const $crate::abi::PatchEnv,
idx: i32,
source_id: i32,
) -> u8 {
let is_knob = source_id == 0;
u8::from(is_knob && $crate::ParamId::from_raw(idx).is_some())
}
unsafe extern "C" fn patch_agent_get_param_name(
_env: *const $crate::abi::PatchEnv,
_idx: i32,
out: *mut u8,
buf_size: usize,
) {
if !out.is_null() && buf_size > 0 {
unsafe { *out = 0 };
}
}
unsafe extern "C" fn patch_agent_get_param_unit(
_env: *const $crate::abi::PatchEnv,
_idx: i32,
out: *mut u8,
buf_size: usize,
) {
if !out.is_null() && buf_size > 0 {
unsafe { *out = 0 };
}
}
unsafe extern "C" fn patch_agent_set_param(_env: *const $crate::abi::PatchEnv, idx: i32, value: f32) {
if let Some(param) = $crate::ParamId::from_raw(idx) {
$crate::Patch::set_param(unsafe { patch() }, param, value);
}
}
unsafe extern "C" fn patch_agent_special_action(_env: *const $crate::abi::PatchEnv, idx: i32) {
if let Some(action) = $crate::ActionId::from_raw(idx) {
$crate::Patch::handle_action(unsafe { patch() }, action);
}
}
unsafe extern "C" fn patch_agent_get_state_idx(_env: *const $crate::abi::PatchEnv) -> i32 {
if unsafe { state().error } {
return $crate::Color::Red as i32;
}
$crate::Patch::state_led_color(unsafe { patch() }) as i32
}
core::arch::global_asm!(
".section .patch_header, \"a\"",
".balign 4",
".global patch_header",
"patch_header:",
".word {magic}",
".short {abi_version}",
".short 0",
".word {init}",
".word {update_buffers}",
".word {set_buffer}",
".word {get_buffer_size}",
".word {get_param_min}",
".word {get_param_max}",
".word {get_param_default}",
".word {is_param_enabled}",
".word {get_param_name}",
".word {get_param_unit}",
".word {set_param}",
".word {special_action}",
".word {get_state_idx}",
".word patch_image_size",
".word __patch_bss_start",
".word patch_bss_size",
".fill 16, 4, 0",
magic = const $crate::abi::PATCH_MAGIC,
abi_version = const $crate::abi::PATCH_ABI_VERSION,
init = sym patch_init,
update_buffers = sym patch_agent_update_buffers,
set_buffer = sym patch_agent_set_buffer,
get_buffer_size = sym patch_agent_get_buffer_size,
get_param_min = sym patch_agent_get_param_min,
get_param_max = sym patch_agent_get_param_max,
get_param_default = sym patch_agent_get_param_default,
is_param_enabled = sym patch_agent_is_param_enabled,
get_param_name = sym patch_agent_get_param_name,
get_param_unit = sym patch_agent_get_param_unit,
set_param = sym patch_agent_set_param,
special_action = sym patch_agent_special_action,
get_state_idx = sym patch_agent_get_state_idx,
);
};
}