1use ratatui::layout::Rect;
6use ratatui::style::{Color, Modifier, Style};
7use ratatui::text::{Line, Span};
8use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
9use ratatui::Frame;
10
11use crate::audio::engine::EngineHandle;
12use crate::audio::preset::PresetKind;
13
14use super::app::AppState;
15
16pub fn render(f: &mut Frame, area: Rect, engine: &EngineHandle, app: &AppState) {
17 let tracks = engine.tracks.lock();
18 let Some(track) = tracks.get(app.selected_track) else {
19 return;
20 };
21 let s = track.params.snapshot();
22 let bpm = engine.global.bpm.value();
23
24 let title = format!(" formula · {} · {} ", track.name, track.kind.label());
25 let lines: Vec<Line> = lines_for(track.kind, &s, bpm);
26
27 let block = Block::default()
28 .borders(Borders::ALL)
29 .title(title)
30 .title_style(Style::default().add_modifier(Modifier::BOLD));
31 let para = Paragraph::new(lines).block(block).wrap(Wrap { trim: false });
32 f.render_widget(para, area);
33}
34
35fn lines_for(kind: PresetKind, s: &crate::audio::track::TrackSnapshot, bpm: f32) -> Vec<Line<'static>> {
36 let dim = Style::default().fg(Color::DarkGray);
37 let hi = Style::default().fg(Color::Yellow);
38 let key = Style::default().fg(Color::Green);
39
40 let mut out: Vec<Line> = Vec::new();
41 out.push(Line::from(vec![
42 Span::styled(format!("freq = {:6.2} Hz ", s.freq), key),
43 Span::styled(format!("bpm = {:5.1}", bpm), key),
44 ]));
45 out.push(Line::from(""));
46
47 match kind {
48 PresetKind::PadZimmer => {
49 out.push(Line::from(Span::styled("osc(t) = Σ Aₖ · sin(2π·f·rₖ·t)", hi)));
50 out.push(Line::from(Span::styled(
51 " rₖ = [1.000, 1.501, 2.013, 3.007] Aₖ = [.30, .20, .14, .08]",
52 dim,
53 )));
54 out.push(Line::from(""));
55 out.push(Line::from(Span::styled("σ(t) = 1 / (1 + exp(−k·(t − c)))", hi)));
56 out.push(Line::from(vec![
57 Span::styled(" k = ", dim),
58 Span::styled(format!("{:.2}", s.sweep_k), key),
59 Span::styled(" c = ", dim),
60 Span::styled(format!("{:.1} s", s.sweep_center), key),
61 ]));
62 out.push(Line::from(""));
63 out.push(Line::from(Span::styled(
64 "cut(t) = lerp(140, cutoff · (1 + 0.15·phrase), σ(t))",
65 hi,
66 )));
67 out.push(Line::from(vec![
68 Span::styled(" cutoff = ", dim),
69 Span::styled(format!("{:>5.0} Hz", s.cutoff), key),
70 Span::styled(" q = ", dim),
71 Span::styled(format!("{:.2}", s.resonance), key),
72 ]));
73 out.push(Line::from(""));
74 out.push(Line::from(Span::styled(
75 "y = Moog(osc, cut, q) ⇒ chorus ⇒ hall(25m, 8s)",
76 hi,
77 )));
78 out.push(Line::from(vec![
79 Span::styled(" reverb = ", dim),
80 Span::styled(format!("{:.2}", s.reverb_mix), key),
81 Span::styled(" gain = ", dim),
82 Span::styled(format!("{:.2}", s.gain), key),
83 ]));
84 }
85 PresetKind::DroneSub => {
86 out.push(Line::from(Span::styled(
87 "sub(t) = 0.45·sin(2π·f/2·t) + 0.12·sin(2π·f·t)",
88 hi,
89 )));
90 out.push(Line::from(Span::styled(
91 "noise(t) = Moog(brown(t), clip(cut, 40..240), q)",
92 hi,
93 )));
94 out.push(Line::from(Span::styled(
95 "am(t) = 0.75 + 0.25·½(1 − cos(2π·bpm/240·t))",
96 hi,
97 )));
98 out.push(Line::from(""));
99 out.push(Line::from(Span::styled("y = (sub + 0.28·noise) · am · hall(30m,12s)", hi)));
100 out.push(Line::from(vec![
101 Span::styled(" cut = ", dim),
102 Span::styled(format!("{:>5.0}", s.cutoff.min(240.0)), key),
103 Span::styled(" reverb = ", dim),
104 Span::styled(format!("{:.2}", s.reverb_mix), key),
105 Span::styled(" gain = ", dim),
106 Span::styled(format!("{:.2}", s.gain), key),
107 ]));
108 }
109 PresetKind::Shimmer => {
110 out.push(Line::from(Span::styled(
111 "shimmer(t) = .18·sin(4π·f·t) + .12·sin(6π·f·t) + .08·sin(8π·f·t)",
112 hi,
113 )));
114 out.push(Line::from(Span::styled(
115 "y = HP(shimmer, 400Hz) ⇒ hall(28m, 10s)",
116 hi,
117 )));
118 out.push(Line::from(vec![
119 Span::styled(" reverb = ", dim),
120 Span::styled(format!("{:.2}", s.reverb_mix), key),
121 Span::styled(" gain = ", dim),
122 Span::styled(format!("{:.2}", s.gain), key),
123 ]));
124 }
125 PresetKind::Heartbeat => {
126 out.push(Line::from(Span::styled("3-layer kick:", hi)));
127 out.push(Line::from(Span::styled(" body = sin(2π · f·(0.7 + 1.5·e^(−30·φ)) · t)", hi)));
128 out.push(Line::from(Span::styled(" sub = sin(2π · f/2 · t)", hi)));
129 out.push(Line::from(Span::styled(
130 " click = HP(brown, 1.8 kHz)",
131 hi,
132 )));
133 out.push(Line::from(Span::styled(
134 "env_body = e^(−6·φ) env_sub = e^(−3.2·φ) env_click = e^(−55·φ)",
135 dim,
136 )));
137 out.push(Line::from(""));
138 out.push(Line::from(Span::styled(
139 "y = body·env_body + sub·env_sub + click·env_click ⇒ hall(10m, 1.5s)",
140 hi,
141 )));
142 out.push(Line::from(vec![
143 Span::styled(" gain = ", dim),
144 Span::styled(format!("{:.2}", s.gain), key),
145 ]));
146 }
147 PresetKind::BassPulse => {
148 out.push(Line::from(Span::styled(
149 "osc = 0.55·sin(2π·f·t) + 0.22·sin(4π·f·t) + 0.35·sin(π·f·t)",
150 hi,
151 )));
152 out.push(Line::from(Span::styled(
153 "y = Moog(osc, min(cut, 900), q) · groove(t)",
154 hi,
155 )));
156 out.push(Line::from(Span::styled(
157 "groove(t) = 0.45 + 0.55 · e^(−3.5·φ) φ = (t mod 60/bpm)·bpm/60",
158 dim,
159 )));
160 out.push(Line::from(""));
161 out.push(Line::from(vec![
162 Span::styled(" cut = ", dim),
163 Span::styled(format!("{:>5.0} Hz", s.cutoff.min(900.0)), key),
164 Span::styled(" q = ", dim),
165 Span::styled(format!("{:.2}", s.resonance.min(0.65)), key),
166 Span::styled(" rev = ", dim),
167 Span::styled(format!("{:.2}", s.reverb_mix), key),
168 Span::styled(" gain = ", dim),
169 Span::styled(format!("{:.2}", s.gain), key),
170 ]));
171 }
172 }
173
174 if s.supermass > 0.01 {
176 out.push(Line::from(""));
177 out.push(Line::from(vec![
178 Span::styled("Σ supermass ", Style::default().fg(Color::Magenta).add_modifier(Modifier::BOLD)),
179 Span::styled(
180 format!("{:.2}", s.supermass),
181 Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD),
182 ),
183 Span::styled(" + rev(35m,15s)⇒chorus⇒rev(50m,28s)", dim),
184 ]));
185 }
186
187 out.push(Line::from(""));
188 if s.muted {
189 out.push(Line::from(Span::styled("· MUTED · press 'a' to activate next slot", dim)));
190 }
191
192 out
193}