You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
martian-chess/src/audio.rs

178 lines
5.3 KiB
Rust

///
/// TODO: Custom Asset: FmodEventMapper
///
use crate::prelude::*;
use bevy_fmod::prelude::AudioSource;
use bevy_fmod::prelude::*;
use serde::Deserialize;
use bevy::{
asset::{AssetLoader, LoadContext, LoadedAsset},
prelude::*,
reflect::{TypePath, TypeUuid},
utils::BoxedFuture,
};
pub(crate) struct AudioPlugin;
impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.add_event::<AudioEvent>();
app.add_systems(OnEnter(GameState::Loading), load_tweakfile);
app.add_plugins(FmodPlugin {
audio_banks_paths: &[
"./assets/audio/Martian Chess Audio/Build/Desktop/Master.bank",
"./assets/audio/Martian Chess Audio/Build/Desktop/Master.strings.bank",
"./assets/audio/Martian Chess Audio/Build/Desktop/Music.bank",
"./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank",
],
});
app.add_asset::<AudioSettings>().init_asset_loader::<AudioSettingsLoader>();
app.add_systems(OnEnter(GameState::Menu), play_background);
app.add_systems(
Update,
(
play_audio.run_if(any_component_added::<AudioSource>),
audio_trigger.run_if(on_event::<AudioEvent>()),
// play_background.run_if(on_event::<AssetEvent<AudioSettings>>()), TODO: Handle music hot-reload
),
);
}
}
#[derive(Event, Debug)]
pub enum AudioEvent {
MainMusic,
MenuSelect,
PickUp,
PutDown,
Idle,
Invalid,
}
#[derive(Resource)]
struct AudioTweakfile(Handle<AudioSettings>);
fn load_tweakfile(
server: Res<AssetServer>,
mut commands: Commands,
) {
let handle: Handle<AudioSettings> = server.load("audio/martian.audio.tweak");
commands.insert_resource(AudioTweakfile(handle));
}
fn play_background(mut events: EventWriter<AudioEvent>) {
events.send(AudioEvent::MainMusic);
}
fn play_audio(mut events: Query<&AudioSource, Added<AudioSource>>) {
events.iter_mut().for_each(|aud| aud.play());
}
fn audio_trigger(
mut events: EventReader<AudioEvent>,
studio: Res<FmodStudio>,
mut commands: Commands,
server: Res<AssetServer>,
audio_settings: Res<Assets<AudioSettings>>,
display_state: Res<State<DisplayState>>,
) {
let tweak = audio_settings.get(&server.load("audio/martian.audio.tweak")).expect("Load audio tweakfile");
debug!("Audio tweaks: {:?}", tweak);
let state = display_state.get();
events.iter().for_each(|event| {
let aud = match event {
AudioEvent::MainMusic => tweak.music.main.clone(),
AudioEvent::MenuSelect => tweak.menu.select.clone(),
AudioEvent::PickUp => match state {
DisplayState::Display2d => tweak.display2d.pick_up.clone(),
DisplayState::Display3d => tweak.display3d.pick_up.clone(),
},
AudioEvent::PutDown => match state {
DisplayState::Display2d => tweak.display2d.put_down.clone(),
DisplayState::Display3d => tweak.display3d.put_down.clone(),
},
AudioEvent::Idle => match state {
DisplayState::Display2d => tweak.display2d.idle.clone(),
DisplayState::Display3d => tweak.display3d.idle.clone(),
},
AudioEvent::Invalid => match state {
DisplayState::Display2d => tweak.display2d.invalid.clone(),
DisplayState::Display3d => tweak.display3d.invalid.clone(),
},
};
if !aud.is_empty() {
let event_str = format!("event:{}", aud);
if let Ok(event_description) = studio.0.get_event(event_str.as_str()) {
info!("Playing FMOD {:?}", event_str);
commands.spawn(AudioSource::new(event_description));
} else {
warn!("Music not found for {:?}", event_str);
}
} else {
warn!("No music set for {:?} in {:?}", event, state);
}
});
}
#[derive(Debug, Deserialize, TypeUuid, TypePath)]
#[uuid = "5c7a67ee-56a9-4fe9-9e91-1a709fbc3cf0"]
pub(crate) struct AudioSettings {
#[serde(default)]
display2d: PlaySettings,
#[serde(default)]
display3d: PlaySettings,
#[serde(default)]
music: MusicSettings,
#[serde(default)]
menu: MenuSettings,
}
#[derive(Debug, Deserialize, Default)]
struct MusicSettings {
#[serde(default)]
main: String,
}
#[derive(Debug, Deserialize, Default)]
struct MenuSettings {
#[serde(default)]
select: String,
}
#[derive(Debug, Deserialize, Default)]
struct PlaySettings {
#[serde(default)]
pick_up: String,
#[serde(default)]
put_down: String,
#[serde(default)]
idle: String,
#[serde(default)]
invalid: String,
}
#[derive(Default)]
pub struct AudioSettingsLoader;
impl AssetLoader for AudioSettingsLoader {
fn load<'a>(
&'a self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> {
Box::pin(async move {
let s = std::str::from_utf8(bytes)?;
let custom_asset = toml::from_str::<AudioSettings>(s)?;
load_context.set_default_asset(LoadedAsset::new(custom_asset));
Ok(())
})
}
fn extensions(&self) -> &[&str] {
&["audio.tweak"]
}
}