fmod/core/
memory.rs

1// Copyright (c) 2024 Melody Madeline Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use crate::{FmodResultExt, Result};
8use fmod_sys::*;
9use std::ffi::{c_char, c_int, c_uint, c_void};
10
11/// How you want FMOD to handle memory.
12#[derive(PartialEq, Eq, Debug)]
13pub enum MemoryType {
14    /// Static block of memory for FMOD to manage.
15    Pool(&'static mut [u8]),
16    /// Custom allocator functions.
17    Callback {
18        /// Memory allocation callback compatible with ANSI malloc.
19        alloc: unsafe extern "C" fn(
20            size: c_uint,
21            type_: FMOD_MEMORY_TYPE,
22            sourcestr: *const c_char,
23        ) -> *mut c_void,
24        ///  Memory reallocation callback compatible with ANSI realloc.
25        realloc: FMOD_MEMORY_REALLOC_CALLBACK,
26        /// Memory free callback compatible with ANSI free.
27        free: unsafe extern "C" fn(
28            ptr: *mut c_void,
29            type_: FMOD_MEMORY_TYPE,
30            sourcestr: *const c_char,
31        ),
32    },
33}
34
35bitflags::bitflags! {
36    /// Bitfields for memory allocation type being passed into FMOD memory callbacks.
37    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
38    pub struct MemoryFlags: FMOD_MEMORY_TYPE {
39        /// Standard memory.
40      const NORMAL         = FMOD_MEMORY_NORMAL;
41      /// Stream file buffer, size controllable with [`System::setStreamBufferSize`].
42      const STREAM_FILE    = FMOD_MEMORY_STREAM_FILE;
43      /// Stream decode buffer, size controllable with [`FMOD_CREATESOUNDEXINFO::decodebuffersize`].
44      const STREAM_DECODE  = FMOD_MEMORY_STREAM_DECODE;
45      /// Sample data buffer. Raw audio data, usually PCM/MPEG/ADPCM/XMA data.
46      const SAMPLEDATA     = FMOD_MEMORY_SAMPLEDATA;
47      /// Deprecated.
48      #[deprecated]
49      const DSP_BUFFER     = FMOD_MEMORY_DSP_BUFFER;
50      /// Memory allocated by a third party plugin.
51      const PLUGIN         = FMOD_MEMORY_PLUGIN;
52      /// Persistent memory. Memory will be freed when [`System::release`] is called.
53      const PERSISTENT     = FMOD_MEMORY_PERSISTENT;
54      /// Mask specifying all memory types.
55      const ALL            = FMOD_MEMORY_ALL;
56    }
57}
58
59impl From<FMOD_MEMORY_TYPE> for MemoryFlags {
60    fn from(value: FMOD_MEMORY_TYPE) -> Self {
61        MemoryFlags::from_bits_truncate(value)
62    }
63}
64
65impl From<MemoryFlags> for FMOD_MEMORY_TYPE {
66    fn from(value: MemoryFlags) -> Self {
67        value.bits()
68    }
69}
70
71/// Specifies a method for FMOD to allocate and free memory, either through user supplied callbacks or through a user supplied memory buffer with a fixed size.
72///
73/// # Safety
74///
75/// This function must be called before any FMOD System object is created.
76///
77/// If [`MemoryType::Callback::alloc`] and [`MemoryType::Callback::free`] are provided without [`MemoryType::Callback::realloc`]
78/// the reallocation is implemented via an allocation of the new size, copy from old address to new, then a free of the old address.
79///
80/// Callback implementations must be thread safe.
81///
82/// If you specify a fixed size pool that is too small, FMOD will return [`FMOD_RESULT::FMOD_ERR_MEMORY`] when the limit of the fixed size pool is exceeded.
83/// At this point, it's possible that FMOD may become unstable. To maintain stability, do not allow FMOD to run out of memory.
84/// To find out the required fixed size call [`initialize`] with an overly large pool size (or no pool) and find out the maximum RAM usage at any one time with [`get_stats`].
85/// The size of the pool is limited to [`c_int::MAX`].
86pub unsafe fn initialize(memory_type: MemoryType, flags: MemoryFlags) -> Result<()> {
87    match memory_type {
88        MemoryType::Pool(pool) => unsafe {
89            FMOD_Memory_Initialize(
90                pool.as_mut_ptr().cast(),
91                pool.len() as c_int,
92                None,
93                None,
94                None,
95                flags.into(),
96            )
97            .to_result()
98        },
99        MemoryType::Callback {
100            alloc,
101            realloc,
102            free,
103        } => unsafe {
104            FMOD_Memory_Initialize(
105                std::ptr::null_mut(),
106                0,
107                Some(alloc),
108                realloc,
109                Some(free),
110                flags.into(),
111            )
112            .to_result()
113        },
114    }
115}
116
117/// Returns information on the memory usage of FMOD.
118///
119/// This information is byte accurate and counts all allocs and frees internally.
120/// This is useful for determining a fixed memory size to make FMOD work within for fixed memory machines such as consoles.
121///
122/// Note that if using [`initialize`], the memory usage will be slightly higher than without it, as FMOD has to have a small amount of memory overhead to manage the available memory.
123pub fn get_stats(blocking: bool) -> Result<(c_int, c_int)> {
124    let mut current = 0;
125    let mut max = 0;
126    unsafe {
127        FMOD_Memory_GetStats(&raw mut current, &raw mut max, blocking.into()).to_result()?;
128    }
129    Ok((current, max))
130}