Skip to content

Commit

Permalink
add range methods to Channel and Buf
Browse files Browse the repository at this point in the history
  • Loading branch information
Be-ing committed Dec 11, 2022
1 parent 567691c commit 9685483
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 0 deletions.
52 changes: 52 additions & 0 deletions audio-core/src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub use self::skip::Skip;
mod limit;
pub use self::limit::Limit;

mod range;
pub use self::range::Range;

mod tail;
pub use self::tail::Tail;

Expand Down Expand Up @@ -282,6 +285,55 @@ pub trait Buf {
{
Limit::new(self, limit)
}

/// Construct a new buffer limited to the specified range of frames of the
/// original buffer.
///
/// # Examples
///
/// ```
/// use audio::Buf;
///
/// let seq = audio::sequential![[1, 2, 3, 4]; 1];
/// let seq_unbounded = seq.range(..);
/// assert_eq!(seq_unbounded.get_channel(0).unwrap().as_ref(), [1, 2, 3, 4]);
/// let seq_limited = seq_unbounded.range(1..3);
/// assert_eq!(seq_limited.get_channel(0).unwrap().as_ref(), [2, 3]);
///
/// let interleaved = audio::interleaved![[1, 2, 3, 4]; 1];
/// let interleaved_unbounded = interleaved.range(..);
/// assert_eq!(interleaved_unbounded.get_channel(0).unwrap().get(0).unwrap(), 1);
/// assert_eq!(interleaved_unbounded.get_channel(0).unwrap().get(1).unwrap(), 2);
/// assert_eq!(interleaved_unbounded.get_channel(0).unwrap().get(2).unwrap(), 3);
/// assert_eq!(interleaved_unbounded.get_channel(0).unwrap().get(3).unwrap(), 4);
/// let interleaved_limited = interleaved_unbounded.range(1..3);
/// assert_eq!(interleaved_limited.get_channel(0).unwrap().get(0).unwrap(), 2);
/// assert_eq!(interleaved_limited.get_channel(0).unwrap().get(1).unwrap(), 3);
/// ```
///
/// # Panics
///
/// Panics if the end is out of bounds or [frames_hint][Buf::frames_hint] returns [None].
///
/// ```should_panic
/// use audio::Buf;
///
/// let seq = audio::sequential![[1, 2, 3, 4]; 1];
/// let seq_range = seq.range(..5);
/// ```
///
/// ```should_panic
/// use audio::Buf;
///
/// let interleaved = audio::interleaved![[1, 2, 3, 4]; 1];
/// let interleaved_range = interleaved.range(..5);
/// ```
fn range(self, range: impl core::ops::RangeBounds<usize>) -> Range<Self>
where
Self: Sized,
{
Range::new(self, range)
}
}

impl<B> Buf for &B
Expand Down
213 changes: 213 additions & 0 deletions audio-core/src/buf/range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
use crate::{Buf, BufMut, Channel, ChannelMut, ExactSizeBuf, ReadBuf};

use core::ops;
use core::ops::Bound;

/// A range of a buffer
///
/// See [Buf::range].
pub struct Range<B> {
buf: B,
range: ops::Range<usize>,
}

impl<B> Range<B> {
/// Construct a new limited buffer.
pub(crate) fn new(buf: B, range: impl ops::RangeBounds<usize>) -> Self
where B: Buf {
let start = match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(&i) => i,
Bound::Excluded(&i) => i + 1,
};
let max = buf.frames_hint().expect("Unable to check bounds of range for Buf because frames_hint returned None");
let end = match range.end_bound() {
Bound::Unbounded => max,
Bound::Included(&i) => i + 1,
Bound::Excluded(&i) => i,
};
assert!(end <= max, "End index {} out of bounds, maximum {}", end, max);
assert!(start <= end);
let range = core::ops::Range{ start, end };
Self { buf, range }
}
}

impl<B> Buf for Range<B>
where
B: Buf,
{
type Sample = B::Sample;

type Channel<'this> = B::Channel<'this>
where
Self: 'this;

type IterChannels<'this> = IterChannels<B::IterChannels<'this>>
where
Self: 'this;

fn frames_hint(&self) -> Option<usize> {
Some(self.range.len())
}

fn channels(&self) -> usize {
self.buf.channels()
}

fn get_channel(&self, channel: usize) -> Option<Self::Channel<'_>> {
Some(self.buf.get_channel(channel)?.range(self.range.clone()))
}

fn iter_channels(&self) -> Self::IterChannels<'_> {
IterChannels {
iter: self.buf.iter_channels(),
range: self.range.clone(),
}
}
}

impl<B> BufMut for Range<B>
where
B: BufMut,
{
type ChannelMut<'this> = B::ChannelMut<'this>
where
Self: 'this;

type IterChannelsMut<'this> = IterChannelsMut<B::IterChannelsMut<'this>>
where
Self: 'this;

fn get_channel_mut(&mut self, channel: usize) -> Option<Self::ChannelMut<'_>> {
Some(self.buf.get_channel_mut(channel)?.range(self.range.clone()))
}

fn copy_channel(&mut self, from: usize, to: usize)
where
Self::Sample: Copy,
{
self.buf.copy_channel(from, to);
}

fn iter_channels_mut(&mut self) -> Self::IterChannelsMut<'_> {
IterChannelsMut {
iter: self.buf.iter_channels_mut(),
range: self.range.clone(),
}
}
}

/// [Range] adjusts the implementation of [ExactSizeBuf] to take the frame
/// limiting into account.
///
/// ```
/// use audio::{Buf, ExactSizeBuf};
///
/// let buf = audio::interleaved![[0; 4]; 2];
///
/// assert_eq!((&buf).limit(0).frames(), 0);
/// assert_eq!((&buf).limit(1).frames(), 1);
/// assert_eq!((&buf).limit(5).frames(), 4);
/// ```
impl<B> ExactSizeBuf for Range<B>
where
B: ExactSizeBuf,
{
fn frames(&self) -> usize {
self.range.len()
}
}

impl<B> ReadBuf for Range<B>
where
B: ReadBuf,
{
fn remaining(&self) -> usize {
usize::min(self.buf.remaining(), self.range.len())
}

fn advance(&mut self, n: usize) {
self.buf.advance(usize::min(n, self.range.len()));
}
}

// TODO: fix macro
// iterators!(range: core::ops::Range<usize> => self.range(range.clone(());

pub struct IterChannels<I> {
iter: I,
range: core::ops::Range<usize>,
}
impl<I> Iterator for IterChannels<I>
where
I: Iterator,
I::Item: Channel,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
Some(self.iter.next()?.range(self.range.clone()))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
Some(self.iter.nth(n)?.range(self.range.clone()))
}
}
impl<I> DoubleEndedIterator for IterChannels<I>
where
I: DoubleEndedIterator,
I::Item: Channel,
{
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.iter.next_back()?.range(self.range.clone()))
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
Some(self.iter.nth_back(n)?.range(self.range.clone()))
}
}
impl<I> ExactSizeIterator for IterChannels<I>
where
I: ExactSizeIterator,
I::Item: ChannelMut,
{
fn len(&self) -> usize {
self.iter.len()
}
}
pub struct IterChannelsMut<I> {
iter: I,
range: core::ops::Range<usize>,
}
impl<I> Iterator for IterChannelsMut<I>
where
I: Iterator,
I::Item: ChannelMut,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
Some(self.iter.next()?.range(self.range.clone()))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
Some(self.iter.nth(n)?.range(self.range.clone()))
}
}
impl<I> DoubleEndedIterator for IterChannelsMut<I>
where
I: DoubleEndedIterator,
I::Item: ChannelMut,
{
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.iter.next_back()?.range(self.range.clone()))
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
Some(self.iter.nth_back(n)?.range(self.range.clone()))
}
}
impl<I> ExactSizeIterator for IterChannelsMut<I>
where
I: ExactSizeIterator,
I::Item: ChannelMut,
{
fn len(&self) -> usize {
self.iter.len()
}
}
47 changes: 47 additions & 0 deletions audio-core/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,51 @@ pub trait Channel {
/// assert_eq!(to.as_slice(), &[1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
/// ```
fn limit(self, limit: usize) -> Self;

/// Construct a channel using the specified range of frames
///
/// # Examples
///
/// ```
/// use audio::Channel;
///
/// let seq = audio::sequential![[1, 2, 3, 4]; 1];
/// let seq_channel = seq.get_channel(0).unwrap();
/// let seq_unbounded = seq_channel.range(..);
/// assert_eq!(seq_unbounded.as_ref(), [1, 2, 3, 4]);
/// let seq_limited = seq_unbounded.range(1..3);
/// assert_eq!(seq_limited.as_ref(), [2, 3]);
///
/// let interleaved = audio::interleaved![[1, 2, 3, 4]; 1];
/// let interleaved_channel = interleaved.get_channel(0).unwrap();
/// let interleaved_unbounded = interleaved_channel.range(..);
/// assert_eq!(interleaved_unbounded.get(0).unwrap(), 1);
/// assert_eq!(interleaved_unbounded.get(1).unwrap(), 2);
/// assert_eq!(interleaved_unbounded.get(2).unwrap(), 3);
/// assert_eq!(interleaved_unbounded.get(3).unwrap(), 4);
/// let interleaved_limited = interleaved_unbounded.range(1..3);
/// assert_eq!(interleaved_limited.get(0).unwrap(), 2);
/// assert_eq!(interleaved_limited.get(1).unwrap(), 3);
/// ```
///
/// # Panics
///
/// Panics if the end index is out of bounds.
///
/// ```should_panic
/// use audio::Channel;
///
/// let seq = audio::sequential![[1, 2, 3, 4]; 1];
/// let seq_channel = seq.get_channel(0).unwrap();
/// let seq_range = seq_channel.range(..5);
/// ```
///
/// ```should_panic
/// use audio::Channel;
///
/// let interleaved = audio::interleaved![[1, 2, 3, 4]; 1];
/// let interleaved_channel = interleaved.get_channel(0).unwrap();
/// let interleaved_range = interleaved_channel.range(..5);
/// ```
fn range(self, range: impl core::ops::RangeBounds<usize>) -> Self;
}
43 changes: 43 additions & 0 deletions audio/src/channel/interleaved/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,49 @@ macro_rules! interleaved_channel {
self
}

fn range(self, range: impl core::ops::RangeBounds<usize>) -> Self {
// Hack around trait method taking `self` rather than `mut self`.
// This method returns a Self by value, so it is okay to move self
// into a mut variable.
let mut new = self;

let start_index = match range.start_bound() {
core::ops::Bound::Unbounded => 0,
core::ops::Bound::Included(&i) => i,
core::ops::Bound::Excluded(&i) => i + 1,
};
let original_length = len!(new);
let end_index = match range.end_bound() {
core::ops::Bound::Unbounded => original_length,
core::ops::Bound::Included(&i) => {
let end_index = i + 1;
assert!(end_index <= original_length);
end_index
},
core::ops::Bound::Excluded(&i) => {
assert!(i <= original_length);
i
},
};

if mem::size_of::<T>() == 0 {
let len = usize::min(original_length, end_index - start_index);
zst_set_len!(new, len);
} else {
let start_ptr_offset = usize::min(original_length, start_index).saturating_mul(new.step);
// Safety: internal invariants in this structure ensures it
// doesn't go out of bounds.
new.ptr = unsafe { ptr::NonNull::new_unchecked(new.ptr.as_ptr().wrapping_add(start_ptr_offset)) };

let end_ptr_offset = original_length.saturating_sub(end_index).saturating_mul(new.step);
// Safety: internal invariants in this structure ensures it
// doesn't go out of bounds.
new.end = new.end.wrapping_sub(end_ptr_offset);
}

new
}

fn try_as_linear(&self) -> Option<&[T]> {
None
}
Expand Down
Loading

0 comments on commit 9685483

Please sign in to comment.