mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
audio: initial (very minimal) audio plugin
This commit is contained in:
parent
af109174dd
commit
3eb393548d
10 changed files with 139 additions and 0 deletions
|
@ -17,6 +17,7 @@ members = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# bevy
|
# bevy
|
||||||
|
bevy_audio = { path = "crates/bevy_audio" }
|
||||||
bevy_app = { path = "crates/bevy_app" }
|
bevy_app = { path = "crates/bevy_app" }
|
||||||
bevy_asset = { path = "crates/bevy_asset" }
|
bevy_asset = { path = "crates/bevy_asset" }
|
||||||
bevy_type_registry = { path = "crates/bevy_type_registry" }
|
bevy_type_registry = { path = "crates/bevy_type_registry" }
|
||||||
|
@ -115,6 +116,10 @@ path = "examples/asset/hot_asset_reloading.rs"
|
||||||
name = "asset_loading"
|
name = "asset_loading"
|
||||||
path = "examples/asset/asset_loading.rs"
|
path = "examples/asset/asset_loading.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "audio"
|
||||||
|
path = "examples/audio/audio.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "custom_diagnostic"
|
name = "custom_diagnostic"
|
||||||
path = "examples/diagnostics/custom_diagnostic.rs"
|
path = "examples/diagnostics/custom_diagnostic.rs"
|
||||||
|
|
BIN
assets/sounds/Windless Slopes.mp3
Normal file
BIN
assets/sounds/Windless Slopes.mp3
Normal file
Binary file not shown.
14
crates/bevy_audio/Cargo.toml
Normal file
14
crates/bevy_audio/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
name = "bevy_audio"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy_app = {path = "../bevy_app"}
|
||||||
|
bevy_asset = {path = "../bevy_asset"}
|
||||||
|
bevy_ecs = {path = "../bevy_ecs"}
|
||||||
|
anyhow = "1.0"
|
||||||
|
rodio = {version = "0.11", default-features = false, features = ["mp3"]}
|
54
crates/bevy_audio/src/audio_output.rs
Normal file
54
crates/bevy_audio/src/audio_output.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::AudioSource;
|
||||||
|
use bevy_asset::{Assets, Handle};
|
||||||
|
use bevy_ecs::Res;
|
||||||
|
use rodio::{Decoder, Device, Sink};
|
||||||
|
use std::{collections::VecDeque, io::Cursor, sync::RwLock};
|
||||||
|
|
||||||
|
pub struct AudioOutput {
|
||||||
|
device: Device,
|
||||||
|
queue: RwLock<VecDeque<Handle<AudioSource>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AudioOutput {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
device: rodio::default_output_device().unwrap(),
|
||||||
|
queue: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioOutput {
|
||||||
|
pub fn play(&self, audio_source: &AudioSource) {
|
||||||
|
let sink = Sink::new(&self.device);
|
||||||
|
sink.append(Decoder::new(Cursor::new(audio_source.clone())).unwrap());
|
||||||
|
sink.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_play(&self, audio_source: Handle<AudioSource>) {
|
||||||
|
self.queue.write().unwrap().push_front(audio_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_play_queued(&self, audio_sources: &Assets<AudioSource>) {
|
||||||
|
let mut queue = self.queue.write().unwrap();
|
||||||
|
let len = queue.len();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < len {
|
||||||
|
let audio_source_handle = queue.pop_back().unwrap();
|
||||||
|
if let Some(audio_source) = audio_sources.get(&audio_source_handle) {
|
||||||
|
self.play(audio_source);
|
||||||
|
} else {
|
||||||
|
// audio source hasn't loaded yet. add it back to the queue
|
||||||
|
queue.push_front(audio_source_handle);
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_queued_audio_system(
|
||||||
|
audio_sources: Res<Assets<AudioSource>>,
|
||||||
|
audio_output: Res<AudioOutput>,
|
||||||
|
) {
|
||||||
|
audio_output.try_play_queued(&audio_sources);
|
||||||
|
}
|
27
crates/bevy_audio/src/audio_source.rs
Normal file
27
crates/bevy_audio/src/audio_source.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use bevy_asset::AssetLoader;
|
||||||
|
use std::{sync::Arc, path::Path};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AudioSource {
|
||||||
|
pub bytes: Arc<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for AudioSource {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Mp3Loader;
|
||||||
|
|
||||||
|
impl AssetLoader<AudioSource> for Mp3Loader {
|
||||||
|
fn from_bytes(&self, _asset_path: &Path, bytes: Vec<u8>) -> Result<AudioSource> {
|
||||||
|
Ok(AudioSource { bytes: Arc::new(bytes) })
|
||||||
|
}
|
||||||
|
fn extensions(&self) -> &[&str] {
|
||||||
|
static EXTENSIONS: &[&str] = &["mp3"];
|
||||||
|
EXTENSIONS
|
||||||
|
}
|
||||||
|
}
|
21
crates/bevy_audio/src/lib.rs
Normal file
21
crates/bevy_audio/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
mod audio_output;
|
||||||
|
mod audio_source;
|
||||||
|
|
||||||
|
pub use audio_output::*;
|
||||||
|
pub use audio_source::*;
|
||||||
|
|
||||||
|
use bevy_app::{stage, AppBuilder, AppPlugin};
|
||||||
|
use bevy_asset::AddAsset;
|
||||||
|
use bevy_ecs::IntoQuerySystem;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AudioPlugin;
|
||||||
|
|
||||||
|
impl AppPlugin for AudioPlugin {
|
||||||
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
|
app.init_resource::<AudioOutput>()
|
||||||
|
.add_asset::<AudioSource>()
|
||||||
|
.add_asset_loader::<AudioSource, Mp3Loader>()
|
||||||
|
.add_system_to_stage(stage::POST_UPDATE, play_queued_audio_system.system());
|
||||||
|
}
|
||||||
|
}
|
15
examples/audio/audio.rs
Normal file
15
examples/audio/audio.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::build()
|
||||||
|
.add_default_plugins()
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(asset_server: Res<AssetServer>, audio_output: Res<AudioOutput>) {
|
||||||
|
let music = asset_server
|
||||||
|
.load("assets/sounds/Windless Slopes.mp3")
|
||||||
|
.unwrap();
|
||||||
|
audio_output.queue_play(music);
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ impl AddDefaultPlugins for AppBuilder {
|
||||||
self.add_plugin(bevy_ui::UiPlugin::default());
|
self.add_plugin(bevy_ui::UiPlugin::default());
|
||||||
self.add_plugin(bevy_gltf::GltfPlugin::default());
|
self.add_plugin(bevy_gltf::GltfPlugin::default());
|
||||||
self.add_plugin(bevy_text::TextPlugin::default());
|
self.add_plugin(bevy_text::TextPlugin::default());
|
||||||
|
self.add_plugin(bevy_audio::AudioPlugin::default());
|
||||||
|
|
||||||
#[cfg(feature = "bevy_winit")]
|
#[cfg(feature = "bevy_winit")]
|
||||||
self.add_plugin(bevy_winit::WinitPlugin::default());
|
self.add_plugin(bevy_winit::WinitPlugin::default());
|
||||||
|
|
|
@ -46,6 +46,7 @@ pub use bevy_app as app;
|
||||||
pub use glam as math;
|
pub use glam as math;
|
||||||
|
|
||||||
pub use bevy_asset as asset;
|
pub use bevy_asset as asset;
|
||||||
|
pub use bevy_audio as audio;
|
||||||
pub use bevy_core as core;
|
pub use bevy_core as core;
|
||||||
pub use bevy_diagnostic as diagnostic;
|
pub use bevy_diagnostic as diagnostic;
|
||||||
pub use bevy_ecs as ecs;
|
pub use bevy_ecs as ecs;
|
||||||
|
|
|
@ -4,6 +4,7 @@ pub use crate::{
|
||||||
EventReader, Events,
|
EventReader, Events,
|
||||||
},
|
},
|
||||||
asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle},
|
asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle},
|
||||||
|
audio::{AudioOutput, AudioSource},
|
||||||
core::{
|
core::{
|
||||||
time::{Time, Timer},
|
time::{Time, Timer},
|
||||||
transform::FaceToward,
|
transform::FaceToward,
|
||||||
|
|
Loading…
Reference in a new issue