use fmod_sys::*;
use std::{
ffi::{c_float, c_int},
ops::Deref,
os::raw::c_void,
};
use crate::{Channel, ChannelControl, ChannelGroup, panic_wrapper};
use crate::{FmodResultExt, Result};
#[derive(Debug, Clone, Copy)]
pub enum ChannelControlType {
Channel(Channel),
ChannelGroup(ChannelGroup),
}
#[allow(unused_variables)]
pub trait ChannelControlCallback {
fn end(channel_control: ChannelControlType) -> Result<()> {
Ok(())
}
fn virtual_voice(channel_control: ChannelControlType, is_virtual: bool) -> Result<()> {
Ok(())
}
fn sync_point(channel_control: ChannelControlType, sync_point: c_int) -> Result<()> {
Ok(())
}
fn occlusion(
channel_control: ChannelControlType,
direct: &mut c_float,
reverb: &mut c_float,
) -> Result<()> {
Ok(())
}
}
impl Deref for ChannelControlType {
type Target = ChannelControl;
fn deref(&self) -> &Self::Target {
match self {
ChannelControlType::Channel(channel) => channel,
ChannelControlType::ChannelGroup(channel_group) => channel_group,
}
}
}
unsafe extern "C" fn callback_impl<C: ChannelControlCallback>(
channel_control: *mut FMOD_CHANNELCONTROL,
control_type: FMOD_CHANNELCONTROL_TYPE,
callback_type: FMOD_CHANNELCONTROL_CALLBACK_TYPE,
commanddata1: *mut c_void,
commanddata2: *mut c_void,
) -> FMOD_RESULT {
panic_wrapper(|| {
let channel_control = match control_type {
FMOD_CHANNELCONTROL_CHANNEL => {
let channel = unsafe { Channel::from_ffi(channel_control.cast()) };
ChannelControlType::Channel(channel)
}
FMOD_CHANNELCONTROL_CHANNELGROUP => {
let channel_group = unsafe { ChannelGroup::from_ffi(channel_control.cast()) };
ChannelControlType::ChannelGroup(channel_group)
}
_ => return FMOD_RESULT::FMOD_ERR_INVALID_PARAM, };
let result = match callback_type {
FMOD_CHANNELCONTROL_CALLBACK_END => C::end(channel_control),
FMOD_CHANNELCONTROL_CALLBACK_VIRTUALVOICE => {
let is_virtual = unsafe { *commanddata1.cast::<i32>() } != 0;
C::virtual_voice(channel_control, is_virtual)
}
FMOD_CHANNELCONTROL_CALLBACK_SYNCPOINT => {
let sync_point = unsafe { *commanddata1.cast::<c_int>() };
C::sync_point(channel_control, sync_point)
}
FMOD_CHANNELCONTROL_CALLBACK_OCCLUSION => {
let direct = unsafe { &mut *commanddata1.cast::<c_float>() };
let reverb = unsafe { &mut *commanddata2.cast::<c_float>() };
C::occlusion(channel_control, &mut *direct, &mut *reverb)
}
_ => {
eprintln!("warning: unknown callback type {callback_type}");
return FMOD_RESULT::FMOD_OK;
}
};
FMOD_RESULT::from_result(result)
})
}
impl ChannelControl {
pub fn set_callback<C: ChannelControlCallback>(&self) -> Result<()> {
unsafe {
FMOD_ChannelControl_SetCallback(self.inner.as_ptr(), Some(callback_impl::<C>))
.to_result()
}
}
}