phosphor_app/state/
params.rs1use super::*;
4
5impl NavState {
6
7 pub fn adjust_synth_param(&mut self, delta: f32) -> Option<(usize, usize, f32)> {
10 let idx = self.clip_view.synth_param_cursor;
11 if let Some(track) = self.tracks.get_mut(self.track_cursor) {
12 if idx < track.synth_params.len() {
13 let is_jupiter = track.instrument_type == Some(InstrumentType::Jupiter8);
16 let is_odyssey = track.instrument_type == Some(InstrumentType::Odyssey);
17 let is_juno = track.instrument_type == Some(InstrumentType::Juno60);
18 let is_dx7 = track.instrument_type == Some(InstrumentType::DX7);
19 let is_discrete = if is_jupiter {
20 phosphor_dsp::jupiter::is_discrete(idx)
21 } else if is_odyssey {
22 phosphor_dsp::odyssey::is_discrete(idx)
23 } else if is_juno {
24 phosphor_dsp::juno::is_discrete(idx)
25 } else if is_dx7 {
26 phosphor_dsp::dx7::is_discrete(idx)
30 } else {
31 idx == 0
32 };
33 let actual_delta = if is_discrete {
34 let step = if is_jupiter {
35 match idx {
36 0 => 1.0 / (phosphor_dsp::jupiter::PATCH_COUNT as f32 - 0.01),
37 _ => 0.25,
38 }
39 } else if is_odyssey {
40 match idx {
41 0 => 1.0 / (phosphor_dsp::odyssey::PATCH_COUNT as f32 - 0.01),
42 6 => 0.34, _ => 0.5,
44 }
45 } else if is_juno {
46 match idx {
47 0 => 1.0 / (phosphor_dsp::juno::PATCH_COUNT as f32 - 0.01),
48 12 => 0.25, _ => 0.5, }
51 } else {
52 match track.instrument_type {
53 Some(InstrumentType::DrumRack) => 0.1, _ => 0.25,
55 }
56 };
57 if delta > 0.0 { step } else { -step }
58 } else {
59 delta
60 };
61 let new_val = if is_dx7 && is_discrete {
62 phosphor_dsp::dx7::step_discrete(idx, track.synth_params[idx], delta > 0.0)
68 } else {
69 (track.synth_params[idx] + actual_delta).clamp(0.0, 1.0)
70 };
71 track.synth_params[idx] = new_val;
72
73 if idx == 0 {
75 let new_params = match track.instrument_type {
76 Some(InstrumentType::Jupiter8) => {
77 Some(phosphor_dsp::jupiter::Jupiter8Synth::params_for_patch(new_val))
78 }
79 Some(InstrumentType::Odyssey) => {
80 Some(phosphor_dsp::odyssey::OdysseySynth::params_for_patch(new_val))
81 }
82 Some(InstrumentType::Juno60) => {
83 Some(phosphor_dsp::juno::Juno60Synth::params_for_patch(new_val))
84 }
85 _ => None,
86 };
87 if let Some(preset_params) = new_params {
88 for (i, &v) in preset_params.iter().enumerate() {
89 track.synth_params[i] = v;
90 }
91 }
92 }
93
94 if let Some(mixer_id) = track.mixer_id {
95 return Some((mixer_id, idx, new_val));
96 }
97 }
98 }
99 None
100 }
101
102
103 pub fn show_current_track_controls(&mut self) {
107 for track in &self.tracks {
109 if let Some(ref h) = track.handle {
110 h.config.midi_active.store(false, std::sync::atomic::Ordering::Relaxed);
111 }
112 }
113
114 if let Some(track) = self.tracks.get(self.track_cursor) {
115 if track.is_live() {
116 if let Some(ref h) = track.handle {
117 h.config.midi_active.store(true, std::sync::atomic::Ordering::Relaxed);
118 }
119 self.clip_view_visible = true;
120
121 let clip_idx = match self.track_element {
123 super::TrackElement::Clip(i) if i < track.clips.len() => i,
124 _ => 0,
125 };
126 self.clip_view_target = Some((self.track_cursor, clip_idx));
127
128 if !track.clips.is_empty() {
130 self.clip_view.clip_tab = ClipTab::PianoRoll;
131 self.clip_view.focus = ClipViewFocus::PianoRoll;
132 self.clip_view.piano_roll.focus = PianoRollFocus::Navigation;
133 self.clip_view.piano_roll.column = 0;
134 } else {
135 self.clip_view.fx_panel_tab = FxPanelTab::Synth;
136 self.clip_view.focus = ClipViewFocus::FxPanel;
137 self.clip_view.synth_param_cursor = 0;
138 }
139 } else {
140 self.clip_view_visible = false;
142 self.clip_view_target = None;
143 }
144 }
145 }
146
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use phosphor_dsp::dx7;
153
154 fn dx7_track() -> NavState {
156 let mut nav = NavState::new(super::super::initial_tracks());
157 let mut track = TrackState::new("dx7", 0, true, TrackKind::Instrument, vec![]);
158 track.instrument_type = Some(InstrumentType::DX7);
159 track.synth_params = dx7::PARAM_DEFAULTS.to_vec();
160 nav.tracks.insert(0, track);
161 nav.track_cursor = 0;
162 nav
163 }
164
165 fn selected(nav: &NavState) -> (usize, usize) {
166 let p = &nav.tracks[0].synth_params;
167 (dx7::bank_index(p[dx7::P_BANK]), dx7::patch_index(p[dx7::P_PATCH]))
168 }
169
170 #[test]
171 fn dx7_selectors_move_one_step_per_keypress() {
172 let mut nav = dx7_track();
176 nav.clip_view.synth_param_cursor = dx7::P_PATCH;
177 let (bank, patch) = selected(&nav);
178 for step in 1..=5 {
179 nav.adjust_synth_param(0.05);
180 assert_eq!(selected(&nav), (bank, patch + step), "patch knob step {step}");
181 }
182 for step in (0..5).rev() {
183 nav.adjust_synth_param(-0.05);
184 assert_eq!(selected(&nav), (bank, patch + step), "patch knob back to {step}");
185 }
186
187 nav.clip_view.synth_param_cursor = dx7::P_BANK;
188 for step in 1..dx7::BANK_COUNT {
189 nav.adjust_synth_param(0.05);
190 assert_eq!(selected(&nav), (step, patch), "bank knob step {step}");
191 }
192 for _ in 0..4 {
194 nav.adjust_synth_param(0.05);
195 }
196 assert_eq!(selected(&nav), (dx7::BANK_COUNT - 1, patch));
197 }
198
199 #[test]
200 fn the_dx7_bank_knob_is_the_last_parameter() {
201 let nav = dx7_track();
205 assert_eq!(nav.tracks[0].synth_params.len(), dx7::PARAM_COUNT);
206 assert_eq!(dx7::P_BANK, dx7::PARAM_COUNT - 1);
207 assert_eq!(dx7::PARAM_NAMES[dx7::P_GAIN], "gain", "index 0-7 must not move");
208 }
209}