1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Add audio support to Bevy through the [`kira`] crate.
//!
//! This crate aims at creating a replacement for `bevy_audio` by instead integrating Kira, a crate
//! for audio playback aimed at games, and used in several other Rust projects.
//!
//! This particular crate is an experiment in making a component-based ECS API, instead of a
//! resource-based approach, currently taken by `bevy_kira_audio`.
//!
//! To get started playing sounds, insert an [`AudioBundle`](prelude::AudioBundle) on an entity.
//! This is a generic bundle which supports any compatible sound source. An implementation over
//! audio files is provided, with streaming support, using the
//! [`AudioFileBundle`](prelude::AudioFileBundle) type alias.
//!
//! When an [`AudioFile`](prelude::AudioFile) is inserted, its playback will begin right away. To
//! prevent that, use the [`start_paused`](prelude::AudioFileSettings) field from
//! [`AudioFileBundle::settings`](prelude::AudioFileBundle::settings) and set it to false.
//!
//! The audio system creates an [`AudioHandle`](prelude::AudioHandle) component when registering an
//! added sound for
//! playback. This handle allows you to control the sound. For [`AudioFile`](prelude::AudioFile)s,
//! this means pausing/resuming, setting the volume, panning, and playback rate of the sound.
//!
//! Spatial audio is supported built-in with the [`SpatialEmitter`](prelude::SpatialEmitter)
//! component, which tells the plugin to add the entity as an emitter, provided it also has a
//! [`GlobalTransform`] component attached. Its settings control the behavior of the spatial effect.
//!
//! ## Example
//!
//! ```no_run
//! use bevy::prelude::*;
//! use bevy_kira_components::prelude::*;
//!
//! fn main() {
//!     App::new()
//!         .insert_non_send_resource(AudioSettings {
//!             // Only needed for tests
//!             backend_settings: AudioBackendSelector::Mock { sample_rate: 48000, },
//!             ..default()
//!         })
//!         .add_plugins((DefaultPlugins, AudioPlugin))
//!         .add_systems(Startup, add_sound)
//!         .run();
//! }
//!
//! fn add_sound(mut commands: Commands, asset_server: Res<AssetServer>) {
//!     commands.spawn(AudioFileBundle {
//!         source: asset_server.load("my_sound.ogg"),
//!         ..default()
//!     });
//! }
//! ```
#![warn(missing_docs)]

use bevy::prelude::*;
use bevy::transform::TransformSystem;
pub use kira;
use kira::manager::{AudioManager, AudioManagerSettings};

use crate::backend::AudioBackend;
use crate::sources::audio_file::AudioFilePlugin;
use crate::spatial::SpatialAudioPlugin;

mod backend;
pub mod diagnostics;
pub mod sources;
pub mod spatial;

#[doc(hidden)]
#[allow(missing_docs)]
pub mod prelude {
    pub use super::{AudioPlaybackSet, AudioPlugin, AudioSettings, AudioWorld};
    pub use crate::backend::*;
    pub use crate::sources::prelude::*;
    pub use crate::spatial::prelude::*;
}

/// Type of settings for the audio engine. Insert it as a resource before adding the plugin to
/// change the default settings.
pub type AudioSettings = AudioManagerSettings<AudioBackend>;

/// System set used in grouping systems that setup audio sources. Used in the
/// [`AudioSourcePlugin`](prelude::AudioSourcePlugin)'s systems. Useful to place systems right
/// after to be able to react to added audio assets.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, SystemSet)]
pub struct AudioSourceSetup;

/// General audio system set, used by the systems in this plugin.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, SystemSet)]
pub enum AudioPlaybackSet {
    /// Systems related to setting up audio sources, registering them with the audio engine, and
    /// bookkeeping handles.
    Setup,
    /// Systems related to keeping the audio engine in sync with Bevy's world. Mainly used by the
    /// spatial systems to copy position and rotation information from listeners and emitters.
    Update,
    /// Systems that clean up resources. Handles that have finished playing or are no longer
    /// useful, are removed automatically here.
    Cleanup,
}

/// Adds audio to Bevy games via the [`kira`] crate.
#[derive(Debug, Default)]
pub struct AudioPlugin;

impl Plugin for AudioPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<AudioWorld>()
            .add_plugins((
                #[cfg(feature = "diagnostics")]
                diagnostics::KiraStatisticsDiagnosticPlugin,
                SpatialAudioPlugin,
                AudioFilePlugin,
            ))
            .configure_sets(PreUpdate, AudioPlaybackSet::Setup)
            .configure_sets(
                PostUpdate,
                AudioPlaybackSet::Update.after(TransformSystem::TransformPropagate),
            );
    }
}

/// Main resource holding all the bookkeeping necessary to link the ECS data to the audio engine.
#[derive(Resource)]
pub struct AudioWorld {
    pub(crate) audio_manager: AudioManager<AudioBackend>,
}

impl FromWorld for AudioWorld {
    fn from_world(world: &mut World) -> Self {
        let audio_manager_settings = world
            .remove_non_send_resource::<AudioSettings>()
            .unwrap_or_default();
        let audio_manager =
            AudioManager::new(audio_manager_settings).expect("Cannot create audio backend");
        Self { audio_manager }
    }
}

#[derive(Component)]
#[doc(hidden)]
/// Internal marker for entities with audio components. Needed to be able to query in a
/// non-generic way for having added audio support through the [`AudioBundle`] struct.
pub struct InternalAudioMarker;