Skip to content

Commit 44444db

Browse files
refactor(spectrogram): isolate history ring state
Signed-off-by: Maika Namuo <httpworldview@gmail.com>
1 parent 86c13b0 commit 44444db

1 file changed

Lines changed: 116 additions & 113 deletions

File tree

src/visuals/spectrogram/state.rs

Lines changed: 116 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,8 @@ impl Default for SpectrogramStyle {
7474
}
7575
}
7676

77-
pub(in crate::visuals) struct SpectrogramState {
78-
pub(in crate::visuals) style: SpectrogramStyle,
79-
pub(in crate::visuals) palette: [Color; SPECTROGRAM_PALETTE_SIZE],
80-
pub(in crate::visuals) stop_positions: [f32; SPECTROGRAM_PALETTE_SIZE],
81-
pub(in crate::visuals) stop_spreads: [f32; SPECTROGRAM_PALETTE_SIZE],
82-
key: u64,
83-
pub(in crate::visuals) piano_roll_overlay: PianoRollOverlay,
84-
pub(in crate::visuals) rotation: i8,
85-
sample_rate: f32,
86-
fft_size: usize,
87-
hop_size: usize,
88-
pub(in crate::visuals) freq_scale: FrequencyScale,
77+
struct SpectrogramHistory {
8978
col_kind: ColumnKind,
90-
zoom: f32,
91-
pan: f32,
92-
pub(in crate::visuals) view_width: u32,
9379
points_per_column: usize,
9480
reassigned_points_per_slot: u32,
9581
ring_capacity: u32,
@@ -101,25 +87,10 @@ pub(in crate::visuals) struct SpectrogramState {
10187
pending_copy: Option<RingCopyPlan>,
10288
}
10389

104-
impl SpectrogramState {
105-
pub fn new() -> Self {
106-
let cfg = SpectrogramConfig::default();
90+
impl Default for SpectrogramHistory {
91+
fn default() -> Self {
10792
Self {
108-
style: SpectrogramStyle::default(),
109-
palette: palettes::spectrogram::COLORS,
110-
stop_positions: palettes::spectrogram::DEFAULT_POSITIONS,
111-
stop_spreads: [1.0; SPECTROGRAM_PALETTE_SIZE],
112-
key: crate::visuals::next_key(),
113-
piano_roll_overlay: PianoRollOverlay::Off,
114-
rotation: 0,
115-
sample_rate: cfg.sample_rate,
116-
fft_size: cfg.fft_size * cfg.zero_padding_factor.max(1),
117-
hop_size: cfg.hop_size,
118-
freq_scale: cfg.frequency_scale,
11993
col_kind: ColumnKind::Reassigned,
120-
zoom: 1.0,
121-
pan: 0.5,
122-
view_width: 0,
12394
points_per_column: 0,
12495
reassigned_points_per_slot: 1,
12596
ring_capacity: 0,
@@ -131,49 +102,12 @@ impl SpectrogramState {
131102
pending_copy: None,
132103
}
133104
}
105+
}
134106

135-
pub fn set_palette(&mut self, palette: &[Color; SPECTROGRAM_PALETTE_SIZE]) {
136-
self.palette = *palette;
137-
}
138-
139-
pub fn set_stop_positions(&mut self, positions: &[f32]) {
140-
if let Ok(arr) = <[f32; SPECTROGRAM_PALETTE_SIZE]>::try_from(positions) {
141-
self.stop_positions = arr;
142-
}
143-
}
144-
145-
pub fn set_stop_spreads(&mut self, spreads: &[f32]) {
146-
if let Ok(arr) = <[f32; SPECTROGRAM_PALETTE_SIZE]>::try_from(spreads) {
147-
self.stop_spreads = arr;
148-
}
149-
}
150-
151-
pub fn set_floor_db(&mut self, floor_db: f32) {
152-
self.style.floor_db = sanitize_negative_db(floor_db, DB_FLOOR)
153-
.min(self.style.ceiling_db - 1.0);
154-
}
155-
156-
pub fn set_tilt_db(&mut self, tilt_db: f32) {
157-
self.style.tilt_db = if tilt_db.is_finite() { tilt_db } else { 0.0 };
158-
}
159-
160-
pub fn set_rotation(&mut self, rotation: i8) {
161-
self.rotation = rotation.clamp(-1, 2);
162-
}
163-
164-
pub fn apply_snapshot(&mut self, snap: SpectrogramUpdate) {
165-
if snap.new_columns.is_empty() && !snap.reset {
166-
return;
167-
}
168-
self.sample_rate = snap.sample_rate;
169-
self.fft_size = snap.fft_size;
170-
self.hop_size = snap.hop_size;
171-
self.freq_scale = snap.frequency_scale;
172-
107+
impl SpectrogramHistory {
108+
fn apply_update(&mut self, snap: SpectrogramUpdate) {
173109
let ppc = snap.points_per_column;
174-
if ppc == 0 {
175-
return;
176-
}
110+
if ppc == 0 { return; }
177111
let new_kind = match snap.new_columns.first() {
178112
Some(SpectrogramColumn::Reassigned(_)) => ColumnKind::Reassigned,
179113
Some(SpectrogramColumn::Classic(_)) => ColumnKind::Classic,
@@ -185,21 +119,16 @@ impl SpectrogramState {
185119
let capacity = (snap.history_length as u32)
186120
.clamp(1, MAX_SPECTROGRAM_HISTORY_COLUMNS as u32)
187121
.min(max_cols);
188-
if capacity == 0 {
189-
return;
190-
}
122+
if capacity == 0 { return; }
191123

192124
if snap.reset || self.points_per_column != ppc || new_kind != self.col_kind {
193-
self.points_per_column = ppc;
194-
self.reassigned_points_per_slot = 1;
195-
self.ring_capacity = capacity;
196-
self.gpu_capacity = 0;
197-
self.col_kind = new_kind;
198-
self.write_slot = 0;
199-
self.col_count = 0;
200-
self.slot_counts = vec![0; capacity as usize];
201-
self.pending = VecDeque::new();
202-
self.pending_copy = None;
125+
*self = Self {
126+
col_kind: new_kind,
127+
points_per_column: ppc,
128+
ring_capacity: capacity,
129+
slot_counts: vec![0; capacity as usize],
130+
..Default::default()
131+
};
203132
} else if capacity != self.ring_capacity {
204133
self.ensure_pending_copy();
205134
if capacity > self.ring_capacity && self.col_count >= self.ring_capacity {
@@ -227,14 +156,10 @@ impl SpectrogramState {
227156
}
228157
SpectrogramColumn::Classic(mags) => PendingUpload::Classic { slot, mags },
229158
};
230-
if self.pending.len() as u32 >= self.ring_capacity {
231-
self.pending.pop_front();
232-
}
159+
if self.pending.len() as u32 >= self.ring_capacity { self.pending.pop_front(); }
233160
self.pending.push_back(upload);
234161
self.write_slot = (self.write_slot + 1) % self.ring_capacity;
235-
if self.col_count < self.ring_capacity {
236-
self.col_count += 1;
237-
}
162+
if self.col_count < self.ring_capacity { self.col_count += 1; }
238163
}
239164
self.fit_reassigned_slot_capacity();
240165
}
@@ -256,9 +181,7 @@ impl SpectrogramState {
256181
.iter()
257182
.take(self.ring_capacity as usize)
258183
.copied()
259-
.max()
260-
.unwrap_or(0)
261-
.max(1);
184+
.fold(1, u32::max);
262185
let current = self.reassigned_points_per_slot.max(1);
263186
if needed > current || current > needed.saturating_mul(4).max(1) {
264187
self.ensure_pending_copy();
@@ -289,37 +212,117 @@ impl SpectrogramState {
289212
copies.retain_mut(|[_, dst]| remap(dst));
290213
}
291214
}
215+
}
216+
217+
pub(in crate::visuals) struct SpectrogramState {
218+
pub(in crate::visuals) style: SpectrogramStyle,
219+
pub(in crate::visuals) palette: [Color; SPECTROGRAM_PALETTE_SIZE],
220+
pub(in crate::visuals) stop_positions: [f32; SPECTROGRAM_PALETTE_SIZE],
221+
pub(in crate::visuals) stop_spreads: [f32; SPECTROGRAM_PALETTE_SIZE],
222+
key: u64,
223+
pub(in crate::visuals) piano_roll_overlay: PianoRollOverlay,
224+
pub(in crate::visuals) rotation: i8,
225+
sample_rate: f32,
226+
fft_size: usize,
227+
hop_size: usize,
228+
pub(in crate::visuals) freq_scale: FrequencyScale,
229+
zoom: f32,
230+
pan: f32,
231+
pub(in crate::visuals) view_width: u32,
232+
history: SpectrogramHistory,
233+
}
234+
235+
impl SpectrogramState {
236+
pub fn new() -> Self {
237+
let cfg = SpectrogramConfig::default();
238+
Self {
239+
style: SpectrogramStyle::default(),
240+
palette: palettes::spectrogram::COLORS,
241+
stop_positions: palettes::spectrogram::DEFAULT_POSITIONS,
242+
stop_spreads: [1.0; SPECTROGRAM_PALETTE_SIZE],
243+
key: crate::visuals::next_key(),
244+
piano_roll_overlay: PianoRollOverlay::Off,
245+
rotation: 0,
246+
sample_rate: cfg.sample_rate,
247+
fft_size: cfg.fft_size * cfg.zero_padding_factor.max(1),
248+
hop_size: cfg.hop_size,
249+
freq_scale: cfg.frequency_scale,
250+
zoom: 1.0,
251+
pan: 0.5,
252+
view_width: 0,
253+
history: SpectrogramHistory::default(),
254+
}
255+
}
256+
257+
pub fn set_palette(&mut self, palette: &[Color; SPECTROGRAM_PALETTE_SIZE]) {
258+
self.palette = *palette;
259+
}
260+
261+
pub fn set_stop_positions(&mut self, positions: &[f32]) {
262+
if let Ok(arr) = <[f32; SPECTROGRAM_PALETTE_SIZE]>::try_from(positions) {
263+
self.stop_positions = arr;
264+
}
265+
}
266+
267+
pub fn set_stop_spreads(&mut self, spreads: &[f32]) {
268+
if let Ok(arr) = <[f32; SPECTROGRAM_PALETTE_SIZE]>::try_from(spreads) {
269+
self.stop_spreads = arr;
270+
}
271+
}
272+
273+
pub fn set_floor_db(&mut self, floor_db: f32) {
274+
self.style.floor_db = sanitize_negative_db(floor_db, DB_FLOOR)
275+
.min(self.style.ceiling_db - 1.0);
276+
}
277+
278+
pub fn set_tilt_db(&mut self, tilt_db: f32) {
279+
self.style.tilt_db = if tilt_db.is_finite() { tilt_db } else { 0.0 };
280+
}
281+
282+
pub fn set_rotation(&mut self, rotation: i8) {
283+
self.rotation = rotation.clamp(-1, 2);
284+
}
285+
286+
pub fn apply_snapshot(&mut self, snap: SpectrogramUpdate) {
287+
if snap.new_columns.is_empty() && !snap.reset { return; }
288+
self.sample_rate = snap.sample_rate;
289+
self.fft_size = snap.fft_size;
290+
self.hop_size = snap.hop_size;
291+
self.freq_scale = snap.frequency_scale;
292+
self.history.apply_update(snap);
293+
}
292294

293295
pub fn visual_params(
294296
&mut self,
295297
bounds: Rectangle,
296298
uv_y_range: [f32; 2],
297299
) -> Option<SpectrogramParams> {
298-
if self.col_count == 0 && self.pending.is_empty() { return None; }
300+
let history = &mut self.history;
301+
if history.col_count == 0 && history.pending.is_empty() { return None; }
302+
let copy_plan = history.pending_copy.take();
303+
history.gpu_capacity = history.ring_capacity;
304+
let slot_counts = if history.col_kind == ColumnKind::Reassigned {
305+
history.slot_counts.clone()
306+
} else {
307+
Vec::new()
308+
};
299309
let op = self.style.opacity.clamp(0.0, 1.0);
300310
let to_rgba = |c: Color| rgba_with_alpha(color_to_rgba(c), c.a * op);
301311
let bin_hz = self.sample_rate / (self.fft_size.max(1) as f32);
302312
let (freq_min, freq_max) = display_axis(self.sample_rate);
303313

304-
let copy_plan = self.pending_copy.take();
305-
self.gpu_capacity = self.ring_capacity;
306-
307314
Some(SpectrogramParams {
308315
key: self.key,
309316
bounds,
310-
ring_capacity: self.ring_capacity,
311-
points_per_column: self.points_per_column as u32,
312-
reassigned_points_per_slot: self.reassigned_points_per_slot.max(1),
313-
col_count: self.col_count,
314-
write_slot: self.write_slot,
315-
pending_uploads: std::mem::take(&mut self.pending),
317+
ring_capacity: history.ring_capacity,
318+
points_per_column: history.points_per_column as u32,
319+
reassigned_points_per_slot: history.reassigned_points_per_slot.max(1),
320+
col_count: history.col_count,
321+
write_slot: history.write_slot,
322+
pending_uploads: std::mem::take(&mut history.pending),
316323
copy_plan,
317-
slot_counts: if self.col_kind == ColumnKind::Reassigned {
318-
self.slot_counts.clone()
319-
} else {
320-
Vec::new()
321-
},
322-
col_kind: self.col_kind,
324+
slot_counts,
325+
col_kind: history.col_kind,
323326
freq_min,
324327
freq_max,
325328
bin_hz,
@@ -374,7 +377,7 @@ impl SpectrogramState {
374377
// 1 column = 1 logical pixel on the time axis, matching the shader.
375378
fn time_ago_at_cursor(&self, cursor: Point, bounds: Rectangle) -> Option<f32> {
376379
if !bounds.contains(cursor)
377-
|| self.col_count == 0
380+
|| self.history.col_count == 0
378381
|| self.hop_size == 0
379382
|| self.sample_rate <= 0.0
380383
{
@@ -387,7 +390,7 @@ impl SpectrogramState {
387390
3 => cursor.y - bounds.y,
388391
_ => return None,
389392
};
390-
if age < 0.0 || age >= self.col_count as f32 { return None; }
393+
if age < 0.0 || age >= self.history.col_count as f32 { return None; }
391394
let secs = age * (self.hop_size as f32 / self.sample_rate);
392395
secs.is_finite().then_some(secs)
393396
}

0 commit comments

Comments
 (0)