devalang_wasm/engine/plugin/
runner.rs1use std::cell::RefCell;
2use std::collections::HashMap;
3#[cfg(feature = "cli")]
4use wasmtime::{Engine, Instance, Linker, Module, Store};
5
6#[cfg(feature = "cli")]
7pub struct WasmPluginRunner {
8 engine: Engine,
9 cache: RefCell<HashMap<u64, (Store<()>, Instance)>>,
11}
12
13#[cfg(feature = "cli")]
14impl Default for WasmPluginRunner {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20#[cfg(feature = "cli")]
21impl WasmPluginRunner {
22 pub fn new() -> Self {
23 let engine = Engine::default();
24 Self {
25 engine,
26 cache: RefCell::new(HashMap::new()),
27 }
28 }
29
30 pub fn render_note_in_place(
36 &self,
37 wasm_bytes: &[u8],
38 buffer: &mut [f32],
39 instance_key: Option<&str>, synth_name: Option<&str>, freq: f32,
42 amp: f32,
43 duration_ms: i32,
44 sample_rate: i32,
45 channels: i32,
46 options: Option<&HashMap<String, f32>>,
47 ) -> Result<(), String> {
48 use std::collections::hash_map::DefaultHasher;
50 use std::hash::{Hash, Hasher};
51 let mut hasher = DefaultHasher::new();
52 wasm_bytes.hash(&mut hasher);
53 instance_key.hash(&mut hasher); let hash = hasher.finish();
55
56 let mut cache = self.cache.borrow_mut();
58
59 if !cache.contains_key(&hash) {
61 let module = Module::new(&self.engine, wasm_bytes)
63 .map_err(|e| format!("Failed to compile wasm: {e}"))?;
64
65 let mut store = Store::new(&self.engine, ());
66 let mut linker = Linker::new(&self.engine);
67
68 linker
70 .func_wrap(
71 "__wbindgen_placeholder__",
72 "__wbindgen_describe",
73 |_: i32| {},
74 )
75 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
76 linker
77 .func_wrap(
78 "__wbindgen_placeholder__",
79 "__wbindgen_object_clone_ref",
80 |_: i32| -> i32 { 0 },
81 )
82 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
83 linker
84 .func_wrap(
85 "__wbindgen_placeholder__",
86 "__wbindgen_object_drop_ref",
87 |_: i32| {},
88 )
89 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
90 linker
91 .func_wrap(
92 "__wbindgen_placeholder__",
93 "__wbindgen_string_new",
94 |_: i32, _: i32| -> i32 { 0 },
95 )
96 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
97 linker
98 .func_wrap(
99 "__wbindgen_placeholder__",
100 "__wbindgen_throw",
101 |_: i32, _: i32| {},
102 )
103 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
104
105 let instance = linker
106 .instantiate(&mut store, &module)
107 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
108
109 cache.insert(hash, (store, instance));
110 } else {
111 }
113
114 let entry = cache.get_mut(&hash).unwrap();
116
117 let memory = entry
118 .1
119 .get_memory(&mut entry.0, "memory")
120 .ok_or_else(|| "WASM memory export not found".to_string())?;
121
122 let func_name = if let Some(name) = synth_name {
124 if entry.1.get_func(&mut entry.0, name).is_some() {
126 name
127 } else if entry.1.get_func(&mut entry.0, "render_note").is_some() {
128 "render_note"
129 } else {
130 return Err(format!("Plugin export '{}' not found", name));
131 }
132 } else {
133 "render_note"
134 };
135
136 let func = entry
137 .1
138 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut entry.0, func_name)
139 .map_err(|e| {
140 format!(
141 "Function '{}' not found or wrong signature: {}",
142 func_name, e
143 )
144 })?;
145
146 if let Some(opts) = options {
148 let setter_map: HashMap<&str, &str> = [
152 ("waveform", "setWaveform"),
153 ("cutoff", "setCutoff"),
154 ("resonance", "setResonance"),
155 ("env_mod", "setEnvMod"),
156 ("decay", "setDecay"),
157 ("accent", "setAccent"),
158 ("drive", "setDrive"),
159 ("tone", "setTone"),
160 ("slide", "setSlide"),
161 ("glide", "setGlide"),
162 ]
163 .iter()
164 .cloned()
165 .collect();
166
167 for (key, value) in opts.iter() {
168 if let Some(setter_name) = setter_map.get(key.as_str()) {
170 if let Ok(setter) = entry.1.get_typed_func::<f32, ()>(&mut entry.0, setter_name)
172 {
173 let _ = setter.call(&mut entry.0, *value);
176 } else {
177 }
179 } else {
180 }
182 }
183 } else {
184 }
186
187 let byte_len = std::mem::size_of_val(buffer);
189 let ptr = Self::alloc_temp(&mut entry.0, &entry.1, &memory, byte_len)? as i32;
190
191 let mem_slice = memory
193 .data_mut(&mut entry.0)
194 .get_mut(ptr as usize..(ptr as usize) + byte_len)
195 .ok_or_else(|| "Failed to get memory slice".to_string())?;
196 let src_bytes =
197 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len) };
198 mem_slice.copy_from_slice(src_bytes);
199
200 func.call(
202 &mut entry.0,
203 (
204 ptr,
205 buffer.len() as i32,
206 freq,
207 amp,
208 duration_ms,
209 sample_rate,
210 channels,
211 ),
212 )
213 .map_err(|e| format!("Error calling '{}': {}", func_name, e))?;
214
215 let mem_slice_after = memory
217 .data(&entry.0)
218 .get(ptr as usize..(ptr as usize) + byte_len)
219 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
220 let dst_bytes =
221 unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len) };
222 dst_bytes.copy_from_slice(mem_slice_after);
223
224 let _non_zero = buffer.iter().any(|&x| x.abs() > 0.0001);
226 let _max_val = buffer.iter().map(|&x| x.abs()).fold(0.0f32, f32::max);
227 Ok(())
230 }
231
232 fn alloc_temp(
234 store: &mut Store<()>,
235 _instance: &Instance,
236 memory: &wasmtime::Memory,
237 size: usize,
238 ) -> Result<i32, String> {
239 let pages = memory.size(&*store);
241 let current_size = pages * 65536; let ptr = current_size as i32;
243
244 let needed_pages = ((size + 65535) / 65536) as u64;
246 if needed_pages > 0 {
247 memory
248 .grow(store, needed_pages)
249 .map_err(|e| format!("Failed to grow memory: {}", e))?;
250 }
251
252 Ok(ptr)
253 }
254}