Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions crates/kira/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ mint = "0.5.9"
pastey = "0.2.1"
rtrb = "0.3.2"
serde = { version = "1.0.219", features = ["derive"], optional = true }
symphonia = { version = "0.5.4", optional = true, default-features = false }
symphonia = { version = "0.6.0", optional = true, default-features = false }
triple_buffer = "9.0.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies.cpal]
version = "0.17.0"
version = "0.18.1"
optional = true

[target.'cfg(target_arch = "wasm32")'.dependencies.cpal]
version = "0.17.0"
version = "0.18.1"
optional = true
features = ["wasm-bindgen"]

Expand Down
4 changes: 2 additions & 2 deletions crates/kira/src/backend/cpal/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use stream_manager::{StreamManager, StreamManagerController};

use crate::backend::{Backend, Renderer};
use cpal::{
BufferSize, Device, StreamConfig, StreamError,
BufferSize, Device, StreamConfig,
traits::{DeviceTrait, HostTrait},
};

Expand Down Expand Up @@ -60,7 +60,7 @@ impl CpalBackend {

/// Returns the oldest available stream error in the queue.
#[cfg_attr(docsrs, doc(cfg(not(wasm32))))]
pub fn pop_error(&mut self) -> Option<StreamError> {
pub fn pop_error(&mut self) -> Option<cpal::Error> {
if let State::Initialized {
stream_manager_controller,
} = &mut self.state
Expand Down
21 changes: 11 additions & 10 deletions crates/kira/src/backend/cpal/desktop/stream_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{

use super::renderer_with_cpu_usage::RendererWithCpuUsage;
use cpal::{
BufferSize, Device, Stream, StreamConfig, StreamError,
BufferSize, Device, Stream, StreamConfig,
traits::{DeviceTrait, HostTrait, StreamTrait},
};
use rtrb::{Consumer, Producer, PushError, RingBuffer};
Expand All @@ -35,7 +35,7 @@ enum State {
pub(super) struct StreamManagerController {
should_drop: Arc<AtomicBool>,
num_stream_errors_discarded: Arc<AtomicU64>,
handled_stream_error_consumer: Mutex<Consumer<StreamError>>,
handled_stream_error_consumer: Mutex<Consumer<cpal::Error>>,
}

impl StreamManagerController {
Expand All @@ -48,7 +48,7 @@ impl StreamManagerController {
self.num_stream_errors_discarded.load(Ordering::Acquire)
}

pub fn pop_handled_error(&mut self) -> Option<StreamError> {
pub fn pop_handled_error(&mut self) -> Option<cpal::Error> {
self.handled_stream_error_consumer
.get_mut()
.unwrap()
Expand Down Expand Up @@ -139,15 +139,15 @@ impl StreamManager {
/// Restarts the stream if the audio device gets disconnected.
fn check_stream(
&mut self,
unhandled_stream_error_consumer: &mut Consumer<StreamError>,
handled_stream_error_producer: &mut Producer<StreamError>,
unhandled_stream_error_consumer: &mut Consumer<cpal::Error>,
handled_stream_error_producer: &mut Producer<cpal::Error>,
num_stream_errors_discarded: &Arc<AtomicU64>,
) {
if let State::Running { .. } = &self.state {
while let Ok(error) = unhandled_stream_error_consumer.pop() {
match error {
match error.kind() {
// check for device disconnection
StreamError::DeviceNotAvailable | StreamError::StreamInvalidated => {
cpal::ErrorKind::DeviceNotAvailable | cpal::ErrorKind::StreamInvalidated => {
self.stop_stream();
if let Ok((device, mut config)) = default_device_and_config() {
// TODO: gracefully handle errors that occur in this function
Expand All @@ -160,7 +160,8 @@ impl StreamManager {
.unwrap();
}
}
StreamError::BackendSpecific { err: _ } | StreamError::BufferUnderrun => {}
// cpal::ErrorKind::BackendSpecific { err: _ } | StreamError::BufferUnderrun => {}
_ => {}
}
match handled_stream_error_producer.push(error) {
Ok(()) => {}
Expand Down Expand Up @@ -194,7 +195,7 @@ impl StreamManager {
device: &Device,
config: &mut StreamConfig,
num_stream_errors_discarded: Arc<AtomicU64>,
) -> Result<Consumer<StreamError>, Error> {
) -> Result<Consumer<cpal::Error>, Error> {
let mut renderer =
if let State::Idle { renderer } = std::mem::replace(&mut self.state, State::Empty) {
renderer
Expand All @@ -214,7 +215,7 @@ impl StreamManager {
RingBuffer::new(64);
let channels = config.channels;
let stream = device.build_output_stream(
config,
*config,
move |data: &mut [f32], _| {
#[cfg(feature = "assert_no_alloc")]
assert_no_alloc::assert_no_alloc(|| {
Expand Down
40 changes: 7 additions & 33 deletions crates/kira/src/backend/cpal/error.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,32 @@
use std::fmt::{Display, Formatter};

use cpal::{BuildStreamError, DefaultStreamConfigError, PlayStreamError};

/// Errors that can occur when using the cpal backend.
#[derive(Debug)]
pub enum Error {
/// A default audio output device could not be determined.
NoDefaultOutputDevice,
/// An error occurred when getting the default output configuration.
DefaultStreamConfigError(DefaultStreamConfigError),
/// An error occurred when building the audio stream.
BuildStreamError(BuildStreamError),
/// An error occurred when starting the audio stream.
PlayStreamError(PlayStreamError),
/// An error occurred in the cpal backend.
CpalError(cpal::Error),
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::NoDefaultOutputDevice => {
f.write_str("Cannot find the default audio output device")
}
Error::DefaultStreamConfigError(error) => error.fmt(f),
Error::BuildStreamError(error) => error.fmt(f),
Error::PlayStreamError(error) => error.fmt(f),
Error::CpalError(error) => error.fmt(f),
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::DefaultStreamConfigError(error) => Some(error),
Error::BuildStreamError(error) => Some(error),
Error::PlayStreamError(error) => Some(error),
Error::CpalError(error) => Some(error),
_ => None,
}
}
}

impl From<DefaultStreamConfigError> for Error {
fn from(v: DefaultStreamConfigError) -> Self {
Self::DefaultStreamConfigError(v)
}
}

impl From<BuildStreamError> for Error {
fn from(v: BuildStreamError) -> Self {
Self::BuildStreamError(v)
}
}

impl From<PlayStreamError> for Error {
fn from(v: PlayStreamError) -> Self {
Self::PlayStreamError(v)
impl From<cpal::Error> for Error {
fn from(v: cpal::Error) -> Self {
Self::CpalError(v)
}
}
41 changes: 18 additions & 23 deletions crates/kira/src/sound/static_sound/data/from_file.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io::Cursor;

use symphonia::core::io::{MediaSource, MediaSourceStream};
use symphonia::core::{codecs::CodecParameters, formats::TrackType};

use crate::sound::{
FromFileError, static_sound::StaticSoundSettings, symphonia::load_frames_from_buffer_ref,
Expand Down Expand Up @@ -37,41 +38,35 @@ impl StaticSoundData {
let codecs = symphonia::default::get_codecs();
let probe = symphonia::default::get_probe();
let mss = MediaSourceStream::new(media_source, Default::default());
let mut format_reader = probe
.format(
&Default::default(),
mss,
&Default::default(),
&Default::default(),
)?
.format;
let mut format_reader = probe.probe(
&Default::default(),
mss,
Default::default(),
Default::default(),
)?;
let default_track = format_reader
.default_track()
.default_track(TrackType::Audio)
.ok_or(FromFileError::NoDefaultTrack)?;
let default_track_id = default_track.id;
let codec_params = &default_track.codec_params;
let sample_rate = codec_params
let audio_params = match default_track.codec_params.as_ref() {
Some(CodecParameters::Audio(p)) => p,
_ => return Err(FromFileError::NoDefaultTrack),
};
let sample_rate = audio_params
.sample_rate
.ok_or(FromFileError::UnknownSampleRate)?;
let mut decoder = codecs.make(codec_params, &Default::default())?;
let mut decoder = codecs.make_audio_decoder(audio_params, &Default::default())?;
let mut frames = vec![];
loop {
match format_reader.next_packet() {
Ok(packet) => {
if default_track_id == packet.track_id() {
Ok(Some(packet)) => {
if default_track_id == packet.track_id {
let buffer = decoder.decode(&packet)?;
frames.append(&mut load_frames_from_buffer_ref(&buffer)?);
}
}
Err(error) => match error {
symphonia::core::errors::Error::IoError(error) => {
if error.kind() == std::io::ErrorKind::UnexpectedEof {
break;
}
return Err(symphonia::core::errors::Error::IoError(error).into());
}
error => return Err(error.into()),
},
Ok(None) => break,
Err(error) => return Err(error.into()),
}
}
Ok(Self {
Expand Down
57 changes: 30 additions & 27 deletions crates/kira/src/sound/streaming/decoder/symphonia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use crate::{
sound::{FromFileError, symphonia::load_frames_from_buffer_ref},
};
use symphonia::core::{
codecs::Decoder,
formats::{FormatReader, SeekMode, SeekTo},
codecs::{CodecParameters, audio::AudioDecoder},
formats::{FormatReader, SeekMode, SeekTo, TrackType, probe::Hint},
io::{MediaSource, MediaSourceStream},
probe::Hint,
units::Timestamp,
};

pub(crate) struct SymphoniaDecoder {
format_reader: Box<dyn FormatReader>,
decoder: Box<dyn Decoder>,
decoder: Box<dyn AudioDecoder>,
sample_rate: u32,
num_frames: usize,
track_id: u32,
Expand All @@ -24,28 +24,28 @@ impl SymphoniaDecoder {
let codecs = symphonia::default::get_codecs();
let probe = symphonia::default::get_probe();
let mss = MediaSourceStream::new(media_source, Default::default());
let format_reader = probe
.format(
&Hint::default(),
mss,
&Default::default(),
&Default::default(),
)?
.format;
let format_reader = probe.probe(
&Hint::default(),
mss,
Default::default(),
Default::default(),
)?;
let default_track = format_reader
.default_track()
.default_track(TrackType::Audio)
.ok_or(FromFileError::NoDefaultTrack)?;
let sample_rate = default_track
.codec_params
let audio_params = match default_track.codec_params.as_ref() {
Some(CodecParameters::Audio(p)) => p,
_ => return Err(FromFileError::NoDefaultTrack),
};
let sample_rate = audio_params
.sample_rate
.ok_or(FromFileError::UnknownSampleRate)?;
let num_frames = default_track
.codec_params
.n_frames
.num_frames
.ok_or(FromFileError::UnknownSampleRate)?
.try_into()
.expect("could not convert u64 into usize");
let decoder = codecs.make(&default_track.codec_params, &Default::default())?;
let decoder = codecs.make_audio_decoder(audio_params, &Default::default())?;
let track_id = default_track.id;
Ok(Self {
format_reader,
Expand All @@ -70,9 +70,10 @@ impl super::Decoder for SymphoniaDecoder {

fn decode(&mut self) -> Result<Vec<Frame>, Self::Error> {
let packet = loop {
let packet = self.format_reader.next_packet()?;
if self.track_id == packet.track_id() {
break packet;
match self.format_reader.next_packet()? {
Some(packet) if packet.track_id == self.track_id => break packet,
Some(_) => continue,
None => return Ok(vec![]),
}
};
let buffer = self.decoder.decode(&packet)?;
Expand All @@ -82,14 +83,16 @@ impl super::Decoder for SymphoniaDecoder {
fn seek(&mut self, index: usize) -> Result<usize, Self::Error> {
let seeked_to = self.format_reader.seek(
SeekMode::Accurate,
SeekTo::TimeStamp {
ts: index.try_into().expect("could not convert usize into u64"),
SeekTo::Timestamp {
ts: Timestamp::new(index as i64),
track_id: self.track_id,
},
)?;
Ok(seeked_to
.actual_ts
.try_into()
.expect("could not convert u64 into usize"))
let actual = seeked_to.actual_ts.get();
Ok(if actual < 0 {
0
} else {
(actual as usize).min(self.num_frames)
})
}
}
Loading