Skip to content

Commit

Permalink
Revert extra changes for merge request
Browse files Browse the repository at this point in the history
  • Loading branch information
PetrGlad committed Nov 14, 2024
1 parent ebc673e commit b720273
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 25 deletions.
14 changes: 10 additions & 4 deletions src/conversions/sample_rate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ mod test {
use cpal::SampleRate;
use quickcheck::quickcheck;

// TODO: Remove once cpal 0.12.2 is released and the dependency is updated
// (cpal#483 implemented ops::Mul on SampleRate)
const fn multiply_rate(r: SampleRate, k: u32) -> SampleRate {
SampleRate(k * r.0)
}

quickcheck! {
/// Check that resampling an empty input produces no output.
fn empty(from: u32, to: u32, n: u16) -> () {
Expand Down Expand Up @@ -289,9 +295,9 @@ mod test {
/// Check that dividing the sample rate by k (integer) is the same as
/// dropping a sample from each channel.
fn divide_sample_rate(to: u32, k: u32, input: Vec<u16>, n: u16) -> () {
if k == 0 || n == 0 || to.checked_mul(k).is_none() { return; }
let to = if to == 0 { return; } else { SampleRate(to) };
let from = to * k;
let from = multiply_rate(to, k);
if k == 0 || n == 0 { return; }

// Truncate the input, so it contains an integer number of frames.
let input = {
Expand All @@ -313,9 +319,9 @@ mod test {
/// Check that, after multiplying the sample rate by k, every k-th
/// sample in the output matches exactly with the input.
fn multiply_sample_rate(from: u32, k: u32, input: Vec<u16>, n: u16) -> () {
if k == 0 || n == 0 || from.checked_mul(k).is_none() { return; }
let from = if from == 0 { return; } else { SampleRate(from) };
let to = from * k;
let to = multiply_rate(from, k);
if k == 0 || n == 0 { return; }

// Truncate the input, so it contains an integer number of frames.
let input = {
Expand Down
41 changes: 27 additions & 14 deletions src/dynamic_mixer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Mixer that plays multiple sounds at the same time.

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
Expand Down Expand Up @@ -27,6 +28,8 @@ where
current_sources: Vec::with_capacity(16),
input: input.clone(),
sample_count: 0,
still_pending: vec![],
still_current: vec![],
};

(input, output)
Expand All @@ -51,8 +54,10 @@ where
T: Source<Item = S> + Send + 'static,
{
let uniform_source = UniformSourceIterator::new(source, self.channels, self.sample_rate);
let mut pending = self.pending_sources.lock().unwrap();
pending.push(Box::new(uniform_source) as Box<_>);
self.pending_sources
.lock()
.unwrap()
.push(Box::new(uniform_source) as Box<_>);
self.has_pending.store(true, Ordering::SeqCst); // TODO: can we relax this ordering?
}
}
Expand All @@ -67,6 +72,12 @@ pub struct MixerSource<S> {

// The number of samples produced so far.
sample_count: usize,

// A temporary vec used in start_pending_sources.
still_pending: Vec<Box<dyn Source<Item = S> + Send>>,

// A temporary vec used in sum_current_sources.
still_current: Vec<Box<dyn Source<Item = S> + Send>>,
}

impl<S> Source for MixerSource<S>
Expand Down Expand Up @@ -168,32 +179,34 @@ where
// in-step with the modulo of the samples produced so far. Otherwise, the
// sound will play on the wrong channels, e.g. left / right will be reversed.
fn start_pending_sources(&mut self) {
let mut pending = self.input.pending_sources.lock().unwrap();
let mut i = 0;
while i < pending.len() {
let in_step = self.sample_count % pending[i].channels() as usize == 0;
let mut pending = self.input.pending_sources.lock().unwrap(); // TODO: relax ordering?

for source in pending.drain(..) {
let in_step = self.sample_count % source.channels() as usize == 0;

if in_step {
self.current_sources.push(pending.swap_remove(i));
self.current_sources.push(source);
} else {
i += 1;
self.still_pending.push(source);
}
}
std::mem::swap(&mut self.still_pending, &mut pending);

let has_pending = !pending.is_empty();
self.input.has_pending.store(has_pending, Ordering::SeqCst); // TODO: relax ordering?
}

fn sum_current_sources(&mut self) -> S {
let mut sum = S::zero_value();
let mut i = 0;
while i < self.current_sources.len() {
if let Some(value) = self.current_sources[i].next() {

for mut source in self.current_sources.drain(..) {
if let Some(value) = source.next() {
sum = sum.saturating_add(value);
i += 1;
} else {
self.current_sources.swap_remove(i);
self.still_current.push(source);
}
}
std::mem::swap(&mut self.still_current, &mut self.current_sources);

sum
}
}
Expand Down
1 change: 1 addition & 0 deletions src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ pub use self::noise::{pink, white, PinkNoise, WhiteNoise};
/// In order to properly handle this situation, the `current_frame_len()` method should return
/// the number of samples that remain in the iterator before the samples rate and number of
/// channels can potentially change.
///
pub trait Source: Iterator
where
Self::Item: Sample,
Expand Down
7 changes: 4 additions & 3 deletions src/source/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ where
target_sample_rate: u32,
) -> UniformSourceIterator<I, D> {
let total_duration = input.total_duration();
let input = Self::bootstrap(input, target_channels, target_sample_rate);
let input = UniformSourceIterator::bootstrap(input, target_channels, target_sample_rate);

Self {
UniformSourceIterator {
inner: Some(input),
target_channels,
target_sample_rate,
Expand Down Expand Up @@ -102,7 +102,8 @@ where
.into_inner()
.iter;

let mut input = Self::bootstrap(input, self.target_channels, self.target_sample_rate);
let mut input =
UniformSourceIterator::bootstrap(input, self.target_channels, self.target_sample_rate);

let value = input.next();
self.inner = Some(input);
Expand Down
59 changes: 56 additions & 3 deletions src/spatial_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,20 @@ impl SpatialSink {
self.sink.set_volume(value);
}

/// Gets the speed of the sound.
/// Changes the play speed of the sound. Does not adjust the samples, only the playback speed.
///
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0` will
/// change the play speed of the sound.
/// # Note:
/// 1. **Increasing the speed will increase the pitch by the same factor**
/// - If you set the speed to 0.5 this will halve the frequency of the sound
/// lowering its pitch.
/// - If you set the speed to 2 the frequency will double raising the
/// pitch of the sound.
/// 2. **Change in the speed affect the total duration inversely**
/// - If you set the speed to 0.5, the total duration will be twice as long.
/// - If you set the speed to 2 the total duration will be halve of what it
/// was.
///
/// See [`Speed`] for details
#[inline]
pub fn speed(&self) -> f32 {
self.sink.speed()
Expand Down Expand Up @@ -138,6 +148,14 @@ impl SpatialSink {
self.sink.is_paused()
}

/// Removes all currently loaded `Source`s from the `SpatialSink` and pauses it.
///
/// See `pause()` for information about pausing a `Sink`.
#[inline]
pub fn clear(&self) {
self.sink.clear();
}

/// Stops the sink by emptying the queue.
#[inline]
pub fn stop(&self) {
Expand All @@ -163,8 +181,43 @@ impl SpatialSink {
}

/// Returns the number of sounds currently in the queue.
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.sink.len()
}

/// Attempts to seek to a given position in the current source.
///
/// This blocks between 0 and ~5 milliseconds.
///
/// As long as the duration of the source is known seek is guaranteed to saturate
/// at the end of the source. For example given a source that reports a total duration
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds.
///
/// # Errors
/// This function will return [`SeekError::NotSupported`] if one of the underlying
/// sources does not support seeking.
///
/// It will return an error if an implementation ran
/// into one during the seek.
///
/// When seeking beyond the end of a source this
/// function might return an error if the duration of the source is not known.
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos)
}

/// Returns the position of the sound that's being played.
///
/// This takes into account any speedup or delay applied.
///
/// Example: if you apply a speedup of *2* to an mp3 decoder source and
/// [`get_pos()`](Sink::get_pos) returns *5s* then the position in the mp3
/// recording is *10s* from its start.
#[inline]
pub fn get_pos(&self) -> Duration {
self.sink.get_pos()
}
}
2 changes: 1 addition & 1 deletion src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100);
///
/// If this is dropped, playback will end, and the associated output stream will be disposed.
pub struct OutputStream {
_stream: cpal::Stream,
mixer: Arc<Mixer<f32>>,
_stream: cpal::Stream,
}

impl OutputStream {
Expand Down

0 comments on commit b720273

Please sign in to comment.