mimium_web/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use mimium_audiodriver::backends::local_buffer::LocalBufferDriver;
use mimium_audiodriver::driver::Driver;
use mimium_lang::interner::ToSymbol;
use mimium_lang::log;
use mimium_lang::utils::error::report;
use mimium_lang::ExecContext;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Default)]
pub struct Config {
    pub sample_rate: f64,
    pub input_channels: u32,
    pub output_channels: u32,
    pub buffer_size: u32,
}
#[wasm_bindgen]
impl Config {
    #[wasm_bindgen]
    pub fn new() -> Self {
        Self::default()
    }
}

type Output<'a> = &'a mut [f32];
type Input<'a> = &'a [f32];

type Processer = Box<dyn FnMut(Input, Output) -> u64>;
#[wasm_bindgen]
#[derive(Default)]
pub struct Context {
    processor: Option<Processer>,
    config: Config,
}

fn get_default_context() -> ExecContext {
    let mut ctx = ExecContext::new([].into_iter(), None);
    ctx.add_system_plugin(mimium_scheduler::get_default_scheduler_plugin());
    if let Some(midi_plug) = mimium_midi::MidiPlugin::try_new() {
        ctx.add_system_plugin(midi_plug);
    } else {
        log::warn!("Midi is not supported on this platform.")
    }
    ctx
}

#[wasm_bindgen]
impl Context {
    #[wasm_bindgen(constructor)]
    pub fn new(config: Config) -> Self {
        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
        Context {
            config,
            ..Default::default()
        }
    }
    #[wasm_bindgen]
    pub fn compile(&mut self, src: String) {
        let mut ctx = get_default_context();
        let mut driver = LocalBufferDriver::new(self.config.buffer_size as usize);
        ctx.add_plugin(driver.get_as_plugin());

        if let Err(e) = ctx.prepare_machine(src.as_str()) {
            report(&src, "".to_symbol(), &e);
        }
        ctx.run_main();
        let iochannels = driver.init(
            ctx,
            Some(mimium_audiodriver::driver::SampleRate::from(
                self.config.sample_rate as u32,
            )),
        );
        let (ichannels, ochannels) = iochannels.map_or((0, 0), |io| (io.input, io.output));
        self.config.input_channels = ichannels;
        self.config.output_channels = ochannels;
        let out_ch = self.config.output_channels;
        let mut out_buf = vec![0.0; (out_ch * self.config.buffer_size) as usize];
        self.processor = Some(Box::new(move |_input, output: Output| -> u64 {
            driver.play();
            driver
                .get_generated_samples()
                .iter()
                .map(|f| *f as f32)
                .enumerate()
                .for_each(|(i, f)| {
                    out_buf[i] = f;
                });
            output.copy_from_slice(&out_buf);
            0
        }));
    }
    #[wasm_bindgen]
    pub fn get_input_channels(&self) -> u32 {
        self.config.input_channels
    }
    #[wasm_bindgen]
    pub fn get_output_channels(&self) -> u32 {
        self.config.output_channels
    }
    /// .
    ///
    /// # Safety
    /// Array size of input and output must be equal to `input_channels * buffer_size` and `output_channels * buffer_size` respectively.
    /// .
    #[wasm_bindgen]
    pub fn process(&mut self, input: &[f32], output: &mut [f32]) -> u64 {
        unsafe { self.processor.as_mut().unwrap_unchecked()(input, output) }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_iochannels() {
        let mut ctx = Context::new(Config::default());
        ctx.compile(
            r#"fn dsp(input:float){
        (0,input)
        }"#
            .to_string(),
        );
        assert_eq!(1, ctx.get_input_channels());
        assert_eq!(2, ctx.get_output_channels());
    }
}