1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
pub use ark_api_ffi::entrypoints::audio::AudioRenderInfo;
pub use ark_api_ffi::entrypoints::audio::AudioSourceFlags;
pub use ark_api_ffi::entrypoints::audio::AudioSourceInfo;

pub trait AudioInstance {
    fn new() -> Self;
    fn update(
        &mut self,
        frame_info: &AudioRenderInfo,
        object_to_world: macaw::Mat4,
        static_data: &[u8],
        dynamic_data: &[u8],
        stereo_buffer: &mut [f32],
    );
}

/// Implement an audio source module.
///
/// The API is instance based so you don't need to keep track of instances yourself.
#[macro_export]
macro_rules! impl_audio_instance {
    ($instance: ty) => {
        use std::collections::HashMap;
        use $crate::audio::AudioInstance as _;

        struct Module {
            instances: HashMap<u64, $instance>,
        }

        static mut MODULE: Option<Module> = None;

        #[no_mangle]
        pub fn ark_initialize() {
            $crate::init();
            // SAFETY: Sound to access static as we do not use threads in WASM and this is guaranteed to be called first in the module
            unsafe {
                MODULE = Some(Module {
                    instances: Default::default(),
                });
            }
        }

        #[no_mangle]
        pub unsafe fn ark_audio_render(
            render_info_ptr: *const $crate::audio::AudioRenderInfo,
            source_info_ptr: *const $crate::audio::AudioSourceInfo,
            source_info_len: u32,
        ) {
            // 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
            unsafe {
                let module = MODULE.as_mut().unwrap();
                let slice = std::slice::from_raw_parts::<$crate::audio::AudioSourceInfo>(
                    source_info_ptr,
                    source_info_len as usize,
                );
                for entry in slice {
                    let static_data_slice = std::slice::from_raw_parts::<u8>(
                        entry.static_data_ptr as *const u8,
                        entry.static_data_len as usize,
                    );
                    let dynamic_data_slice = std::slice::from_raw_parts::<u8>(
                        entry.dynamic_data_ptr as *const u8,
                        entry.dynamic_data_len as usize,
                    );
                    let buffer_slice = std::slice::from_raw_parts_mut::<f32>(
                        entry.out_buffer_ptr as *mut f32,
                        entry.out_buffer_len as usize,
                    );

                    let instance = module
                        .instances
                        .entry(entry.cache_id)
                        .or_insert_with(|| <$instance>::new());

                    // using rate as a flag for removal
                    if !entry.flags.contains(AudioSourceFlags::REMOVED) {
                        instance.update(
                            render_info_ptr.as_ref().unwrap(),
                            macaw::Mat4::from_cols_array(&entry.object_to_world),
                            static_data_slice,
                            dynamic_data_slice,
                            buffer_slice,
                        );
                    } else {
                        let _ = module.instances.remove(&entry.cache_id);
                    }
                }
            }
        }
    };
}