Skip to content

Commit 71c1628

Browse files
committed
temp commit
1 parent 1b6a88b commit 71c1628

File tree

9 files changed

+381
-231
lines changed

9 files changed

+381
-231
lines changed

src/audio/buffer.rs

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
use std::{fs::File, io::BufReader, path::PathBuf};
22

3-
use hound::WavReader;
3+
use hound::{WavReader, WavSpec};
44

55
use super::time::AudioTime;
66

7-
pub struct Buffer {
8-
reader: WavReader<BufReader<File>>,
7+
pub type WavFileReader = WavReader<BufReader<File>>;
8+
9+
pub trait SampleReader {
10+
fn extract_audio(
11+
&mut self,
12+
start: AudioTime,
13+
end: AudioTime,
14+
) -> Result<Vec<i16>, OutOfBoundsError>;
15+
}
16+
17+
pub struct Buffer<R> {
18+
reader: R,
19+
spec: WavSpec,
920
}
1021

1122
#[derive(Debug)]
@@ -17,34 +28,17 @@ impl From<std::io::Error> for OutOfBoundsError {
1728
}
1829
}
1930

20-
impl Buffer {
21-
pub fn new(buffer_file: PathBuf) -> Buffer {
31+
impl Buffer<WavReader<BufReader<File>>> {
32+
pub fn new(buffer_file: PathBuf) -> Self {
2233
let reader = hound::WavReader::open(buffer_file).unwrap();
23-
Self { reader }
34+
let spec = reader.spec();
35+
Self { reader, spec }
2436
}
37+
}
2538

26-
pub fn spec(&self) -> hound::WavSpec {
27-
self.reader.spec()
28-
}
29-
30-
fn extract_audio(
31-
&mut self,
32-
start: AudioTime,
33-
end: AudioTime,
34-
) -> Result<Vec<i16>, OutOfBoundsError> {
35-
let num_samples = (end - start).interleaved_sample_num;
36-
self.reader.seek(start.frame_num())?;
37-
let samples_interleaved: Vec<i16> = self
38-
.reader
39-
.samples::<i16>()
40-
.take(num_samples as usize)
41-
.collect::<Result<Vec<_>, hound::Error>>()
42-
.map_err(|_| OutOfBoundsError)?;
43-
if samples_interleaved.len() as u32 != num_samples {
44-
Err(OutOfBoundsError {})
45-
} else {
46-
Ok(samples_interleaved)
47-
}
39+
impl<R: SampleReader> Buffer<R> {
40+
pub fn spec(&self) -> &hound::WavSpec {
41+
&self.spec
4842
}
4943

5044
pub fn get_volume_at(&mut self, time: AudioTime) -> Result<f64, OutOfBoundsError> {
@@ -61,11 +55,32 @@ impl Buffer {
6155
);
6256
let inv_len = 1.0 / ((end.interleaved_sample_num - start.interleaved_sample_num) as f64);
6357
let inv_i16 = 1.0 / (i16::MAX as f64);
64-
let samples = self.extract_audio(start, end)?;
58+
let samples = self.reader.extract_audio(start, end)?;
6559
let average: f64 = samples
6660
.iter()
6761
.map(|x| (*x as f64).abs() * inv_len * inv_i16)
6862
.sum::<f64>();
6963
Ok(average)
7064
}
7165
}
66+
67+
impl SampleReader for WavReader<BufReader<File>> {
68+
fn extract_audio(
69+
&mut self,
70+
start: AudioTime,
71+
end: AudioTime,
72+
) -> Result<Vec<i16>, OutOfBoundsError> {
73+
let num_samples = (end - start).interleaved_sample_num;
74+
self.seek(start.frame_num())?;
75+
let samples_interleaved: Vec<i16> = self
76+
.samples::<i16>()
77+
.take(num_samples as usize)
78+
.collect::<Result<Vec<_>, hound::Error>>()
79+
.map_err(|_| OutOfBoundsError)?;
80+
if samples_interleaved.len() as u32 != num_samples {
81+
Err(OutOfBoundsError {})
82+
} else {
83+
Ok(samples_interleaved)
84+
}
85+
}
86+
}

src/audio/cutter.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,26 @@ use log::{error, warn};
55
use crate::{config::Config, recording_session::RecordingSessionWithPath};
66

77
use super::{
8-
buffer::Buffer,
8+
buffer::{Buffer, WavFileReader},
99
cut::{cut_multiple_songs, CutInfo},
1010
cutting_strategy::CuttingStrategy,
1111
time::AudioTime,
1212
};
1313

1414
pub struct Cutter {
15-
buffer: Buffer,
15+
reader: WavFileReader,
1616
session: RecordingSessionWithPath,
1717
}
1818

1919
impl Cutter {
2020
pub fn new(_: &Config, path: &Path) -> Self {
2121
let session = RecordingSessionWithPath::load_from_dir(path).unwrap();
22-
let buffer = Buffer::new(session.path.get_buffer_file());
23-
Self { buffer, session }
22+
let reader = hound::WavReader::open(session.path.get_buffer_file()).unwrap()
23+
Self { reader, session }
2424
}
2525

2626
fn get_cuts(&mut self, s: impl CuttingStrategy) -> Vec<CutInfo> {
27-
let timestamps = s.get_timestamps(&mut self.buffer, &self.session.session);
27+
let timestamps = s.get_timestamps(&mut self.reader, &self.session.session);
2828
assert_eq!(timestamps.len(), self.session.session.songs.len() + 1);
2929
timestamps
3030
.iter()
@@ -48,10 +48,10 @@ impl Cutter {
4848
.iter()
4949
.enumerate()
5050
.take_while(|(_, timestamp)| {
51-
self.buffer
51+
self.reader
5252
.get_volume_at(AudioTime::from_time_and_spec(
5353
timestamp.in_secs(),
54-
self.buffer.spec(),
54+
self.reader.spec(),
5555
))
5656
.is_ok()
5757
})

src/audio/cutting_strategy.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ use log::debug;
22

33
use crate::{audio::buffer::OutOfBoundsError, recording_session::RecordingSession, song::Song};
44

5-
use super::{buffer::Buffer, time::AudioTime};
5+
use super::{
6+
buffer::{Buffer, WavFileReader},
7+
time::AudioTime,
8+
};
69

710
pub trait CuttingStrategy {
8-
fn get_timestamps(&self, buffer: &mut Buffer, session: &RecordingSession) -> Vec<AudioTime>;
11+
fn get_timestamps(
12+
&self,
13+
buffer: &mut Buffer<WavFileReader>,
14+
session: &RecordingSession,
15+
) -> Vec<AudioTime>;
916
}
1017

1118
pub struct DbusStrategy;
1219

1320
impl CuttingStrategy for DbusStrategy {
14-
fn get_timestamps(&self, buffer: &mut Buffer, session: &RecordingSession) -> Vec<AudioTime> {
21+
fn get_timestamps(
22+
&self,
23+
buffer: &mut Buffer<WavFileReader>,
24+
session: &RecordingSession,
25+
) -> Vec<AudioTime> {
1526
let spec = buffer.spec();
1627
session
1728
.timestamps
@@ -38,7 +49,11 @@ fn get_cut_timestamps_from_song_lengths(
3849
pub struct DbusLengthsStrategy;
3950

4051
impl CuttingStrategy for DbusLengthsStrategy {
41-
fn get_timestamps(&self, buffer: &mut Buffer, session: &RecordingSession) -> Vec<AudioTime> {
52+
fn get_timestamps(
53+
&self,
54+
buffer: &mut Buffer<WavFileReader>,
55+
session: &RecordingSession,
56+
) -> Vec<AudioTime> {
4257
let spec = buffer.spec();
4358
let first_timestamp = &session.timestamps[0];
4459
get_cut_timestamps_from_song_lengths(&session.songs, first_timestamp.in_secs())
@@ -50,14 +65,18 @@ impl CuttingStrategy for DbusLengthsStrategy {
5065
pub struct SilenceOptimizer;
5166

5267
impl CuttingStrategy for SilenceOptimizer {
53-
fn get_timestamps(&self, buffer: &mut Buffer, session: &RecordingSession) -> Vec<AudioTime> {
68+
fn get_timestamps(
69+
&self,
70+
buffer: &mut Buffer<WavFileReader>,
71+
session: &RecordingSession,
72+
) -> Vec<AudioTime> {
5473
let guesses = DbusLengthsStrategy::get_timestamps(&DbusLengthsStrategy, buffer, session);
5574
let offset = optimize_cut_offset(buffer, &guesses);
5675
guesses.into_iter().map(|time| time + offset).collect()
5776
}
5877
}
5978

60-
fn optimize_cut_offset(buffer: &mut Buffer, guesses: &[AudioTime]) -> AudioTime {
79+
fn optimize_cut_offset(buffer: &mut Buffer<WavFileReader>, guesses: &[AudioTime]) -> AudioTime {
6180
// We can assume that some of the songs begin or end with silence.
6281
// If that is the case then the offset of the cuts should be chosen by finding an offset that
6382
// puts as many of the cuts at positions where the recording is silent. In other words, the offset is given by

src/audio/excerpt.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use hound::WavSpec;
2+
use rodio::Source;
3+
4+
use super::{
5+
buffer::{Buffer, OutOfBoundsError, SampleReader},
6+
AudioTime,
7+
};
8+
9+
struct Excerpt {
10+
pub samples: Vec<i16>,
11+
pub start: AudioTime,
12+
pub end: AudioTime,
13+
}
14+
15+
pub struct AudioExcerpt {
16+
buffer: Buffer<Excerpt>,
17+
}
18+
19+
pub const NUM_PLOT_DATA_POINTS: usize = 100;
20+
21+
impl AudioExcerpt {
22+
pub fn get_volume_plot_data(&self) -> Vec<f32> {
23+
let times = self.get_sample_times();
24+
times
25+
.into_iter()
26+
.map(|time| self.buffer.get_volume_at(time as f64) as f32)
27+
.collect()
28+
}
29+
30+
pub fn get_sample_times(&self) -> Vec<f32> {
31+
let width = self.end.time - self.start.time;
32+
let step_size = width as f32 / NUM_PLOT_DATA_POINTS as f32;
33+
(1..NUM_PLOT_DATA_POINTS)
34+
.map(|x| self.start.time as f32 + (x as f32) * step_size as f32)
35+
.collect()
36+
}
37+
38+
pub fn get_absolute_time_by_relative_progress(&self, pos: f64) -> AudioTime {
39+
AudioTime::from_time_and_spec(
40+
self.start.time + (self.end.time - self.start.time) * pos,
41+
&self.spec,
42+
)
43+
}
44+
45+
pub fn get_relative_time_by_relative_progress(&self, pos: f64) -> AudioTime {
46+
AudioTime::from_time_and_spec((self.end.time - self.start.time) * pos, &self.spec)
47+
}
48+
49+
pub fn get_relative_time(&self, absolute_time: AudioTime) -> AudioTime {
50+
absolute_time - self.start
51+
}
52+
53+
pub fn get_relative_progress_from_time_offset(&self, time_offset: f64) -> f64 {
54+
// time_offset is relative to the center
55+
0.5 + (time_offset / (self.end.time - self.start.time))
56+
}
57+
58+
pub fn get_absolute_time_from_time_offset(&self, time_offset: f64) -> AudioTime {
59+
self.get_absolute_time_by_relative_progress(
60+
self.get_relative_progress_from_time_offset(time_offset),
61+
)
62+
}
63+
}
64+
65+
impl SampleReader for AudioExcerpt {
66+
fn extract_audio(
67+
&mut self,
68+
start: AudioTime,
69+
end: AudioTime,
70+
) -> Result<Vec<i16>, OutOfBoundsError> {
71+
todo!()
72+
}
73+
}
74+
75+
pub struct AudioExcerptSource {
76+
excerpt: AudioExcerpt,
77+
position: u32,
78+
}
79+
80+
impl AudioExcerptSource {
81+
pub fn new(excerpt: AudioExcerpt, start_time: AudioTime) -> Self {
82+
Self {
83+
excerpt,
84+
position: start_time.interleaved_sample_num,
85+
}
86+
}
87+
}
88+
89+
impl Source for AudioExcerptSource {
90+
fn current_frame_len(&self) -> Option<usize> {
91+
None
92+
}
93+
94+
fn channels(&self) -> u16 {
95+
self.excerpt.spec.channels
96+
}
97+
98+
fn sample_rate(&self) -> u32 {
99+
self.excerpt.spec.sample_rate
100+
}
101+
102+
fn total_duration(&self) -> Option<std::time::Duration> {
103+
None
104+
}
105+
}
106+
107+
impl Iterator for AudioExcerptSource {
108+
type Item = i16;
109+
110+
fn next(&mut self) -> Option<Self::Item> {
111+
let item = self.excerpt.samples.get(self.position as usize);
112+
self.position += 1;
113+
item.copied()
114+
}
115+
}

src/audio/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ mod buffer;
22
mod cut;
33
mod cutter;
44
mod cutting_strategy;
5+
mod excerpt;
56
mod time;
67

8+
pub use cut::Cut;
79
pub use cutter::Cutter;
8-
910
pub use cutting_strategy::*;
11+
pub use time::AudioTime;

src/audio/time.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct AudioTime {
1313
}
1414

1515
impl AudioTime {
16-
pub fn from_time_and_spec(time: f64, spec: WavSpec) -> AudioTime {
16+
pub fn from_time_and_spec(time: f64, spec: &WavSpec) -> AudioTime {
1717
AudioTime {
1818
time,
1919
channels: spec.channels,
@@ -23,7 +23,7 @@ impl AudioTime {
2323
}
2424
}
2525

26-
pub fn from_sample_and_spec(interleaved_sample_num: u32, spec: WavSpec) -> AudioTime {
26+
pub fn from_sample_and_spec(interleaved_sample_num: u32, spec: &WavSpec) -> AudioTime {
2727
let time =
2828
(interleaved_sample_num as f64) / (spec.channels as u32 * spec.sample_rate) as f64;
2929
AudioTime {

0 commit comments

Comments
 (0)