From 96854837ec0cf4e85b73d676a5955b478f5118c7 Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 11 Dec 2022 13:03:15 -0600 Subject: [PATCH] add range methods to Channel and Buf --- audio-core/src/buf.rs | 52 ++++++ audio-core/src/buf/range.rs | 213 ++++++++++++++++++++++++ audio-core/src/channel.rs | 47 ++++++ audio/src/channel/interleaved/macros.rs | 43 +++++ audio/src/channel/linear.rs | 45 +++++ 5 files changed, 400 insertions(+) create mode 100644 audio-core/src/buf/range.rs diff --git a/audio-core/src/buf.rs b/audio-core/src/buf.rs index 5f8a169..10611ee 100644 --- a/audio-core/src/buf.rs +++ b/audio-core/src/buf.rs @@ -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; @@ -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) -> Range + where + Self: Sized, + { + Range::new(self, range) + } } impl Buf for &B diff --git a/audio-core/src/buf/range.rs b/audio-core/src/buf/range.rs new file mode 100644 index 0000000..8b93942 --- /dev/null +++ b/audio-core/src/buf/range.rs @@ -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 { + buf: B, + range: ops::Range, +} + +impl Range { + /// Construct a new limited buffer. + pub(crate) fn new(buf: B, range: impl ops::RangeBounds) -> 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 Buf for Range +where + B: Buf, +{ + type Sample = B::Sample; + + type Channel<'this> = B::Channel<'this> + where + Self: 'this; + + type IterChannels<'this> = IterChannels> + where + Self: 'this; + + fn frames_hint(&self) -> Option { + Some(self.range.len()) + } + + fn channels(&self) -> usize { + self.buf.channels() + } + + fn get_channel(&self, channel: usize) -> Option> { + 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 BufMut for Range +where + B: BufMut, +{ + type ChannelMut<'this> = B::ChannelMut<'this> + where + Self: 'this; + + type IterChannelsMut<'this> = IterChannelsMut> + where + Self: 'this; + + fn get_channel_mut(&mut self, channel: usize) -> Option> { + 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 ExactSizeBuf for Range +where + B: ExactSizeBuf, +{ + fn frames(&self) -> usize { + self.range.len() + } +} + +impl ReadBuf for Range +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 => self.range(range.clone(()); + +pub struct IterChannels { + iter: I, + range: core::ops::Range, +} +impl Iterator for IterChannels +where + I: Iterator, + I::Item: Channel, +{ + type Item = I::Item; + fn next(&mut self) -> Option { + Some(self.iter.next()?.range(self.range.clone())) + } + fn nth(&mut self, n: usize) -> Option { + Some(self.iter.nth(n)?.range(self.range.clone())) + } +} +impl DoubleEndedIterator for IterChannels +where + I: DoubleEndedIterator, + I::Item: Channel, +{ + fn next_back(&mut self) -> Option { + Some(self.iter.next_back()?.range(self.range.clone())) + } + fn nth_back(&mut self, n: usize) -> Option { + Some(self.iter.nth_back(n)?.range(self.range.clone())) + } +} +impl ExactSizeIterator for IterChannels +where + I: ExactSizeIterator, + I::Item: ChannelMut, +{ + fn len(&self) -> usize { + self.iter.len() + } +} +pub struct IterChannelsMut { + iter: I, + range: core::ops::Range, +} +impl Iterator for IterChannelsMut +where + I: Iterator, + I::Item: ChannelMut, +{ + type Item = I::Item; + fn next(&mut self) -> Option { + Some(self.iter.next()?.range(self.range.clone())) + } + fn nth(&mut self, n: usize) -> Option { + Some(self.iter.nth(n)?.range(self.range.clone())) + } +} +impl DoubleEndedIterator for IterChannelsMut +where + I: DoubleEndedIterator, + I::Item: ChannelMut, +{ + fn next_back(&mut self) -> Option { + Some(self.iter.next_back()?.range(self.range.clone())) + } + fn nth_back(&mut self, n: usize) -> Option { + Some(self.iter.nth_back(n)?.range(self.range.clone())) + } +} +impl ExactSizeIterator for IterChannelsMut +where + I: ExactSizeIterator, + I::Item: ChannelMut, +{ + fn len(&self) -> usize { + self.iter.len() + } +} diff --git a/audio-core/src/channel.rs b/audio-core/src/channel.rs index 0bf5f18..093d6db 100644 --- a/audio-core/src/channel.rs +++ b/audio-core/src/channel.rs @@ -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) -> Self; } diff --git a/audio/src/channel/interleaved/macros.rs b/audio/src/channel/interleaved/macros.rs index 639e4e7..21ef4f4 100644 --- a/audio/src/channel/interleaved/macros.rs +++ b/audio/src/channel/interleaved/macros.rs @@ -367,6 +367,49 @@ macro_rules! interleaved_channel { self } + fn range(self, range: impl core::ops::RangeBounds) -> 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::() == 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 } diff --git a/audio/src/channel/linear.rs b/audio/src/channel/linear.rs index 48ff892..b136124 100644 --- a/audio/src/channel/linear.rs +++ b/audio/src/channel/linear.rs @@ -3,6 +3,7 @@ use core::cmp; use core::fmt; use core::ops; +use core::ops::Bound; use core::slice; use audio_core::{Channel, ChannelMut}; @@ -140,6 +141,28 @@ where } } + #[inline] + fn range(self, range: impl ops::RangeBounds) -> Self { + // TODO: simplify when slice::range is stabilized + // https://github.com/rust-lang/rust/issues/76393 + let start = match range.start_bound() { + Bound::Unbounded => 0, + Bound::Included(&i) => i, + Bound::Excluded(&i) => i + 1, + }; + + let end = match range.end_bound() { + Bound::Unbounded => self.buf.len(), + Bound::Included(&i) => i + 1, + Bound::Excluded(&i) => i, + }; + + Self { + // Slice indexing takes care of bounds checking + buf: &self.buf[start..end] + } + } + #[inline] fn try_as_linear(&self) -> Option<&[T]> { Some(self.buf) @@ -315,6 +338,28 @@ where } } + #[inline] + fn range(self, range: impl ops::RangeBounds) -> Self { + // TODO: simplify when slice::range is stabilized + // https://github.com/rust-lang/rust/issues/76393 + let start = match range.start_bound() { + Bound::Unbounded => 0, + Bound::Included(&i) => i, + Bound::Excluded(&i) => i + 1, + }; + + let end = match range.end_bound() { + Bound::Unbounded => self.buf.len(), + Bound::Included(&i) => i + 1, + Bound::Excluded(&i) => i, + }; + + Self { + // Slice indexing takes care of bounds checking + buf: &mut self.buf[start..end] + } + } + #[inline] fn try_as_linear(&self) -> Option<&[T]> { Some(self.buf)