Skip to content
Snippets Groups Projects
Verified Commit a6c8ae01 authored by Louis's avatar Louis :fire:
Browse files

Initial commit; Copy through from advent code base

parent 9c3adce4
No related branches found
No related tags found
No related merge requests found
...@@ -6,3 +6,6 @@ Cargo.lock ...@@ -6,3 +6,6 @@ Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
# IDE Files
.idea
[package]
name = "micro_bevy_musicbox"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy = { version = "0.7", default-features = false }
bevy_kira_audio = { version = "0.10", features = ["mp3"] }
serde = "1"
serde_json = "1"
\ No newline at end of file
[toolchain]
channel = "nightly"
\ No newline at end of file
hard_tabs = true
group_imports = "StdExternalCrate"
use_field_init_shorthand = true
use_try_shorthand = true
\ No newline at end of file
/// The first channel to use for main music tracks. Combined with
/// `music audio channel b` to perform cross-fades
/// *Volume Type:* Music
pub struct MusicAudioChannelA;
/// The backup channel to use for main music tracks. Combined with
/// `music audio channel a` to perform cross-fades
/// *Volume Type:* Music
pub struct MusicAudioChannelB;
/// The first channel to use for background ambiance tracks.
/// Combined with `ambiance audio channel b` to perform cross-fades
/// *Volume Type:* SFX
pub struct AmbianceAudioChannelA;
/// The backup channel to use for background ambiance tracks.
/// Combined with `ambiance audio channel a` to perform cross-fades
/// *Volume Type:* SFX
pub struct AmbianceAudioChannelB;
/// The channel used for generic sound effects, such as spells, hits,
/// attacks, etc.
/// *Volume Type:* SFX
pub struct SfxAudioChannel;
/// The channel used for any UI related sound effects
/// *Volume Type:* UI SFX
pub struct UiSfxAudioChannel;
use bevy::app::{App, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_kira_audio::{AudioApp, AudioPlugin};
use crate::channels::{
AmbianceAudioChannelA, AmbianceAudioChannelB, MusicAudioChannelA, MusicAudioChannelB,
SfxAudioChannel, UiSfxAudioChannel,
};
use crate::utilities::{AudioCrossFade, AudioSettings};
pub mod channels;
pub mod music_box;
pub mod utilities;
pub mod prelude {
pub use super::channels::*;
pub use super::music_box::MusicBox;
pub use bevy_kira_audio::{AudioSource, AudioChannel};
}
pub struct MusicBoxPlugin;
impl Plugin for MusicBoxPlugin {
fn build(&self, app: &mut App) {
app.add_audio_channel::<MusicAudioChannelA>()
.add_audio_channel::<MusicAudioChannelB>()
.add_audio_channel::<AmbianceAudioChannelA>()
.add_audio_channel::<AmbianceAudioChannelB>()
.add_audio_channel::<SfxAudioChannel>()
.add_audio_channel::<UiSfxAudioChannel>()
.insert_resource(AudioSettings::default())
.insert_resource(AudioCrossFade::default());
}
}
pub struct CombinedAudioPlugins;
impl PluginGroup for CombinedAudioPlugins {
fn build(&mut self, group: &mut PluginGroupBuilder) {
group.add(AudioPlugin)
.add(MusicBoxPlugin);
}
}
\ No newline at end of file
use std::marker::PhantomData;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use bevy_kira_audio::{AudioChannel, InstanceHandle};
use crate::utilities::{AudioSettings, CrossFadeTrack, SuppliesAudio, TrackType};
use crate::{
AmbianceAudioChannelA, AmbianceAudioChannelB, AudioCrossFade, MusicAudioChannelA,
MusicAudioChannelB, SfxAudioChannel, UiSfxAudioChannel,
};
#[derive(SystemParam)]
pub struct AudioChannels<'w, 's> {
pub music_channel_a: Res<'w, AudioChannel<MusicAudioChannelA>>,
pub music_channel_b: Res<'w, AudioChannel<MusicAudioChannelB>>,
pub ambiance_channel_a: Res<'w, AudioChannel<AmbianceAudioChannelA>>,
pub ambiance_channel_b: Res<'w, AudioChannel<AmbianceAudioChannelB>>,
pub sfx_channel: Res<'w, AudioChannel<SfxAudioChannel>>,
pub ui_sfx_channel: Res<'w, AudioChannel<UiSfxAudioChannel>>,
#[system_param(ignore)]
_p: PhantomData<&'s ()>,
}
#[derive(SystemParam)]
pub struct MusicBox<'w, 's, T: SuppliesAudio> {
pub commands: Commands<'w, 's>,
pub channels: AudioChannels<'w, 's>,
pub handles: Res<'w, T>,
pub settings: Res<'w, AudioSettings>,
pub fade_state: Res<'w, AudioCrossFade>,
}
pub enum MusicTrackState {
Pending,
Playing,
FadeOut { progress: f32 },
FadeIn { progress: f32 },
CrossFade { out_progress: f32, in_progress: f32 },
}
impl Default for MusicTrackState {
fn default() -> Self {
Self::Pending
}
}
impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> {
pub fn play_looped_music<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> {
self.channels.music_channel_a.stop();
self.channels.music_channel_b.stop();
match self.resolve_track_name(name) {
TrackType::Single(track) => match self.fade_state.music.active {
CrossFadeTrack::A => Some(
self.channels
.music_channel_a
.play_looped(track.clone_weak()),
),
CrossFadeTrack::B => Some(
self.channels
.music_channel_b
.play_looped(track.clone_weak()),
),
},
TrackType::WithIntro(intro, looped) => match self.fade_state.music.active {
CrossFadeTrack::A => Some(
self.channels
.music_channel_a
.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
),
CrossFadeTrack::B => Some(
self.channels
.music_channel_b
.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
),
},
TrackType::Missing => None,
}
}
pub fn play_effect_once<Name: ToString>(&mut self, name: Name) -> Option<InstanceHandle> {
let name = name.to_string();
match self.handles.get_audio_track(&name) {
Some(track) => Some(self.channels.sfx_channel.play(track)),
None => None,
}
}
pub fn play_ambiance<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> {
self.channels.ambiance_channel_a.stop();
self.channels.ambiance_channel_b.stop();
match self.resolve_track_name(name) {
TrackType::Single(track) => match self.fade_state.ambiance.active {
CrossFadeTrack::A => Some(
self.channels
.ambiance_channel_a
.play_looped(track.clone_weak()),
),
CrossFadeTrack::B => Some(
self.channels
.ambiance_channel_b
.play_looped(track.clone_weak()),
),
},
TrackType::WithIntro(intro, looped) => match self.fade_state.ambiance.active {
CrossFadeTrack::A => Some(
self.channels
.ambiance_channel_a
.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
),
CrossFadeTrack::B => Some(
self.channels
.ambiance_channel_b
.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
),
},
TrackType::Missing => None,
}
}
}
use bevy::ecs::system::Resource;
use bevy::prelude::*;
use bevy_kira_audio::AudioSource;
use serde::{Deserialize, Serialize};
use crate::music_box::MusicBox;
pub trait SuppliesAudio: Resource {
fn get_audio_track<T: ToString>(&self, name: T) -> Option<Handle<AudioSource>>;
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct AudioSettings {
master_volume: f32,
music_volume: f32,
sfx_volume: f32,
ui_volume: f32,
}
impl Default for AudioSettings {
fn default() -> Self {
Self {
master_volume: 1.0,
music_volume: 0.0,
sfx_volume: 0.0,
ui_volume: 0.0,
}
}
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub enum CrossFadeTrack {
#[default]
A,
B,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct CrossFadeState {
pub active: CrossFadeTrack,
pub next: CrossFadeTrack,
pub progress: f32,
}
impl Default for CrossFadeState {
fn default() -> Self {
Self {
active: CrossFadeTrack::A,
next: CrossFadeTrack::B,
progress: 0.0,
}
}
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct AudioCrossFade {
pub music: CrossFadeState,
pub ambiance: CrossFadeState,
}
pub enum TrackType {
Single(Handle<AudioSource>),
WithIntro(Handle<AudioSource>, Handle<AudioSource>),
Missing,
}
impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> {
pub fn resolve_track_name<Name: ToString>(&'w self, name: Name) -> TrackType {
let name = name.to_string();
if let (Some(intro), Some(looped)) = (
self.handles.get_audio_track(format!("{}_intro", &name)),
self.handles.get_audio_track(format!("{}_loop", &name)),
) {
TrackType::WithIntro(intro, looped)
} else if let Some(track) = self.handles.get_audio_track(name.clone()) {
TrackType::Single(track)
} else if let Some(track) = self.handles.get_audio_track(format!("{}_looped", name)) {
TrackType::Single(track)
} else {
TrackType::Missing
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment