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}