fmod_utils/
voice.rs

1//! Channel management
2
3use fmod::{self, ChannelControl};
4
5/// Manages a single FMOD Channel for monophonic playback.
6///
7/// When a sound is played by FMOD, it is assigned to play on a virtual channel
8/// and a weak handle to the channel is returned.
9///
10/// This handle will be invalidated after the next call to `system.update()` if
11/// playback has reached the end of a non-looping sound, or otherwise if
12/// `channel.stop()` is called. The channel can also be *stolen* by the priority
13/// system if a new request to play a sound is made and there are no free
14/// channels.
15///
16/// This type will act as a strong reference for a single playing sound,
17/// controlling playback using a single channel and re-acquiring it in the case
18/// the channel is stolen or invalidated, and stopping the channel when the
19/// voice is dropped.
20#[derive(Debug, Default, PartialEq)]
21pub struct Voice {
22  channel       : Option <fmod::Channel>,
23  channel_group : Option <fmod::ChannelGroup>
24}
25
26pub type IndexType = u16;
27#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
28pub struct Id (pub IndexType);
29
30impl Voice {
31  pub fn new() -> Self {
32    Voice::default()
33  }
34
35  pub fn channel (&self) -> Option <&fmod::Channel> {
36    self.channel.as_ref()
37  }
38
39  pub fn channel_mut (&mut self) -> Option <&mut fmod::Channel> {
40    self.channel.as_mut()
41  }
42
43  pub fn channel_group (&self) -> Option <&fmod::ChannelGroup> {
44    self.channel_group.as_ref()
45  }
46
47  pub fn channel_group_mut (&mut self) -> Option <&mut fmod::ChannelGroup> {
48    self.channel_group.as_mut()
49  }
50
51  pub fn is_playing (&self) -> Option <bool> {
52    if let Some (channel) = self.channel.as_ref() {
53      let playing = match channel.is_playing() {
54        Ok (playing) => playing,
55        Err (fmod::Error::ChannelStolen) |
56        Err (fmod::Error::InvalidHandle) => false,
57        Err (err) => unreachable!("err: {:?}", err)
58      };
59      Some (playing)
60    } else {
61      None
62    }
63  }
64  //
65  //  acquiring a fresh channel
66  //
67  /// Play a 2D sound on a fresh channel.
68  ///
69  /// Note that playing the same sound twice will re-acquire the channel. To
70  /// restart playback at the beginning of the sound use `trigger()`.
71  pub fn play (&mut self,
72    sound         : &mut fmod::Sound,
73    mut channel_group : Option <fmod::ChannelGroup>
74  ) {
75    let _ = self.stop();
76    sound.set_mode (fmod::Mode::LOOP_OFF | fmod::Mode::_2D).unwrap();
77    self.channel = Some (sound.play (channel_group.as_mut(), false).unwrap());
78    self.channel_group = channel_group;
79  }
80
81  /// Loop a 2D sound on a fresh channel.
82  ///
83  /// Note that playing the same sound twice will re-acquire the channel. To
84  /// restart playback at the beginning of the sound use `trigger()`.
85  pub fn loop_ (&mut self,
86    sound         : &mut fmod::Sound,
87    mut channel_group : Option <fmod::ChannelGroup>
88  ) {
89    let _ = self.stop();
90    sound.set_mode (fmod::Mode::LOOP_NORMAL | fmod::Mode::_2D).unwrap();
91    self.channel = Some (sound.play (channel_group.as_mut(), false).unwrap());
92    self.channel_group = channel_group;
93  }
94
95  /// Play a 2D sound on a fresh channel starting from the given position.
96  ///
97  /// Note that playing the same sound twice will re-acquire the channel. To
98  /// restart playback at the beginning of the sound use `trigger()`.
99  pub fn play_from (&mut self,
100    sound         : &mut fmod::Sound,
101    channel_group : Option <fmod::ChannelGroup>,
102    position      : u32
103  ) {
104    let _ = self.stop();
105    self.cue (sound, channel_group);
106    assert!(!self.position (position, None).unwrap());
107    assert!(!self.resume().unwrap());
108  }
109
110  /// Play a 2D sound on a fresh channel starting from the given position.
111  ///
112  /// Note that looping will loop back to the beginning of the sound when the
113  /// end is reached.
114  ///
115  /// Note that playing the same sound twice will re-acquire the channel. To
116  /// restart playback at the beginning of the sound use `trigger()`.
117  pub fn loop_from (&mut self,
118    sound         : &mut fmod::Sound,
119    mut channel_group : Option <fmod::ChannelGroup>,
120    position      : u32
121  ) {
122    let _ = self.stop();
123    sound.set_mode (fmod::Mode::LOOP_NORMAL | fmod::Mode::_2D).unwrap();
124    self.channel = Some (sound.play (channel_group.as_mut(), true).unwrap());
125    self.channel_group = channel_group;
126    assert!(!self.position (position, None).unwrap());
127    assert!(!self.resume().unwrap());
128  }
129
130  /// "Play" a 2D sound on a fresh channel with 'paused = true'.
131  pub fn cue (&mut self,
132    sound         : &mut fmod::Sound,
133    mut channel_group : Option <fmod::ChannelGroup>
134  ) {
135    let _ = self.stop();
136    sound.set_mode (fmod::Mode::LOOP_OFF | fmod::Mode::_2D).unwrap();
137    self.channel = Some (sound.play (channel_group.as_mut(), true).unwrap());
138    self.channel_group = channel_group;
139  }
140
141  //
142  //  sound playback control on an existing channel
143  //
144
145  /// Set the position to the beginning of the sound in the un-paused state.
146  ///
147  /// Returns Some(true) if the channel was playing and None if this voice is
148  /// uninitialized.
149  pub fn trigger (&mut self) -> Option <bool> {
150    if let Some (mut channel) = self.channel.take() {
151      let playing = match channel.is_playing() {
152        Ok (playing) => {
153          channel.set_position (0, fmod::Timeunit::PCM).unwrap();
154          channel.set_paused (false).unwrap();
155          self.channel = Some (channel);
156          playing
157        }
158        Err (fmod::Error::ChannelStolen) |
159        Err (fmod::Error::InvalidHandle) => {
160          let mut sound = channel.sound_ref();
161          self.play (&mut sound, self.channel_group.clone());
162          false
163        }
164        Err (err) => unreachable!("err: {:?}", err)
165      };
166      Some (playing)
167    } else {
168      None
169    }
170  }
171
172  /// Pauses playback.
173  ///
174  /// Returns Some(true) if the channel was playing, and None if the voice was
175  /// uninitialized.
176  pub fn pause (&mut self) -> Option <bool> {
177    if let Some (mut channel) = self.channel.take() {
178      let playing = match channel.is_playing() {
179        Ok (playing) => {
180          channel.set_paused (true).unwrap();
181          self.channel = Some (channel);
182          playing
183        }
184        Err (fmod::Error::ChannelStolen) |
185        Err (fmod::Error::InvalidHandle) => {
186          let mut sound = channel.sound_ref();
187          self.cue (&mut sound, self.channel_group.clone());
188          false
189        }
190        Err (err) => unreachable!("err: {:?}", err)
191      };
192      Some (playing)
193    } else {
194      None
195    }
196  }
197
198  /// Resumes playback.
199  ///
200  /// Returns Some(true) if the channel was already playing, and None if the
201  /// voice is uninitialized.
202  pub fn resume (&mut self) -> Option <bool> {
203    if let Some (mut channel) = self.channel.take() {
204      let playing = match channel.is_playing() {
205        Ok (playing) => {
206          channel.set_paused (false).unwrap();
207          self.channel = Some (channel);
208          playing
209        }
210        Err (fmod::Error::ChannelStolen) |
211        Err (fmod::Error::InvalidHandle) => {
212          let mut sound = channel.sound_ref();
213          self.play (&mut sound, self.channel_group.clone());
214          false
215        }
216        Err (err) => unreachable!("err: {:?}", err)
217      };
218      Some (playing)
219    } else {
220      None
221    }
222  }
223
224  /// Set playback position.
225  ///
226  /// Default `timeunit` is `Timeunit::PCM` (samples).
227  ///
228  /// Returns Some(true) if the channel was currently playing, and None if the
229  /// voice was uninitialized.
230  pub fn position (&mut self,
231    position : u32, timeunit : Option <fmod::Timeunit>
232  ) -> Option <bool> {
233    if let Some (mut channel) = self.channel.take() {
234      let timeunit = timeunit.unwrap_or (fmod::Timeunit::PCM);
235      let playing = match channel.is_playing() {
236        Ok (playing) => {
237          channel.set_position (position, timeunit).unwrap();
238          self.channel = Some (channel);
239          playing
240        }
241        Err (fmod::Error::ChannelStolen) |
242        Err (fmod::Error::InvalidHandle) => {
243          let mut sound = channel.sound_ref();
244          self.cue (&mut sound, self.channel_group.clone());
245          self.channel.as_mut().unwrap().set_position (position, timeunit)
246            .unwrap();
247          false
248        }
249        Err (err) => unreachable!("err: {:?}", err)
250      };
251      Some (playing)
252    } else {
253      None
254    }
255  }
256
257  /// Stops playback and releases the channel.
258  ///
259  /// Returns Some(true) if the channel was playing. Returns None if the voice
260  /// was uninitialized.
261  ///
262  /// To instead pause playback and keep the channel reference, use `pause()`.
263  pub fn stop (&mut self) -> Option <bool> {
264    // NOTE: always forgets channel group
265    self.channel_group = None;
266    if let Some (mut channel) = self.channel.take() {
267      let playing = match channel.is_playing() {
268        Ok (playing) => {
269          channel.stop().unwrap();
270          playing
271        },
272        Err (fmod::Error::ChannelStolen) |
273        Err (fmod::Error::InvalidHandle) => false,
274        Err (err)                        => unreachable!("err: {:?}", err)
275      };
276      Some (playing)
277    } else {
278      None
279    }
280  }
281}
282
283impl Drop for Voice {
284  fn drop (&mut self) {
285    self.stop();
286  }
287}