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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) 2024 Lily Lyons
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use fmod_sys::*;
use std::{
    ffi::{c_int, c_longlong, c_uint},
    mem::MaybeUninit,
    os::raw::c_void,
};

use crate::{CpuUsage, SpeakerMode, System};

impl System {
    /// Retrieves the FMOD version number.
    ///
    /// The version is a 32 bit hexadecimal value formatted as 16:8:8, with the upper 16 bits being the product version,
    /// the middle 8 bits being the major version and the bottom 8 bits being the minor version.
    /// For example a value of 0x00010203 is equal to 1.02.03.
    ///
    /// Compare against [`crate::VERSION`] to make sure crate and runtime library versions match.
    pub fn get_version(&self) -> Result<c_uint> {
        let mut version = 0;
        unsafe {
            FMOD_System_GetVersion(self.inner, &mut version).to_result()?;
        }
        Ok(version)
    }

    /// Retrieves an output type specific internal native interface.
    ///
    /// Reinterpret the returned handle based on the selected output type, not all types return something.
    ///   [`OutputType::WavWriter`] Pointer to stdio FILE is returned
    ///   [`OutputType::WavWriterNRT`] Pointer to stdio FILE is returned
    ///   [`OutputType::WASAPI`] Pointer to type `IAudioClient` is returned.
    ///   [`OutputType::Alsa`] Pointer to type `snd_pcm_t` is returned.
    ///   [`OutputType::CoreAudio`] Handle of type `AudioUnit` is returned.
    ///   [`OutputType::AudioOut`] Pointer to type int is returned. Handle returned from sceAudioOutOpen.
    ///
    ///
    /// NOTE: Calling this function is safe, but doing anything with the returned pointer is not!!
    pub fn get_output_handle(&self) -> Result<*mut c_void> {
        let mut handle = std::ptr::null_mut();
        unsafe {
            FMOD_System_GetOutputHandle(self.inner, &mut handle).to_result()?;
        }
        Ok(handle)
    }

    /// Retrieves the number of currently playing Channels.
    ///
    /// For differences between real and virtual voices see the Virtual Voices guide for more information.
    pub fn get_playing_channels(&self) -> Result<(c_int, c_int)> {
        let mut channels = 0;
        let mut real_channels = 0;
        unsafe {
            FMOD_System_GetChannelsPlaying(self.inner, &mut channels, &mut real_channels)
                .to_result()?;
        }
        Ok((channels, real_channels))
    }

    /// Retrieves the amount of CPU used for different parts of the Core engine.
    ///
    /// For readability, the percentage values are smoothed to provide a more stable output.
    pub fn get_cpu_usage(&self) -> Result<CpuUsage> {
        let mut cpu_usage = MaybeUninit::zeroed();
        unsafe {
            FMOD_System_GetCPUUsage(self.inner, cpu_usage.as_mut_ptr()).to_result()?;
            let cpu_usage = cpu_usage.assume_init().into();
            Ok(cpu_usage)
        }
    }

    /// Retrieves information about file reads.
    ///
    /// The values returned are running totals that never reset.
    pub fn get_file_usage(&self) -> Result<(c_longlong, c_longlong, c_longlong)> {
        let mut sample_read = 0;
        let mut stream_read = 0;
        let mut other_read = 0;
        unsafe {
            FMOD_System_GetFileUsage(
                self.inner,
                &mut sample_read,
                &mut stream_read,
                &mut other_read,
            )
            .to_result()?;
        }
        Ok((sample_read, stream_read, other_read))
    }

    /// Retrieves the default matrix used to convert from one speaker mode to another.
    ///
    /// The gain for source channel 's' to target channel 't' is `matrix[t * <number of source channels> + s]`.
    ///
    /// If '`source_mode`' or '`target_mode`' is [`SpeakerMode::Raw`], this function will return [`FMOD_RESULT::FMOD_ERR_INVALID_PARAM`].
    /// The number of source channels can be found from [`System::get_speaker_mode_channels`].
    // FIXME: do we take an out slice param?
    pub fn get_default_mix_matrix(
        &self,
        source_mode: SpeakerMode,
        target_mode: SpeakerMode,
    ) -> Result<Vec<f32>> {
        let source_channels = self.get_speaker_mode_channels(source_mode)?;
        let target_channels = self.get_speaker_mode_channels(target_mode)?;
        debug_assert!(source_channels <= FMOD_MAX_CHANNEL_WIDTH as c_int);
        debug_assert!(target_channels <= FMOD_MAX_CHANNEL_WIDTH as c_int);
        let mut matrix = vec![0.0; source_channels as usize * target_channels as usize];

        unsafe {
            FMOD_System_GetDefaultMixMatrix(
                self.inner,
                source_mode.into(),
                target_mode.into(),
                matrix.as_mut_ptr(),
                source_channels,
            )
            .to_result()?;
        }
        Ok(matrix)
    }

    /// Retrieves the channel count for a given speaker mode.
    pub fn get_speaker_mode_channels(&self, speaker_mode: SpeakerMode) -> Result<c_int> {
        let mut channels = 0;
        unsafe {
            FMOD_System_GetSpeakerModeChannels(self.inner, speaker_mode.into(), &mut channels)
                .to_result()?;
        }
        Ok(channels)
    }
}