fmod_sys/
lib.rs

1//! Low level bindings to the FMOD sound engine.
2//!
3//! Due to licensing restrictions, the FMOD API cannot be distributed with this crate.
4//! docs.rs documention is provided for the FMOD API but the actual API is not available without the FMOD library.
5//!
6//! Currently, this crate provides both core and studio bindings (no fsbank bindings).
7//!
8//! # Configuration
9//! In debug builds this crate will link against the logging version of the FMOD library.
10//! In release builds this crate will link against the release version of the FMOD library.
11//! You can force the debug version of the library by enabling the `force-debug` feature.
12//!
13//! By default, this crate will search for an FMOD installation directory in the following places:
14//!
15//! (On windows)
16//! - `C:/Program Files (x86)/FMOD SoundSystem/FMOD Studio API Windows`
17//! - `D:/Program Files (x86)/FMOD SoundSystem/FMOD Studio API Windows`
18//! - `$OUT_DIR/FMOD Studio API Windows`
19//! - `$OUT_DIR/FMOD SoundSystem`
20//!
21//! (On all platforms)
22//! - `$CARGO_MANIFEST_DIR/fmod`
23//! - `$OUT_DIR/fmod`
24//! - `$FMOD_SYS_FMOD_DIRECTORY`
25//!
26//! This installation is expected to follow the standard FMOD directory structure.
27//!
28//! fmod:
29//!  - api:
30//!    - core:
31//!      - inc
32//!      - lib (contains architecture specific directories)
33//!    - studio:
34//!      - inc
35//!      - lib (contains architecture specific directories)
36#![allow(non_upper_case_globals)]
37#![allow(non_camel_case_types)]
38#![allow(non_snake_case)]
39#![doc(html_favicon_url = "https://www.fmod.com/assets/fmod-logo.svg")]
40#![doc(html_logo_url = "https://www.fmod.com/assets/fmod-logo.svg")]
41
42#[cfg(any(docsrs, feature = "force-docs-bindings"))]
43include!("../docs/documentation.rs");
44
45#[cfg(not(any(docsrs, feature = "force-docs-bindings")))]
46include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
47
48#[derive(Clone, PartialEq, Eq)]
49pub enum Error {
50    Fmod(FMOD_RESULT), // FIXME make FMOD_RESULT be a NonZero
51    NulError(std::ffi::NulError),
52    EnumFromPrivitive { name: &'static str, primitive: i64 },
53}
54
55impl std::fmt::Debug for Error {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        let mut debug_struct = f.debug_struct("Error");
58        match self {
59            Self::Fmod(code) => debug_struct
60                .field("code", code)
61                .field("message", &error_code_to_str(*code))
62                .finish(),
63            Self::NulError(e) => debug_struct.field("nul error", e).finish(),
64            Self::EnumFromPrivitive { name, primitive } => debug_struct
65                .field("name", name)
66                .field("primitive", primitive)
67                .finish(),
68        }
69    }
70}
71
72impl std::fmt::Display for Error {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            Self::Fmod(code) => f.write_str(error_code_to_str(*code)),
76            Self::NulError(error) => error.fmt(f),
77            Self::EnumFromPrivitive { name, primitive } => f.write_fmt(format_args!(
78                "No discriminant in enum `{name}` matches the value `{primitive:?}"
79            )),
80        }
81    }
82}
83
84impl std::error::Error for Error {}
85
86pub type Result<T> = std::result::Result<T, Error>;
87
88impl From<FMOD_RESULT> for Error {
89    fn from(value: FMOD_RESULT) -> Self {
90        Self::Fmod(value)
91    }
92}
93
94impl From<std::ffi::NulError> for Error {
95    fn from(value: std::ffi::NulError) -> Self {
96        Self::NulError(value)
97    }
98}
99
100impl<T> From<num_enum::TryFromPrimitiveError<T>> for Error
101where
102    T: num_enum::TryFromPrimitive,
103    T::Primitive: Into<i64>,
104{
105    fn from(value: num_enum::TryFromPrimitiveError<T>) -> Self {
106        Self::EnumFromPrivitive {
107            name: T::NAME,
108            primitive: value.number.into(),
109        }
110    }
111}
112
113impl From<FMOD_RESULT> for Result<()> {
114    fn from(value: FMOD_RESULT) -> Self {
115        if matches!(value, FMOD_RESULT::FMOD_OK) {
116            Ok(())
117        } else {
118            Err(Error::Fmod(value))
119        }
120    }
121}
122
123impl<T> From<Result<T>> for FMOD_RESULT {
124    fn from(value: Result<T>) -> Self {
125        match value {
126            Ok(_) => FMOD_RESULT::FMOD_OK,
127            Err(e) => e.into(),
128        }
129    }
130}
131
132impl PartialEq<FMOD_RESULT> for Error {
133    fn eq(&self, other: &FMOD_RESULT) -> bool {
134        match self {
135            Self::Fmod(code) => code == other,
136            _ => false,
137        }
138    }
139}
140
141impl From<Error> for FMOD_RESULT {
142    fn from(val: Error) -> Self {
143        match val {
144            Error::Fmod(code) => code,
145            Error::NulError(_) => FMOD_RESULT::FMOD_ERR_INVALID_PARAM,
146            Error::EnumFromPrivitive { .. } => FMOD_RESULT::FMOD_ERR_INVALID_PARAM,
147        }
148    }
149}
150
151impl FMOD_RESULT {
152    pub fn to_result(self) -> Result<()> {
153        self.into()
154    }
155
156    pub fn to_error(self) -> Option<Error> {
157        self.to_result().err()
158    }
159}
160
161impl From<FMOD_BOOL> for bool {
162    fn from(val: FMOD_BOOL) -> Self {
163        val.0 > 0
164    }
165}
166
167impl From<bool> for FMOD_BOOL {
168    fn from(value: bool) -> Self {
169        Self(value as _)
170    }
171}
172
173impl FMOD_BOOL {
174    pub const FALSE: Self = Self(0);
175    pub const TRUE: Self = Self(1);
176}
177
178#[allow(non_snake_case)]
179pub const fn error_code_to_str(result: FMOD_RESULT) -> &'static str {
180    match result
181    {
182        FMOD_RESULT::FMOD_OK=>                             "No errors.",
183        FMOD_RESULT::FMOD_ERR_BADCOMMAND=>                 "Tried to call a function on a data type that does not allow this type of functionality (ie calling Sound=>=>lock on a streaming sound).",
184        FMOD_RESULT::FMOD_ERR_CHANNEL_ALLOC=>              "Error trying to allocate a channel.",
185        FMOD_RESULT::FMOD_ERR_CHANNEL_STOLEN=>             "The specified channel has been reused to play another sound.",
186        FMOD_RESULT::FMOD_ERR_DMA=>                        "DMA Failure.  See debug output for more information.",
187        FMOD_RESULT::FMOD_ERR_DSP_CONNECTION=>             "DSP connection error.  Connection possibly caused a cyclic dependency or connected dsps with incompatible buffer counts.",
188        FMOD_RESULT::FMOD_ERR_DSP_DONTPROCESS=>            "DSP  code from a DSP process query callback.  Tells mixer not to call the process callback and therefore not consume CPU.  Use this to optimize the DSP graph.",
189        FMOD_RESULT::FMOD_ERR_DSP_FORMAT=>                 "DSP Format error.  A DSP unit may have attempted to connect to this network with the wrong format, or a matrix may have been set with the wrong size if the target unit has a specified channel map.",
190        FMOD_RESULT::FMOD_ERR_DSP_INUSE=>                  "DSP is already in the mixer's DSP network. It must be removed before being reinserted or released.",
191        FMOD_RESULT::FMOD_ERR_DSP_NOTFOUND=>               "DSP connection error.  Couldn't find the DSP unit specified.",
192        FMOD_RESULT::FMOD_ERR_DSP_RESERVED=>               "DSP operation error.  Cannot perform operation on this DSP as it is reserved by the system.",
193        FMOD_RESULT::FMOD_ERR_DSP_SILENCE=>                "DSP  code from a DSP process query callback.  Tells mixer silence would be produced from read, so go idle and not consume CPU.  Use this to optimize the DSP graph.",
194        FMOD_RESULT::FMOD_ERR_DSP_TYPE=>                   "DSP operation cannot be performed on a DSP of this type.",
195        FMOD_RESULT::FMOD_ERR_FILE_BAD=>                   "Error loading file.",
196        FMOD_RESULT::FMOD_ERR_FILE_COULDNOTSEEK=>          "Couldn't perform seek operation.  This is a limitation of the medium (ie netstreams) or the file format.",
197        FMOD_RESULT::FMOD_ERR_FILE_DISKEJECTED=>           "Media was ejected while reading.",
198        FMOD_RESULT::FMOD_ERR_FILE_EOF=>                   "End of file unexpectedly reached while trying to read essential data (truncated?).",
199        FMOD_RESULT::FMOD_ERR_FILE_ENDOFDATA=>             "End of current chunk reached while trying to read data.",
200        FMOD_RESULT::FMOD_ERR_FILE_NOTFOUND=>              "File not found.",
201        FMOD_RESULT::FMOD_ERR_FORMAT=>                     "Unsupported file or audio format.",
202        FMOD_RESULT::FMOD_ERR_HEADER_MISMATCH=>            "There is a version mismatch between the FMOD header and either the FMOD Studio library or the FMOD Low Level library.",
203        FMOD_RESULT::FMOD_ERR_HTTP=>                       "A HTTP error occurred. This is a catch-all for HTTP errors not listed elsewhere.",
204        FMOD_RESULT::FMOD_ERR_HTTP_ACCESS=>                "The specified resource requires authentication or is forbidden.",
205        FMOD_RESULT::FMOD_ERR_HTTP_PROXY_AUTH=>            "Proxy authentication is required to access the specified resource.",
206        FMOD_RESULT::FMOD_ERR_HTTP_SERVER_ERROR=>          "A HTTP server error occurred.",
207        FMOD_RESULT::FMOD_ERR_HTTP_TIMEOUT=>               "The HTTP request timed out.",
208        FMOD_RESULT::FMOD_ERR_INITIALIZATION=>             "FMOD was not initialized correctly to support this function.",
209        FMOD_RESULT::FMOD_ERR_INITIALIZED=>                "Cannot call this command after System=>=>init.",
210        FMOD_RESULT::FMOD_ERR_INTERNAL=>                   "An error occured in the FMOD system. Use the logging version of FMOD for more information.",
211        FMOD_RESULT::FMOD_ERR_INVALID_FLOAT=>              "Value passed in was a NaN, Inf or denormalized float.",
212        FMOD_RESULT::FMOD_ERR_INVALID_HANDLE=>             "An invalid object handle was used.",
213        FMOD_RESULT::FMOD_ERR_INVALID_PARAM=>              "An invalid parameter was passed to this function.",
214        FMOD_RESULT::FMOD_ERR_INVALID_POSITION=>           "An invalid seek position was passed to this function.",
215        FMOD_RESULT::FMOD_ERR_INVALID_SPEAKER=>            "An invalid speaker was passed to this function based on the current speaker mode.",
216        FMOD_RESULT::FMOD_ERR_INVALID_SYNCPOINT=>          "The syncpoint did not come from this sound handle.",
217        FMOD_RESULT::FMOD_ERR_INVALID_THREAD=>             "Tried to call a function on a thread that is not supported.",
218        FMOD_RESULT::FMOD_ERR_INVALID_VECTOR=>             "The vectors passed in are not unit length, or perpendicular.",
219        FMOD_RESULT::FMOD_ERR_MAXAUDIBLE=>                 "Reached maximum audible playback count for this sound's soundgroup.",
220        FMOD_RESULT::FMOD_ERR_MEMORY=>                     "Not enough memory or resources.",
221        FMOD_RESULT::FMOD_ERR_MEMORY_CANTPOINT=>           "Can't use FMOD_OPENMEMORY_POINT on non PCM source data, or non mp3/xma/adpcm data if FMOD_CREATECOMPRESSEDSAMPLE was used.",
222        FMOD_RESULT::FMOD_ERR_NEEDS3D=>                    "Tried to call a command on a 2d sound when the command was meant for 3d sound.",
223        FMOD_RESULT::FMOD_ERR_NEEDSHARDWARE=>              "Tried to use a feature that requires hardware support.",
224        FMOD_RESULT::FMOD_ERR_NET_CONNECT=>                "Couldn't connect to the specified host.",
225        FMOD_RESULT::FMOD_ERR_NET_SOCKET_ERROR=>           "A socket error occurred.  This is a catch-all for socket-related errors not listed elsewhere.",
226        FMOD_RESULT::FMOD_ERR_NET_URL=>                    "The specified URL couldn't be resolved.",
227        FMOD_RESULT::FMOD_ERR_NET_WOULD_BLOCK=>            "Operation on a non-blocking socket could not complete immediately.",
228        FMOD_RESULT::FMOD_ERR_NOTREADY=>                   "Operation could not be performed because specified sound/DSP connection is not ready.",
229        FMOD_RESULT::FMOD_ERR_OUTPUT_ALLOCATED=>           "Error initializing output device, but more specifically, the output device is already in use and cannot be reused.",
230        FMOD_RESULT::FMOD_ERR_OUTPUT_CREATEBUFFER=>        "Error creating hardware sound buffer.",
231        FMOD_RESULT::FMOD_ERR_OUTPUT_DRIVERCALL=>          "A call to a standard soundcard driver failed, which could possibly mean a bug in the driver or resources were missing or exhausted.",
232        FMOD_RESULT::FMOD_ERR_OUTPUT_FORMAT=>              "Soundcard does not support the specified format.",
233        FMOD_RESULT::FMOD_ERR_OUTPUT_INIT=>                "Error initializing output device.",
234        FMOD_RESULT::FMOD_ERR_OUTPUT_NODRIVERS=>           "The output device has no drivers installed.  If pre-init, FMOD_OUTPUT_NOSOUND is selected as the output mode.  If post-init, the function just fails.",
235        FMOD_RESULT::FMOD_ERR_PLUGIN=>                     "An unspecified error has been ed from a plugin.",
236        FMOD_RESULT::FMOD_ERR_PLUGIN_MISSING=>             "A requested output, dsp unit type or codec was not available.",
237        FMOD_RESULT::FMOD_ERR_PLUGIN_RESOURCE=>            "A resource that the plugin requires cannot be allocated or found. (ie the DLS file for MIDI playback)",
238        FMOD_RESULT::FMOD_ERR_PLUGIN_VERSION=>             "A plugin was built with an unsupported SDK version.",
239        FMOD_RESULT::FMOD_ERR_RECORD=>                     "An error occurred trying to initialize the recording device.",
240        FMOD_RESULT::FMOD_ERR_REVERB_CHANNELGROUP=>        "Reverb properties cannot be set on this channel because a parent channelgroup owns the reverb connection.",
241        FMOD_RESULT::FMOD_ERR_REVERB_INSTANCE=>            "Specified instance in FMOD_REVERB_PROPERTIES couldn't be set. Most likely because it is an invalid instance number or the reverb doesn't exist.",
242        FMOD_RESULT::FMOD_ERR_SUBSOUNDS=>                  "The error occurred because the sound referenced contains subsounds when it shouldn't have, or it doesn't contain subsounds when it should have.  The operation may also not be able to be performed on a parent sound.",
243        FMOD_RESULT::FMOD_ERR_SUBSOUND_ALLOCATED=>         "This subsound is already being used by another sound, you cannot have more than one parent to a sound.  Null out the other parent's entry first.",
244        FMOD_RESULT::FMOD_ERR_SUBSOUND_CANTMOVE=>          "Shared subsounds cannot be replaced or moved from their parent stream, such as when the parent stream is an FSB file.",
245        FMOD_RESULT::FMOD_ERR_TAGNOTFOUND=>                "The specified tag could not be found or there are no tags.",
246        FMOD_RESULT::FMOD_ERR_TOOMANYCHANNELS=>            "The sound created exceeds the allowable input channel count.  This can be increased using the 'maxinputchannels' parameter in System=>=>setSoftwareFormat.",
247        FMOD_RESULT::FMOD_ERR_TRUNCATED=>                  "The retrieved string is too long to fit in the supplied buffer and has been truncated.",
248        FMOD_RESULT::FMOD_ERR_UNIMPLEMENTED=>              "Something in FMOD hasn't been implemented when it should be. Contact support.",
249        FMOD_RESULT::FMOD_ERR_UNINITIALIZED=>              "This command failed because System=>=>init or System=>=>setDriver was not called.",
250        FMOD_RESULT::FMOD_ERR_UNSUPPORTED=>                "A command issued was not supported by this object.  Possibly a plugin without certain callbacks specified.",
251        FMOD_RESULT::FMOD_ERR_VERSION=>                    "The version number of this file format is not supported.",
252        FMOD_RESULT::FMOD_ERR_EVENT_ALREADY_LOADED=>       "The specified bank has already been loaded.",
253        FMOD_RESULT::FMOD_ERR_EVENT_LIVEUPDATE_BUSY=>      "The live update connection failed due to the game already being connected.",
254        FMOD_RESULT::FMOD_ERR_EVENT_LIVEUPDATE_MISMATCH=>  "The live update connection failed due to the game data being out of sync with the tool.",
255        FMOD_RESULT::FMOD_ERR_EVENT_LIVEUPDATE_TIMEOUT=>   "The live update connection timed out.",
256        FMOD_RESULT::FMOD_ERR_EVENT_NOTFOUND=>             "The requested event, parameter, bus or vca could not be found.",
257        FMOD_RESULT::FMOD_ERR_STUDIO_UNINITIALIZED=>       "The Studio=>=>System object is not yet initialized.",
258        FMOD_RESULT::FMOD_ERR_STUDIO_NOT_LOADED=>          "The specified resource is not loaded, so it can't be unloaded.",
259        FMOD_RESULT::FMOD_ERR_INVALID_STRING=>             "An invalid string was passed to this function.",
260        FMOD_RESULT::FMOD_ERR_ALREADY_LOCKED=>             "The specified resource is already locked.",
261        FMOD_RESULT::FMOD_ERR_NOT_LOCKED=>                 "The specified resource is not locked, so it can't be unlocked.",
262        FMOD_RESULT::FMOD_ERR_RECORD_DISCONNECTED=>        "The specified recording driver has been disconnected.",
263        FMOD_RESULT::FMOD_ERR_TOOMANYSAMPLES=>             "The length provided exceeds the allowable limit.",
264        _ =>                                               "Unknown error.",
265    }
266}