Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(documentation/example) FMOD callbacks to bevy #69

Open
passivedragon opened this issue Jan 29, 2024 · 2 comments
Open

(documentation/example) FMOD callbacks to bevy #69

passivedragon opened this issue Jan 29, 2024 · 2 comments
Labels
enhancement New feature or request
Milestone

Comments

@passivedragon
Copy link

passivedragon commented Jan 29, 2024

One of the more advanced features of FMOD allows pulling information back out of FMOD at runtime.
I've been trying to figure out how to do so with bevy_fmod recently. While I can register callbacks, capturing bevy context to trigger Events for example has been proving... a bit more challenging.

It seems useful enough to me to consider adding an example for such useage (if possible at all).

Has anyone attempted to do this before with bevy? I'll share some code snippets as examples for what I mean.

basic callback example:

use bevy::prelude::*;
use bevy_fmod::fmod_studio::FmodStudio;
use libfmod::ffi::{FMOD_OK, FMOD_RESULT, FMOD_STUDIO_EVENTINSTANCE, FMOD_STUDIO_EVENT_CALLBACK_TYPE};

// Implement a function with the expected FFI signature that calls the closure
extern "C" fn callback_bridge<F>(
    param1: u32,
    param2: *mut FMOD_STUDIO_EVENTINSTANCE,
    param3: *mut std::ffi::c_void,
) -> i32
where
    F: FnMut(u32, *mut FMOD_STUDIO_EVENTINSTANCE, *mut std::ffi::c_void) -> i32,
{
    unsafe {
        // Get the closure from the context data
        let callback_wrapper: &mut CallbackWrapper<F> = &mut *(param3 as *mut CallbackWrapper<F>);

        // Call the closure with the provided parameters
        (callback_wrapper.callback)(param1, param2, param3)
    }
}

impl<F> CallbackWrapper<F>
where
    F: FnMut(u32, *mut FMOD_STUDIO_EVENTINSTANCE, *mut std::ffi::c_void) -> i32,
{
    // Function to create a new CallbackWrapper
    fn new(callback: F) -> CallbackWrapper<F> {
        CallbackWrapper { callback }
    }

    // Function to get the function pointer to the bridge function
    fn get_callback_bridge(&self) -> libfmod::ffi::FMOD_STUDIO_EVENT_CALLBACK {
        Some(callback_bridge::<F>)
    }
}

pub fn setup_fmod_callbacks_system(
    studio: Res<FmodStudio>
) {
   let mut my_closure = move |param1: u32,
                               param2: *mut FMOD_STUDIO_EVENTINSTANCE,
                               param3: *mut std::ffi::c_void| {
        print!("triggered by FMOD event");
        FMOD_OK
    };

    let callback_wrapper = CallbackWrapper::new(my_closure);
    let callback_bridge = callback_wrapper.get_callback_bridge();

    let _ = studio
        .0
        .get_event("event:/my-event")
        .unwrap()
        .set_callback(callback_bridge, 0xFFFF_FFFFu32);
}

This reliably triggers when the appropriate FMOD event triggers, multiple times due to the catch_all mask FFFF_FFFF.

But as for relaying this information to bevy... I've tried an approach using crossbeam-channel.

@passivedragon
Copy link
Author

My bevy attempts so far ranged from bevy EventWriters constructions such as NonSend<EventWriter<FmodEvent>>, up to my most recent approach that compiles but unfortunately segfaults at runtime.

#[derive(Resource)]
struct FmodReceiver(Receiver<LevelEvent>);

#[derive(Event)]
pub struct FmodEvent;


pub fn setup_fmod_callbacks_system(
    studio: Res<FmodStudio>
) {
    let (sender, receiver): (Sender<FmodEvent>, Receiver<FmodEvent>) = unbounded();

    commands.insert_resource(FmodReceiver(receiver));
    
   let mut my_closure = move |param1: u32,
                               param2: *mut FMOD_STUDIO_EVENTINSTANCE,
                               param3: *mut std::ffi::c_void| {
        print!("triggered by FMOD event");
        // Send the event to the main thread
        sender.send(FmodEvent::new()).expect("crossbeams sender failed"); // doesn't print, just segfaults
        FMOD_OK
    };

    let callback_wrapper = CallbackWrapper::new(my_closure);
    let callback_bridge = callback_wrapper.get_callback_bridge();

    let _ = studio
        .0
        .get_event("event:/my-event")
        .unwrap()
        .set_callback(callback_bridge, 0xFFFF_FFFFu32);
}

@Salzian Salzian added the enhancement New feature or request label Jan 31, 2024
@Salzian
Copy link
Owner

Salzian commented Jan 31, 2024

I'll take a look at this over the weekend.

@Salzian Salzian added this to the v1.0 milestone Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants