1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// Copyright (c) 2024 Melody Madeline Lyons
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use fmod_sys::*;
use lanyard::Utf8CStr;
use std::ffi::c_int;
use crate::{
Channel, ChannelGroup, Dsp, DspType, Reverb3D, Sound, SoundBuilder, SoundGroup, System,
};
#[cfg(fmod_gte_2_3_9)]
use crate::{DspConnection, DspConnectionType};
use crate::{FmodResultExt, Result};
#[cfg(doc)]
use crate::Mode;
impl System {
/// Loads a sound into memory, opens it for streaming or sets it up for callback based sounds.
///
/// [`Mode::CREATE_SAMPLE`] will try to load and decompress the whole sound into memory, use [`Mode::CREATE_STREAM`] to open it as a stream and have it play back in realtime from disk or another medium.
/// [`Mode::CREATE_COMPRESSED_SAMPLE`] can also be used for certain formats to play the sound directly in its compressed format from the mixer.
/// - To open a file or URL as a stream, so that it decompresses / reads at runtime, instead of loading / decompressing into memory all at the time of this call, use the [`Mode::CREATE_STREAM`] flag.
/// - To open a file or URL as a compressed sound effect that is not streamed and is not decompressed into memory at load time, use [`Mode::CREATE_COMPRESSED_SAMPLE`].
/// This is supported with MPEG (mp2/mp3), ADPCM/FADPCM, XMA, AT9 and FSB Vorbis files only. This is useful for those who want realtime compressed soundeffects, but not the overhead of disk access.
/// - To open a sound as 2D, so that it is not affected by 3D processing, use the [`Mode::D2`] flag. 3D sound commands will be ignored on these types of sounds.
/// - To open a sound as 3D, so that it is treated as a 3D sound, use the [`Mode::D3`] flag.
///
/// Note that [`Mode::OPEN_RAW`], [`Mode::OPEN_MEMORY`], [`Mode::OPEN_MEMORY_POINT`] and [`Mode::OPEN_USER`] will not work here without the exinfo structure present, as more information is needed.
///
/// Use [`Mode::NONBLOCKING`] to have the sound open or load in the background.
/// You can use `Sound::get_open_state` to determine if it has finished loading / opening or not. While it is loading (not ready), sound functions are not accessible for that sound.
/// Do not free memory provided with [`Mode::OPEN_MEMORY`] if the sound is not in a ready state, as it will most likely lead to a crash.
///
/// To account for slow media that might cause buffer underrun (skipping / stuttering / repeating blocks of audio) with sounds created with [`FMOD_CREATESTREAM`],
/// use `System::setStreamBufferSize` to increase read ahead.
///
/// As using [`Mode::OPEN_USER`] causes FMOD to ignore whatever is passed as the first argument `name_or_data`, recommended practice is to pass None.
///
/// Specifying [`Mode::OPEN_MEMORY_POINT`] will POINT to your memory rather allocating its own sound buffers and duplicating it internally,
/// this means you cannot free the memory while FMOD is using it, until after `Sound::release` is called.
///
/// With [`Mode::OPEN_MEMORY_POINT`], only PCM formats and compressed formats using [`Mode::CREATE_COMPRESSED_SAMPLE`] are supported.
pub fn create_sound(&self, builder: &SoundBuilder<'_>) -> Result<Sound> {
let mut sound = std::ptr::null_mut();
let mut ex_info = builder.raw_ex_info();
let ex_info_ptr = if builder.ex_info_is_empty() {
std::ptr::null_mut()
} else {
&raw mut ex_info
};
unsafe {
FMOD_System_CreateSound(
self.inner.as_ptr(),
builder.name_or_data,
builder.mode,
ex_info_ptr,
&raw mut sound,
)
.to_result()?;
Ok(Sound::from_ffi(sound))
}
}
/// Opens a sound for streaming.
///
/// This is a convenience function for [`System::create_sound`] with the [`Mode::CREATE_STREAM`] flag added.
///
/// A stream only has one decode buffer and file handle, and therefore can only be played once.
/// It cannot play multiple times at once because it cannot share a stream buffer if the stream is playing at different positions.
/// Open multiple streams to have them play concurrently.
pub fn create_stream(&self, builder: &SoundBuilder<'_>) -> Result<Sound> {
let mut sound = std::ptr::null_mut();
let mut ex_info = builder.raw_ex_info();
let ex_info_ptr = if builder.ex_info_is_empty() {
std::ptr::null_mut()
} else {
&raw mut ex_info
};
unsafe {
FMOD_System_CreateStream(
self.inner.as_ptr(),
builder.name_or_data,
builder.mode,
ex_info_ptr,
&raw mut sound,
)
.to_result()?;
Ok(Sound::from_ffi(sound))
}
}
/// WARNING: At the moment this function has no guardrails and WILL cause undefined behaviour if used incorrectly.
/// The [`FMOD_DSP_DESCRIPTION`] API is *really* complicated and I felt it was better to provide an (unsafe) way to use it until I can figure out a better way to handle it.
///
/// Create a DSP object given a plugin description structure.
///
/// A DSP object is a module that can be inserted into the mixing graph to allow sound filtering or sound generation.
/// See the DSP architecture guide for more information.
///
/// DSPs must be attached to the DSP graph before they become active, either via `ChannelControl::addDSP` or `DSP::addInput`.
///
/// # Safety
///
/// The [`FMOD_DSP_DESCRIPTION`] pointer must point to a valid [`FMOD_DSP_DESCRIPTION`]. Said plugin description must alsoconform to FMOD's plugin API!
pub unsafe fn create_dsp(&self, description: *const FMOD_DSP_DESCRIPTION) -> Result<Dsp> {
let mut dsp = std::ptr::null_mut();
unsafe {
FMOD_System_CreateDSP(self.inner.as_ptr(), description, &raw mut dsp).to_result()?;
Ok(Dsp::from_ffi(dsp))
}
}
/// Create a DSP object given a built in type index.
///
/// A DSP object is a module that can be inserted into the mixing graph to allow sound filtering or sound generation. See the DSP architecture guide for more information.
///
/// DSPs must be attached to the DSP graph before they become active, either via `ChannelControl::addDSP` or `DSP::addInput`.
///
#[cfg_attr(
fmod_2_2,
doc(
"Using [`DspType::VstPlugin`] or [`DspType::WinampPlugin`] will return the first loaded plugin of this type."
)
)]
/// To access other plugins of these types, use `System::createDSPByPlugin` instead.
pub fn create_dsp_by_type(&self, kind: DspType) -> Result<Dsp> {
let mut dsp = std::ptr::null_mut();
unsafe {
FMOD_System_CreateDSPByType(self.inner.as_ptr(), kind.into(), &raw mut dsp)
.to_result()?;
Ok(Dsp::from_ffi(dsp))
}
}
/// Create a [`DspConnection`] object.
///
/// Creating a DSP connection with this function allows you to configure its
/// initial state before using it to connect two DSPs via [`Dsp::add_input`].
#[cfg(fmod_gte_2_3_9)]
pub fn create_dsp_connection(&self, kind: DspConnectionType) -> Result<DspConnection> {
let mut connection = std::ptr::null_mut();
unsafe {
FMOD_System_CreateDSPConnection(self.inner.as_ptr(), kind.into(), &raw mut connection)
.to_result()?;
Ok(DspConnection::from_ffi(connection))
}
}
/// Create a [`ChannelGroup`] object.
///
/// [`ChannelGroup`]s can be used to assign / group [`Channel`]s, for things such as volume scaling.
/// [`ChannelGroup`]s are also used for sub-mixing.
/// Any [`Channel`]s that are assigned to a [`ChannelGroup`] get submixed into that [`ChannelGroup`]'s 'tail' [`Dsp`]. See `FMOD_CHANNELCONTROL_DSP_TAIL`.
///
/// If a [`ChannelGroup`] has an effect added to it, the effect is processed post-mix from the [`Channel`]s and [`ChannelGroup`]s below it in the mix hierarchy.
/// See the DSP architecture guide for more information.
///
/// All [`ChannelGroup`]s will initially output directly to the master [`ChannelGroup`] (See `System::getMasterChannelGroup`).
/// [`ChannelGroup`]s can be re-parented this with `ChannelGroup::addGroup`.
pub fn create_channel_group(&self, name: &Utf8CStr) -> Result<ChannelGroup> {
let mut channel_group = std::ptr::null_mut();
unsafe {
FMOD_System_CreateChannelGroup(
self.inner.as_ptr(),
name.as_ptr(),
&raw mut channel_group,
)
.to_result()?;
Ok(ChannelGroup::from_ffi(channel_group))
}
}
/// Creates a [`SoundGroup`] object.
///
/// A [`SoundGroup`] is a way to address multiple [`Sound`]s at once with group level commands, such as:
///
/// - Attributes of Sounds that are playing or about to be played, such as volume. See (`SoundGroup::setVolume`).
/// - Control of playback, such as stopping [`Sound`]s. See (`SoundGroup::stop`).
/// - Playback behavior such as 'max audible', to limit playback of certain types of [`Sound`]s. See (`SoundGroup::setMaxAudible`).
///
/// Once a [`SoundGroup`] is created, `Sound::setSoundGroup` is used to put a [`Sound`] in a [`SoundGroup`].
pub fn create_sound_group(&self, name: &Utf8CStr) -> Result<SoundGroup> {
let mut sound_group = std::ptr::null_mut();
unsafe {
FMOD_System_CreateSoundGroup(self.inner.as_ptr(), name.as_ptr(), &raw mut sound_group)
.to_result()?;
Ok(SoundGroup::from_ffi(sound_group))
}
}
/// Creates a 'virtual reverb' object.
/// This object reacts to 3D location and morphs the reverb environment based on how close it is to the reverb object's center.
///
/// Multiple reverb objects can be created to achieve a multi-reverb environment.
/// 1 reverb object is used for all 3D reverb objects (slot 0 by default).
///
/// The 3D reverb object is a sphere having 3D attributes (position, minimum distance, maximum distance) and reverb properties.
///
/// The properties and 3D attributes of all reverb objects collectively determine, along with the listener's position,
/// the settings of and input gains into a single 3D reverb [`Dsp`].
///
/// When the listener is within the sphere of effect of one or more 3D reverbs,
/// the listener's 3D reverb properties are a weighted combination of such 3D reverbs.
///
/// When the listener is outside all of the reverbs, no reverb is applied.
///
/// `System::setReverbProperties` can be used to create an alternative reverb that can be used for 2D and background global reverb.
///
/// To avoid this reverb interfering with the reverb slot used by the 3D reverb, 2D reverb should use a different slot id with `System::setReverbProperties`,
/// otherwise `FMOD_ADVANCEDSETTINGS::reverb3Dinstance` can also be used to place 3D reverb on a different reverb slot.
///
/// Use `ChannelControl::setReverbProperties` to turn off reverb for 2D sounds (ie set wet = 0).
///
/// Creating multiple reverb objects does not impact performance.
/// These are 'virtual reverbs'.
/// There will still be only one reverb [`Dsp`] running that just morphs between the different virtual reverbs.
///
/// Note about reverb [`Dsp`] unit allocation.
/// To remove the [`Dsp`] unit and the associated CPU cost, first make sure all 3D reverb objects are released.
/// Then call `System::setReverbProperties` with the 3D reverb's slot ID (default is 0) with a property point of 0 or NULL, to signal that the reverb instance should be deleted.
///
/// If a 3D reverb is still present, and `System::setReverbProperties` function is called to free the reverb,
/// the 3D reverb system will immediately recreate it upon the next `System::update` call.
///
/// Note that the 3D reverb system will not affect Studio events unless it is explicitly enabled by calling `Studio::EventInstance::setReverbLevel` on each event instance.
pub fn create_reverb_3d(&self) -> Result<Reverb3D> {
let mut reverb = std::ptr::null_mut();
unsafe {
FMOD_System_CreateReverb3D(self.inner.as_ptr(), &raw mut reverb).to_result()?;
Ok(Reverb3D::from_ffi(reverb))
}
}
/// Plays a Sound on a Channel.
///
/// When a sound is played, it will use the sound's default frequency and priority. See `Sound::setDefaults`.
///
/// A sound defined as [`Mode::D3`] will by default play at the 3D position of the listener.
/// To set the 3D position of the Channel before the sound is audible, start the Channel paused by setting the paused parameter to true, and call `ChannelControl::set3DAttributes`.
///
/// Specifying a channelgroup as part of playSound is more efficient than using `Channel::setChannelGroup` after playSound, and could avoid audible glitches if the playSound is not in a paused state.
///
/// Channels are reference counted to handle dead or stolen Channel handles.
/// See the white paper on Channel handles for more information.
///
/// Playing more Sounds than physical Channels allow is handled with virtual voices.
/// See the white paper on Virtual Voices for more information.
pub fn play_sound(
&self,
sound: Sound,
channel_group: Option<ChannelGroup>,
paused: bool,
) -> Result<Channel> {
let mut channel = std::ptr::null_mut();
unsafe {
FMOD_System_PlaySound(
self.inner.as_ptr(),
sound.into(),
channel_group.map_or(std::ptr::null_mut(), ChannelGroup::into),
paused.into(),
&raw mut channel,
)
.to_result()?;
Ok(Channel::from_ffi(channel))
}
}
/// Plays a [`Dsp`] along with any of its inputs on a [`Channel`].
///
/// Specifying a `channel_group` as part of playDSP is more efficient than using `Channel::setChannelGroup` after playDSP,
/// and could avoid audible glitches if the playDSP is not in a paused state.
///
/// [`Channel`]s are reference counted to handle dead or stolen [`Channel`] handles. See the white paper on [`Channel`] handles for more information.
///
/// Playing more Sounds or [`Dsp`]s than physical [`Channel`]s allowed is handled with virtual voices.
/// See the white paper on Virtual Voices for more information.
pub fn play_dsp(
&self,
dsp: Dsp,
channel_group: Option<ChannelGroup>,
paused: bool,
) -> Result<Channel> {
let mut channel = std::ptr::null_mut();
unsafe {
FMOD_System_PlayDSP(
self.inner.as_ptr(),
dsp.into(),
channel_group.map_or(std::ptr::null_mut(), ChannelGroup::into),
paused.into(),
&raw mut channel,
)
.to_result()?;
Ok(Channel::from_ffi(channel))
}
}
/// Retrieves a handle to a [`Channel`] by ID.
///
/// This function is mainly for getting handles to existing (playing) [`Channel`]s and setting their attributes.
/// The only way to 'create' an instance of a [`Channel`] for playback is to use [`System::play_sound`] or [`System::play_dsp`].
pub fn get_channel(&self, channel_id: c_int) -> Result<Channel> {
let mut channel = std::ptr::null_mut();
unsafe {
FMOD_System_GetChannel(self.inner.as_ptr(), channel_id, &raw mut channel)
.to_result()?;
Ok(Channel::from_ffi(channel))
}
}
/// Retrieve the description structure for a built in DSP plugin.
///
/// `FMOD_DSP_TYPE_MIXER` not supported.
pub fn get_dsp_info_by_type(&self, dsp_type: DspType) -> Result<*const FMOD_DSP_DESCRIPTION> {
let mut description = std::ptr::null();
unsafe {
FMOD_System_GetDSPInfoByType(
self.inner.as_ptr(),
dsp_type.into(),
&raw mut description,
)
.to_result()?;
}
Ok(description)
}
/// Retrieves the master [`ChannelGroup`] that all sounds ultimately route to.
///
/// This is the default [`ChannelGroup`] that [`Channel`]s play on,
/// unless a different [`ChannelGroup`] is specified with [`System::play_sound`], [`System::play_dsp`] or `Channel::setChannelGroup`.
/// A master [`ChannelGroup`] can be used to do things like set the 'master volume' for all playing [`Channel`]s. See `ChannelControl::setVolume`.
pub fn get_master_channel_group(&self) -> Result<ChannelGroup> {
let mut channel_group = std::ptr::null_mut();
unsafe {
FMOD_System_GetMasterChannelGroup(self.inner.as_ptr(), &raw mut channel_group)
.to_result()?;
Ok(ChannelGroup::from_ffi(channel_group))
}
}
/// Retrieves the default [`SoundGroup`], where all sounds are placed when they are created.
///
/// If [`SoundGroup`] is released, the [`Sound`]s will be put back into this [`SoundGroup`].
pub fn get_master_sound_group(&self) -> Result<SoundGroup> {
let mut sound_group = std::ptr::null_mut();
unsafe {
FMOD_System_GetMasterSoundGroup(self.inner.as_ptr(), &raw mut sound_group)
.to_result()?;
Ok(SoundGroup::from_ffi(sound_group))
}
}
}