1use devalang_utils::logger::{LogLevel, Logger};
2use std::collections::HashMap;
3
4use wasmtime::{Engine, Instance, Linker, Module, Store, TypedFunc};
5
6type RenderFunc = TypedFunc<(i32, i32, f32, f32, i32, i32, i32), ()>;
7
8pub struct WasmPluginRunner {
9 engine: Engine,
10}
11
12impl Default for WasmPluginRunner {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl WasmPluginRunner {
19 pub fn new() -> Self {
20 let engine = Engine::default();
21 Self { engine }
22 }
23
24 pub fn process_in_place(&self, wasm_bytes: &[u8], buffer: &mut [f32]) -> Result<(), String> {
25 let module = Module::new(&self.engine, wasm_bytes)
26 .map_err(|e| format!("Failed to compile wasm: {e}"))?;
27
28 let mut store = Store::new(&self.engine, ());
29 let linker = Linker::new(&self.engine);
30
31 let instance = linker
32 .instantiate(&mut store, &module)
33 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
34
35 let memory = instance
36 .get_memory(&mut store, "memory")
37 .ok_or_else(|| "WASM memory export not found".to_string())?;
38
39 let func = instance
40 .get_typed_func::<(i32, i32), ()>(&mut store, "process")
41 .map_err(|_| "Exported function `process(i32,i32)` not found".to_string())?;
42
43 let byte_len = std::mem::size_of_val(buffer) as i32;
44 let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
45 let mem_slice = memory
46 .data_mut(&mut store)
47 .get_mut(ptr as usize..(ptr as usize) + (byte_len as usize))
48 .ok_or_else(|| "Failed to get memory slice".to_string())?;
49
50 let src_bytes =
51 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len as usize) };
52 mem_slice.copy_from_slice(src_bytes);
53
54 func.call(&mut store, (ptr, buffer.len() as i32))
55 .map_err(|e| format!("Error calling `process`: {e}"))?;
56
57 let mem_slice_after = memory
58 .data(&store)
59 .get(ptr as usize..(ptr as usize) + (byte_len as usize))
60 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
61 let dst_bytes = unsafe {
62 std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len as usize)
63 };
64 dst_bytes.copy_from_slice(mem_slice_after);
65
66 Ok(())
67 }
68
69 pub fn render_note_in_place(
70 &self,
71 wasm_bytes: &[u8],
72 buffer: &mut [f32],
73 synth_name: Option<&str>,
74 freq: f32,
75 amp: f32,
76 duration_ms: i32,
77 sample_rate: i32,
78 channels: i32,
79 ) -> Result<(), String> {
80 let module = Module::new(&self.engine, wasm_bytes)
81 .map_err(|e| format!("Failed to compile wasm: {e}"))?;
82
83 let mut store = Store::new(&self.engine, ());
84 let linker = Linker::new(&self.engine);
85
86 let instance = linker
87 .instantiate(&mut store, &module)
88 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
89
90 let memory = instance
91 .get_memory(&mut store, "memory")
92 .ok_or_else(|| "WASM memory export not found".to_string())?;
93
94 let mut func_opt: Option<RenderFunc> = None;
100 if let Some(name) = synth_name {
101 let specific = format!("render_note_{}", name);
102 if let Ok(f) = instance
103 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut store, &specific)
104 {
105 func_opt = Some(f);
106 }
107 }
108 if func_opt.is_none() {
109 if let Ok(f) = instance.get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(
110 &mut store,
111 "render_note",
112 ) {
113 func_opt = Some(f);
114 }
115 }
116 if func_opt.is_none() {
117 if let Some(name) = synth_name {
118 let specific = format!("synth_{}", name);
119 if let Ok(f) = instance.get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(
120 &mut store, &specific,
121 ) {
122 func_opt = Some(f);
123 }
124 }
125 }
126 if func_opt.is_none() {
127 if let Ok(f) = instance
128 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut store, "synth")
129 {
130 func_opt = Some(f);
131 }
132 }
133
134 let func = func_opt.ok_or_else(|| {
135 "Exported function not found: tried render_note[_<name>] and synth[_<name>]".to_string()
136 })?;
137
138 let byte_len = std::mem::size_of_val(buffer) as i32;
140 let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
141 let mem_slice = memory
142 .data_mut(&mut store)
143 .get_mut(ptr as usize..(ptr as usize) + (byte_len as usize))
144 .ok_or_else(|| "Failed to get memory slice".to_string())?;
145 let src_bytes =
146 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len as usize) };
147 mem_slice.copy_from_slice(src_bytes);
148
149 func.call(
151 &mut store,
152 (
153 ptr,
154 buffer.len() as i32,
155 freq,
156 amp,
157 duration_ms,
158 sample_rate,
159 channels,
160 ),
161 )
162 .map_err(|e| format!("Error calling `render_note`: {e}"))?;
163
164 let mem_slice_after = memory
166 .data(&store)
167 .get(ptr as usize..(ptr as usize) + (byte_len as usize))
168 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
169 let dst_bytes = unsafe {
170 std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len as usize)
171 };
172 dst_bytes.copy_from_slice(mem_slice_after);
173
174 Ok(())
175 }
176
177 pub fn render_note_with_params_in_place(
180 &self,
181 wasm_bytes: &[u8],
182 buffer: &mut [f32],
183 synth_name: Option<&str>,
184 freq: f32,
185 amp: f32,
186 duration_ms: i32,
187 sample_rate: i32,
188 channels: i32,
189 params_num: &HashMap<String, f32>,
190 params_str: Option<&HashMap<String, String>>,
191 exported_names: Option<&[String]>,
192 ) -> Result<(), String> {
193 let module = Module::new(&self.engine, wasm_bytes)
194 .map_err(|e| format!("Failed to compile wasm: {e}"))?;
195
196 let mut store = Store::new(&self.engine, ());
197 let linker = Linker::new(&self.engine);
198
199 let instance = linker
200 .instantiate(&mut store, &module)
201 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
202
203 let memory = instance
204 .get_memory(&mut store, "memory")
205 .ok_or_else(|| "WASM memory export not found".to_string())?;
206
207 let logger = Logger::new();
209 for (k, v) in params_num.iter() {
210 let candidates = [
212 format!("set_synth_{}", k),
213 format!("set_{}", k),
214 format!("set_note_{}", k),
215 ];
216
217 let mut any_called = false;
218
219 if let Some(exports) = exported_names {
221 logger.log_message(
222 LogLevel::Debug,
223 &format!("Plugin exports provided ({} names)", exports.len()),
224 );
225 for c in &candidates {
226 if exports.iter().any(|e| e == c) {
227 match instance.get_typed_func::<f32, ()>(&mut store, c) {
228 Ok(setter) => {
229 let _ = setter.call(&mut store, *v);
230 any_called = true;
231 logger.log_message(
232 LogLevel::Debug,
233 &format!("Called setter '{}' with {}", c, v),
234 );
235 }
236 Err(_) => {
237 logger.log_message(
238 LogLevel::Debug,
239 &format!("Export '{}' declared but signature lookup failed", c),
240 );
241 }
242 }
243 }
244 }
245 }
246
247 if !any_called {
249 for fname in &candidates {
250 match instance.get_typed_func::<f32, ()>(&mut store, fname) {
251 Ok(setter) => {
252 let _ = setter.call(&mut store, *v);
253 any_called = true;
254 logger.log_message(
255 LogLevel::Debug,
256 &format!("Dynamically called setter '{}' with {}", fname, v),
257 );
258 }
259 Err(_) => {
260 }
262 }
263 }
264 }
265
266 if !any_called {
267 logger.log_message(
268 LogLevel::Warning,
269 &format!("No setter found/called for numeric param '{}'", k),
270 );
271 }
272 }
273
274 if let Some(smap) = params_str {
276 for (k, v) in smap.iter() {
277 let candidates = [
278 format!("set_synth_{}_str", k),
279 format!("set_{}_str", k),
280 format!("set_note_{}_str", k),
281 ];
282
283 let logger = Logger::new();
284 for (k, v) in smap.iter() {
285 let candidates = [
286 format!("set_synth_{}_str", k),
287 format!("set_{}_str", k),
288 format!("set_note_{}_str", k),
289 ];
290
291 let mut any_called = false;
292
293 if let Some(exports) = exported_names {
294 for c in &candidates {
295 if exports.iter().any(|e| e == c) {
296 match instance.get_typed_func::<(i32, i32), ()>(&mut store, c) {
297 Ok(setter) => {
298 let bytes = v.as_bytes();
299 let ptr = Self::alloc_temp(
300 &mut store,
301 &instance,
302 &memory,
303 bytes.len(),
304 )? as i32;
305 let mem_slice = memory
306 .data_mut(&mut store)
307 .get_mut(ptr as usize..(ptr as usize) + bytes.len())
308 .ok_or_else(|| {
309 "Failed to get memory slice for string".to_string()
310 })?;
311 mem_slice.copy_from_slice(bytes);
312 let _ = setter.call(&mut store, (ptr, bytes.len() as i32));
313 any_called = true;
314 logger.log_message(
315 LogLevel::Debug,
316 &format!("Called string setter '{}' with '{}'", c, v),
317 );
318 }
319 Err(_) => {
320 logger.log_message(
321 LogLevel::Debug,
322 &format!(
323 "Export '{}' declared but signature lookup failed",
324 c
325 ),
326 );
327 }
328 }
329 }
330 }
331 }
332
333 if !any_called {
334 for fname in &candidates {
335 if let Ok(setter) =
336 instance.get_typed_func::<(i32, i32), ()>(&mut store, fname)
337 {
338 let bytes = v.as_bytes();
339 let ptr =
340 Self::alloc_temp(&mut store, &instance, &memory, bytes.len())?
341 as i32;
342 let mem_slice = memory
343 .data_mut(&mut store)
344 .get_mut(ptr as usize..(ptr as usize) + bytes.len())
345 .ok_or_else(|| {
346 "Failed to get memory slice for string".to_string()
347 })?;
348 mem_slice.copy_from_slice(bytes);
349 let _ = setter.call(&mut store, (ptr, bytes.len() as i32));
350 any_called = true;
351 logger.log_message(
352 LogLevel::Debug,
353 &format!(
354 "Dynamically called string setter '{}' with '{}'",
355 fname, v
356 ),
357 );
358 }
359 }
360 }
361
362 if !any_called {
363 logger.log_message(
364 LogLevel::Warning,
365 &format!("No string setter found/called for param '{}'", k),
366 );
367 }
368 }
369 }
370 }
371
372 let mut func_opt: Option<RenderFunc> = None;
378 if let Some(name) = synth_name {
379 let specific = format!("render_note_{}", name);
380 if let Ok(f) = instance
381 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut store, &specific)
382 {
383 func_opt = Some(f);
384 }
385 }
386 if func_opt.is_none() {
387 if let Ok(f) = instance.get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(
388 &mut store,
389 "render_note",
390 ) {
391 func_opt = Some(f);
392 }
393 }
394 if func_opt.is_none() {
395 if let Some(name) = synth_name {
396 let specific = format!("synth_{}", name);
397 if let Ok(f) = instance.get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(
398 &mut store, &specific,
399 ) {
400 func_opt = Some(f);
401 }
402 }
403 }
404 if func_opt.is_none() {
405 if let Ok(f) = instance
406 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut store, "synth")
407 {
408 func_opt = Some(f);
409 }
410 }
411 let func = func_opt.ok_or_else(|| {
412 "Exported function not found: tried render_note[_<name>] and synth[_<name>]".to_string()
413 })?;
414
415 let byte_len = std::mem::size_of_val(buffer) as i32;
417 let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
418 let mem_slice = memory
419 .data_mut(&mut store)
420 .get_mut(ptr as usize..(ptr as usize) + (byte_len as usize))
421 .ok_or_else(|| "Failed to get memory slice".to_string())?;
422 let src_bytes =
423 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len as usize) };
424 mem_slice.copy_from_slice(src_bytes);
425
426 func.call(
428 &mut store,
429 (
430 ptr,
431 buffer.len() as i32,
432 freq,
433 amp,
434 duration_ms,
435 sample_rate,
436 channels,
437 ),
438 )
439 .map_err(|e| format!("Error calling `render_note`: {e}"))?;
440
441 let mem_slice_after = memory
443 .data(&store)
444 .get(ptr as usize..(ptr as usize) + (byte_len as usize))
445 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
446 let dst_bytes = unsafe {
447 std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len as usize)
448 };
449 dst_bytes.copy_from_slice(mem_slice_after);
450
451 Ok(())
452 }
453
454 fn alloc_temp(
455 store: &mut Store<()>,
456 instance: &Instance,
457 memory: &wasmtime::Memory,
458 size: usize,
459 ) -> Result<usize, String> {
460 if let Ok(malloc) = instance.get_typed_func::<i32, i32>(&mut *store, "__wbindgen_malloc") {
462 let ptr = malloc
463 .call(&mut *store, size as i32)
464 .map_err(|e| format!("malloc failed: {e}"))? as usize;
465 return Ok(ptr);
466 }
467
468 let current_len = memory.data_size(&mut *store);
470 let need = size;
471 let pages_needed = (current_len + need).div_ceil(0x10000) as u64; let current_pages = memory.size(&mut *store);
473 if pages_needed > current_pages {
474 let to_grow = pages_needed - current_pages;
475 memory
476 .grow(&mut *store, to_grow)
477 .map_err(|e| format!("memory.grow failed: {e}"))?;
478 }
479 Ok(current_len)
480 }
481}