1use std;
2use std::rc::Rc;
3use std::ops::{Deref, DerefMut};
4use log;
5use num_derive::FromPrimitive;
6use crate::{ll, fmod_result, channel, Channel, ChannelGroup, Error, Guid, Mode,
7 SoundGroup, System, Timeunit};
8
9#[derive(Clone, Debug, PartialEq)]
12pub struct Sound {
13 inner : Rc <Inner>,
14 pub (crate) system : System
15}
16
17#[derive(Clone, Debug, PartialEq)]
19pub struct SoundRef {
20 pub (crate) sound : Sound
21}
22
23#[derive(PartialEq)]
24struct Inner {
25 raw : *mut ll::FMOD_SOUND,
26 owned : bool
28}
29
30#[derive(Default)]
32pub struct Createsoundexinfo {
33 pub length : u32,
34 pub fileoffset : u32,
35 pub numchannels : i32,
36 pub defaultfrequency : i32,
37 pub format : Format,
38 pub decodebuffersize : u32,
39 pub initialsubsound : i32,
40 pub numsubsounds : i32,
41 pub inclusionlist : Vec <i32>,
42 pub pcmreadcallback : ll::FMOD_SOUND_PCMREAD_CALLBACK,
43 pub pcmsetposcallback : ll::FMOD_SOUND_PCMSETPOS_CALLBACK,
44 pub nonblockcallback : ll::FMOD_SOUND_NONBLOCK_CALLBACK,
45 pub dlsname : std::ffi::CString,
46 pub encryptionkey : std::ffi::CString,
47 pub maxpolyphony : i32,
48 pub userdata : Vec <u8>, pub suggestedsoundtype : Type,
50 pub fileuseropen : ll::FMOD_FILE_OPEN_CALLBACK,
51 pub fileuserclose : ll::FMOD_FILE_CLOSE_CALLBACK,
52 pub fileuserread : ll::FMOD_FILE_READ_CALLBACK,
53 pub fileuserseek : ll::FMOD_FILE_SEEK_CALLBACK,
54 pub fileuserasyncread : ll::FMOD_FILE_ASYNCREAD_CALLBACK,
55 pub fileuserasynccancel : ll::FMOD_FILE_ASYNCCANCEL_CALLBACK,
56 pub fileuserdata : Vec <u8>, pub filebuffersize : i32,
58 pub channelorder : channel::Channelorder,
59 pub channelmask : channel::Channelmask,
60 pub initialsoundgroup : Option <SoundGroup>,
61 pub initialseekposition : u32,
62 pub initialseekpostype : Timeunit,
63 pub ignoresetfilesystem : i32,
64 pub audioqueuepolicy : u32,
65 pub minmidigranularity : u32,
66 pub nonblockthreadid : i32,
67 pub fsbguid : Option <Guid>,
68}
69
70#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
71#[derive(Default)]
72pub enum Format {
73 #[default]
74 None = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_NONE as isize,
75 Pcm8 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM8 as isize,
76 Pcm16 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM16 as isize,
77 Pcm24 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM24 as isize,
78 Pcm32 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM32 as isize,
79 PcmFloat = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCMFLOAT as isize,
80 Bitstream = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_BITSTREAM as isize,
81 MAX = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_MAX as isize
82}
83
84#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
85#[derive(Default)]
86pub enum Type {
87 #[default]
88 Unknown = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_UNKNOWN as isize,
89 Aiff = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AIFF as isize,
90 Asf = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_ASF as isize,
91 Dls = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_DLS as isize,
92 Flac = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FLAC as isize,
93 Fsb = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FSB as isize,
94 It = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_IT as isize,
95 Midi = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MIDI as isize,
96 Mod = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MOD as isize,
97 Mpeg = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MPEG as isize,
98 Oggvorbis = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_OGGVORBIS as isize,
99 Playlist = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_PLAYLIST as isize,
100 Raw = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_RAW as isize,
101 S3m = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_S3M as isize,
102 User = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_USER as isize,
103 Wav = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_WAV as isize,
104 Xm = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_XM as isize,
105 Xma = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_XMA as isize,
106 Audioqueue = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AUDIOQUEUE as isize,
107 At9 = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AT9 as isize,
108 Vorbis = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_VORBIS as isize,
109 MediaFoundation = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MEDIA_FOUNDATION as isize,
110 Mediacodec = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MEDIACODEC as isize,
111 Fadpcm = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FADPCM as isize,
112 MAX = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MAX as isize
113}
114
115#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
116pub enum Openstate {
117 Ready = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_READY as isize,
118 Loading = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_LOADING as isize,
119 Error = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_ERROR as isize,
120 Connecting = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_CONNECTING as isize,
121 Buffering = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_BUFFERING as isize,
122 Seeking = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_SEEKING as isize,
123 Playing = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_PLAYING as isize,
124 Setposition = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_SETPOSITION as isize,
125 MAX = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_MAX as isize
126}
127
128impl Sound {
129 #[inline]
130 pub fn from_raw_parts (
131 raw : *mut ll::FMOD_SOUND,
132 owned : bool,
133 system : System
134 ) -> Self {
135 let inner = Rc::new (Inner { raw, owned });
136 Sound { inner, system }
137 }
138
139 #[inline]
140 fn raw (&self) -> *mut ll::FMOD_SOUND {
141 self.inner.raw
142 }
143
144 #[inline]
145 pub fn sound_ref (&self) -> SoundRef {
146 let sound = {
147 let inner = Rc::new (Inner {
148 raw: self.raw(),
149 owned: false
150 });
151 let system = self.system.clone();
152 Sound { inner, system }
153 };
154 SoundRef { sound }
155 }
156
157 #[inline]
161 pub fn get_3d_cone_settings (&self) -> Result <(f32, f32, f32), Error> {
162 let mut insideconeangle = 0.0;
163 let mut outsideconeangle = 0.0;
164 let mut outsidevolume = 0.0;
165 unsafe {
166 fmod_result!(ll::FMOD_Sound_Get3DConeSettings (self.raw(),
167 &mut insideconeangle, &mut outsideconeangle, &mut outsidevolume
168 ))?;
169 }
170 Ok ((insideconeangle, outsideconeangle, outsidevolume))
171 }
172
173 #[inline]
175 pub fn get_3d_min_max_distance (&self) -> Result <(f32, f32), Error> {
176 let mut min = 0.0;
177 let mut max = 0.0;
178 unsafe {
179 fmod_result!(
180 ll::FMOD_Sound_Get3DMinMaxDistance (self.raw(), &mut min, &mut max)
181 )?;
182 }
183 Ok ((min, max))
184 }
185
186 #[inline]
191 pub fn get_defaults (&self) -> Result <(f32, i32), Error> {
192 let mut frequency = 0.0;
193 let mut priority = 0;
194 unsafe {
195 fmod_result!(
196 ll::FMOD_Sound_GetDefaults (self.raw(), &mut frequency, &mut priority)
197 )?;
198 }
199 Ok ((frequency, priority))
200 }
201
202 #[inline]
206 pub fn get_format (&self) -> Result <(Type, Format, i32, i32), Error> {
207 let mut type_ = 0;
208 let mut format = 0;
209 let mut channels = 0;
210 let mut bits = 0;
211 unsafe {
212 fmod_result!(
213 ll::FMOD_Sound_GetFormat (self.raw(),
214 &mut type_, &mut format, &mut channels, &mut bits)
215 )?;
216 }
217 let type_ = Type::from_ll (type_);
218 let format = Format::from_ll (format);
219 Ok ((type_, format, channels, bits))
220 }
221
222 #[inline]
223 pub fn get_length (&self, timeunit : Timeunit) -> Result <u32, Error> {
224 let mut length = 0;
225 unsafe {
226 fmod_result!(
227 ll::FMOD_Sound_GetLength (self.raw(), &mut length, timeunit.bits())
228 )?;
229 }
230 Ok (length)
231 }
232
233 #[inline]
234 pub fn get_loop_count (&self) -> Result <i32, Error> {
235 let mut loopcount = 0;
236 unsafe {
237 fmod_result!(ll::FMOD_Sound_GetLoopCount (self.raw(), &mut loopcount))?;
238 }
239 Ok (loopcount)
240 }
241
242 #[inline]
243 pub fn get_loop_points (&self,
244 loopstarttype : Timeunit, loopendtype : Timeunit
245 ) -> Result <(u32, u32), Error> {
246 let mut loopstart = 0;
247 let mut loopend = 0;
248 unsafe {
249 fmod_result!(ll::FMOD_Sound_GetLoopPoints (self.raw(),
250 &mut loopstart, loopstarttype.bits(),
251 &mut loopend, loopendtype.bits())
252 )?;
253 }
254 Ok ((loopstart, loopend))
255 }
256
257 #[inline]
258 pub fn get_mode (&self) -> Result <Mode, Error> {
259 let mut mode = 0;
260 unsafe {
261 fmod_result!(ll::FMOD_Sound_GetMode (self.raw(), &mut mode))?;
262 }
263 Mode::from_bits (mode).ok_or_else (||{
264 log::error!("sound get mode bits not valid: {mode:032b}");
265 Error::InvalidParam
266 })
267 }
268
269 #[inline]
274 pub fn get_open_state (&self) -> Result <(Openstate, u32, bool, bool), Error> {
275 let mut openstate = 0;
276 let mut percentbuffered = 0;
277 let mut starving = 0;
278 let mut diskbusy = 0;
279 unsafe {
280 fmod_result!(
281 ll::FMOD_Sound_GetOpenState (self.raw(),
282 &mut openstate, &mut percentbuffered, &mut starving, &mut diskbusy)
283 )?;
284 }
285 let openstate = Openstate::from_ll (openstate);
286 let starving = starving != 0;
287 let diskbusy = diskbusy != 0;
288 Ok ((openstate, percentbuffered, starving, diskbusy))
289 }
290
291 #[inline]
292 pub fn get_name (&self) -> Result <String, Error> {
293 let namelen = 256i32;
295 #[expect(clippy::cast_sign_loss)]
296 let mut name = vec![0; namelen as usize];
297 unsafe {
298 fmod_result!(
299 ll::FMOD_Sound_GetName (self.raw(), name.as_mut_ptr() as *mut i8, namelen)
300 )?;
301 }
302 name.retain (|c| *c != 0x0);
303 String::from_utf8 (name).map_err (|e|{
304 log::error!("sound get name string invalid utf8: {e}");
305 Error::InvalidString
306 })
307 }
308
309 #[inline]
310 pub fn get_num_sub_sounds (&self) -> Result <i32, Error> {
311 let mut numsubsounds = 0;
312 unsafe {
313 fmod_result!(
314 ll::FMOD_Sound_GetNumSubSounds (self.raw(), &mut numsubsounds)
315 )?;
316 }
317 Ok (numsubsounds)
318 }
319
320 #[inline]
321 pub fn get_num_sync_points (&self) -> Result <i32, Error> {
322 let mut numsyncpoints = 0;
323 unsafe {
324 fmod_result!(
325 ll::FMOD_Sound_GetNumSyncPoints (self.raw(), &mut numsyncpoints)
326 )?;
327 }
328 Ok (numsyncpoints)
329 }
330
331 #[inline]
339 pub fn get_num_tags (&self) -> Result <(i32, i32), Error> {
340 let mut numtags = 0;
341 let mut numtagsupdated = 0;
342 unsafe {
343 fmod_result!(
344 ll::FMOD_Sound_GetNumTags (self.raw(), &mut numtags, &mut numtagsupdated)
345 )?;
346 }
347 Ok ((numtags, numtagsupdated))
348 }
349
350 #[inline]
369 pub fn play (&mut self, channel_group : Option <&mut ChannelGroup>, paused : bool)
370 -> Result <Channel, Error>
371 {
372 let channel_group = channel_group
373 .map_or (std::ptr::null_mut(), ChannelGroup::raw_mut);
374 let mut channel = std::ptr::null_mut();
375 unsafe {
376 fmod_result!(ll::FMOD_System_PlaySound (
377 self.system.raw_mut(), self.raw(), channel_group, paused as i32,
378 &mut channel)
379 )?;
380 }
381 Ok (Channel::from_raw_parts (channel, self.sound_ref()))
382 }
383
384 #[inline]
391 pub fn set_defaults (&self, frequency : f32, priority : u8)
392 -> Result <(), Error>
393 {
394 unsafe {
395 fmod_result!(
396 ll::FMOD_Sound_SetDefaults (self.raw(), frequency, priority as i32)
397 )
398 }
399 }
400
401 #[inline]
402 pub fn set_mode (&mut self, mode : Mode) -> Result <(), Error> {
403 unsafe {
404 fmod_result!(ll::FMOD_Sound_SetMode (self.raw(), mode.bits()))
405 }
406 }
407}
408
409impl Deref for SoundRef {
410 type Target = Sound;
411 fn deref (&self) -> &Sound {
412 &self.sound
413 }
414}
415
416impl DerefMut for SoundRef {
417 fn deref_mut (&mut self) -> &mut Sound {
418 &mut self.sound
419 }
420}
421
422impl std::fmt::Debug for Inner {
423 fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
424 write!(f, "Inner {{ raw: {:p}, owned: {} }}", self.raw, self.owned)
425 }
426}
427
428impl Drop for Inner {
429 fn drop (&mut self) {
430 if self.owned {
431 unsafe {
432 let _ = fmod_result!(ll::FMOD_Sound_Release (self.raw)).map_err (|err|
433 log::error!("error releasing FMOD Sound@{self:?}: {err:?}"));
434 }
435 }
436 }
437}
438
439impl Createsoundexinfo {
440 pub fn to_ll (&mut self) -> ll::FMOD_CREATESOUNDEXINFO {
441 #[expect(clippy::cast_possible_truncation)]
442 ll::FMOD_CREATESOUNDEXINFO {
443 cbsize: size_of::<ll::FMOD_CREATESOUNDEXINFO>() as i32,
444 length: self.length,
445 fileoffset: self.fileoffset,
446 numchannels: self.numchannels,
447 defaultfrequency: self.defaultfrequency,
448 format: self.format as ll::FMOD_SOUND_FORMAT,
449 decodebuffersize: self.decodebuffersize,
450 initialsubsound: self.initialsubsound,
451 numsubsounds: self.numsubsounds,
452 inclusionlist: if !self.inclusionlist.is_empty() {
453 self.inclusionlist.as_mut_ptr()
454 } else {
455 std::ptr::null_mut()
456 },
457 inclusionlistnum: self.inclusionlist.len() as i32,
458 pcmreadcallback: self.pcmreadcallback,
459 pcmsetposcallback: self.pcmsetposcallback,
460 nonblockcallback: self.nonblockcallback,
461 dlsname: if !self.dlsname.as_bytes().is_empty() {
462 self.dlsname.as_c_str().as_ptr() as *mut std::os::raw::c_char
463 } else {
464 std::ptr::null_mut()
465 },
466 encryptionkey: if !self.encryptionkey.as_bytes().is_empty() {
467 self.encryptionkey.as_c_str().as_ptr() as *mut std::os::raw::c_char
468 } else {
469 std::ptr::null_mut()
470 },
471 maxpolyphony: self.maxpolyphony,
472 userdata: std::ptr::null_mut(), suggestedsoundtype: self.suggestedsoundtype as ll::FMOD_SOUND_TYPE,
474 fileuseropen: self.fileuseropen,
475 fileuserclose: self.fileuserclose,
476 fileuserread: self.fileuserread,
477 fileuserseek: self.fileuserseek,
478 fileuserasyncread: self.fileuserasyncread,
479 fileuserasynccancel: self.fileuserasynccancel,
480 fileuserdata: std::ptr::null_mut(), filebuffersize: self.filebuffersize,
482 channelorder: self.channelorder as ll::FMOD_CHANNELORDER,
483 channelmask: self.channelmask.bits(),
484 initialsoundgroup: self.initialsoundgroup.as_mut()
485 .map_or (std::ptr::null_mut(), SoundGroup::raw_mut),
486 initialseekposition: self.initialseekposition,
487 initialseekpostype: self.initialseekpostype.bits(),
488 ignoresetfilesystem: self.ignoresetfilesystem,
489 audioqueuepolicy: self.audioqueuepolicy,
490 minmidigranularity: self.minmidigranularity,
491 nonblockthreadid: self.nonblockthreadid,
492 fsbguid: self.fsbguid.as_mut().map_or (
493 std::ptr::null_mut(),
494 |guid| std::ptr::from_mut::<ll::FMOD_GUID> (guid.as_mut_ll()))
495 }
496 }
497}
498
499impl Format {
500 pub fn from_ll (ll : ll::FMOD_SOUND_FORMAT) -> Self {
504 use num_traits::FromPrimitive;
505 #[allow(clippy::allow_attributes, clippy::unnecessary_cast, trivial_numeric_casts)]
507 Self::from_u32 (ll as u32).unwrap()
508 }
509}
510
511
512impl Type {
513 pub fn from_ll (ll : ll::FMOD_SOUND_TYPE) -> Self {
517 use num_traits::FromPrimitive;
518 #[allow(clippy::allow_attributes, clippy::unnecessary_cast, trivial_numeric_casts)]
520 Self::from_u32 (ll as u32).unwrap()
521 }
522}
523
524
525impl Openstate {
526 pub fn from_ll (ll : ll::FMOD_OPENSTATE) -> Self {
530 use num_traits::FromPrimitive;
531 #[allow(clippy::allow_attributes, clippy::unnecessary_cast, trivial_numeric_casts)]
533 Self::from_u32 (ll as u32).unwrap()
534 }
535}