Skip to main content

phosphor_app/state/
params.rs

1//! NavState methods: params.
2
3use super::*;
4
5impl NavState {
6
7    /// Adjust the currently selected synth parameter by delta.
8    /// Returns the (mixer_id, param_index, new_value) if changed, for sending to audio.
9    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                // Index 0 is always a discrete selector (waveform for synth, kit for drums)
14                // Synth: 4 options → step 0.25. Drums: 5 kits → step 0.20
15                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_discrete = if is_jupiter {
19                    phosphor_dsp::jupiter::is_discrete(idx)
20                } else if is_odyssey {
21                    phosphor_dsp::odyssey::is_discrete(idx)
22                } else if is_juno {
23                    phosphor_dsp::juno::is_discrete(idx)
24                } else {
25                    idx == 0
26                };
27                let actual_delta = if is_discrete {
28                    let step = if is_jupiter {
29                        match idx {
30                            0 => 1.0 / (phosphor_dsp::jupiter::PATCH_COUNT as f32 - 0.01),
31                            _ => 0.25,
32                        }
33                    } else if is_odyssey {
34                        match idx {
35                            0 => 1.0 / (phosphor_dsp::odyssey::PATCH_COUNT as f32 - 0.01),
36                            6 => 0.34, // 3 filter types
37                            _ => 0.5,
38                        }
39                    } else if is_juno {
40                        match idx {
41                            0 => 1.0 / (phosphor_dsp::juno::PATCH_COUNT as f32 - 0.01),
42                            12 => 0.25, // 4 chorus modes
43                            _ => 0.5,   // on/off switches
44                        }
45                    } else {
46                        match track.instrument_type {
47                            Some(InstrumentType::DrumRack) => 0.1, // 10 kits
48                            Some(InstrumentType::DX7) => 1.0 / (phosphor_dsp::dx7::PATCH_COUNT as f32 - 0.01),
49                            _ => 0.25,
50                        }
51                    };
52                    if delta > 0.0 { step } else { -step }
53                } else {
54                    delta
55                };
56                let new_val = (track.synth_params[idx] + actual_delta).clamp(0.0, 1.0);
57                track.synth_params[idx] = new_val;
58
59                // When patch selector changes, sync all params from preset
60                if idx == 0 {
61                    let new_params = match track.instrument_type {
62                        Some(InstrumentType::Jupiter8) => {
63                            Some(phosphor_dsp::jupiter::Jupiter8Synth::params_for_patch(new_val))
64                        }
65                        Some(InstrumentType::Odyssey) => {
66                            Some(phosphor_dsp::odyssey::OdysseySynth::params_for_patch(new_val))
67                        }
68                        Some(InstrumentType::Juno60) => {
69                            Some(phosphor_dsp::juno::Juno60Synth::params_for_patch(new_val))
70                        }
71                        _ => None,
72                    };
73                    if let Some(preset_params) = new_params {
74                        for (i, &v) in preset_params.iter().enumerate() {
75                            track.synth_params[i] = v;
76                        }
77                    }
78                }
79
80                if let Some(mixer_id) = track.mixer_id {
81                    return Some((mixer_id, idx, new_val));
82                }
83            }
84        }
85        None
86    }
87
88
89    /// Show controls for the currently selected track and route MIDI to it.
90    /// For instrument tracks: opens clip view with Synth tab, activates MIDI input.
91    /// For bus tracks: no clip view, deactivates MIDI.
92    pub fn show_current_track_controls(&mut self) {
93        // Deactivate MIDI on ALL tracks first
94        for track in &self.tracks {
95            if let Some(ref h) = track.handle {
96                h.config.midi_active.store(false, std::sync::atomic::Ordering::Relaxed);
97            }
98        }
99
100        if let Some(track) = self.tracks.get(self.track_cursor) {
101            if track.is_live() {
102                if let Some(ref h) = track.handle {
103                    h.config.midi_active.store(true, std::sync::atomic::Ordering::Relaxed);
104                }
105                self.clip_view_visible = true;
106
107                // Use the currently selected clip element, or default to clip 0
108                let clip_idx = match self.track_element {
109                    super::TrackElement::Clip(i) if i < track.clips.len() => i,
110                    _ => 0,
111                };
112                self.clip_view_target = Some((self.track_cursor, clip_idx));
113
114                // If track has recorded clips, show piano roll. Otherwise show synth.
115                if !track.clips.is_empty() {
116                    self.clip_view.clip_tab = ClipTab::PianoRoll;
117                    self.clip_view.focus = ClipViewFocus::PianoRoll;
118                    self.clip_view.piano_roll.focus = PianoRollFocus::Navigation;
119                    self.clip_view.piano_roll.column = 0;
120                } else {
121                    self.clip_view.fx_panel_tab = FxPanelTab::Synth;
122                    self.clip_view.focus = ClipViewFocus::FxPanel;
123                    self.clip_view.synth_param_cursor = 0;
124                }
125            } else {
126                // Bus track — hide clip view
127                self.clip_view_visible = false;
128                self.clip_view_target = None;
129            }
130        }
131    }
132
133}