Skip to content

Commit

Permalink
core/audio: generalize owned / borrowed storage to SampleBuffer too
Browse files Browse the repository at this point in the history
  • Loading branch information
fengalin committed Feb 19, 2022
1 parent 053e960 commit 3025743
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 91 deletions.
5 changes: 4 additions & 1 deletion symphonia-check/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ fn get_next_audio_buf_best_effort(inst: &mut DecoderInstance) -> Result<()> {
}
}

fn copy_audio_buf_to_sample_buf(src: AudioBufferRef<'_>, dst: &mut Option<SampleBuffer<f32>>) {
fn copy_audio_buf_to_sample_buf(
src: AudioBufferRef<'_>,
dst: &mut Option<SampleBuffer<'static, f32>>,
) {
if dst.is_none() {
let spec = *src.spec();
let duration = src.capacity() as u64;
Expand Down
203 changes: 113 additions & 90 deletions symphonia-core/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,32 +689,122 @@ impl<S: Sample> Signal<S> for AudioBuffer<S> {
}
}

/// Owned or borrowed Storage.
enum Storage<'a, T> {
Owned(Vec<T>),
Borrowed(&'a mut [T]),
}

impl<'a, T> Storage<'a, T> {
/// Returns an immutable slice containing the entire storage content.
fn as_slice(&self) -> &[T] {
use Storage::*;
match self {
Owned(owned) => owned.as_slice(),
Borrowed(slice) => slice,
}
}

/// Returns a mutable slice containing the entire storage content.
fn as_mut_slice(&mut self) -> &mut [T] {
use Storage::*;
match self {
Owned(ref mut owned) => owned.as_mut_slice(),
Borrowed(ref mut slice) => slice,
}
}
}

impl<T> From<Vec<T>> for Storage<'static, T> {
fn from(owned: Vec<T>) -> Self {
Storage::Owned(owned)
}
}

impl<'a, T> From<&'a mut [T]> for Storage<'a, T> {
fn from(borrowed: &'a mut [T]) -> Self {
Storage::Borrowed(borrowed)
}
}

#[derive(Clone, Copy)]
struct RequiredCapacity<T> {
n_samples: usize,
n_bytes: usize,
sample_type: PhantomData<T>,
}

impl<T> RequiredCapacity<T> {
/// Computes the capacity required to store the samples.
// FIXME duration seems more like n_frames
// FIXME we could return a Result so that caller gets a chance to
// to handle the error and try with a less frames.
fn from(duration: Duration, spec: SignalSpec) -> Self {
use std::convert::TryInto;

// The total number of samples the buffer will store.
let n_samples = duration
.try_into()
.ok()
.and_then(|dur: usize| dur.checked_mul(spec.channels.count()))
.expect("duration too large");

// Practically speaking, it is not possible to allocate more than usize::MAX bytes of raw
// samples. Make sure we won't exceed this limit.
let n_bytes = n_samples.checked_mul(mem::size_of::<T>())
.expect("duration too large");

RequiredCapacity { n_samples, n_bytes, sample_type: PhantomData }
}
}

/// A `SampleBuffer`, is a sample oriented buffer. It is agnostic to the ordering/layout of samples
/// within the buffer. `SampleBuffer` is mean't for safely importing and exporting sample data to
/// and from Symphonia using the sample's in-memory data-type.
pub struct SampleBuffer<S: Sample> {
buf: Vec<S>,
pub struct SampleBuffer<'a, S: Sample> {
buf: Storage<'a, S>,
n_written: usize,
}

impl<S: Sample> SampleBuffer<S> {
impl<S: Sample> SampleBuffer<'static, S> {
/// Instantiate a new `SampleBuffer` using the specified signal specification and of the given
/// duration.
pub fn new(duration: Duration, spec: SignalSpec) -> SampleBuffer<S> {
// The number of channels * duration cannot exceed u64::MAX.
assert!(duration <= u64::MAX / spec.channels.count() as u64, "duration too large");

pub fn new(duration: Duration, spec: SignalSpec) -> SampleBuffer<'static, S> {
// The total number of samples the buffer will store.
let n_samples = duration * spec.channels.count() as u64;
let n_samples = RequiredCapacity::<S>::from(duration, spec).n_samples;

// Practically speaking, it is not possible to allocate more than usize::MAX bytes of
// samples. This assertion ensures the potential downcast of n_samples to usize below is
// safe.
assert!(n_samples <= (usize::MAX / mem::size_of::<S>()) as u64, "duration too large");
SampleBuffer { buf: vec![S::MID; n_samples].into(), n_written: 0 }
}
}

impl<'a, S: Sample> SampleBuffer<'a, S> {
/// Instantiates a new `RawSampleBuffer` based on the provided storage.
///
/// The provided `storage` must be large enough to received the target samples.
/// See [`Self::required_byte_capacity`].
// FIXME duration seems more like n_frames
pub fn from(
storage: &'a mut [S],
duration: Duration,
spec: SignalSpec,
) -> SampleBuffer<'a, S> {
// FIXME another approach could be to expect an exact sized slice.
let n_samples = RequiredCapacity::<S>::from(duration, spec).n_samples;
assert!(storage.len() >= n_samples, "Insufficient capacity in storage");

SampleBuffer { buf: vec![S::MID; n_samples as usize], n_written: 0 }
SampleBuffer { buf: storage.into(), n_written: 0 }
}

/// Computes the byte count required to store the raw samples.
// FIXME duration seems more like n_frames
// FIXME we could return a Result so that caller gets a chance to
// to handle the error and try with a less frames.
pub fn required_byte_capacity(duration: Duration, spec: SignalSpec) -> usize {
RequiredCapacity::<S>::from(duration, spec).n_bytes
}
}

impl<'a, S: Sample> SampleBuffer<'a, S> {
/// Gets the number of written samples.
pub fn len(&self) -> usize {
self.n_written
Expand All @@ -727,12 +817,12 @@ impl<S: Sample> SampleBuffer<S> {

/// Gets an immutable slice of all written samples.
pub fn samples(&self) -> &[S] {
&self.buf[..self.n_written]
&self.buf.as_slice()[..self.n_written]
}

/// Gets the maximum number of samples the `SampleBuffer` may store.
pub fn capacity(&self) -> usize {
self.buf.len()
self.buf.as_slice().len()
}

/// Copies all audio data from the source `AudioBufferRef` in planar channel order into the
Expand Down Expand Up @@ -772,7 +862,8 @@ impl<S: Sample> SampleBuffer<S> {
for ch in 0..n_channels {
let ch_slice = src.chan(ch);

for (dst, src) in self.buf[ch * n_frames..].iter_mut().zip(ch_slice) {
let dst_ch_iter = self.buf.as_mut_slice()[ch * n_frames..].iter_mut();
for (dst, src) in dst_ch_iter.zip(ch_slice) {
*dst = (*src).into_sample();
}
}
Expand Down Expand Up @@ -818,7 +909,8 @@ impl<S: Sample> SampleBuffer<S> {
for ch in 0..n_channels {
let ch_slice = src.chan(ch);

for (dst, src) in self.buf[ch..].iter_mut().step_by(n_channels).zip(ch_slice) {
let dst_ch_iter = self.buf.as_mut_slice()[ch..].iter_mut().step_by(n_channels);
for (dst, src) in dst_ch_iter.zip(ch_slice) {
*dst = (*src).into_sample();
}
}
Expand Down Expand Up @@ -956,88 +1048,22 @@ impl RawSample for f64 {
}
}

/// Raw Samples owned or borrowed Storage.
enum RawSampleStorage<'a, S: RawSample> {
Owned(Vec<S::RawType>),
Borrowed(&'a mut [S::RawType]),
}

impl<'a, S: RawSample> RawSampleStorage<'a, S> {
/// Computes the capacity required to store the raw samples.
// FIXME duration seems more like n_frames
// FIXME we could return a Result so that caller gets a chance to
// to handle the error and try with a less frames.
fn required_capacity(duration: Duration, spec: SignalSpec) -> RequiredCapacity {
use std::convert::TryInto;

// The total number of samples the buffer will store.
let n_samples = duration
.try_into()
.ok()
.and_then(|dur: usize| dur.checked_mul(spec.channels.count()))
.expect("duration too large");

// Practically speaking, it is not possible to allocate more than usize::MAX bytes of raw
// samples. Make sure we won't exceed this limit.
let n_bytes = n_samples.checked_mul(mem::size_of::<S::RawType>())
.expect("duration too large");

RequiredCapacity { n_samples, n_bytes }
}

/// Returns an immutable slice containing the entire storage content.
fn as_slice(&self) -> &[S::RawType] {
use RawSampleStorage::*;
match self {
Owned(owned) => owned.as_slice(),
Borrowed(slice) => slice,
}
}

/// Returns a mutable slice containing the entire storage content.
fn as_mut_slice(&mut self) -> &mut [S::RawType] {
use RawSampleStorage::*;
match self {
Owned(ref mut owned) => owned.as_mut_slice(),
Borrowed(ref mut slice) => slice,
}
}
}

impl<S: RawSample> From<Vec<S::RawType>> for RawSampleStorage<'static, S> {
fn from(owned: Vec<S::RawType>) -> Self {
RawSampleStorage::Owned(owned)
}
}

impl<'a, S: RawSample> From<&'a mut [S::RawType]> for RawSampleStorage<'a, S> {
fn from(borrowed: &'a mut [S::RawType]) -> Self {
RawSampleStorage::Borrowed(borrowed)
}
}

/// A `RawSampleBuffer`, is a byte-oriented sample buffer. All samples copied to this buffer are
/// converted into their packed data-type and stored as a stream of bytes. `RawSampleBuffer` is
/// mean't for safely importing and exporting sample data to and from Symphonia as raw bytes.
pub struct RawSampleBuffer<'a, S: Sample + RawSample> {
buf: RawSampleStorage<'a, S>,
buf: Storage<'a, S::RawType>,
n_written: usize,
// Might take your heart.
sample_format: PhantomData<S>,
}

struct RequiredCapacity {
n_samples: usize,
n_bytes: usize,
}

impl<S: Sample + RawSample> RawSampleBuffer<'static, S> {
/// Instantiates a new `RawSampleBuffer` using the specified signal specification and of the given
/// duration.
pub fn new(duration: Duration, spec: SignalSpec) -> RawSampleBuffer<'static, S> {
// The total number of samples the buffer will store.
let RequiredCapacity { n_samples, .. } =
RawSampleStorage::<'static, S>::required_capacity(duration, spec);
let n_samples = RequiredCapacity::<S::RawType>::from(duration, spec).n_samples;

// Allocate enough memory for all the samples and fill the buffer with silence.
let buf = vec![S::MID.into_raw_sample(); n_samples];
Expand All @@ -1058,8 +1084,7 @@ impl<'a, S: Sample + RawSample> RawSampleBuffer<'a, S> {
spec: SignalSpec,
) -> RawSampleBuffer<'a, S> {
// FIXME another approach could be to expect an exact sized slice.
let RequiredCapacity { n_samples, .. } =
RawSampleStorage::<'a, S>::required_capacity(duration, spec);
let n_samples = RequiredCapacity::<S::RawType>::from(duration, spec).n_samples;
assert!(storage.len() >= n_samples, "Insufficient capacity in storage");

RawSampleBuffer { buf: storage.into(), n_written: 0, sample_format: PhantomData }
Expand All @@ -1070,9 +1095,7 @@ impl<'a, S: Sample + RawSample> RawSampleBuffer<'a, S> {
// FIXME we could return a Result so that caller gets a chance to
// to handle the error and try with a less frames.
pub fn required_byte_capacity(duration: Duration, spec: SignalSpec) -> usize {
let RequiredCapacity { n_bytes, .. } =
RawSampleStorage::<'a, S>::required_capacity(duration, spec);
n_bytes
RequiredCapacity::<S::RawType>::from(duration, spec).n_bytes
}
}

Expand Down

0 comments on commit 3025743

Please sign in to comment.