From 2165e5d45d0f431570d402846fd7be37a8cb49ef Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 3 Jul 2024 18:11:40 +0300 Subject: [PATCH] bye launcher --- src/control.rs | 308 +++++++++++++++++++++++++- src/control/launcher.rs | 236 -------------------- src/core.rs | 1 + src/core/jack.rs | 4 +- src/main.rs | 470 ++++++++-------------------------------- src/model.rs | 2 - src/model/launcher.rs | 184 ---------------- src/model/phrase.rs | 9 + src/view.rs | 48 +++- src/view/launcher.rs | 51 ----- 10 files changed, 451 insertions(+), 862 deletions(-) delete mode 100644 src/control/launcher.rs delete mode 100644 src/model/launcher.rs delete mode 100644 src/view/launcher.rs diff --git a/src/control.rs b/src/control.rs index 034ea823..ae5c9e12 100644 --- a/src/control.rs +++ b/src/control.rs @@ -1,9 +1,315 @@ pub mod chain; pub mod focus; -pub mod launcher; pub mod mixer; pub mod plugin; pub mod sampler; pub mod sequencer; pub use self::focus::*; + +use crate::{core::*, model::*, handle, App}; + +handle!(App |self, e| { + if let Some(ref mut modal) = self.modal { + if modal.handle(e)? { + self.modal = None; + return Ok(true) + }; + } + handle_keymap(self, e, KEYMAP) +}); + +const KEYMAP: &'static [KeyBinding] = keymap!(App { + [F(1), NONE, "toggle_help", "toggle help", toggle_help], + [Tab, NONE, "focus_next", "focus next area", focus_next], + [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], + [Up, NONE, "cursor_up", "move cursor up", cursor_up], + [Down, NONE, "cursor_down", "move cursor down", cursor_down], + [Left, NONE, "cursor_left", "move cursor left", cursor_left], + [Right, NONE, "cursor_right", "move cursor right", cursor_right], + [Char('.'), NONE, "increment", "increment value at cursor", increment], + [Char(','), NONE, "decrement", "decrement value at cursor", decrement], + [Delete, CONTROL, "delete", "delete track", delete], + [Char('d'), CONTROL, "duplicate", "duplicate scene or track", duplicate], + [Enter, NONE, "activate", "activate item at cursor", enter], + [Esc, NONE, "escape", "unfocus", escape], + [Char(' '), NONE, "toggle_play", "play or pause", toggle_play], + [Char('r'), NONE, "toggle_record", "toggle recording", toggle_record], + [Char('d'), NONE, "toggle_overdub", "toggle overdub", toggle_overdub], + [Char('m'), NONE, "toggle_monitor", "toggle input monitoring", toggle_monitor], + [Char('r'), CONTROL, "rename", "rename current element", rename], + [Char('t'), CONTROL, "add_track", "add a new track", add_track], + [Char('a'), CONTROL, "add_scene", "add a new scene", add_scene], + [Char('`'), NONE, "switch_mode", "switch the display mode", switch_mode], +}); + +fn toggle_play (app: &mut App) -> Usually { + app.playing = match app.playing.expect("after jack init") { + TransportState::Stopped => { + app.transport.as_ref().unwrap().start()?; + Some(TransportState::Starting) + }, + _ => { + app.transport.as_ref().unwrap().stop()?; + app.transport.as_ref().unwrap().locate(0)?; + Some(TransportState::Stopped) + }, + }; + Ok(true) +} + +fn toggle_record (_: &mut App) -> Usually { Ok(true) } + +fn toggle_overdub (_: &mut App) -> Usually { Ok(true) } + +fn toggle_monitor (_: &mut App) -> Usually { Ok(true) } + +fn toggle_help (_: &mut App) -> Usually { Ok(true) } + +fn switch_mode (app: &mut App) -> Usually { + match app.section { + 0 => {app.grid_mode = !app.grid_mode; Ok(true)}, + 1 => {app.chain_mode = !app.chain_mode; Ok(true)}, + 2 => {app.seq_mode = !app.seq_mode; Ok(true)}, + _ => Ok(false) + } +} + +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 { + if app.entered { + match app.section { + 0 => match app.grid_mode { + false => {app.scene_cursor = app.scene_cursor.saturating_sub(1); Ok(true)}, + true => {app.track_cursor = app.track_cursor.saturating_sub(1); Ok(true)}, + }, + _ => Ok(false) + } + } else { + focus_prev(app) + } +} + +fn cursor_down (app: &mut App) -> Usually { + if app.entered { + match app.section { + 0 => match app.grid_mode { + false => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); Ok(true)}, + true => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); Ok(true)}, + }, + _ => Ok(false) + } + } else { + focus_next(app) + } +} + +fn cursor_left (app: &mut App) -> Usually { + match app.section { + 0 => match app.grid_mode { + false => {app.track_cursor = app.track_cursor.saturating_sub(1); Ok(true)}, + true => {app.scene_cursor = app.scene_cursor.saturating_sub(1); Ok(true)}, + }, + _ => Ok(false) + } +} + +fn cursor_right (app: &mut App) -> Usually { + match app.section { + 0 => match app.grid_mode { + false => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); Ok(true)}, + true => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); Ok(true)}, + }, + _ => Ok(false) + } +} + +fn increment (app: &mut App) -> Usually { + match app.section { + 0 => clip_next(app), + _ => Ok(false) + } +} + +fn clip_next (_: &mut App) -> Usually { Ok(true) } +//fn clip_next (state: &mut Launcher) -> Usually { + //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { + //let scene_id = state.cursor.1 - 1; + //let clip_id = state.cursor.0 - 1; + //let scene = &mut state.scenes[scene_id]; + //scene.clips[clip_id] = match scene.clips[clip_id] { + //None => Some(0), + //Some(i) => if i >= state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1) { + //None + //} else { + //Some(i + 1) + //} + //}; + //} + //Ok(true) +//} + +fn decrement (app: &mut App) -> Usually { + match app.section { + 0 => clip_prev(app), + _ => Ok(false) + } +} + +fn clip_prev (_: &mut App) -> Usually { Ok(true) } +//fn clip_prev (state: &mut Launcher) -> Usually { + //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { + //let scene_id = state.cursor.1 - 1; + //let clip_id = state.cursor.0 - 1; + //let scene = &mut state.scenes[scene_id]; + //scene.clips[clip_id] = match scene.clips[clip_id] { + //None => Some(state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1)), + //Some(i) => if i == 0 { + //None + //} else { + //Some(i - 1) + //} + //}; + //} + //Ok(true) +//} + +fn activate (_: &mut App) -> Usually { Ok(true) } +//fn activate (_: &mut Launcher) -> Usually { + //unimplemented!(); + ////if let ( + ////Some((_scene_id, scene)), + ////Some((track_id, track)), + ////) = (state.scene_mut(), state.track_mut()) { + ////// Launch clip + ////if let Some(phrase_id) = scene.clips.get(track_id) { + ////track.sequencer.sequence = *phrase_id; + ////} + ////if state.playing == TransportState::Stopped { + ////state.transport.start()?; + ////state.playing = TransportState::Starting; + ////} + ////} else if let Some((_scene_id, scene)) = state.scene() { + ////// Launch scene + ////for (track_id, track) in state.tracks.iter().enumerate() { + ////if let Some(phrase_id) = scene.clips.get(track_id) { + ////track.sequencer.sequence = *phrase_id; + ////} + ////} + ////if state.playing == TransportState::Stopped { + ////state.transport.start()?; + ////state.playing = TransportState::Starting; + ////} + ////} else if let Some((_track_id, _track)) = state.track() { + ////// Rename track? + ////} + + ////let track = state.active_track().unwrap(); + ////let scene = state.active_scene(); + ////if state.cursor.1 >= 2 { + ////if let Some(Some(index)) = scene.clips.get(state.cursor.1 - 2) { + ////track.enqueue(index) + ////} else { + ////} + ////} + ////if state.cursor.0 >= 1 { + ////let sequencer = state.tracks.get_mut(state.cursor.0 - 1); + ////if state.cursor.1 >= 2 { + ////let scene = state.scenes.get_mut(state.cursor.1 - 2); + ////if let Some(index) = scene.get(state.cursor.0 - 1) { + ////let phrase = sequencer.phrases.get(index); + ////} else { + ////let index = sequencer.phrases.len(); + ////let phrase = Phrase::new(&format!("Phrase#{index}")); + ////sequencer.phrases.push(phrase); + ////scene[state.cursor.0 - 1] = Some(index); + ////} + ////} + ////} + //Ok(true) +//} + +fn delete (app: &mut App) -> Usually { + match app.section { + 0 => delete_track(app), + _ => Ok(false) + } +} + +fn enter (app: &mut App) -> Usually { + if app.entered { + activate(app) + } else { + app.entered = true; + Ok(true) + } +} + +fn escape (app: &mut App) -> Usually { + if app.entered { + app.entered = false; + Ok(true) + } else { + Ok(false) + } +} + +fn add_scene (app: &mut App) -> Usually { + let name = format!("Scene {}", app.scenes.len() + 1); + app.scenes.push(Scene::new(&name, vec![])); + app.scene_cursor = app.scenes.len(); + Ok(true) +} + +fn add_track (app: &mut App) -> Usually { + let name = format!("Track {}", app.tracks.len() + 1); + app.tracks.push(Track::new(&name, app.jack.as_ref().unwrap().as_client(), &app.timebase, None, None)?); + app.track_cursor = app.tracks.len(); + Ok(true) +} + +fn delete_track (app: &mut App) -> Usually { + if app.tracks.len() > 0 { + let track = app.tracks.remove(app.track_cursor); + app.track_cursor = app.track_cursor.saturating_sub(1); + app.jack.as_ref().unwrap().as_client().unregister_port(track.midi_out)?; + return Ok(true) + } + Ok(false) +} + +fn duplicate (_: &mut App) -> Usually { Ok(true) } + +fn rename (_: &mut App) -> Usually { Ok(true) } + +//fn toggle_record (s: &mut Launcher) -> Usually { + //s.sequencer_mut().map(|s|s.recording = !s.recording); + //Ok(true) +//} + +//fn toggle_overdub (s: &mut Launcher) -> Usually { + //s.sequencer_mut().map(|s|s.overdub = !s.overdub); + //Ok(true) +//} + +//fn toggle_monitor (s: &mut Launcher) -> Usually { + //s.sequencer_mut().map(|s|s.monitoring = !s.monitoring); + //Ok(true) +//} diff --git a/src/control/launcher.rs b/src/control/launcher.rs deleted file mode 100644 index 752a1306..00000000 --- a/src/control/launcher.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::{core::*,model::*}; - -pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually { - Ok(handle_keymap(state, event, KEYMAP)? || match state.view { - LauncherMode::Tracks => { - handle_keymap(state, event, KEYMAP_TRACKS)? - }, - LauncherMode::Sequencer => { - let i = state.col().saturating_sub(1); - if let Some(track) = state.tracks.get_mut(i) { - crate::control::sequencer::handle(&mut track.sequencer, event)? - } else { - true - } - }, - LauncherMode::Chains => { - let i = state.col().saturating_sub(1); - if let Some(track) = state.tracks.get_mut(i) { - crate::control::chain::handle(&mut track.chain, event)? - } else { - true - } - } - }) -} -pub const KEYMAP: &'static [KeyBinding] = keymap!(Launcher { - [F(1), NONE, "toggle_help", "toggle help", toggle_help], - [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], - [Tab, NONE, "focus_next", "focus next area", focus_next], - [Char(' '), NONE, "play_toggle", "play or pause", play_toggle], - [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(' '), SHIFT, "play_start", "play from start", play_start], -}); -pub const KEYMAP_TRACKS: &'static [KeyBinding] = keymap!(Launcher { - [Up, NONE, "cursor_up", "move cursor up", cursor_up], - [Down, NONE, "cursor_down", "move cursor down", cursor_down], - [Left, NONE, "cursor_left", "move cursor left", cursor_left], - [Right, NONE, "cursor_right", "move cursor right", cursor_right], - [Char('.'), NONE, "clip_next", "set clip to next phrase", clip_next], - [Char(','), NONE, "clip_prev", "set clip to last phrase", clip_prev], - [Delete, CONTROL, "delete_track", "delete track", delete_track], - [Char('d'), CONTROL, "duplicate", "duplicate scene or track", duplicate], - [Enter, NONE, "activate", "activate item at cursor", activate], -}); -fn duplicate (_: &mut Launcher) -> Usually { - unimplemented!(); - Ok(true) -} - -fn activate (_: &mut Launcher) -> Usually { - unimplemented!(); - //if let ( - //Some((_scene_id, scene)), - //Some((track_id, track)), - //) = (state.scene_mut(), state.track_mut()) { - //// Launch clip - //if let Some(phrase_id) = scene.clips.get(track_id) { - //track.sequencer.sequence = *phrase_id; - //} - //if state.playing == TransportState::Stopped { - //state.transport.start()?; - //state.playing = TransportState::Starting; - //} - //} else if let Some((_scene_id, scene)) = state.scene() { - //// Launch scene - //for (track_id, track) in state.tracks.iter().enumerate() { - //if let Some(phrase_id) = scene.clips.get(track_id) { - //track.sequencer.sequence = *phrase_id; - //} - //} - //if state.playing == TransportState::Stopped { - //state.transport.start()?; - //state.playing = TransportState::Starting; - //} - //} else if let Some((_track_id, _track)) = state.track() { - //// Rename track? - //} - - //let track = state.active_track().unwrap(); - //let scene = state.active_scene(); - //if state.cursor.1 >= 2 { - //if let Some(Some(index)) = scene.clips.get(state.cursor.1 - 2) { - //track.enqueue(index) - //} else { - //} - //} - //if state.cursor.0 >= 1 { - //let sequencer = state.tracks.get_mut(state.cursor.0 - 1); - //if state.cursor.1 >= 2 { - //let scene = state.scenes.get_mut(state.cursor.1 - 2); - //if let Some(index) = scene.get(state.cursor.0 - 1) { - //let phrase = sequencer.phrases.get(index); - //} else { - //let index = sequencer.phrases.len(); - //let phrase = Phrase::new(&format!("Phrase#{index}")); - //sequencer.phrases.push(phrase); - //scene[state.cursor.0 - 1] = Some(index); - //} - //} - //} - 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 delete_track (state: &mut Launcher) -> Usually { - if state.tracks.len() > 0 && state.cursor.0 >= 1 { - state.tracks.remove(state.cursor.0 - 1); - state.cursor.0 = state.cursor.0.min(state.tracks.len()); - } - Ok(true) -} - -fn cursor_up (state: &mut Launcher) -> Usually { - state.dec_row(); - Ok(true) -} - -fn cursor_down (state: &mut Launcher) -> Usually { - state.inc_row(); - Ok(true) -} - -fn cursor_left (state: &mut Launcher) -> Usually { - state.dec_col(); - Ok(true) -} - -fn cursor_right (state: &mut Launcher) -> Usually { - state.inc_col(); - Ok(true) -} - -fn toggle_help (state: &mut Launcher) -> Usually { - state.show_help = !state.show_help; - Ok(true) -} - -fn focus_next (state: &mut Launcher) -> Usually { - match state.view { - LauncherMode::Tracks => { state.view = LauncherMode::Sequencer; }, - LauncherMode::Sequencer => { state.view = LauncherMode::Chains; }, - LauncherMode::Chains => { state.view = LauncherMode::Tracks; }, - }; - Ok(true) -} - -fn focus_prev (state: &mut Launcher) -> Usually { - match state.view { - LauncherMode::Tracks => { state.view = LauncherMode::Chains; }, - LauncherMode::Chains => { state.view = LauncherMode::Sequencer; }, - LauncherMode::Sequencer => { state.view = LauncherMode::Tracks; }, - }; - Ok(true) -} - -fn clip_next (state: &mut Launcher) -> Usually { - if state.cursor.0 >= 1 && state.cursor.1 >= 1 { - let scene_id = state.cursor.1 - 1; - let clip_id = state.cursor.0 - 1; - let scene = &mut state.scenes[scene_id]; - scene.clips[clip_id] = match scene.clips[clip_id] { - None => Some(0), - Some(i) => if i >= state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1) { - None - } else { - Some(i + 1) - } - }; - } - Ok(true) -} - -fn clip_prev (state: &mut Launcher) -> Usually { - if state.cursor.0 >= 1 && state.cursor.1 >= 1 { - let scene_id = state.cursor.1 - 1; - let clip_id = state.cursor.0 - 1; - let scene = &mut state.scenes[scene_id]; - scene.clips[clip_id] = match scene.clips[clip_id] { - None => Some(state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1)), - Some(i) => if i == 0 { - None - } else { - Some(i - 1) - } - }; - } - Ok(true) -} - -fn play_toggle (s: &mut Launcher) -> Usually { - s.playing = match s.playing { - TransportState::Stopped => { - s.transport.start()?; - TransportState::Starting - }, - _ => { - s.transport.stop()?; - s.transport.locate(0)?; - TransportState::Stopped - }, - }; - Ok(true) -} -//fn play_start (_: &mut Launcher) -> Usually { - //unimplemented!() -//} - -fn record_toggle (s: &mut Launcher) -> Usually { - s.sequencer_mut().map(|s|s.recording = !s.recording); - Ok(true) -} - -fn overdub_toggle (s: &mut Launcher) -> Usually { - s.sequencer_mut().map(|s|s.overdub = !s.overdub); - Ok(true) -} - -fn monitor_toggle (s: &mut Launcher) -> Usually { - s.sequencer_mut().map(|s|s.monitoring = !s.monitoring); - Ok(true) -} - diff --git a/src/core.rs b/src/core.rs index 6849295d..dc193765 100644 --- a/src/core.rs +++ b/src/core.rs @@ -24,6 +24,7 @@ pub use crate::{ render, handle, process, + phrase, keymap, key }; diff --git a/src/core/jack.rs b/src/core/jack.rs index c4d33193..a8604318 100644 --- a/src/core/jack.rs +++ b/src/core/jack.rs @@ -16,8 +16,8 @@ pub fn jack_run (name: &str, app: &Arc>) -> Usually $msg:expr),* $(,)?) => {{ - let mut phrase = BTreeMap::new(); - $(phrase.insert($t, vec![]);)* - $(phrase.get_mut(&$t).unwrap().push($msg);)* - phrase - }} -} +use crate::{core::*, model::*}; pub fn main () -> Usually<()> { App::default().run(Some(|app: Arc>|{ @@ -80,7 +69,10 @@ pub fn main () -> Usually<()> { ]; state.track_cursor = 1; state.scene_cursor = 1; - state.midi_in = Some(jack.as_client().register_port("midi-in", MidiIn)?); + let client = jack.as_client(); + state.midi_in = Some(client.register_port("midi-in", MidiIn)?); + state.transport = Some(client.transport()); + state.playing = Some(TransportState::Stopped); state.jack = Some(jack); Ok(()) })) @@ -98,385 +90,93 @@ pub struct App { pub tracks: Vec, pub track_cursor: usize, pub frame: usize, - pub timebase: Arc, pub modal: Option>, pub section: usize, pub entered: bool, + pub playing: Option, + pub transport: Option, + pub timebase: Arc, + pub playhead: usize, pub midi_in: Option>, + pub audio_outs: Option>>, + pub metronome: bool, } -process!(App); - -render!(App |self, buf, area| { - let Rect { x, mut y, width, height } = area; - - y = y + TransportView { - timebase: &self.timebase, - playing: TransportState::Stopped, - record: false, - overdub: false, - monitor: false, - frame: 0, - }.render(buf, area)?.height; - - y = y + SceneGridView { - buf, - area: Rect { x, y, width, height: height / 3 }, - name: "", - mode: self.grid_mode, - focused: self.section == 0, - scenes: &self.scenes, - tracks: &self.tracks, - cursor: &(self.track_cursor, self.scene_cursor), - }.draw()?.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: 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; +process!(App |self, client, scope| { + let transport = self.transport.as_ref().unwrap().query().unwrap(); + self.playing = Some(transport.state); + self.playhead = transport.pos.frame() as usize; + for Track { sequencer, .. } in self.tracks.iter_mut() { + sequencer.process(client, scope); } - - if let Some(ref modal) = self.modal { - modal.render(buf, area)?; - } - - Ok(area) + Control::Continue }); -handle!(App |self, e| { - if let Some(ref mut modal) = self.modal { - if modal.handle(e)? { - self.modal = None; - return Ok(true) - }; - } - handle_keymap(self, e, KEYMAP) -}); - -const KEYMAP: &'static [KeyBinding] = keymap!(App { - [F(1), NONE, "toggle_help", "toggle help", toggle_help], - [Tab, NONE, "focus_next", "focus next area", focus_next], - [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], - [Up, NONE, "cursor_up", "move cursor up", cursor_up], - [Down, NONE, "cursor_down", "move cursor down", cursor_down], - [Left, NONE, "cursor_left", "move cursor left", cursor_left], - [Right, NONE, "cursor_right", "move cursor right", cursor_right], - [Char('.'), NONE, "increment", "increment value at cursor", increment], - [Char(','), NONE, "decrement", "decrement value at cursor", decrement], - [Delete, CONTROL, "delete", "delete track", delete], - [Char('d'), CONTROL, "duplicate", "duplicate scene or track", duplicate], - [Enter, NONE, "activate", "activate item at cursor", enter], - [Esc, NONE, "escape", "unfocus", escape], - [Char(' '), NONE, "play_toggle", "play or pause", play_toggle], - [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('a'), CONTROL, "add_scene", "add a new scene", add_scene], - [Char('`'), NONE, "switch_mode", "switch the display mode", switch_mode], - //[Char(' '), SHIFT, "play_start", "play from start", play_start], -}); - -fn switch_mode (app: &mut App) -> Usually { - match app.section { - 0 => {app.grid_mode = !app.grid_mode; Ok(true)}, - 1 => {app.chain_mode = !app.chain_mode; Ok(true)}, - 2 => {app.seq_mode = !app.seq_mode; Ok(true)}, - _ => Ok(false) - } -} -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 { - if app.entered { - match app.section { - 0 => match app.grid_mode { - false => {app.scene_cursor = app.scene_cursor.saturating_sub(1); Ok(true)}, - true => {app.track_cursor = app.track_cursor.saturating_sub(1); Ok(true)}, - }, - _ => Ok(false) - } - } else { - focus_prev(app) - } -} -fn cursor_down (app: &mut App) -> Usually { - if app.entered { - match app.section { - 0 => match app.grid_mode { - false => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); Ok(true)}, - true => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); Ok(true)}, - }, - _ => Ok(false) - } - } else { - focus_next(app) - } -} -fn cursor_left (app: &mut App) -> Usually { - match app.section { - 0 => match app.grid_mode { - false => {app.track_cursor = app.track_cursor.saturating_sub(1); Ok(true)}, - true => {app.scene_cursor = app.scene_cursor.saturating_sub(1); Ok(true)}, - }, - _ => Ok(false) - } -} -fn cursor_right (app: &mut App) -> Usually { - match app.section { - 0 => match app.grid_mode { - false => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); Ok(true)}, - true => {app.scene_cursor = app.scenes.len().min(app.scene_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 enter (app: &mut App) -> Usually { - if app.entered { - activate(app) - } else { - app.entered = true; - Ok(true) - } -} -fn activate (_: &mut App) -> Usually { - Ok(true) -} -fn escape (app: &mut App) -> Usually { - if app.entered { - app.entered = false; - Ok(true) - } else { - Ok(false) - } -} -fn rename (_: &mut App) -> Usually { Ok(true) } -fn add_scene (app: &mut App) -> Usually { - let name = format!("Scene {}", app.scenes.len() + 1); - app.scenes.push(Scene::new(&name, vec![])); - app.scene_cursor = app.scenes.len(); - Ok(true) -} -fn add_track (app: &mut App) -> Usually { - let name = format!("Track {}", app.tracks.len() + 1); - app.tracks.push(Track::new(&name, app.jack.as_ref().unwrap().as_client(), &app.timebase, None, None)?); - app.track_cursor = app.tracks.len(); - Ok(true) -} -fn delete_track (app: &mut App) -> Usually { - if app.tracks.len() > 0 { - let track = app.tracks.remove(app.track_cursor); - app.track_cursor = app.track_cursor.saturating_sub(1); - app.jack.as_ref().unwrap().as_client().unregister_port(track.midi_out)?; - return Ok(true) - } - Ok(false) -} -fn toggle_help (_: &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) } -fn record_toggle (_: &mut App) -> Usually { Ok(true) } -fn overdub_toggle (_: &mut App) -> Usually { Ok(true) } -fn monitor_toggle (_: &mut App) -> Usually { Ok(true) } - -//fn main () -> Usually<()> { - //let _cli = cli::Cli::parse(); - //let xdg = microxdg::XdgApp::new("tek")?; - //crate::config::create_dirs(&xdg)?; - ////run(Sampler::new("Sampler#000")?) - //let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?; - //let timebase = Arc::new(Timebase::new(client.sample_rate() as f64, 125.0, 96.0)); - //let ppq = timebase.ppq() as usize; - //macro_rules! play { - //($t1:expr => [ $($msg:expr),* $(,)? ]) => { - //( $t1 * ppq / 4, vec![ $($msg),* ] ) +impl App { + pub fn connect_tracks (&self) -> Usually<()> { + //let (client, _status) = Client::new( + //&format!("{}-init", &self.name), ClientOptions::NO_START_SERVER + //)?; + //let midi_ins = client.ports(Some(midi_in), None, PortFlags::IS_OUTPUT); + //let audio_outs: Vec> = audio_outs.iter() + //.map(|pattern|client.ports(Some(pattern), None, PortFlags::IS_INPUT)) + //.collect(); + //for (i, sequencer) in self.tracks.iter().enumerate() { + //for sequencer_midi_in in sequencer.midi_ins()?.iter() { + //for midi_in in midi_ins.iter() { + //client.connect_ports_by_name(&midi_in, &sequencer_midi_in)?; + //} + //} + //let chain: &Chain = &self.tracks[i].chain; + //for port in sequencer.midi_outs()?.iter() { + //for midi_in in chain.midi_ins()?.iter() { + //client.connect_ports_by_name(&port, &midi_in)?; + //} + //} + //for (j, port) in chain.audio_outs()?.iter().enumerate() { + //for audio_out in audio_outs[j % audio_outs.len()].iter() { + //client.connect_ports_by_name(&port, &audio_out)?; + //} + //} //} - //} - //Launcher::new("Launcher#0", &timebase, - //Some(vec![ - - //Track::new("Drums", &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("KSH", ppq * 4, Some(phrase! { - //00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //01 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //02 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - //04 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //06 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //08 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //10 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //10 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //11 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - //12 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, - //14 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - //14 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() } - //})), - - //Phrase::new("4K", 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() }, - //})), - - //Phrase::new("KS", ppq * 4, Some(phrase! { - //00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - //10 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, - //})), - - //]))?, - - //Track::new("Odin2", &timebase, Some(vec![ - - //Plugin::lv2("Odin2", "file:///home/user/.lv2/Odin2.lv2")?.boxed(), - - //]), Some(vec![ - //Phrase::new("E G A Bb", ppq * 4, Some(BTreeMap::from([ - //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() }, - //]), - //]))), - //Phrase::new("E E G Bb", ppq * 4, Some(BTreeMap::from([ - //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: 36.into(), vel: 100.into() }, - //]), - //play!(10 => [ - //MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, - //MidiMessage::NoteOn { key: 39.into(), vel: 100.into() }, - //]), - //play!(14 => [ - //MidiMessage::NoteOff { key: 39.into(), vel: 100.into() }, - //MidiMessage::NoteOn { key: 42.into(), vel: 100.into() }, - //]), - //]))), - //Phrase::new("E E E E", ppq * 4, Some(BTreeMap::from([ - //play!(0 => [ - //MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, - //]), - //play!(2 => [ - //MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //]), - //play!(4 => [ - //MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, - //]), - //play!(6 => [ - //MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //]), - //play!(8 => [ - //MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, - //]), - //play!(10 => [ - //MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //]), - //play!(12 => [ - //MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }, - //]), - //play!(14 => [ - //MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, - //]), - //]))) - //]))?, - - ////Plugin::lv2("Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(), - ////Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(), - ////Plugin::lv2("Bass/Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(), - ////Plugin::lv2("Pads/Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(), - //]), - //Some(vec![ - //Scene::new(&"Scene 1", &[Some(0), None, None, None]), - //Scene::new(&"Scene 2", &[Some(0), Some(0), None, None]), - //Scene::new(&"Scene 3", &[Some(1), Some(1), None, None]), - //Scene::new(&"Scene 4", &[Some(2), Some(2), None, None]), - ////Scene::new(&"Scene#03", &[None, Some(0), None, None]), - ////Scene::new(&"Scene#04", &[None, None, None, None]), - ////Scene::new(&"Scene#05", &[None, None, None, None]), - //]) - - //)? - //.run(Some(init)) -//} - -//fn init (state: Arc>>) -> Usually<()> { - //let input = ".*nanoKEY.*"; - //let output = ["Komplete.*:playback_FL", "Komplete.*:playback_FR"]; - //let state = state.lock().unwrap(); - //state.connect(input, &output)?; - //Ok(()) -//} + Ok(()) + } + pub fn track (&self) -> Option<(usize, &Track)> { + match self.track_cursor { 0 => None, _ => { + let id = self.track_cursor as usize - 1; + self.tracks.get(id).map(|t|(id, t)) + } } + } + pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> { + match self.track_cursor { 0 => None, _ => { + let id = self.track_cursor as usize - 1; + self.tracks.get_mut(id).map(|t|(id, t)) + } } + } + pub fn scene (&self) -> Option<(usize, &Scene)> { + match self.scene_cursor { 0 => None, _ => { + let id = self.scene_cursor as usize - 1; + self.scenes.get(id).map(|t|(id, t)) + } } + } + pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> { + match self.scene_cursor { 0 => None, _ => { + let id = self.scene_cursor as usize - 1; + self.scenes.get_mut(id).map(|t|(id, t)) + } } + } + pub fn sequencer (&self) -> Option<&Sequencer> { + Some(&self.track()?.1.sequencer) + } + pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> { + Some(&mut self.track_mut()?.1.sequencer) + } + pub fn chain (&self) -> Option<&Chain> { + Some(&self.track()?.1.chain) + } + pub fn phrase_id (&self) -> Option { + let (track_id, _) = self.track()?; + let (_, scene) = self.scene()?; + *scene.clips.get(track_id)? + } +} diff --git a/src/model.rs b/src/model.rs index 75da9382..29de73db 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,5 +1,4 @@ pub mod chain; -pub mod launcher; pub mod looper; pub mod mixer; pub mod phrase; @@ -13,7 +12,6 @@ pub use self::phrase::Phrase; pub use self::scene::Scene; pub use self::track::Track; pub use self::sequencer::{Sequencer, SequencerMode}; -pub use self::launcher::{Launcher, LauncherMode}; pub use self::chain::Chain; pub use self::sampler::{Sampler, Sample}; pub use self::mixer::Mixer; diff --git a/src/model/launcher.rs b/src/model/launcher.rs deleted file mode 100644 index 510f1f99..00000000 --- a/src/model/launcher.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::{core::*, model::*}; -pub enum LauncherMode { - Tracks, - Sequencer, - Chains -} -impl LauncherMode { - pub fn is_chains (&self) -> bool { - match self { Self::Chains => true, _ => false } - } - pub fn is_tracks (&self) -> bool { - match self { Self::Tracks => true, _ => false } - } - pub fn is_sequencer (&self) -> bool { - match self { Self::Sequencer => true, _ => false } - } -} -pub struct Launcher { - pub name: String, - pub timebase: Arc, - pub transport: ::jack::Transport, - pub playing: TransportState, - pub monitoring: bool, - pub recording: bool, - pub overdub: bool, - pub current_frame: usize, - pub cursor: (usize, usize), - pub tracks: Vec, - pub scenes: Vec, - pub show_help: bool, - pub view: LauncherMode, -} -render!(Launcher = crate::view::launcher::render); -handle!(Launcher = crate::control::launcher::handle); -process!(Launcher |self, _client, _scope| { - let transport = self.transport.query().unwrap(); - self.playing = transport.state; - self.current_frame = transport.pos.frame() as usize; - Control::Continue -}); -impl PortList for Launcher {} -impl Launcher { - pub fn new ( - name: &str, - timebase: &Arc, - tracks: Option>, - scenes: Option> - ) -> Result> { - let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; - let transport = client.transport(); - let ppq = timebase.ppq() as usize; - Ok(Self { - name: name.into(), - view: LauncherMode::Chains, - playing: transport.query_state()?, - transport, - timebase: timebase.clone(), - monitoring: true, - recording: false, - overdub: true, - cursor: (2, 2), - current_frame: 0, - scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), - 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() }] ), - ( ppq * 1, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), - ( ppq * 2, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), - ( ppq * 3, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), - ]))) - ]))?, - ] }*/, - show_help: true, - }) - } - pub fn connect (&self, midi_in: &str, audio_outs: &[&str]) -> Usually<()> { - let (client, _status) = Client::new( - &format!("{}-init", &self.name), ClientOptions::NO_START_SERVER - )?; - let midi_ins = client.ports(Some(midi_in), None, PortFlags::IS_OUTPUT); - let audio_outs: Vec> = audio_outs.iter() - .map(|pattern|client.ports(Some(pattern), None, PortFlags::IS_INPUT)) - .collect(); - for (i, sequencer) in self.tracks.iter().enumerate() { - for sequencer_midi_in in sequencer.midi_ins()?.iter() { - for midi_in in midi_ins.iter() { - client.connect_ports_by_name(&midi_in, &sequencer_midi_in)?; - } - } - let chain: &Chain = &self.tracks[i].chain; - for port in sequencer.midi_outs()?.iter() { - for midi_in in chain.midi_ins()?.iter() { - client.connect_ports_by_name(&port, &midi_in)?; - } - } - for (j, port) in chain.audio_outs()?.iter().enumerate() { - for audio_out in audio_outs[j % audio_outs.len()].iter() { - client.connect_ports_by_name(&port, &audio_out)?; - } - } - } - Ok(()) - } - pub fn cols (&self) -> usize { - (self.tracks.len() + 2) as usize - } - pub fn col (&self) -> usize { - self.cursor.0 as usize - } - pub fn dec_col (&mut self) { - self.cursor.0 = if self.cursor.0 > 0 { - self.cursor.0 - 1 - } else { - (self.cols() - 1) as usize - } - } - pub fn inc_col (&mut self) { - self.cursor.0 = if self.cursor.0 >= self.cols() - 1 { - 0 - } else { - self.cursor.0 + 1 - } - } - pub fn rows (&self) -> usize { - (self.scenes.len() + 2) as usize - } - pub fn row (&self) -> usize { - self.cursor.1 as usize - } - pub fn dec_row (&mut self) { - self.cursor.1 = if self.cursor.1 > 0 { - self.cursor.1 - 1 - } else { - self.rows() - 1 - } - } - pub fn inc_row (&mut self) { - self.cursor.1 = if self.cursor.1 >= self.rows() - 1 { - 0 - } else { - self.cursor.1 + 1 - } - } - - pub fn track (&self) -> Option<(usize, &Track)> { - match self.col() { 0 => None, _ => { - let id = self.col() as usize - 1; - self.tracks.get(id).map(|t|(id, t)) - } } - } - pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> { - match self.col() { 0 => None, _ => { - let id = self.col() as usize - 1; - self.tracks.get_mut(id).map(|t|(id, t)) - } } - } - pub fn scene (&self) -> Option<(usize, &Scene)> { - match self.row() { 0 => None, _ => { - let id = self.row() as usize - 1; - self.scenes.get(id).map(|t|(id, t)) - } } - } - pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> { - match self.row() { 0 => None, _ => { - let id = self.row() as usize - 1; - self.scenes.get_mut(id).map(|t|(id, t)) - } } - } - pub fn sequencer (&self) -> Option<&Sequencer> { - Some(&self.track()?.1.sequencer) - } - pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> { - Some(&mut self.track_mut()?.1.sequencer) - } - pub fn chain (&self) -> Option<&Chain> { - Some(&self.track()?.1.chain) - } - pub fn phrase_id (&self) -> Option { - let (track_id, _) = self.track()?; - let (_, scene) = self.scene()?; - *scene.clips.get(track_id)? - } -} diff --git a/src/model/phrase.rs b/src/model/phrase.rs index 58ddae72..be954ac7 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -127,3 +127,12 @@ impl Phrase { } } + +#[macro_export] macro_rules! phrase { + ($($t:expr => $msg:expr),* $(,)?) => {{ + let mut phrase = BTreeMap::new(); + $(phrase.insert($t, vec![]);)* + $(phrase.get_mut(&$t).unwrap().push($msg);)* + phrase + }} +} diff --git a/src/view.rs b/src/view.rs index 9504d073..ad9564b2 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,7 +1,6 @@ pub mod chain; pub mod grid; pub mod layout; -pub mod launcher; pub mod mixer; pub mod sampler; pub mod sequencer; @@ -13,3 +12,50 @@ pub use self::transport::TransportView; pub use self::grid::SceneGridView; pub use self::chain::{ChainView, ChainViewMode}; pub use self::sequencer::SequencerView; + +use crate::{render, App, core::*}; + +render!(App |self, buf, area| { + let Rect { x, mut y, width, height } = area; + + y = y + TransportView { + timebase: &self.timebase, + playing: *self.playing.as_ref().unwrap(), + record: false, + overdub: false, + monitor: false, + frame: self.playhead, + }.render(buf, area)?.height; + + y = y + SceneGridView { + buf, + area: Rect { x, y, width, height: height / 3 }, + name: "", + mode: self.grid_mode, + focused: self.section == 0, + scenes: &self.scenes, + tracks: &self.tracks, + cursor: &(self.track_cursor, self.scene_cursor), + }.draw()?.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: 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 { + modal.render(buf, area)?; + } + + Ok(area) +}); diff --git a/src/view/launcher.rs b/src/view/launcher.rs deleted file mode 100644 index 476760ac..00000000 --- a/src/view/launcher.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::{core::*, model::*, view::*}; - -pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually { - //area.width = 80; // DOS mode - //area.height = 25; - let Rect { x, mut y, width, height } = area; - - y = y + TransportView { - timebase: &state.timebase, - playing: state.playing, - record: state.sequencer().map(|s|s.recording).unwrap_or(false), - overdub: state.sequencer().map(|s|s.overdub).unwrap_or(false), - monitor: state.sequencer().map(|s|s.monitoring).unwrap_or(false), - frame: state.current_frame - }.render(buf, area)?.height; - - y = y + SceneGridView { - buf, - area: Rect { x, y, width, height }, - mode: false, - name: &state.name, - focused: state.view.is_tracks(), - scenes: &state.scenes, - tracks: &state.tracks, - cursor: &state.cursor - }.draw_vertical()?.height; - - if let Some(chain) = state.chain() { - y = y + ChainView { - focused: state.view.is_chains(), - chain: Some(&*chain), - }.render(buf, Rect { x, y, width, height: height/3 })?.height - } - - let track = state.track().map(|t|t.1); - y = y + SequencerView { - focused: state.view.is_sequencer(), - ppq: state.timebase.ppq() as usize, - track: track, - phrase: track.unwrap().sequencer.phrases.get(state.phrase_id().unwrap()) - }.render(buf, Rect { x, y, width, height: height - y })?.height; - - area.height = y; - if state.show_help { - let style = Some(Style::default().bold().white().not_dim().on_black().italic()); - let hide = "[Tab] Mode [Arrows] Move [.,] Value [F1] Toggle help "; - hide.blit(buf, x + (width - hide.len() as u16) / 2, height - 1, style); - } - - Ok(area) -}