Skip to main content

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      #[expect(trivial_numeric_casts)]
290      fmod_result!(ll::FMOD_System_GetDriverInfo (
291        self.raw(),
292        id as raw::c_int,
293        name.as_mut_ptr() as *mut raw::c_char,
294        name_len as raw::c_int,
295        &mut guid,
296        &mut systemrate,
297        &mut speakermode,
298        &mut speakermodechannels
299      ))?;
300    }
301    let name = {
302      name.retain (|c| *c != 0x0);
303      String::from_utf8 (name).map_err (|e|{
304        log::error!("system driver name string invalid utf8: {e}");
305        Error::InvalidString
306      })?
307    };
308    let guid = Guid::from (guid);
309    let speakermode = Speakermode::from_ll (speakermode);
310    let info = DriverInfo {
311      name, guid, systemrate, speakermode, speakermodechannels
312    };
313    Ok (info)
314  }
315
316  /// Retrieves the buffer size settings for the FMOD software mixing engine.
317  ///
318  /// Returns `(bufferlength, numbuffers)`:
319  ///
320  /// - `buffelength` -- Address of a variable that receives the mixer engine
321  ///   block size in samples. Default = 1024. (milliseconds = 1024 at 48khz =
322  ///   1024 / 48000 * 1000 = 10.66ms). This means the mixer updates every
323  ///   21.3ms.
324  /// - `numbuffers` -- Address of a variable that receives the mixer engine
325  ///   number of buffers used. Default = 4. To get the total buffersize
326  ///   multiply the bufferlength by the numbuffers value. By default this would
327  ///   be 4*1024.
328  #[inline]
329  pub fn get_dsp_buffer_size (&self) -> Result <(u32, i32), Error> {
330    let mut bufferlength = 0;
331    let mut numbuffers = 0;
332    unsafe {
333      fmod_result!(
334        ll::FMOD_System_GetDSPBufferSize (
335          self.raw(), &mut bufferlength, &mut numbuffers)
336      )?;
337    }
338    Ok ((bufferlength, numbuffers))
339  }
340
341  /// Calculates geometry occlusion between a listener and a sound source.
342  ///
343  /// Returns `(direct, reverb)`.
344  #[inline]
345  pub fn get_geometry_occlusion (&self, listener : [f32; 3], source : [f32; 3])
346    -> Result <(f32, f32), Error>
347  {
348    let listener = vector::to_ll (listener);
349    let source   = vector::to_ll (source);
350    let mut direct = 0.0;
351    let mut reverb = 0.0;
352    unsafe {
353      fmod_result!(
354        ll::FMOD_System_GetGeometryOcclusion (self.raw(),
355          &listener, &source, &mut direct, &mut reverb)
356      )?;
357    }
358    Ok ((direct, reverb))
359  }
360
361  /// Retrieves the maximum world size for the geometry engine
362  #[inline]
363  pub fn get_geometry_settings (&self) -> Result <f32, Error> {
364    let mut maxworldsize = 0.0;
365    unsafe {
366      fmod_result!(
367        ll::FMOD_System_GetGeometrySettings (self.raw(), &mut maxworldsize)
368      )?;
369    }
370    Ok (maxworldsize)
371  }
372
373  #[inline]
374  pub fn get_master_channel_group (&self) -> Result <ChannelGroupRef, Error> {
375    let mut raw = std::ptr::null_mut();
376    unsafe {
377      fmod_result!(
378        ll::FMOD_System_GetMasterChannelGroup (self.raw(), &mut raw)
379      )?;
380    }
381    let channel_group =
382      ChannelGroup::from_raw_parts (raw, false, self.clone());
383    Ok (ChannelGroupRef { channel_group })
384  }
385
386  #[inline]
387  pub fn get_num_drivers (&self) -> Result <i32, Error> {
388    let mut numdrivers = 0;
389    unsafe {
390      fmod_result!(ll::FMOD_System_GetNumDrivers (self.raw(), &mut numdrivers))?;
391    }
392    Ok (numdrivers)
393  }
394
395  #[inline]
396  pub fn get_num_plugins (&self, plugintype : Plugintype)
397    -> Result <i32, Error>
398  {
399    let mut numplugins = 0;
400    unsafe {
401      fmod_result!(ll::FMOD_System_GetNumPlugins (
402        self.raw(), plugintype as ll::FMOD_PLUGINTYPE, &mut numplugins)
403      )?;
404    }
405    Ok (numplugins)
406  }
407
408  #[inline]
409  pub fn get_output (&self) -> Result <Outputtype, Error> {
410    let mut outputtype = 0;
411    unsafe {
412      fmod_result!(ll::FMOD_System_GetOutput (self.raw(), &mut outputtype))?;
413    }
414    Ok (Outputtype::from_ll (outputtype))
415  }
416
417  /// Returns the currently selected output as an id in the list of output
418  /// plugins
419  #[inline]
420  pub fn get_output_by_plugin (&self) -> Result <PluginHandle, Error> {
421    let mut handle = 0;
422    unsafe {
423      fmod_result!(ll::FMOD_System_GetOutputByPlugin (self.raw(), &mut handle))?;
424    }
425    Ok (PluginHandle (handle))
426  }
427
428  #[inline]
429  pub fn get_record_driver_info (&self, id : i32)
430    -> Result <(DriverInfo, DriverState), Error>
431  {
432    // NB: assuming names are not longer than 256 bytes
433    let name_len = 256i32;
434    #[expect(clippy::cast_sign_loss)]
435    let mut name = vec![0; name_len as usize];
436    let mut guid = ll::FMOD_GUID {
437      Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8]
438    };
439    let mut systemrate = 0;
440    let mut speakermode = 0;
441    let mut speakermodechannels = 0;
442    let mut state = 0;
443    unsafe {
444      use std::os::raw;
445      #[expect(trivial_numeric_casts)]
446      fmod_result!(ll::FMOD_System_GetRecordDriverInfo (
447        self.raw(),
448        id as raw::c_int,
449        name.as_mut_ptr() as *mut raw::c_char,
450        name_len as raw::c_int,
451        &mut guid,
452        &mut systemrate,
453        &mut speakermode,
454        &mut speakermodechannels,
455        &mut state
456      ))?;
457    }
458    let name = {
459      name.retain (|c| *c != 0x0);
460      String::from_utf8 (name).map_err (|e|{
461        log::error!("system record driver name string invalid utf8: {e}");
462        Error::InvalidString
463      })?
464    };
465    let guid = Guid::from (guid);
466    let speakermode = Speakermode::from_ll (speakermode);
467    let info = DriverInfo {
468      name, guid, systemrate, speakermode, speakermodechannels
469    };
470    let state = DriverState::from_bits (state).ok_or_else (||{
471      log::error!("system driver state bits not valid: {state:032b}");
472      Error::InvalidParam
473    })?;
474    Ok ((info, state))
475  }
476
477  /// Returns the number of recording drivers available for this output mode and
478  /// the number of recording drivers currently plugged in
479  #[inline]
480  pub fn get_record_num_drivers (&self) -> Result <(i32, i32), Error> {
481    let mut numdrivers   = 0;
482    let mut numconnected = 0;
483    unsafe {
484      fmod_result!(ll::FMOD_System_GetRecordNumDrivers (
485        self.raw(), &mut numdrivers, &mut numconnected
486      ))?;
487    }
488    Ok ((numdrivers, numconnected))
489  }
490
491  #[inline]
492  pub fn get_reverb_properties (&self, instance : i32)
493    -> Result <reverb3d::Properties, Error>
494  {
495    let mut properties = ll::FMOD_REVERB_PROPERTIES {
496      DecayTime:          0.0,
497      EarlyDelay:         0.0,
498      LateDelay:          0.0,
499      HFReference:        0.0,
500      HFDecayRatio:       0.0,
501      Diffusion:          0.0,
502      Density:            0.0,
503      LowShelfFrequency:  0.0,
504      LowShelfGain:       0.0,
505      HighCut:            0.0,
506      EarlyLateMix:       0.0,
507      WetLevel:           0.0
508    };
509    unsafe {
510      fmod_result!(
511        ll::FMOD_System_GetReverbProperties (self.raw(),
512          instance, &mut properties)
513      )?;
514    }
515    let properties = reverb3d::Properties::from_ll (&properties);
516    Ok (properties)
517  }
518
519  #[inline]
520  pub fn get_software_channels (&self) -> Result <i32, Error> {
521    let mut numsoftwarechannels = 0;
522    unsafe {
523      fmod_result!(
524        ll::FMOD_System_GetSoftwareChannels (
525          self.raw(), &mut numsoftwarechannels)
526      )?;
527    }
528    Ok (numsoftwarechannels)
529  }
530
531  /// Retrieves the output format for the software mixer:
532  ///
533  /// `(samplerate, speakermode, numrawspeakers)`
534  #[inline]
535  pub fn get_software_format (&self)
536    -> Result <(i32, Speakermode, i32), Error>
537  {
538    let mut samplerate = 0;
539    let mut speakermode = 0;
540    let mut numrawspeakers = 0;
541    unsafe {
542      fmod_result!(
543        ll::FMOD_System_GetSoftwareFormat (
544          self.raw(), &mut samplerate, &mut speakermode, &mut numrawspeakers)
545      )?;
546    }
547    let speakermode = Speakermode::from_ll (speakermode);
548    Ok ((samplerate, speakermode, numrawspeakers))
549  }
550
551  #[inline]
552  pub fn get_sound_ram (&self) -> Result <SoundRam, Error> {
553    let mut ram = SoundRam::default();
554    unsafe {
555      fmod_result!(
556        ll::FMOD_System_GetSoundRAM (self.raw(),
557          &mut ram.currentalloced, &mut ram.maxalloced, &mut ram.total)
558      )?;
559    }
560    Ok (ram)
561  }
562
563  #[inline]
564  pub fn get_speaker_mode_channels (&self, mode : Speakermode)
565    -> Result <i32, Error>
566  {
567    let mut channels = 0;
568    unsafe {
569      fmod_result!(
570        ll::FMOD_System_GetSpeakerModeChannels (
571          self.raw(), mode as ll::FMOD_SPEAKERMODE, &mut channels)
572      )?;
573    }
574    Ok (channels)
575  }
576
577  /// Retrieves the current speaker position information for the selected
578  /// speaker.
579  ///
580  /// Returns `(x, y, active)`.
581  #[inline]
582  pub fn get_speaker_position (&self, speaker : Speaker)
583    -> Result <(f32, f32, bool), Error>
584  {
585    let mut x      = 0.0;
586    let mut y      = 0.0;
587    let mut active = 0;
588    unsafe {
589      fmod_result!(
590        ll::FMOD_System_GetSpeakerPosition (
591          self.raw(), speaker as ll::FMOD_SPEAKER, &mut x, &mut y, &mut active)
592      )?;
593    }
594    Ok ((x, y, active != 0))
595  }
596
597  /// Returns the current internal buffersize settings for streamable sounds
598  #[inline]
599  pub fn get_stream_buffer_size (&self)
600    -> Result <(u32, Timeunit), Error>
601  {
602    let mut filebuffersize     = 0;
603    let mut filebuffersizetype = 0;
604    unsafe {
605      fmod_result!(
606        ll::FMOD_System_GetStreamBufferSize (
607          self.raw(), &mut filebuffersize, &mut filebuffersizetype)
608      )?;
609    }
610    let timeunit = Timeunit::from_bits (filebuffersizetype).ok_or_else (||{
611      log::error!("system stream buffer timeunit bits not valid: \
612        {filebuffersizetype:032b}");
613      Error::InvalidParam
614    })?;
615    Ok ((filebuffersize, timeunit))
616  }
617
618  /// Version of linked FMOD shared library.
619  ///
620  /// The version is a 32 bit hexadecimal value formated as 16:8:8, with the
621  /// upper 16 bits being the product version, the middle 8 bits being the major
622  /// version and the bottom 8 bits being the minor version.
623  #[inline]
624  pub fn get_version (&self) -> Result <u32, Error> {
625    let mut version = 0;
626    unsafe {
627      fmod_result!(ll::FMOD_System_GetVersion (self.raw(), &mut version))?;
628    }
629    Ok (version)
630  }
631
632  /// Version of linked FMOD shared library
633  #[inline]
634  pub fn get_version_string (&self) -> Result <String, Error> {
635    self.get_version().map (crate::version_string)
636  }
637
638  //
639  //  &mut self
640  //
641
642  #[inline]
643  pub fn create_channel_group (&mut self, name : Option <&str>)
644    -> Result <ChannelGroup, Error>
645  {
646    let mut raw     = std::ptr::null_mut();
647    let name_str    = name.unwrap_or ("");
648    let name_string = std::ffi::CString::new (name_str).map_err (|e|{
649      log::error!("create channel group name \"{name_str}\" contains a null byte: {e}");
650      Error::InvalidString
651    })?;
652    let name_ptr = if name.is_some() {
653      name_string.as_ptr()
654    } else {
655      std::ptr::null()
656    };
657    unsafe {
658      fmod_result!(ll::FMOD_System_CreateChannelGroup (self.raw(), name_ptr, &mut raw))?
659    }
660    Ok (ChannelGroup::from_raw_parts (raw, true, self.clone()))
661  }
662
663  #[inline]
664  pub fn create_dsp (&mut self, description : &'static dsp::Description)
665    -> Result <Dsp, Error>
666  {
667    let mut raw = std::ptr::null_mut();
668    unsafe {
669      fmod_result!(
670        ll::FMOD_System_CreateDSP (self.raw(),
671          std::ptr::from_ref (description.as_ref()), &mut raw)
672      )?
673    }
674    Ok (Dsp::from_raw_parts (raw, true, self.clone()))
675  }
676
677  #[inline]
678  pub fn create_dsp_by_type (&mut self, type_ : dsp::Type)
679    -> Result <Dsp, Error>
680  {
681    let mut raw = std::ptr::null_mut();
682    unsafe {
683      fmod_result!(
684        ll::FMOD_System_CreateDSPByType (self.raw(), type_ as ll::FMOD_DSP_TYPE,
685          &mut raw)
686      )?;
687    }
688    Ok (Dsp::from_raw_parts (raw, true, self.clone()))
689  }
690
691  /// A convenience method that creates a `dsp::Sfxreverb` from a given
692  /// `reverb3d::Properties` and dry level.
693  pub fn create_dsp_sfxreverb (&mut self,
694    properties : &reverb3d::Properties, dry_level : f32
695  ) -> Result <Dsp, Error> {
696    let mut raw = std::ptr::null_mut();
697    unsafe {
698      fmod_result!(
699        ll::FMOD_System_CreateDSPByType (
700          self.raw(),
701          dsp::Type::Sfxreverb as ll::FMOD_DSP_TYPE,
702          &mut raw
703        )
704      )?;
705    }
706    let mut dsp = Dsp::from_raw_parts (raw, true, self.clone());
707    dsp.set_parameters_sfxreverb (properties, dry_level)?;
708    Ok (dsp)
709  }
710
711  #[inline]
712  pub fn create_reverb3d (&mut self) -> Result <Reverb3d, Error> {
713    let mut raw = std::ptr::null_mut();
714    unsafe {
715      fmod_result!(ll::FMOD_System_CreateReverb3D (self.raw(), &mut raw))?;
716    }
717    Ok (Reverb3d::from_raw_parts (raw, self.clone()))
718  }
719
720  pub fn create_sound_from_file (&mut self,
721    filename : &str,
722    mode     : Mode,
723    exinfo   : Option <&mut sound::Createsoundexinfo>
724  ) -> Result <Sound, Error> {
725    let filename = std::ffi::CString::new (filename).map_err (|e|{
726      log::error!("create sound filename \"{filename}\" contains a null byte: {e}");
727      Error::InvalidString
728    })?;
729    let mut exinfo = exinfo.map (sound::Createsoundexinfo::to_ll);
730    let mut raw    = std::ptr::null_mut();
731    unsafe {
732      fmod_result!(
733        ll::FMOD_System_CreateSound (
734          self.raw(),
735          filename.as_ptr(),
736          mode.bits(),
737          exinfo.as_mut().map_or (
738            std::ptr::null_mut(), std::ptr::from_mut::<ll::FMOD_CREATESOUNDEXINFO>),
739          &mut raw)
740      )?;
741    }
742    Ok (Sound::from_raw_parts (raw, true, self.clone()))
743  }
744
745  pub fn create_sound_from_memory (&mut self,
746    data     : &[u8],
747    mut mode : Mode,
748    exinfo   : Option <&mut sound::Createsoundexinfo>
749  ) -> Result <Sound, Error> {
750    mode |= Mode::OPENMEMORY;
751    let mut exinfo_default = Default::default();
752    let mut exinfo = {
753      let exinfo    = exinfo.unwrap_or (&mut exinfo_default);
754      exinfo.length = u32::try_from (data.len()).map_err (|_|{
755        log::error!("create sound from memory data too big: {}", data.len());
756        Error::InvalidParam
757      })?;
758      sound::Createsoundexinfo::to_ll (exinfo)
759    };
760    let mut raw = std::ptr::null_mut();
761    let data = if !data.is_empty() {
762      data.as_ptr() as *const std::os::raw::c_char
763    } else {
764      std::ptr::null()
765    };
766    unsafe {
767      fmod_result!(
768        ll::FMOD_System_CreateSound (
769          self.raw(),
770          data,
771          mode.bits(),
772          &mut exinfo,
773          &mut raw)
774      )?;
775    }
776    Ok (Sound::from_raw_parts (raw, true, self.clone()))
777  }
778
779  /// # Panics
780  ///
781  /// Panics on write failures.
782  pub fn create_sound_from_pcm (&mut self,
783    pcm    : &[i16],
784    mode   : Mode,
785    exinfo : Option <&mut sound::Createsoundexinfo>
786  ) -> Result <Sound, Error> {
787    const WAV_HEADER_LEN : usize = 44;
788    let pcm_bytelen = pcm.len() * 2;
789    let wav_bytelen = WAV_HEADER_LEN + pcm_bytelen;
790    let mut wav     = vec![0; wav_bytelen];
791    {
792      let cursor = std::io::Cursor::new (&mut wav);
793      let writer = std::io::BufWriter::new (cursor);
794      let mut wav_writer = riff_wave::WaveWriter::new (1, 44100, 16, writer)
795        .unwrap();
796      for sample in pcm.iter() {
797        wav_writer.write_sample_i16 (*sample).unwrap();
798      }
799    }
800    self.create_sound_from_memory (wav.as_slice(), mode, exinfo)
801  }
802
803  /// Open a sound in `DEFAULT` mode with no exinfo argument
804  #[inline]
805  pub fn create_sound_from_file_default (&mut self, filename : &str)
806    -> Result <Sound, Error>
807  {
808    self.create_sound_from_file (filename, Mode::DEFAULT, None)
809  }
810
811  #[inline]
812  pub fn create_sound_from_memory_default (&mut self, data : &[u8])
813    -> Result <Sound, Error>
814  {
815    self.create_sound_from_memory (data, Mode::DEFAULT, None)
816  }
817
818  #[inline]
819  pub fn create_sound_from_pcm_default (&mut self, pcm : &[i16])
820    -> Result <Sound, Error>
821  {
822    self.create_sound_from_pcm (pcm, Mode::DEFAULT, None)
823  }
824
825  #[inline]
826  pub fn set_3d_listener_attributes (&mut self,
827    listener : i32, attributes : &ListenerAttributes
828  ) -> Result <(), Error> {
829    let pos     = vector::to_ll (attributes.pos);
830    let vel     = vector::to_ll (attributes.vel);
831    let forward = vector::to_ll (attributes.forward);
832    let up      = vector::to_ll (attributes.up);
833    unsafe {
834      fmod_result!(
835        ll::FMOD_System_Set3DListenerAttributes (self.raw(),
836          listener, &pos, &vel, &forward, &up)
837      )
838    }
839  }
840
841  #[inline]
842  pub fn set_output (&mut self, output : Outputtype) -> Result <(), Error> {
843    unsafe {
844      fmod_result!(
845        ll::FMOD_System_SetOutput (self.raw(), output as ll::FMOD_OUTPUTTYPE))
846    }
847  }
848
849  /// Sets parameters for the global reverb environment.
850  ///
851  /// `instance` -- Index of the particular reverb instance to target, from 0 to
852  /// `fmod::dsp::REVERB_MAXINSTANCES`.
853  ///
854  /// When using each instance for the first time, FMOD will create a physical
855  /// SFX reverb DSP unit that takes up several hundred kilobytes of memory and
856  /// some CPU.
857  #[inline]
858  pub fn set_reverb_properties (&mut self,
859    instance : i32, properties : &reverb3d::Properties
860  ) -> Result <(), Error> {
861    let properties = properties.to_ll();
862    unsafe {
863      fmod_result!(
864        ll::FMOD_System_SetReverbProperties (self.raw(), instance, &properties)
865      )
866    }
867  }
868
869  //
870  //  private
871  //
872
873  #[inline]
874  fn create() -> Result <Self, Error> {
875    let raw = {
876      let _lock = FMOD_SYSTEM_CREATE_DESTROY_MUTEX.lock().unwrap();
877      let mut raw = std::ptr::null_mut();
878      unsafe {
879        fmod_result!(ll::FMOD_System_Create (&mut raw))?;
880      }
881      raw
882      // unlock
883    };
884    let inner          = Rc::new (Inner (raw));
885    let system         = System { inner };
886    let system_version = system.get_version_string()?;
887    if system_version != *crate::FMOD_VERSION_STRING {
888      log::error!(
889        "FMOD created system got shared library version {:?}, \
890          bindings were generated for version {:?}",
891        system_version, *crate::FMOD_VERSION_STRING);
892      panic!(
893        "FMOD created system got shared library version {:?}, \
894          bindings were generated for version {:?}",
895        system_version, *crate::FMOD_VERSION_STRING);
896    }
897    Ok (system)
898  }
899
900  /// `max_channels` is the maximum number of *virtual channels*
901  #[inline]
902  fn init (&mut self, max_channels : u16, init_flags : Initflags)
903    -> Result <(), Error>
904  {
905    let max_channels = max_channels as i32;
906    unsafe {
907      fmod_result!(ll::FMOD_System_Init (
908        self.raw(), max_channels, init_flags.bits(), std::ptr::null_mut()))
909    }
910  }
911
912  // /!\ NOTE /!\: this method can only be called when the system has not yet
913  // been initialized
914  #[inline]
915  fn set_software_channels (&mut self, numsoftwarechannels : i32)
916    -> Result <(), Error>
917  {
918    unsafe {
919      fmod_result!(
920        ll::FMOD_System_SetSoftwareChannels (self.raw(), numsoftwarechannels)
921      )
922    }
923  }
924
925}
926
927impl std::fmt::Debug for Inner {
928  fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
929    write!(f, "{:p}", self.0)
930  }
931}
932
933impl Drop for Inner {
934  fn drop (&mut self) {
935    let _lock = FMOD_SYSTEM_CREATE_DESTROY_MUTEX.lock().unwrap();
936    unsafe {
937      let _ = fmod_result!(ll::FMOD_System_Release (self.0)).map_err (
938        |err| log::error!("error releasing FMOD System@{:p}: {:?}", self.0, err));
939    }
940    // unlock
941  }
942}
943
944impl From <ll::FMOD_GUID> for Guid {
945  fn from (guid : ll::FMOD_GUID) -> Self {
946    Guid {
947      data1: guid.Data1,
948      data2: guid.Data2,
949      data3: guid.Data3,
950      data4: guid.Data4
951    }
952  }
953}
954
955impl Outputtype {
956  /// # Panics
957  ///
958  /// Panics if `ll` is not a valid output type
959  pub fn from_ll (ll : ll::FMOD_OUTPUTTYPE) -> Self {
960    use num_traits::FromPrimitive;
961    // ll is i32 on windows and u32 on linux
962    #[allow(clippy::allow_attributes, clippy::unnecessary_cast, trivial_numeric_casts)]
963    Self::from_u32 (ll as u32).unwrap()
964  }
965}