Skip to main content

max / audiofiles

2.2 KB · 75 lines History Blame Raw
1 //! Waveform rendering widget: custom egui Painter-based display with playback position and click-to-seek.
2
3 use egui;
4
5 use audiofiles_core::analysis::waveform::WaveformData;
6
7 use crate::ui::theme;
8
9 /// Draw a waveform display. Returns the response for click-to-seek handling.
10 pub fn draw_waveform(
11 ui: &mut egui::Ui,
12 waveform: &WaveformData,
13 playback_pos: Option<f32>,
14 height: f32,
15 ) -> egui::Response {
16 let available_width = ui.available_width();
17 let size = egui::vec2(available_width, height);
18 let (rect, response) = ui.allocate_exact_size(size, egui::Sense::click());
19
20 if !ui.is_rect_visible(rect) {
21 return response;
22 }
23
24 let painter = ui.painter_at(rect);
25
26 // Dark background
27 painter.rect_filled(rect, 4.0, theme::bg_primary());
28
29 // Center line (zero crossing)
30 let center_y = rect.center().y;
31 painter.line_segment(
32 [
33 egui::pos2(rect.left(), center_y),
34 egui::pos2(rect.right(), center_y),
35 ],
36 egui::Stroke::new(0.5, theme::text_muted()),
37 );
38
39 // Draw waveform peaks
40 let num_buckets = waveform.num_buckets;
41 if num_buckets > 0 && !waveform.peaks.is_empty() {
42 let bucket_width = rect.width() / num_buckets as f32;
43 let half_height = rect.height() / 2.0;
44
45 for i in 0..num_buckets {
46 let pair_idx = i * 2;
47 if pair_idx + 1 >= waveform.peaks.len() {
48 break;
49 }
50 let min_val = waveform.peaks[pair_idx];
51 let max_val = waveform.peaks[pair_idx + 1];
52
53 let x = rect.left() + (i as f32 + 0.5) * bucket_width;
54 let y_min = center_y - max_val * half_height;
55 let y_max = center_y - min_val * half_height;
56
57 painter.line_segment(
58 [egui::pos2(x, y_min), egui::pos2(x, y_max)],
59 egui::Stroke::new(1.0, theme::accent_green()),
60 );
61 }
62 }
63
64 // Playback position line
65 if let Some(pos) = playback_pos {
66 let x = rect.left() + pos.clamp(0.0, 1.0) * rect.width();
67 painter.line_segment(
68 [egui::pos2(x, rect.top()), egui::pos2(x, rect.bottom())],
69 egui::Stroke::new(1.5, theme::text_primary()),
70 );
71 }
72
73 response
74 }
75