use std::sync::Arc;
use bevy::prelude::*;
use firewheel::{
channel_config::{ChannelConfig, ChannelCount},
event::ProcEvents,
node::{
AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, EmptyConfig,
ProcBuffers, ProcExtra, ProcInfo, ProcessStatus,
},
};
use midix::prelude::*;
mod channel_node;
mod plugin;
use midix_synth::prelude::{SoundFont, Synthesizer, SynthesizerSettings};
pub(super) use plugin::plugin;
#[derive(Debug, Component, TypePath)]
pub struct MidiSynthNode<C: Clone = ()> {
pub soundfont: Arc<SoundFont>,
pub enable_reverb_and_chorus: bool,
pub channel: C,
}
impl<C: Clone> Clone for MidiSynthNode<C> {
fn clone(&self) -> Self {
Self {
soundfont: Arc::clone(&self.soundfont),
enable_reverb_and_chorus: self.enable_reverb_and_chorus,
channel: self.channel.clone(),
}
}
}
impl MidiSynthNode {
pub fn new(soundfont: Arc<SoundFont>, enable_reverb_and_chorus: bool) -> Self {
Self {
soundfont,
enable_reverb_and_chorus,
channel: (),
}
}
}
impl AudioNode for MidiSynthNode {
type Configuration = EmptyConfig;
fn info(&self, _config: &Self::Configuration) -> AudioNodeInfo {
AudioNodeInfo::new()
.debug_name("MIDI Synthesizer")
.channel_config(ChannelConfig {
num_inputs: ChannelCount::ZERO,
num_outputs: ChannelCount::STEREO,
})
}
fn construct_processor(
&self,
_config: &Self::Configuration,
cx: ConstructProcessorContext,
) -> impl AudioNodeProcessor {
MidiSynthProcessor::new(self, cx)
}
}
pub struct MidiSynthProcessor<C = ()> {
pub(crate) synthesizer: Synthesizer,
pub(crate) channel: C,
}
impl MidiSynthProcessor {
pub fn new(config: &MidiSynthNode, cx: ConstructProcessorContext) -> Self {
let mut settings = SynthesizerSettings::new(cx.stream_info.sample_rate.get() as i32);
settings.enable_reverb_and_chorus = config.enable_reverb_and_chorus;
let synthesizer = Synthesizer::new(config.soundfont.clone(), &settings)
.expect("Failed to create synthesizer");
Self {
synthesizer,
channel: (),
}
}
}
impl<C> MidiSynthProcessor<C> {
pub fn process_message(&mut self, command: ChannelVoiceMessage) {
self.synthesizer.process_midi_message(command);
}
}
impl AudioNodeProcessor for MidiSynthProcessor {
fn process(
&mut self,
info: &ProcInfo,
ProcBuffers { outputs, .. }: ProcBuffers,
events: &mut ProcEvents,
_extra: &mut ProcExtra,
) -> ProcessStatus {
for event in events.drain() {
if let Some(message) = event.downcast_ref::<ChannelVoiceMessage>() {
self.process_message(*message);
}
}
let frames = info.frames;
let (left, right) = outputs.split_at_mut(1);
self.synthesizer
.render(&mut left[0][..frames], &mut right[0][..frames]);
ProcessStatus::outputs_not_silent()
}
}