1use std::{
8 ffi::{c_float, c_int, c_short, c_uchar, c_uint, c_ushort},
9 mem::MaybeUninit,
10};
11
12use crate::{FmodResultExt, Result};
13use fmod_sys::*;
14use lanyard::{Utf8CStr, Utf8CString};
15
16use super::{FloatMappingType, Resampler, Speaker};
17use crate::{DspParameterDataType, TagType, string_from_utf16_be, string_from_utf16_le};
18
19#[cfg(doc)]
20use crate::{Channel, Geometry, Reverb3D, Sound, System, SystemBuilder};
21
22#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
24#[repr(C)]
26pub struct Guid {
27 pub data_1: c_uint,
29 pub data_2: c_ushort,
31 pub data_3: c_ushort,
33 pub data_4: [c_uchar; 8],
35}
36
37impl Guid {
38 #[cfg(feature = "studio")]
40 pub fn parse(string: &Utf8CStr) -> Result<Self> {
41 let mut guid = MaybeUninit::uninit();
42 unsafe {
43 FMOD_Studio_ParseID(string.as_ptr(), guid.as_mut_ptr()).to_result()?;
44 Ok(guid.assume_init().into())
45 }
46 }
47}
48
49impl From<FMOD_GUID> for Guid {
50 fn from(value: FMOD_GUID) -> Self {
51 Guid {
52 data_1: value.Data1,
53 data_2: value.Data2,
54 data_3: value.Data3,
55 data_4: value.Data4,
56 }
57 }
58}
59
60impl From<Guid> for FMOD_GUID {
61 fn from(value: Guid) -> Self {
62 FMOD_GUID {
63 Data1: value.data_1,
64 Data2: value.data_2,
65 Data3: value.data_3,
66 Data4: value.data_4,
67 }
68 }
69}
70
71impl std::fmt::Display for Guid {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 let Guid {
74 data_1,
75 data_2,
76 data_3,
77 data_4,
78 } = self;
79
80 f.write_str("{")?;
81 f.write_fmt(format_args!("{data_1:0>8x}-{data_2:0>4x}-{data_3:0>4x}-"))?;
82 f.write_fmt(format_args!("{:0>2x}{:0>2x}-", data_4[0], data_4[1]))?;
83 for b in &data_4[2..] {
84 f.write_fmt(format_args!("{b:0>2x}"))?;
85 }
86 f.write_str("}")
87 }
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
96#[repr(C)]
97pub struct Vector {
98 pub x: c_float,
100 pub y: c_float,
102 pub z: c_float,
104}
105
106impl From<Vector> for FMOD_VECTOR {
107 fn from(value: Vector) -> Self {
108 FMOD_VECTOR {
109 x: value.x,
110 y: value.y,
111 z: value.z,
112 }
113 }
114}
115
116impl From<FMOD_VECTOR> for Vector {
117 fn from(value: FMOD_VECTOR) -> Self {
118 Vector {
119 x: value.x,
120 y: value.y,
121 z: value.z,
122 }
123 }
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
128#[repr(C)]
129pub struct Attributes3D {
130 pub position: Vector,
132 pub velocity: Vector,
134 pub forward: Vector,
136 pub up: Vector,
138}
139
140impl From<FMOD_3D_ATTRIBUTES> for Attributes3D {
141 fn from(value: FMOD_3D_ATTRIBUTES) -> Self {
142 Attributes3D {
143 position: value.position.into(),
144 velocity: value.velocity.into(),
145 forward: value.forward.into(),
146 up: value.up.into(),
147 }
148 }
149}
150
151impl From<Attributes3D> for FMOD_3D_ATTRIBUTES {
152 fn from(value: Attributes3D) -> Self {
153 FMOD_3D_ATTRIBUTES {
154 position: value.position.into(),
155 velocity: value.velocity.into(),
156 forward: value.forward.into(),
157 up: value.up.into(),
158 }
159 }
160}
161
162#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
164pub struct CpuUsage {
165 pub dsp: c_float,
169 pub stream: c_float,
173 pub geometry: c_float,
177 pub update: c_float,
179 pub convolution_1: c_float,
183 pub convolution_2: c_float,
187}
188
189impl From<FMOD_CPU_USAGE> for CpuUsage {
190 fn from(value: FMOD_CPU_USAGE) -> Self {
191 CpuUsage {
192 dsp: value.dsp,
193 stream: value.stream,
194 geometry: value.geometry,
195 update: value.update,
196 convolution_1: value.convolution1,
197 convolution_2: value.convolution2,
198 }
199 }
200}
201
202impl From<CpuUsage> for FMOD_CPU_USAGE {
203 fn from(value: CpuUsage) -> Self {
204 FMOD_CPU_USAGE {
205 dsp: value.dsp,
206 stream: value.stream,
207 geometry: value.geometry,
208 update: value.update,
209 convolution1: value.convolution_1,
210 convolution2: value.convolution_2,
211 }
212 }
213}
214
215#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
217#[repr(C)]
218pub struct ReverbProperties {
219 pub decay_time: c_float,
221 pub early_delay: c_float,
223 pub late_delay: c_float,
225 pub hf_reference: c_float,
227 pub hf_decay_ratio: c_float,
229 pub diffusion: c_float,
231 pub density: c_float,
233 pub low_shelf_frequency: c_float,
235 pub low_shelf_gain: c_float,
237 pub high_cut: c_float,
239 pub early_late_mix: c_float,
241 pub wet_level: c_float,
243}
244
245impl From<FMOD_REVERB_PROPERTIES> for ReverbProperties {
246 fn from(value: FMOD_REVERB_PROPERTIES) -> Self {
247 ReverbProperties {
248 decay_time: value.DecayTime,
249 early_delay: value.EarlyDelay,
250 late_delay: value.LateDelay,
251 hf_reference: value.HFReference,
252 hf_decay_ratio: value.HFDecayRatio,
253 diffusion: value.Diffusion,
254 density: value.Density,
255 low_shelf_frequency: value.LowShelfFrequency,
256 low_shelf_gain: value.LowShelfGain,
257 high_cut: value.HighCut,
258 early_late_mix: value.EarlyLateMix,
259 wet_level: value.WetLevel,
260 }
261 }
262}
263
264impl From<ReverbProperties> for FMOD_REVERB_PROPERTIES {
265 fn from(value: ReverbProperties) -> Self {
266 FMOD_REVERB_PROPERTIES {
267 DecayTime: value.decay_time,
268 EarlyDelay: value.early_delay,
269 LateDelay: value.late_delay,
270 HFReference: value.hf_reference,
271 HFDecayRatio: value.hf_decay_ratio,
272 Diffusion: value.diffusion,
273 Density: value.density,
274 LowShelfFrequency: value.low_shelf_frequency,
275 LowShelfGain: value.low_shelf_gain,
276 HighCut: value.high_cut,
277 EarlyLateMix: value.early_late_mix,
278 WetLevel: value.wet_level,
279 }
280 }
281}
282
283#[derive(Debug)]
285pub struct DspParameterDescription {
286 pub kind: DspParameterType,
288 pub name: Utf8CString,
290 pub label: Utf8CString,
292 pub description: Utf8CString,
294}
295
296#[derive(Clone, Debug, PartialEq)]
298pub enum DspParameterType {
299 Float {
301 min: f32,
303 max: f32,
305 default: f32,
307 mapping: FloatMapping,
309 },
310 Int {
312 min: i32,
314 max: i32,
316 default: i32,
318 goes_to_infinity: bool,
320 names: Option<Vec<Utf8CString>>,
324 },
325 Bool {
327 default: bool,
329 names: Option<[Utf8CString; 2]>,
331 },
332 Data {
334 data_type: DspParameterDataType,
336 },
337}
338
339#[derive(Clone, Debug, PartialEq)]
341pub struct FloatMapping {
342 pub kind: FloatMappingType,
344 pub piecewise_linear_mapping: Option<PiecewiseLinearMapping>,
346}
347
348#[derive(Clone, Debug, PartialEq)]
350pub struct PiecewiseLinearMapping {
351 pub point_param_values: Vec<c_float>,
353 pub point_positions: Option<Vec<c_float>>,
360}
361
362impl FloatMapping {
363 unsafe fn from_ffi(value: FMOD_DSP_PARAMETER_FLOAT_MAPPING) -> Self {
364 let kind = value.type_.try_into().unwrap();
365
366 let piecewise_linear_mapping = if kind == FloatMappingType::PiecewiceLinear {
367 let point_param_values = unsafe {
368 std::slice::from_raw_parts(
369 value.piecewiselinearmapping.pointparamvalues,
370 value.piecewiselinearmapping.numpoints as _,
371 )
372 .to_vec()
373 };
374 let point_positions = if value.piecewiselinearmapping.pointpositions.is_null() {
375 None
376 } else {
377 Some(unsafe {
378 std::slice::from_raw_parts(
379 value.piecewiselinearmapping.pointpositions,
380 value.piecewiselinearmapping.numpoints as _,
381 )
382 .to_vec()
383 })
384 };
385 Some(PiecewiseLinearMapping {
386 point_param_values,
387 point_positions,
388 })
389 } else {
390 None
391 };
392
393 Self {
394 kind,
395 piecewise_linear_mapping,
396 }
397 }
398}
399
400impl DspParameterDescription {
401 pub unsafe fn from_ffi(value: FMOD_DSP_PARAMETER_DESC) -> Self {
415 let name = unsafe { Utf8CStr::from_ptr_unchecked(value.name.as_ptr()).to_cstring() };
417 let label = unsafe { Utf8CStr::from_ptr_unchecked(value.label.as_ptr()).to_cstring() };
418 let description = unsafe { Utf8CStr::from_ptr_unchecked(value.description).to_cstring() };
419 let kind = match value.type_ {
420 FMOD_DSP_PARAMETER_TYPE_FLOAT => {
421 let floatdesc = unsafe { value.__bindgen_anon_1.floatdesc };
422 let mapping = unsafe { FloatMapping::from_ffi(floatdesc.mapping) };
423
424 DspParameterType::Float {
425 min: floatdesc.min,
426 max: floatdesc.max,
427 default: floatdesc.defaultval,
428 mapping,
429 }
430 }
431 FMOD_DSP_PARAMETER_TYPE_INT => {
432 let intdesc = unsafe { value.__bindgen_anon_1.intdesc };
433 let names = if intdesc.valuenames.is_null() {
434 None
435 } else {
436 let pointers = unsafe {
437 std::slice::from_raw_parts(
438 intdesc.valuenames,
439 intdesc.max as usize - intdesc.min as usize + 1,
440 )
441 };
442 Some(
443 pointers
444 .iter()
445 .map(|p| unsafe { Utf8CStr::from_ptr_unchecked(*p).to_cstring() })
446 .collect(),
447 )
448 };
449
450 DspParameterType::Int {
451 min: intdesc.min,
452 max: intdesc.max,
453 default: intdesc.defaultval,
454 goes_to_infinity: intdesc.goestoinf.into(),
455 names,
456 }
457 }
458 FMOD_DSP_PARAMETER_TYPE_BOOL => {
459 let booldesc = unsafe { value.__bindgen_anon_1.booldesc };
460 let names = if booldesc.valuenames.is_null() {
461 None
462 } else {
463 let [p1, p2] =
464 unsafe { *std::ptr::from_ref(&booldesc.valuenames).cast::<[_; 2]>() };
465 Some([
466 unsafe { Utf8CStr::from_ptr_unchecked(p1).to_cstring() },
467 unsafe { Utf8CStr::from_ptr_unchecked(p2).to_cstring() },
468 ])
469 };
470
471 DspParameterType::Bool {
472 default: booldesc.defaultval.into(),
473 names,
474 }
475 }
476 FMOD_DSP_PARAMETER_TYPE_DATA => {
477 let datadesc = unsafe { value.__bindgen_anon_1.datadesc };
478 DspParameterType::Data {
479 data_type: datadesc.datatype.into(),
480 }
481 }
482 _ => panic!("invalid parameter description type"), };
484 Self {
485 kind,
486 name,
487 label,
488 description,
489 }
490 }
491
492 }
494
495#[derive(Clone, Copy, Debug, PartialEq)]
497pub struct DspMeteringInfo {
498 pub sample_count: c_int,
500 pub peak_level: [c_float; 32],
502 pub rms_level: [c_float; 32],
504 pub channel_count: c_short,
506}
507
508impl From<FMOD_DSP_METERING_INFO> for DspMeteringInfo {
509 fn from(value: FMOD_DSP_METERING_INFO) -> Self {
510 Self {
511 sample_count: value.numsamples,
512 peak_level: value.peaklevel,
513 rms_level: value.rmslevel,
514 channel_count: value.numchannels,
515 }
516 }
517}
518
519impl From<DspMeteringInfo> for FMOD_DSP_METERING_INFO {
520 fn from(value: DspMeteringInfo) -> Self {
521 FMOD_DSP_METERING_INFO {
522 numsamples: value.sample_count,
523 peaklevel: value.peak_level,
524 rmslevel: value.rms_level,
525 numchannels: value.channel_count,
526 }
527 }
528}
529
530#[derive(Debug)]
532pub struct Tag {
533 pub kind: TagType,
535 pub name: Utf8CString,
537 pub data: TagData,
539 pub updated: bool,
541}
542
543#[derive(Debug)]
545pub enum TagData {
547 Binary(Vec<u8>),
549 Integer(i64),
551 Float(f64),
553 String(String),
555 Utf8String(String),
557 Utf16StringBE(String),
559 Utf16String(String),
561}
562
563impl Tag {
564 #[allow(clippy::cast_lossless)]
576 pub unsafe fn from_ffi(value: FMOD_TAG) -> Self {
577 let kind = value.type_.try_into().unwrap();
578 let name = unsafe { Utf8CStr::from_ptr_unchecked(value.name).to_cstring() };
579 let updated = value.updated.into();
580 let data = unsafe {
581 match value.datatype {
583 FMOD_TAGDATATYPE_BINARY => {
584 let slice =
585 std::slice::from_raw_parts(value.data as *const u8, value.datalen as usize);
586 TagData::Binary(slice.to_vec())
587 }
588 FMOD_TAGDATATYPE_INT => match value.datalen {
589 1 => TagData::Integer(*value.data.cast::<i8>() as i64),
590 2 => TagData::Integer(*value.data.cast::<i16>() as i64),
591 4 => TagData::Integer(*value.data.cast::<i32>() as i64),
592 8 => TagData::Integer(*value.data.cast::<i64>()),
593 _ => panic!("unrecognized integer data len"),
594 },
595 FMOD_TAGDATATYPE_FLOAT => match value.datalen {
596 4 => TagData::Float(*value.data.cast::<f32>() as f64),
597 8 => TagData::Float(*value.data.cast::<f64>()),
598 _ => panic!("unrecognized float data len"),
599 },
600 FMOD_TAGDATATYPE_STRING => {
601 let ascii =
602 std::slice::from_raw_parts(value.data.cast(), value.datalen as usize);
603 let string = String::from_utf8_lossy(ascii).into_owned();
604 TagData::String(string)
605 }
606 FMOD_TAGDATATYPE_STRING_UTF8 => {
607 let utf8 =
608 std::slice::from_raw_parts(value.data.cast(), value.datalen as usize);
609 let string = String::from_utf8_lossy(utf8).into_owned();
610 TagData::Utf8String(string)
611 }
612 FMOD_TAGDATATYPE_STRING_UTF16 => {
616 let slice =
617 std::slice::from_raw_parts(value.data.cast(), value.datalen as usize);
618 let string = string_from_utf16_le(slice);
619 TagData::Utf16String(string)
620 }
621 FMOD_TAGDATATYPE_STRING_UTF16BE => {
622 let slice =
623 std::slice::from_raw_parts(value.data.cast(), value.datalen as usize);
624 let string = string_from_utf16_be(slice);
625 TagData::Utf16StringBE(string)
626 }
627 _ => panic!("unrecognized tag data type"), }
629 };
630 Tag {
631 kind,
632 name,
633 data,
634 updated,
635 }
636 }
637}
638
639#[derive(Debug, Default)]
644pub struct AdvancedSettings {
645 pub max_mpeg_codecs: c_int,
647 pub max_adpcm_codecs: c_int,
649 pub max_xma_codecs: c_int,
651 pub max_vorbis_codecs: c_int,
653 pub max_at9_codecs: c_int,
655 pub max_fadpcm_codecs: c_int,
657 pub max_opus_codecs: c_int,
659 #[cfg(fmod_lt_2_3)]
660 pub max_pcm_codecs: c_int,
661
662 pub asio_channel_list: Option<Vec<Utf8CString>>,
669 pub asio_speaker_list: Option<Vec<Speaker>>, pub vol0_virtual_vol: c_float,
679 pub default_decode_buffer_size: c_uint,
681 pub profile_port: c_ushort,
684 pub geometry_max_fade_time: c_uint,
687 pub distance_filter_center_freq: c_float,
690 pub reverb_3d_instance: c_int,
692 pub dsp_buffer_pool_size: c_int,
697 pub resampler_method: Resampler,
699 pub random_seed: c_uint,
701 pub max_convolution_threads: c_int,
708 pub max_spatial_objects: c_int,
717}
718
719impl From<&AdvancedSettings> for FMOD_ADVANCEDSETTINGS {
720 fn from(value: &AdvancedSettings) -> Self {
721 let speaker_count = value.asio_speaker_list.as_ref().map_or(0, Vec::len);
722 let speaker_ptr: *const Speaker = value
723 .asio_speaker_list
724 .as_ref()
725 .map_or(std::ptr::null_mut(), Vec::as_ptr);
726
727 Self {
728 cbSize: size_of::<FMOD_ADVANCEDSETTINGS>() as c_int,
729 maxMPEGCodecs: value.max_mpeg_codecs,
730 maxADPCMCodecs: value.max_adpcm_codecs,
731 maxXMACodecs: value.max_xma_codecs,
732 maxVorbisCodecs: value.max_vorbis_codecs,
733 maxAT9Codecs: value.max_at9_codecs,
734 maxFADPCMCodecs: value.max_fadpcm_codecs,
735 maxOpusCodecs: value.max_opus_codecs,
736 #[cfg(fmod_lt_2_3)]
737 maxPCMCodecs: value.max_pcm_codecs,
738 ASIONumChannels: speaker_count as i32,
739 ASIOChannelList: std::ptr::null_mut(),
740 ASIOSpeakerList: speaker_ptr.cast_mut().cast(),
743 vol0virtualvol: value.vol0_virtual_vol,
744 defaultDecodeBufferSize: value.default_decode_buffer_size,
745 profilePort: value.profile_port,
746 geometryMaxFadeTime: value.geometry_max_fade_time,
747 distanceFilterCenterFreq: value.distance_filter_center_freq,
748 reverb3Dinstance: value.reverb_3d_instance,
749 DSPBufferPoolSize: value.dsp_buffer_pool_size,
750 resamplerMethod: value.resampler_method.into(),
751 randomSeed: value.random_seed,
752 maxConvolutionThreads: value.max_convolution_threads,
753 maxSpatialObjects: value.max_spatial_objects,
754 }
755 }
756}
757
758impl AdvancedSettings {
759 pub unsafe fn from_ffi(value: FMOD_ADVANCEDSETTINGS) -> Self {
780 let channels = if value.ASIONumChannels > 0 {
781 let slice = unsafe {
782 std::slice::from_raw_parts(value.ASIOChannelList, value.ASIONumChannels as _)
783 };
784 let vec = slice
785 .iter()
786 .map(|&ptr| unsafe { Utf8CStr::from_ptr_unchecked(ptr) }.to_cstring())
787 .collect();
788 Some(vec)
789 } else {
790 None
791 };
792
793 Self {
794 max_mpeg_codecs: value.maxMPEGCodecs,
795 max_adpcm_codecs: value.maxADPCMCodecs,
796 max_xma_codecs: value.maxXMACodecs,
797 max_vorbis_codecs: value.maxVorbisCodecs,
798 max_at9_codecs: value.maxAT9Codecs,
799 max_fadpcm_codecs: value.maxFADPCMCodecs,
800 max_opus_codecs: value.maxOpusCodecs,
801 #[cfg(fmod_lt_2_3)]
802 max_pcm_codecs: value.maxPCMCodecs,
803
804 asio_channel_list: channels,
805 asio_speaker_list: None,
806
807 vol0_virtual_vol: value.vol0virtualvol,
808 default_decode_buffer_size: value.defaultDecodeBufferSize,
809 profile_port: value.profilePort,
810 geometry_max_fade_time: value.geometryMaxFadeTime,
811 distance_filter_center_freq: value.distanceFilterCenterFreq,
812 reverb_3d_instance: value.reverb3Dinstance,
813 dsp_buffer_pool_size: value.DSPBufferPoolSize,
814 resampler_method: value.resamplerMethod.try_into().unwrap(),
815 random_seed: value.randomSeed,
816 max_convolution_threads: value.maxConvolutionThreads,
817 max_spatial_objects: value.maxSpatialObjects,
818 }
819 }
820}