use std;
use std::rc::Rc;
use std::ops::{Deref, DerefMut};
use log;
use crate::{ll, fmod_result, vector, Channel, ChannelControl, Dsp, DspRef,
Delay, Error, Mode, Sound, System};
#[derive(Clone, Debug, PartialEq)]
pub struct ChannelGroup {
inner : Rc <Inner>,
system : System
}
#[derive(Clone, Debug, PartialEq)]
pub struct ChannelGroupRef {
pub (crate) channel_group : ChannelGroup
}
#[derive(PartialEq)]
struct Inner {
raw : *mut ll::FMOD_CHANNELGROUP,
owned : bool
}
impl ChannelGroup {
#[inline]
pub (crate) fn from_raw_parts (
raw : *mut ll::FMOD_CHANNELGROUP,
owned : bool,
system : System
) -> Self {
let inner = Rc::new (Inner { raw, owned });
ChannelGroup { inner, system }
}
#[inline]
pub fn is_master (&self) -> Result <bool, Error> {
let master = self.system.get_master_channel_group()?;
Ok (self.raw() == master.raw())
}
#[inline]
pub fn raw (&self) -> *mut ll::FMOD_CHANNELGROUP {
self.inner.raw
}
#[inline]
pub (crate) fn raw_mut (&mut self) -> *mut ll::FMOD_CHANNELGROUP {
self.inner.raw
}
#[inline]
pub fn channel_group_ref (&self) -> ChannelGroupRef {
let channel_group = {
let inner = Rc::new (Inner {
raw: self.raw(),
owned: false
});
let system = self.system.clone();
ChannelGroup { inner, system }
};
ChannelGroupRef { channel_group }
}
#[inline]
pub fn get_channel (&self, index : i32) -> Result <Channel, Error> {
let mut raw_channel = std::ptr::null_mut();
let mut raw_sound = std::ptr::null_mut();
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetChannel (self.raw(), index, &mut raw_channel)
)?;
}
unsafe {
fmod_result!(
ll::FMOD_Channel_GetCurrentSound (raw_channel, &mut raw_sound)
)?;
}
let sound = Sound::from_raw_parts (raw_sound, false, self.system.clone());
Ok (Channel::from_raw_parts (raw_channel, sound.sound_ref()))
}
#[inline]
pub fn get_group (&self, index : i32) -> Result <ChannelGroupRef, Error> {
let mut raw = std::ptr::null_mut();
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetGroup (self.raw(), index, &mut raw)
)?;
}
let channel_group =
ChannelGroup::from_raw_parts (raw, false, self.system.clone());
Ok (ChannelGroupRef { channel_group })
}
#[inline]
pub fn get_name (&self) -> Result <String, Error> {
let namelen = 256i32;
#[expect(clippy::cast_sign_loss)]
let mut name = vec![0; namelen as usize];
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetName (self.raw(),
name.as_mut_ptr() as *mut i8, namelen
))?;
}
name.retain (|c| *c != 0x0);
String::from_utf8 (name).map_err (|e|{
log::error!("channel group get name string invalid utf8: {e}");
Error::InvalidString
})
}
#[inline]
pub fn get_num_channels (&self) -> Result <i32, Error> {
let mut numchannels = 0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetNumChannels (self.raw(), &mut numchannels)
)?;
}
Ok (numchannels)
}
#[inline]
pub fn get_num_groups (&self) -> Result <i32, Error> {
let mut numgroups = 0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetNumGroups (self.raw(), &mut numgroups)
)?;
}
Ok (numgroups)
}
#[inline]
pub fn get_parent_group (&self) -> Result <Option <ChannelGroupRef>, Error> {
let mut raw = std::ptr::null_mut();
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetParentGroup (self.raw(), &mut raw)
)?;
}
if !self.is_master()? {
let channel_group =
ChannelGroup::from_raw_parts (raw, false, self.system.clone());
Ok (Some (ChannelGroupRef { channel_group }))
} else {
debug_assert_eq!(raw, std::ptr::null_mut());
Ok (None)
}
}
}
impl ChannelControl for ChannelGroup {
#[inline]
fn add_dsp (&mut self, index : i32, dsp : &mut Dsp) -> Result <(), Error> {
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_AddDSP (self.raw(), index, dsp.raw())
)?;
}
Ok (())
}
#[inline]
fn add_fade_point (&mut self, dspclock : u64, volume : f32)
-> Result <(), Error>
{
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_AddFadePoint (
self.raw_mut(), dspclock, volume))
}
}
#[inline]
fn get_3d_attributes (&self) -> Result <([f32; 3], [f32; 3]), Error> {
let mut pos = vector::to_ll ([0.0; 3]);
let mut vel = vector::to_ll ([0.0; 3]);
let mut alt_pan_pos = vector::to_ll ([0.0; 3]); unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DAttributes (self.raw(),
&mut pos, &mut vel, &mut alt_pan_pos)
)?;
}
Ok ((vector::from_ll (pos), vector::from_ll (vel)))
}
#[inline]
fn get_3d_cone_orientation (&self) -> Result <[f32; 3], Error> {
let mut orientation = vector::to_ll ([0.0; 3]);
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_Get3DConeOrientation (self.raw(), &mut orientation)
)?;
}
Ok (vector::from_ll (orientation))
}
#[inline]
fn get_3d_cone_settings (&self) -> Result <(f32, f32, f32), Error> {
let mut insideconeangle = 0.0;
let mut outsideconeangle = 0.0;
let mut outsidevolume = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DConeSettings (self.raw(),
&mut insideconeangle, &mut outsideconeangle, &mut outsidevolume
))?;
}
Ok ((insideconeangle, outsideconeangle, outsidevolume))
}
fn get_3d_custom_rolloff (&self) -> Result <Vec <[f32; 3]>, Error> {
let mut points = std::ptr::null_mut();
let mut numpoints = 0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_Get3DCustomRolloff (self.raw(),
&mut points, &mut numpoints)
)?;
}
#[expect(clippy::cast_sign_loss)]
let mut curve = Vec::with_capacity (numpoints as usize);
for i in 0..numpoints as isize {
let point = unsafe {
std::ptr::read (points.offset (i) as *const ll::FMOD_VECTOR)
};
curve.push (vector::from_ll (point));
}
Ok (curve)
}
#[inline]
fn get_3d_distance_filter (&self) -> Result <(bool, f32, f32), Error> {
let mut custom = 0;
let mut customlevel = 0.0;
let mut centerfreq = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DDistanceFilter (self.raw(),
&mut custom, &mut customlevel, &mut centerfreq)
)?;
}
let custom = custom != 0;
Ok ((custom, customlevel, centerfreq))
}
#[inline]
fn get_3d_doppler_level (&self) -> Result <f32, Error> {
let mut level = 0.0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_Get3DDopplerLevel (self.raw(), &mut level)
)?;
}
Ok (level)
}
#[inline]
fn get_3d_min_max_distance (&self) -> Result <(f32, f32), Error> {
let mut mindistance = 0.0;
let mut maxdistance = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DMinMaxDistance (self.raw(),
&mut mindistance, &mut maxdistance)
)?;
}
Ok ((mindistance, maxdistance))
}
#[inline]
fn get_3d_occlusion (&self) -> Result <(f32, f32), Error> {
let mut directocclusion = 0.0;
let mut reverbocclusion = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DOcclusion (self.raw(),
&mut directocclusion, &mut reverbocclusion)
)?;
}
Ok ((directocclusion, reverbocclusion))
}
#[inline]
fn get_3d_spread (&self) -> Result <f32, Error> {
let mut angle = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Get3DSpread (self.raw(), &mut angle))?;
}
Ok (angle)
}
#[inline]
fn get_audibility (&self) -> Result <f32, Error> {
let mut audibility = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetAudibility (self.raw(), &mut audibility))?;
}
Ok (audibility)
}
#[inline]
fn get_delay (&self) -> Result <Delay, Error> {
let mut dspclock_start = 0;
let mut dspclock_end = 0;
let mut stopchannels = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetDelay (self.raw(),
&mut dspclock_start,
&mut dspclock_end,
&mut stopchannels
))?;
}
let stopchannels = stopchannels != 0;
Ok (Delay { dspclock_start, dspclock_end, stopchannels })
}
#[inline]
fn get_dsp (&self, index : i32) -> Result <DspRef, Error> {
let mut raw = std::ptr::null_mut();
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetDSP (self.raw(), index, &mut raw)
)?;
}
let dsp = Dsp::from_raw_parts (raw, false, self.system.clone());
Ok (DspRef { dsp })
}
#[inline]
fn get_dsp_clock (&self) -> Result <u64, Error> {
let mut dspclock = 0;
let parentclock = std::ptr::null_mut();
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetDSPClock (self.raw(), &mut dspclock, parentclock)
)?;
}
Ok (dspclock)
}
#[inline]
fn get_dsp_clock_parent (&self) -> Result <u64, Error> {
let dspclock = std::ptr::null_mut();
let mut parentclock = 0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetDSPClock (self.raw(), dspclock, &mut parentclock)
)?;
}
Ok (parentclock)
}
#[inline]
fn get_dsp_index (&self, dsp : &Dsp) -> Result <i32, Error> {
let mut index = 0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetDSPIndex (self.raw(), dsp.raw(), &mut index)
)?;
}
Ok (index)
}
#[inline]
fn get_low_pass_gain (&self) -> Result <f32, Error> {
let mut gain = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetLowPassGain (self.raw(), &mut gain))?;
}
Ok (gain)
}
#[inline]
fn get_mode (&self) -> Result <Mode, Error> {
let mut mode = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetMode (self.raw(), &mut mode))?;
}
Ok (Mode::from_bits (mode).unwrap())
}
fn get_mute (&self) -> Result <bool, Error> {
let mut mute = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetMute (self.raw(), &mut mute))?;
}
Ok (mute != 0)
}
#[inline]
fn get_num_dsps (&self) -> Result <u32, Error> {
let mut num = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetNumDSPs (self.raw(), &mut num))?;
}
debug_assert!(num >= 0);
#[expect(clippy::cast_sign_loss)]
Ok (num as u32)
}
#[inline]
fn get_paused (&self) -> Result <bool, Error> {
let mut paused = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetPaused (self.raw(), &mut paused))?;
}
Ok (paused != 0)
}
#[inline]
fn get_reverb_properties (&self, instance : i32) -> Result <f32, Error> {
let mut wet = 0.0;
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_GetReverbProperties (self.raw(), instance, &mut wet)
)?;
}
Ok (wet)
}
#[inline]
fn get_volume (&self) -> Result <f32, Error> {
let mut volume = 0.0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_GetVolume (self.raw(), &mut volume))?;
}
Ok (volume)
}
#[inline]
fn is_playing (&self) -> Result <bool, Error> {
let mut isplaying = 0;
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_IsPlaying (self.raw(), &mut isplaying))?;
}
Ok (isplaying != 0)
}
#[inline]
fn remove_dsp (&mut self, dsp : &mut Dsp) -> Result <(), Error> {
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_RemoveDSP (self.raw_mut(), dsp.raw())
)
}
}
#[inline]
fn set_3d_attributes (&mut self, pos : [f32; 3], vel : [f32; 3])
-> Result <(), Error>
{
let pos = vector::to_ll (pos);
let vel = vector::to_ll (vel);
const ALT_PAN_POS : ll::FMOD_VECTOR = vector::to_ll ([0.0; 3]);
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_Set3DAttributes (self.raw_mut(), &pos, &vel,
&ALT_PAN_POS)
)
}
}
#[inline]
fn set_delay (&mut self,
dspclock_start : u64, dspclock_end : u64, stopchannels : bool
) -> Result <(), Error> {
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_SetDelay (self.raw_mut(), dspclock_start,
dspclock_end, stopchannels as i32))
}
}
#[inline]
fn set_fade_point_ramp (&mut self, dspclock : u64, volume : f32)
-> Result <(), Error>
{
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_SetFadePointRamp (self.raw_mut(), dspclock, volume))
}
}
#[inline]
fn set_mute (&mut self, mute : bool) -> Result <(), Error> {
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_SetMute (self.raw_mut(), mute as i32))
}
}
#[inline]
fn set_paused (&mut self, paused : bool) -> Result <(), Error> {
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_SetPaused (self.raw_mut(), paused as i32))
}
}
#[inline]
fn set_reverb_properties (&mut self, instance : i32, wet : f32)
-> Result <(), Error>
{
unsafe {
fmod_result!(
ll::FMOD_ChannelGroup_SetReverbProperties (self.raw_mut(), instance, wet))
}
}
#[inline]
fn set_volume (&mut self, volume : f32) -> Result <(), Error> {
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_SetVolume (self.raw_mut(), volume))
}
}
#[inline]
fn stop (&mut self) -> Result <(), Error> {
unsafe {
fmod_result!(ll::FMOD_ChannelGroup_Stop (self.raw_mut()))
}
}
}
impl Deref for ChannelGroupRef {
type Target = ChannelGroup;
fn deref (&self) -> &ChannelGroup {
&self.channel_group
}
}
impl DerefMut for ChannelGroupRef {
fn deref_mut (&mut self) -> &mut ChannelGroup {
&mut self.channel_group
}
}
impl std::fmt::Debug for Inner {
fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Inner {{ raw: {:p}, owned: {} }}", self.raw, self.owned)
}
}
impl Drop for Inner {
fn drop (&mut self) {
if self.owned {
unsafe {
let _ = fmod_result!(ll::FMOD_ChannelGroup_Release (self.raw)).map_err (
|err| log::error!("error releasing FMOD ChannelGroup@{:p}: {:?}",
self.raw, err));
}
}
}
}