tek/src/device/launcher.rs

444 lines
16 KiB
Rust

use crate::prelude::*;
pub struct Launcher {
name: String,
timebase: Arc<Timebase>,
transport: Transport,
playing: TransportState,
monitoring: bool,
recording: bool,
overdub: bool,
cursor: (usize, usize),
tracks: Vec<DynamicDevice<Sequencer>>,
chains: Vec<DynamicDevice<Chain>>,
scenes: Vec<Scene>,
show_help: bool,
view: LauncherView,
}
pub enum LauncherView {
Tracks,
Sequencer,
Chains
}
pub struct Scene {
name: String,
clips: Vec<Option<usize>>,
}
impl Scene {
pub fn new (name: impl AsRef<str>, clips: impl AsRef<[Option<usize>]>) -> Self {
Self {
name: name.as_ref().into(),
clips: clips.as_ref().iter().map(|x|x.clone()).collect()
}
}
}
impl Launcher {
pub fn new (name: &str,) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let transport = client.transport();
let timebase = Arc::new(Timebase {
rate: AtomicUsize::new(client.sample_rate()),
tempo: AtomicUsize::new(113000),
ppq: AtomicUsize::new(96),
});
DynamicDevice::new(render, handle, process, Self {
name: name.into(),
view: LauncherView::Tracks,
playing: transport.query_state()?,
monitoring: true,
recording: false,
overdub: true,
transport,
cursor: (1, 2),
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]),
],
tracks: vec![
Sequencer::new("Drum", &timebase)?,
Sequencer::new("Bass", &timebase)?,
Sequencer::new("Pads", &timebase)?,
Sequencer::new("Lead", &timebase)?,
],
chains: vec![
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#000")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#001")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#002")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#003")?),
])?,
],
timebase,
show_help: true
}).activate(client)
}
fn cols (&self) -> usize {
(self.tracks.len() + 1) as usize
}
fn col (&self) -> usize {
self.cursor.0 as usize
}
fn dec_col (&mut self) {
self.cursor.0 = if self.cursor.0 > 0 {
self.cursor.0 - 1
} else {
(self.cols() - 1) as usize
}
}
fn inc_col (&mut self) {
self.cursor.0 = if self.cursor.0 >= self.cols() - 1 {
0
} else {
self.cursor.0 + 1
}
}
fn rows (&self) -> usize {
(self.scenes.len() + 2) as usize
}
fn row (&self) -> usize {
self.cursor.1 as usize
}
fn dec_row (&mut self) {
self.cursor.1 = if self.cursor.1 > 0 {
self.cursor.1 - 1
} else {
self.rows() - 1
}
}
fn inc_row (&mut self) {
self.cursor.1 = if self.cursor.1 >= self.rows() - 1 {
0
} else {
self.cursor.1 + 1
}
}
}
impl DevicePorts for Launcher {}
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
state.playing = state.transport.query_state().unwrap();
Control::Continue
}
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area;
crate::device::sequencer::draw_timer(buf, x + width - 1, y, 0, 0, 0, 0);
crate::device::sequencer::draw_play_stop(buf, x + 1, y, &state.playing);
crate::device::sequencer::draw_rec(buf, x + 12, y, state.recording);
crate::device::sequencer::draw_mon(buf, x + 19, y, state.monitoring);
crate::device::sequencer::draw_dub(buf, x + 26, y, state.overdub);
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.chains[0].state();
let (_, plugins) = crate::device::chain::draw_as_row(chain, buf, chain_area, style)?;
match state.view {
LauncherView::Tracks => draw_box_styled(buf, track_area, style),
LauncherView::Sequencer => draw_box_styled(buf, seq_area, style),
LauncherView::Chains => draw_box_styled(buf, Rect { height: 18, ..chain_area }, style),
};
draw_highlight(state, buf, &track_highlight, match state.view {
LauncherView::Tracks => Style::default().green().not_dim(),
_ => Style::default().green().dim()
});
draw_highlight(state, buf, &Some(plugins[chain.focus]), match state.view {
LauncherView::Chains => Style::default().green().not_dim(),
_ => Style::default().green().dim()
});
draw_sequencer(state, buf, seq_area.x, seq_area.y + 1, seq_area.width, seq_area.height - 2)?;
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 ";
hide.blit(buf, width - hide.len() as u16, height - 1, style);
}
Ok(area)
}
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()
};
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.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_index, 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(sequencer) = 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(
&sequencer.state(), buf, Rect { x, y: y + 1, width, height }
)?;
crate::device::sequencer::horizontal::lanes(
&sequencer.state(), buf, x, y + 1, width,
);
crate::device::sequencer::horizontal::cursor(
&sequencer.state(), buf, x, y + 1, match state.view {
LauncherView::Sequencer => Style::default().green().not_dim(),
_ => Style::default().green().dim(),
}
);
}
Ok(())
}
fn draw_highlight (
state: &Launcher, 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::Tracks => {
handle_keymap(state, event, KEYMAP_TRACKS)?
},
LauncherView::Sequencer => {
let i = state.col().saturating_sub(1);
if let Some(sequencer) = state.tracks.get_mut(i) {
crate::device::sequencer::handle(&mut *sequencer.state(), event)?
} else {
true
}
},
LauncherView::Chains => {
true
}
})
}
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
[Char('n'), NONE, "rename", "rename current element", rename],
[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(' '), 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_next", "set clip to last phrase", clip_prev],
});
fn rename (_: &mut Launcher) -> Usually<bool> {
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> {
state.view = match state.view {
LauncherView::Tracks => LauncherView::Sequencer,
LauncherView::Sequencer => LauncherView::Chains,
LauncherView::Chains => LauncherView::Tracks,
};
Ok(true)
}
fn focus_prev (state: &mut Launcher) -> Usually<bool> {
state.view = match state.view {
LauncherView::Tracks => LauncherView::Chains,
LauncherView::Chains => LauncherView::Sequencer,
LauncherView::Sequencer => 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].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].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 (s: &mut Launcher) -> Usually<bool> {
unimplemented!()
}
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
s.recording = !s.recording;
Ok(true)
}
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
s.overdub = !s.overdub;
Ok(true)
}
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
s.monitoring = !s.monitoring;
Ok(true)
}