Skip to content

Commit 0007985

Browse files
committed
Implement cutting via stream
1 parent 1dc509b commit 0007985

File tree

10 files changed

+96
-29
lines changed

10 files changed

+96
-29
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ anyhow = "1.0.86"
1212
chrono = "0.4.38"
1313
clap = { version = "4.5.11", features = ["derive"] }
1414
dbus = "0.9.7"
15+
futures = "0.3.30"
1516
hound = "3.5.1"
1617
iced = { version = "0.12.1", features = ["canvas"] }
1718
log = "0.4.22"

src/audio/cut.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ use crate::consts::{self};
1515
use crate::recording_session::RecordingSessionWithPath;
1616
use crate::song::Song;
1717

18-
#[derive(Deserialize, Serialize, Debug)]
18+
#[derive(Deserialize, Serialize, Debug, Clone)]
1919
pub struct Cut {
2020
pub start_time_secs: f64,
2121
pub end_time_secs: f64,
2222
pub song: Song,
2323
}
2424

25-
#[derive(Deserialize, Serialize, Debug)]
25+
#[derive(Deserialize, Serialize, Debug, Clone)]
2626
pub struct CutInfo {
2727
pub buffer_file: PathBuf,
2828
pub music_dir: PathBuf,

src/audio/cutter.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
process::Child,
44
};
55

6+
use futures::{channel::mpsc::Sender, SinkExt};
67
use log::{error, warn};
78

89
use crate::recording_session::{RecordingSession, RecordingSessionWithPath};
@@ -57,15 +58,17 @@ fn filter_invalid_songs(reader: &mut WavFileReader, session: &mut RecordingSessi
5758

5859
pub struct Cutter {
5960
cuts: Vec<CutInfo>,
60-
handles: Vec<Child>,
61+
handles: Vec<(Child, CutInfo)>,
62+
sender: Sender<CutInfo>,
6163
}
6264

6365
impl Cutter {
64-
pub async fn run(path: PathBuf, strategy: impl CuttingStrategy) {
66+
pub async fn run(path: PathBuf, strategy: impl CuttingStrategy, sender: Sender<CutInfo>) {
6567
let cuts = get_cuts(&path, strategy);
6668
Self {
6769
cuts,
6870
handles: vec![],
71+
sender,
6972
}
7073
.run_internal()
7174
.await
@@ -85,13 +88,13 @@ impl Cutter {
8588
if let Some(cut) = self.pop_front() {
8689
let mut command = get_cut_command(&cut).unwrap();
8790
self.handles
88-
.push(command.spawn().expect("Failed to cut song."));
91+
.push((command.spawn().expect("Failed to cut song."), cut.clone()));
8992
}
9093
}
9194
let mut finished: Vec<_> = self
9295
.handles
9396
.iter_mut()
94-
.map(|handle| match handle.try_wait().unwrap() {
97+
.map(|(handle, _)| match handle.try_wait().unwrap() {
9598
Some(status) => {
9699
if status.success() {
97100
true
@@ -102,8 +105,12 @@ impl Cutter {
102105
None => false,
103106
})
104107
.collect();
105-
let (_, still_running): (Vec<_>, Vec<_>) =
108+
let (finished, still_running): (Vec<_>, Vec<_>) =
106109
self.handles.drain(..).partition(|_| finished.remove(0));
110+
111+
for (_, cut) in finished {
112+
self.sender.send(cut).await.unwrap()
113+
}
107114
self.handles = still_running;
108115
}
109116
}

src/audio/cutting_strategy.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,14 @@ impl Manual {
126126
}
127127

128128
impl CuttingStrategy for Manual {
129-
fn get_timestamps(&self, _: &mut WavFileReader, _: &RecordingSession) -> Vec<AudioTime> {
130-
self.0.clone()
129+
fn get_timestamps(&self, _: &mut WavFileReader, session: &RecordingSession) -> Vec<AudioTime> {
130+
// Only take as many as there are (valid!) songs in the session.
131+
// TODO: maybe do the song filtering before all of this, so we can
132+
// avoid this uglyness
133+
self.0
134+
.iter()
135+
.take(session.songs.len() + 1)
136+
.cloned()
137+
.collect()
131138
}
132139
}

src/audio/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod playback;
66
mod sample_reader;
77
mod time;
88

9+
pub use cut::CutInfo;
910
pub use cutter::Cutter;
1011
pub use cutting_strategy::*;
1112
pub use sample_reader::get_volume_at;

src/gui/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ mod plot;
22
mod session_gui;
33
mod session_selector;
44

5-
use crate::audio::{AudioTime, Cutter};
65
use crate::config::Config;
76
use crate::recording_session::SessionPath;
87
use anyhow::Result;

src/gui/plot.rs

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use iced::{
77
};
88

99
use crate::{
10-
audio::{get_volume_at, interpolate, interpolation_factor, AudioTime, WavFileReader},
10+
audio::{get_volume_at, interpolate, interpolation_factor, AudioTime, CutInfo, WavFileReader},
1111
song::Song,
1212
};
1313

@@ -21,11 +21,13 @@ const MARKER_COLOR: Color = Palette::GRUVBOX_DARK.primary;
2121

2222
pub struct Plot {
2323
data: Vec<Point>,
24-
_song_before: Option<Song>,
25-
_song_after: Option<Song>,
24+
song_before: Option<Song>,
25+
song_after: Option<Song>,
2626
start: AudioTime,
2727
end: AudioTime,
2828
cut_time: AudioTime,
29+
finished_cut_before: bool,
30+
finished_cut_after: bool,
2931
}
3032

3133
impl Plot {
@@ -48,11 +50,13 @@ impl Plot {
4850
.collect();
4951
Self {
5052
data,
51-
_song_before: song_before,
52-
_song_after: song_after,
53+
song_before,
54+
song_after,
5355
start,
5456
end,
5557
cut_time,
58+
finished_cut_before: false,
59+
finished_cut_after: false,
5660
}
5761
}
5862

@@ -64,16 +68,34 @@ impl Plot {
6468
WIDTH * interpolation_factor(self.start, self.end, time) as f32
6569
}
6670

67-
pub fn get_plot_path(&self) -> Path {
71+
pub fn get_plot_path(&self, data: &[Point]) -> Path {
6872
let mut path = Builder::new();
6973

70-
path.move_to(self.data[0]);
71-
for point in self.data.iter() {
72-
path.line_to(*point);
74+
if data.len() > 0 {
75+
path.move_to(data[0]);
76+
for point in data.iter() {
77+
path.line_to(*point);
78+
}
7379
}
7480
path.build()
7581
}
7682

83+
/// Return the path left of the marker and the path right
84+
/// of the marker, so they can be colored individually.
85+
pub fn get_plot_paths(&self) -> (Path, Path) {
86+
let cutoff = self
87+
.data
88+
.iter()
89+
.enumerate()
90+
.find(|(_, p)| self.pos_to_time(**p) > self.cut_time)
91+
.map(|(i, _)| i)
92+
.unwrap_or(self.data.len() - 1);
93+
(
94+
self.get_plot_path(&self.data[..=cutoff]),
95+
self.get_plot_path(&self.data[cutoff..]),
96+
)
97+
}
98+
7799
pub fn get_marker_path(&self) -> Path {
78100
let mut path = Builder::new();
79101
let x = self.time_to_pos(self.cut_time);
@@ -84,6 +106,17 @@ impl Plot {
84106

85107
pub fn set_cut_position(&mut self, time: AudioTime) {
86108
self.cut_time = time;
109+
self.finished_cut_before = false;
110+
self.finished_cut_after = false;
111+
}
112+
113+
pub fn update_on_finished_cut(&mut self, cut_song: &CutInfo) {
114+
if self.song_before.as_ref() == Some(&cut_song.cut.song) {
115+
self.finished_cut_before = true;
116+
}
117+
if self.song_after.as_ref() == Some(&cut_song.cut.song) {
118+
self.finished_cut_after = true;
119+
}
87120
}
88121
}
89122

@@ -126,12 +159,25 @@ impl Program<PlotMarkerMoved> for Plot {
126159
) -> Vec<Geometry> {
127160
let mut frame = Frame::new(renderer, bounds.size());
128161

129-
let plot = self.get_plot_path();
162+
let (plot_before, plot_after) = self.get_plot_paths();
163+
let color = |finished_cutting| {
164+
if finished_cutting {
165+
Color::from_rgb(0.0, 0.8, 0.0)
166+
} else {
167+
PLOT_COLOR
168+
}
169+
};
170+
frame.stroke(
171+
&plot_before,
172+
Stroke::default()
173+
.with_width(PLOT_STROKE_WIDTH)
174+
.with_color(color(self.finished_cut_before)),
175+
);
130176
frame.stroke(
131-
&plot,
177+
&plot_after,
132178
Stroke::default()
133179
.with_width(PLOT_STROKE_WIDTH)
134-
.with_color(PLOT_COLOR),
180+
.with_color(color(self.finished_cut_after)),
135181
);
136182
let marker = self.get_marker_path();
137183
frame.stroke(

src/gui/session_gui.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use crate::audio::{
2-
AudioTime, Cutter, CuttingStrategy, DbusLengthsStrategy, Manual, WavFileReader,
2+
AudioTime, CutInfo, Cutter, CuttingStrategy, DbusLengthsStrategy, Manual, WavFileReader,
33
};
44
use crate::recording_session::{RecordingSession, RecordingSessionWithPath, SessionPath};
55
use anyhow::Result;
6+
use iced::command::channel;
67
use iced::widget::{row, Canvas, Column};
78
use iced::{Command, Element};
9+
use log::debug;
810

911
use super::plot::{Plot, PlotMarkerMoved};
10-
use super::Message;
1112

1213
const CANVAS_WIDTH: f32 = 800.0;
1314
const CANVAS_HEIGHT: f32 = 80.0;
@@ -16,7 +17,7 @@ const CANVAS_HEIGHT: f32 = 80.0;
1617
pub enum SessionMessage {
1718
SetCutPosition(SetCutPosition),
1819
CutSongs,
19-
FinishedCutting,
20+
FinishedCutting(CutInfo),
2021
}
2122

2223
#[derive(Clone, Debug)]
@@ -82,7 +83,12 @@ impl SessionGui {
8283
SessionMessage::CutSongs => {
8384
return self.cut_current_session();
8485
}
85-
_ => panic!("we did it"),
86+
SessionMessage::FinishedCutting(cut) => {
87+
for plot in self.plots.iter_mut() {
88+
plot.update_on_finished_cut(&cut);
89+
}
90+
debug!("Finished {}", cut.cut.song);
91+
}
8692
}
8793
Command::none()
8894
}
@@ -117,6 +123,7 @@ impl SessionGui {
117123
pub fn cut_current_session(&self) -> Command<SessionMessage> {
118124
let path = self.path.0.clone();
119125
let cuts = self.cuts.clone();
120-
Command::perform(Cutter::run(path, cuts), |_| SessionMessage::FinishedCutting)
126+
channel(5, move |sender| Cutter::run(path, cuts, sender))
127+
.map(|m| SessionMessage::FinishedCutting(m))
121128
}
122129
}

src/main.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ mod song;
1010
use std::path::Path;
1111

1212
use anyhow::Result;
13-
use audio::Cutter;
14-
use audio::SilenceOptimizer;
1513
use config::Command;
1614
use config::Config;
1715
use log::info;

0 commit comments

Comments
 (0)