Add audio segment

This commit is contained in:
Daniel Wolf 2023-01-14 13:25:28 +01:00
parent 3d3cbdabb1
commit 229ef2a25b
3 changed files with 252 additions and 0 deletions

View File

@ -0,0 +1,23 @@
use crate::{AudioClip, Segment};
/// Blanket implementations of audio filters for audio clips.
pub trait AudioFilters {
/// Returns a new audio clip containing a segment of the specified clip.
fn segment(self, start: u64, end: u64) -> Box<dyn AudioClip>;
}
impl AudioFilters for Box<dyn AudioClip> {
fn segment(self, start: u64, end: u64) -> Box<dyn AudioClip> {
Box::new(Segment::new(self, start, end))
}
}
impl<TAudioClip> AudioFilters for TAudioClip
where
TAudioClip: AudioClip + 'static,
{
fn segment(self, start: u64, end: u64) -> Box<dyn AudioClip> {
let boxed_audio_clip: Box<dyn AudioClip> = Box::new(self);
boxed_audio_clip.segment(start, end)
}
}

View File

@ -14,17 +14,21 @@
mod audio_clip; mod audio_clip;
mod audio_error; mod audio_error;
mod audio_filters;
mod memory_audio_clip; mod memory_audio_clip;
mod ogg_audio_clip; mod ogg_audio_clip;
mod open_audio_file; mod open_audio_file;
mod read_and_seek; mod read_and_seek;
mod sample_reader_assertions; mod sample_reader_assertions;
mod segment;
mod wave_audio_clip; mod wave_audio_clip;
pub use audio_clip::{AudioClip, Sample, SampleReader}; pub use audio_clip::{AudioClip, Sample, SampleReader};
pub use audio_error::AudioError; pub use audio_error::AudioError;
pub use audio_filters::AudioFilters;
pub use memory_audio_clip::MemoryAudioClip; pub use memory_audio_clip::MemoryAudioClip;
pub use ogg_audio_clip::ogg_audio_clip::OggAudioClip; pub use ogg_audio_clip::ogg_audio_clip::OggAudioClip;
pub use open_audio_file::{open_audio_file, open_audio_file_with_reader}; pub use open_audio_file::{open_audio_file, open_audio_file_with_reader};
pub use read_and_seek::ReadAndSeek; pub use read_and_seek::ReadAndSeek;
pub use segment::Segment;
pub use wave_audio_clip::wave_audio_clip::WaveAudioClip; pub use wave_audio_clip::wave_audio_clip::WaveAudioClip;

View File

@ -0,0 +1,225 @@
use std::sync::Arc;
use crate::{
sample_reader_assertions::SampleReaderAssertions, AudioClip, AudioError, SampleReader,
};
/// An audio clip representing a segment of another audio clip.
#[derive(Debug, Clone)]
pub struct Segment {
inner_clip: Arc<dyn AudioClip>,
start: u64,
end: u64,
}
impl Segment {
/// Creates a new audio clip from a segment of an existing audio clip.
pub fn new(inner_clip: Box<dyn AudioClip>, start: u64, end: u64) -> Self {
assert!(
start <= end,
"Start ({start}) must not be greater than end ({end})."
);
let inner_clip_len = inner_clip.len();
assert!(
end <= inner_clip_len,
"Segment {start}..{end} exceeds {inner_clip_len}-frame audio clip.",
);
Self {
inner_clip: Arc::from(inner_clip),
start,
end,
}
}
}
impl AudioClip for Segment {
fn len(&self) -> u64 {
self.end - self.start
}
fn sampling_rate(&self) -> u32 {
self.inner_clip.sampling_rate()
}
fn create_sample_reader(&self) -> Result<Box<dyn SampleReader>, AudioError> {
Ok(Box::new(SegmentSampleReader::new(self)?))
}
}
#[derive(Debug)]
struct SegmentSampleReader {
inner_sample_reader: Box<dyn SampleReader>,
start: u64,
end: u64,
}
impl SegmentSampleReader {
fn new(segment: &Segment) -> Result<Self, AudioError> {
let mut inner_sample_reader = segment.inner_clip.create_sample_reader()?;
inner_sample_reader.set_position(segment.start);
Ok(Self {
inner_sample_reader,
start: segment.start,
end: segment.end,
})
}
}
impl SampleReader for SegmentSampleReader {
fn len(&self) -> u64 {
self.end - self.start
}
fn position(&self) -> u64 {
self.inner_sample_reader.position() - self.start
}
fn set_position(&mut self, position: u64) {
self.assert_valid_seek_position(position);
self.inner_sample_reader.set_position(position + self.start)
}
fn read(&mut self, buffer: &mut [crate::Sample]) -> Result<(), crate::AudioError> {
self.assert_valid_read_size(buffer);
self.inner_sample_reader.read(buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
use speculoos::prelude::*;
use crate::{MemoryAudioClip, SampleReader};
#[fixture]
fn segment() -> Segment {
let inner_clip = MemoryAudioClip::new(
&[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
16000,
);
Segment::new(Box::new(inner_clip), 1, 9)
}
mod can_be_created_via_fluent_syntax {
use super::*;
use crate::AudioFilters;
#[rstest]
fn from_value() {
let clip = MemoryAudioClip::new(&[0.0, 0.1, 0.2, 0.3, 0.4], 16000);
clip.segment(1, 2);
}
#[rstest]
fn from_box() {
let clip = Box::new(MemoryAudioClip::new(&[0.0, 0.1, 0.2, 0.3, 0.4], 16000));
clip.segment(1, 2);
}
}
#[rstest]
fn supports_debug(segment: Segment) {
assert_that!(format!("{segment:?}"))
.is_equal_to("Segment { inner_clip: MemoryAudioClip { buffer: 11 samples, sampling_rate: 16000 }, start: 1, end: 9 }".to_owned());
}
#[rstest]
fn provides_length(segment: Segment) {
assert_that!(segment.len()).is_equal_to(8);
}
#[rstest]
fn provides_sampling_rate(segment: Segment) {
assert_that!(segment.sampling_rate()).is_equal_to(16000);
}
#[rstest]
fn supports_zero_samples() {
let inner_clip = MemoryAudioClip::new(&[], 16000);
let segment = Segment::new(Box::new(inner_clip), 0, 0);
assert_that!(segment.len()).is_equal_to(0);
assert_that!(segment.sampling_rate()).is_equal_to(16000);
let mut sample_reader = segment.create_sample_reader().unwrap();
let mut buffer = [0.0f32; 0];
sample_reader.read(&mut buffer).unwrap();
sample_reader.set_position(0);
}
mod sample_reader {
use super::*;
#[fixture]
fn reader(segment: Segment) -> Box<dyn SampleReader> {
segment.create_sample_reader().unwrap()
}
#[rstest]
fn supports_debug(reader: Box<dyn SampleReader>) {
assert_that!(format!("{reader:?}"))
.is_equal_to("SegmentSampleReader { inner_sample_reader: MemorySampleReader { buffer: 11 samples, position: 1 }, start: 1, end: 9 }".to_owned());
}
#[rstest]
fn provides_length(reader: Box<dyn SampleReader>) {
assert_that!(reader.len()).is_equal_to(8);
}
#[rstest]
fn position_is_initially_0(reader: Box<dyn SampleReader>) {
assert_that!(reader.position()).is_equal_to(0);
}
#[rstest]
fn reads_samples_up_to_the_end(mut reader: Box<dyn SampleReader>) {
let mut three_samples = [0f32; 3];
reader.read(&mut three_samples).unwrap();
assert_that!(three_samples).is_equal_to([0.1, 0.2, 0.3]);
let mut five_samples = [0f32; 5];
reader.read(&mut five_samples).unwrap();
assert_that!(five_samples).is_equal_to([0.4, 0.5, 0.6, 0.7, 0.8]);
}
#[rstest]
fn seeks(mut reader: Box<dyn SampleReader>) {
reader.set_position(2);
let mut three_samples = [0f32; 3];
reader.read(&mut three_samples).unwrap();
assert_that!(three_samples).is_equal_to([0.3, 0.4, 0.5]);
reader.set_position(1);
reader.read(&mut three_samples).unwrap();
assert_that!(three_samples).is_equal_to([0.2, 0.3, 0.4]);
reader.read(&mut three_samples).unwrap();
assert_that!(three_samples).is_equal_to([0.5, 0.6, 0.7]);
}
#[rstest]
fn seeks_up_to_the_end(mut reader: Box<dyn SampleReader>) {
reader.set_position(8);
let mut zero_samples = [0f32; 0];
reader.read(&mut zero_samples).unwrap();
}
#[rstest]
#[should_panic(expected = "Attempting to read up to position 9 of 8-frame audio clip.")]
fn reading_beyond_the_end(mut reader: Box<dyn SampleReader>) {
reader.set_position(6);
let mut three_samples = [0f32; 3];
reader.read(&mut three_samples).unwrap();
}
#[rstest]
#[should_panic(expected = "Attempting to seek to position 9 of 8-frame audio clip.")]
fn seeking_beyond_the_end(mut reader: Box<dyn SampleReader>) {
reader.set_position(9);
}
}
}