#![warn(missing_docs)] //! A plugin which adds MIDI file and soundfont audio support to the [bevy](https://crates.io/crates/bevy) engine via [rustysynth](https://crates.io/crates/rustysynth). #[cfg(all(feature = "bevy_audio", feature = "kira"))] compile_error!("Cannot compile with both bevy_audio and kira features enabled simultaneously. Please disable one of these features"); #[cfg(feature = "bevy_audio")] use bevy::audio::AddAudioSource; use bevy::prelude::*; use lazy_static::lazy_static; use rustysynth::SoundFont; #[cfg(feature = "hl4mgm")] use std::io::Cursor; use std::{ fs::{self, File}, io::Read, path::PathBuf, sync::{Arc, Mutex}, }; mod assets; pub use assets::*; #[cfg(feature = "hl4mgm")] pub(crate) static HL4MGM: &[u8] = include_bytes!("./embedded_assets/hl4mgm.sf2"); lazy_static! { pub(crate) static ref DEFAULT_SOUNDFONT: Arc>>> = Arc::new(Mutex::new(None)); pub(crate) static ref SOUNDFONT: Arc>>> = Arc::new(Mutex::new(None)); } #[derive(SystemSet, Hash, Clone, PartialEq, Eq, Debug)] pub enum RustySynthSet { Setup, Update, } /// This plugin configures the soundfont used for playback and registers MIDI assets. #[derive(Debug)] pub struct RustySynthPlugin { /// Reader for soundfont data. pub soundfont: R, } #[cfg(feature = "hl4mgm")] impl Default for RustySynthPlugin> { fn default() -> Self { Self { soundfont: Cursor::new(HL4MGM), } } } impl Plugin for RustySynthPlugin { fn build(&self, app: &mut App) { *DEFAULT_SOUNDFONT.lock().unwrap() = Some(Arc::new( SoundFont::new(&mut self.soundfont.clone()).unwrap(), )); info!("Setting Soundfont Initially"); *SOUNDFONT.lock().unwrap() = DEFAULT_SOUNDFONT.lock().unwrap().clone(); app.init_asset_loader::() .add_event::() .add_systems(Startup, handle_set_soundfont.in_set(RustySynthSet::Setup)) .add_systems(Update, handle_set_soundfont.in_set(RustySynthSet::Update)); #[cfg(feature = "bevy_audio")] app.init_asset::() .add_audio_source::(); } } pub(crate) fn set_soundfont(mut reader: R) { info!("Setting Soundfont"); *SOUNDFONT.lock().unwrap() = Some(Arc::new(SoundFont::new(&mut reader).unwrap())); } /// Event for setting the soundfont after initialization /// This will not affect sounds which have already been rendered #[derive(Event)] pub enum SetSoundfontEvent { /// Load soundfont from bytes Bytes(Vec), /// Load soundfont at path Path(PathBuf), /// Load default soundfont Default, } fn handle_set_soundfont(mut event_reader: EventReader) { for event in event_reader.read() { match event { SetSoundfontEvent::Bytes(items) => { set_soundfont(Cursor::new(items.clone())); } SetSoundfontEvent::Path(path_buf) => { set_soundfont(File::open(path_buf).unwrap()); } SetSoundfontEvent::Default => { *SOUNDFONT.lock().unwrap() = DEFAULT_SOUNDFONT.lock().unwrap().clone(); } } } }