Skip to main content

fmod/
channel_group.rs

1use std;
2use std::rc::Rc;
3use std::ops::{Deref, DerefMut};
4use log;
5use crate::{ll, fmod_result, vector, Channel, ChannelControl, Dsp, DspRef,
6  Delay, Error, Mode, Sound, System};
7
8#[derive(Clone, Debug, PartialEq)]
9pub struct ChannelGroup {
10  inner  : Rc <Inner>,
11  system : System
12}
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct ChannelGroupRef {
16  pub (crate) channel_group : ChannelGroup
17}
18
19#[derive(PartialEq)]
20struct Inner {
21  raw    : *mut ll::FMOD_CHANNELGROUP,
22  owned  : bool
23}
24
25impl ChannelGroup {
26  #[inline]
27  pub (crate) fn from_raw_parts (
28    raw    : *mut ll::FMOD_CHANNELGROUP,
29    owned  : bool,
30    system : System
31  ) -> Self {
32    let inner = Rc::new (Inner { raw, owned });
33    ChannelGroup { inner, system }
34  }
35
36  #[inline]
37  pub fn is_master (&self) -> Result <bool, Error> {
38    let master = self.system.get_master_channel_group()?;
39    Ok (self.raw() == master.raw())
40  }
41
42  #[inline]
43  pub fn raw (&self) -> *mut ll::FMOD_CHANNELGROUP {
44    self.inner.raw
45  }
46
47  #[inline]
48  pub (crate) fn raw_mut (&mut self) -> *mut ll::FMOD_CHANNELGROUP {
49    self.inner.raw
50  }
51
52  #[inline]
53  pub fn channel_group_ref (&self) -> ChannelGroupRef {
54    let channel_group = {
55      let inner = Rc::new (Inner {
56        raw:    self.raw(),
57        owned:  false
58      });
59      let system = self.system.clone();
60      ChannelGroup { inner, system }
61    };
62    ChannelGroupRef { channel_group }
63  }
64
65  /// Retrieves the specified channel from the channel group for the index from
66  /// 0 to the number of channels returned by `channel_group.get_num_channels()`
67  #[inline]
68  pub fn get_channel (&self, index : i32) -> Result <Channel, Error> {
69    let mut raw_channel = std::ptr::null_mut();
70    let mut raw_sound   = std::ptr::null_mut();
71    unsafe {
72      fmod_result!(
73        ll::FMOD_ChannelGroup_GetChannel (self.raw(), index, &mut raw_channel)
74      )?;
75    }
76    unsafe {
77      fmod_result!(
78        ll::FMOD_Channel_GetCurrentSound (raw_channel, &mut raw_sound)
79      )?;
80    }
81    let sound = Sound::from_raw_parts (raw_sound, false, self.system.clone());
82    Ok (Channel::from_raw_parts (raw_channel, sound.sound_ref()))
83  }
84
85  /// Retrieves a handle to a specified sub channel group
86  #[inline]
87  pub fn get_group (&self, index : i32) -> Result <ChannelGroupRef, Error> {
88    let mut raw = std::ptr::null_mut();
89    unsafe {
90      fmod_result!(
91        ll::FMOD_ChannelGroup_GetGroup (self.raw(), index, &mut raw)
92      )?;
93    }
94    // NOTE: this assumes a sub group is never the master channel group
95    let channel_group =
96      ChannelGroup::from_raw_parts (raw, false, self.system.clone());
97    Ok (ChannelGroupRef { channel_group })
98  }
99
100  #[inline]
101  pub fn get_name (&self) -> Result <String, Error> {
102    // NB: assuming names are not longer than 256 bytes
103    let namelen = 256i32;
104    #[expect(clippy::cast_sign_loss)]
105    let mut name = vec![0; namelen as usize];
106    unsafe {
107      fmod_result!(ll::FMOD_ChannelGroup_GetName (self.raw(),
108        name.as_mut_ptr() as *mut i8, namelen
109      ))?;
110    }
111    name.retain (|c| *c != 0x0);
112    String::from_utf8 (name).map_err (|e|{
113      log::error!("channel group get name string invalid utf8: {e}");
114      Error::InvalidString
115    })
116  }
117
118  #[inline]
119  pub fn get_num_channels (&self) -> Result <i32, Error> {
120    let mut numchannels = 0;
121    unsafe {
122      fmod_result!(
123        ll::FMOD_ChannelGroup_GetNumChannels (self.raw(), &mut numchannels)
124      )?;
125    }
126    Ok (numchannels)
127  }
128
129  #[inline]
130  pub fn get_num_groups (&self) -> Result <i32, Error> {
131    let mut numgroups = 0;
132    unsafe {
133      fmod_result!(
134        ll::FMOD_ChannelGroup_GetNumGroups (self.raw(), &mut numgroups)
135      )?;
136    }
137    Ok (numgroups)
138  }
139
140  /// Retrieves a handle to the channel group parent.
141  ///
142  /// Returns `None` if this is the master channel group.
143  #[inline]
144  pub fn get_parent_group (&self) -> Result <Option <ChannelGroupRef>, Error> {
145    let mut raw = std::ptr::null_mut();
146    unsafe {
147      // NOTE: this should return NULL for the master channel group, we go ahead and
148      // make the call anyway so that any error can be reported
149      fmod_result!(
150        ll::FMOD_ChannelGroup_GetParentGroup (self.raw(), &mut raw)
151      )?;
152    }
153    if !self.is_master()? {
154      let channel_group =
155        ChannelGroup::from_raw_parts (raw, false, self.system.clone());
156      Ok (Some (ChannelGroupRef { channel_group }))
157    } else {
158      debug_assert_eq!(raw, std::ptr::null_mut());
159      Ok (None)
160    }
161  }
162
163}
164
165impl ChannelControl for ChannelGroup {
166  /// Add a DSP to the DSP chain at the given index. Index must be a position
167  /// in the range $[0, numDSPs]$, or else one of the special indices:
168  ///
169  /// - `DspIndex::Head  == -1` -- head of the chain; always equal to 0
170  /// - `DspIndex::Fader == -2` -- built-in fader DSP; initially 0
171  /// - `DspIndex::Tail  == -3` -- tail of the chain; equal to $numDSPs-1$
172  ///
173  /// # Errors
174  ///
175  /// An `InvalidParam` error will be returned if `index > self.get_num_dsps()`.
176  #[inline]
177  fn add_dsp (&mut self, index : i32, dsp : &mut Dsp) -> Result <(), Error> {
178    unsafe {
179      fmod_result!(
180        ll::FMOD_ChannelGroup_AddDSP (self.raw(), index, dsp.raw())
181      )?;
182    }
183    Ok (())
184  }
185
186  #[inline]
187  fn add_fade_point (&mut self, dspclock : u64, volume : f32)
188    -> Result <(), Error>
189  {
190    unsafe {
191      fmod_result!(ll::FMOD_ChannelGroup_AddFadePoint (
192        self.raw_mut(), dspclock, volume))
193    }
194  }
195
196  #[inline]
197  fn get_3d_attributes (&self) -> Result <([f32; 3], [f32; 3]), Error> {
198    let mut pos = vector::to_ll ([0.0; 3]);
199    let mut vel = vector::to_ll ([0.0; 3]);
200    let mut alt_pan_pos = vector::to_ll ([0.0; 3]);  // unimplemented
201    unsafe {
202      fmod_result!(ll::FMOD_ChannelGroup_Get3DAttributes (self.raw(),
203        &mut pos, &mut vel, &mut alt_pan_pos)
204      )?;
205    }
206    Ok ((vector::from_ll (pos), vector::from_ll (vel)))
207  }
208
209  #[inline]
210  fn get_3d_cone_orientation (&self) -> Result <[f32; 3], Error> {
211    let mut orientation = vector::to_ll ([0.0; 3]);
212    unsafe {
213      fmod_result!(
214        ll::FMOD_ChannelGroup_Get3DConeOrientation (self.raw(), &mut orientation)
215      )?;
216    }
217    Ok (vector::from_ll (orientation))
218  }
219
220  /// Retrieves the angles that define the sound projection cone including the
221  /// volume when outside the cone.
222  ///
223  /// Returns `(insideconeangle, outsideconeangle, outsidevolume)`.
224  #[inline]
225  fn get_3d_cone_settings (&self) -> Result <(f32, f32, f32), Error> {
226    let mut insideconeangle  = 0.0;
227    let mut outsideconeangle = 0.0;
228    let mut outsidevolume    = 0.0;
229    unsafe {
230      fmod_result!(ll::FMOD_ChannelGroup_Get3DConeSettings (self.raw(),
231        &mut insideconeangle, &mut outsideconeangle, &mut outsidevolume
232      ))?;
233    }
234    Ok ((insideconeangle, outsideconeangle, outsidevolume))
235  }
236
237  fn get_3d_custom_rolloff (&self) -> Result <Vec <[f32; 3]>, Error> {
238    let mut points = std::ptr::null_mut();
239    let mut numpoints = 0;
240    unsafe {
241      fmod_result!(
242        ll::FMOD_ChannelGroup_Get3DCustomRolloff (self.raw(),
243          &mut points, &mut numpoints)
244      )?;
245    }
246    #[expect(clippy::cast_sign_loss)]
247    let mut curve = Vec::with_capacity (numpoints as usize);
248    for i in 0..numpoints as isize {
249      let point = unsafe {
250        std::ptr::read (points.offset (i) as *const ll::FMOD_VECTOR)
251      };
252      curve.push (vector::from_ll (point));
253    }
254    Ok (curve)
255  }
256
257  /// Retrieve the settings for the 3D distance filter properties for a Channel
258  /// or Channel Group.
259  ///
260  /// Returns `(custom, customlevel, centerfreq)`.
261  #[inline]
262  fn get_3d_distance_filter (&self) -> Result <(bool, f32, f32), Error> {
263    let mut custom      = 0;
264    let mut customlevel = 0.0;
265    let mut centerfreq  = 0.0;
266    unsafe {
267      fmod_result!(ll::FMOD_ChannelGroup_Get3DDistanceFilter (self.raw(),
268        &mut custom, &mut customlevel, &mut centerfreq)
269      )?;
270    }
271    let custom = custom != 0;
272    Ok ((custom, customlevel, centerfreq))
273  }
274
275  #[inline]
276  fn get_3d_doppler_level (&self) -> Result <f32, Error> {
277    let mut level = 0.0;
278    unsafe {
279      fmod_result!(
280        ll::FMOD_ChannelGroup_Get3DDopplerLevel (self.raw(), &mut level)
281      )?;
282    }
283    Ok (level)
284  }
285
286  /// Retrieves the minimum and maximum audible distance
287  #[inline]
288  fn get_3d_min_max_distance (&self) -> Result <(f32, f32), Error> {
289    let mut mindistance = 0.0;
290    let mut maxdistance = 0.0;
291    unsafe {
292      fmod_result!(ll::FMOD_ChannelGroup_Get3DMinMaxDistance (self.raw(),
293        &mut mindistance, &mut maxdistance)
294      )?;
295    }
296    Ok ((mindistance, maxdistance))
297  }
298
299  /// Retrieves the occlusion factors.
300  ///
301  /// Returns `(directocclusion, reverbocclusion)`.
302  #[inline]
303  fn get_3d_occlusion (&self) -> Result <(f32, f32), Error> {
304    let mut directocclusion = 0.0;
305    let mut reverbocclusion = 0.0;
306    unsafe {
307      fmod_result!(ll::FMOD_ChannelGroup_Get3DOcclusion (self.raw(),
308        &mut directocclusion, &mut reverbocclusion)
309      )?;
310    }
311    Ok ((directocclusion, reverbocclusion))
312  }
313
314  /// Retrieves the spread of a 3D sound in speaker space.
315  ///
316  /// Returns the speaker spread angle.
317  #[inline]
318  fn get_3d_spread (&self) -> Result <f32, Error> {
319    let mut angle = 0.0;
320    unsafe {
321      fmod_result!(ll::FMOD_ChannelGroup_Get3DSpread (self.raw(), &mut angle))?;
322    }
323    Ok (angle)
324  }
325
326  #[inline]
327  fn get_audibility (&self) -> Result <f32, Error> {
328    let mut audibility = 0.0;
329    unsafe {
330      fmod_result!(ll::FMOD_ChannelGroup_GetAudibility (self.raw(), &mut audibility))?;
331    }
332    Ok (audibility)
333  }
334
335  #[inline]
336  fn get_delay (&self) -> Result <Delay, Error> {
337    let mut dspclock_start = 0;
338    let mut dspclock_end   = 0;
339    let mut stopchannels   = 0;
340    unsafe {
341      fmod_result!(ll::FMOD_ChannelGroup_GetDelay (self.raw(),
342        &mut dspclock_start,
343        &mut dspclock_end,
344        &mut stopchannels
345      ))?;
346    }
347    let stopchannels = stopchannels != 0;
348    Ok (Delay { dspclock_start, dspclock_end, stopchannels })
349  }
350
351  /// Retrieve the DSP unit at the specified index
352  #[inline]
353  fn get_dsp (&self, index : i32) -> Result <DspRef, Error> {
354    let mut raw = std::ptr::null_mut();
355    unsafe {
356      fmod_result!(
357        ll::FMOD_ChannelGroup_GetDSP (self.raw(), index, &mut raw)
358      )?;
359    }
360    let dsp = Dsp::from_raw_parts (raw, false, self.system.clone());
361    Ok (DspRef { dsp })
362  }
363
364  /// DSP clock value for the head DSP node
365  #[inline]
366  fn get_dsp_clock (&self) -> Result <u64, Error> {
367    let mut dspclock = 0;
368    let parentclock = std::ptr::null_mut();
369    unsafe {
370      fmod_result!(
371        ll::FMOD_ChannelGroup_GetDSPClock (self.raw(), &mut dspclock, parentclock)
372      )?;
373    }
374    Ok (dspclock)
375  }
376
377  /// DSP clock value for the tail DSP node
378  #[inline]
379  fn get_dsp_clock_parent (&self) -> Result <u64, Error> {
380    let dspclock = std::ptr::null_mut();
381    let mut parentclock = 0;
382    unsafe {
383      fmod_result!(
384        ll::FMOD_ChannelGroup_GetDSPClock (self.raw(), dspclock, &mut parentclock)
385      )?;
386    }
387    Ok (parentclock)
388  }
389
390  /// Retrieve the index in the DSP chain of the provided DSP
391  #[inline]
392  fn get_dsp_index (&self, dsp : &Dsp) -> Result <i32, Error> {
393    let mut index = 0;
394    unsafe {
395      fmod_result!(
396        ll::FMOD_ChannelGroup_GetDSPIndex (self.raw(), dsp.raw(), &mut index)
397      )?;
398    }
399    Ok (index)
400  }
401
402  #[inline]
403  fn get_low_pass_gain (&self) -> Result <f32, Error> {
404    let mut gain = 0.0;
405    unsafe {
406      fmod_result!(ll::FMOD_ChannelGroup_GetLowPassGain (self.raw(), &mut gain))?;
407    }
408    Ok (gain)
409  }
410
411  #[inline]
412  fn get_mode (&self) -> Result <Mode, Error> {
413    let mut mode = 0;
414    unsafe {
415      fmod_result!(ll::FMOD_ChannelGroup_GetMode (self.raw(), &mut mode))?;
416    }
417    Ok (Mode::from_bits (mode).unwrap())
418  }
419
420  fn get_mute (&self) -> Result <bool, Error> {
421    let mut mute = 0;
422    unsafe {
423      fmod_result!(ll::FMOD_ChannelGroup_GetMute (self.raw(), &mut mute))?;
424    }
425    Ok (mute != 0)
426  }
427
428  /// Retrieves the number of DSP units in the DSP chain
429  #[inline]
430  fn get_num_dsps (&self) -> Result <u32, Error> {
431    let mut num = 0;
432    unsafe {
433      fmod_result!(ll::FMOD_ChannelGroup_GetNumDSPs (self.raw(), &mut num))?;
434    }
435    debug_assert!(num >= 0);
436    #[expect(clippy::cast_sign_loss)]
437    Ok (num as u32)
438  }
439
440  #[inline]
441  fn get_paused (&self) -> Result <bool, Error> {
442    let mut paused = 0;
443    unsafe {
444      fmod_result!(ll::FMOD_ChannelGroup_GetPaused (self.raw(), &mut paused))?;
445    }
446    Ok (paused != 0)
447  }
448
449  /// Retrieves the wet level (or send level) for a particular reverb instance.
450  ///
451  /// Returns the send level for the signal to the reverb, from 0 (none) to 1.0
452  /// (full).
453  ///
454  /// `instance` -- Index of the particular reverb instance to target, from 0 to
455  /// `dsp::REVERB_MAXINSTANCES`.
456  #[inline]
457  fn get_reverb_properties (&self, instance : i32) -> Result <f32, Error> {
458    let mut wet = 0.0;
459    unsafe {
460      fmod_result!(
461        ll::FMOD_ChannelGroup_GetReverbProperties (self.raw(), instance, &mut wet)
462      )?;
463    }
464    Ok (wet)
465  }
466
467  #[inline]
468  fn get_volume (&self) -> Result <f32, Error> {
469    let mut volume = 0.0;
470    unsafe {
471      fmod_result!(ll::FMOD_ChannelGroup_GetVolume (self.raw(), &mut volume))?;
472    }
473    Ok (volume)
474  }
475
476  #[inline]
477  fn is_playing (&self) -> Result <bool, Error> {
478    let mut isplaying = 0;
479    unsafe {
480      fmod_result!(ll::FMOD_ChannelGroup_IsPlaying (self.raw(), &mut isplaying))?;
481    }
482    Ok (isplaying != 0)
483  }
484
485  #[inline]
486  fn remove_dsp (&mut self, dsp : &mut Dsp) -> Result <(), Error> {
487    unsafe {
488      fmod_result!(
489        ll::FMOD_ChannelGroup_RemoveDSP (self.raw_mut(), dsp.raw())
490      )
491    }
492  }
493
494  #[inline]
495  fn set_3d_attributes (&mut self, pos : [f32; 3], vel : [f32; 3])
496    -> Result <(), Error>
497  {
498    let pos = vector::to_ll (pos);
499    let vel = vector::to_ll (vel);
500    // FMOD1.10: unimplemented
501    const ALT_PAN_POS : ll::FMOD_VECTOR = vector::to_ll ([0.0; 3]);
502    unsafe {
503      fmod_result!(
504        ll::FMOD_ChannelGroup_Set3DAttributes (self.raw_mut(), &pos, &vel,
505          &ALT_PAN_POS)
506      )
507    }
508  }
509
510  #[inline]
511  fn set_delay (&mut self,
512    dspclock_start : u64, dspclock_end : u64, stopchannels : bool
513  ) -> Result <(), Error> {
514    unsafe {
515      fmod_result!(
516        ll::FMOD_ChannelGroup_SetDelay (self.raw_mut(), dspclock_start,
517          dspclock_end, stopchannels as i32))
518    }
519  }
520
521  #[inline]
522  fn set_fade_point_ramp (&mut self, dspclock : u64, volume : f32)
523    -> Result <(), Error>
524  {
525    unsafe {
526      fmod_result!(
527        ll::FMOD_ChannelGroup_SetFadePointRamp (self.raw_mut(), dspclock, volume))
528    }
529  }
530
531  #[inline]
532  fn set_mute (&mut self, mute : bool) -> Result <(), Error> {
533    unsafe {
534      fmod_result!(ll::FMOD_ChannelGroup_SetMute (self.raw_mut(), mute as i32))
535    }
536  }
537
538  #[inline]
539  fn set_paused (&mut self, paused : bool) -> Result <(), Error> {
540    unsafe {
541      fmod_result!(ll::FMOD_ChannelGroup_SetPaused (self.raw_mut(), paused as i32))
542    }
543  }
544
545  #[inline]
546  fn set_reverb_properties (&mut self, instance : i32, wet : f32)
547    -> Result <(), Error>
548  {
549    unsafe {
550      fmod_result!(
551        ll::FMOD_ChannelGroup_SetReverbProperties (self.raw_mut(), instance, wet))
552    }
553  }
554
555  #[inline]
556  fn set_volume (&mut self, volume : f32) -> Result <(), Error> {
557    unsafe {
558      fmod_result!(ll::FMOD_ChannelGroup_SetVolume (self.raw_mut(), volume))
559    }
560  }
561
562  #[inline]
563  fn stop (&mut self) -> Result <(), Error> {
564    unsafe {
565      fmod_result!(ll::FMOD_ChannelGroup_Stop (self.raw_mut()))
566    }
567  }
568
569}
570
571impl Deref for ChannelGroupRef {
572  type Target = ChannelGroup;
573  fn deref (&self) -> &ChannelGroup {
574    &self.channel_group
575  }
576}
577
578impl DerefMut for ChannelGroupRef {
579  fn deref_mut (&mut self) -> &mut ChannelGroup {
580    &mut self.channel_group
581  }
582}
583
584impl std::fmt::Debug for Inner {
585  fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
586    write!(f, "Inner {{ raw: {:p}, owned: {} }}", self.raw, self.owned)
587  }
588}
589
590impl Drop for Inner {
591  fn drop (&mut self) {
592    if self.owned {
593      unsafe {
594        let _ = fmod_result!(ll::FMOD_ChannelGroup_Release (self.raw)).map_err (
595          |err| log::error!("error releasing FMOD ChannelGroup@{:p}: {:?}",
596            self.raw, err));
597      }
598    }
599  }
600}