diff --git a/theremotion-ui/ui/main.slint b/theremotion-ui/ui/main.slint index 25c1059..2634400 100644 --- a/theremotion-ui/ui/main.slint +++ b/theremotion-ui/ui/main.slint @@ -109,6 +109,7 @@ export component MainWindow inherits Window { in property lead-volume <=> tplay.volume; in property chords-number <=> tplay.chords-number; + in property drone-number <=> tplay.drone-number; in property raw-note <=> tplay.tuner-note; in property tuned-note <=> tplay.tuner-note-tuned; in property tuner-note-focus <=> tplay.tuner-note-focus; diff --git a/theremotion-ui/ui/plot.slint b/theremotion-ui/ui/plot.slint index df0e243..6abc7d8 100644 --- a/theremotion-ui/ui/plot.slint +++ b/theremotion-ui/ui/plot.slint @@ -5,6 +5,11 @@ export struct Graduation { text: string } +export struct GraduationValue { + value: float, + color: color, +} + export component Plot inherits BevelRectangle { in property text; in property icon; @@ -37,7 +42,10 @@ export component Plot inherits BevelRectangle { export component Plot1D inherits Plot { in property min: 0; in property max: 2; - in property value: 1.6; + in property <[GraduationValue]> values: [ + {value: 1.5, color: Palette.drone}, + {value: 1, color: Palette.raw}, + ]; in property <[Graduation]> graduations: [ {value: 1, text: "1"}, {value: 1.5, text: "1.5"}, @@ -62,6 +70,7 @@ export component Plot1D inherits Plot { width: 60%; background: Palette.plot-grad; height: Style.grad-light-thickness; + border-radius: self.height / 2; y: to-px-y(graduation.value); GradText { text: graduation.text; @@ -69,12 +78,12 @@ export component Plot1D inherits Plot { } } - Rectangle { + for value in values: Rectangle { border-radius: Style.area-corner-radius; - background: Palette.raw; + background: value.color; opacity: 0.5; height: root.height - self.y; - y: to-px-y(value); + y: to-px-y(value.value); } } } @@ -142,7 +151,7 @@ export component Tuner inherits Plot { width: Style.grad-strong-thickness; x: note-to-px(octave * 12 + note.value) - self.width / 2; GradText { - text: "\{note.text}\{octave}"; + text: "\{note.text}"; x: 15px; } } diff --git a/theremotion-ui/ui/tab-play.slint b/theremotion-ui/ui/tab-play.slint index 9bd738b..7442517 100644 --- a/theremotion-ui/ui/tab-play.slint +++ b/theremotion-ui/ui/tab-play.slint @@ -1,4 +1,4 @@ -import { Plot1D, Plot2D, Tuner } from "plot.slint"; +import { Plot1D, Plot2D, Tuner, GraduationValue } from "plot.slint"; import { Tab, Circle, Palette, Style, NotePoint, Handedness , VL, HL, IHL} from "common.slint"; import { EditMode , KeyboardFooter} from "keyboard.slint"; @@ -72,13 +72,17 @@ component FilterPlot inherits Plot2D { } component VolumePlot inherits Plot1D { + in property volume; text: "Volume"; graduations: []; + values: [{color: Palette.raw, value: volume}]; min: 0; max: 1; } component ChordNumberPlot inherits Plot1D { + in property chords-number; + in property drone-number; text: "Chord"; graduations: [ // Graduations are offset by one: @@ -87,6 +91,10 @@ component ChordNumberPlot inherits Plot1D { {value: 2, text: "3"}, {value: 3, text: "4"}, ]; + values: [ + {color: Palette.raw, value: chords-number}, + {color: Palette.drone, value: drone-number}, + ]; min: 0; max: 4; } @@ -106,6 +114,7 @@ export component PlayingTab inherits Tab { in property <[NotePoint]> notes: []; in property volume: 0; in property chords_number: 0; + in property drone-number: 0; in property tuner-note: 0; in property tuner-note-tuned: 0; in-out property tuner-note-focus: 0; @@ -116,7 +125,7 @@ export component PlayingTab inherits Tab { if handedness == Handedness.RightHanded: IHL { VolumePlot { width: gauge-width; - value: volume; + volume: volume; } FilterPlot { cutoff: filter-cutoff; @@ -137,14 +146,16 @@ export component PlayingTab inherits Tab { } ChordNumberPlot { width: gauge-width; - value: chords-number; + chords-number: chords-number; + drone-number: drone-number; } } if handedness == Handedness.LeftHanded: IHL { spacing: 4px; ChordNumberPlot { width: gauge-width; - value: chords-number; + chords-number: chords-number; + drone-number: drone-number; } PitchPlot { min-x: -2.0; @@ -168,7 +179,7 @@ export component PlayingTab inherits Tab { } VolumePlot { width: gauge-width; - value: volume; + volume: volume; } } Tuner { diff --git a/theremotion/src/conductor_thread.rs b/theremotion/src/conductor_thread.rs index 11cad85..132f021 100644 --- a/theremotion/src/conductor_thread.rs +++ b/theremotion/src/conductor_thread.rs @@ -306,6 +306,38 @@ impl Conductor { .zip(lead_volumes.into_iter().map(Volume)) .collect_vec(); let lead_chord = [lead_chord[0], lead_chord[1], lead_chord[2], lead_chord[3]]; + if h.grab >= 1.0 { + if let Some(drone_volume_angle) = h.rotation_from_body() { + let (init_drone_volume, init_drone_volume_angle) = *self + .play_state + .drone_grab_state + .get_or_insert((self.play_state.drone_state, drone_volume_angle)); + let offset = drone_volume_angle - init_drone_volume_angle; + self.play_state.drone_state = (init_drone_volume + offset).clamp(0.0, 5.0); + let drone_volumes = [0.0, 1.0, 2.0, 3.0] + .map(|v| (self.play_state.drone_state.clamp(0.0, 4.0) - v).clamp(0.0, 1.0)); + let drone_interval = preset.drone_interval(); + for ((control, drone), volume) in self + .controls + .drone_notes + .iter() + .zip(preset.drone_notes()) + .zip(drone_volumes) + { + if let Some(drone) = drone { + control + .note + .send(dsp_tx, ((drone + drone_interval).into_byte()) as f32)?; + control.volume.send(dsp_tx, volume)?; + } else { + control.volume.send(dsp_tx, 0.0)?; + } + } + } + } else { + self.play_state.drone_grab_state = None; + } + ui_tx.send(ui_thread::Msg::DroneNumber(self.play_state.drone_state))?; ui_tx.send(ui_thread::Msg::AutotuneAmount(autotune))?; ui_tx.send(ui_thread::Msg::Lead( lead_chord, @@ -364,37 +396,6 @@ impl Conductor { .controls .lead_volume .get_scaled(position_from_body.y, &(300.0..=400.0)); - if h.grab >= 1.0 { - if let Some(drone_volume_angle) = h.rotation_from_body() { - let (init_drone_volume, init_drone_volume_angle) = *self - .play_state - .drone_grab_state - .get_or_insert((self.play_state.drone_state, drone_volume_angle)); - let offset = drone_volume_angle - init_drone_volume_angle; - self.play_state.drone_state = (init_drone_volume + offset).clamp(0.0, 5.0); - let drone_volumes = [0.0, 1.0, 2.0, 3.0] - .map(|v| (self.play_state.drone_state.clamp(0.0, 4.0) - v).clamp(0.0, 1.0)); - let drone_interval = preset.drone_interval(); - for ((control, drone), volume) in self - .controls - .drone_notes - .iter() - .zip(preset.drone_notes()) - .zip(drone_volumes) - { - if let Some(drone) = drone { - control - .note - .send(dsp_tx, ((drone + drone_interval).into_byte()) as f32)?; - control.volume.send(dsp_tx, volume)?; - } else { - control.volume.send(dsp_tx, 0.0)?; - } - } - } - } else { - self.play_state.drone_grab_state = None; - } self.controls.cutoff_note.send(dsp_tx, cutoff_note)?; self.controls.lead_volume.send(dsp_tx, lead_volume)?; self.controls.resonance.send(dsp_tx, resonance)?; diff --git a/theremotion/src/ui_thread.rs b/theremotion/src/ui_thread.rs index 4384af8..3dc1d1f 100644 --- a/theremotion/src/ui_thread.rs +++ b/theremotion/src/ui_thread.rs @@ -24,6 +24,8 @@ pub enum Msg { Lead([(MidiNoteF, Volume); 4], Vector2), /// Floating number of chord notes (2.5 is 2 chord notes and the next half volume) ChordsNumber(f32), + /// Floating number of drone notes + DroneNumber(f32), /// Lead instrument note, without autotune RawNote(MidiNoteF), /// Filter cutoff and resonance. @@ -225,6 +227,7 @@ fn read_updates( } } Msg::ChordsNumber(c) => window.set_chords_number(c), + Msg::DroneNumber(d) => window.set_drone_number(d), Msg::RawNote(n) => { window.set_tuner_note_focus(restricted_scale_window.closest_in_scale(n).0); window.set_raw_note(n.0);