fmod/
system.rs

1use std::{self, rc::Rc};
2use log;
3use riff_wave;
4use num_derive::FromPrimitive;
5use crate::{ll, fmod_result, dsp, reverb3d, sound, vector, ChannelGroup,
6  ChannelGroupRef, CpuUsage, DriverState, Dsp, Error, Guid, ListenerAttributes,
7  Mode, PluginHandle, Plugintype, Reverb3d, Sound, SoundRam, Speaker,
8  Speakermode, Timeunit};
9
10static FMOD_SYSTEM_CREATE_DESTROY_MUTEX : std::sync::LazyLock <std::sync::Mutex <()>> =
11  std::sync::LazyLock::new (std::sync::Mutex::default);
12
13/// Fmod system object.
14///
15/// This is a single reference counted pointer, so it can be freely cloned.
16///
17/// # Channels
18///
19/// There are three types of *channel* considered by the FMOD system:
20///
21/// 1. hardware output channels or "speakermode channels" -- the number of
22///    speakermode channels is returned in the `DriverInfo` structure by the
23///    `system.get_driver_info()` method
24/// 2. software channels -- "mixable voices"; unless specified, the FMOD default
25///    number of software channels is 64
26/// 3. virtual channels -- these are virtual voices that can be dynamically
27///    mapped to an available software channel depending on how audible it is
28///
29/// When playing a `Sound` with `sound.play()`, that sound is assigned to the
30/// returned virtual channel. If there are more playing virtual channels than
31/// the number of software channels, some channels will be dynamically
32/// *virtualized* and excluded from mixing, although other aspects such as
33/// playback position will continue to be updated.
34///
35/// If, at the time of the call to `sound.play()`, all virtual channels are in
36/// use, the virtual channel with the lowest *priority* will be 'stolen' from
37/// the currently playing sound, i.e. the sound currently playing on that
38/// channel is stopped *and* any subsequent function calls on the stolen sound's
39/// previously returned `Channel` will return `Error::ChannelStolen`.
40///
41/// The default priority for playback is 128. In practice if a number of default
42/// priority `play()` calls exceeding the virtual channel count are made all at
43/// once, the last played channel is re-stolen on each subsequent call to
44/// `play`.
45///
46/// # Reverb
47///
48/// TODO
49#[derive(Clone, Debug, PartialEq)]
50pub struct System {
51  inner : Rc <Inner>
52}
53
54#[derive(PartialEq)]
55struct Inner (*mut ll::FMOD_SYSTEM);
56
57#[derive(Debug)]
58pub struct DriverInfo {
59  pub name                : String,
60  pub guid                : Guid,
61  pub systemrate          : i32,
62  pub speakermode         : Speakermode,
63  pub speakermodechannels : i32
64}
65
66bitflags!{
67  #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
68  pub struct CallbackType : u32 {
69    const DEVICELISTCHANGED      = ll::FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED;
70    const DEVICELOST             = ll::FMOD_SYSTEM_CALLBACK_DEVICELOST;
71    const MEMORYALLOCATIONFAILED = ll::FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED;
72    const THREADCREATED          = ll::FMOD_SYSTEM_CALLBACK_THREADCREATED;
73    const BADDSPCONNECTION       = ll::FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION;
74    const PREMIX                 = ll::FMOD_SYSTEM_CALLBACK_PREMIX;
75    const POSTMIX                = ll::FMOD_SYSTEM_CALLBACK_POSTMIX;
76    const ERROR                  = ll::FMOD_SYSTEM_CALLBACK_ERROR;
77    const MIDMIX                 = ll::FMOD_SYSTEM_CALLBACK_MIDMIX;
78    const THREADDESTROYED        = ll::FMOD_SYSTEM_CALLBACK_THREADDESTROYED;
79    const PREUPDATE              = ll::FMOD_SYSTEM_CALLBACK_PREUPDATE;
80    const POSTUPDATE             = ll::FMOD_SYSTEM_CALLBACK_POSTUPDATE;
81    const RECORDLISTCHANGED      = ll::FMOD_SYSTEM_CALLBACK_RECORDLISTCHANGED;
82    const ALL                    = ll::FMOD_SYSTEM_CALLBACK_ALL;
83  }
84}
85
86bitflags!{
87  #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
88  pub struct Initflags : u32 {
89    const NORMAL                     = ll::FMOD_INIT_NORMAL;
90    const STREAM_FROM_UPDATE         = ll::FMOD_INIT_STREAM_FROM_UPDATE;
91    const MIX_FROM_UPDATE            = ll::FMOD_INIT_MIX_FROM_UPDATE;
92    const _3D_RIGHTHANDED            = ll::FMOD_INIT_3D_RIGHTHANDED;
93    const CHANNEL_LOWPASS            = ll::FMOD_INIT_CHANNEL_LOWPASS;
94    const CHANNEL_DISTANCEFILTER     = ll::FMOD_INIT_CHANNEL_DISTANCEFILTER;
95    const PROFILE_ENABLE             = ll::FMOD_INIT_PROFILE_ENABLE;
96    const VOL0_BECOMES_VIRTUAL       = ll::FMOD_INIT_VOL0_BECOMES_VIRTUAL;
97    const GEOMETRY_USECLOSEST        = ll::FMOD_INIT_GEOMETRY_USECLOSEST;
98    const PREFER_DOLBY_DOWNMIX       = ll::FMOD_INIT_PREFER_DOLBY_DOWNMIX;
99    const THREAD_UNSAFE              = ll::FMOD_INIT_THREAD_UNSAFE;
100    const PROFILE_METER_ALL          = ll::FMOD_INIT_PROFILE_METER_ALL;
101    const DISABLE_SRS_HIGHPASSFILTER = ll::FMOD_INIT_DISABLE_SRS_HIGHPASSFILTER;
102  }
103}
104
105#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
106pub enum Outputtype {
107  Autodetect    = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_AUTODETECT    as isize,
108  Unknown       = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_UNKNOWN       as isize,
109  Nosound       = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_NOSOUND       as isize,
110  Wavwriter     = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WAVWRITER     as isize,
111  NosoundNrt    = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_NOSOUND_NRT   as isize,
112  WavwriterNrt  = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WAVWRITER_NRT as isize,
113  Dsound        = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_DSOUND        as isize,
114  Winmm         = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WINMM         as isize,
115  Wasapi        = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WASAPI        as isize,
116  Asio          = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_ASIO          as isize,
117  Pulseaudio    = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_PULSEAUDIO    as isize,
118  Alsa          = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_ALSA          as isize,
119  Coreaudio     = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_COREAUDIO     as isize,
120  Xaudio        = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_XAUDIO        as isize,
121  Ps3           = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_PS3           as isize,
122  Audiotrack    = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_AUDIOTRACK    as isize,
123  Opensl        = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_OPENSL        as isize,
124  Wiiu          = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WIIU          as isize,
125  Audioout      = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_AUDIOOUT      as isize,
126  Audio3d       = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_AUDIO3D       as isize,
127  Atmos         = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_ATMOS         as isize,
128  Webaudio      = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WEBAUDIO      as isize,
129  Nnaudio       = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_NNAUDIO       as isize,
130  Winsonic      = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_WINSONIC      as isize,
131  MAX           = ll::FMOD_OUTPUTTYPE_FMOD_OUTPUTTYPE_MAX           as isize
132}
133
134impl System {
135  /// Create and initialize with 256 max virtual channels, the default number of
136  /// software channels (64), and `Initflags::NORMAL`.
137  #[inline]
138  #[expect(clippy::should_implement_trait)]
139  pub fn default() -> Result <Self, Error> {
140    Self::new (None, 256, Initflags::NORMAL)
141  }
142
143  /// Create with the given number of software channels, max number of virtual
144  /// channels, and initflags.
145  ///
146  /// If the number of software channels is not provided, by default FMOD will
147  /// create a system with 64 software channels.
148  #[inline]
149  pub fn new (
150    num_software_channels : Option <u16>,
151    num_virtual_channels  : u16,
152    initflags             : Initflags
153  ) -> Result <Self, Error> {
154    let mut system = Self::create()?;
155    if let Some (n) = num_software_channels {
156      system.set_software_channels (n as i32)?;
157    }
158    // TODO: make this a parameter; when FMOD sees the pulseaudio daemon, it
159    // defaults to pulseaudio and init returns `FileNotFound` error, so we
160    // manually set the output to always be Alsa
161    #[cfg(not(target_os = "windows"))]
162    system.set_output (Outputtype::Alsa)?;
163    system.init (num_virtual_channels, initflags)?;
164    Ok (system)
165  }
166
167  //
168  //  &self
169  //
170
171  #[inline]
172  fn raw (&self) -> *mut ll::FMOD_SYSTEM {
173    self.inner.0
174  }
175
176  #[inline]
177  #[expect(clippy::needless_pass_by_ref_mut)]
178  pub (crate) fn raw_mut (&mut self) -> *mut ll::FMOD_SYSTEM {
179    self.inner.0
180  }
181
182  #[inline]
183  pub fn update (&mut self) -> Result <(), Error> {
184    unsafe {
185      fmod_result!(ll::FMOD_System_Update (self.raw()))
186    }
187  }
188
189  #[inline]
190  pub fn get_3d_listener_attributes (&self, listener : i32)
191    -> Result <ListenerAttributes, Error>
192  {
193    let zero = ll::FMOD_VECTOR { x: 0.0, y: 0.0, z: 0.0 };
194    let mut pos     = zero;
195    let mut vel     = zero;
196    let mut forward = zero;
197    let mut up      = zero;
198    unsafe {
199      fmod_result!(
200        ll::FMOD_System_Get3DListenerAttributes (
201          self.raw(), listener, &mut pos, &mut vel, &mut forward, &mut up)
202      )?;
203    }
204    let listener_attributes =
205      ListenerAttributes::from_ll (pos, vel, forward, up);
206    Ok (listener_attributes)
207  }
208
209  #[inline]
210  pub fn get_3d_num_listeners (&self) -> Result <i32, Error> {
211    let mut numlisteners = 0;
212    unsafe {
213      fmod_result!(ll::FMOD_System_Get3DNumListeners (
214        self.raw(), &mut numlisteners)
215      )?;
216    }
217    Ok (numlisteners)
218  }
219
220  /// Retrieves the global doppler scale, distance factor and rolloff scale for
221  /// all 3D sound in FMOD
222  #[inline]
223  pub fn get_3d_settings (&self) -> Result <(f32, f32, f32), Error> {
224    let mut dopplerscale   = 0.0;
225    let mut distancefactor = 0.0;
226    let mut rolloffscale   = 0.0;
227    unsafe {
228      fmod_result!(ll::FMOD_System_Get3DSettings (
229        self.raw(), &mut dopplerscale, &mut distancefactor, &mut rolloffscale)
230      )?;
231    }
232    Ok ((dopplerscale, distancefactor, rolloffscale))
233  }
234
235  #[inline]
236  pub fn get_channels_playing (&self) -> Result <(i32, i32), Error> {
237    let mut channels = 0;
238    let mut realchannels = 0;
239    unsafe {
240      fmod_result!(
241        ll::FMOD_System_GetChannelsPlaying (
242          self.raw(), &mut channels, &mut realchannels)
243      )?;
244    }
245    Ok ((channels, realchannels))
246  }
247
248  #[inline]
249  pub fn get_cpu_usage (&self) -> Result <CpuUsage, Error> {
250    let mut usage = CpuUsage::default();
251    unsafe {
252      fmod_result!(
253        ll::FMOD_System_GetCPUUsage (
254          self.raw(),
255          &mut usage.dsp,
256          &mut usage.stream,
257          &mut usage.geometry,
258          &mut usage.update,
259          &mut usage.total)
260      )?;
261    }
262    Ok (usage)
263  }
264
265  /// Returns the currently selected driver number
266  #[inline]
267  pub fn get_driver (&self) -> Result <i32, Error> {
268    let mut driver = 0;
269    unsafe {
270      fmod_result!(ll::FMOD_System_GetDriver (self.raw(), &mut driver))?;
271    }
272    Ok (driver)
273  }
274
275  #[inline]
276  pub fn get_driver_info (&self, id : i32) -> Result <DriverInfo, Error> {
277    // NB: assuming names are not longer than 256 bytes
278    let name_len = 256i32;
279    #[expect(clippy::cast_sign_loss)]
280    let mut name = vec![0; name_len as usize];
281    let mut guid = ll::FMOD_GUID {
282      Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8]
283    };
284    let mut systemrate = 0;
285    let mut speakermode = 0;
286    let mut speakermodechannels = 0;
287    unsafe {
288      use std::os::raw;
289      fmod_result!(ll::FMOD_System_GetDriverInfo (
290        self.raw(),
291        id as raw::c_int,
292        name.as_mut_ptr() as *mut raw::c_char,
293        name_len as raw::c_int,
294        &mut guid,
295        &mut systemrate,
296        &mut speakermode,
297        &mut speakermodechannels
298      ))?;
299    }
300    let name = {
301      name.retain (|c| *c != 0x0);
302      String::from_utf8 (name).map_err (|e|{
303        log::error!("system driver name string invalid utf8: {e}");
304        Error::InvalidString
305      })?
306    };
307    let guid = Guid::from (guid);
308    let speakermode = Speakermode::from_ll (speakermode);
309    let info = DriverInfo {
310      name, guid, systemrate, speakermode, speakermodechannels
311    };
312    Ok (info)
313  }
314
315  /// Retrieves the buffer size settings for the FMOD software mixing engine.
316  ///
317  /// Returns `(bufferlength, numbuffers)`:
318  ///
319  /// - `buffelength` -- Address of a variable that receives the mixer engine
320  ///   block size in samples. Default = 1024. (milliseconds = 1024 at 48khz =
321  ///   1024 / 48000 * 1000 = 10.66ms). This means the mixer updates every
322  ///   21.3ms.
323  /// - `numbuffers` -- Address of a variable that receives the mixer engine
324  ///   number of buffers used. Default = 4. To get the total buffersize
325  ///   multiply the bufferlength by the numbuffers value. By default this would
326  ///   be 4*1024.
327  #[inline]
328  pub fn get_dsp_buffer_size (&self) -> Result <(u32, i32), Error> {
329    let mut bufferlength = 0;
330    let mut numbuffers = 0;
331    unsafe {
332      fmod_result!(
333        ll::FMOD_System_GetDSPBufferSize (
334          self.raw(), &mut bufferlength, &mut numbuffers)
335      )?;
336    }
337    Ok ((bufferlength, numbuffers))
338  }
339
340  /// Calculates geometry occlusion between a listener and a sound source.
341  ///
342  /// Returns `(direct, reverb)`.
343  #[inline]
344  pub fn get_geometry_occlusion (&self, listener : [f32; 3], source : [f32; 3])
345    -> Result <(f32, f32), Error>
346  {
347    let listener = vector::to_ll (listener);
348    let source   = vector::to_ll (source);
349    let mut direct = 0.0;
350    let mut reverb = 0.0;
351    unsafe {
352      fmod_result!(
353        ll::FMOD_System_GetGeometryOcclusion (self.raw(),
354          &listener, &source, &mut direct, &mut reverb)
355      )?;
356    }
357    Ok ((direct, reverb))
358  }
359
360  /// Retrieves the maximum world size for the geometry engine
361  #[inline]
362  pub fn get_geometry_settings (&self) -> Result <f32, Error> {
363    let mut maxworldsize = 0.0;
364    unsafe {
365      fmod_result!(
366        ll::FMOD_System_GetGeometrySettings (self.raw(), &mut maxworldsize)
367      )?;
368    }
369    Ok (maxworldsize)
370  }
371
372  #[inline]
373  pub fn get_master_channel_group (&self) -> Result <ChannelGroupRef, Error> {
374    let mut raw = std::ptr::null_mut();
375    unsafe {
376      fmod_result!(
377        ll::FMOD_System_GetMasterChannelGroup (self.raw(), &mut raw)
378      )?;
379    }
380    let channel_group =
381      ChannelGroup::from_raw_parts (raw, false, self.clone());
382    Ok (ChannelGroupRef { channel_group })
383  }
384
385  #[inline]
386  pub fn get_num_drivers (&self) -> Result <i32, Error> {
387    let mut numdrivers = 0;
388    unsafe {
389      fmod_result!(ll::FMOD_System_GetNumDrivers (self.raw(), &mut numdrivers))?;
390    }
391    Ok (numdrivers)
392  }
393
394  #[inline]
395  pub fn get_num_plugins (&self, plugintype : Plugintype)
396    -> Result <i32, Error>
397  {
398    let mut numplugins = 0;
399    unsafe {
400      fmod_result!(ll::FMOD_System_GetNumPlugins (
401        self.raw(), plugintype as ll::FMOD_PLUGINTYPE, &mut numplugins)
402      )?;
403    }
404    Ok (numplugins)
405  }
406
407  #[inline]
408  pub fn get_output (&self) -> Result <Outputtype, Error> {
409    let mut outputtype = 0;
410    unsafe {
411      fmod_result!(ll::FMOD_System_GetOutput (self.raw(), &mut outputtype))?;
412    }
413    Ok (Outputtype::from_ll (outputtype))
414  }
415
416  /// Returns the currently selected output as an id in the list of output
417  /// plugins
418  #[inline]
419  pub fn get_output_by_plugin (&self) -> Result <PluginHandle, Error> {
420    let mut handle = 0;
421    unsafe {
422      fmod_result!(ll::FMOD_System_GetOutputByPlugin (self.raw(), &mut handle))?;
423    }
424    Ok (PluginHandle (handle))
425  }
426
427  #[inline]
428  pub fn get_record_driver_info (&self, id : i32)
429    -> Result <(DriverInfo, DriverState), Error>
430  {
431    // NB: assuming names are not longer than 256 bytes
432    let name_len = 256i32;
433    #[expect(clippy::cast_sign_loss)]
434    let mut name = vec![0; name_len as usize];
435    let mut guid = ll::FMOD_GUID {
436      Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8]
437    };
438    let mut systemrate = 0;
439    let mut speakermode = 0;
440    let mut speakermodechannels = 0;
441    let mut state = 0;
442    unsafe {
443      use std::os::raw;
444      fmod_result!(ll::FMOD_System_GetRecordDriverInfo (
445        self.raw(),
446        id as raw::c_int,
447        name.as_mut_ptr() as *mut raw::c_char,
448        name_len as raw::c_int,
449        &mut guid,
450        &mut systemrate,
451        &mut speakermode,
452        &mut speakermodechannels,
453        &mut state
454      ))?;
455    }
456    let name = {
457      name.retain (|c| *c != 0x0);
458      String::from_utf8 (name).map_err (|e|{
459        log::error!("system record driver name string invalid utf8: {e}");
460        Error::InvalidString
461      })?
462    };
463    let guid = Guid::from (guid);
464    let speakermode = Speakermode::from_ll (speakermode);
465    let info = DriverInfo {
466      name, guid, systemrate, speakermode, speakermodechannels
467    };
468    let state = DriverState::from_bits (state).ok_or_else (||{
469      log::error!("system driver state bits not valid: {state:032b}");
470      Error::InvalidParam
471    })?;
472    Ok ((info, state))
473  }
474
475  /// Returns the number of recording drivers available for this output mode and
476  /// the number of recording drivers currently plugged in
477  #[inline]
478  pub fn get_record_num_drivers (&self) -> Result <(i32, i32), Error> {
479    let mut numdrivers   = 0;
480    let mut numconnected = 0;
481    unsafe {
482      fmod_result!(ll::FMOD_System_GetRecordNumDrivers (
483        self.raw(), &mut numdrivers, &mut numconnected
484      ))?;
485    }
486    Ok ((numdrivers, numconnected))
487  }
488
489  #[inline]
490  pub fn get_reverb_properties (&self, instance : i32)
491    -> Result <reverb3d::Properties, Error>
492  {
493    let mut properties = ll::FMOD_REVERB_PROPERTIES {
494      DecayTime:          0.0,
495      EarlyDelay:         0.0,
496      LateDelay:          0.0,
497      HFReference:        0.0,
498      HFDecayRatio:       0.0,
499      Diffusion:          0.0,
500      Density:            0.0,
501      LowShelfFrequency:  0.0,
502      LowShelfGain:       0.0,
503      HighCut:            0.0,
504      EarlyLateMix:       0.0,
505      WetLevel:           0.0
506    };
507    unsafe {
508      fmod_result!(
509        ll::FMOD_System_GetReverbProperties (self.raw(),
510          instance, &mut properties)
511      )?;
512    }
513    let properties = reverb3d::Properties::from_ll (&properties);
514    Ok (properties)
515  }
516
517  #[inline]
518  pub fn get_software_channels (&self) -> Result <i32, Error> {
519    let mut numsoftwarechannels = 0;
520    unsafe {
521      fmod_result!(
522        ll::FMOD_System_GetSoftwareChannels (
523          self.raw(), &mut numsoftwarechannels)
524      )?;
525    }
526    Ok (numsoftwarechannels)
527  }
528
529  /// Retrieves the output format for the software mixer:
530  ///
531  /// `(samplerate, speakermode, numrawspeakers)`
532  #[inline]
533  pub fn get_software_format (&self)
534    -> Result <(i32, Speakermode, i32), Error>
535  {
536    let mut samplerate = 0;
537    let mut speakermode = 0;
538    let mut numrawspeakers = 0;
539    unsafe {
540      fmod_result!(
541        ll::FMOD_System_GetSoftwareFormat (
542          self.raw(), &mut samplerate, &mut speakermode, &mut numrawspeakers)
543      )?;
544    }
545    let speakermode = Speakermode::from_ll (speakermode);
546    Ok ((samplerate, speakermode, numrawspeakers))
547  }
548
549  #[inline]
550  pub fn get_sound_ram (&self) -> Result <SoundRam, Error> {
551    let mut ram = SoundRam::default();
552    unsafe {
553      fmod_result!(
554        ll::FMOD_System_GetSoundRAM (self.raw(),
555          &mut ram.currentalloced, &mut ram.maxalloced, &mut ram.total)
556      )?;
557    }
558    Ok (ram)
559  }
560
561  #[inline]
562  pub fn get_speaker_mode_channels (&self, mode : Speakermode)
563    -> Result <i32, Error>
564  {
565    let mut channels = 0;
566    unsafe {
567      fmod_result!(
568        ll::FMOD_System_GetSpeakerModeChannels (
569          self.raw(), mode as ll::FMOD_SPEAKERMODE, &mut channels)
570      )?;
571    }
572    Ok (channels)
573  }
574
575  /// Retrieves the current speaker position information for the selected
576  /// speaker.
577  ///
578  /// Returns `(x, y, active)`.
579  #[inline]
580  pub fn get_speaker_position (&self, speaker : Speaker)
581    -> Result <(f32, f32, bool), Error>
582  {
583    let mut x      = 0.0;
584    let mut y      = 0.0;
585    let mut active = 0;
586    unsafe {
587      fmod_result!(
588        ll::FMOD_System_GetSpeakerPosition (
589          self.raw(), speaker as ll::FMOD_SPEAKER, &mut x, &mut y, &mut active)
590      )?;
591    }
592    Ok ((x, y, active != 0))
593  }
594
595  /// Returns the current internal buffersize settings for streamable sounds
596  #[inline]
597  pub fn get_stream_buffer_size (&self)
598    -> Result <(u32, Timeunit), Error>
599  {
600    let mut filebuffersize     = 0;
601    let mut filebuffersizetype = 0;
602    unsafe {
603      fmod_result!(
604        ll::FMOD_System_GetStreamBufferSize (
605          self.raw(), &mut filebuffersize, &mut filebuffersizetype)
606      )?;
607    }
608    let timeunit = Timeunit::from_bits (filebuffersizetype).ok_or_else (||{
609      log::error!("system stream buffer timeunit bits not valid: \
610        {filebuffersizetype:032b}");
611      Error::InvalidParam
612    })?;
613    Ok ((filebuffersize, timeunit))
614  }
615
616  /// Version of linked FMOD shared library.
617  ///
618  /// The version is a 32 bit hexadecimal value formated as 16:8:8, with the
619  /// upper 16 bits being the product version, the middle 8 bits being the major
620  /// version and the bottom 8 bits being the minor version.
621  #[inline]
622  pub fn get_version (&self) -> Result <u32, Error> {
623    let mut version = 0;
624    unsafe {
625      fmod_result!(ll::FMOD_System_GetVersion (self.raw(), &mut version))?;
626    }
627    Ok (version)
628  }
629
630  /// Version of linked FMOD shared library
631  #[inline]
632  pub fn get_version_string (&self) -> Result <String, Error> {
633    self.get_version().map (crate::version_string)
634  }
635
636  //
637  //  &mut self
638  //
639
640  #[inline]
641  pub fn create_channel_group (&mut self, name : Option <&str>)
642    -> Result <ChannelGroup, Error>
643  {
644    let mut raw     = std::ptr::null_mut();
645    let name_str    = name.unwrap_or ("");
646    let name_string = std::ffi::CString::new (name_str).map_err (|e|{
647      log::error!("create channel group name \"{name_str}\" contains a null byte: {e}");
648      Error::InvalidString
649    })?;
650    let name_ptr = if name.is_some() {
651      name_string.as_ptr()
652    } else {
653      std::ptr::null()
654    };
655    unsafe {
656      fmod_result!(ll::FMOD_System_CreateChannelGroup (self.raw(), name_ptr, &mut raw))?
657    }
658    Ok (ChannelGroup::from_raw_parts (raw, true, self.clone()))
659  }
660
661  #[inline]
662  pub fn create_dsp (&mut self, description : &'static dsp::Description)
663    -> Result <Dsp, Error>
664  {
665    let mut raw = std::ptr::null_mut();
666    unsafe {
667      fmod_result!(
668        ll::FMOD_System_CreateDSP (self.raw(),
669          std::ptr::from_ref (description.as_ref()), &mut raw)
670      )?
671    }
672    Ok (Dsp::from_raw_parts (raw, true, self.clone()))
673  }
674
675  #[inline]
676  pub fn create_dsp_by_type (&mut self, type_ : dsp::Type)
677    -> Result <Dsp, Error>
678  {
679    let mut raw = std::ptr::null_mut();
680    unsafe {
681      fmod_result!(
682        ll::FMOD_System_CreateDSPByType (self.raw(), type_ as ll::FMOD_DSP_TYPE,
683          &mut raw)
684      )?;
685    }
686    Ok (Dsp::from_raw_parts (raw, true, self.clone()))
687  }
688
689  /// A convenience method that creates a `dsp::Sfxreverb` from a given
690  /// `reverb3d::Properties` and dry level.
691  pub fn create_dsp_sfxreverb (&mut self,
692    properties : &reverb3d::Properties, dry_level : f32
693  ) -> Result <Dsp, Error> {
694    let mut raw = std::ptr::null_mut();
695    unsafe {
696      fmod_result!(
697        ll::FMOD_System_CreateDSPByType (
698          self.raw(),
699          dsp::Type::Sfxreverb as ll::FMOD_DSP_TYPE,
700          &mut raw
701        )
702      )?;
703    }
704    let mut dsp = Dsp::from_raw_parts (raw, true, self.clone());
705    dsp.set_parameters_sfxreverb (properties, dry_level)?;
706    Ok (dsp)
707  }
708
709  #[inline]
710  pub fn create_reverb3d (&mut self) -> Result <Reverb3d, Error> {
711    let mut raw = std::ptr::null_mut();
712    unsafe {
713      fmod_result!(ll::FMOD_System_CreateReverb3D (self.raw(), &mut raw))?;
714    }
715    Ok (Reverb3d::from_raw_parts (raw, self.clone()))
716  }
717
718  pub fn create_sound_from_file (&mut self,
719    filename : &str,
720    mode     : Mode,
721    exinfo   : Option <&mut sound::Createsoundexinfo>
722  ) -> Result <Sound, Error> {
723    let filename = std::ffi::CString::new (filename).map_err (|e|{
724      log::error!("create sound filename \"{filename}\" contains a null byte: {e}");
725      Error::InvalidString
726    })?;
727    let mut exinfo = exinfo.map (sound::Createsoundexinfo::to_ll);
728    let mut raw    = std::ptr::null_mut();
729    unsafe {
730      fmod_result!(
731        ll::FMOD_System_CreateSound (
732          self.raw(),
733          filename.as_ptr(),
734          mode.bits(),
735          exinfo.as_mut().map_or (
736            std::ptr::null_mut(), std::ptr::from_mut::<ll::FMOD_CREATESOUNDEXINFO>),
737          &mut raw)
738      )?;
739    }
740    Ok (Sound::from_raw_parts (raw, true, self.clone()))
741  }
742
743  pub fn create_sound_from_memory (&mut self,
744    data     : &[u8],
745    mut mode : Mode,
746    exinfo   : Option <&mut sound::Createsoundexinfo>
747  ) -> Result <Sound, Error> {
748    mode |= Mode::OPENMEMORY;
749    let mut exinfo_default = Default::default();
750    let mut exinfo = {
751      let exinfo    = exinfo.unwrap_or (&mut exinfo_default);
752      exinfo.length = u32::try_from (data.len()).map_err (|_|{
753        log::error!("create sound from memory data too big: {}", data.len());
754        Error::InvalidParam
755      })?;
756      sound::Createsoundexinfo::to_ll (exinfo)
757    };
758    let mut raw = std::ptr::null_mut();
759    let data = if !data.is_empty() {
760      data.as_ptr() as *const std::os::raw::c_char
761    } else {
762      std::ptr::null()
763    };
764    unsafe {
765      fmod_result!(
766        ll::FMOD_System_CreateSound (
767          self.raw(),
768          data,
769          mode.bits(),
770          &mut exinfo,
771          &mut raw)
772      )?;
773    }
774    Ok (Sound::from_raw_parts (raw, true, self.clone()))
775  }
776
777  /// # Panics
778  ///
779  /// Panics on write failures.
780  pub fn create_sound_from_pcm (&mut self,
781    pcm    : &[i16],
782    mode   : Mode,
783    exinfo : Option <&mut sound::Createsoundexinfo>
784  ) -> Result <Sound, Error> {
785    const WAV_HEADER_LEN : usize = 44;
786    let pcm_bytelen = pcm.len() * 2;
787    let wav_bytelen = WAV_HEADER_LEN + pcm_bytelen;
788    let mut wav     = vec![0; wav_bytelen];
789    {
790      let cursor = std::io::Cursor::new (&mut wav);
791      let writer = std::io::BufWriter::new (cursor);
792      let mut wav_writer = riff_wave::WaveWriter::new (1, 44100, 16, writer)
793        .unwrap();
794      for sample in pcm.iter() {
795        wav_writer.write_sample_i16 (*sample).unwrap();
796      }
797    }
798    self.create_sound_from_memory (wav.as_slice(), mode, exinfo)
799  }
800
801  /// Open a sound in `DEFAULT` mode with no exinfo argument
802  #[inline]
803  pub fn create_sound_from_file_default (&mut self, filename : &str)
804    -> Result <Sound, Error>
805  {
806    self.create_sound_from_file (filename, Mode::DEFAULT, None)
807  }
808
809  #[inline]
810  pub fn create_sound_from_memory_default (&mut self, data : &[u8])
811    -> Result <Sound, Error>
812  {
813    self.create_sound_from_memory (data, Mode::DEFAULT, None)
814  }
815
816  #[inline]
817  pub fn create_sound_from_pcm_default (&mut self, pcm : &[i16])
818    -> Result <Sound, Error>
819  {
820    self.create_sound_from_pcm (pcm, Mode::DEFAULT, None)
821  }
822
823  #[inline]
824  pub fn set_3d_listener_attributes (&mut self,
825    listener : i32, attributes : &ListenerAttributes
826  ) -> Result <(), Error> {
827    let pos     = vector::to_ll (attributes.pos);
828    let vel     = vector::to_ll (attributes.vel);
829    let forward = vector::to_ll (attributes.forward);
830    let up      = vector::to_ll (attributes.up);
831    unsafe {
832      fmod_result!(
833        ll::FMOD_System_Set3DListenerAttributes (self.raw(),
834          listener, &pos, &vel, &forward, &up)
835      )
836    }
837  }
838
839  #[inline]
840  pub fn set_output (&mut self, output : Outputtype) -> Result <(), Error> {
841    unsafe {
842      fmod_result!(
843        ll::FMOD_System_SetOutput (self.raw(), output as ll::FMOD_OUTPUTTYPE))
844    }
845  }
846
847  /// Sets parameters for the global reverb environment.
848  ///
849  /// `instance` -- Index of the particular reverb instance to target, from 0 to
850  /// `fmod::dsp::REVERB_MAXINSTANCES`.
851  ///
852  /// When using each instance for the first time, FMOD will create a physical
853  /// SFX reverb DSP unit that takes up several hundred kilobytes of memory and
854  /// some CPU.
855  #[inline]
856  pub fn set_reverb_properties (&mut self,
857    instance : i32, properties : &reverb3d::Properties
858  ) -> Result <(), Error> {
859    let properties = properties.to_ll();
860    unsafe {
861      fmod_result!(
862        ll::FMOD_System_SetReverbProperties (self.raw(), instance, &properties)
863      )
864    }
865  }
866
867  //
868  //  private
869  //
870
871  #[inline]
872  fn create() -> Result <Self, Error> {
873    let raw = {
874      let _lock = FMOD_SYSTEM_CREATE_DESTROY_MUTEX.lock().unwrap();
875      let mut raw = std::ptr::null_mut();
876      unsafe {
877        fmod_result!(ll::FMOD_System_Create (&mut raw))?;
878      }
879      raw
880      // unlock
881    };
882    let inner          = Rc::new (Inner (raw));
883    let system         = System { inner };
884    let system_version = system.get_version_string()?;
885    if system_version != *crate::FMOD_VERSION_STRING {
886      log::error!(
887        "FMOD created system got shared library version {:?}, \
888          bindings were generated for version {:?}",
889        system_version, *crate::FMOD_VERSION_STRING);
890      panic!(
891        "FMOD created system got shared library version {:?}, \
892          bindings were generated for version {:?}",
893        system_version, *crate::FMOD_VERSION_STRING);
894    }
895    Ok (system)
896  }
897
898  /// `max_channels` is the maximum number of *virtual channels*
899  #[inline]
900  fn init (&mut self, max_channels : u16, init_flags : Initflags)
901    -> Result <(), Error>
902  {
903    let max_channels = max_channels as i32;
904    unsafe {
905      fmod_result!(ll::FMOD_System_Init (
906        self.raw(), max_channels, init_flags.bits(), std::ptr::null_mut()))
907    }
908  }
909
910  // /!\ NOTE /!\: this method can only be called when the system has not yet
911  // been initialized
912  #[inline]
913  fn set_software_channels (&mut self, numsoftwarechannels : i32)
914    -> Result <(), Error>
915  {
916    unsafe {
917      fmod_result!(
918        ll::FMOD_System_SetSoftwareChannels (self.raw(), numsoftwarechannels)
919      )
920    }
921  }
922
923}
924
925impl std::fmt::Debug for Inner {
926  fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
927    write!(f, "{:p}", self.0)
928  }
929}
930
931impl Drop for Inner {
932  fn drop (&mut self) {
933    let _lock = FMOD_SYSTEM_CREATE_DESTROY_MUTEX.lock().unwrap();
934    unsafe {
935      let _ = fmod_result!(ll::FMOD_System_Release (self.0)).map_err (
936        |err| log::error!("error releasing FMOD System@{:p}: {:?}", self.0, err));
937    }
938    // unlock
939  }
940}
941
942impl From <ll::FMOD_GUID> for Guid {
943  fn from (guid : ll::FMOD_GUID) -> Self {
944    Guid {
945      data1: guid.Data1,
946      data2: guid.Data2,
947      data3: guid.Data3,
948      data4: guid.Data4
949    }
950  }
951}
952
953impl Outputtype {
954  /// # Panics
955  ///
956  /// Panics if `ll` is not a valid output type
957  pub fn from_ll (ll : ll::FMOD_OUTPUTTYPE) -> Self {
958    use num_traits::FromPrimitive;
959    // ll is i32 on windows and u32 on linux
960    #[allow(clippy::allow_attributes, clippy::unnecessary_cast)]
961    Self::from_u32 (ll as u32).unwrap()
962  }
963}