rust_synth/tui/
waveform.rs1use ratatui::layout::Rect;
4use ratatui::style::{Color, Modifier, Style};
5use ratatui::symbols::Marker;
6use ratatui::widgets::canvas::{Canvas, Context, Line};
7use ratatui::widgets::{Block, Borders};
8use ratatui::Frame;
9
10use crate::audio::engine::{EngineHandle, SCOPE_CAPACITY};
11
12pub fn render(f: &mut Frame, area: Rect, engine: &EngineHandle) {
13 let samples: Vec<(f32, f32)> = engine.scope.lock().iter().copied().collect();
14 let len = samples.len().max(1);
15
16 let canvas = Canvas::default()
17 .block(
18 Block::default()
19 .borders(Borders::ALL)
20 .title(" scope · L/R ")
21 .title_style(Style::default().add_modifier(Modifier::BOLD)),
22 )
23 .marker(Marker::Braille)
24 .x_bounds([0.0, SCOPE_CAPACITY as f64])
25 .y_bounds([-1.2, 1.2])
26 .paint(move |ctx| {
27 draw_channel(ctx, &samples, |s| s.0, Color::Cyan, len);
28 draw_channel(ctx, &samples, |s| s.1, Color::Magenta, len);
29 ctx.draw(&Line {
31 x1: 0.0,
32 y1: 0.0,
33 x2: SCOPE_CAPACITY as f64,
34 y2: 0.0,
35 color: Color::DarkGray,
36 });
37 });
38
39 f.render_widget(canvas, area);
40}
41
42fn draw_channel(
43 ctx: &mut Context,
44 samples: &[(f32, f32)],
45 pick: impl Fn(&(f32, f32)) -> f32,
46 color: Color,
47 len: usize,
48) {
49 let n = samples.len();
50 if n < 2 {
51 return;
52 }
53 let step = SCOPE_CAPACITY as f64 / len as f64;
54 for i in 1..n {
55 let x1 = (i - 1) as f64 * step;
56 let x2 = i as f64 * step;
57 let y1 = pick(&samples[i - 1]) as f64;
58 let y2 = pick(&samples[i]) as f64;
59 ctx.draw(&Line {
60 x1,
61 y1: y1.clamp(-1.2, 1.2),
62 x2,
63 y2: y2.clamp(-1.2, 1.2),
64 color,
65 });
66 }
67}