Skip to content

Commit c4216e1

Browse files
committed
Refactor plots
1 parent 0286280 commit c4216e1

File tree

6 files changed

+114
-52
lines changed

6 files changed

+114
-52
lines changed

Cargo.lock

Lines changed: 10 additions & 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
@@ -16,6 +16,7 @@ futures = "0.3.30"
1616
hound = "3.5.1"
1717
iced = { version = "0.13.1", features = ["canvas"] }
1818
log = "0.4.22"
19+
ordered-float = "4.6.0"
1920
regex = "1"
2021
rodio = "0.19"
2122
serde = { version = "1.0", features = ["derive"] }

src/audio/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ pub use cutting_strategy::*;
1212
pub use sample_reader::get_volume_at;
1313
pub use sample_reader::SampleReader;
1414
pub use sample_reader::WavFileReader;
15-
pub use time::{interpolate, interpolation_factor, AudioTime};
15+
pub use time::AudioTime;

src/audio/time.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,6 @@ impl AudioTime {
5151
}
5252
}
5353

54-
pub fn interpolate(start: AudioTime, end: AudioTime, factor: f64) -> AudioTime {
55-
start + (end - start) * factor
56-
}
57-
58-
pub fn interpolation_factor(start: AudioTime, end: AudioTime, x: AudioTime) -> f64 {
59-
(x.time - start.time) / (end.time - start.time)
60-
}
61-
6254
impl ops::Sub<AudioTime> for AudioTime {
6355
type Output = AudioTime;
6456

src/gui/plot.rs

Lines changed: 100 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,62 @@ use iced::{
33
mouse::{self, Button},
44
theme::Palette,
55
widget::canvas::{path::Builder, Event, Frame, Geometry, Path, Program, Stroke},
6-
Color, Point, Rectangle, Renderer, Theme, Vector,
6+
Color, Point, Rectangle, Renderer, Theme,
77
};
8+
use ordered_float::OrderedFloat;
89

910
use crate::{
10-
audio::{get_volume_at, interpolate, interpolation_factor, AudioTime, CutInfo, WavFileReader},
11+
audio::{get_volume_at, AudioTime, CutInfo, WavFileReader},
1112
song::Song,
1213
};
1314

14-
const WIDTH: f32 = 800.0;
15-
const HEIGHT: f32 = 50.0;
1615
const NUM_PLOT_POINTS: usize = 100;
1716
const PLOT_STROKE_WIDTH: f32 = 1.5;
18-
const PLOT_COLOR: Color = Palette::GRUVBOX_DARK.text;
17+
const PLOT_COLOR: Color = Palette::GRUVBOX_DARK.primary;
1918
const MARKER_STROKE_WIDTH: f32 = 2.5;
20-
const MARKER_COLOR: Color = Palette::GRUVBOX_DARK.primary;
19+
const MARKER_COLOR: Color = Palette::GRUVBOX_DARK.danger;
20+
21+
type Volume = f32;
22+
23+
struct Data {
24+
time: AudioTime,
25+
/// The volume of this data point normalized
26+
/// by the maximum volume of all points in this
27+
/// plot.
28+
volume: Volume,
29+
}
30+
31+
struct Bounds {
32+
min_time: AudioTime,
33+
max_time: AudioTime,
34+
height: f32,
35+
width: f32,
36+
}
37+
38+
pub fn interpolate(start: AudioTime, end: AudioTime, factor: f32) -> AudioTime {
39+
start + (end - start) * factor as f64
40+
}
41+
42+
pub fn interpolation_factor(start: AudioTime, end: AudioTime, x: AudioTime) -> f64 {
43+
(x.time - start.time) / (end.time - start.time)
44+
}
45+
46+
impl Bounds {
47+
fn x_pos_to_time(&self, pos: Point) -> AudioTime {
48+
let f = pos.x / self.width;
49+
interpolate(self.min_time, self.max_time, f)
50+
}
51+
52+
fn data_to_point(&self, data: &Data) -> Point {
53+
let f = interpolation_factor(self.min_time, self.max_time, data.time);
54+
Point::new(f as f32 * self.width, data.volume * self.height)
55+
}
56+
}
57+
58+
impl Data {}
2159

2260
pub struct Plot {
23-
data: Vec<Point>,
61+
volume_data: Vec<Data>,
2462
song_before: Option<Song>,
2563
song_after: Option<Song>,
2664
start: AudioTime,
@@ -40,16 +78,29 @@ impl Plot {
4078
let delta = AudioTime::from_time_same_spec(3.0, cut_time);
4179
let start = cut_time - delta;
4280
let end = cut_time + delta;
43-
let data = (0..NUM_PLOT_POINTS)
81+
let volume_data: Vec<_> = (0..NUM_PLOT_POINTS)
4482
.map(|i| {
4583
let f = i as f32 / NUM_PLOT_POINTS as f32;
46-
let time = interpolate(start, end, f as f64);
47-
let vol = get_volume_at(reader, time).unwrap_or(0.0) as f32;
48-
Point::new(f * WIDTH, HEIGHT / 2.0 + vol * HEIGHT)
84+
let time = interpolate(start, end, f);
85+
let volume = get_volume_at(reader, time).unwrap_or(0.0) as f32;
86+
Data { time, volume }
87+
})
88+
.collect();
89+
// Normalize
90+
let max_volume = volume_data
91+
.iter()
92+
.map(|data| OrderedFloat(data.volume))
93+
.max()
94+
.unwrap();
95+
let volume_data = volume_data
96+
.into_iter()
97+
.map(|data| Data {
98+
time: data.time,
99+
volume: data.volume / *max_volume,
49100
})
50101
.collect();
51102
Self {
52-
data,
103+
volume_data,
53104
song_before,
54105
song_after,
55106
start,
@@ -60,47 +111,44 @@ impl Plot {
60111
}
61112
}
62113

63-
fn pos_to_time(&self, pos: Point) -> AudioTime {
64-
interpolate(self.start, self.end, (pos.x / WIDTH) as f64)
65-
}
66-
67-
fn time_to_pos(&self, time: AudioTime) -> f32 {
68-
WIDTH * interpolation_factor(self.start, self.end, time) as f32
69-
}
70-
71-
pub fn get_plot_path(&self, data: &[Point]) -> Path {
114+
pub fn get_plot_path(&self, data: &[Data], bounds: &Bounds) -> Path {
72115
let mut path = Builder::new();
73116

74117
if data.len() > 0 {
75-
path.move_to(data[0]);
76-
for point in data.iter() {
77-
path.line_to(*point);
118+
path.move_to(bounds.data_to_point(&data[0]));
119+
for data in data.iter() {
120+
path.line_to(bounds.data_to_point(data));
78121
}
79122
}
80123
path.build()
81124
}
82125

83126
/// Return the path left of the marker and the path right
84127
/// of the marker, so they can be colored individually.
85-
pub fn get_plot_paths(&self) -> (Path, Path) {
128+
pub fn get_plot_paths(&self, bounds: &Bounds) -> (Path, Path) {
86129
let cutoff = self
87-
.data
130+
.volume_data
88131
.iter()
89132
.enumerate()
90-
.find(|(_, p)| self.pos_to_time(**p) > self.cut_time)
133+
.find(|(_, data)| data.time > self.cut_time)
91134
.map(|(i, _)| i)
92-
.unwrap_or(self.data.len() - 1);
135+
.unwrap_or(self.volume_data.len() - 1);
93136
(
94-
self.get_plot_path(&self.data[..=cutoff]),
95-
self.get_plot_path(&self.data[cutoff..]),
137+
self.get_plot_path(&self.volume_data[..=cutoff], bounds),
138+
self.get_plot_path(&self.volume_data[cutoff..], bounds),
96139
)
97140
}
98141

99-
pub fn get_marker_path(&self) -> Path {
142+
pub fn get_marker_path(&self, bounds: &Bounds) -> Path {
100143
let mut path = Builder::new();
101-
let x = self.time_to_pos(self.cut_time);
102-
path.move_to(Point::new(x, 0.0));
103-
path.line_to(Point::new(x, HEIGHT));
144+
path.move_to(bounds.data_to_point(&Data {
145+
time: self.cut_time,
146+
volume: -1.0,
147+
}));
148+
path.line_to(bounds.data_to_point(&Data {
149+
time: self.cut_time,
150+
volume: 1.0,
151+
}));
104152
path.build()
105153
}
106154

@@ -126,6 +174,15 @@ impl Plot {
126174
pub fn song_before(&self) -> Option<&Song> {
127175
self.song_before.as_ref()
128176
}
177+
178+
fn get_bounds(&self, bounds: Rectangle) -> Bounds {
179+
Bounds {
180+
width: bounds.width,
181+
height: bounds.height,
182+
min_time: self.volume_data.first().unwrap().time,
183+
max_time: self.volume_data.last().unwrap().time,
184+
}
185+
}
129186
}
130187

131188
pub struct PlotMarkerMoved {
@@ -139,16 +196,17 @@ impl Program<PlotMarkerMoved> for Plot {
139196
&self,
140197
_state: &mut Self::State,
141198
event: Event,
142-
bounds: Rectangle,
199+
rect: Rectangle,
143200
cursor: mouse::Cursor,
144201
) -> (Status, Option<PlotMarkerMoved>) {
202+
let bounds = self.get_bounds(rect);
145203
if let Event::Mouse(mouse::Event::ButtonPressed(ev)) = event {
146204
if ev == Button::Left {
147-
if let Some(pos) = cursor.position_in(bounds) {
205+
if let Some(pos) = cursor.position_in(rect) {
148206
return (
149207
Status::Ignored,
150208
Some(PlotMarkerMoved {
151-
time: self.pos_to_time(pos),
209+
time: bounds.x_pos_to_time(pos),
152210
}),
153211
);
154212
}
@@ -162,12 +220,13 @@ impl Program<PlotMarkerMoved> for Plot {
162220
_state: &(),
163221
renderer: &Renderer,
164222
_theme: &Theme,
165-
bounds: Rectangle,
223+
rect: Rectangle,
166224
_cursor: mouse::Cursor,
167225
) -> Vec<Geometry> {
168-
let mut frame = Frame::new(renderer, bounds.size());
226+
let mut frame = Frame::new(renderer, rect.size());
169227

170-
let (plot_before, plot_after) = self.get_plot_paths();
228+
let bounds = self.get_bounds(rect);
229+
let (plot_before, plot_after) = self.get_plot_paths(&bounds);
171230
let color = |finished_cutting| {
172231
if finished_cutting {
173232
Color::from_rgb(0.0, 0.8, 0.0)
@@ -187,7 +246,7 @@ impl Program<PlotMarkerMoved> for Plot {
187246
.with_width(PLOT_STROKE_WIDTH)
188247
.with_color(color(self.finished_cut_after)),
189248
);
190-
let marker = self.get_marker_path();
249+
let marker = self.get_marker_path(&bounds);
191250
frame.stroke(
192251
&marker,
193252
Stroke::default()

src/gui/session_gui.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ use iced::alignment::Horizontal;
99
use iced::stream::channel;
1010
use iced::widget::{column, horizontal_space, text, Canvas, Column, Row};
1111
use iced::Length::Fill;
12-
use iced::{Element, Subscription, Task};
12+
use iced::{Element, Subscription};
1313
use log::debug;
1414

1515
use super::plot::{Plot, PlotMarkerMoved};
1616

17-
const CANVAS_HEIGHT: f32 = 80.0;
17+
pub const CANVAS_HEIGHT: f32 = 80.0;
1818

1919
#[derive(Clone, Debug)]
2020
pub enum SessionMessage {

0 commit comments

Comments
 (0)