audio: initial (very minimal) audio plugin

This commit is contained in:
Carter Anderson 2020-07-16 13:46:51 -07:00
parent af109174dd
commit 3eb393548d
10 changed files with 139 additions and 0 deletions

View file

@ -17,6 +17,7 @@ members = [
[dependencies]
# bevy
bevy_audio = { path = "crates/bevy_audio" }
bevy_app = { path = "crates/bevy_app" }
bevy_asset = { path = "crates/bevy_asset" }
bevy_type_registry = { path = "crates/bevy_type_registry" }
@ -115,6 +116,10 @@ path = "examples/asset/hot_asset_reloading.rs"
name = "asset_loading"
path = "examples/asset/asset_loading.rs"
[[example]]
name = "audio"
path = "examples/audio/audio.rs"
[[example]]
name = "custom_diagnostic"
path = "examples/diagnostics/custom_diagnostic.rs"

Binary file not shown.

View 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"]}

View 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);
}

View 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
}
}

View 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
View 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);
}

View file

@ -19,6 +19,7 @@ impl AddDefaultPlugins for AppBuilder {
self.add_plugin(bevy_ui::UiPlugin::default());
self.add_plugin(bevy_gltf::GltfPlugin::default());
self.add_plugin(bevy_text::TextPlugin::default());
self.add_plugin(bevy_audio::AudioPlugin::default());
#[cfg(feature = "bevy_winit")]
self.add_plugin(bevy_winit::WinitPlugin::default());

View file

@ -46,6 +46,7 @@ pub use bevy_app as app;
pub use glam as math;
pub use bevy_asset as asset;
pub use bevy_audio as audio;
pub use bevy_core as core;
pub use bevy_diagnostic as diagnostic;
pub use bevy_ecs as ecs;

View file

@ -4,6 +4,7 @@ pub use crate::{
EventReader, Events,
},
asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle},
audio::{AudioOutput, AudioSource},
core::{
time::{Time, Timer},
transform::FaceToward,