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