mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
big worky on sequencer and launcher
This commit is contained in:
parent
a4c3593840
commit
8a8d7b8704
14 changed files with 852 additions and 680 deletions
|
|
@ -1,4 +1,8 @@
|
|||
use crate::prelude::*;
|
||||
mod grid;
|
||||
pub use self::grid::*;
|
||||
mod handle;
|
||||
pub use self::handle::*;
|
||||
pub struct Launcher {
|
||||
name: String,
|
||||
timebase: Arc<Timebase>,
|
||||
|
|
@ -20,6 +24,11 @@ pub enum LauncherView {
|
|||
Chains,
|
||||
Modal(Box<dyn Device>),
|
||||
}
|
||||
impl LauncherView {
|
||||
fn is_tracks (&self) -> bool {
|
||||
match self { Self::Tracks => true, _ => false }
|
||||
}
|
||||
}
|
||||
pub struct Scene {
|
||||
name: String,
|
||||
clips: Vec<Option<usize>>,
|
||||
|
|
@ -92,6 +101,7 @@ impl Launcher {
|
|||
tempo: AtomicUsize::new(113000),
|
||||
ppq: AtomicUsize::new(96),
|
||||
});
|
||||
let ppq = timebase.ppq() as u32;
|
||||
DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
view: LauncherView::Tracks,
|
||||
|
|
@ -100,38 +110,54 @@ impl Launcher {
|
|||
recording: false,
|
||||
overdub: true,
|
||||
transport,
|
||||
cursor: (1, 2),
|
||||
cursor: (1, 1),
|
||||
position: 0,
|
||||
scenes: vec![
|
||||
Scene::new(&"Scene#01", &[Some(0), None, None, None]),
|
||||
Scene::new(&"Scene#02", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#03", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#04", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#05", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#06", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#07", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#08", &[None, None, None, None]),
|
||||
Scene::new(&"Scene#01", &[Some(0), None, None, None]),
|
||||
Scene::new(&"Scene#02", &[Some(0), Some(0), 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]),
|
||||
],
|
||||
tracks: vec![
|
||||
Track::new("Samples", &timebase, vec![
|
||||
Sampler::new("Samples")?.boxed(),
|
||||
])?,
|
||||
Track::new("Kick", &timebase, vec![
|
||||
//Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(),
|
||||
])?,
|
||||
//Track::new("Bass", &timebase, vec![
|
||||
//Plugin::lv2("Bass/Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(),
|
||||
//])?,
|
||||
//Track::new("Pads", &timebase, vec![
|
||||
//Plugin::lv2("Pads/Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(),
|
||||
//])?,
|
||||
|
||||
Track::new("Kick", &timebase, Some(vec![
|
||||
//Sampler::new("Sampler")?.boxed(),
|
||||
Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(),
|
||||
]), Some(vec![
|
||||
Phrase::new("HelloKick", 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() }] ),
|
||||
])))
|
||||
]))?,
|
||||
|
||||
Track::new("Helm", &timebase, Some(vec![
|
||||
Plugin::lv2("Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(),
|
||||
]), Some(vec![
|
||||
Phrase::new("HelloBass", ppq * 4, Some(BTreeMap::from([
|
||||
( ppq / 2 + ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 1 / 2, vec![MidiMessage::NoteOff { key: 36.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 1, vec![MidiMessage::NoteOn { key: 39.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 3 / 2, vec![MidiMessage::NoteOff { key: 39.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 2, vec![MidiMessage::NoteOn { key: 41.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 5 / 2, vec![MidiMessage::NoteOff { key: 41.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 3, vec![MidiMessage::NoteOn { key: 42.into(), vel: 100.into() }] ),
|
||||
( ppq / 2 + ppq * 7 / 2 - 1, vec![MidiMessage::NoteOff { key: 42.into(), vel: 100.into() }] ),
|
||||
])))
|
||||
]))?,
|
||||
|
||||
//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(),
|
||||
],
|
||||
timebase,
|
||||
show_help: true
|
||||
}).activate(client)
|
||||
}
|
||||
fn cols (&self) -> usize {
|
||||
(self.tracks.len() + 1) as usize
|
||||
(self.tracks.len() + 2) as usize
|
||||
}
|
||||
fn col (&self) -> usize {
|
||||
self.cursor.0 as usize
|
||||
|
|
@ -200,66 +226,15 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect>
|
|||
crate::device::sequencer::draw_dub(buf, x + 26, y, state.overdub);
|
||||
draw_bpm(buf, x + 33, y, state.timebase.tempo());
|
||||
draw_timer(buf, x + width - 1, y, &state.timebase, state.position);
|
||||
let track_area = Rect { x: x, y: y+1, width, height: 22 };
|
||||
let seq_area = Rect { x: x, y: y+22, width, height: 20 };
|
||||
let chain_area = Rect { x: x, y: y+41, width, height: 21 };
|
||||
let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
||||
let scenes = draw_scenes(state, buf, x, y + 1);
|
||||
separator.blit(buf, x, y + 3, Some(Style::default().dim()));
|
||||
separator.blit(buf, x, y + 5, Some(Style::default().dim()));
|
||||
separator.blit(buf, x, y + 22, Some(Style::default().dim()));
|
||||
separator.blit(buf, x, y + 41, Some(Style::default().dim()));
|
||||
let (w, mut track_highlight) = draw_tracks(state, buf, track_area.x, track_area.y);
|
||||
if state.col() == 0 {
|
||||
track_highlight = Some(scenes);
|
||||
}
|
||||
draw_crossings(state, buf, x + w - 2, y + 1);
|
||||
draw_box(buf, Rect { x, y: y + 1, width, height: height - 1 });
|
||||
let style = Some(Style::default().green().dim());
|
||||
let chain = state.active_chain();
|
||||
let plugins = if let Some(chain) = &chain {
|
||||
let (_, plugins) = crate::device::chain::draw_as_row(
|
||||
&*chain, buf, chain_area, style
|
||||
)?;
|
||||
plugins
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
match state.view {
|
||||
LauncherView::Tracks => {
|
||||
draw_box_styled(buf, track_area, style);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
draw_highlight(state, buf, &track_highlight, match state.view {
|
||||
LauncherView::Tracks => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim()
|
||||
});
|
||||
|
||||
match state.view {
|
||||
LauncherView::Chains => {
|
||||
draw_box_styled(buf, Rect { height: 18, ..chain_area }, style);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
if let Some(chain) = &chain {
|
||||
if let Some(plugin) = plugins.get(chain.focus) {
|
||||
draw_highlight(state, buf, &Some(*plugin), match state.view {
|
||||
LauncherView::Chains => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
match state.view {
|
||||
LauncherView::Sequencer => {
|
||||
draw_box_styled(buf, seq_area, style);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
draw_sequencer(state, buf, seq_area.x, seq_area.y + 1, seq_area.width, seq_area.height - 2)?;
|
||||
|
||||
//let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
||||
//separator.blit(buf, x, y + 22, Some(Style::default().dim()));
|
||||
//separator.blit(buf, x, y + 41, Some(Style::default().dim()));
|
||||
let mut y = y + 1;
|
||||
y = y + LauncherGridView
|
||||
::new(state, buf, Rect { x, y, width, height: 22 }, state.view.is_tracks())
|
||||
.draw()?.height + 1;
|
||||
y = y + draw_section_sequencer(state, buf, Rect { x, y, width, height: 28 })?.height + 1;
|
||||
y = y + draw_section_chains(state, buf, Rect { x, y, width, height: 21 })?.height;
|
||||
if state.show_help {
|
||||
let style = Some(Style::default().bold().white().not_dim().on_black().italic());
|
||||
let hide = "[Left Right] Track [Up Down] Scene [, .] Value [F1] Toggle help ";
|
||||
|
|
@ -273,6 +248,14 @@ fn draw_bpm (buf: &mut Buffer, x: u16, y: u16, tempo: usize) {
|
|||
.blit(buf, x, y, Some(style));
|
||||
format!("{:03}.{:03}", tempo / 1000, tempo % 1000)
|
||||
.blit(buf, x + 4, y, Some(style.bold()));
|
||||
"SYNC"
|
||||
.blit(buf, x + 13, y, Some(style));
|
||||
"4/4"
|
||||
.blit(buf, x + 18, y, Some(style.bold()));
|
||||
"QUANT"
|
||||
.blit(buf, x + 23, y, Some(style));
|
||||
"1/16"
|
||||
.blit(buf, x + 29, y, Some(style.bold()));
|
||||
}
|
||||
fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc<Timebase>, frame: usize) {
|
||||
let tick = (frame as f64 / timebase.frames_per_tick()) as usize;
|
||||
|
|
@ -281,298 +264,59 @@ fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc<Timebase>, frame
|
|||
let timer = format!("{}.{}.{ticks:02}", bars + 1, beats + 1);
|
||||
timer.blit(buf, x - timer.len() as u16, y, Some(Style::default().not_dim()));
|
||||
}
|
||||
fn draw_scenes (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
|
||||
) -> Rect {
|
||||
let style = Style::default().not_dim().bold();
|
||||
let row = state.row() as u16;
|
||||
let col = state.col() as u16;
|
||||
let green = |r: u16, c: u16| if row == r && col == c {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default()
|
||||
fn draw_section_sequencer (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, width, height } = area;
|
||||
let style = Some(Style::default().green().dim());
|
||||
let view = &state.view;
|
||||
match state.view {
|
||||
LauncherView::Sequencer => {
|
||||
draw_box_styled(buf, area, style);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
let mut width = 8u16;
|
||||
let mut height = 6u16;
|
||||
format!("{} | ", state.name).blit(buf, x+2, y+1, Some(green(0, 0)));
|
||||
format!("Sync 1/1").blit(buf, x+2, y+3, Some(green(1, 0)));
|
||||
for (scene_index, scene) in state.scenes.iter().enumerate() {
|
||||
let y = y + 5 + scene_index as u16 * 2;
|
||||
let label = format!("▶ {}", &scene.name);
|
||||
width = width.max(label.len() as u16 + 2);
|
||||
label.blit(buf, x + 2, y, Some(green(scene_index as u16 + 2, 0)));
|
||||
height = height + 2;
|
||||
}
|
||||
Rect { x, y, width, height }
|
||||
}
|
||||
fn draw_tracks (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
|
||||
) -> (u16, Option<Rect>) {
|
||||
let mut w = 15;
|
||||
let mut highlight = None;
|
||||
for (i, track) in state.tracks.iter().enumerate() {
|
||||
let track = track.sequencer.state();
|
||||
draw_crossings(state, buf, x + w - 2, y);
|
||||
let width = draw_track(state, buf, x + w, y, i as u16, &track);
|
||||
if i + 1 == state.col() {
|
||||
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
|
||||
}
|
||||
w = w + width;
|
||||
}
|
||||
(w, highlight)
|
||||
}
|
||||
fn draw_track (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
|
||||
) -> u16 {
|
||||
let mut width = 11.max(track.name.len() as u16 + 3);
|
||||
let row = state.row() as u16;
|
||||
let col = state.col() as u16;
|
||||
track.name.blit(buf, x, y + 1, Some(
|
||||
if row == 0 && col == i + 1 { Style::default().green() } else { Style::default() }
|
||||
));
|
||||
"(global)".blit(buf, x, y + 3, Some(
|
||||
if row == 1 && col == i + 1 { Style::default().green() } else { Style::default().dim() }
|
||||
));
|
||||
"┊".blit(buf, x - 2, y + 3, Some(
|
||||
Style::default().dim()
|
||||
));
|
||||
let green = |r: u16, c: u16| if row == r && col == c {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
for (_, scene) in state.scenes.iter().enumerate() {
|
||||
if let Some(Some(sequence_index)) = scene.clips.get(i as usize) {
|
||||
if let Some(sequence) = track.sequences.get(*sequence_index) {
|
||||
width = width.max(sequence.name.len() as u16 + 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (scene_index, scene) in state.scenes.iter().enumerate() {
|
||||
let y = y + 5 + scene_index as u16 * 2;
|
||||
let style = if scene_index + 2 == row as usize && i + 1 == col {
|
||||
Style::default().green().bold()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
};
|
||||
if let Some(Some(sequence_index)) = scene.clips.get(i as usize) {
|
||||
if let Some(sequence) = track.sequences.get(*sequence_index) {
|
||||
let label = format!("▶ {}", &sequence.name);
|
||||
label.blit(buf, x, y, Some(style));
|
||||
} else {
|
||||
format!("┊ {}", &"?".repeat(track.name.len()))
|
||||
.blit(buf, x - 2, y as u16, Some(Style::default().dim()));
|
||||
}
|
||||
} else {
|
||||
"····".blit(buf, x, y, Some(style.dim()));
|
||||
}
|
||||
let style = Some(style.dim());
|
||||
"┊".blit(buf, x - 2, y + 0, style);
|
||||
"┊".blit(buf, x - 2, y + 1, style);
|
||||
"┊".blit(buf, x + width - 2, y + 0, style);
|
||||
"┊".blit(buf, x + width - 2, y + 1, style);
|
||||
}
|
||||
width
|
||||
}
|
||||
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
|
||||
"╭".blit(buf, x, y + 0, None);
|
||||
"┊".blit(buf, x, y + 1, Some(Style::default().dim()));
|
||||
"┼".blit(buf, x, y + 2, Some(Style::default().dim()));
|
||||
"┊".blit(buf, x, y + 3, Some(Style::default().dim()));
|
||||
"┼".blit(buf, x, y + 4, Some(Style::default().dim()));
|
||||
}
|
||||
fn draw_sequencer (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
|
||||
) -> Usually<()> {
|
||||
if let Some(track) = state.tracks.get(state.col().saturating_sub(1)) {
|
||||
//crate::device::sequencer::horizontal::footer(
|
||||
//&sequencer.state(), buf, 0, y, width, 0
|
||||
//);
|
||||
crate::device::sequencer::horizontal::keys(
|
||||
&track.sequencer.state(), buf, Rect { x, y: y + 1, width, height }
|
||||
)?;
|
||||
crate::device::sequencer::horizontal::lanes(
|
||||
&track.sequencer.state(), buf, x, y + 1, width,
|
||||
);
|
||||
let state = track.sequencer.state();
|
||||
crate::device::sequencer::horizontal::keys(&state, buf, Rect { x, y: y + 1, width, height })?;
|
||||
crate::device::sequencer::horizontal::lanes(&state, buf, x, y + 1, width);
|
||||
crate::device::sequencer::horizontal::cursor(
|
||||
&track.sequencer.state(), buf, x, y + 1, match state.view {
|
||||
&state, buf, x, y + 1, match view {
|
||||
LauncherView::Sequencer => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim(),
|
||||
}
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
Ok(area)
|
||||
}
|
||||
fn draw_highlight (
|
||||
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>, style: Style
|
||||
) {
|
||||
fn draw_highlight (buf: &mut Buffer, highlight: &Option<Rect>, style: Style) {
|
||||
if let Some(area) = highlight {
|
||||
draw_box_styled(buf, *area, Some(style));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
|
||||
Ok(handle_keymap(state, event, KEYMAP)? || match state.view {
|
||||
LauncherView::Modal(ref mut device) => {
|
||||
device.handle(event)?
|
||||
},
|
||||
LauncherView::Tracks => {
|
||||
handle_keymap(state, event, KEYMAP_TRACKS)?
|
||||
},
|
||||
LauncherView::Sequencer => {
|
||||
let i = state.col().saturating_sub(1);
|
||||
if let Some(track) = state.tracks.get_mut(i) {
|
||||
crate::device::sequencer::handle(&mut *track.sequencer.state(), event)?
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, width, height } = area;
|
||||
let style = Some(Style::default().green().dim());
|
||||
let chain = state.active_chain();
|
||||
let plugins = if let Some(chain) = &chain {
|
||||
let (_, plugins) = crate::device::chain::draw_as_row(
|
||||
&*chain, buf, area, style
|
||||
)?;
|
||||
plugins
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
match state.view {
|
||||
LauncherView::Chains => {
|
||||
true
|
||||
draw_box_styled(buf, Rect { height: 18, ..area }, style);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
if let Some(chain) = &chain {
|
||||
if let Some(plugin) = plugins.get(chain.focus) {
|
||||
draw_highlight(buf, &Some(*plugin), match state.view {
|
||||
LauncherView::Chains => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim()
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
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],
|
||||
});
|
||||
fn rename (state: &mut Launcher) -> Usually<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
fn add_track (state: &mut Launcher) -> Usually<bool> {
|
||||
state.tracks.push(Track::new("", &state.timebase, vec![])?);
|
||||
state.cursor.0 = state.tracks.len();
|
||||
Ok(true)
|
||||
}
|
||||
fn delete_track (state: &mut Launcher) -> Usually<bool> {
|
||||
if 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 {
|
||||
LauncherView::Tracks => { state.view = LauncherView::Sequencer; },
|
||||
LauncherView::Sequencer => { state.view = LauncherView::Chains; },
|
||||
LauncherView::Chains => { state.view = LauncherView::Tracks; },
|
||||
_ => {},
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
fn focus_prev (state: &mut Launcher) -> Usually<bool> {
|
||||
match state.view {
|
||||
LauncherView::Tracks => { state.view = LauncherView::Chains; },
|
||||
LauncherView::Chains => { state.view = LauncherView::Sequencer; },
|
||||
LauncherView::Sequencer => { state.view = LauncherView::Tracks; },
|
||||
_ => {},
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
fn clip_next (state: &mut Launcher) -> Usually<bool> {
|
||||
if state.cursor.0 >= 1 && state.cursor.1 >= 2 {
|
||||
let scene_id = state.cursor.1 - 2;
|
||||
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.state().sequences.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 >= 2 {
|
||||
let scene_id = state.cursor.1 - 2;
|
||||
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.state().sequences.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.recording = !s.recording;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().recording = s.recording;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.overdub = !s.overdub;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().overdub = s.overdub;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.monitoring = !s.monitoring;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().monitoring = s.monitoring;
|
||||
}
|
||||
Ok(true)
|
||||
Ok(area)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue