1#[macro_export]
19macro_rules! endless_patch {
20 ($PatchType:ty, $init_expr:expr) => {
21 struct PatchState {
27 instance: core::mem::MaybeUninit<$PatchType>,
28 initialized: bool,
29 error: bool,
30 }
31
32 #[repr(transparent)]
34 struct SyncPatchState(core::cell::UnsafeCell<PatchState>);
35
36 unsafe impl Sync for SyncPatchState {}
40
41 static PATCH_STATE: SyncPatchState = SyncPatchState(core::cell::UnsafeCell::new(PatchState {
42 instance: core::mem::MaybeUninit::uninit(),
43 initialized: false,
44 error: false,
45 }));
46
47 #[inline(always)]
52 unsafe fn state() -> &'static mut PatchState {
53 unsafe { &mut *PATCH_STATE.0.get() }
54 }
55
56 #[inline(always)]
65 unsafe fn patch() -> &'static mut $PatchType {
66 let s = unsafe { state() };
67 if !s.initialized {
68 s.instance.write($init_expr);
69 s.initialized = true;
70 }
71 unsafe { s.instance.assume_init_mut() }
72 }
73
74 #[inline(always)]
80 unsafe fn param_metadata(idx: i32) -> $crate::ParameterMetadata {
81 match $crate::ParamId::from_raw(idx) {
82 Some(param) => $crate::Patch::param_metadata(unsafe { patch() }, param),
83 None => $crate::ParameterMetadata::default(),
84 }
85 }
86
87 unsafe extern "C" fn patch_init(env: *const $crate::abi::PatchEnv) {
94 let supported =
95 !env.is_null() && unsafe { (*env).abi_version } == u32::from($crate::abi::PATCH_ABI_VERSION);
96 if !supported {
97 unsafe { state().error = true };
98 return;
99 }
100 $crate::Patch::init(unsafe { patch() });
101 }
102
103 unsafe extern "C" fn patch_agent_update_buffers(
104 _env: *const $crate::abi::PatchEnv,
105 left: *mut f32,
106 right: *mut f32,
107 samples: i32,
108 ) -> f32 {
109 if left.is_null() || right.is_null() {
110 return 0.0;
111 }
112 let n = usize::try_from(samples).unwrap_or(0);
115 let left = unsafe { core::slice::from_raw_parts_mut(left, n) };
116 let right = unsafe { core::slice::from_raw_parts_mut(right, n) };
117 $crate::Patch::process_audio(unsafe { patch() }, left, right)
118 }
119
120 unsafe extern "C" fn patch_agent_set_buffer(_env: *const $crate::abi::PatchEnv, buffer: *mut f32) {
121 if buffer.is_null() {
122 unsafe { state().error = true };
123 return;
124 }
125 let buf = unsafe { core::slice::from_raw_parts_mut(buffer, $crate::WORKING_BUFFER_SIZE) };
126 $crate::Patch::set_working_buffer(unsafe { patch() }, buf);
127 }
128
129 unsafe extern "C" fn patch_agent_get_buffer_size(_env: *const $crate::abi::PatchEnv) -> i32 {
130 $crate::WORKING_BUFFER_SIZE as i32
131 }
132
133 unsafe extern "C" fn patch_agent_get_param_min(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
134 unsafe { param_metadata(idx) }.min
135 }
136
137 unsafe extern "C" fn patch_agent_get_param_max(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
138 unsafe { param_metadata(idx) }.max
139 }
140
141 unsafe extern "C" fn patch_agent_get_param_default(_env: *const $crate::abi::PatchEnv, idx: i32) -> f32 {
142 unsafe { param_metadata(idx) }.default
143 }
144
145 unsafe extern "C" fn patch_agent_is_param_enabled(
146 _env: *const $crate::abi::PatchEnv,
147 idx: i32,
148 source_id: i32,
149 ) -> u8 {
150 let is_knob = source_id == 0;
151 u8::from(is_knob && $crate::ParamId::from_raw(idx).is_some())
152 }
153
154 unsafe extern "C" fn patch_agent_get_param_name(
155 _env: *const $crate::abi::PatchEnv,
156 _idx: i32,
157 out: *mut u8,
158 buf_size: usize,
159 ) {
160 if !out.is_null() && buf_size > 0 {
161 unsafe { *out = 0 };
162 }
163 }
164
165 unsafe extern "C" fn patch_agent_get_param_unit(
166 _env: *const $crate::abi::PatchEnv,
167 _idx: i32,
168 out: *mut u8,
169 buf_size: usize,
170 ) {
171 if !out.is_null() && buf_size > 0 {
172 unsafe { *out = 0 };
173 }
174 }
175
176 unsafe extern "C" fn patch_agent_set_param(_env: *const $crate::abi::PatchEnv, idx: i32, value: f32) {
177 if let Some(param) = $crate::ParamId::from_raw(idx) {
178 $crate::Patch::set_param(unsafe { patch() }, param, value);
179 }
180 }
181
182 unsafe extern "C" fn patch_agent_special_action(_env: *const $crate::abi::PatchEnv, idx: i32) {
183 if let Some(action) = $crate::ActionId::from_raw(idx) {
184 $crate::Patch::handle_action(unsafe { patch() }, action);
185 }
186 }
187
188 unsafe extern "C" fn patch_agent_get_state_idx(_env: *const $crate::abi::PatchEnv) -> i32 {
189 if unsafe { state().error } {
190 return $crate::Color::Red as i32;
191 }
192 $crate::Patch::state_led_color(unsafe { patch() }) as i32
193 }
194
195 core::arch::global_asm!(
206 ".section .patch_header, \"a\"",
207 ".balign 4",
208 ".global patch_header",
209 "patch_header:",
210 ".word {magic}",
212 ".short {abi_version}",
214 ".short 0",
215 ".word {init}",
217 ".word {update_buffers}",
219 ".word {set_buffer}",
220 ".word {get_buffer_size}",
221 ".word {get_param_min}",
222 ".word {get_param_max}",
223 ".word {get_param_default}",
224 ".word {is_param_enabled}",
225 ".word {get_param_name}",
226 ".word {get_param_unit}",
227 ".word {set_param}",
228 ".word {special_action}",
229 ".word {get_state_idx}",
230 ".word patch_image_size",
232 ".word __patch_bss_start",
234 ".word patch_bss_size",
236 ".fill 16, 4, 0",
238 magic = const $crate::abi::PATCH_MAGIC,
239 abi_version = const $crate::abi::PATCH_ABI_VERSION,
240 init = sym patch_init,
241 update_buffers = sym patch_agent_update_buffers,
242 set_buffer = sym patch_agent_set_buffer,
243 get_buffer_size = sym patch_agent_get_buffer_size,
244 get_param_min = sym patch_agent_get_param_min,
245 get_param_max = sym patch_agent_get_param_max,
246 get_param_default = sym patch_agent_get_param_default,
247 is_param_enabled = sym patch_agent_is_param_enabled,
248 get_param_name = sym patch_agent_get_param_name,
249 get_param_unit = sym patch_agent_get_param_unit,
250 set_param = sym patch_agent_set_param,
251 special_action = sym patch_agent_special_action,
252 get_state_idx = sym patch_agent_get_state_idx,
253 );
254 };
255}