use super::{Aspect, Node}; use crate::core::client::Client; use crate::core::destroy_queue; use crate::core::registry::Registry; use crate::core::resource::get_resource_file; use crate::create_interface; use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO; use crate::nodes::spatial::{Spatial, Transform}; use color_eyre::eyre::{eyre, Result}; use glam::{vec3, Vec4Swizzles}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use stardust_xr::values::ResourceID; use std::ops::DerefMut; use std::sync::Arc; use std::{ffi::OsStr, path::PathBuf}; use stereokit_rust::sound::{Sound as SkSound, SoundInst}; static SOUND_REGISTRY: Registry = Registry::new(); stardust_xr_server_codegen::codegen_audio_protocol!(); pub struct Sound { space: Arc, volume: f32, pending_audio_path: PathBuf, sk_sound: OnceCell, instance: Mutex>, stop: Mutex>, play: Mutex>, } impl Sound { pub fn add_to(node: &Arc, resource_id: ResourceID) -> Result> { let pending_audio_path = get_resource_file( &resource_id, &*node.get_client().ok_or_else(|| eyre!("Client not found"))?, &[OsStr::new("wav"), OsStr::new("mp3")], ) .ok_or_else(|| eyre!("Resource not found"))?; let sound = Sound { space: node.get_aspect::().unwrap().clone(), volume: 1.0, pending_audio_path, sk_sound: OnceCell::new(), instance: Mutex::new(None), stop: Mutex::new(None), play: Mutex::new(None), }; let sound_arc = SOUND_REGISTRY.add(sound); node.add_aspect_raw(sound_arc.clone()); ::add_node_members(node); Ok(sound_arc) } fn update(&self) { let sound = self .sk_sound .get_or_init(|| SkSound::from_file(self.pending_audio_path.clone()).unwrap()); if self.stop.lock().take().is_some() { if let Some(instance) = self.instance.lock().take() { instance.stop(); } } if self.instance.lock().is_none() && self.play.lock().take().is_some() { let instance = sound.play(vec3(0.0, 0.0, 0.0), Some(self.volume)); self.instance.lock().replace(instance); } if let Some(instance) = self.instance.lock().deref_mut() { instance.position(self.space.global_transform().w_axis.xyz()); } } } impl Aspect for Sound { const NAME: &'static str = "Sound"; } impl SoundAspect for Sound { fn play(node: Arc, _calling_client: Arc) -> Result<()> { let sound = node.get_aspect::().unwrap(); sound.play.lock().replace(()); Ok(()) } fn stop(node: Arc, _calling_client: Arc) -> Result<()> { let sound = node.get_aspect::().unwrap(); sound.stop.lock().replace(()); Ok(()) } } impl Drop for Sound { fn drop(&mut self) { if let Some(sk_sound) = self.sk_sound.take() { destroy_queue::add(sk_sound); } SOUND_REGISTRY.remove(self); } } pub fn update() { for sound in SOUND_REGISTRY.get_valid_contents() { sound.update() } } create_interface!(AudioInterface); struct AudioInterface; impl InterfaceAspect for AudioInterface { #[doc = "Create a sound node. WAV and MP3 are supported."] fn create_sound( _node: Arc, calling_client: Arc, id: u64, parent: Arc, transform: Transform, resource: ResourceID, ) -> Result<()> { let node = Node::from_id(&calling_client, id, true); let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, true); let node = node.add_to_scenegraph()?; Spatial::add_to(&node, Some(parent.clone()), transform, false); Sound::add_to(&node, resource)?; Ok(()) } }