fmod/core/channel_control/
playback.rs

1// Copyright (c) 2024 Lily 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_float;
8
9use fmod_sys::*;
10
11use crate::{ChannelControl, Mode};
12
13impl ChannelControl {
14    /// Retrieves the playing state.
15    ///
16    /// A [`Channel`] is considered playing after System::playSound or System::playDSP, even if it is paused.
17    ///
18    /// A [`ChannelGroup`] is considered playing if it has any playing [`Channel`]s.
19    pub fn is_playing(&self) -> Result<bool> {
20        // because we use c exports of the c++ api, we get to use bool! no fmod_bool here :3
21        let mut playing = false;
22        unsafe {
23            FMOD_ChannelControl_IsPlaying(self.inner, &mut playing).to_result()?;
24        }
25        Ok(playing)
26    }
27
28    /// Stops the Channel (or all [`Channel`]s in nested [`ChannelGroup`]s) from playing.
29    ///
30    /// This will free up internal resources for reuse by the virtual voice system.
31    ///
32    /// [`Channel`]s are stopped automatically when their playback position reaches the length of the Sound being played.
33    /// This is not the case however if the [`Channel`] is playing a DSP or the Sound is looping,
34    /// in which case the [`Channel`] will continue playing until stop is called. Once stopped,
35    /// the [`Channel`] handle will become invalid and can be discarded and any API calls made with it will return [`FMOD_RESULT::FMOD_ERR_INVALID_HANDLE`].
36    pub fn stop(&self) -> Result<()> {
37        unsafe { FMOD_ChannelControl_Stop(self.inner).to_result() }
38    }
39
40    /// Sets the paused state.
41    ///
42    /// Pause halts playback which effectively freezes Channel::getPosition and ChannelControl::getDSPClock values.
43    ///
44    /// An individual pause state is kept for each object,
45    /// pausing a parent ChannelGroup will effectively pause this object however when queried the individual pause state is returned.
46    pub fn set_paused(&self, paused: bool) -> Result<()> {
47        unsafe { FMOD_ChannelControl_SetPaused(self.inner, paused).to_result() }
48    }
49
50    /// Retrieves the paused state.
51    ///
52    /// An individual pause state is kept for each object,
53    /// a parent [`ChannelGroup`] being paused will effectively pause this object however when queried the individual pause state is returned.
54    pub fn get_paused(&self) -> Result<bool> {
55        let mut paused = false;
56        unsafe {
57            FMOD_ChannelControl_GetPaused(self.inner, &mut paused).to_result()?;
58        }
59        Ok(paused)
60    }
61
62    /// Sets the playback mode that controls how this object behaves.
63    ///
64    /// Modes supported:
65    ///
66    /// - [`Mode::LOOP_OFF`]
67    /// - [`Mode::LOOP_NORMAL`]
68    /// - [`Mode::LOOP_BIDI`]
69    /// - [`Mode::D2`]
70    /// - [`Mode::D3`]
71    /// - [`Mode::HEADRELATIVE_3D`]
72    /// - [`Mode::WORLDRELATIVE_3D`]
73    /// - [`Mode::INVERSE_ROLLOFF_3D`]
74    /// - [`Mode::LINEAR_ROLLOFF_3D`]
75    /// - [`Mode::LINEAR_SQUARE_ROLLOFF_3D`]
76    /// - [`Mode::INVERSE_TAPERED_ROLLOFF_3D`]
77    /// - [`Mode::CUSTOM_ROLLOFF_3D`]
78    /// - [`Mode::IGNORE_GEOMETRY_3D`]
79    /// - [`Mode::VIRTUAL_PLAYFROM_START`]
80    ///
81    /// When changing the loop mode, sounds created with
82    /// System::createStream or [`Mode::CREATE_STREAM`] may have already been pre-buffered and executed their loop logic ahead of time before this call was even made.
83    /// This is dependent on the size of the sound versus the size of the stream decode buffer (see FMOD_CREATESOUNDEXINFO).
84    /// If this happens, you may need to reflush the stream buffer by calling Channel::setPosition.
85    /// Note this will usually only happen if you have sounds or loop points that are smaller than the stream decode buffer size.
86    ///
87    /// When changing the loop mode of sounds created with with System::createSound or FMOD_CREATESAMPLE,
88    /// if the sound was set up as [`Mode::LOOP_OFF`], then set to [`Mode::LOOP_NORMAL`] with this function, the sound may click when playing the end of the sound.
89    /// This is because the sound needs to be prepared for looping using Sound::setMode,
90    /// by modifying the content of the PCM data (i.e. data past the end of the actual sample data) to allow the interpolators to read ahead without clicking.
91    /// If you use ChannelControl::setMode it will not do this (because different Channels may have different loop modes for the same sound)
92    /// and may click if you try to set it to looping on an unprepared sound.
93    /// If you want to change the loop mode at runtime it may be better to load the sound as looping first (or use Sound::setMode),
94    /// to let it prepare the data as if it was looping so that it does not click whenever ChannelControl::setMode is used to turn looping on.
95    ///
96    /// If [`Mode::IGNORE_GEOMETRY_3D`] or [`Mode::VIRTUAL_PLAYFROM_START`] is not specified, the flag will be cleared if it was specified previously.
97    pub fn set_mode(&self, mode: Mode) -> Result<()> {
98        unsafe { FMOD_ChannelControl_SetMode(self.inner, mode.into()).to_result() }
99    }
100
101    /// Retrieves the playback mode bits that control how this object behaves.
102    pub fn get_mode(&self) -> Result<Mode> {
103        let mut mode = 0;
104        unsafe {
105            FMOD_ChannelControl_GetMode(self.inner, &mut mode).to_result()?;
106        }
107        Ok(mode.into())
108    }
109
110    /// Sets the relative pitch / playback rate.
111    ///
112    /// Scales playback frequency of [`Channel`] object or if issued on a [`ChannelGroup`] it scales the frequencies of all [`Channel`]s contained in the [`ChannelGroup`].
113    ///
114    /// An individual pitch value is kept for each object,
115    /// changing the pitch of a parent [`ChannelGroup`] will effectively alter the pitch of this object however when queried the individual pitch value is returned.
116    pub fn set_pitch(&self, pitch: c_float) -> Result<()> {
117        unsafe { FMOD_ChannelControl_SetPitch(self.inner, pitch).to_result() }
118    }
119
120    /// Retrieves the relative pitch / playback rate.
121    ///
122    /// An individual pitch value is kept for each object, a parent [`ChannelGroup`] pitch will effectively scale the pitch of this object however when queried the individual pitch value is returned.
123    pub fn get_pitch(&self) -> Result<c_float> {
124        let mut pitch = 0.0;
125        unsafe {
126            FMOD_ChannelControl_GetPitch(self.inner, &mut pitch).to_result()?;
127        }
128        Ok(pitch)
129    }
130}