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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
// 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 fmod_sys::*;
use std::ffi::{c_float, c_int, c_uint};
use crate::{Channel, ChannelGroup, TimeUnit};
impl Channel {
/// Sets the frequency or playback rate.
///
/// Default frequency is determined by the audio format of the Sound or DSP.
///
/// Sounds opened as [`SoundMode::CreateSample`] (not [`SoundMode::CreateStream`] or [`SoundMode::CreateCompressedSample`]) can be played backwards by giving a negative frequency.
pub fn set_frequency(&self, frequency: c_float) -> Result<()> {
unsafe { FMOD_Channel_SetFrequency(self.inner, frequency).to_result() }
}
/// Retrieves the playback frequency or playback rate.
pub fn get_frequency(&self) -> Result<c_float> {
let mut frequency = 0.0;
unsafe { FMOD_Channel_GetFrequency(self.inner, &mut frequency).to_result()? }
Ok(frequency)
}
/// Sets the priority used for virtual voice ordering.
///
/// Priority is used as a coarse grain control for the virtual voice system, lower priority [`Channel`]s will always be stolen before higher ones.
/// For [`Channel`]s of equal priority, those with the quietest [`ChannelControl::get_audibility`] value will be stolen first.
///
/// See the Virtual Voices guide for more information.
pub fn set_priority(&self, priority: c_int) -> Result<()> {
unsafe { FMOD_Channel_SetPriority(self.inner, priority).to_result() }
}
/// Retrieves the priority used for virtual voice ordering.
///
/// Priority is used as a coarse grain control for the virtual voice system, lower priority [`Channel`]s will always be stolen before higher ones.
/// For [`Channel`]s of equal priority, those with the quietest [`ChannelControl::get_audibility`] value will be stolen first.
///
///See the Virtual Voices guide for more information.
pub fn get_priority(&self) -> Result<c_int> {
let mut priority = 0;
unsafe { FMOD_Channel_GetPriority(self.inner, &mut priority).to_result()? }
Ok(priority)
}
/// Sets the current playback position.
///
/// Certain [`TimeUnit`] types are always available: [`TimeUnit::PCM`], [`TimeUnit::PCMBytes`] and [`TimeUnit::MS`].
/// The others are format specific such as [`TimeUnit::ModOrder`] / [`TimeUnit::ModRow`] / [`TimeUnit::ModPattern`] which is specific to files of type MOD / S3M / XM / IT.
///
/// If playing a Sound created with [`System::create_stream`] or [`SoundMode::CreateStream`] changing the position may cause a slow reflush operation while the file seek and decode occurs.
/// You can avoid this by creating the stream with [`SoundMode::Nonblocking`].
/// This will cause the stream to go into FMOD_OPENSTATE_SETPOSITION state (see Sound::getOpenState) and Sound commands will return [`FMOD_RESULT::FMOD_ERR_NOTREADY`].
/// [`Channel::get_position`] will also not update until this non-blocking set position operation has completed.
///
/// Using a VBR source that does not have an associated seek table or seek information (such as MP3 or MOD/S3M/XM/IT) may cause inaccurate seeking if you specify [`TimeUnit::MS`] or [`TimeUnit::PCM`].
/// If you want FMOD to create a PCM vs bytes seek table so that seeking is accurate, you will have to specify [`SoundMode::AccurrateTime`] when loading or opening the sound.
/// This means there is a slight delay as FMOD scans the whole file when loading the sound to create this table.
pub fn set_position(&self, position: c_uint, time_unit: TimeUnit) -> Result<()> {
unsafe { FMOD_Channel_SetPosition(self.inner, position, time_unit.into()).to_result() }
}
/// Retrieves the current playback position.
///
/// Certain [`TimeUnit`] types are always available: [`TimeUnit::PCM`], [`TimeUnit::PCMBytes`] and [`TimeUnit::MS`].
/// The others are format specific such as [`TimeUnit::ModOrder`] / [`TimeUnit::ModRow`] / [`TimeUnit::ModPattern`] which is specific to files of type MOD / S3M / XM / IT.
///
/// If [`TimeUnit::MS`] or [`TimeUnit::PCMBytes`] are used, the value is internally converted from [`TimeUnit::PCM`], so the retrieved value may not exactly match the set value.
pub fn get_position(&self, time_unit: TimeUnit) -> Result<c_uint> {
let mut position = 0;
unsafe {
FMOD_Channel_GetPosition(self.inner, &mut position, time_unit.into()).to_result()?;
}
Ok(position)
}
/// Sets the [`ChannelGroup`] this object outputs to.
///
/// A [`ChannelGroup`] may contain many Channels.
///
/// [`Channel`]s may only output to a single [`ChannelGroup`]. This operation will remove it from the previous group first.
pub fn set_channel_group(&self, channel_group: ChannelGroup) -> Result<()> {
unsafe { FMOD_Channel_SetChannelGroup(self.inner, channel_group.into()).to_result() }
}
/// Retrieves the [`ChannelGroup`] this object outputs to.
pub fn get_channel_group(&self) -> Result<ChannelGroup> {
let mut channel_group = std::ptr::null_mut();
unsafe {
FMOD_Channel_GetChannelGroup(self.inner, &mut channel_group).to_result()?;
}
Ok(channel_group.into())
}
/// Sets the number of times to loop before stopping.
///
/// The 'mode' of the Sound or Channel must be [`SoundMode::LoopNormal`] or [`SoundMode::LoopBidi`] for this function to work.
pub fn set_loop_count(&self, loop_count: c_int) -> Result<()> {
unsafe { FMOD_Channel_SetLoopCount(self.inner, loop_count).to_result() }
}
/// Retrieves the number of times to loop before stopping.
///
/// This is the current loop countdown value that will decrement as it plays until reaching 0.
/// Reset with [`Channel::set_loop_count`].
pub fn get_loop_count(&self) -> Result<c_int> {
let mut loop_count = 0;
unsafe { FMOD_Channel_GetLoopCount(self.inner, &mut loop_count).to_result()? }
Ok(loop_count)
}
/// Sets the loop start and end points.
///
/// Loop points may only be set on a Channel playing a Sound, not a Channel playing a DSP (See System::playDSP).
///
/// Valid [`TimeUnit`] types are [`TimeUnit::PCM`], [`TimeUnit::MS`], [`TimeUnit::PCMBytes`]. Any other time units return [`FMOD_RESULT::FMOD_ERR_FORMAT`].
/// If [`TimeUnit::MS`] or [`TimeUnit::PCMBytes`], the value is internally converted to [`TimeUnit::PCM`].
///
/// The Channel's mode must be set to [`SoundMode::LoopNormal`] or [`SoundMode::LoopBidi`] for loop points to affect playback.
pub fn set_loop_points(
&self,
start: c_uint,
start_type: TimeUnit,
end: c_uint,
end_type: TimeUnit,
) -> Result<()> {
unsafe {
FMOD_Channel_SetLoopPoints(self.inner, start, start_type.into(), end, end_type.into())
.to_result()
}
}
/// Retrieves the loop start and end points.
///
/// Valid [`TimeUnit`] types are [`TimeUnit::PCM`], [`TimeUnit::MS`], [`TimeUnit::PCMBytes`]. Any other time units return [`FMOD_RESULT::FMOD_ERR_FORMAT`].
/// If [`TimeUnit::MS`] or [`TimeUnit::PCMBytes`] are used, the value is internally converted from [`TimeUnit::PCM`], so the retrieved value may not exactly match the set value.
pub fn get_loop_points(
&self,
start_type: TimeUnit,
end_type: TimeUnit,
) -> Result<(c_uint, c_uint)> {
let mut start = 0;
let mut end = 0;
unsafe {
FMOD_Channel_GetLoopPoints(
self.inner,
&mut start,
start_type.into(),
&mut end,
end_type.into(),
)
.to_result()?;
}
Ok((start, end))
}
}