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
71 .func_wrap(
72 "__wbindgen_placeholder__",
73 "__wbindgen_describe",
74 |_: i32| {},
75 )
76 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
77 linker
78 .func_wrap(
79 "__wbindgen_placeholder__",
80 "__wbindgen_object_clone_ref",
81 |_: i32| -> i32 { 0 },
82 )
83 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
84 linker
85 .func_wrap(
86 "__wbindgen_placeholder__",
87 "__wbindgen_object_drop_ref",
88 |_: i32| {},
89 )
90 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
91 linker
92 .func_wrap(
93 "__wbindgen_placeholder__",
94 "__wbindgen_string_new",
95 |_: i32, _: i32| -> i32 { 0 },
96 )
97 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
98 linker
99 .func_wrap(
100 "__wbindgen_placeholder__",
101 "__wbindgen_throw",
102 |_: i32, _: i32| {},
103 )
104 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
105
106 let instance = linker
107 .instantiate(&mut store, &module)
108 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
109
110 cache.insert(hash, (store, instance));
111 } else {
112 }
114
115 let entry = cache.get_mut(&hash).unwrap();
117
118 let memory = entry
119 .1
120 .get_memory(&mut entry.0, "memory")
121 .ok_or_else(|| "WASM memory export not found".to_string())?;
122
123 let func_name = if let Some(name) = synth_name {
125 if entry.1.get_func(&mut entry.0, name).is_some() {
127 name
128 } else if entry.1.get_func(&mut entry.0, "render_note").is_some() {
129 "render_note"
130 } else {
131 return Err(format!("Plugin export '{}' not found", name));
132 }
133 } else {
134 "render_note"
135 };
136
137 let func = entry
138 .1
139 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut entry.0, func_name)
140 .map_err(|e| {
141 format!(
142 "Function '{}' not found or wrong signature: {}",
143 func_name, e
144 )
145 })?;
146
147 if let Some(opts) = options {
149 let setter_map: HashMap<&str, &str> = [
153 ("waveform", "setWaveform"),
154 ("cutoff", "setCutoff"),
155 ("resonance", "setResonance"),
156 ("env_mod", "setEnvMod"),
157 ("decay", "setDecay"),
158 ("accent", "setAccent"),
159 ("drive", "setDrive"),
160 ("tone", "setTone"),
161 ("slide", "setSlide"),
162 ("glide", "setGlide"),
163 ]
164 .iter()
165 .cloned()
166 .collect();
167
168 for (key, value) in opts.iter() {
169 if let Some(setter_name) = setter_map.get(key.as_str()) {
171 if let Ok(setter) = entry.1.get_typed_func::<f32, ()>(&mut entry.0, setter_name)
173 {
174 let _ = setter.call(&mut entry.0, *value);
177 } else {
178 }
180 } else {
181 }
183 }
184 } else {
185 }
187
188 let byte_len = std::mem::size_of_val(buffer);
190 let ptr = Self::alloc_temp(&mut entry.0, &entry.1, &memory, byte_len)? as i32;
191
192 let mem_slice = memory
196 .data_mut(&mut entry.0)
197 .get_mut(ptr as usize..(ptr as usize) + byte_len)
198 .ok_or_else(|| "Failed to get memory slice".to_string())?;
199 let src_bytes =
200 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len) };
201 mem_slice.copy_from_slice(src_bytes);
202
203 func.call(
205 &mut entry.0,
206 (
207 ptr,
208 buffer.len() as i32,
209 freq,
210 amp,
211 duration_ms,
212 sample_rate,
213 channels,
214 ),
215 )
216 .map_err(|e| format!("Error calling '{}': {}", func_name, e))?;
217
218 let mem_slice_after = memory
220 .data(&entry.0)
221 .get(ptr as usize..(ptr as usize) + byte_len)
222 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
223 let dst_bytes =
224 unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len) };
225 dst_bytes.copy_from_slice(mem_slice_after);
226
227 let _non_zero = buffer.iter().any(|&x| x.abs() > 0.0001);
229 let _max_val = buffer.iter().map(|&x| x.abs()).fold(0.0f32, f32::max);
230 Ok(())
233 }
234
235 fn alloc_temp(
237 store: &mut Store<()>,
238 _instance: &Instance,
239 memory: &wasmtime::Memory,
240 size: usize,
241 ) -> Result<i32, String> {
242 let pages = memory.size(&*store);
244 let current_size = pages * 65536; let ptr = current_size as i32;
246
247 let needed_pages = ((size + 65535) / 65536) as u64;
249 if needed_pages > 0 {
250 memory
251 .grow(store, needed_pages)
252 .map_err(|e| format!("Failed to grow memory: {}", e))?;
253 }
254
255 Ok(ptr)
256 }
257}