diff --git a/Cargo.toml b/Cargo.toml index 0047fb33a2f30430f30120569b157a196d76f8dd..8dfe121acc8037fd3afba01b1aa0e8ed321c3eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "micro_musicbox" -version = "0.7.0" +version = "0.8.0" edition = "2021" license = "Apache-2.0" authors = ["Louis Capitanchik <louis@microhacks.co.uk>"] @@ -13,26 +13,32 @@ exclude = ["assets"] [[example]] name = "kitchen-sink" path = "examples/kitchen_sink.rs" -required-features = ["mp3"] +required-features = ["mp3", "ogg"] [[example]] name = "basic" path = "examples/basic.rs" required-features = ["mp3"] +[[example]] +name = "from-settings" +path = "examples/from-settings.rs" +required-features = ["ogg", "settings"] + [features] -default = [] +default = ["settings"] serde = ["dep:serde"] mp3 = ["bevy_kira_audio/mp3"] flac = ["bevy_kira_audio/flac"] wav = ["bevy_kira_audio/wav"] ogg = ["bevy_kira_audio/ogg"] +settings = ["bevy_kira_audio/settings_loader"] [dependencies] -bevy = { version = "0.11.0", default-features = false } -bevy_kira_audio = { version = "0.16.0", default-features = false } +bevy = { version = "0.12", default-features = false } +bevy_kira_audio = { version = "0.18.0", default-features = false } serde = { version = "1", optional = true } [dev_dependencies] -bevy = "0.11.0" +bevy = "0.12" log = "0.4" diff --git a/README.md b/README.md index 5a7feac8950a179c5ffdd787e5fceac930a24612..6304318f657192151025e910a381937c8d8cb780 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ The examples in this repository use assets available under the following license | musicbox version | bevy version | bka version | |------------------|--------------|-------------| +| 0.8 | 0.12 | 0.18 | | 0.7 | 0.11 | 0.16 | | 0.6 | 0.10 | 0.15 | | 0.5 | 0.9 | 0.13 | diff --git a/assets/The-White-Kitty.mp3 b/assets/The-White-Kitty.mp3 deleted file mode 100644 index a8424cb0ab99e3ffc9045cd9cdd004fa32af1071..0000000000000000000000000000000000000000 --- a/assets/The-White-Kitty.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7804ea15d05840abfb0240455266d4ab1269dce99a12c8df846a97ed14131600 -size 1552452 diff --git a/assets/The-White-Kitty.ogg b/assets/The-White-Kitty.ogg new file mode 100644 index 0000000000000000000000000000000000000000..eaa7e3f93956103480cbf5a188fdb81e2b1c8963 Binary files /dev/null and b/assets/The-White-Kitty.ogg differ diff --git a/assets/The-White-Kitty.ogg.ron b/assets/The-White-Kitty.ogg.ron new file mode 100644 index 0000000000000000000000000000000000000000..67ea4a3daf1a6ff60e40217066069b23537af442 --- /dev/null +++ b/assets/The-White-Kitty.ogg.ron @@ -0,0 +1,5 @@ +( + file: "The-White-Kitty.ogg", + playback_region: Region(start: Seconds(10), end: Custom(Seconds(20))), + loop_region: Some(Region(start: Seconds(10), end: Custom(Seconds(20)))), +) \ No newline at end of file diff --git a/examples/from-settings.rs b/examples/from-settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..069da6d26c921d2d185f572bf419932f63eb6480 --- /dev/null +++ b/examples/from-settings.rs @@ -0,0 +1,23 @@ +use bevy::prelude::*; +use bevy::window::WindowResolution; +use micro_musicbox::prelude::*; +use micro_musicbox::CombinedAudioPlugins; + +fn main() { + App::new() + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + resolution: WindowResolution::new(800.0, 600.0), + title: String::from("Settings File Example"), + ..Default::default() + }), + ..Default::default() + })) + .add_plugins(CombinedAudioPlugins::<AssetServer>::new()) + .add_systems(Startup, play_audio) + .run(); +} + +pub fn play_audio(mut music_box: MusicBox<AssetServer>) { + music_box.play_music_raw("The-White-Kitty.ogg.ron"); +} diff --git a/examples/kitchen_sink.rs b/examples/kitchen_sink.rs index 0f76aee02b69c34aa68901a3a5360c7bc6cb8c38..3f0ca50cef3b09db2b62a3a0fb931d05bd316454 100644 --- a/examples/kitchen_sink.rs +++ b/examples/kitchen_sink.rs @@ -57,7 +57,7 @@ pub fn set_instructions( } pub fn setup_audio(mut commands: Commands, mut musicbox: MusicBox<AssetServer>) { - musicbox.play_music("The-White-Kitty.mp3"); + musicbox.play_music("The-White-Kitty.ogg"); commands.insert_resource(MusicState { playing_first: true, }); @@ -76,7 +76,7 @@ pub fn cross_fade_tracks( ); } else { music_box.cross_fade_music( - "The-White-Kitty.mp3", + "The-White-Kitty.ogg", AudioTween::new(Duration::from_millis(500), AudioEasing::InOutPowf(0.6)), ); } diff --git a/examples/utilities/mod.rs b/examples/utilities/mod.rs index 03cacb46ce96835d856947f247c4d6660e9df8c5..25a143827798edb6ef7beafaf0fbd20f368fa933 100644 --- a/examples/utilities/mod.rs +++ b/examples/utilities/mod.rs @@ -1,4 +1,4 @@ -use bevy::asset::LoadState; +use bevy::asset::{LoadState, RecursiveDependencyLoadState}; use bevy::prelude::*; use bevy_kira_audio::AudioSource; @@ -18,7 +18,7 @@ pub enum AppState { } pub fn load_resources(mut commands: Commands, assets: Res<AssetServer>) { - let white_kitty = assets.load("The-White-Kitty.mp3"); + let white_kitty = assets.load("The-White-Kitty.ogg"); let great_madeja = assets.load("The-Great-Madeja.mp3"); commands.insert_resource(AudioResources { @@ -30,23 +30,22 @@ pub fn load_resources(mut commands: Commands, assets: Res<AssetServer>) { pub fn check_load_state( assets: Res<AssetServer>, resources: Res<AudioResources>, - appstate: Res<State<AppState>>, mut next_state: ResMut<NextState<AppState>>, ) { - let load_state = assets.get_group_load_state(vec![ - resources.white_kitty.id(), - resources.great_madeja.id(), - ]); + let load_list = vec![resources.white_kitty.id(), resources.great_madeja.id()]; - match load_state { - LoadState::Loaded => { - log::info!("STATE {:?}", appstate); - next_state.set(AppState::Running); - } - LoadState::Loading => {} - _ => { - log::error!("The resources are in a bad state! This is a problem"); - } + let loaded = load_list + .iter() + .map(|a| assets.get_load_states(a.untyped())) + .all(|state| match state { + None => false, + Some((own, _, deps)) => { + own == LoadState::Loaded && deps == RecursiveDependencyLoadState::Loaded + } + }); + + if loaded { + next_state.set(AppState::Running); } } @@ -77,7 +76,7 @@ pub fn create_ui(mut commands: Commands, assets: Res<AssetServer>) { text: Text::from_section( "Loading Audio Tracks", TextStyle { - color: Color::BLACK, + color: Color::ANTIQUE_WHITE, font_size: 48.0, font: assets.load("KenneyBlocks.ttf"), }, @@ -90,7 +89,7 @@ pub fn create_ui(mut commands: Commands, assets: Res<AssetServer>) { text: Text::from_section( "...", TextStyle { - color: Color::BLACK, + color: Color::ANTIQUE_WHITE, font_size: 32.0, font: assets.load("KenneyBlocks.ttf"), }, diff --git a/src/music_box.rs b/src/music_box.rs index 3d81edb92be8d8be7d6d30e59c5e8435b060bdff..6e5214e9a2e441582dee1c454a69d47b573fa2cc 100644 --- a/src/music_box.rs +++ b/src/music_box.rs @@ -9,14 +9,11 @@ use crate::{AmbianceAudioChannel, MusicAudioChannel, SfxAudioChannel, UiSfxAudio /// A wrapper for each of the audio channels created and controlled by MusicBox #[derive(SystemParam)] -pub struct AudioChannels<'w, 's> { +pub struct AudioChannels<'w> { pub music_channel: Res<'w, AudioChannel<MusicAudioChannel>>, pub ambiance_channel: Res<'w, AudioChannel<AmbianceAudioChannel>>, pub sfx_channel: Res<'w, AudioChannel<SfxAudioChannel>>, pub ui_sfx_channel: Res<'w, AudioChannel<UiSfxAudioChannel>>, - - #[system_param(ignore)] - _p: PhantomData<&'s ()>, } /// The service object used to control your game's audio @@ -26,8 +23,8 @@ pub struct AudioChannels<'w, 's> { /// The particular implementation of `SuppliesAudio` will be used to verify that a music track exists, /// and then to retrieve the associated `AudioSource`. #[derive(SystemParam)] -pub struct MusicBox<'w, 's, T: SuppliesAudio> { - channels: AudioChannels<'w, 's>, +pub struct MusicBox<'w, T: SuppliesAudio> { + channels: AudioChannels<'w>, handles: Res<'w, T>, settings: ResMut<'w, AudioSettings>, state: ResMut<'w, MusicBoxState>, @@ -46,7 +43,23 @@ pub struct MusicBoxState { pub active_ambiance_name: Option<String>, } -impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> { +impl<'w, T: SuppliesAudio> MusicBox<'w, T> { + /// Get the instance backing the currently active music track, if one is active + pub fn get_music_track(&mut self) -> Option<&mut AudioInstance> { + self.state + .active_music + .as_ref() + .and_then(|hand| self.audio_instances.get_mut(hand)) + } + + /// Get the instance backing the currently active ambiance track, if one is active + pub fn get_ambiance_track(&mut self) -> Option<&mut AudioInstance> { + self.state + .active_ambiance + .as_ref() + .and_then(|hand| self.audio_instances.get_mut(hand)) + } + /// Start playing a new audio track on the Music channel. The provided tween will be used /// to fade in the new track, and to fade out the old track /// @@ -102,6 +115,63 @@ impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> { self.fade_in_music(name, AudioTween::default()) } + /// Start playing a new audio track on the Music channel. No extra configuration will be applied, + /// making this method suitable for audio loaded via a settings file + /// + /// # Returns + /// + /// A handle for the newly started audio instance, or `None` if the track was not found + pub fn play_music_raw<Name: ToString>(&mut self, name: Name) -> Option<Handle<AudioInstance>> { + let name = name.to_string(); + + if self.state.active_music_name == Some(name.to_string()) { + return self.state.active_music.as_ref().map(|f| f.clone_weak()); + } + + match self.map_tracks(&name) { + TrackType::Single(track) => { + self.stop_music(); + let next = self.channels.music_channel.play(track).handle(); + + self.state.active_music_name = Some(name); + self.state.active_music = Some(next.clone_weak()); + + Some(next) + } + _ => None, + } + } + + /// Start playing a new audio track on the Ambiance channel. No extra configuration will be applied, + /// making this method suitable for audio loaded via a settings file + /// + /// # Returns + /// + /// A handle for the newly started audio instance, or `None` if the track was not found + pub fn play_ambiance_raw<Name: ToString>( + &mut self, + name: Name, + ) -> Option<Handle<AudioInstance>> { + let name = name.to_string(); + + if self.state.active_ambiance_name == Some(name.to_string()) { + return self.state.active_ambiance.as_ref().map(|f| f.clone_weak()); + } + + match self.map_tracks(&name) { + TrackType::Single(track) => { + self.stop_ambiance(); + let next = self.channels.ambiance_channel.play(track).handle(); + + self.state.active_ambiance_name = Some(name); + self.state.active_ambiance = Some(next.clone_weak()); + + Some(next) + } + _ => None, + } + } + /// Stop playing music on the Music channel pub fn stop_music(&mut self) { self.fade_out_music(AudioTween::default());