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}