160 lines
4.4 KiB
Rust
160 lines
4.4 KiB
Rust
use params::{ParamId, ParamStore};
|
|
use vizia::prelude::*;
|
|
|
|
use crate::widgets::piano::PianoKeyboard;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Data)]
|
|
pub enum Panel {
|
|
Osc,
|
|
Env,
|
|
Lfo,
|
|
Filter,
|
|
Fx,
|
|
ModMatrix,
|
|
}
|
|
|
|
impl Panel {
|
|
pub const ALL: &'static [Self] = &[
|
|
Self::Osc,
|
|
Self::Env,
|
|
Self::Lfo,
|
|
Self::Filter,
|
|
Self::Fx,
|
|
Self::ModMatrix,
|
|
];
|
|
pub fn label(self) -> &'static str {
|
|
match self {
|
|
Self::Osc => "OSC",
|
|
Self::Env => "ENV",
|
|
Self::Lfo => "LFO",
|
|
Self::Filter => "FLT",
|
|
Self::Fx => "FX",
|
|
Self::ModMatrix => "MOD",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Lens, Clone)]
|
|
pub struct AppData {
|
|
pub params: Vec<f32>,
|
|
pub active_panel: Panel,
|
|
pub preset_name: String,
|
|
pub voice_count: u8,
|
|
pub cpu_load: f32,
|
|
pub host_bpm: f32,
|
|
#[lens(ignore)]
|
|
pub store: ParamStore,
|
|
pub held_notes: Vec<u8>,
|
|
pub octave: i32,
|
|
}
|
|
|
|
impl AppData {
|
|
pub fn new(store: ParamStore) -> Self {
|
|
let params = (0..ParamId::COUNT)
|
|
.map(|i| store.get(unsafe { std::mem::transmute::<usize, ParamId>(i) }))
|
|
.collect();
|
|
Self {
|
|
params,
|
|
store,
|
|
active_panel: Panel::Osc,
|
|
preset_name: "Init".into(),
|
|
voice_count: 0,
|
|
cpu_load: 0.0,
|
|
host_bpm: 120.0,
|
|
held_notes: Vec::new(),
|
|
octave: 4,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum AppEvent {
|
|
SetParam(ParamId, f32),
|
|
SetPanel(Panel),
|
|
UpdateMetrics { voices: u8, cpu: f32 },
|
|
|
|
NoteOn(u8, u8),
|
|
NoteOff(u8),
|
|
OctaveUp,
|
|
OctaveDown,
|
|
}
|
|
|
|
impl Model for AppData {
|
|
fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
|
|
event.map(|e: &AppEvent, _| match e {
|
|
AppEvent::SetParam(id, val) => {
|
|
let v = val.clamp(0.0, 1.0);
|
|
self.params[*id as usize] = v;
|
|
self.store.set(*id, v);
|
|
}
|
|
AppEvent::SetPanel(p) => self.active_panel = *p,
|
|
AppEvent::UpdateMetrics { voices, cpu } => {
|
|
self.voice_count = *voices;
|
|
self.cpu_load = *cpu;
|
|
}
|
|
AppEvent::NoteOn(note, _vel) => {
|
|
if !self.held_notes.contains(note) {
|
|
self.held_notes.push(*note);
|
|
}
|
|
}
|
|
AppEvent::NoteOff(note) => self.held_notes.retain(|n| n != note),
|
|
AppEvent::OctaveUp => self.octave = (self.octave + 1).min(8),
|
|
AppEvent::OctaveDown => self.octave = (self.octave - 1).max(0),
|
|
});
|
|
event.map(|we: &WindowEvent, _| {
|
|
crate::widgets::piano::handle_kbd(cx, we, self.octave);
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn build_root(cx: &mut Context) {
|
|
VStack::new(cx, |cx| {
|
|
crate::panels::header::build(cx);
|
|
|
|
HStack::new(cx, |cx| {
|
|
VStack::new(cx, |cx| {
|
|
for &p in Panel::ALL {
|
|
tab_button(cx, p);
|
|
}
|
|
})
|
|
.width(Pixels(56.0))
|
|
.height(Stretch(1.0))
|
|
.background_color(Color::rgb(14, 14, 26));
|
|
|
|
Binding::new(cx, AppData::active_panel, |cx, panel_lens| {
|
|
VStack::new(cx, |cx| match panel_lens.get(cx) {
|
|
Panel::Osc => crate::panels::osc::build(cx),
|
|
Panel::Env => crate::panels::env_panel::build(cx),
|
|
Panel::Lfo => crate::panels::lfo::build(cx),
|
|
Panel::Filter => crate::panels::filter::build(cx),
|
|
Panel::Fx => crate::panels::fx::build(cx),
|
|
Panel::ModMatrix => crate::panels::mod_matrix::build(cx),
|
|
})
|
|
.class("panel")
|
|
.width(Stretch(1.0))
|
|
.height(Stretch(1.0));
|
|
});
|
|
|
|
crate::panels::macro_bar::build(cx);
|
|
})
|
|
.height(Stretch(1.0));
|
|
PianoKeyboard::new(cx);
|
|
})
|
|
.background_color(Color::rgb(18, 18, 28))
|
|
.width(Stretch(1.0))
|
|
.height(Stretch(1.0));
|
|
}
|
|
|
|
fn tab_button(cx: &mut Context, p: Panel) {
|
|
Binding::new(cx, AppData::active_panel, move |cx, active_lens| {
|
|
let al = active_lens.get(cx) == p;
|
|
Label::new(cx, p.label())
|
|
.class("tab")
|
|
.checked(al)
|
|
.on_press(move |cx| cx.emit(AppEvent::SetPanel(p)))
|
|
.width(Stretch(1.0))
|
|
.height(Pixels(44.0))
|
|
.text_align(TextAlign::Center);
|
|
});
|
|
}
|