Skip to content

Commit

Permalink
Reworked channels and etc (#5)
Browse files Browse the repository at this point in the history
Co-authored-by: harudagondi <giogdeasis@gmail.com>
  • Loading branch information
harudagondi and harudagondi authored Aug 7, 2022
1 parent 8efed7d commit 2e98c5f
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
args: -- -D warnings
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Support for mono.
- Support for `oddio::Sine` and `oddio::SpatialScene`.

### Changed
- `Audio::play` now returns `AudioHandle` and `AudioSink`.
- All public facing structs now need `F` to be specified, where `F` is either `Mono` or `Stereo`.

## [0.1.0] - 2022-07-01
- Released `bevy_oddio` 0.1 🎉

[Unreleased]: https://github.com/harudagondi/bevy_oddio/compare/v0.1.0..HEAD
[0.1.0]: https://github.com/harudagondi/bevy_oddio/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ categories = ["game-development", "multimedia::audio"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
oddio = "0.5"
oddio = "0.6"
cpal = "0.13"
parking_lot = "0.12"
hound = { version = "3.4", optional = true }
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ A third party Bevy plugin that integrates [`oddio`] into [Bevy].
```rust no_run
use bevy::prelude::*;
use bevy_oddio::*;
use bevy_oddio::frames::Stereo;

fn main() {
App::new()
Expand All @@ -21,7 +22,7 @@ fn main() {
.run();
}

fn play_background_audio(asset_server: Res<AssetServer>, mut audio: ResMut<Audio>) {
fn play_background_audio(asset_server: Res<AssetServer>, mut audio: ResMut<Audio<Stereo>>) {
audio.play(asset_server.load("background_audio.wav"), 0.0);
}
```
Expand Down
11 changes: 8 additions & 3 deletions examples/noise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use bevy::{
DefaultPlugins,
};
use bevy_oddio::{
frames::Stereo,
output::{AudioHandle, AudioSink},
Audio, AudioApp, AudioPlugin, Stereo, ToSignal,
Audio, AudioApp, AudioPlugin, ToSignal,
};
use oddio::Signal;

Expand Down Expand Up @@ -39,7 +40,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(AudioPlugin)
.add_audio_source::<Noise>()
.add_audio_source::<2, _, Noise>()
.add_startup_system(init_assets)
.add_startup_system_to_stage(StartupStage::PostStartup, play_noise)
.run();
Expand All @@ -55,7 +56,11 @@ fn init_assets(mut commands: Commands, mut assets: ResMut<Assets<Noise>>) {
commands.insert_resource(NoiseHandle(handle));
}

fn play_noise(mut commands: Commands, mut audio: ResMut<Audio<Noise>>, noise: Res<NoiseHandle>) {
fn play_noise(
mut commands: Commands,
mut audio: ResMut<Audio<Stereo, Noise>>,
noise: Res<NoiseHandle>,
) {
let handles = audio.play(noise.clone(), ());
commands.insert_resource(NoiseSink(handles.0, handles.1));
}
26 changes: 3 additions & 23 deletions src/builtins/spatial_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,11 @@ use crate::ToSignal;
#[uuid = "4c5ea5bb-293e-485e-93d9-7a4f69d2130a"]
pub struct SpatialScene;

/// Settings for initialization of [`SpatialScene`] audio source.
/// See [`SpatialScene::new`](oddio::SpatialScene::new) for more information.
pub struct Settings {
/// The sample rate.
pub rate: u32,
/// The duration of the buffer.
pub buffer_duration: f32,
}

impl Settings {
/// Generate settings for [`SpatialScene`].
#[must_use]
pub fn new(rate: u32, buffer_duration: f32) -> Self {
Self {
rate,
buffer_duration,
}
}
}

impl ToSignal for SpatialScene {
type Settings = Settings;
type Settings = ();
type Signal = oddio::SpatialScene;

fn to_signal(&self, settings: Self::Settings) -> Self::Signal {
oddio::SpatialScene::new(settings.rate, settings.buffer_duration)
fn to_signal(&self, _settings: Self::Settings) -> Self::Signal {
oddio::SpatialScene::new()
}
}
100 changes: 100 additions & 0 deletions src/frames.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use bevy::{
prelude::{Deref, DerefMut},
reflect::TypeUuid,
};
use oddio::{Frame, Sample};

/// Internal trait to convert frames.
pub trait FromFrame<F: Frame> {
/// The number of channels of the given frame.
const CHANNELS: usize;
/// Convert given frame to the implementing type.
fn from_frame(f: F) -> Self;
}

impl<const N: usize> FromFrame<[Sample; N]> for [Sample; N] {
const CHANNELS: usize = N;

fn from_frame(f: [Sample; N]) -> Self {
f
}
}

impl FromFrame<[Sample; 1]> for Sample {
const CHANNELS: usize = 1;

fn from_frame(f: [Sample; 1]) -> Self {
f[0]
}
}

macro_rules! impl_frame {
($(#[$meta:meta])* $name:ident, $n:literal, $uuid:expr) => {
#[repr(transparent)]
#[derive(TypeUuid, Deref, DerefMut, Clone, Copy)]
#[uuid = $uuid]
$(#[$meta])*
pub struct $name([Sample; $n]);

impl From<$name> for [Sample; $n] {
fn from(x: $name) -> Self {
x.0
}
}

impl From<[Sample; $n]> for $name {
fn from(x: [Sample; $n]) -> Self {
$name(x)
}
}

impl FromFrame<[Sample; $n]> for $name {
const CHANNELS: usize = $n;

fn from_frame(f: [Sample; $n]) -> Self {
f.into()
}
}

impl Frame for $name {
const ZERO: Self = $name(Frame::ZERO);

fn channels(&self) -> &[Sample] {
self.0.channels()
}

fn channels_mut(&mut self) -> &mut [Sample] {
self.0.channels_mut()
}
}
};
}

impl_frame!(
/// A frame with a single channel.
Mono,
1,
"43915b3a-332c-4104-81ff-3b68bdb192c3"
);
impl_frame!(
/// A frame with two channels.
Stereo,
2,
"94ca7739-0a77-4142-91de-b7150fecc689"
);

/// Convert a mutable reference of a list of samples to the corresponding newtyped frame.
///
/// # Safety
///
/// This function must uphold the following invariants:
///
/// 1. `F` must have equivalent memory representation to `[Sample; N]`.
/// 2. `N` must be a number where 64 mod `N` == 0.
pub unsafe fn frame_n<F: Frame + FromFrame<[Sample; N]>, const N: usize>(
input: &mut [Sample],
) -> &mut [F] {
let slice: &mut [[Sample; N]] =
core::slice::from_raw_parts_mut(input.as_mut_ptr().cast(), input.len() / N);
&mut *(slice as *mut [[Sample; N]] as *mut [F])
}
61 changes: 36 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
#![warn(missing_docs)]
#![warn(clippy::pedantic)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![allow(clippy::module_name_repetitions)]

//! A plugin that integrates [`oddio`] with [`bevy`].
//!
//! Note that audio must have two channels or it will not work.
//! Thus, non-wav files are more likely to break.
//!
//! When implementing [`oddio::Signal`] for your types,
//! use [`Stereo`] as your output.
//!
//! See [`#1`](https://github.com/harudagondi/bevy_oddio/issues/1).

use std::{collections::VecDeque, sync::Arc};
use std::{collections::VecDeque, marker::PhantomData, sync::Arc};

use bevy::{
asset::{Asset, HandleId},
prelude::{AddAsset, App, CoreStage, Handle as BevyHandle, Plugin},
reflect::TypeUuid,
};
use oddio::{Frames, FramesSignal, Sample, Signal};
use frames::{FromFrame, Mono, Stereo};
use oddio::{Frame, Frames, FramesSignal, Sample, Signal};

pub use oddio;
use output::{play_queued_audio, AudioHandle, AudioHandles, AudioOutput, AudioSink, AudioSinks};
use parking_lot::RwLock;

/// [`oddio`] builtin types that can be directly used in [`Audio::play`].
pub mod builtins;
/// Newtypes for working around [bevyengine/bevy#5432](https://github.com/bevyengine/bevy/issues/5432)
pub mod frames;

pub use frames::*;

mod loader;
/// Audio output
pub mod output;

/// The frame used in the `oddio` types
pub type Stereo = [Sample; 2];

struct AudioToPlay<Source>
where
Source: ToSignal + Asset,
Expand All @@ -45,16 +46,19 @@ where
}

/// Resource that can play any type that implements [`Signal`].
pub struct Audio<Source = AudioSource>
pub struct Audio<F, Source = AudioSource<F>>
where
Source: ToSignal + Asset,
F: Frame,
{
queue: RwLock<VecDeque<AudioToPlay<Source>>>,
_frame: PhantomData<fn() -> F>,
}

impl<Source> Audio<Source>
impl<F, Source> Audio<F, Source>
where
Source: ToSignal + Asset,
F: Frame,
{
/// Play the given type that implements [`Signal`].
///
Expand Down Expand Up @@ -83,13 +87,15 @@ where
}
}

impl<Source> Default for Audio<Source>
impl<F, Source> Default for Audio<F, Source>
where
Source: ToSignal + Asset,
F: Frame,
{
fn default() -> Self {
Self {
queue: RwLock::default(),
_frame: PhantomData,
}
}
}
Expand All @@ -99,9 +105,9 @@ where
/// Accepts an atomically reference-counted [`Frames`] with two channels.
#[derive(Clone, TypeUuid)]
#[uuid = "2b024eb6-88f1-4001-b678-0446f2fab0f4"]
pub struct AudioSource {
pub struct AudioSource<F: Frame> {
/// Raw audio data. See [`Frames`].
pub frames: Arc<Frames<Stereo>>,
pub frames: Arc<Frames<F>>,
}

/// Trait for a type that generates a signal.
Expand All @@ -119,9 +125,9 @@ pub trait ToSignal {
fn to_signal(&self, settings: Self::Settings) -> Self::Signal;
}

impl ToSignal for AudioSource {
impl<F: Frame + Send + Sync + Copy> ToSignal for AudioSource<F> {
type Settings = f64;
type Signal = FramesSignal<Stereo>;
type Signal = FramesSignal<F>;

fn to_signal(&self, settings: Self::Settings) -> Self::Signal {
FramesSignal::new(self.frames.clone(), settings)
Expand All @@ -135,10 +141,13 @@ pub struct AudioPlugin;

impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<AudioOutput>()
.add_audio_source::<AudioSource>()
// .add_audio_source::<builtins::sine::Sine>()
.add_audio_source::<builtins::spatial_scene::SpatialScene>();
app.init_resource::<AudioOutput<1, Mono>>()
.init_resource::<AudioOutput<1, Sample>>()
.init_resource::<AudioOutput<2, Stereo>>()
.add_audio_source::<1, Mono, AudioSource<Mono>>()
.add_audio_source::<2, Stereo, AudioSource<Stereo>>()
.add_audio_source::<1, Sample, builtins::sine::Sine>();
// .add_audio_source::<builtins::spatial_scene::SpatialScene>();
#[cfg(feature = "flac")]
app.init_asset_loader::<loader::flac_loader::FlacLoader>();
#[cfg(feature = "mp3")]
Expand All @@ -153,25 +162,27 @@ impl Plugin for AudioPlugin {
/// Extension trait to add new audio sources implemented by users
pub trait AudioApp {
/// Add support for custom audio sources.
fn add_audio_source<Source>(&mut self) -> &mut Self
fn add_audio_source<const N: usize, F, Source>(&mut self) -> &mut Self
where
Source: ToSignal + Asset + Send,
Source::Signal: Signal<Frame = Stereo> + Send;
Source::Signal: Signal<Frame = F> + Send,
F: Frame + FromFrame<[Sample; N]> + 'static;
}

impl AudioApp for App {
fn add_audio_source<Source>(&mut self) -> &mut Self
fn add_audio_source<const N: usize, F, Source>(&mut self) -> &mut Self
where
Source: ToSignal + Asset + Send,
Source::Signal: Signal<Frame = Stereo> + Send,
Source::Signal: Signal<Frame = F> + Send,
F: Frame + FromFrame<[Sample; N]> + 'static,
{
self.add_asset::<Source>()
.add_asset::<AudioSink<Source>>()
.add_asset::<AudioHandle<Source>>()
.init_resource::<Audio<Source>>()
.init_resource::<Audio<F, Source>>()
.init_resource::<AudioSinks<Source>>()
.init_resource::<AudioHandles<Source>>()
.add_system_to_stage(CoreStage::PostUpdate, play_queued_audio::<Source>)
.add_system_to_stage(CoreStage::PostUpdate, play_queued_audio::<N, F, Source>)
}
}

Expand Down
Loading

0 comments on commit 2e98c5f

Please sign in to comment.