1#![allow(missing_docs, deprecated)]
7
8use crate::{Error, FmodResultExt, Result};
9use std::ffi::{c_int, c_void};
10
11use fmod_sys::*;
12use lanyard::Utf8CStr;
13
14#[cfg(feature = "studio")]
15use crate::studio;
16use crate::{
17 Channel, ChannelControl, ChannelGroup, Dsp, DspConnection, Geometry, OutputType, Reverb3D,
18 Sound, SoundGroup, System, panic_wrapper,
19};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct ErrorCallbackInfo<'a> {
24 pub error: Error,
26 pub instance: Instance,
28 pub function_name: &'a Utf8CStr,
30 pub function_params: &'a Utf8CStr,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum Instance {
37 None,
39 System(System),
41 Channel(Channel),
43 ChannelGroup(ChannelGroup),
44 ChannelControl(ChannelControl),
46 Sound(Sound),
47 SoundGroup(SoundGroup),
49 Dsp(Dsp),
51 DspConnection(DspConnection),
53 Geometry(Geometry),
55 Reverb3D(Reverb3D),
57 #[deprecated]
59 StudioParameterInstance,
60
61 #[cfg(feature = "studio")]
63 StudioSystem(studio::System),
64 #[cfg(feature = "studio")]
66 StudioEventDescription(studio::EventDescription),
67 #[cfg(feature = "studio")]
68 StudioEventInstance(studio::EventInstance),
70 #[cfg(feature = "studio")]
71 StudioBus(studio::Bus),
73 #[cfg(feature = "studio")]
74 StudioVCA(studio::Vca),
76 #[cfg(feature = "studio")]
77 StudioBank(studio::Bank),
79 #[cfg(feature = "studio")]
80 StudioCommandReplay(studio::CommandReplay),
82
83 #[cfg(not(feature = "studio"))]
86 StudioSystem(*mut c_void),
89 #[cfg(not(feature = "studio"))]
90 StudioEventDescription(*mut c_void),
93 #[cfg(not(feature = "studio"))]
94 StudioEventInstance(*mut c_void),
97 #[cfg(not(feature = "studio"))]
98 StudioBus(*mut c_void),
101 #[cfg(not(feature = "studio"))]
102 StudioVCA(*mut c_void),
105 #[cfg(not(feature = "studio"))]
106 StudioBank(*mut c_void),
109 #[cfg(not(feature = "studio"))]
112 StudioCommandReplay(*mut c_void),
113}
114
115impl Instance {
116 fn from_raw(kind: FMOD_ERRORCALLBACK_INSTANCETYPE, pointer: *mut c_void) -> Self {
117 match kind {
118 FMOD_ERRORCALLBACK_INSTANCETYPE_NONE => Instance::None,
119 FMOD_ERRORCALLBACK_INSTANCETYPE_SYSTEM => {
120 Instance::System(unsafe { System::from_ffi(pointer.cast()) })
121 }
122 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNEL => {
123 Instance::Channel(unsafe { Channel::from_ffi(pointer.cast()) })
124 }
125 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELGROUP => {
126 Instance::ChannelGroup(unsafe { ChannelGroup::from_ffi(pointer.cast()) })
127 }
128 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELCONTROL => {
129 Instance::ChannelControl(unsafe { ChannelControl::from_ffi(pointer.cast()) })
130 }
131 FMOD_ERRORCALLBACK_INSTANCETYPE_SOUND => {
132 Instance::Sound(unsafe { Sound::from_ffi(pointer.cast()) })
133 }
134 FMOD_ERRORCALLBACK_INSTANCETYPE_SOUNDGROUP => {
135 Instance::SoundGroup(unsafe { SoundGroup::from_ffi(pointer.cast()) })
136 }
137 FMOD_ERRORCALLBACK_INSTANCETYPE_DSP => {
138 Instance::Dsp(unsafe { Dsp::from_ffi(pointer.cast()) })
139 }
140 FMOD_ERRORCALLBACK_INSTANCETYPE_DSPCONNECTION => {
141 Instance::DspConnection(unsafe { DspConnection::from_ffi(pointer.cast()) })
142 }
143 FMOD_ERRORCALLBACK_INSTANCETYPE_GEOMETRY => {
144 Instance::Geometry(unsafe { Geometry::from_ffi(pointer.cast()) })
145 }
146 FMOD_ERRORCALLBACK_INSTANCETYPE_REVERB3D => {
147 Instance::Reverb3D(unsafe { Reverb3D::from_ffi(pointer.cast()) })
148 }
149 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_PARAMETERINSTANCE => {
150 Instance::StudioParameterInstance
151 }
152 #[cfg(not(feature = "studio"))]
153 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_SYSTEM => Instance::StudioSystem(pointer),
154 #[cfg(not(feature = "studio"))]
155 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTDESCRIPTION => {
156 Instance::StudioEventDescription(pointer)
157 }
158 #[cfg(not(feature = "studio"))]
159 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTINSTANCE => {
160 Instance::StudioEventInstance(pointer)
161 }
162 #[cfg(not(feature = "studio"))]
163 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BUS => Instance::StudioBus(pointer),
164 #[cfg(not(feature = "studio"))]
165 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_VCA => Instance::StudioVCA(pointer),
166 #[cfg(not(feature = "studio"))]
167 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BANK => Instance::StudioBank(pointer),
168 #[cfg(not(feature = "studio"))]
169 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_COMMANDREPLAY => {
170 Instance::StudioCommandReplay(pointer)
171 }
172 #[cfg(feature = "studio")]
173 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_SYSTEM => {
174 Instance::StudioSystem(unsafe { studio::System::from_ffi(pointer.cast()) })
175 }
176 #[cfg(feature = "studio")]
177 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTDESCRIPTION => {
178 Instance::StudioEventDescription(unsafe {
179 studio::EventDescription::from_ffi(pointer.cast())
180 })
181 }
182 #[cfg(feature = "studio")]
183 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTINSTANCE => {
184 Instance::StudioEventInstance(unsafe {
185 studio::EventInstance::from_ffi(pointer.cast())
186 })
187 }
188 #[cfg(feature = "studio")]
189 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BUS => {
190 Instance::StudioBus(unsafe { studio::Bus::from_ffi(pointer.cast()) })
191 }
192 #[cfg(feature = "studio")]
193 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_VCA => {
194 Instance::StudioVCA(unsafe { studio::Vca::from_ffi(pointer.cast()) })
195 }
196 #[cfg(feature = "studio")]
197 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BANK => {
198 Instance::StudioBank(unsafe { studio::Bank::from_ffi(pointer.cast()) })
199 }
200 #[cfg(feature = "studio")]
201 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_COMMANDREPLAY => {
202 Instance::StudioCommandReplay(unsafe {
203 studio::CommandReplay::from_ffi(pointer.cast())
204 })
205 }
206 _ => {
207 eprintln!("warning: unknown instance type {kind}");
208 Instance::None
209 }
210 }
211 }
212}
213
214impl ErrorCallbackInfo<'_> {
215 pub unsafe fn from_ffi(value: FMOD_ERRORCALLBACK_INFO) -> Self {
221 Self {
222 error: value.result.into(),
223 instance: Instance::from_raw(value.instancetype, value.instance),
224 function_name: unsafe { Utf8CStr::from_ptr_unchecked(value.functionname) },
225 function_params: unsafe { Utf8CStr::from_ptr_unchecked(value.functionparams) },
226 }
227 }
228}
229
230bitflags::bitflags! {
231 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
232 pub struct SystemCallbackMask: FMOD_SYSTEM_CALLBACK_TYPE {
233 const DEVICELISTCHANGED = FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED;
234 const DEVICELOST = FMOD_SYSTEM_CALLBACK_DEVICELOST;
235 const MEMORYALLOCATIONFAILED= FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED;
236 const THREADCREATED = FMOD_SYSTEM_CALLBACK_THREADCREATED;
237 const BADDSPCONNECTION = FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION;
238 const PREMIX = FMOD_SYSTEM_CALLBACK_PREMIX;
239 const POSTMIX = FMOD_SYSTEM_CALLBACK_POSTMIX;
240 const ERROR = FMOD_SYSTEM_CALLBACK_ERROR;
241 #[cfg(fmod_eq_2_2)]
242 const MIDMIX = FMOD_SYSTEM_CALLBACK_MIDMIX;
243 const THREADDESTROYED = FMOD_SYSTEM_CALLBACK_THREADDESTROYED;
244 const PREUPDATE = FMOD_SYSTEM_CALLBACK_PREUPDATE;
245 const POSTUPDATE = FMOD_SYSTEM_CALLBACK_POSTUPDATE;
246 const RECORDLISTCHANGED = FMOD_SYSTEM_CALLBACK_RECORDLISTCHANGED;
247 const BUFFEREDNOMIX = FMOD_SYSTEM_CALLBACK_BUFFEREDNOMIX;
248 const DEVICEREINITIALIZE = FMOD_SYSTEM_CALLBACK_DEVICEREINITIALIZE;
249 const OUTPUTUNDERRUN = FMOD_SYSTEM_CALLBACK_OUTPUTUNDERRUN;
250 const RECORDPOSITIONCHANGED = FMOD_SYSTEM_CALLBACK_RECORDPOSITIONCHANGED ;
251 const ALL = FMOD_SYSTEM_CALLBACK_ALL;
252 }
253}
254
255impl From<SystemCallbackMask> for FMOD_SYSTEM_CALLBACK_TYPE {
256 fn from(mask: SystemCallbackMask) -> Self {
257 mask.bits()
258 }
259}
260
261impl From<FMOD_SYSTEM_CALLBACK_TYPE> for SystemCallbackMask {
262 fn from(mask: FMOD_SYSTEM_CALLBACK_TYPE) -> Self {
263 Self::from_bits_truncate(mask)
264 }
265}
266
267#[allow(unused_variables)]
271pub trait SystemCallback {
272 fn device_list_changed(system: System, userdata: *mut c_void) -> Result<()> {
277 Ok(())
278 }
279
280 #[deprecated]
282 fn device_lost(system: System, userdata: *mut c_void) -> Result<()> {
283 Ok(())
284 }
285
286 fn memory_allocation_failed(
288 system: System,
289 file: &Utf8CStr,
290 size: c_int,
291 userdata: *mut c_void,
292 ) -> Result<()> {
293 Ok(())
294 }
295
296 fn thread_created(
298 system: System,
299 handle: *mut c_void,
300 thread_name: &Utf8CStr,
301 userdata: *mut c_void,
302 ) -> Result<()> {
303 Ok(())
304 }
305
306 #[deprecated]
308 fn bad_dsp_connection(system: System, userdata: *mut c_void) -> Result<()> {
309 Ok(())
310 }
311
312 fn premix(system: System, userdata: *mut c_void) -> Result<()> {
314 Ok(())
315 }
316
317 fn postmix(system: System, userdata: *mut c_void) -> Result<()> {
319 Ok(())
320 }
321
322 fn error(
324 system: System,
325 error_info: ErrorCallbackInfo<'_>,
326 userdata: *mut c_void,
327 ) -> Result<()> {
328 Ok(())
329 }
330
331 #[cfg(fmod_eq_2_2)]
332 fn mid_mix(system: System, userdata: *mut c_void) -> Result<()> {
333 Ok(())
334 }
335
336 fn thread_destroyed(
338 system: System,
339 handle: *mut c_void,
340 thread_name: &Utf8CStr,
341 userdata: *mut c_void,
342 ) -> Result<()> {
343 Ok(())
344 }
345
346 fn pre_update(system: System, userdata: *mut c_void) -> Result<()> {
350 Ok(())
351 }
352
353 fn post_update(system: System, userdata: *mut c_void) -> Result<()> {
357 Ok(())
358 }
359
360 fn record_list_changed(system: System, userdata: *mut c_void) -> Result<()> {
364 Ok(())
365 }
366
367 fn buffered_no_mix(system: System, userdata: *mut c_void) -> Result<()> {
370 Ok(())
371 }
372
373 fn device_reinitialize(
377 system: System,
378 output_type: OutputType,
379 driver_index: c_int,
380 userdata: *mut c_void,
381 ) -> Result<()> {
382 Ok(())
383 }
384
385 fn output_underrun(system: System, userdata: *mut c_void) -> Result<()> {
387 Ok(())
388 }
389
390 fn record_position_changed(
392 system: System,
393 sound: Sound,
394 record_position: c_int,
395 userdata: *mut c_void,
396 ) -> Result<()> {
397 Ok(())
398 }
399}
400
401unsafe extern "C" fn callback_impl<C: SystemCallback>(
402 system: *mut FMOD_SYSTEM,
403 callback_type: FMOD_SYSTEM_CALLBACK_TYPE,
404 command_data_1: *mut c_void,
405 command_data_2: *mut c_void,
406 userdata: *mut c_void,
407) -> FMOD_RESULT {
408 #[allow(deprecated)]
409 panic_wrapper(|| {
410 let system = unsafe { System::from_ffi(system) };
411 let result = match callback_type {
412 FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED => C::device_list_changed(system, userdata),
413 FMOD_SYSTEM_CALLBACK_DEVICELOST => C::device_lost(system, userdata),
414 FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED => {
415 let file = unsafe { Utf8CStr::from_ptr_unchecked(command_data_1.cast()) };
416 C::memory_allocation_failed(system, file, command_data_2 as c_int, userdata)
417 }
418 FMOD_SYSTEM_CALLBACK_THREADCREATED => {
419 let thread_name = unsafe { Utf8CStr::from_ptr_unchecked(command_data_2.cast()) };
420 C::thread_created(system, command_data_1, thread_name, userdata)
421 }
422 FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION => C::bad_dsp_connection(system, userdata),
423 FMOD_SYSTEM_CALLBACK_PREMIX => C::premix(system, userdata),
424 FMOD_SYSTEM_CALLBACK_POSTMIX => C::postmix(system, userdata),
425 FMOD_SYSTEM_CALLBACK_ERROR => {
426 let error_info = unsafe { ErrorCallbackInfo::from_ffi(*command_data_1.cast()) };
427 C::error(system, error_info, userdata)
428 }
429 #[cfg(fmod_eq_2_2)]
430 FMOD_SYSTEM_CALLBACK_MIDMIX => C::mid_mix(system, userdata),
431 FMOD_SYSTEM_CALLBACK_THREADDESTROYED => {
432 let thread_name = unsafe { Utf8CStr::from_ptr_unchecked(command_data_2.cast()) };
433 C::thread_destroyed(system, command_data_1, thread_name, userdata)
434 }
435 FMOD_SYSTEM_CALLBACK_PREUPDATE => C::pre_update(system, userdata),
436 FMOD_SYSTEM_CALLBACK_POSTUPDATE => C::post_update(system, userdata),
437 FMOD_SYSTEM_CALLBACK_RECORDLISTCHANGED => C::record_list_changed(system, userdata),
438 FMOD_SYSTEM_CALLBACK_BUFFEREDNOMIX => C::buffered_no_mix(system, userdata),
439 FMOD_SYSTEM_CALLBACK_DEVICEREINITIALIZE => {
440 let output_type = OutputType::try_from(command_data_1 as FMOD_OUTPUTTYPE)
441 .expect("invalid output type");
442 C::device_reinitialize(system, output_type, command_data_2 as c_int, userdata)
443 }
444 FMOD_SYSTEM_CALLBACK_OUTPUTUNDERRUN => C::output_underrun(system, userdata),
445 FMOD_SYSTEM_CALLBACK_RECORDPOSITIONCHANGED => {
446 let sound = unsafe { Sound::from_ffi(command_data_1.cast()) };
447 C::record_position_changed(system, sound, command_data_2 as c_int, userdata)
448 }
449 _ => {
450 eprintln!("warning: unknown callback type {callback_type}");
451 return FMOD_RESULT::FMOD_OK;
452 }
453 };
454 FMOD_RESULT::from_result(result)
455 })
456}
457
458impl System {
459 pub fn set_callback<C: SystemCallback>(&self, mask: SystemCallbackMask) -> Result<()> {
460 unsafe {
461 FMOD_System_SetCallback(self.inner.as_ptr(), Some(callback_impl::<C>), mask.into())
462 .to_result()
463 }
464 }
465}