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}