fmod/lib.rs
1//! fmod-oxide
2//! Safe rust bindings to the FMOD sound engine.
3//! This crate tries to be as rusty and low-cost as possible, without compromising on any APIs.
4//! Certain APIs, such as loading banks from a pointer, are marked as unsafe, but are still available for use.
5//!
6//! Supports FMOD versions >2.0.2.28 and >2.0.3.07, and Windows/Linux/MacOS/HTML5 platforms.
7//!
8//! Any newer patch-level FMOD versions should compile but might have missing features.
9//!
10//! ### Using this crate
11//!
12//! Due to licensing restrictions this crate can't bundle FMOD, so you'll need to [download](https://www.fmod.com/download) a copy of FMOD yourself.
13//!
14//! Make sure to download from `FMOD Engine` specifically.
15//! 
16//!
17//! After downloading FMOD, you have to tell this crate where FMOD is located.
18//! **If you're on Windows and used the FMOD installer, you don't have to worry about this.**
19//!
20//! The easiest way is to create a cargo config in your project's root.
21//!
22//! ```toml
23//! # `.cargo/config.toml`
24//!
25//! [env]
26//! FMOD_SYS_FMOD_DIRECTORY = { value = "<absolute install path here>" }
27//! ```
28//!
29//! You can also specify a relative install path like so:
30//!
31//! ```toml
32//! # `.cargo/config.toml`
33//!
34//! [env]
35//! FMOD_SYS_FMOD_DIRECTORY = { value = "<install path here>", relative = true }
36//! ```
37//!
38//! (not recommended because rust-analyzer won't know this) Alternatively, you can specify `FMOD_SYS_FMOD_DIRECTORY` when building your project:
39//!
40//! `FMOD_SYS_FMOD_DIRECTORY=<path> cargo run`
41//!
42//! ### Cross compilation
43//!
44//! This crate supports cross compilation and will look for a target-specific FMOD install.
45//!
46//! The logic is quite basic at the moment, but it'll check if `<fmod install dir>/<target os>` exists and use that.
47//!
48//! If no target specific directory was found, it'll default to `<fmod install dir>`
49//!
50//! ### Using with webassembly
51//!
52//! Currently only `wasm32-unknown-emscripten` works well.
53//! `wasm32-unknown-unknown` also works in some capacity but you have to essentially reimplement parts of libc and emscripten.
54//!
55//! Unfortunately `wasm-bindgen` doesn't work without patches right now, so your milage may vary
56//!
57//! The setup is roughly the same, except you'll need to add some arguments to `EMCC_FLAGS`.
58//!
59//! You can do this by editing `.cargo/config.toml`:
60//! ```toml
61//! # `.cargo/config.toml`
62//!
63//! [env]
64//! EMCC_CFLAGS="-s EXPORTED_RUNTIME_METHODS=ccall,cwrap,setValue,getValue" # FMOD requires this
65//! ```
66//!
67//! If you're using `wasm32-unknown-unknown`, you'll additionally need to add this until [this issue](https://github.com/rust-lang/rust/issues/138762) is closed.
68//!
69//! ```toml
70//! # `.cargo/config.toml`
71//!
72//! [build]
73//! rustflags="-Zwasm-c-abi=spec"
74//! ```
75//!
76//! See [`web-examples/emscripten`](web-examples/emscripten) for a more detailed example.
77//!
78//! # Memory management & Copy types
79//!
80//! All FMOD objects are Copy, Clone, Send and Sync because it's possible to have multiple references to the same object. (e.g. loading a bank and then retrieving it by its path)
81//! There are a lot of use-cases where you may want to fetch something (like a bank) and never use it again.
82//! Implementing `Drop` to automatically release things would go against that particular use-case, so this crate opts to have manual `release()` methods instead.
83//!
84//! This crate does not currently guard against use-after-frees, *however* using most of FMOD's types (especially FMOD Studio's types) after calling `release()` is safe.
85//! I'm still not 100% sure of what is and isn't safe and I'm actively trying to test this.
86//!
87//! # String types
88//!
89//! fmod-oxide aims to be as zero-cost as possible, and as such, it uses UTF-8 C strings from the `lanyard` crate as its string type.
90//! This means that all FMOD functions take a `&Utf8CStr` instead of a `&str` or `&CStr`.
91//! `&Utf8CStr` is pretty cheap to construct (and can even be done statically with the `c!` macro), so this should not be a problem
92//!
93//! When FMOD returns a string, it will always return a `Utf8CString` (the owned version of `Utf8CStr`) because it's difficult to encode lifetime requirements of FMOD strings.
94//!
95//! This applies to structs like `fmod::studio::AdvancedSettings` which store C strings.
96//! Converting structs like `AdvancedSettings` to their FFI equivalents is done by reference as to not pass ownership of the string to FMOD
97//!
98//! # Basic example
99//! ```ignore
100//! // System creation is unsafe and must be performed prior to any other FMOD operations.
101//! let mut builder = unsafe { fmod::studio::SystemBuilder::new() }?;
102//! let system = builder.build()?;
103//!
104//! // Load a bank
105//! let bank = system.load_bank_file("path/to/bank.bank", fmod::studio::LoadBankFlags::NORMAL)?;
106//! // Query all events in the bank
107//! for event in bank.get_event_list().unwrap() {
108//! println!("Event: {}", event.get_path()?);
109//! }
110//!
111//! // Releasing Systems is unsafe because it cannot be called concurrently, and all FMOD objects are rendered invalid.
112//! unsafe { system.release() };
113//! ```
114//! # Feature flags
115#![doc = document_features::document_features!()]
116// Used to document cfgs (copied from https://docs.rs/winit/latest/src/winit/lib.rs.html#1-207)
117#![cfg_attr(
118 docsrs,
119 feature(doc_auto_cfg, doc_cfg_hide),
120 doc(cfg_hide(doc, docsrs))
121)]
122#![warn(
123 rust_2018_idioms,
124 clippy::pedantic,
125 missing_debug_implementations,
126 missing_copy_implementations,
127 missing_docs,
128 rustdoc::all
129)]
130#![allow(
131 clippy::missing_errors_doc,
132 clippy::wildcard_imports,
133 clippy::module_name_repetitions,
134 clippy::cast_possible_truncation,
135 clippy::cast_possible_wrap,
136 clippy::cast_sign_loss,
137 clippy::must_use_candidate
138)]
139#![forbid(unsafe_op_in_unsafe_fn)]
140#![doc(html_favicon_url = "https://www.fmod.com/assets/fmod-logo.svg")]
141#![doc(html_logo_url = "https://www.fmod.com/assets/fmod-logo.svg")]
142
143/// How much of FMOD's API fmod-oxide covers.
144#[cfg(any(doc, doctest, test))]
145pub mod coverage {
146 pub mod _2_0_3 {
147 #![doc = include_str!("../COVERAGE.2.03.md")]
148 #[allow(unused_imports)]
149 use fmod_sys::*;
150 }
151 pub mod _2_0_2 {
152 #![doc = include_str!("../COVERAGE.2.02.md")]
153 #[allow(unused_imports)]
154 use fmod_sys::*;
155 }
156}
157
158mod result;
159pub(crate) use result::FmodResultExt;
160pub use result::{Error, Result};
161
162// Not really practical to go no_std.
163// FMOD requires libc on pretty much every platform (even webassembly!)
164// If you're using libc you probably can use std too.
165
166/// The low-level FMOD core API.
167pub mod core;
168
169#[doc(inline)]
170pub use core::*;
171
172#[doc(no_inline)]
173pub use core::effects::*;
174#[doc(no_inline)]
175pub use fmod_sys as sys;
176#[doc(no_inline)]
177pub use lanyard::*;
178
179#[cfg(test)]
180mod tests;
181
182/// The FMOD Studio API.
183///
184/// The Studio API is a more high-level library which is tightly integrated with *FMOD Studio*, FMOD's production tool.
185#[cfg(feature = "studio")]
186pub mod studio;
187
188/// Current FMOD version number.
189///
190/// The version is a 32 bit hexadecimal value formatted as 16:8:8, with the upper 16 bits being the product version,
191/// the middle 8 bits being the major version and the bottom 8 bits being the minor version.
192/// For example a value of `0x00010203` is equal to `1.02.03`.
193pub const VERSION: u32 = fmod_sys::FMOD_VERSION;
194/// The FMOD build number.
195pub const BUILD_NUMBER: u32 = fmod_sys::FMOD_BUILDNUMBER;
196/// Maximum number of channels per sample of audio supported by audio files, buffers, connections and [`Dsp`]s.
197pub const MAX_CHANNEL_WIDTH: u32 = fmod_sys::FMOD_MAX_CHANNEL_WIDTH;
198/// Maximum number of listeners supported.
199pub const MAX_LISTENERS: u32 = fmod_sys::FMOD_MAX_LISTENERS;
200/// The maximum number of global reverb instances.
201///
202/// Each instance of a reverb is an instance of an [`DspType::SfxReverb`] DSP in the DSP graph.
203/// This is unrelated to the number of possible [`Reverb3D`] objects, which is unlimited.
204pub const MAX_REVERB_INSTANCES: u32 = fmod_sys::FMOD_REVERB_MAXINSTANCES;
205/// Maximum number of System objects allowed.
206pub const MAX_SYSTEMS: u32 = fmod_sys::FMOD_MAX_SYSTEMS;
207
208pub(crate) fn panic_wrapper<F>(f: F) -> fmod_sys::FMOD_RESULT
209where
210 F: FnOnce() -> fmod_sys::FMOD_RESULT,
211 F: std::panic::UnwindSafe,
212{
213 let result = std::panic::catch_unwind(f);
214 match result {
215 Ok(r) => r,
216 Err(e) => {
217 print_panic_msg(&e);
218 fmod_sys::FMOD_RESULT::FMOD_OK
219 }
220 }
221}
222
223pub(crate) fn print_panic_msg(msg: &dyn std::any::Any) {
224 if let Some(str) = msg.downcast_ref::<&'static str>() {
225 eprintln!("WARNING: caught {str}");
226 } else if let Some(str) = msg.downcast_ref::<String>() {
227 eprintln!("WARNING: caught {str}");
228 } else {
229 eprintln!("WARNING: caught panic!");
230 }
231}