fmod/core/channel_control/
callback.rs

1// Copyright (c) 2024 Lily 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 fmod_sys::*;
8use std::{
9    ffi::{c_float, c_int},
10    ops::Deref,
11    os::raw::c_void,
12};
13
14use crate::{Channel, ChannelControl, ChannelGroup};
15
16pub enum ChannelControlType {
17    Channel(Channel),
18    ChannelGroup(ChannelGroup),
19}
20
21#[allow(unused_variables)]
22pub trait ChannelControlCallback {
23    fn end(channel_control: ChannelControlType) -> Result<()> {
24        Ok(())
25    }
26
27    fn virtual_voice(channel_control: ChannelControlType, is_virtual: bool) -> Result<()> {
28        Ok(())
29    }
30
31    fn sync_point(channel_control: ChannelControlType, sync_point: c_int) -> Result<()> {
32        Ok(())
33    }
34
35    // FIXME: is this &mut safe?
36    fn occlusion(
37        channel_control: ChannelControlType,
38        direct: &mut c_float,
39        reverb: &mut c_float,
40    ) -> Result<()> {
41        Ok(())
42    }
43}
44
45impl Deref for ChannelControlType {
46    type Target = ChannelControl;
47
48    fn deref(&self) -> &Self::Target {
49        match self {
50            ChannelControlType::Channel(channel) => channel,
51            ChannelControlType::ChannelGroup(channel_group) => channel_group,
52        }
53    }
54}
55
56unsafe extern "C" fn callback_impl<C: ChannelControlCallback>(
57    channel_control: *mut FMOD_CHANNELCONTROL,
58    control_type: FMOD_CHANNELCONTROL_TYPE,
59    callback_type: FMOD_CHANNELCONTROL_CALLBACK_TYPE,
60    commanddata1: *mut c_void,
61    commanddata2: *mut c_void,
62) -> FMOD_RESULT {
63    let channel_control = match control_type {
64        FMOD_CHANNELCONTROL_CHANNEL => {
65            let channel = Channel::from(channel_control.cast::<FMOD_CHANNEL>());
66            ChannelControlType::Channel(channel)
67        }
68        FMOD_CHANNELCONTROL_CHANNELGROUP => {
69            let channel_group = ChannelGroup::from(channel_control.cast::<FMOD_CHANNELGROUP>());
70            ChannelControlType::ChannelGroup(channel_group)
71        }
72        _ => return FMOD_RESULT::FMOD_ERR_INVALID_PARAM, // this should never happen
73    };
74
75    match callback_type {
76        FMOD_CHANNELCONTROL_CALLBACK_END => C::end(channel_control).into(),
77        FMOD_CHANNELCONTROL_CALLBACK_VIRTUALVOICE => {
78            let is_virtual = unsafe { *commanddata1.cast::<i32>() } != 0;
79            C::virtual_voice(channel_control, is_virtual).into()
80        }
81        FMOD_CHANNELCONTROL_CALLBACK_SYNCPOINT => {
82            let sync_point = unsafe { *commanddata1.cast::<c_int>() };
83            C::sync_point(channel_control, sync_point).into()
84        }
85        FMOD_CHANNELCONTROL_CALLBACK_OCCLUSION => {
86            let direct = unsafe { &mut *commanddata1.cast::<c_float>() };
87            let reverb = unsafe { &mut *commanddata2.cast::<c_float>() };
88            C::occlusion(channel_control, &mut *direct, &mut *reverb).into()
89        }
90        _ => {
91            eprintln!("warning: unknown callback type {callback_type}");
92            FMOD_RESULT::FMOD_OK
93        }
94    }
95}
96
97impl ChannelControl {
98    pub fn set_callback<C: ChannelControlCallback>(&self) -> Result<()> {
99        unsafe { FMOD_ChannelControl_SetCallback(self.inner, Some(callback_impl::<C>)).to_result() }
100    }
101}