mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
bye launcher
This commit is contained in:
parent
7bc396e748
commit
2165e5d45d
10 changed files with 451 additions and 862 deletions
308
src/control.rs
308
src/control.rs
|
|
@ -1,9 +1,315 @@
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
pub mod focus;
|
pub mod focus;
|
||||||
pub mod launcher;
|
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
pub mod sequencer;
|
pub mod sequencer;
|
||||||
|
|
||||||
pub use self::focus::*;
|
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<App>] = 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<bool> {
|
||||||
|
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<bool> { Ok(true) }
|
||||||
|
|
||||||
|
fn toggle_overdub (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
|
||||||
|
fn toggle_monitor (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
|
||||||
|
fn toggle_help (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
|
||||||
|
fn switch_mode (app: &mut App) -> Usually<bool> {
|
||||||
|
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<bool> {
|
||||||
|
if app.section >= 2 {
|
||||||
|
app.section = 0;
|
||||||
|
} else {
|
||||||
|
app.section = app.section + 1;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_prev (app: &mut App) -> Usually<bool> {
|
||||||
|
if app.section == 0 {
|
||||||
|
app.section = 2;
|
||||||
|
} else {
|
||||||
|
app.section = app.section - 1;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_up (app: &mut App) -> Usually<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
match app.section {
|
||||||
|
0 => clip_next(app),
|
||||||
|
_ => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clip_next (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
//fn clip_next (state: &mut Launcher) -> Usually<bool> {
|
||||||
|
//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<bool> {
|
||||||
|
match app.section {
|
||||||
|
0 => clip_prev(app),
|
||||||
|
_ => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clip_prev (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
//fn clip_prev (state: &mut Launcher) -> Usually<bool> {
|
||||||
|
//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<bool> { Ok(true) }
|
||||||
|
//fn activate (_: &mut Launcher) -> Usually<bool> {
|
||||||
|
//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<bool> {
|
||||||
|
match app.section {
|
||||||
|
0 => delete_track(app),
|
||||||
|
_ => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter (app: &mut App) -> Usually<bool> {
|
||||||
|
if app.entered {
|
||||||
|
activate(app)
|
||||||
|
} else {
|
||||||
|
app.entered = true;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape (app: &mut App) -> Usually<bool> {
|
||||||
|
if app.entered {
|
||||||
|
app.entered = false;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_scene (app: &mut App) -> Usually<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> { Ok(true) }
|
||||||
|
|
||||||
|
fn rename (_: &mut App) -> Usually<bool> { Ok(true) }
|
||||||
|
|
||||||
|
//fn toggle_record (s: &mut Launcher) -> Usually<bool> {
|
||||||
|
//s.sequencer_mut().map(|s|s.recording = !s.recording);
|
||||||
|
//Ok(true)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fn toggle_overdub (s: &mut Launcher) -> Usually<bool> {
|
||||||
|
//s.sequencer_mut().map(|s|s.overdub = !s.overdub);
|
||||||
|
//Ok(true)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fn toggle_monitor (s: &mut Launcher) -> Usually<bool> {
|
||||||
|
//s.sequencer_mut().map(|s|s.monitoring = !s.monitoring);
|
||||||
|
//Ok(true)
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -1,236 +0,0 @@
|
||||||
use crate::{core::*,model::*};
|
|
||||||
|
|
||||||
pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
|
|
||||||
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<Launcher>] = 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<Launcher>] = 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<bool> {
|
|
||||||
unimplemented!();
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate (_: &mut Launcher) -> Usually<bool> {
|
|
||||||
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<bool> {
|
|
||||||
//Ok(true)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn add_track (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
//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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
state.dec_row();
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_down (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
state.inc_row();
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_left (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
state.dec_col();
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_right (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
state.inc_col();
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle_help (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
state.show_help = !state.show_help;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_next (state: &mut Launcher) -> Usually<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
//unimplemented!()
|
|
||||||
//}
|
|
||||||
|
|
||||||
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
|
|
||||||
s.sequencer_mut().map(|s|s.recording = !s.recording);
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
|
||||||
s.sequencer_mut().map(|s|s.overdub = !s.overdub);
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
|
||||||
s.sequencer_mut().map(|s|s.monitoring = !s.monitoring);
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -24,6 +24,7 @@ pub use crate::{
|
||||||
render,
|
render,
|
||||||
handle,
|
handle,
|
||||||
process,
|
process,
|
||||||
|
phrase,
|
||||||
keymap,
|
keymap,
|
||||||
key
|
key
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ pub fn jack_run <T> (name: &str, app: &Arc<Mutex<T>>) -> Usually<DynamicAsyncCli
|
||||||
ClosureProcessHandler::new(Box::new({
|
ClosureProcessHandler::new(Box::new({
|
||||||
let app = app.clone();
|
let app = app.clone();
|
||||||
move|c: &Client, s: &ProcessScope|{
|
move|c: &Client, s: &ProcessScope|{
|
||||||
//app.lock().unwrap().process(c, s)
|
app.lock().unwrap().process(c, s)
|
||||||
Control::Continue
|
//Control::Continue
|
||||||
}
|
}
|
||||||
}) as BoxedProcessHandler)
|
}) as BoxedProcessHandler)
|
||||||
)?)
|
)?)
|
||||||
|
|
|
||||||
470
src/main.rs
470
src/main.rs
|
|
@ -13,18 +13,7 @@ pub mod core;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod view;
|
pub mod view;
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::{core::*, model::*};
|
||||||
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<()> {
|
pub fn main () -> Usually<()> {
|
||||||
App::default().run(Some(|app: Arc<Mutex<App>>|{
|
App::default().run(Some(|app: Arc<Mutex<App>>|{
|
||||||
|
|
@ -80,7 +69,10 @@ pub fn main () -> Usually<()> {
|
||||||
];
|
];
|
||||||
state.track_cursor = 1;
|
state.track_cursor = 1;
|
||||||
state.scene_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);
|
state.jack = Some(jack);
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
|
|
@ -98,385 +90,93 @@ pub struct App {
|
||||||
pub tracks: Vec<Track>,
|
pub tracks: Vec<Track>,
|
||||||
pub track_cursor: usize,
|
pub track_cursor: usize,
|
||||||
pub frame: usize,
|
pub frame: usize,
|
||||||
pub timebase: Arc<Timebase>,
|
|
||||||
pub modal: Option<Box<dyn Component>>,
|
pub modal: Option<Box<dyn Component>>,
|
||||||
pub section: usize,
|
pub section: usize,
|
||||||
pub entered: bool,
|
pub entered: bool,
|
||||||
|
pub playing: Option<TransportState>,
|
||||||
|
pub transport: Option<Transport>,
|
||||||
|
pub timebase: Arc<Timebase>,
|
||||||
|
pub playhead: usize,
|
||||||
pub midi_in: Option<Port<MidiIn>>,
|
pub midi_in: Option<Port<MidiIn>>,
|
||||||
|
pub audio_outs: Option<Vec<Port<AudioOut>>>,
|
||||||
|
pub metronome: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
process!(App);
|
process!(App |self, client, scope| {
|
||||||
|
let transport = self.transport.as_ref().unwrap().query().unwrap();
|
||||||
render!(App |self, buf, area| {
|
self.playing = Some(transport.state);
|
||||||
let Rect { x, mut y, width, height } = area;
|
self.playhead = transport.pos.frame() as usize;
|
||||||
|
for Track { sequencer, .. } in self.tracks.iter_mut() {
|
||||||
y = y + TransportView {
|
sequencer.process(client, scope);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
Control::Continue
|
||||||
if let Some(ref modal) = self.modal {
|
|
||||||
modal.render(buf, area)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(area)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(App |self, e| {
|
impl App {
|
||||||
if let Some(ref mut modal) = self.modal {
|
pub fn connect_tracks (&self) -> Usually<()> {
|
||||||
if modal.handle(e)? {
|
//let (client, _status) = Client::new(
|
||||||
self.modal = None;
|
//&format!("{}-init", &self.name), ClientOptions::NO_START_SERVER
|
||||||
return Ok(true)
|
//)?;
|
||||||
};
|
//let midi_ins = client.ports(Some(midi_in), None, PortFlags::IS_OUTPUT);
|
||||||
}
|
//let audio_outs: Vec<Vec<String>> = audio_outs.iter()
|
||||||
handle_keymap(self, e, KEYMAP)
|
//.map(|pattern|client.ports(Some(pattern), None, PortFlags::IS_INPUT))
|
||||||
});
|
//.collect();
|
||||||
|
//for (i, sequencer) in self.tracks.iter().enumerate() {
|
||||||
const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
|
//for sequencer_midi_in in sequencer.midi_ins()?.iter() {
|
||||||
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
|
//for midi_in in midi_ins.iter() {
|
||||||
[Tab, NONE, "focus_next", "focus next area", focus_next],
|
//client.connect_ports_by_name(&midi_in, &sequencer_midi_in)?;
|
||||||
[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],
|
//let chain: &Chain = &self.tracks[i].chain;
|
||||||
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
//for port in sequencer.midi_outs()?.iter() {
|
||||||
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
//for midi_in in chain.midi_ins()?.iter() {
|
||||||
[Char('.'), NONE, "increment", "increment value at cursor", increment],
|
//client.connect_ports_by_name(&port, &midi_in)?;
|
||||||
[Char(','), NONE, "decrement", "decrement value at cursor", decrement],
|
//}
|
||||||
[Delete, CONTROL, "delete", "delete track", delete],
|
//}
|
||||||
[Char('d'), CONTROL, "duplicate", "duplicate scene or track", duplicate],
|
//for (j, port) in chain.audio_outs()?.iter().enumerate() {
|
||||||
[Enter, NONE, "activate", "activate item at cursor", enter],
|
//for audio_out in audio_outs[j % audio_outs.len()].iter() {
|
||||||
[Esc, NONE, "escape", "unfocus", escape],
|
//client.connect_ports_by_name(&port, &audio_out)?;
|
||||||
[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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
if app.section >= 2 {
|
|
||||||
app.section = 0;
|
|
||||||
} else {
|
|
||||||
app.section = app.section + 1;
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
fn focus_prev (app: &mut App) -> Usually<bool> {
|
|
||||||
if app.section == 0 {
|
|
||||||
app.section = 2;
|
|
||||||
} else {
|
|
||||||
app.section = app.section - 1;
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
fn cursor_up (app: &mut App) -> Usually<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
match app.section {
|
|
||||||
0 => clip_next(app),
|
|
||||||
_ => Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn decrement (app: &mut App) -> Usually<bool> {
|
|
||||||
match app.section {
|
|
||||||
0 => clip_prev(app),
|
|
||||||
_ => Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn delete (app: &mut App) -> Usually<bool> {
|
|
||||||
match app.section {
|
|
||||||
0 => delete_track(app),
|
|
||||||
_ => Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn duplicate (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn enter (app: &mut App) -> Usually<bool> {
|
|
||||||
if app.entered {
|
|
||||||
activate(app)
|
|
||||||
} else {
|
|
||||||
app.entered = true;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn activate (_: &mut App) -> Usually<bool> {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
fn escape (app: &mut App) -> Usually<bool> {
|
|
||||||
if app.entered {
|
|
||||||
app.entered = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn rename (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn add_scene (app: &mut App) -> Usually<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<bool> { Ok(true) }
|
|
||||||
fn clip_next (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn clip_prev (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn play_toggle (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn record_toggle (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn overdub_toggle (_: &mut App) -> Usually<bool> { Ok(true) }
|
|
||||||
fn monitor_toggle (_: &mut App) -> Usually<bool> { 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),* ] )
|
|
||||||
//}
|
//}
|
||||||
//}
|
Ok(())
|
||||||
//Launcher::new("Launcher#0", &timebase,
|
}
|
||||||
//Some(vec![
|
pub fn track (&self) -> Option<(usize, &Track)> {
|
||||||
|
match self.track_cursor { 0 => None, _ => {
|
||||||
//Track::new("Drums", &timebase, Some(vec![
|
let id = self.track_cursor as usize - 1;
|
||||||
|
self.tracks.get(id).map(|t|(id, t))
|
||||||
//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"),
|
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
||||||
//sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh.wav"),
|
match self.track_cursor { 0 => None, _ => {
|
||||||
//])))?.boxed(),
|
let id = self.track_cursor as usize - 1;
|
||||||
|
self.tracks.get_mut(id).map(|t|(id, t))
|
||||||
////Plugin::lv2("Panagement", "file:///home/user/.lv2/Auburn Sounds Panagement 2.lv2")?.boxed(),
|
} }
|
||||||
|
}
|
||||||
//]), Some(vec![
|
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
||||||
|
match self.scene_cursor { 0 => None, _ => {
|
||||||
//Phrase::new("KSH", ppq * 4, Some(phrase! {
|
let id = self.scene_cursor as usize - 1;
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
self.scenes.get(id).map(|t|(id, t))
|
||||||
//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() },
|
pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> {
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
match self.scene_cursor { 0 => None, _ => {
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
let id = self.scene_cursor as usize - 1;
|
||||||
//06 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
self.scenes.get_mut(id).map(|t|(id, t))
|
||||||
//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() },
|
pub fn sequencer (&self) -> Option<&Sequencer> {
|
||||||
//11 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
Some(&self.track()?.1.sequencer)
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
}
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> {
|
||||||
//14 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
Some(&mut self.track_mut()?.1.sequencer)
|
||||||
//14 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }
|
}
|
||||||
//})),
|
pub fn chain (&self) -> Option<&Chain> {
|
||||||
|
Some(&self.track()?.1.chain)
|
||||||
//Phrase::new("4K", ppq * 4, Some(phrase! {
|
}
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
pub fn phrase_id (&self) -> Option<usize> {
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
let (track_id, _) = self.track()?;
|
||||||
//08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
let (_, scene) = self.scene()?;
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
*scene.clips.get(track_id)?
|
||||||
//})),
|
}
|
||||||
|
}
|
||||||
//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<Mutex<DynamicDevice<Launcher>>>) -> Usually<()> {
|
|
||||||
//let input = ".*nanoKEY.*";
|
|
||||||
//let output = ["Komplete.*:playback_FL", "Komplete.*:playback_FR"];
|
|
||||||
//let state = state.lock().unwrap();
|
|
||||||
//state.connect(input, &output)?;
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
pub mod launcher;
|
|
||||||
pub mod looper;
|
pub mod looper;
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
pub mod phrase;
|
pub mod phrase;
|
||||||
|
|
@ -13,7 +12,6 @@ pub use self::phrase::Phrase;
|
||||||
pub use self::scene::Scene;
|
pub use self::scene::Scene;
|
||||||
pub use self::track::Track;
|
pub use self::track::Track;
|
||||||
pub use self::sequencer::{Sequencer, SequencerMode};
|
pub use self::sequencer::{Sequencer, SequencerMode};
|
||||||
pub use self::launcher::{Launcher, LauncherMode};
|
|
||||||
pub use self::chain::Chain;
|
pub use self::chain::Chain;
|
||||||
pub use self::sampler::{Sampler, Sample};
|
pub use self::sampler::{Sampler, Sample};
|
||||||
pub use self::mixer::Mixer;
|
pub use self::mixer::Mixer;
|
||||||
|
|
|
||||||
|
|
@ -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<Timebase>,
|
|
||||||
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<Track>,
|
|
||||||
pub scenes: Vec<Scene>,
|
|
||||||
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<Timebase>,
|
|
||||||
tracks: Option<Vec<Track>>,
|
|
||||||
scenes: Option<Vec<Scene>>
|
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
|
||||||
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<Vec<String>> = 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<usize> {
|
|
||||||
let (track_id, _) = self.track()?;
|
|
||||||
let (_, scene) = self.scene()?;
|
|
||||||
*scene.clips.get(track_id)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
|
||||||
48
src/view.rs
48
src/view.rs
|
|
@ -1,7 +1,6 @@
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod launcher;
|
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
pub mod sequencer;
|
pub mod sequencer;
|
||||||
|
|
@ -13,3 +12,50 @@ pub use self::transport::TransportView;
|
||||||
pub use self::grid::SceneGridView;
|
pub use self::grid::SceneGridView;
|
||||||
pub use self::chain::{ChainView, ChainViewMode};
|
pub use self::chain::{ChainView, ChainViewMode};
|
||||||
pub use self::sequencer::SequencerView;
|
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)
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
use crate::{core::*, model::*, view::*};
|
|
||||||
|
|
||||||
pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
|
||||||
//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)
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue