Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve player #823

Merged
merged 9 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions playback/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ byteorder = "1.4"
shell-words = "1.0.0"
tokio = { version = "1", features = ["sync"] }
zerocopy = { version = "0.3" }
thiserror = { version = "1" }

# Backends
alsa = { version = "0.5", optional = true }
Expand All @@ -40,7 +41,6 @@ glib = { version = "0.10", optional = true }
# Rodio dependencies
rodio = { version = "0.14", optional = true, default-features = false }
cpal = { version = "0.13", optional = true }
thiserror = { version = "1", optional = true }

# Decoder
lewton = "0.10"
Expand All @@ -51,11 +51,11 @@ rand = "0.8"
rand_distr = "0.4"

[features]
alsa-backend = ["alsa", "thiserror"]
alsa-backend = ["alsa"]
portaudio-backend = ["portaudio-rs"]
pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding", "thiserror"]
pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"]
jackaudio-backend = ["jack"]
rodio-backend = ["rodio", "cpal", "thiserror"]
rodiojack-backend = ["rodio", "cpal/jack", "thiserror"]
rodio-backend = ["rodio", "cpal"]
rodiojack-backend = ["rodio", "cpal/jack"]
sdl-backend = ["sdl2"]
gstreamer-backend = ["gstreamer", "gstreamer-app", "glib"]
5 changes: 4 additions & 1 deletion playback/src/audio_backend/jackaudio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ impl Open for JackSink {

impl Sink for JackSink {
fn write(&mut self, packet: &AudioPacket, converter: &mut Converter) -> io::Result<()> {
let samples_f32: &[f32] = &converter.f64_to_f32(packet.samples());
let samples = packet
.samples()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
JasonLG1979 marked this conversation as resolved.
Show resolved Hide resolved
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
for sample in samples_f32.iter() {
let res = self.send.send(*sample);
if res.is_err() {
Expand Down
5 changes: 4 additions & 1 deletion playback/src/audio_backend/portaudio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ impl<'a> Sink for PortAudioSink<'a> {
};
}

let samples = packet.samples();
let samples = packet
.samples()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

let result = match self {
Self::F32(stream, _parameters) => {
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
Expand Down
4 changes: 3 additions & 1 deletion playback/src/audio_backend/rodio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ pub fn open(host: cpal::Host, device: Option<String>, format: AudioFormat) -> Ro

impl Sink for RodioSink {
fn write(&mut self, packet: &AudioPacket, converter: &mut Converter) -> io::Result<()> {
let samples = packet.samples();
let samples = packet
.samples()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
match self.format {
AudioFormat::F32 => {
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
Expand Down
4 changes: 3 additions & 1 deletion playback/src/audio_backend/sdl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ impl Sink for SdlSink {
}};
}

let samples = packet.samples();
let samples = packet
.samples()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
match self {
Self::F32(queue) => {
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
Expand Down
58 changes: 15 additions & 43 deletions playback/src/decoder/lewton_decoder.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,46 @@
use super::{AudioDecoder, AudioError, AudioPacket};
use super::{AudioDecoder, AudioError, AudioPacket, DecoderResult};

use lewton::audio::AudioReadError::AudioIsHeader;
use lewton::inside_ogg::OggStreamReader;
use lewton::samples::InterleavedSamples;
use lewton::OggReadError::NoCapturePatternFound;
use lewton::VorbisError::{BadAudio, OggError};

use std::error;
use std::fmt;
use std::io::{Read, Seek};
use std::time::Duration;

pub struct VorbisDecoder<R: Read + Seek>(OggStreamReader<R>);
pub struct VorbisError(lewton::VorbisError);

impl<R> VorbisDecoder<R>
where
R: Read + Seek,
{
pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
Ok(VorbisDecoder(OggStreamReader::new(input)?))
pub fn new(input: R) -> DecoderResult<VorbisDecoder<R>> {
let reader =
OggStreamReader::new(input).map_err(|e| AudioError::LewtonDecoder(e.to_string()))?;
Ok(VorbisDecoder(reader))
}
}

impl<R> AudioDecoder for VorbisDecoder<R>
where
R: Read + Seek,
{
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
let absgp = Duration::from_millis(ms as u64 * crate::SAMPLE_RATE as u64).as_secs();
match self.0.seek_absgp_pg(absgp as u64) {
Ok(_) => Ok(()),
Err(err) => Err(AudioError::VorbisError(err.into())),
}
fn seek(&mut self, absgp: u64) -> DecoderResult<()> {
self.0
.seek_absgp_pg(absgp)
.map_err(|e| AudioError::LewtonDecoder(e.to_string()))?;
Ok(())
}

fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
use lewton::audio::AudioReadError::AudioIsHeader;
use lewton::OggReadError::NoCapturePatternFound;
use lewton::VorbisError::{BadAudio, OggError};
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>> {
loop {
match self.0.read_dec_packet_generic::<InterleavedSamples<f32>>() {
Ok(Some(packet)) => return Ok(Some(AudioPacket::samples_from_f32(packet.samples))),
Ok(None) => return Ok(None),

Err(BadAudio(AudioIsHeader)) => (),
Err(OggError(NoCapturePatternFound)) => (),
Err(err) => return Err(AudioError::VorbisError(err.into())),
Err(e) => return Err(AudioError::LewtonDecoder(e.to_string())),
}
}
}
}

impl From<lewton::VorbisError> for VorbisError {
fn from(err: lewton::VorbisError) -> VorbisError {
VorbisError(err)
}
}

impl fmt::Debug for VorbisError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}

impl fmt::Display for VorbisError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}

impl error::Error for VorbisError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
error::Error::source(&self.0)
}
}
63 changes: 25 additions & 38 deletions playback/src/decoder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
use std::fmt;
use thiserror::Error;

mod lewton_decoder;
pub use lewton_decoder::{VorbisDecoder, VorbisError};
pub use lewton_decoder::VorbisDecoder;

mod passthrough_decoder;
pub use passthrough_decoder::{PassthroughDecoder, PassthroughError};
pub use passthrough_decoder::PassthroughDecoder;

#[derive(Error, Debug)]
pub enum AudioError {
#[error("Lewton Decoder Error: {0}")]
LewtonDecoder(String),
#[error("Passthrough Decoder Error: {0}")]
PassthroughDecoder(String),
#[error("Decoder OggData Error: Can't return OggData on Samples")]
OggData,
#[error("Decoder Samples Error: Can't return Samples on OggData")]
Samples,
JasonLG1979 marked this conversation as resolved.
Show resolved Hide resolved
}

pub type DecoderResult<T> = Result<T, AudioError>;
JasonLG1979 marked this conversation as resolved.
Show resolved Hide resolved

pub enum AudioPacket {
Samples(Vec<f64>),
Expand All @@ -17,17 +31,17 @@ impl AudioPacket {
AudioPacket::Samples(f64_samples)
}

pub fn samples(&self) -> &[f64] {
pub fn samples(&self) -> DecoderResult<&[f64]> {
match self {
AudioPacket::Samples(s) => s,
AudioPacket::OggData(_) => panic!("can't return OggData on samples"),
AudioPacket::Samples(s) => Ok(s),
AudioPacket::OggData(_) => Err(AudioError::OggData),
}
}

pub fn oggdata(&self) -> &[u8] {
pub fn oggdata(&self) -> DecoderResult<&[u8]> {
match self {
AudioPacket::Samples(_) => panic!("can't return samples on OggData"),
AudioPacket::OggData(d) => d,
AudioPacket::OggData(d) => Ok(d),
AudioPacket::Samples(_) => Err(AudioError::Samples),
}
}

Expand All @@ -39,34 +53,7 @@ impl AudioPacket {
}
}

#[derive(Debug)]
pub enum AudioError {
PassthroughError(PassthroughError),
VorbisError(VorbisError),
}

impl fmt::Display for AudioError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AudioError::PassthroughError(err) => write!(f, "PassthroughError({})", err),
AudioError::VorbisError(err) => write!(f, "VorbisError({})", err),
}
}
}

impl From<VorbisError> for AudioError {
fn from(err: VorbisError) -> AudioError {
AudioError::VorbisError(err)
}
}

impl From<PassthroughError> for AudioError {
fn from(err: PassthroughError) -> AudioError {
AudioError::PassthroughError(err)
}
}

pub trait AudioDecoder {
fn seek(&mut self, ms: i64) -> Result<(), AudioError>;
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError>;
fn seek(&mut self, absgp: u64) -> DecoderResult<()>;
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>>;
}
Loading