diff --git a/src/control/launcher.rs b/src/control/launcher.rs index f35a6e5e..752a1306 100644 --- a/src/control/launcher.rs +++ b/src/control/launcher.rs @@ -31,8 +31,8 @@ pub const KEYMAP: &'static [KeyBinding] = keymap!(Launcher { [Char('r'), NONE, "record_toggle", "toggle recording", record_toggle], [Char('d'), NONE, "overdub_toggle", "toggle overdub", overdub_toggle], [Char('m'), NONE, "monitor_toggle", "toggle input monitoring", monitor_toggle], - [Char('r'), CONTROL, "rename", "rename current element", rename], - [Char('t'), CONTROL, "add_track", "add a new track", add_track], + //[Char('r'), CONTROL, "rename", "rename current element", rename], + //[Char('t'), CONTROL, "add_track", "add a new track", add_track], //[Char(' '), SHIFT, "play_start", "play from start", play_start], }); pub const KEYMAP_TRACKS: &'static [KeyBinding] = keymap!(Launcher { @@ -105,16 +105,16 @@ fn activate (_: &mut Launcher) -> Usually { Ok(true) } -fn rename (_: &mut Launcher) -> Usually { - Ok(true) -} +//fn rename (_: &mut Launcher) -> Usually { + //Ok(true) +//} -fn add_track (state: &mut Launcher) -> Usually { - let name = format!("Track {}", state.tracks.len() + 1); - state.tracks.push(Track::new(&name, &state.timebase, None, None)?); - state.cursor.0 = state.tracks.len(); - Ok(true) -} +//fn add_track (state: &mut Launcher) -> Usually { + //let name = format!("Track {}", state.tracks.len() + 1); + //state.tracks.push(Track::new(&name, &state.timebase, None, None)?); + //state.cursor.0 = state.tracks.len(); + //Ok(true) +//} fn delete_track (state: &mut Launcher) -> Usually { if state.tracks.len() > 0 && state.cursor.0 >= 1 { diff --git a/src/control/sequencer.rs b/src/control/sequencer.rs index 93d29a92..d9115f3b 100644 --- a/src/control/sequencer.rs +++ b/src/control/sequencer.rs @@ -25,27 +25,8 @@ pub const KEYMAP: &'static [KeyBinding] = keymap!(Sequencer { [Char('s'), NONE, "stop_and_rewind", "Stop and rewind", stop_and_rewind], [Char('q'), NONE, "quantize_next", "Next quantize value", quantize_next], [Char('Q'), SHIFT, "quantize_prev", "Previous quantize value", quantize_prev], - [Char('n'), NONE, "note_axis", "Focus note axis", nop], - [Char('t'), NONE, "time_axis", "Focus time axis", nop], - [Char('v'), NONE, "variations", "Focus variation selector", nop], - [Char('s'), SHIFT, "sync", "Focus sync selector", nop], - [Char('1'), NONE, "seq_1", "Phrase 1", focus_seq(0)], - [Char('2'), NONE, "seq_2", "Phrase 2", focus_seq(1)], - [Char('3'), NONE, "seq_3", "Phrase 3", focus_seq(2)], - [Char('4'), NONE, "seq_4", "Phrase 4", focus_seq(3)], - [Char('5'), NONE, "seq_5", "Phrase 5", focus_seq(4)], - [Char('6'), NONE, "seq_6", "Phrase 6", focus_seq(5)], - [Char('7'), NONE, "seq_7", "Phrase 7", focus_seq(6)], - [Char('8'), NONE, "seq_8", "Phrase 8", focus_seq(7)], }); -const fn focus_seq (i: usize) -> impl Fn(&mut Sequencer)->Usually { - move |s: &mut Sequencer| { - s.sequence = Some(i); - Ok(true) - } -} - fn nop (_: &mut Sequencer) -> Usually { Ok(false) } diff --git a/src/core/layout.rs b/src/core/layout.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/main.rs b/src/main.rs index e67eb927..9f5828e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,15 @@ use crate::core::*; use crate::model::*; use crate::view::*; +macro_rules! phrase { + ($($t:expr => $msg:expr),* $(,)?) => {{ + let mut phrase = BTreeMap::new(); + $(phrase.insert($t, vec![]);)* + $(phrase.get_mut(&$t).unwrap().push($msg);)* + phrase + }} +} + pub fn main () -> Usually<()> { App::default().run(Some(|app: Arc>|{ let mut state = app.lock().unwrap(); @@ -26,20 +35,50 @@ pub fn main () -> Usually<()> { state.modal = Some(Box::new(crate::config::SetupModal(Some(xdg.clone())))); } state.scenes = vec![ - Scene::new("Intro", vec![None, None, None, None]), + Scene::new("Intro", vec![Some(0), Some(0), None, None]), ]; + let jack = jack_run("tek", &app)?; + let timebase = &state.timebase; + let ppq = timebase.ppq() as usize; state.tracks = vec![ - Track::new("Drums", &state.timebase, Some(vec![ + + Track::new("Drums", &jack.as_client(), &timebase, Some(vec![ + Sampler::new("Sampler", Some(BTreeMap::from([ sample!(36, "Kick", "/home/user/Lab/Music/pak/kik.wav"), sample!(40, "Snare", "/home/user/Lab/Music/pak/sna.wav"), sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh.wav"), ])))?.boxed(), + + Plugin::lv2( + "Panagement", + "file:///home/user/.lv2/Auburn Sounds Panagement 2.lv2" + )?.boxed(), + ]), Some(vec![ - Phrase::new("4 kicks", state.timebase.ppq() as usize * 4, None), + Phrase::new("4 kicks", ppq * 4, Some(phrase! { + 00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 04 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + })), + ]))?, + + Track::new("Bass", &jack.as_client(), &timebase, Some(vec![ + ]), Some(vec![ + Phrase::new("Offbeat", ppq * 4, Some(phrase! { + 00 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() }, + 02 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + 04 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() }, + 06 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + 08 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() }, + 10 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + 12 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() }, + 14 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, + })), ]))?, ]; - state.jack = Some(jack_run("tek", &app)?); + state.jack = Some(jack); Ok(()) })) } @@ -48,13 +87,15 @@ pub fn main () -> Usually<()> { pub struct App { pub xdg: Option>, pub jack: Option, - pub phrases: Vec, + pub scene: usize, pub scenes: Vec, + pub track_cursor: usize, pub tracks: Vec, pub frame: usize, - pub scene: Vec, pub timebase: Arc, pub modal: Option>, + pub section: usize, + pub entered: bool, } process!(App); @@ -75,23 +116,26 @@ render!(App |self, buf, area| { buf, area: Rect { x, y, width, height: height / 3 }, name: "", - focused: true, + focused: self.section == 0, scenes: &self.scenes, tracks: &self.tracks, - cursor: &(0, 0) + cursor: &(self.track_cursor, self.scene + 1) }.draw()?.height; - y = y + ChainView { - focused: true, - chain: Some(&self.tracks[0].chain), - }.render(buf, Rect { x, y, width, height: height / 3 })?.height; + if self.track_cursor > 0 { + let track = self.tracks.get(self.track_cursor - 1); + y = y + ChainView { + focused: self.section == 1, + chain: track.map(|t|&t.chain), + }.render(buf, Rect { x, y, width, height: height / 3 })?.height; - y = y + SequencerView { - focused: true, - ppq: self.timebase.ppq() as usize, - track: Some(&self.tracks[0]), - phrase: Some(&self.tracks[0].sequencer.phrases[0]), - }.render(buf, Rect { x, y, width, height })?.height; + y = y + SequencerView { + focused: self.section == 2, + ppq: self.timebase.ppq() as usize, + track: track, + phrase: track.map(|t|&t.sequencer.phrases[0]), + }.render(buf, Rect { x, y, width, height })?.height; + } if let Some(ref modal) = self.modal { for cell in buf.content.iter_mut() { @@ -134,27 +178,90 @@ handle!(App |self, e| { })) }); -fn increment (_: &mut App) -> Usually { Ok(true) } -fn decrement (_: &mut App) -> Usually { Ok(true) } -fn delete (_: &mut App) -> Usually { Ok(true) } +fn focus_next (app: &mut App) -> Usually { + if app.section >= 2 { + app.section = 0; + } else { + app.section = app.section + 1; + } + Ok(true) +} +fn focus_prev (app: &mut App) -> Usually { + if app.section == 0 { + app.section = 2; + } else { + app.section = app.section - 1; + } + Ok(true) +} +fn cursor_up (app: &mut App) -> Usually { + focus_prev(app) +} +fn cursor_down (app: &mut App) -> Usually { + focus_next(app) +} +fn cursor_left (app: &mut App) -> Usually { + match app.section { + 0 => { + app.track_cursor = if app.track_cursor == 0 { + app.tracks.len() + } else { + app.track_cursor + } - 1; + Ok(true) + } + _ => Ok(false) + } +} +fn cursor_right (app: &mut App) -> Usually { + match app.section { + 0 => { + app.track_cursor = if app.track_cursor >= app.tracks.len() { + 0 + } else { + app.track_cursor + 1 + }; + Ok(true) + }, + _ => Ok(false) + } +} +fn increment (app: &mut App) -> Usually { + match app.section { + 0 => clip_next(app), + _ => Ok(false) + } +} +fn decrement (app: &mut App) -> Usually { + match app.section { + 0 => clip_prev(app), + _ => Ok(false) + } +} +fn delete (app: &mut App) -> Usually { + match app.section { + 0 => delete_track(app), + _ => Ok(false) + } +} fn duplicate (_: &mut App) -> Usually { Ok(true) } fn activate (_: &mut App) -> Usually { Ok(true) } fn rename (_: &mut App) -> Usually { Ok(true) } fn add_track (app: &mut App) -> Usually { let name = format!("Track {}", app.tracks.len() + 1); - app.tracks.push(Track::new(&name, &app.timebase, None, None)?); + app.tracks.push(Track::new(&name, app.jack.as_ref().unwrap().as_client(), &app.timebase, None, None)?); + app.track_cursor = app.tracks.len() - 1; Ok(true) } -fn delete_track (_: &mut App) -> Usually { - Ok(true) +fn delete_track (app: &mut App) -> Usually { + if app.tracks.len() > 0 { + app.tracks.remove(app.track_cursor); + app.track_cursor = app.track_cursor.saturating_sub(1); + return Ok(true) + } + Ok(false) } -fn cursor_up (_: &mut App) -> Usually { Ok(true) } -fn cursor_down (_: &mut App) -> Usually { Ok(true) } -fn cursor_left (_: &mut App) -> Usually { Ok(true) } -fn cursor_right (_: &mut App) -> Usually { Ok(true) } fn toggle_help (_: &mut App) -> Usually { Ok(true) } -fn focus_next (_: &mut App) -> Usually { Ok(true) } -fn focus_prev (_: &mut App) -> Usually { Ok(true) } fn clip_next (_: &mut App) -> Usually { Ok(true) } fn clip_prev (_: &mut App) -> Usually { Ok(true) } fn play_toggle (_: &mut App) -> Usually { Ok(true) } @@ -175,14 +282,6 @@ fn monitor_toggle (_: &mut App) -> Usually { Ok(true) } //( $t1 * ppq / 4, vec![ $($msg),* ] ) //} //} - //macro_rules! phrase { - //($($t:expr => $msg:expr),* $(,)?) => {{ - //let mut phrase = BTreeMap::new(); - //$(phrase.insert($t, vec![]);)* - //$(phrase.get_mut(&$t).unwrap().push($msg);)* - //phrase - //}} - //} //Launcher::new("Launcher#0", &timebase, //Some(vec![ diff --git a/src/model/launcher.rs b/src/model/launcher.rs index 3e358b5d..510f1f99 100644 --- a/src/model/launcher.rs +++ b/src/model/launcher.rs @@ -61,7 +61,7 @@ impl Launcher { cursor: (2, 2), current_frame: 0, scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), - tracks: if let Some(tracks) = tracks { tracks } else { vec![ + tracks: vec![]/*if let Some(tracks) = tracks { tracks } else { vec![ Track::new("Track 1", &timebase, None, Some(vec![ Phrase::new("MIDI Clip 1", ppq * 4, Some(BTreeMap::from([ ( ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), @@ -70,7 +70,7 @@ impl Launcher { ( ppq * 3, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), ]))) ]))?, - ] }, + ] }*/, show_help: true, }) } diff --git a/src/model/track.rs b/src/model/track.rs index 8618ee65..1589aa81 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -5,17 +5,20 @@ pub struct Track { pub name: String, pub sequencer: Sequencer, pub chain: Chain, + pub midi_out: Port, } impl Track { pub fn new ( - name: &str, - tempo: &Arc, - devices: Option>>, - phrases: Option>, + name: &str, + jack: &Client, + timebase: &Arc, + devices: Option>>, + phrases: Option>, ) -> Usually { - let sequencer = Sequencer::new(&name, tempo, phrases)?; - let chain = Chain::new(&name, devices)?; + let sequencer = Sequencer::new(&name, timebase, phrases)?; + let chain = Chain::new(&name, devices)?; + let midi_out = jack.register_port(name, MidiOut)?; //let (client, _status) = Client::new("init", ClientOptions::NO_START_SERVER)?; //{ //if let (Some(output), Some(input)) = ( @@ -33,7 +36,7 @@ impl Track { //client.connect_ports_by_name(&output, &input)?; //} //} - Ok(Self { name: name.to_string(), sequencer, chain }) + Ok(Self { name: name.to_string(), sequencer, chain, midi_out }) } } diff --git a/src/view/chain.rs b/src/view/chain.rs index 1977831b..b0420518 100644 --- a/src/view/chain.rs +++ b/src/view/chain.rs @@ -37,7 +37,7 @@ pub fn draw_as_row ( state: &Chain, buf: &mut Buffer, area: Rect, style: Option