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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
//! Live, controllable audio instances.
//!
//! Tracks add control and event data around the bytestreams offered by [`Input`],
//! where each represents a live audio source inside of the driver's mixer.
//!
//! To prevent locking and stalling of the driver, tracks are controlled from your bot using a
//! [`TrackHandle`]. These handles remotely send commands from your bot's (a)sync
//! context to control playback, register events, and execute synchronous closures.
//!
//! If you want a new track from an [`Input`], i.e., for direct control before
//! playing your source on the driver, use [`create_player`].
//!
//! [`Input`]: ../input/struct.Input.html
//! [`TrackHandle`]: struct.TrackHandle.html
//! [`create_player`]: fn.create_player.html

mod command;
mod error;
mod handle;
mod looping;
mod mode;
mod queue;
mod state;

pub use self::{command::*, error::*, handle::*, looping::*, mode::*, queue::*, state::*};

use crate::{constants::*, driver::tasks::message::*, events::EventStore, input::Input};
use flume::{Receiver, TryRecvError};
use std::time::Duration;
use uuid::Uuid;

/// Control object for audio playback.
///
/// Accessed by both commands and the playback code -- as such, access from user code is
/// almost always guarded via a [`TrackHandle`]. You should expect to receive
/// access to a raw object of this type via [`create_player`], for use in
/// [`Driver::play`] or [`Driver::play_only`].
///
/// # Example
///
/// ```rust,no_run
/// use songbird::{driver::Driver, ffmpeg, tracks::create_player};
///
/// # async {
/// // A Call is also valid here!
/// let mut handler: Driver = Default::default();
/// let source = ffmpeg("../audio/my-favourite-song.mp3")
///     .await
///     .expect("This might fail: handle this error!");
/// let (mut audio, audio_handle) = create_player(source);
///
/// audio.set_volume(0.5);
///
/// handler.play_only(audio);
///
/// // Future access occurs via audio_handle.
/// # };
/// ```
///
/// [`Driver::play_only`]: crate::driver::Driver::play_only
/// [`Driver::play`]: crate::driver::Driver::play
/// [`TrackHandle`]: TrackHandle
/// [`create_player`]: create_player
#[derive(Debug)]
pub struct Track {
    /// Whether or not this sound is currently playing.
    ///
    /// Can be controlled with [`play`] or [`pause`] if chaining is desired.
    ///
    /// [`play`]: Track::play
    /// [`pause`]: Track::pause
    pub(crate) playing: PlayMode,

    /// The desired volume for playback.
    ///
    /// Sensible values fall between `0.0` and `1.0`.
    ///
    /// Can be controlled with [`volume`] if chaining is desired.
    ///
    /// [`volume`]: Track::volume
    pub(crate) volume: f32,

    /// Underlying data access object.
    ///
    /// *Calling code is not expected to use this.*
    pub(crate) source: Input,

    /// The current playback position in the track.
    pub(crate) position: Duration,

    /// The total length of time this track has been active.
    pub(crate) play_time: Duration,

    /// List of events attached to this audio track.
    ///
    /// This may be used to add additional events to a track
    /// before it is sent to the audio context for playing.
    pub events: Option<EventStore>,

    /// Channel from which commands are received.
    ///
    /// Track commands are sent in this manner to ensure that access
    /// occurs in a thread-safe manner, without allowing any external
    /// code to lock access to audio objects and block packet generation.
    pub(crate) commands: Receiver<TrackCommand>,

    /// Handle for safe control of this audio track from other threads.
    ///
    /// Typically, this is used by internal code to supply context information
    /// to event handlers, though more may be cloned from this handle.
    pub handle: TrackHandle,

    /// Count of remaining loops.
    pub loops: LoopState,

    /// Unique identifier for this track.
    pub(crate) uuid: Uuid,
}

impl Track {
    /// Create a new track directly from an input, command source,
    /// and handle.
    ///
    /// In general, you should probably use [`create_player`].
    ///
    /// [`create_player`]: fn.create_player.html
    pub fn new_raw(source: Input, commands: Receiver<TrackCommand>, handle: TrackHandle) -> Self {
        let uuid = handle.uuid();

        Self {
            playing: Default::default(),
            volume: 1.0,
            source,
            position: Default::default(),
            play_time: Default::default(),
            events: Some(EventStore::new_local()),
            commands,
            handle,
            loops: LoopState::Finite(0),
            uuid,
        }
    }

    /// Sets a track to playing if it is paused.
    pub fn play(&mut self) -> &mut Self {
        self.set_playing(PlayMode::Play)
    }

    /// Pauses a track if it is playing.
    pub fn pause(&mut self) -> &mut Self {
        self.set_playing(PlayMode::Pause)
    }

    /// Manually stops a track.
    ///
    /// This will cause the audio track to be removed, with any relevant events triggered.
    /// Stopped/ended tracks cannot be restarted.
    pub fn stop(&mut self) -> &mut Self {
        self.set_playing(PlayMode::Stop)
    }

    pub(crate) fn end(&mut self) -> &mut Self {
        self.set_playing(PlayMode::End)
    }

    #[inline]
    fn set_playing(&mut self, new_state: PlayMode) -> &mut Self {
        self.playing = self.playing.change_to(new_state);

        self
    }

    /// Returns the current play status of this track.
    pub fn playing(&self) -> PlayMode {
        self.playing
    }

    /// Sets [`volume`] in a manner that allows method chaining.
    ///
    /// [`volume`]: Track::volume
    pub fn set_volume(&mut self, volume: f32) -> &mut Self {
        self.volume = volume;

        self
    }

    /// Returns the current playback position.
    pub fn volume(&self) -> f32 {
        self.volume
    }

    /// Returns the current playback position.
    pub fn position(&self) -> Duration {
        self.position
    }

    /// Returns the total length of time this track has been active.
    pub fn play_time(&self) -> Duration {
        self.play_time
    }

    /// Set an audio track to loop a set number of times.
    ///
    /// If the underlying [`Input`] does not support seeking,
    /// then all calls will fail with [`TrackError::SeekUnsupported`].
    ///
    /// [`Input`]: crate::input::Input
    /// [`TrackError::SeekUnsupported`]: TrackError::SeekUnsupported
    pub fn set_loops(&mut self, loops: LoopState) -> TrackResult<()> {
        if self.source.is_seekable() {
            self.loops = loops;
            Ok(())
        } else {
            Err(TrackError::SeekUnsupported)
        }
    }

    pub(crate) fn do_loop(&mut self) -> bool {
        match self.loops {
            LoopState::Infinite => true,
            LoopState::Finite(0) => false,
            LoopState::Finite(ref mut n) => {
                *n -= 1;
                true
            },
        }
    }

    /// Steps playback location forward by one frame.
    pub(crate) fn step_frame(&mut self) {
        self.position += TIMESTEP_LENGTH;
        self.play_time += TIMESTEP_LENGTH;
    }

    /// Receives and acts upon any commands forwarded by TrackHandles.
    ///
    /// *Used internally*, this should not be exposed to users.
    pub(crate) fn process_commands(&mut self, index: usize, ic: &Interconnect) {
        // Note: disconnection and an empty channel are both valid,
        // and should allow the audio object to keep running as intended.

        // Note that interconnect failures are not currently errors.
        // In correct operation, the event thread should never panic,
        // but it receiving status updates is secondary do actually
        // doing the work.
        loop {
            match self.commands.try_recv() {
                Ok(cmd) => {
                    use TrackCommand::*;
                    match cmd {
                        Play => {
                            self.play();
                            let _ = ic.events.send(EventMessage::ChangeState(
                                index,
                                TrackStateChange::Mode(self.playing),
                            ));
                        },
                        Pause => {
                            self.pause();
                            let _ = ic.events.send(EventMessage::ChangeState(
                                index,
                                TrackStateChange::Mode(self.playing),
                            ));
                        },
                        Stop => {
                            self.stop();
                            let _ = ic.events.send(EventMessage::ChangeState(
                                index,
                                TrackStateChange::Mode(self.playing),
                            ));
                        },
                        Volume(vol) => {
                            self.set_volume(vol);
                            let _ = ic.events.send(EventMessage::ChangeState(
                                index,
                                TrackStateChange::Volume(self.volume),
                            ));
                        },
                        Seek(time) =>
                            if let Ok(new_time) = self.seek_time(time) {
                                let _ = ic.events.send(EventMessage::ChangeState(
                                    index,
                                    TrackStateChange::Position(new_time),
                                ));
                            },
                        AddEvent(evt) => {
                            let _ = ic.events.send(EventMessage::AddTrackEvent(index, evt));
                        },
                        Do(action) => {
                            action(self);
                            let _ = ic.events.send(EventMessage::ChangeState(
                                index,
                                TrackStateChange::Total(self.state()),
                            ));
                        },
                        Request(tx) => {
                            let _ = tx.send(Box::new(self.state()));
                        },
                        Loop(loops) =>
                            if self.set_loops(loops).is_ok() {
                                let _ = ic.events.send(EventMessage::ChangeState(
                                    index,
                                    TrackStateChange::Loops(self.loops, true),
                                ));
                            },
                        MakePlayable => self.make_playable(),
                    }
                },
                Err(TryRecvError::Disconnected) => {
                    // this branch will never be visited.
                    break;
                },
                Err(TryRecvError::Empty) => {
                    break;
                },
            }
        }
    }

    /// Ready a track for playing if it is lazily initialised.
    ///
    /// Currently, only [`Restartable`] sources support lazy setup.
    /// This call is a no-op for all others.
    ///
    /// [`Restartable`]: crate::input::restartable::Restartable
    pub fn make_playable(&mut self) {
        self.source.reader.make_playable();
    }

    /// Creates a read-only copy of the audio track's state.
    ///
    /// The primary use-case of this is sending information across
    /// threads in response to a [`TrackHandle`].
    ///
    /// [`TrackHandle`]: TrackHandle
    pub fn state(&self) -> TrackState {
        TrackState {
            playing: self.playing,
            volume: self.volume,
            position: self.position,
            play_time: self.play_time,
            loops: self.loops,
        }
    }

    /// Seek to a specific point in the track.
    ///
    /// If the underlying [`Input`] does not support seeking,
    /// then all calls will fail with [`TrackError::SeekUnsupported`].
    ///
    /// [`Input`]: crate::input::Input
    /// [`TrackError::SeekUnsupported`]: TrackError::SeekUnsupported
    pub fn seek_time(&mut self, pos: Duration) -> TrackResult<Duration> {
        if let Some(t) = self.source.seek_time(pos) {
            self.position = t;
            Ok(t)
        } else {
            Err(TrackError::SeekUnsupported)
        }
    }

    /// Returns this track's unique identifier.
    pub fn uuid(&self) -> Uuid {
        self.uuid
    }
}

/// Creates a [`Track`] object to pass into the audio context, and a [`TrackHandle`]
/// for safe, lock-free access in external code.
///
/// Typically, this would be used if you wished to directly work on or configure
/// the [`Track`] object before it is passed over to the driver.
///
/// [`Track`]: Track
/// [`TrackHandle`]: TrackHandle
#[inline]
pub fn create_player(source: Input) -> (Track, TrackHandle) {
    create_player_with_uuid(source, Uuid::new_v4())
}

/// Creates a [`Track`] and [`TrackHandle`] as in [`create_player`], allowing
/// a custom UUID to be set.
///
/// [`create_player`]: create_player
/// [`Track`]: Track
/// [`TrackHandle`]: TrackHandle
pub fn create_player_with_uuid(source: Input, uuid: Uuid) -> (Track, TrackHandle) {
    let (tx, rx) = flume::unbounded();
    let can_seek = source.is_seekable();
    let metadata = source.metadata.clone();
    let handle = TrackHandle::new(tx, can_seek, uuid, metadata);

    let player = Track::new_raw(source, rx, handle.clone());

    (player, handle)
}