fmod/core/channel/playback_control.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 fmod_sys::*;
8use std::ffi::{c_float, c_int, c_uint};
9
10use crate::{Channel, ChannelGroup, TimeUnit};
11
12impl Channel {
13 /// Sets the frequency or playback rate.
14 ///
15 /// Default frequency is determined by the audio format of the Sound or DSP.
16 ///
17 /// Sounds opened as [`SoundMode::CreateSample`] (not [`SoundMode::CreateStream`] or [`SoundMode::CreateCompressedSample`]) can be played backwards by giving a negative frequency.
18 pub fn set_frequency(&self, frequency: c_float) -> Result<()> {
19 unsafe { FMOD_Channel_SetFrequency(self.inner, frequency).to_result() }
20 }
21
22 /// Retrieves the playback frequency or playback rate.
23 pub fn get_frequency(&self) -> Result<c_float> {
24 let mut frequency = 0.0;
25 unsafe { FMOD_Channel_GetFrequency(self.inner, &mut frequency).to_result()? }
26 Ok(frequency)
27 }
28 /// Sets the priority used for virtual voice ordering.
29 ///
30 /// Priority is used as a coarse grain control for the virtual voice system, lower priority [`Channel`]s will always be stolen before higher ones.
31 /// For [`Channel`]s of equal priority, those with the quietest [`ChannelControl::get_audibility`] value will be stolen first.
32 ///
33 /// See the Virtual Voices guide for more information.
34 pub fn set_priority(&self, priority: c_int) -> Result<()> {
35 unsafe { FMOD_Channel_SetPriority(self.inner, priority).to_result() }
36 }
37
38 /// Retrieves the priority used for virtual voice ordering.
39 ///
40 /// Priority is used as a coarse grain control for the virtual voice system, lower priority [`Channel`]s will always be stolen before higher ones.
41 /// For [`Channel`]s of equal priority, those with the quietest [`ChannelControl::get_audibility`] value will be stolen first.
42 ///
43 ///See the Virtual Voices guide for more information.
44 pub fn get_priority(&self) -> Result<c_int> {
45 let mut priority = 0;
46 unsafe { FMOD_Channel_GetPriority(self.inner, &mut priority).to_result()? }
47 Ok(priority)
48 }
49
50 /// Sets the current playback position.
51 ///
52 /// Certain [`TimeUnit`] types are always available: [`TimeUnit::PCM`], [`TimeUnit::PCMBytes`] and [`TimeUnit::MS`].
53 /// The others are format specific such as [`TimeUnit::ModOrder`] / [`TimeUnit::ModRow`] / [`TimeUnit::ModPattern`] which is specific to files of type MOD / S3M / XM / IT.
54 ///
55 /// 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.
56 /// You can avoid this by creating the stream with [`SoundMode::Nonblocking`].
57 /// 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`].
58 /// [`Channel::get_position`] will also not update until this non-blocking set position operation has completed.
59 ///
60 /// 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`].
61 /// 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.
62 /// This means there is a slight delay as FMOD scans the whole file when loading the sound to create this table.
63 pub fn set_position(&self, position: c_uint, time_unit: TimeUnit) -> Result<()> {
64 unsafe { FMOD_Channel_SetPosition(self.inner, position, time_unit.into()).to_result() }
65 }
66
67 /// Retrieves the current playback position.
68 ///
69 /// Certain [`TimeUnit`] types are always available: [`TimeUnit::PCM`], [`TimeUnit::PCMBytes`] and [`TimeUnit::MS`].
70 /// The others are format specific such as [`TimeUnit::ModOrder`] / [`TimeUnit::ModRow`] / [`TimeUnit::ModPattern`] which is specific to files of type MOD / S3M / XM / IT.
71 ///
72 /// 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.
73 pub fn get_position(&self, time_unit: TimeUnit) -> Result<c_uint> {
74 let mut position = 0;
75 unsafe {
76 FMOD_Channel_GetPosition(self.inner, &mut position, time_unit.into()).to_result()?;
77 }
78 Ok(position)
79 }
80
81 /// Sets the [`ChannelGroup`] this object outputs to.
82 ///
83 /// A [`ChannelGroup`] may contain many Channels.
84 ///
85 /// [`Channel`]s may only output to a single [`ChannelGroup`]. This operation will remove it from the previous group first.
86 pub fn set_channel_group(&self, channel_group: ChannelGroup) -> Result<()> {
87 unsafe { FMOD_Channel_SetChannelGroup(self.inner, channel_group.into()).to_result() }
88 }
89
90 /// Retrieves the [`ChannelGroup`] this object outputs to.
91 pub fn get_channel_group(&self) -> Result<ChannelGroup> {
92 let mut channel_group = std::ptr::null_mut();
93 unsafe {
94 FMOD_Channel_GetChannelGroup(self.inner, &mut channel_group).to_result()?;
95 }
96 Ok(channel_group.into())
97 }
98
99 /// Sets the number of times to loop before stopping.
100 ///
101 /// The 'mode' of the Sound or Channel must be [`SoundMode::LoopNormal`] or [`SoundMode::LoopBidi`] for this function to work.
102 pub fn set_loop_count(&self, loop_count: c_int) -> Result<()> {
103 unsafe { FMOD_Channel_SetLoopCount(self.inner, loop_count).to_result() }
104 }
105
106 /// Retrieves the number of times to loop before stopping.
107 ///
108 /// This is the current loop countdown value that will decrement as it plays until reaching 0.
109 /// Reset with [`Channel::set_loop_count`].
110 pub fn get_loop_count(&self) -> Result<c_int> {
111 let mut loop_count = 0;
112 unsafe { FMOD_Channel_GetLoopCount(self.inner, &mut loop_count).to_result()? }
113 Ok(loop_count)
114 }
115
116 /// Sets the loop start and end points.
117 ///
118 /// Loop points may only be set on a Channel playing a Sound, not a Channel playing a DSP (See System::playDSP).
119 ///
120 /// Valid [`TimeUnit`] types are [`TimeUnit::PCM`], [`TimeUnit::MS`], [`TimeUnit::PCMBytes`]. Any other time units return [`FMOD_RESULT::FMOD_ERR_FORMAT`].
121 /// If [`TimeUnit::MS`] or [`TimeUnit::PCMBytes`], the value is internally converted to [`TimeUnit::PCM`].
122 ///
123 /// The Channel's mode must be set to [`SoundMode::LoopNormal`] or [`SoundMode::LoopBidi`] for loop points to affect playback.
124 pub fn set_loop_points(
125 &self,
126 start: c_uint,
127 start_type: TimeUnit,
128 end: c_uint,
129 end_type: TimeUnit,
130 ) -> Result<()> {
131 unsafe {
132 FMOD_Channel_SetLoopPoints(self.inner, start, start_type.into(), end, end_type.into())
133 .to_result()
134 }
135 }
136
137 /// Retrieves the loop start and end points.
138 ///
139 /// Valid [`TimeUnit`] types are [`TimeUnit::PCM`], [`TimeUnit::MS`], [`TimeUnit::PCMBytes`]. Any other time units return [`FMOD_RESULT::FMOD_ERR_FORMAT`].
140 /// 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.
141 pub fn get_loop_points(
142 &self,
143 start_type: TimeUnit,
144 end_type: TimeUnit,
145 ) -> Result<(c_uint, c_uint)> {
146 let mut start = 0;
147 let mut end = 0;
148 unsafe {
149 FMOD_Channel_GetLoopPoints(
150 self.inner,
151 &mut start,
152 start_type.into(),
153 &mut end,
154 end_type.into(),
155 )
156 .to_result()?;
157 }
158 Ok((start, end))
159 }
160}