1use std::ffi::{c_int, c_void};
8
9use fmod_sys::*;
10use lanyard::Utf8CStr;
11
12use crate::{
13 studio, Channel, ChannelControl, ChannelGroup, Dsp, DspConnection, Geometry, OutputType,
14 Reverb3D, Sound, SoundGroup, System,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct ErrorCallbackInfo<'a> {
19 pub error: Error,
20 pub instance: Instance,
21 pub function_name: &'a Utf8CStr,
22 pub function_params: &'a Utf8CStr,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum Instance {
27 None,
28 System(System),
29 Channel(Channel),
30 ChannelGroup(ChannelGroup),
31 ChannelControl(ChannelControl),
32 Sound(Sound),
33 SoundGroup(SoundGroup),
34 Dsp(Dsp),
35 DspConnection(DspConnection),
36 Geometry(Geometry),
37 Reverb3D(Reverb3D),
38 StudioSystem(studio::System),
39 StudioEventDescription(studio::EventDescription),
40 StudioEventInstance(studio::EventInstance),
41 StudioParameterInstance,
42 StudioBus(studio::Bus),
43 StudioVCA(studio::Vca),
44 StudioBank(studio::Bank),
45 StudioCommandReplay(studio::CommandReplay),
46}
47
48impl ErrorCallbackInfo<'_> {
49 pub unsafe fn from_ffi(value: FMOD_ERRORCALLBACK_INFO) -> Self {
55 Self {
56 error: value.result.into(),
57 instance: match value.instancetype {
58 FMOD_ERRORCALLBACK_INSTANCETYPE_NONE => Instance::None,
59 FMOD_ERRORCALLBACK_INSTANCETYPE_SYSTEM => {
60 Instance::System(System::from(value.instance.cast()))
61 }
62 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNEL => {
63 Instance::Channel(Channel::from(value.instance.cast()))
64 }
65 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELGROUP => {
66 Instance::ChannelGroup(ChannelGroup::from(value.instance.cast()))
67 }
68 FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELCONTROL => {
69 Instance::ChannelControl(ChannelControl::from(value.instance.cast()))
70 }
71 FMOD_ERRORCALLBACK_INSTANCETYPE_SOUND => {
72 Instance::Sound(Sound::from(value.instance.cast()))
73 }
74 FMOD_ERRORCALLBACK_INSTANCETYPE_SOUNDGROUP => {
75 Instance::SoundGroup(SoundGroup::from(value.instance.cast()))
76 }
77 FMOD_ERRORCALLBACK_INSTANCETYPE_DSP => {
78 Instance::Dsp(Dsp::from(value.instance.cast()))
79 }
80 FMOD_ERRORCALLBACK_INSTANCETYPE_DSPCONNECTION => {
81 Instance::DspConnection(DspConnection::from(value.instance.cast()))
82 }
83 FMOD_ERRORCALLBACK_INSTANCETYPE_GEOMETRY => {
84 Instance::Geometry(Geometry::from(value.instance.cast()))
85 }
86 FMOD_ERRORCALLBACK_INSTANCETYPE_REVERB3D => {
87 Instance::Reverb3D(Reverb3D::from(value.instance.cast()))
88 }
89 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_SYSTEM => {
90 Instance::StudioSystem(studio::System::from(value.instance.cast()))
91 }
92 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTDESCRIPTION => {
93 Instance::StudioEventDescription(studio::EventDescription::from(
94 value.instance.cast(),
95 ))
96 }
97 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTINSTANCE => {
98 Instance::StudioEventInstance(studio::EventInstance::from(
99 value.instance.cast(),
100 ))
101 }
102 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_PARAMETERINSTANCE => {
103 Instance::StudioParameterInstance
104 }
105 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BUS => {
106 Instance::StudioBus(studio::Bus::from(value.instance.cast()))
107 }
108 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_VCA => {
109 Instance::StudioVCA(studio::Vca::from(value.instance.cast()))
110 }
111 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BANK => {
112 Instance::StudioBank(studio::Bank::from(value.instance.cast()))
113 }
114 FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_COMMANDREPLAY => {
115 Instance::StudioCommandReplay(studio::CommandReplay::from(
116 value.instance.cast(),
117 ))
118 }
119 _ => {
120 eprintln!("warning: unknown instance type {}", value.instancetype);
121 Instance::None
122 }
123 },
124 function_name: unsafe { Utf8CStr::from_ptr_unchecked(value.functionname) },
125 function_params: unsafe { Utf8CStr::from_ptr_unchecked(value.functionparams) },
126 }
127 }
128}
129
130bitflags::bitflags! {
131 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
132 pub struct SystemCallbackMask: FMOD_SYSTEM_CALLBACK_TYPE {
133 const DEVICELISTCHANGED = FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED;
134 const DEVICELOST = FMOD_SYSTEM_CALLBACK_DEVICELOST;
135 const MEMORYALLOCATIONFAILED= FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED;
136 const THREADCREATED = FMOD_SYSTEM_CALLBACK_THREADCREATED;
137 const BADDSPCONNECTION = FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION;
138 const PREMIX = FMOD_SYSTEM_CALLBACK_PREMIX;
139 const POSTMIX = FMOD_SYSTEM_CALLBACK_POSTMIX;
140 const ERROR = FMOD_SYSTEM_CALLBACK_ERROR;
141 const MIDMIX = FMOD_SYSTEM_CALLBACK_MIDMIX;
142 const THREADDESTROYED = FMOD_SYSTEM_CALLBACK_THREADDESTROYED;
143 const PREUPDATE = FMOD_SYSTEM_CALLBACK_PREUPDATE;
144 const POSTUPDATE = FMOD_SYSTEM_CALLBACK_POSTUPDATE;
145 const RECORDLISTCHANGED = FMOD_SYSTEM_CALLBACK_RECORDLISTCHANGED;
146 const BUFFEREDNOMIX = FMOD_SYSTEM_CALLBACK_BUFFEREDNOMIX;
147 const DEVICEREINITIALIZE = FMOD_SYSTEM_CALLBACK_DEVICEREINITIALIZE;
148 const OUTPUTUNDERRUN = FMOD_SYSTEM_CALLBACK_OUTPUTUNDERRUN;
149 const RECORDPOSITIONCHANGED = FMOD_SYSTEM_CALLBACK_RECORDPOSITIONCHANGED ;
150 const ALL = FMOD_SYSTEM_CALLBACK_ALL;
151 }
152}
153
154impl From<SystemCallbackMask> for FMOD_SYSTEM_CALLBACK_TYPE {
155 fn from(mask: SystemCallbackMask) -> Self {
156 mask.bits()
157 }
158}
159
160impl From<FMOD_SYSTEM_CALLBACK_TYPE> for SystemCallbackMask {
161 fn from(mask: FMOD_SYSTEM_CALLBACK_TYPE) -> Self {
162 Self::from_bits_truncate(mask)
163 }
164}
165
166#[allow(unused_variables)]
167pub trait SystemCallback {
168 fn device_list_changed(system: System, userdata: *mut c_void) -> Result<()> {
169 Ok(())
170 }
171
172 fn device_lost(system: System, userdata: *mut c_void) -> Result<()> {
173 Ok(())
174 }
175
176 fn memory_allocation_failed(
177 system: System,
178 file: &Utf8CStr,
179 size: c_int,
180 userdata: *mut c_void,
181 ) -> Result<()> {
182 Ok(())
183 }
184
185 fn thread_created(
186 system: System,
187 handle: *mut c_void,
188 thread_name: &Utf8CStr,
189 userdata: *mut c_void,
190 ) -> Result<()> {
191 Ok(())
192 }
193
194 fn bad_dsp_connection(system: System, userdata: *mut c_void) -> Result<()> {
195 Ok(())
196 }
197
198 fn premix(system: System, userdata: *mut c_void) -> Result<()> {
199 Ok(())
200 }
201
202 fn postmix(system: System, userdata: *mut c_void) -> Result<()> {
203 Ok(())
204 }
205
206 fn error(
207 system: System,
208 error_info: ErrorCallbackInfo<'_>,
209 userdata: *mut c_void,
210 ) -> Result<()> {
211 Ok(())
212 }
213
214 fn mid_mix(system: System, userdata: *mut c_void) -> Result<()> {
215 Ok(())
216 }
217
218 fn thread_destroyed(
219 system: System,
220 handle: *mut c_void,
221 thread_name: &Utf8CStr,
222 userdata: *mut c_void,
223 ) -> Result<()> {
224 Ok(())
225 }
226
227 fn pre_update(system: System, userdata: *mut c_void) -> Result<()> {
228 Ok(())
229 }
230
231 fn post_update(system: System, userdata: *mut c_void) -> Result<()> {
232 Ok(())
233 }
234
235 fn record_list_changed(system: System, userdata: *mut c_void) -> Result<()> {
236 Ok(())
237 }
238
239 fn buffered_no_mix(system: System, userdata: *mut c_void) -> Result<()> {
240 Ok(())
241 }
242
243 fn device_reinitialize(
244 system: System,
245 output_type: OutputType,
246 driver_index: c_int,
247 userdata: *mut c_void,
248 ) -> Result<()> {
249 Ok(())
250 }
251
252 fn output_underrun(system: System, userdata: *mut c_void) -> Result<()> {
253 Ok(())
254 }
255
256 fn record_position_changed(
257 system: System,
258 sound: Sound,
259 record_position: c_int,
260 userdata: *mut c_void,
261 ) -> Result<()> {
262 Ok(())
263 }
264}
265
266unsafe extern "C" fn callback_impl<C: SystemCallback>(
267 system: *mut FMOD_SYSTEM,
268 callback_type: FMOD_SYSTEM_CALLBACK_TYPE,
269 command_data_1: *mut c_void,
270 command_data_2: *mut c_void,
271 userdata: *mut c_void,
272) -> FMOD_RESULT {
273 let system = System::from(system);
274 match callback_type {
275 FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED => C::device_list_changed(system, userdata).into(),
276 FMOD_SYSTEM_CALLBACK_DEVICELOST => C::device_lost(system, userdata).into(),
277 FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED => {
278 let file = unsafe { Utf8CStr::from_ptr_unchecked(command_data_1.cast()) };
279 C::memory_allocation_failed(system, file, command_data_2 as c_int, userdata).into()
280 }
281 FMOD_SYSTEM_CALLBACK_THREADCREATED => {
282 let thread_name = unsafe { Utf8CStr::from_ptr_unchecked(command_data_2.cast()) };
283 C::thread_created(system, command_data_1, thread_name, userdata).into()
284 }
285 FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION => C::bad_dsp_connection(system, userdata).into(),
286 FMOD_SYSTEM_CALLBACK_PREMIX => C::premix(system, userdata).into(),
287 FMOD_SYSTEM_CALLBACK_POSTMIX => C::postmix(system, userdata).into(),
288 FMOD_SYSTEM_CALLBACK_ERROR => {
289 let error_info = unsafe { ErrorCallbackInfo::from_ffi(*command_data_1.cast()) };
290 C::error(system, error_info, userdata).into()
291 }
292 FMOD_SYSTEM_CALLBACK_MIDMIX => C::mid_mix(system, userdata).into(),
293 FMOD_SYSTEM_CALLBACK_THREADDESTROYED => {
294 let thread_name = unsafe { Utf8CStr::from_ptr_unchecked(command_data_2.cast()) };
295 C::thread_destroyed(system, command_data_1, thread_name, userdata).into()
296 }
297 FMOD_SYSTEM_CALLBACK_PREUPDATE => C::pre_update(system, userdata).into(),
298 FMOD_SYSTEM_CALLBACK_POSTUPDATE => C::post_update(system, userdata).into(),
299 FMOD_SYSTEM_CALLBACK_RECORDLISTCHANGED => C::record_list_changed(system, userdata).into(),
300 FMOD_SYSTEM_CALLBACK_BUFFEREDNOMIX => C::buffered_no_mix(system, userdata).into(),
301 FMOD_SYSTEM_CALLBACK_DEVICEREINITIALIZE => {
302 let output_type = OutputType::try_from(command_data_1 as FMOD_OUTPUTTYPE)
303 .expect("invalid output type");
304 C::device_reinitialize(system, output_type, command_data_2 as c_int, userdata).into()
305 }
306 FMOD_SYSTEM_CALLBACK_OUTPUTUNDERRUN => C::output_underrun(system, userdata).into(),
307 FMOD_SYSTEM_CALLBACK_RECORDPOSITIONCHANGED => {
308 let sound = Sound::from(command_data_1.cast());
309 C::record_position_changed(system, sound, command_data_2 as c_int, userdata).into()
310 }
311 _ => {
312 eprintln!("warning: unknown callback type {callback_type}");
313 FMOD_RESULT::FMOD_OK
314 }
315 }
316}
317
318impl System {
319 pub fn set_callback<C: SystemCallback>(&self, mask: SystemCallbackMask) -> Result<()> {
320 unsafe { FMOD_System_SetCallback(self.inner, Some(callback_impl::<C>), mask.into()).into() }
321 }
322}