#![doc = r#"
Synthesizer resources, setup and plugins
"#]
use crate::prelude::SoundFont;
use bevy::prelude::*;
use midix::prelude::ChannelVoiceMessage;
use midix_synth::prelude::*;
use std::sync::{Arc, Mutex};
use tinyaudio::{OutputDevice, OutputDeviceParameters};
mod plugin;
pub use plugin::*;
enum SynthState {
NotLoaded,
LoadHandle { sound_font: Handle<SoundFont> },
Loaded(Arc<Mutex<Synthesizer>>),
}
#[derive(Resource)]
pub struct Synth {
params: OutputDeviceParameters,
synthesizer: SynthState,
_device: Option<Mutex<OutputDevice>>,
}
impl Synth {
pub fn new(params: SynthParams) -> Self {
Self {
params: OutputDeviceParameters {
channels_count: params.channel_count,
sample_rate: params.sample_rate,
channel_sample_count: params.channel_sample_count,
},
..Default::default()
}
}
pub fn handle_event(&self, event: ChannelVoiceMessage) {
let SynthState::Loaded(synth) = &self.synthesizer else {
error!("An event was passed to the synth, but the soundfont has not been loaded!");
return;
};
let mut synth = synth.lock().unwrap();
let channel = event.channel().to_byte() as i32;
let command = (event.status() & 0xF0) as i32;
let data1 = event.data_1_byte() as i32;
let data2 = event.data_2_byte().unwrap_or(0) as i32;
synth.process_midi_message(channel, command, data1, data2);
}
pub fn is_ready(&self) -> bool {
matches!(self.synthesizer, SynthState::Loaded(_))
}
pub fn use_soundfont(&mut self, sound_font: Handle<SoundFont>) {
self.synthesizer = SynthState::LoadHandle { sound_font };
self._device = None;
}
}
impl Default for Synth {
fn default() -> Self {
let params = OutputDeviceParameters {
channels_count: 2,
sample_rate: 44100,
channel_sample_count: 441,
};
Self {
params,
synthesizer: SynthState::NotLoaded,
_device: None,
}
}
}