Voice Rework -- Events, Track Queues (#806)
This implements a proof-of-concept for an improved audio frontend. The largest change is the introduction of events and event handling: both by time elapsed and by track events, such as ending or looping. Following on from this, the library now includes a basic, event-driven track queue system (which people seem to ask for unusually often). A new sample, `examples/13_voice_events`, demonstrates both the `TrackQueue` system and some basic events via the `~queue` and `~play_fade` commands. Locks are removed from around the control of `Audio` objects, which should allow the backend to be moved to a more granular futures-based backend solution in a cleaner way.
This commit is contained in:
180
src/input/reader.rs
Normal file
180
src/input/reader.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
//! Raw handlers for input bytestreams.
|
||||
|
||||
use super::*;
|
||||
use std::{
|
||||
fmt::{Debug, Error as FormatError, Formatter},
|
||||
fs::File,
|
||||
io::{
|
||||
BufReader,
|
||||
Cursor,
|
||||
Error as IoError,
|
||||
ErrorKind as IoErrorKind,
|
||||
Read,
|
||||
Result as IoResult,
|
||||
Seek,
|
||||
SeekFrom,
|
||||
},
|
||||
result::Result as StdResult,
|
||||
};
|
||||
use streamcatcher::{Catcher, TxCatcher};
|
||||
|
||||
/// Usable data/byte sources for an audio stream.
|
||||
///
|
||||
/// Users may define their own data sources using [`Extension`]
|
||||
/// and [`ExtensionSeek`].
|
||||
///
|
||||
/// [`Extension`]: #variant.Extension
|
||||
/// [`ExtensionSeek`]: #variant.ExtensionSeek
|
||||
pub enum Reader {
|
||||
/// Piped output of another program (i.e., [`ffmpeg`]).
|
||||
///
|
||||
/// Does not support seeking.
|
||||
///
|
||||
/// [`ffmpeg`]: ../fn.ffmpeg.html
|
||||
Pipe(BufReader<ChildContainer>),
|
||||
/// A cached, raw in-memory store, provided by Songbird.
|
||||
///
|
||||
/// Supports seeking.
|
||||
Memory(Catcher<Box<Reader>>),
|
||||
/// A cached, Opus-compressed in-memory store, provided by Songbird.
|
||||
///
|
||||
/// Supports seeking.
|
||||
Compressed(TxCatcher<Box<Input>, OpusCompressor>),
|
||||
/// A source which supports seeking by recreating its inout stream.
|
||||
///
|
||||
/// Supports seeking.
|
||||
Restartable(Restartable),
|
||||
/// A source contained in a local file.
|
||||
///
|
||||
/// Supports seeking.
|
||||
File(BufReader<File>),
|
||||
/// A source contained as an array in memory.
|
||||
///
|
||||
/// Supports seeking.
|
||||
Vec(Cursor<Vec<u8>>),
|
||||
/// A basic user-provided source.
|
||||
///
|
||||
/// Does not support seeking.
|
||||
Extension(Box<dyn Read + Send>),
|
||||
/// A user-provided source which also implements [`Seek`].
|
||||
///
|
||||
/// Supports seeking.
|
||||
///
|
||||
/// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
|
||||
ExtensionSeek(Box<dyn ReadSeek + Send>),
|
||||
}
|
||||
|
||||
impl Reader {
|
||||
/// Returns whether the given source implements [`Seek`].
|
||||
///
|
||||
/// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
|
||||
pub fn is_seekable(&self) -> bool {
|
||||
use Reader::*;
|
||||
match self {
|
||||
Restartable(_) | Compressed(_) | Memory(_) => true,
|
||||
Extension(_) => false,
|
||||
ExtensionSeek(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
pub(crate) fn prep_with_handle(&mut self, handle: Handle) {
|
||||
use Reader::*;
|
||||
match self {
|
||||
Restartable(r) => r.prep_with_handle(handle),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Reader {
|
||||
fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
|
||||
use Reader::*;
|
||||
match self {
|
||||
Pipe(a) => Read::read(a, buffer),
|
||||
Memory(a) => Read::read(a, buffer),
|
||||
Compressed(a) => Read::read(a, buffer),
|
||||
Restartable(a) => Read::read(a, buffer),
|
||||
File(a) => Read::read(a, buffer),
|
||||
Vec(a) => Read::read(a, buffer),
|
||||
Extension(a) => a.read(buffer),
|
||||
ExtensionSeek(a) => a.read(buffer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Reader {
|
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
||||
use Reader::*;
|
||||
match self {
|
||||
Pipe(_) | Extension(_) => Err(IoError::new(
|
||||
IoErrorKind::InvalidInput,
|
||||
"Seeking not supported on Reader of this type.",
|
||||
)),
|
||||
Memory(a) => Seek::seek(a, pos),
|
||||
Compressed(a) => Seek::seek(a, pos),
|
||||
File(a) => Seek::seek(a, pos),
|
||||
Restartable(a) => Seek::seek(a, pos),
|
||||
Vec(a) => Seek::seek(a, pos),
|
||||
ExtensionSeek(a) => a.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Reader {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), FormatError> {
|
||||
use Reader::*;
|
||||
let field = match self {
|
||||
Pipe(a) => format!("{:?}", a),
|
||||
Memory(a) => format!("{:?}", a),
|
||||
Compressed(a) => format!("{:?}", a),
|
||||
Restartable(a) => format!("{:?}", a),
|
||||
File(a) => format!("{:?}", a),
|
||||
Vec(a) => format!("{:?}", a),
|
||||
Extension(_) => "Extension".to_string(),
|
||||
ExtensionSeek(_) => "ExtensionSeek".to_string(),
|
||||
};
|
||||
f.debug_tuple("Reader").field(&field).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Reader {
|
||||
fn from(val: Vec<u8>) -> Reader {
|
||||
Reader::Vec(Cursor::new(val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Fusion trait for custom input sources which allow seeking.
|
||||
pub trait ReadSeek {
|
||||
/// See [`Read::read`].
|
||||
///
|
||||
/// [`Read::read`]: https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize>;
|
||||
/// See [`Seek::seek`].
|
||||
///
|
||||
/// [`Seek::seek`]: https://doc.rust-lang.org/nightly/std/io/trait.Seek.html#tymethod.seek
|
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64>;
|
||||
}
|
||||
|
||||
impl Read for dyn ReadSeek {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
ReadSeek::read(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for dyn ReadSeek {
|
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
||||
ReadSeek::seek(self, pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadSeek for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
Read::read(self, buf)
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
||||
Seek::seek(self, pos)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user