lebedec / libfmod Goto Github PK
View Code? Open in Web Editor NEWA library wrapper for integrating FMOD Engine in Rust applications.
License: MIT License
A library wrapper for integrating FMOD Engine in Rust applications.
License: MIT License
There doesn't seem to be an easy way of accessing FFT spectrum information of a channel.
Dereferencing pointers to libfmod::ffi::FMOD_DSP_PARAMETER_FFT
after receiving them via calls to Dsp::get_parameter_data()
in unsafe blocks doesn't sound like the best idea (it doesn't even return any data anyway, which renders the whole procedure useless), while the FFT DSP feature of FMOD is one that does come in handy and should, in my opinion, be properly implemented as part of the binding.
Is there something that could be done here to fix this?
I noticed that a lot of functions are returning types or results they should not be-
For example: All is_valid
functions return FMOD_BOOL
(a typedef for c_int
) which libfmod
interprets as Result<(), libfmod::Error>
, rather than a bool
.
Other functions, like libfmod::Studio::load_bank_memory
take a String
when they really should take bytes. Functions like these are expecting a char*
as bytes, not a string. This relegates these functions to be practically useless.
pub fn load_bank_memory(
&self,
buffer: &str,
length: i32,
mode: LoadMemoryMode,
flags: ffi::FMOD_STUDIO_LOAD_BANK_FLAGS,
) -> Result<Bank, Error> {
unsafe {
let mut bank = null_mut();
match ffi::FMOD_Studio_System_LoadBankMemory(
self.pointer,
CString::new(buffer)?.as_ptr(),
length,
mode.into(),
flags,
&mut bank,
) {
ffi::FMOD_OK => Ok(Bank::from(bank)),
error => Err(err_fmod!("FMOD_Studio_System_LoadBankMemory", error)),
}
}
}
Should be:
pub fn load_bank_memory(
&self,
buffer: Vec<u8>,
mode: LoadMemoryMode,
flags: ffi::FMOD_STUDIO_LOAD_BANK_FLAGS,
) -> Result<Bank, Error> {
unsafe {
let mut bank = null_mut();
match ffi::FMOD_Studio_System_LoadBankMemory(
self.pointer,
CString::from_vec_unchecked(buffer).as_ptr(),
buffer.len(),
mode.into(),
flags,
&mut bank,
) {
ffi::FMOD_OK => Ok(Bank::from(bank)),
error => Err(err_fmod!("FMOD_Studio_System_LoadBankMemory", error)),
}
}
}
There's also some functions like libfmod::Bank::get_path
which take in the length of a String? and do nothing with it which makes no sense.
pub fn get_path(&self, size: i32) -> Result<(String, i32), Error> {
unsafe {
let path = CString::from_vec_unchecked(b"".to_vec()).into_raw();
let mut retrieved = i32::default();
match ffi::FMOD_Studio_Bank_GetPath(self.pointer, path, size, &mut retrieved) {
ffi::FMOD_OK => Ok((
CString::from_raw(path)
.into_string()
.map_err(Error::String)?,
retrieved,
)),
error => Err(err_fmod!("FMOD_Studio_Bank_GetPath", error)),
}
}
}
This should be converted to something like this: (matching the C# API pretty much)
// Please excuse my crude indentation -_-
pub fn get_path(&self) -> Result<String, Error> {
unsafe {
let mut retrieved = i32::default();
match ffi::FMOD_Studio_Bank_GetPath(self.pointer, std::ptr::null(), 0, &mut retrieved)
{
ffi::FMOD_OK => Ok("".to_string()),
ffi::FMOD_ERR_TRUNCATED => {
let path = CString::from_vec_unchecked(vec![b""; retrieved as usize]).into_raw();
match ffi::FMOD_Studio_Bank_GetPath(self.pointer, path, retrieved, &mut retrieved) {
ffi::FMOD_OK => Ok(CString::from_raw(path)
.into_string()
.map_err(Error::String)?,
),
error => Err(err_fmod!("FMOD_Studio_Bank_GetPath", error)),
}
}
error => Err(err_fmod!("FMOD_Studio_Bank_GetPath", error)),
}
}
}
It may be a good idea to parse the C# bindings as they more closely rusty style bindings or instead to use libfmod-gen to bootstrap most of the boilerplate code and manually edit the bindings from there.
Hello, I just started using the libfmod crate today. Thank you for the work that you've put into it!
Methods accepting Option<Vector>
pass invalid pointers to FMOD, causing errors.
When calling the function System::set_3d_listener_attributes
, I kept getting nonsense error messages from fmod about my vectors being incorrect. When I added assertions about my vector values before calling the function, the errors would go away. I inspected the code, linked above.
The generated code casts the input Vector
values to FMOD_VECTOR
, takes a reference to them, then casts that reference to a pointer, like this:
pos.map(|value| &value.into() as *const _).unwrap_or(null())
This immediately causes a use-after-free. In practice it seems to cause all of the pointers passed to FMOD to point at the same address, which contains garbage. The correct way to approach this problem is to cast the values beforehand, keeping them owned, then taking references to those values and casting them to pointers:
// at the beginning of the function
let pos: Option<ffi::FMOD_VECTOR> = pos.map(Into::into);
// FFI callsite
pos.as_ref().map(|p| p as *const _).unwrap_or(null())
Here is a playground link that reproduces this issue. The function I created to replace FMOD_System_Set3DListenerAttributes
simply prints the address of each of the pointers. In the version based on the code in libfmod, all of the addresses are the same. In the fixed version, each address is different, which is correct.
I searched the codebase for &\w+\.into\(\) as \*const _
and found 12 total occurrences of this pattern. Those functions are likely also impacted.
The simple fix would be to change the generated code to fix these conversions.
I think it would also be reasonable to unify the FMOD_VECTOR
and Vector
types, which would eliminate this conversion completely.
Hi there!
Firstly, thanks so much for putting together this fantastic crate; I've been able to integrate fmod on my Windows machine and successfully get sounds playing from fmod studio in Rust - this is a huge win!
However, there are a couple of quirks about this crate that I'd like to understand more about:
I am more than happy to contribute the required changes and assist with maintenance, if that's required. ๐
Since September 2023 library version number generation has been changed to simplify support for multiple versions and make choosing a specific one even simpler.
For example for FMOD Engine 2.02.06 just use
libfmod = "2.206"
instead of
libfmod = "2.2.607"
I am currently trying to create a sound from an in-memory byte array in OGG format, using System::create_sound
with Mode::OPENMEMORY
to load the array. The first parameter of the function takes a &str
though, which means that it has to be valid UTF-8 and also not contain any null bytes in it.
I have taken a look at the source code for this crate, and identified what should be changed in order for this to work:
https://github.com/lebedec/libfmod-gen/blob/main/src/generators/lib.rs#L714-L715
I don't know much about Rust FFI or C types so any help is appreciated :)
It is idiomatic to use an environment variable in a build script to determine the location of dependencies not provided, and then produce a build error if the environment variable is not set. I would recommend doing this.
Rust 1.78.0 started correctly surfacing some unsafe assertions within the standard library as panics. Since upgrading to Rust 1.78.0, I've started hitting panics when calling some libfmod functions due to unaligned pointers.
I hit this issue calling System::get_advanced_settings
, which calls AdvancedSettings::try_from
, which uses the to_vec
macro, that invokes slice::from_raw_parts
on a pointer that Rust is asserting is invalid.
The error that Rust panics with in this case is:
unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed
isize::MAX
Based on lebedec/libfmod-gen#7
I think this line consumes the iterator
let source = args.nth(1).unwrap_or(FMOD_SDK_PATH.to_string());
thus this unwrap always defaults to ../libfmod
let destination = args.nth(2).unwrap_or("../libfmod".to_string());
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.