diff --git a/src/device/chain/plugin.rs b/src/device/chain/plugin.rs index bf4c4ea7..87480749 100644 --- a/src/device/chain/plugin.rs +++ b/src/device/chain/plugin.rs @@ -171,13 +171,24 @@ pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) let Rect { x, y, height, .. } = area; let mut width = 40u16; match &state.plugin { - Some(PluginKind::LV2 { portList, .. }) => { + Some(PluginKind::LV2 { portList, instance, .. }) => { + let start = state.selected.saturating_sub((height as usize / 2).saturating_sub(1)); + let end = start + height as usize - 2; //draw_box(buf, Rect { x, y, width, height }); - for i in 0..height-3 { - if let Some(port) = portList.get(i as usize) { - let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value); + for i in start..end { + if let Some(port) = portList.get(i) { + let value = if let Some(value) = instance.control_input(port.index) { + value + } else { + port.default_value + }; + let label = &format!("C·· M·· {:25} = {value:.03}", port.name); width = width.max(label.len() as u16); - label.blit(buf, x + 2, y + 2 + i as u16, None); + label.blit(buf, x + 2, y + 1 + i as u16 - start as u16, if i == state.selected { + Some(Style::default().green()) + } else { + None + }); } else { break } @@ -200,26 +211,67 @@ fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usu pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually { handle_keymap(s, event, keymap!(Plugin { + [Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{ - s.selected = s.selected.saturating_sub(1); - if s.selected < s.offset { - s.offset = s.selected; + if s.selected > 0 { + s.selected = s.selected - 1 + } else { + s.selected = match &s.plugin { + Some(PluginKind::LV2 { portList, .. }) => portList.len() - 1, + _ => 0 + } } Ok(true) }], + [Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{ s.selected = s.selected + 1; - if s.selected >= s.offset + 19 { - s.offset = s.offset + 1; + match &s.plugin { + Some(PluginKind::LV2 { portList, .. }) => { + if s.selected >= portList.len() { + s.selected = 0; + } + }, + _ => {} } Ok(true) }], + + [Char(','), NONE, "decrement", "decrement value", + |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2 { portList, ref mut instance, .. }) => { + let index = portList[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value - 0.01); + } + }, + _ => {} + } + Ok(true) + }], + + [Char('.'), NONE, "increment", "increment value", + |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2 { portList, ref mut instance, .. }) => { + let index = portList[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value + 0.01); + } + }, + _ => {} + } + Ok(true) + }], + [Char('m'), NONE, "toggle_midi_map", "toggle midi map mode", |s: &mut Plugin|{ s.mapping = !s.mapping; Ok(true) }] + })) } diff --git a/src/device/launcher/handle.rs b/src/device/launcher/handle.rs index f7983133..0bfc7de4 100644 --- a/src/device/launcher/handle.rs +++ b/src/device/launcher/handle.rs @@ -115,18 +115,18 @@ fn toggle_help (state: &mut Launcher) -> Usually { } fn focus_next (state: &mut Launcher) -> Usually { match state.view { - LauncherView::Tracks => { state.view = LauncherView::Chains; }, - LauncherView::Chains => { state.view = LauncherView::Sequencer; }, - LauncherView::Sequencer => { state.view = LauncherView::Tracks; }, + LauncherView::Tracks => { state.view = LauncherView::Sequencer; }, + LauncherView::Sequencer => { state.view = LauncherView::Chains; }, + LauncherView::Chains => { state.view = LauncherView::Tracks; }, _ => {}, }; Ok(true) } fn focus_prev (state: &mut Launcher) -> Usually { match state.view { - LauncherView::Tracks => { state.view = LauncherView::Sequencer; }, - LauncherView::Sequencer => { state.view = LauncherView::Chains; }, - LauncherView::Chains => { state.view = LauncherView::Tracks; }, + LauncherView::Tracks => { state.view = LauncherView::Chains; }, + LauncherView::Chains => { state.view = LauncherView::Sequencer; }, + LauncherView::Sequencer => { state.view = LauncherView::Tracks; }, _ => {}, }; Ok(true) diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index 3465c1c0..47facbcf 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -60,7 +60,7 @@ impl Launcher { monitoring: true, recording: false, overdub: true, - cursor: (1, 1), + cursor: (2, 2), position: 0, scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), tracks: if let Some(tracks) = tracks { tracks } else { vec![ @@ -184,7 +184,7 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually Cont type MIDIChunk = [Option>>]; /// Add "all notes off" to the start of a buffer. -fn all_notes_off (output: &mut MIDIChunk) { +pub fn all_notes_off (output: &mut MIDIChunk) { output[0] = Some(vec![]); if let Some(Some(frame)) = output.get_mut(0) { let mut buf = vec![]; @@ -83,7 +83,7 @@ fn all_notes_off (output: &mut MIDIChunk) { } } -fn play_from_sequence ( +pub fn play_from_sequence ( output: &mut MIDIChunk, sequence: &PhraseData, timebase: &Arc, @@ -113,7 +113,7 @@ fn play_from_sequence ( } } -fn monitor_and_record ( +pub fn monitor_and_record ( scope: &ProcessScope, output: &mut MIDIChunk, sequence: &mut PhraseData, diff --git a/src/main.rs b/src/main.rs index dedd65d5..4945e720 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,7 @@ fn main () -> Result<(), Box> { let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?; let timebase = Arc::new(Timebase { rate: AtomicUsize::new(client.sample_rate()), - tempo: AtomicUsize::new(113000), + tempo: AtomicUsize::new(150000), ppq: AtomicUsize::new(96), }); let ppq = timebase.ppq() as u32; @@ -64,34 +64,6 @@ fn main () -> Result<(), Box> { sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh.wav"), ])))?.boxed(), ]), Some(vec![ - Phrase::new("4K", ppq * 4, Some(BTreeMap::from([ - play!(0 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ] ), - play!(4 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ]), - play!(8 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ]), - play!(12 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ]), - ]))), - Phrase::new("KS", ppq * 4, Some(BTreeMap::from([ - play!(0 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ] ), - play!(4 => [ - MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - ]), - play!(10 => [ - MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - ]), - play!(12 => [ - MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - ]), - ]))), Phrase::new("KSH", ppq * 4, Some(BTreeMap::from([ play!(0 => [ MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, @@ -123,20 +95,57 @@ fn main () -> Result<(), Box> { MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, ]), ]))), + Phrase::new("4K", ppq * 4, Some(BTreeMap::from([ + play!(0 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ] ), + play!(4 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ]), + play!(8 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ]), + play!(12 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ]), + ]))), + Phrase::new("KS", ppq * 4, Some(BTreeMap::from([ + play!(0 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ] ), + play!(4 => [ + MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + ]), + play!(10 => [ + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ]), + play!(12 => [ + MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + ]), + ]))), ]))?, Track::new("Helm", &timebase, Some(vec![ - Plugin::lv2("Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(), + //Plugin::lv2("Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(), + Plugin::lv2("Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(), ]), Some(vec![ Phrase::new("E G A Bb", ppq * 4, Some(BTreeMap::from([ - ( ppq / 2 + ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 1 / 2, vec![MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 1, vec![MidiMessage::NoteOn { key: 39.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 3 / 2, vec![MidiMessage::NoteOff { key: 39.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 2, vec![MidiMessage::NoteOn { key: 41.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 5 / 2, vec![MidiMessage::NoteOff { key: 41.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 3, vec![MidiMessage::NoteOn { key: 42.into(), vel: 100.into() }] ), - ( ppq / 2 + ppq * 7 / 2 - 1, vec![MidiMessage::NoteOff { key: 42.into(), vel: 100.into() }] ), + play!(2 => [ + MidiMessage::NoteOff { key: 42.into(), vel: 100.into() }, + MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + ]), + play!(6 => [ + MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, + MidiMessage::NoteOn { key: 39.into(), vel: 100.into() }, + ]), + play!(10 => [ + MidiMessage::NoteOff { key: 39.into(), vel: 100.into() }, + MidiMessage::NoteOn { key: 41.into(), vel: 100.into() }, + ]), + play!(14 => [ + MidiMessage::NoteOff { key: 41.into(), vel: 100.into() }, + MidiMessage::NoteOn { key: 42.into(), vel: 100.into() }, + ]), ]))) ]))?,