fmod/core/sound/
synchronization.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::{
8    ffi::{c_int, c_uint},
9    ptr::NonNull,
10};
11
12use crate::{FmodResultExt, Result};
13use fmod_sys::*;
14use lanyard::{Utf8CStr, Utf8CString};
15
16use crate::{Sound, TimeUnit, get_string};
17
18/// Named marker for a given point in time.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[repr(transparent)] // so we can transmute between types
21pub struct SyncPoint {
22    pub(crate) inner: NonNull<FMOD_SYNCPOINT>,
23}
24
25#[cfg(not(feature = "thread-unsafe"))]
26unsafe impl Send for SyncPoint {}
27#[cfg(not(feature = "thread-unsafe"))]
28unsafe impl Sync for SyncPoint {}
29
30impl SyncPoint {
31    /// # Safety
32    ///
33    /// `value` must be a valid pointer either aquired from [`Self::as_ptr`] or FMOD.
34    ///
35    /// # Panics
36    ///
37    /// Panics if `value` is null.
38    pub unsafe fn from_ffi(value: *mut FMOD_SYNCPOINT) -> Self {
39        let inner = NonNull::new(value).unwrap();
40        SyncPoint { inner }
41    }
42
43    /// Converts `self` into its raw representation.
44    pub fn as_ptr(self) -> *mut FMOD_SYNCPOINT {
45        self.inner.as_ptr()
46    }
47}
48
49impl From<SyncPoint> for *mut FMOD_SYNCPOINT {
50    fn from(value: SyncPoint) -> Self {
51        value.inner.as_ptr()
52    }
53}
54
55impl Sound {
56    /// Retrieve a sync point.
57    ///
58    /// For for more information on sync points see Sync Points.
59    pub fn get_sync_point(&self, index: i32) -> Result<SyncPoint> {
60        let mut sync_point = std::ptr::null_mut();
61        unsafe {
62            FMOD_Sound_GetSyncPoint(self.inner.as_ptr(), index, &raw mut sync_point).to_result()?;
63            Ok(SyncPoint::from_ffi(sync_point))
64        }
65    }
66
67    /// Retrieves information on an embedded sync point.
68    ///
69    /// For for more information on sync points see Sync Points.
70    pub fn get_sync_point_info(
71        &self,
72        point: SyncPoint,
73        offset_type: TimeUnit,
74    ) -> Result<(Utf8CString, c_uint)> {
75        let mut offset = 0;
76        let name = get_string(|name| unsafe {
77            FMOD_Sound_GetSyncPointInfo(
78                self.inner.as_ptr(),
79                point.into(),
80                name.as_mut_ptr().cast(),
81                name.len() as c_int,
82                &raw mut offset,
83                offset_type.into(),
84            )
85        })?;
86        Ok((name, offset))
87    }
88
89    /// Retrieves the number of sync points stored within a sound.
90    ///
91    /// For for more information on sync points see Sync Points.
92    pub fn get_sync_point_count(&self) -> Result<i32> {
93        let mut count = 0;
94        unsafe {
95            FMOD_Sound_GetNumSyncPoints(self.inner.as_ptr(), &raw mut count).to_result()?;
96        }
97        Ok(count)
98    }
99
100    /// Adds a sync point at a specific time within the sound.
101    ///
102    /// For more information on sync points see Sync Points.
103    pub fn add_sync_point(
104        &self,
105        offset: c_uint,
106        offset_type: TimeUnit,
107        name: &Utf8CStr,
108    ) -> Result<SyncPoint> {
109        let mut sync_point = std::ptr::null_mut();
110        unsafe {
111            FMOD_Sound_AddSyncPoint(
112                self.inner.as_ptr(),
113                offset,
114                offset_type.into(),
115                name.as_ptr(),
116                &raw mut sync_point,
117            )
118            .to_result()?;
119            Ok(SyncPoint::from_ffi(sync_point))
120        }
121    }
122
123    /// Deletes a sync point within the sound.
124    ///
125    /// For for more information on sync points see Sync Points.
126    pub fn delete_sync_point(&self, point: SyncPoint) -> Result<()> {
127        unsafe {
128            FMOD_Sound_DeleteSyncPoint(self.inner.as_ptr(), point.into()).to_result()?;
129        }
130        Ok(())
131    }
132}