Revert extra changes for merge request

This commit is contained in:
Petr Gladkikh 2024-11-15 03:11:48 +04:00
parent ebc673e315
commit b720273847
6 changed files with 99 additions and 25 deletions

View file

@ -259,6 +259,12 @@ mod test {
use cpal::SampleRate; use cpal::SampleRate;
use quickcheck::quickcheck; 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! { quickcheck! {
/// Check that resampling an empty input produces no output. /// Check that resampling an empty input produces no output.
fn empty(from: u32, to: u32, n: u16) -> () { fn empty(from: u32, to: u32, n: u16) -> () {
@ -289,9 +295,9 @@ mod test {
/// Check that dividing the sample rate by k (integer) is the same as /// Check that dividing the sample rate by k (integer) is the same as
/// dropping a sample from each channel. /// dropping a sample from each channel.
fn divide_sample_rate(to: u32, k: u32, input: Vec<u16>, n: u16) -> () { 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 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. // Truncate the input, so it contains an integer number of frames.
let input = { let input = {
@ -313,9 +319,9 @@ mod test {
/// Check that, after multiplying the sample rate by k, every k-th /// Check that, after multiplying the sample rate by k, every k-th
/// sample in the output matches exactly with the input. /// sample in the output matches exactly with the input.
fn multiply_sample_rate(from: u32, k: u32, input: Vec<u16>, n: u16) -> () { 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 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. // Truncate the input, so it contains an integer number of frames.
let input = { let input = {

View file

@ -1,4 +1,5 @@
//! Mixer that plays multiple sounds at the same time. //! Mixer that plays multiple sounds at the same time.
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
@ -27,6 +28,8 @@ where
current_sources: Vec::with_capacity(16), current_sources: Vec::with_capacity(16),
input: input.clone(), input: input.clone(),
sample_count: 0, sample_count: 0,
still_pending: vec![],
still_current: vec![],
}; };
(input, output) (input, output)
@ -51,8 +54,10 @@ where
T: Source<Item = S> + Send + 'static, T: Source<Item = S> + Send + 'static,
{ {
let uniform_source = UniformSourceIterator::new(source, self.channels, self.sample_rate); let uniform_source = UniformSourceIterator::new(source, self.channels, self.sample_rate);
let mut pending = self.pending_sources.lock().unwrap(); self.pending_sources
pending.push(Box::new(uniform_source) as Box<_>); .lock()
.unwrap()
.push(Box::new(uniform_source) as Box<_>);
self.has_pending.store(true, Ordering::SeqCst); // TODO: can we relax this ordering? self.has_pending.store(true, Ordering::SeqCst); // TODO: can we relax this ordering?
} }
} }
@ -67,6 +72,12 @@ pub struct MixerSource<S> {
// The number of samples produced so far. // The number of samples produced so far.
sample_count: usize, 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> impl<S> Source for MixerSource<S>
@ -168,16 +179,18 @@ where
// in-step with the modulo of the samples produced so far. Otherwise, the // 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. // sound will play on the wrong channels, e.g. left / right will be reversed.
fn start_pending_sources(&mut self) { fn start_pending_sources(&mut self) {
let mut pending = self.input.pending_sources.lock().unwrap(); let mut pending = self.input.pending_sources.lock().unwrap(); // TODO: relax ordering?
let mut i = 0;
while i < pending.len() { for source in pending.drain(..) {
let in_step = self.sample_count % pending[i].channels() as usize == 0; let in_step = self.sample_count % source.channels() as usize == 0;
if in_step { if in_step {
self.current_sources.push(pending.swap_remove(i)); self.current_sources.push(source);
} else { } else {
i += 1; self.still_pending.push(source);
} }
} }
std::mem::swap(&mut self.still_pending, &mut pending);
let has_pending = !pending.is_empty(); let has_pending = !pending.is_empty();
self.input.has_pending.store(has_pending, Ordering::SeqCst); // TODO: relax ordering? self.input.has_pending.store(has_pending, Ordering::SeqCst); // TODO: relax ordering?
@ -185,15 +198,15 @@ where
fn sum_current_sources(&mut self) -> S { fn sum_current_sources(&mut self) -> S {
let mut sum = S::zero_value(); let mut sum = S::zero_value();
let mut i = 0;
while i < self.current_sources.len() { for mut source in self.current_sources.drain(..) {
if let Some(value) = self.current_sources[i].next() { if let Some(value) = source.next() {
sum = sum.saturating_add(value); sum = sum.saturating_add(value);
i += 1; self.still_current.push(source);
} else {
self.current_sources.swap_remove(i);
} }
} }
std::mem::swap(&mut self.still_current, &mut self.current_sources);
sum sum
} }
} }

View file

@ -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 /// 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 /// the number of samples that remain in the iterator before the samples rate and number of
/// channels can potentially change. /// channels can potentially change.
///
pub trait Source: Iterator pub trait Source: Iterator
where where
Self::Item: Sample, Self::Item: Sample,

View file

@ -41,9 +41,9 @@ where
target_sample_rate: u32, target_sample_rate: u32,
) -> UniformSourceIterator<I, D> { ) -> UniformSourceIterator<I, D> {
let total_duration = input.total_duration(); 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), inner: Some(input),
target_channels, target_channels,
target_sample_rate, target_sample_rate,
@ -102,7 +102,8 @@ where
.into_inner() .into_inner()
.iter; .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(); let value = input.next();
self.inner = Some(input); self.inner = Some(input);

View file

@ -96,10 +96,20 @@ impl SpatialSink {
self.sink.set_volume(value); 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 /// # Note:
/// change the play speed of the sound. /// 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] #[inline]
pub fn speed(&self) -> f32 { pub fn speed(&self) -> f32 {
self.sink.speed() self.sink.speed()
@ -138,6 +148,14 @@ impl SpatialSink {
self.sink.is_paused() 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. /// Stops the sink by emptying the queue.
#[inline] #[inline]
pub fn stop(&self) { pub fn stop(&self) {
@ -163,8 +181,43 @@ impl SpatialSink {
} }
/// Returns the number of sounds currently in the queue. /// Returns the number of sounds currently in the queue.
#[allow(clippy::len_without_is_empty)]
#[inline] #[inline]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.sink.len() 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()
}
} }

View file

@ -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. /// If this is dropped, playback will end, and the associated output stream will be disposed.
pub struct OutputStream { pub struct OutputStream {
_stream: cpal::Stream,
mixer: Arc<Mixer<f32>>, mixer: Arc<Mixer<f32>>,
_stream: cpal::Stream,
} }
impl OutputStream { impl OutputStream {