devalang_wasm/engine/plugin/
runner.rs1#[cfg(feature = "cli")]
2use wasmtime::{Engine, Instance, Linker, Module, Store};
3use std::collections::HashMap;
4use std::cell::RefCell;
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.func_wrap("__wbindgen_placeholder__", "__wbindgen_describe", |_: i32| {})
71 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
72 linker.func_wrap("__wbindgen_placeholder__", "__wbindgen_object_clone_ref", |_: i32| -> i32 { 0 })
73 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
74 linker.func_wrap("__wbindgen_placeholder__", "__wbindgen_object_drop_ref", |_: i32| {})
75 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
76 linker.func_wrap("__wbindgen_placeholder__", "__wbindgen_string_new", |_: i32, _: i32| -> i32 { 0 })
77 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
78 linker.func_wrap("__wbindgen_placeholder__", "__wbindgen_throw", |_: i32, _: i32| {})
79 .map_err(|e| format!("Failed to define wbindgen import: {e}"))?;
80
81 let instance = linker
82 .instantiate(&mut store, &module)
83 .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
84
85 cache.insert(hash, (store, instance));
86 } else {
87 }
89
90 let entry = cache.get_mut(&hash).unwrap();
92
93 let memory = entry.1
94 .get_memory(&mut entry.0, "memory")
95 .ok_or_else(|| "WASM memory export not found".to_string())?;
96
97 let func_name = if let Some(name) = synth_name {
99 if entry.1.get_func(&mut entry.0, name).is_some() {
101 name
102 } else if entry.1.get_func(&mut entry.0, "render_note").is_some() {
103 "render_note"
104 } else {
105 return Err(format!("Plugin export '{}' not found", name));
106 }
107 } else {
108 "render_note"
109 };
110
111 let func = entry.1
112 .get_typed_func::<(i32, i32, f32, f32, i32, i32, i32), ()>(&mut entry.0, func_name)
113 .map_err(|e| format!("Function '{}' not found or wrong signature: {}", func_name, e))?;
114
115 if let Some(opts) = options {
117 let setter_map: HashMap<&str, &str> = [
121 ("waveform", "setWaveform"),
122 ("cutoff", "setCutoff"),
123 ("resonance", "setResonance"),
124 ("env_mod", "setEnvMod"),
125 ("decay", "setDecay"),
126 ("accent", "setAccent"),
127 ("drive", "setDrive"),
128 ("tone", "setTone"),
129 ("slide", "setSlide"),
130 ("glide", "setGlide"),
131 ].iter().cloned().collect();
132
133 for (key, value) in opts.iter() {
134 if let Some(setter_name) = setter_map.get(key.as_str()) {
136 if let Ok(setter) = entry.1.get_typed_func::<f32, ()>(&mut entry.0, setter_name) {
138 let _ = setter.call(&mut entry.0, *value);
141 } else {
142 }
144 } else {
145 }
147 }
148 } else {
149 }
151
152 let byte_len = std::mem::size_of_val(buffer);
154 let ptr = Self::alloc_temp(&mut entry.0, &entry.1, &memory, byte_len)? as i32;
155
156 let mem_slice = memory
160 .data_mut(&mut entry.0)
161 .get_mut(ptr as usize..(ptr as usize) + byte_len)
162 .ok_or_else(|| "Failed to get memory slice".to_string())?;
163 let src_bytes = unsafe {
164 std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len)
165 };
166 mem_slice.copy_from_slice(src_bytes);
167
168 func.call(
170 &mut entry.0,
171 (
172 ptr,
173 buffer.len() as i32,
174 freq,
175 amp,
176 duration_ms,
177 sample_rate,
178 channels,
179 ),
180 )
181 .map_err(|e| format!("Error calling '{}': {}", func_name, e))?;
182
183 let mem_slice_after = memory
185 .data(&entry.0)
186 .get(ptr as usize..(ptr as usize) + byte_len)
187 .ok_or_else(|| "Failed to get memory slice after".to_string())?;
188 let dst_bytes = unsafe {
189 std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, byte_len)
190 };
191 dst_bytes.copy_from_slice(mem_slice_after);
192
193 let _non_zero = buffer.iter().any(|&x| x.abs() > 0.0001);
195 let _max_val = buffer.iter().map(|&x| x.abs()).fold(0.0f32, f32::max);
196 Ok(())
199 }
200
201 fn alloc_temp(
203 store: &mut Store<()>,
204 _instance: &Instance,
205 memory: &wasmtime::Memory,
206 size: usize,
207 ) -> Result<i32, String> {
208 let pages = memory.size(&*store);
210 let current_size = pages * 65536; let ptr = current_size as i32;
212
213 let needed_pages = ((size + 65535) / 65536) as u64;
215 if needed_pages > 0 {
216 memory
217 .grow(store, needed_pages)
218 .map_err(|e| format!("Failed to grow memory: {}", e))?;
219 }
220
221 Ok(ptr)
222 }
223}