Make silence more efficient

This commit is contained in:
Imbris 2020-01-25 11:09:54 -05:00 committed by est31
parent 2c7d171670
commit 9c874b7690
2 changed files with 36 additions and 19 deletions

View file

@ -206,7 +206,7 @@ where
if next.len() == 0 {
if self.input.keep_alive_if_empty.load(Ordering::Acquire) {
// Play a short silence in order to avoid spinlocking.
let silence = Zero::<S>::new(1, 44000); // TODO: meh
let silence = Zero::<S>::new(1, 44100); // TODO: meh
(
Box::new(silence.take_duration(Duration::from_millis(10))) as Box<_>,
None,

View file

@ -4,8 +4,14 @@ use Sample;
use Source;
/// Internal function that builds a `TakeDuration` object.
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I> {
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
where
I: Source,
I::Item: Sample,
{
TakeDuration {
current_frame_len: input.current_frame_len(),
duration_per_sample: TakeDuration::get_duration_per_sample(&input),
input: input,
remaining_duration: duration,
requested_duration: duration,
@ -35,6 +41,8 @@ impl DurationFilter
}
}
const NANOS_PER_SEC: u64 = 1_000_000_000;
/// A source that repeats the given source.
#[derive(Clone, Debug)]
pub struct TakeDuration<I> {
@ -42,6 +50,10 @@ pub struct TakeDuration<I> {
remaining_duration: Duration,
requested_duration: Duration,
filter: Option<DurationFilter>,
// Remaining samples in current frame.
current_frame_len: Option<usize>,
// Only updated when the current frame len is exausted.
duration_per_sample: Duration,
}
impl<I> TakeDuration<I>
@ -51,8 +63,8 @@ where
{
/// Returns the duration elapsed for each sample extracted.
#[inline]
fn get_duration_per_sample(&self) -> Duration {
let ns = 1000000000 / (self.input.sample_rate() as u64 * self.channels() as u64);
fn get_duration_per_sample(input: &I) -> Duration {
let ns = NANOS_PER_SEC / input.sample_rate() as u64 * input.channels() as u64;
// \|/ the maximum value of `ns` is one billion, so this can't fail
Duration::new(0, ns as u32)
}
@ -92,9 +104,17 @@ where
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<<I as Iterator>::Item> {
let duration_per_sample = self.get_duration_per_sample();
if let Some(frame_len) = self.current_frame_len.take() {
if frame_len > 0 {
self.current_frame_len = Some(frame_len - 1);
} else {
self.current_frame_len = self.input.current_frame_len();
// Sample rate might have changed
self.duration_per_sample = Self::get_duration_per_sample(&self.input);
}
}
if self.remaining_duration <= duration_per_sample {
if self.remaining_duration <= self.duration_per_sample {
None
} else {
if let Some(sample) = self.input.next() {
@ -102,7 +122,9 @@ where
Some(filter) => filter.apply(sample, &self),
None => sample,
};
self.remaining_duration = self.remaining_duration - duration_per_sample;
self.remaining_duration = self.remaining_duration - self.duration_per_sample;
Some(sample)
} else {
None
@ -120,20 +142,15 @@ where
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
let remaining_nanosecs = self.remaining_duration.as_secs() * 1000000000
let remaining_nanos = self.remaining_duration.as_secs() * NANOS_PER_SEC
+ self.remaining_duration.subsec_nanos() as u64;
let remaining_samples = remaining_nanosecs * self.input.sample_rate() as u64
* self.channels() as u64 / 1000000000;
let nanos_per_sample = self.duration_per_sample.as_secs() * NANOS_PER_SEC
+ self.duration_per_sample.subsec_nanos() as u64;
let remaining_samples = (remaining_nanos / nanos_per_sample) as usize;
if let Some(value) = self.input.current_frame_len() {
if (value as u64) < remaining_samples {
Some(value)
} else {
Some(remaining_samples as usize)
}
} else {
Some(remaining_samples as usize)
}
self.input.current_frame_len()
.filter(|value| *value < remaining_samples)
.or(Some(remaining_samples))
}
#[inline]