@@ -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