From cc60651227c1bb1df0da1913fcbb8f983ee5e0bd Mon Sep 17 00:00:00 2001 From: pillowtrucker Date: Sat, 27 Jan 2024 22:43:06 +0100 Subject: [PATCH] wip audio management --- Cargo.lock | 4 + brainworms_farting_noises/Cargo.toml | 4 + brainworms_farting_noises/src/lib.rs | 119 ++++++++++++++++-- brainworms_lib/Cargo.toml | 2 +- brainworms_lib/src/lib.rs | 4 +- .../play.rs | 1 + .../play/backstage/plumbing.rs | 2 +- .../play/orchestra.rs | 39 ++++++ 8 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/orchestra.rs diff --git a/Cargo.lock b/Cargo.lock index 635e8a1..8888254 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,10 +340,14 @@ dependencies = [ name = "brainworms_farting_noises" version = "0.1.0" dependencies = [ + "anyhow", "cubeb", "glicol_synth", "libymfm", + "parking_lot", + "tokio", "ustr", + "winit", ] [[package]] diff --git a/brainworms_farting_noises/Cargo.toml b/brainworms_farting_noises/Cargo.toml index 438c788..af7f6b6 100644 --- a/brainworms_farting_noises/Cargo.toml +++ b/brainworms_farting_noises/Cargo.toml @@ -10,3 +10,7 @@ cubeb = {git = "https://github.com/mozilla/cubeb-rs"} libymfm = {path = "./libymfm.wasm"} ustr = "1" glicol_synth = "0.13" +anyhow = "1" +tokio = { version = "1.35", features = ["full"] } +winit = {version = "0.29"} +parking_lot = "0.12.1" diff --git a/brainworms_farting_noises/src/lib.rs b/brainworms_farting_noises/src/lib.rs index 03eaebd..ddbc80c 100644 --- a/brainworms_farting_noises/src/lib.rs +++ b/brainworms_farting_noises/src/lib.rs @@ -1,22 +1,119 @@ use std::{ + borrow::BorrowMut, + collections::HashMap, fs::File, - io::Read, + io::{Error, Read}, + path::Path, ptr::slice_from_raw_parts, - sync::{Arc, Condvar, Mutex}, + sync::Arc, }; -use cubeb::{Context, Result, StereoFrame}; +pub use cubeb::{self, Context, StereoFrame}; use libymfm::{driver::VgmPlay, sound::SoundSlot}; + +use parking_lot::{Condvar, Mutex}; +use tokio::sync::mpsc::Receiver; const SAMPLE_FREQUENCY: u32 = 48_000; const STREAM_FORMAT: cubeb::SampleFormat = cubeb::SampleFormat::Float32LE; -pub fn init(ctx_name: &str) -> Result { +const MAX_SAMPLE_SIZE: usize = 2048; +pub fn init(ctx_name: &str) -> anyhow::Result { let ctx_name = ustr::ustr(ctx_name); - Context::init(Some(ctx_name.as_cstr()), None) + Ok(Context::init(Some(ctx_name.as_cstr()), None)?) +} +pub enum AudioCommand { + Prebake(PathToJingle), + Play(JingleName), + Pause(JingleName), + Stop(JingleName), + Drop(JingleName), + Die, +} + +pub type JingleRegistry = HashMap; +pub async fn audio_router_thread( + mut rx: Receiver, + registry: Arc>, + audio_ctx: Arc>, +) { + use tokio::runtime::Handle; + while let Some(cmd) = rx.recv().await { + match cmd { + AudioCommand::Prebake(p) => { + let registry = registry.clone(); + let handle = Handle::current(); + handle.spawn(async move { prebake(p, registry) }); + } + AudioCommand::Play(_) => todo!(), + AudioCommand::Pause(_) => todo!(), + AudioCommand::Stop(_) => todo!(), + AudioCommand::Drop(_) => todo!(), + AudioCommand::Die => return, + } + } +} + +pub type PathToJingle = String; +pub type JingleName = String; +#[derive(Debug, Clone, PartialEq)] +pub struct Jingle { + pub name: JingleName, + pub l: Vec, + pub r: Vec, + pub len: usize, +} +fn prebake(ptj: PathToJingle, registry: Arc>) -> anyhow::Result<()> { + let mut file = File::open(&ptj)?; + let mut buffer = Vec::new(); + let _ = file.read_to_end(&mut buffer)?; + + // read vgm + let mut vgmplay = VgmPlay::new( + SoundSlot::new(SAMPLE_FREQUENCY, SAMPLE_FREQUENCY, MAX_SAMPLE_SIZE), + &buffer, + ) + .unwrap(); + let mut sampling_l; + let mut sampling_r; + + let mut out_l = Vec::::with_capacity(MAX_SAMPLE_SIZE * 2); + let mut out_r = Vec::::with_capacity(MAX_SAMPLE_SIZE * 2); + + #[allow(clippy::absurd_extreme_comparisons)] + while vgmplay.play(false) <= 0 { + unsafe { + sampling_l = slice_from_raw_parts(vgmplay.get_sampling_l_ref(), MAX_SAMPLE_SIZE) + .as_ref() + .unwrap(); + sampling_r = slice_from_raw_parts(vgmplay.get_sampling_r_ref(), MAX_SAMPLE_SIZE) + .as_ref() + .unwrap(); + } + out_l.extend_from_slice(sampling_l); + out_r.extend_from_slice(sampling_r); + } + + let len = out_l.len().max(out_r.len()); + let mut registry = registry.lock(); + let jn = Path::new(&ptj) + .file_name() + .unwrap() + .to_string_lossy() + .into_owned(); + registry.insert( + jn.clone(), + Jingle { + name: jn, + l: out_l, + r: out_r, + len, + }, + ); + + Ok(()) } -const MAX_SAMPLE_SIZE: usize = 2048; -pub async fn play(filepath: &str) -> Result<()> { - let ctx = init("booger")?; +pub async fn play(filepath: &str) -> anyhow::Result<()> { + let ctx = init("booger").await?; let params = cubeb::StreamParamsBuilder::new() .format(STREAM_FORMAT) .rate(SAMPLE_FREQUENCY) @@ -84,7 +181,7 @@ pub async fn play(filepath: &str) -> Result<()> { cubeb::State::Stopped => {} cubeb::State::Drained => { let (lock, cvar) = &*cv_playback_ended_inside_copy; - let mut playback_ended = lock.lock().unwrap(); + let mut playback_ended = lock.lock(); *playback_ended = true; cvar.notify_one(); } @@ -96,8 +193,6 @@ pub async fn play(filepath: &str) -> Result<()> { stream.start()?; let (lock, cvar) = &*cv_playback_ended; - let _guard = cvar - .wait_while(lock.lock().unwrap(), |ended| !*ended) - .unwrap(); + cvar.wait_while(lock.lock().borrow_mut(), |&mut ended| !ended); Ok(()) } diff --git a/brainworms_lib/Cargo.toml b/brainworms_lib/Cargo.toml index 6be1e33..d2db786 100644 --- a/brainworms_lib/Cargo.toml +++ b/brainworms_lib/Cargo.toml @@ -56,7 +56,7 @@ parking_lot = "0.12.1" thiserror = { version = "1" } #rayon = "1.8" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.35", features = ["full"] } notify = "6.1.1" rust-embed = "8.2.0" # Enable shader hot reload for native compilation. diff --git a/brainworms_lib/src/lib.rs b/brainworms_lib/src/lib.rs index 89c3c27..f189081 100644 --- a/brainworms_lib/src/lib.rs +++ b/brainworms_lib/src/lib.rs @@ -4,6 +4,8 @@ pub mod the_great_mind_palace_of_theatrical_arts; pub use brainworms_arson::{self, anyhow, egui, egui_winit, nanorand}; pub use brainworms_farting_noises; + +use brainworms_farting_noises::{cubeb, Jingle}; pub use cfg_if::cfg_if; use egui::{Color32, TextStyle, Visuals}; pub use glam; @@ -97,7 +99,7 @@ pub struct GameProgramme< pub type MyEvent = MyWinitEvent; pub type Event = winit::event::Event; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub enum MyWinitEvent { /// Custom user event types Stage3D(TS), diff --git a/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play.rs b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play.rs index 04024d9..45d21ae 100644 --- a/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play.rs +++ b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play.rs @@ -20,6 +20,7 @@ use super::basement::{cla::GameProgrammeSettings, input_handling::HandlesInputCo pub mod backstage; pub mod definition; +pub mod orchestra; pub mod scene; #[derive(Default)] pub struct Play { diff --git a/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/backstage/plumbing.rs b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/backstage/plumbing.rs index 908f2e3..bcc98cc 100644 --- a/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/backstage/plumbing.rs +++ b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/backstage/plumbing.rs @@ -63,7 +63,7 @@ impl + 'static, InputContextEnum: Inpu Self { data, settings: GameProgrammeSettings::new(), - rts: tokio::runtime::Runtime::new().ok(), + rts: tokio::runtime::Builder::new_multi_thread().build().ok(), state: GameProgrammeState::default(), } } diff --git a/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/orchestra.rs b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/orchestra.rs new file mode 100644 index 0000000..2a7f0be --- /dev/null +++ b/brainworms_lib/src/the_great_mind_palace_of_theatrical_arts/play/orchestra.rs @@ -0,0 +1,39 @@ +use std::{ + mem::{replace, swap}, + sync::Arc, +}; + +use baudio::{audio_router_thread, init, AudioCommand, JingleRegistry}; +use brainworms_farting_noises as baudio; + +use parking_lot::Mutex; +use tokio::{runtime::Handle, sync::mpsc::Sender}; +type Generation = u64; +pub struct Orchestra { + handler: (Generation, Option>), + jingle_registry: Arc>, + rth: Handle, +} +impl Orchestra { + pub fn new(rth: Handle) -> Self { + let jingle_registry = Arc::new(Mutex::new(JingleRegistry::new())); + let mut me = Self { + handler: (0, None), + jingle_registry, + rth, + }; + me.replace_worker(); + me + } + fn replace_worker(&mut self) { + let gen = self.handler.0 + 1; + let new_ctx = Arc::new(Mutex::new(init(&format!("audio ctx gen {}", gen)).unwrap())); + let (tx, rx) = tokio::sync::mpsc::channel(256); + self.rth.spawn(audio_router_thread( + rx, + self.jingle_registry.clone(), + new_ctx, + )); + self.handler = (gen, Some(tx)); + } +}