1use std::cell::RefCell;
51use std::rc::Rc;
52
53use moont::{Frame, Synth, cm32l, smf};
54use wasm_bindgen::prelude::*;
55use web_sys::{
56 AudioContext, AudioContextOptions, AudioProcessingEvent,
57 ScriptProcessorNode,
58};
59
60const BUFFER_SIZE: u32 = 1024;
61const SAMPLE_RATE: f32 = moont::SAMPLE_RATE as f32;
62
63struct Inner {
64 synth: cm32l::Device,
65 smf_events: Vec<smf::Event>,
66 smf_index: usize,
67 smf_start: u32,
68 smf_duration: u32,
69}
70
71impl Inner {
72 fn feed_smf(&mut self, deadline: u32) {
73 while self.smf_index < self.smf_events.len()
74 && self.smf_events[self.smf_index].time() <= deadline
75 {
76 match &self.smf_events[self.smf_index] {
77 smf::Event::Msg { time, msg } => {
78 self.synth.play_msg_at(*msg, *time);
79 }
80 smf::Event::Sysex { time, data } => {
81 self.synth.play_sysex_at(data, *time);
82 }
83 _ => {}
84 }
85 self.smf_index += 1;
86 }
87 }
88}
89
90#[wasm_bindgen]
99pub struct Cm32lSynth {
100 inner: Rc<RefCell<Inner>>,
101 _ctx: AudioContext,
102 _processor: ScriptProcessorNode,
103 _closure: Closure<dyn FnMut(AudioProcessingEvent)>,
104}
105
106fn setup(synth: cm32l::Device) -> Result<Cm32lSynth, JsValue> {
107 let inner = Rc::new(RefCell::new(Inner {
108 synth,
109 smf_events: Vec::new(),
110 smf_index: 0,
111 smf_start: 0,
112 smf_duration: 0,
113 }));
114
115 let opts = AudioContextOptions::new();
116 opts.set_sample_rate(SAMPLE_RATE);
117 let ctx = AudioContext::new_with_context_options(&opts)?;
118
119 let processor = ctx.create_script_processor_with_buffer_size_and_number_of_input_channels_and_number_of_output_channels(
120 BUFFER_SIZE, 0, 2,
121 )?;
122
123 let inner_ref = inner.clone();
124 let closure = Closure::wrap(Box::new(move |event: AudioProcessingEvent| {
125 let buf = event.output_buffer().unwrap();
126 let len = buf.length() as usize;
127 let mut inner = inner_ref.borrow_mut();
128 let current = inner.synth.current_time();
129 inner.feed_smf(current + len as u32);
130 let mut frames = vec![Frame(0, 0); len];
131 inner.synth.render(&mut frames);
132 drop(inner);
133
134 let mut left = vec![0.0f32; len];
135 let mut right = vec![0.0f32; len];
136 for (i, f) in frames.iter().enumerate() {
137 left[i] = f.0 as f32 / 32768.0;
138 right[i] = f.1 as f32 / 32768.0;
139 }
140 buf.copy_to_channel(&left, 0).unwrap();
141 buf.copy_to_channel(&right, 1).unwrap();
142 }) as Box<dyn FnMut(AudioProcessingEvent)>);
143
144 processor.set_onaudioprocess(Some(closure.as_ref().unchecked_ref()));
145 processor.connect_with_audio_node(&ctx.destination())?;
146
147 Ok(Cm32lSynth {
148 inner,
149 _ctx: ctx,
150 _processor: processor,
151 _closure: closure,
152 })
153}
154
155#[wasm_bindgen]
156impl Cm32lSynth {
157 #[cfg(feature = "bundle-rom")]
162 #[wasm_bindgen(constructor)]
163 pub fn new() -> Result<Cm32lSynth, JsValue> {
164 let synth = cm32l::Device::new(cm32l::Rom::bundled());
165 setup(synth)
166 }
167
168 pub fn from_rom(
173 control_rom: &[u8],
174 pcm_rom: &[u8],
175 ) -> Result<Cm32lSynth, JsValue> {
176 let rom = cm32l::Rom::new(control_rom, pcm_rom)
177 .map_err(|e| JsValue::from_str(&format!("{e:?}")))?;
178 let synth = cm32l::Device::new(rom);
179 setup(synth)
180 }
181
182 pub fn play_midi(&self, data: &[u8]) -> bool {
186 if data.is_empty() || data.len() > 3 {
187 return false;
188 }
189 let mut msg: u32 = 0;
190 for (i, &b) in data.iter().enumerate() {
191 msg |= (b as u32) << (i * 8);
192 }
193 self.inner.borrow_mut().synth.play_msg(msg)
194 }
195
196 pub fn play_sysex(&self, data: &[u8]) -> bool {
200 self.inner.borrow_mut().synth.play_sysex(data)
201 }
202
203 pub fn load_smf(&self, data: &[u8]) -> Result<f64, JsValue> {
210 let events =
211 smf::parse(data).map_err(|e| JsValue::from_str(&format!("{e}")))?;
212 let last = events.last().map(|e| e.time()).unwrap_or(0);
213 let tail = 2 * SAMPLE_RATE as u32;
214 let mut inner = self.inner.borrow_mut();
215 let offset = inner.synth.current_time();
216 let shifted: Vec<smf::Event> = events
217 .into_iter()
218 .map(|e| match e {
219 smf::Event::Msg { time, msg } => smf::Event::Msg {
220 time: time + offset,
221 msg,
222 },
223 smf::Event::Sysex { time, data } => smf::Event::Sysex {
224 time: time + offset,
225 data,
226 },
227 _ => e,
228 })
229 .collect();
230 inner.smf_events = shifted;
231 inner.smf_index = 0;
232 inner.smf_start = offset;
233 inner.smf_duration = last + tail;
234 Ok(inner.smf_duration as f64 / SAMPLE_RATE as f64)
235 }
236
237 pub fn stop_smf(&self) {
239 let mut inner = self.inner.borrow_mut();
240 inner.smf_events.clear();
241 inner.smf_index = 0;
242 inner.smf_duration = 0;
243 }
244
245 pub fn smf_elapsed(&self) -> f64 {
247 let inner = self.inner.borrow();
248 let elapsed =
249 inner.synth.current_time().saturating_sub(inner.smf_start);
250 elapsed as f64 / SAMPLE_RATE as f64
251 }
252
253 pub fn current_time(&self) -> f64 {
255 self.inner.borrow().synth.current_time() as f64 / SAMPLE_RATE as f64
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 #[test]
262 fn test_midi_packing() {
263 let data: &[u8] = &[0x90, 0x3C, 0x7F];
264 let mut msg: u32 = 0;
265 for (i, &b) in data.iter().enumerate() {
266 msg |= (b as u32) << (i * 8);
267 }
268 assert_eq!(msg, 0x007F3C90);
269 }
270}