fmod/studio/system/builder.rs
1// Copyright (c) 2024 Melody Madeline Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std::ffi::{c_int, c_void};
8
9use fmod_sys::*;
10
11use crate::studio::{AdvancedSettings, InitFlags, System};
12use crate::{FmodResultExt, Result};
13
14/// A builder for creating and initializing a [`System`].
15///
16/// Handles setting values that can only be set before initialization for you.
17#[must_use]
18#[derive(Debug)]
19pub struct SystemBuilder {
20 system: *mut FMOD_STUDIO_SYSTEM,
21 core_builder: crate::SystemBuilder,
22 sync_update: bool,
23}
24
25#[cfg(not(feature = "thread-unsafe"))]
26unsafe impl Send for SystemBuilder {}
27#[cfg(not(feature = "thread-unsafe"))]
28unsafe impl Sync for SystemBuilder {}
29
30impl SystemBuilder {
31 /// Creates a new [`SystemBuilder`].
32 ///
33 /// # Safety
34 ///
35 /// Calling this function concurrently with any FMOD Studio API function (including this function) may cause undefined behavior.
36 /// External synchronization must be used if calls to [`SystemBuilder::new`] or [`System::release`] could overlap other FMOD Studio API calls.
37 /// All other FMOD Studio API functions are thread safe and may be called freely from any thread unless otherwise documented.
38 pub unsafe fn new() -> Result<Self> {
39 let mut system = std::ptr::null_mut();
40 unsafe { FMOD_Studio_System_Create(&raw mut system, FMOD_VERSION).to_result()? };
41
42 let mut core_system = std::ptr::null_mut();
43 unsafe { FMOD_Studio_System_GetCoreSystem(system, &raw mut core_system).to_result()? };
44
45 Ok(SystemBuilder {
46 system,
47 core_builder: crate::SystemBuilder {
48 system: core_system,
49 thread_unsafe: false,
50 },
51 sync_update: false,
52 })
53 }
54
55 /// # Safety
56 ///
57 /// This function sets up FMOD Studio to run all commands on the calling thread, and FMOD Studio expects all calls to be issued from a single thread.
58 ///
59 /// This has the side effect of making *EVERY* Studio Struct in this crate `!Send` and `!Sync` *without* marking them as `!Send` and `!Sync`.
60 /// This means that there are no handrails preventing you from using FMOD Studio across multiple threads, and you *must* ensure this yourself!
61 #[cfg(not(feature = "thread-unsafe"))]
62 pub unsafe fn synchronous_update(&mut self) {
63 self.sync_update = true;
64 }
65
66 #[cfg(feature = "thread-unsafe")]
67 pub fn synchronous_update(&mut self) {
68 self.sync_update = true;
69 }
70
71 /// Sets advanced settings.
72 pub fn settings(&mut self, settings: &AdvancedSettings) -> Result<&mut Self> {
73 let mut settings = settings.into();
74 // this function expects a pointer. maybe this is incorrect?
75 unsafe {
76 FMOD_Studio_System_SetAdvancedSettings(self.system, &raw mut settings).to_result()
77 }?;
78 Ok(self)
79 }
80
81 /// Builds the Studio System.
82 ///
83 /// The core system used by the studio system is initialized at the same time as the studio system.
84 pub fn build(
85 self,
86 max_channels: c_int,
87 studio_flags: InitFlags,
88 flags: crate::InitFlags,
89 ) -> Result<System> {
90 unsafe {
91 // we don't need
92 self.build_with_extra_driver_data(
93 max_channels,
94 studio_flags,
95 flags,
96 std::ptr::null_mut(),
97 )
98 }
99 }
100
101 /// Returns the FMOD core `SystemBuilder`.
102 ///
103 /// This function only returns a `&mut` reference to prevent building the core `System` as building the studio `System` will handle that for you.
104 pub fn core_builder(&mut self) -> &mut crate::SystemBuilder {
105 &mut self.core_builder
106 }
107
108 /// # Safety
109 ///
110 /// See the FMOD docs explaining driver data for more safety information.
111 pub unsafe fn build_with_extra_driver_data(
112 self,
113 max_channels: c_int,
114 mut studio_flags: InitFlags,
115 flags: crate::InitFlags,
116 driver_data: *mut c_void,
117 ) -> Result<System> {
118 if self.sync_update {
119 studio_flags.insert(InitFlags::SYNCHRONOUS_UPDATE);
120 } else {
121 #[cfg(not(feature = "thread-unsafe"))]
122 studio_flags.remove(InitFlags::SYNCHRONOUS_UPDATE);
123 }
124 unsafe {
125 FMOD_Studio_System_Initialize(
126 self.system,
127 max_channels,
128 studio_flags.bits(),
129 flags.bits(),
130 driver_data,
131 )
132 .to_result()?;
133 Ok(System::from_ffi(self.system))
134 }
135 }
136}