Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to set a Global Volume #7706

Merged
merged 19 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 72 additions & 6 deletions crates/bevy_audio/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ where
/// ```
/// # use bevy_ecs::system::Res;
/// # use bevy_asset::AssetServer;
/// # use bevy_audio::Audio;
/// # use bevy_audio::{Audio, Volume};
/// # use bevy_audio::PlaybackSettings;
/// fn play_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
/// audio.play_with_settings(
/// asset_server.load("my_sound.ogg"),
/// PlaybackSettings::LOOP.with_volume(0.75),
/// PlaybackSettings::LOOP.with_volume(Volume::new_relative(0.75)),
/// );
/// }
/// ```
Expand All @@ -121,13 +121,61 @@ where
}
}

/// Defines the volume to play an audio source at.
#[derive(Clone, Debug)]
pub enum Volume {
/// A volume level relative to the global volume.
Relative(VolumeLevel),
/// A volume level that ignores the global volume.
Absolute(VolumeLevel),
}

impl Default for Volume {
fn default() -> Self {
Self::Relative(VolumeLevel::default())
}
}

impl Volume {
/// Create a new volume level relative to the global volume.
pub fn new_relative(volume: f32) -> Self {
Self::Relative(VolumeLevel::new(volume))
}
/// Create a new volume level that ignores the global volume.
pub fn new_absolute(volume: f32) -> Self {
Self::Absolute(VolumeLevel::new(volume))
}
}

/// A volume level equivalent to a positive only float.
#[derive(Clone, Copy, Debug)]
pub struct VolumeLevel(pub(crate) f32);

impl Default for VolumeLevel {
fn default() -> Self {
Self(1.0)
}
}

impl VolumeLevel {
/// Create a new volume level.
pub fn new(volume: f32) -> Self {
debug_assert!(volume >= 0.0);
Self(volume)
}
/// Get the value of the volume level.
pub fn get(&self) -> f32 {
self.0
}
}

/// Settings to control playback from the start.
#[derive(Clone, Debug)]
pub struct PlaybackSettings {
/// Play in repeat
pub repeat: bool,
/// Volume to play at.
pub volume: f32,
pub volume: Volume,
/// Speed to play at.
pub speed: f32,
}
Expand All @@ -142,19 +190,19 @@ impl PlaybackSettings {
/// Will play the associate audio source once.
pub const ONCE: PlaybackSettings = PlaybackSettings {
repeat: false,
volume: 1.0,
volume: Volume::Relative(VolumeLevel(1.0)),
speed: 1.0,
};

/// Will play the associate audio source in a loop.
pub const LOOP: PlaybackSettings = PlaybackSettings {
repeat: true,
volume: 1.0,
volume: Volume::Relative(VolumeLevel(1.0)),
speed: 1.0,
};

/// Helper to set the volume from start of playback.
pub const fn with_volume(mut self, volume: f32) -> Self {
pub const fn with_volume(mut self, volume: Volume) -> Self {
self.volume = volume;
self
}
Expand Down Expand Up @@ -188,3 +236,21 @@ where
.finish()
}
}

/// Use this [`Resource`] to control the global volume of all audio with a [`Volume::Relative`] volume.
///
/// Keep in mind that changing this value will not affect already playing audio.
#[derive(Resource, Default, Clone, Copy)]
pub struct GlobalVolume {
/// The global volume of all audio.
pub volume: VolumeLevel,
}

impl GlobalVolume {
/// Create a new [`GlobalVolume`] with the given volume.
pub fn new(volume: f32) -> Self {
Self {
volume: VolumeLevel::new(volume),
}
}
}
12 changes: 9 additions & 3 deletions crates/bevy_audio/src/audio_output.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Audio, AudioSource, Decodable};
use crate::{Audio, AudioSource, Decodable, GlobalVolume, Volume};
use bevy_asset::{Asset, Assets};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_reflect::TypeUuid;
Expand Down Expand Up @@ -70,6 +70,7 @@ where
audio_sources: &Assets<Source>,
audio: &mut Audio<Source>,
sinks: &mut Assets<AudioSink>,
global_volume: &GlobalVolume,
) {
let mut queue = audio.queue.write();
let len = queue.len();
Expand All @@ -79,7 +80,11 @@ where
if let Some(audio_source) = audio_sources.get(&config.source_handle) {
if let Some(sink) = self.play_source(audio_source, config.settings.repeat) {
sink.set_speed(config.settings.speed);
sink.set_volume(config.settings.volume);

match config.settings.volume {
Volume::Relative(vol) => sink.set_volume(vol.0 * global_volume.volume.0),
Volume::Absolute(vol) => sink.set_volume(vol.0),
}

// don't keep the strong handle. there is no way to return it to the user here as it is async
let _ = sinks.set(config.sink_handle, AudioSink { sink: Some(sink) });
Expand All @@ -97,11 +102,12 @@ where
pub fn play_queued_audio_system<Source: Asset + Decodable>(
audio_output: Res<AudioOutput<Source>>,
audio_sources: Option<Res<Assets<Source>>>,
global_volume: Res<GlobalVolume>,
mut audio: ResMut<Audio<Source>>,
mut sinks: ResMut<Assets<AudioSink>>,
) {
if let Some(audio_sources) = audio_sources {
audio_output.try_play_queued(&*audio_sources, &mut *audio, &mut sinks);
audio_output.try_play_queued(&*audio_sources, &mut *audio, &mut sinks, &global_volume);
};
}

Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_audio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! App::new()
//! .add_plugins(MinimalPlugins)
//! .add_plugin(AssetPlugin::default())
//! .add_plugin(AudioPlugin)
//! .add_plugin(AudioPlugin::default())
//! .add_startup_system(play_background_audio)
//! .run();
//! }
Expand All @@ -29,7 +29,7 @@ mod audio_source;
#[allow(missing_docs)]
pub mod prelude {
#[doc(hidden)]
pub use crate::{Audio, AudioOutput, AudioSource, Decodable, PlaybackSettings};
pub use crate::{Audio, AudioOutput, AudioSource, Decodable, GlobalVolume, PlaybackSettings};
}

pub use audio::*;
Expand All @@ -48,14 +48,18 @@ use bevy_ecs::prelude::*;
///
/// Use the [`Audio`] resource to play audio.
#[derive(Default)]
pub struct AudioPlugin;
pub struct AudioPlugin {
/// The global volume for all audio sources with a [`Volume::Relative`] volume.
global_volume: GlobalVolume,
}

impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<AudioOutput<AudioSource>>()
.add_asset::<AudioSource>()
.add_asset::<AudioSink>()
.init_resource::<Audio<AudioSource>>()
.insert_resource(self.global_volume)
.add_system(play_queued_audio_system::<AudioSource>.in_base_set(CoreSet::PostUpdate));

#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]
Expand Down