ark_module/types/
audio.rs

1//! 🎵 Audio module
2//!
3//! TODO: Add description how they work and how to use it
4
5pub use ark_api_ffi::entrypoints::audio::AudioRenderInfo;
6pub use ark_api_ffi::entrypoints::audio::AudioSourceFlags;
7pub use ark_api_ffi::entrypoints::audio::AudioSourceInfo;
8
9/// Audio module creation & update trait
10pub trait AudioInstance {
11    /// Creation of a new module
12    ///
13    /// This is called once on startup of the module
14    fn new() -> Self;
15
16    /// Implement the update of the audio module here
17    ///
18    /// TODO: Describe more in detail how it works and what the parameters are
19    fn update(
20        &mut self,
21        frame_info: &AudioRenderInfo,
22        object_to_world: macaw::Mat4,
23        static_data: &[u8],
24        dynamic_data: &[u8],
25        stereo_buffer: &mut [f32],
26    );
27}
28
29/// Implement an audio source module.
30///
31/// The API is instance based so you don't need to keep track of instances yourself.
32#[macro_export]
33macro_rules! impl_audio_instance {
34    ($instance: ty) => {
35        use std::collections::HashMap;
36        use $crate::audio::AudioInstance as _;
37
38        struct Module {
39            instances: HashMap<u64, $instance>,
40        }
41
42        static mut MODULE: Option<Module> = None;
43
44        #[no_mangle]
45        pub fn ark_initialize() {
46            $crate::init();
47            // SAFETY: Sound to access static as we do not use threads in WASM and this is guaranteed to be called first in the module
48            unsafe {
49                MODULE = Some(Module {
50                    instances: Default::default(),
51                });
52            }
53        }
54
55        #[no_mangle]
56        pub unsafe fn ark_audio_render(
57            render_info_ptr: *const $crate::audio::AudioRenderInfo,
58            source_info_ptr: *const $crate::audio::AudioSourceInfo,
59            source_info_len: u32,
60        ) {
61            // SAFETY: Sound to access static as we do not use threads in WASM and this is guaranteed to be initialized on startup with `ark_initialize` first
62            unsafe {
63                let module = MODULE.as_mut().unwrap();
64                let slice = std::slice::from_raw_parts::<$crate::audio::AudioSourceInfo>(
65                    source_info_ptr,
66                    source_info_len as usize,
67                );
68                for entry in slice {
69                    let static_data_slice = std::slice::from_raw_parts::<u8>(
70                        entry.static_data_ptr as *const u8,
71                        entry.static_data_len as usize,
72                    );
73                    let dynamic_data_slice = std::slice::from_raw_parts::<u8>(
74                        entry.dynamic_data_ptr as *const u8,
75                        entry.dynamic_data_len as usize,
76                    );
77                    let buffer_slice = std::slice::from_raw_parts_mut::<f32>(
78                        entry.out_buffer_ptr as *mut f32,
79                        entry.out_buffer_len as usize,
80                    );
81
82                    let instance = module
83                        .instances
84                        .entry(entry.cache_id)
85                        .or_insert_with(|| <$instance>::new());
86
87                    // using rate as a flag for removal
88                    if !entry.flags.contains(AudioSourceFlags::REMOVED) {
89                        instance.update(
90                            render_info_ptr.as_ref().unwrap(),
91                            macaw::Mat4::from_cols_array(&entry.object_to_world),
92                            static_data_slice,
93                            dynamic_data_slice,
94                            buffer_slice,
95                        );
96                    } else {
97                        let _ = module.instances.remove(&entry.cache_id);
98                    }
99                }
100            }
101        }
102    };
103}