mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip
This commit is contained in:
parent
f48f17e9a4
commit
4ebecc2427
7 changed files with 107 additions and 71 deletions
|
|
@ -1,275 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
mod grid;
|
||||
pub use self::grid::*;
|
||||
mod handle;
|
||||
pub use self::handle::*;
|
||||
pub struct Launcher {
|
||||
name: String,
|
||||
timebase: Arc<Timebase>,
|
||||
transport: Transport,
|
||||
playing: TransportState,
|
||||
monitoring: bool,
|
||||
recording: bool,
|
||||
overdub: bool,
|
||||
position: usize,
|
||||
cursor: (usize, usize),
|
||||
pub tracks: Vec<Track>,
|
||||
scenes: Vec<Scene>,
|
||||
show_help: bool,
|
||||
view: LauncherView,
|
||||
}
|
||||
pub enum LauncherView {
|
||||
Tracks,
|
||||
Sequencer,
|
||||
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>>,
|
||||
}
|
||||
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,
|
||||
timebase: &Arc<Timebase>,
|
||||
tracks: Option<Vec<Track>>,
|
||||
scenes: Option<Vec<Scene>>
|
||||
) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
let transport = client.transport();
|
||||
let ppq = timebase.ppq() as u32;
|
||||
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, 1),
|
||||
position: 0,
|
||||
scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]),
|
||||
tracks: 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() }] ),
|
||||
])))
|
||||
]))?,
|
||||
] },
|
||||
timebase: timebase.clone(),
|
||||
show_help: true
|
||||
}).activate(client)
|
||||
}
|
||||
fn cols (&self) -> usize {
|
||||
(self.tracks.len() + 2) 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
|
||||
}
|
||||
}
|
||||
|
||||
fn active_track (&self) -> Option<&Track> {
|
||||
if self.cursor.0 >= 1 {
|
||||
self.tracks.get(self.cursor.0 as usize - 1 as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn active_sequencer <'a> (&'a self) -> Option<std::sync::MutexGuard<Sequencer>> {
|
||||
self.active_track().map(|t|t.sequencer.state())
|
||||
}
|
||||
fn active_chain <'a> (&'a self) -> Option<std::sync::MutexGuard<Chain>> {
|
||||
self.active_track().map(|t|t.chain.state())
|
||||
}
|
||||
}
|
||||
impl DynamicDevice<Launcher> {
|
||||
pub fn connect (self, midi_in: &str, audio_outs: &[&str]) -> Usually<Self> {
|
||||
{
|
||||
let state = &self.state();
|
||||
let (client, _status) = Client::new(
|
||||
&format!("{}-init", &state.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 state.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: &DynamicDevice<Chain> = &state.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(self)
|
||||
}
|
||||
}
|
||||
impl PortList for Launcher {}
|
||||
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||
let transport = state.transport.query().unwrap();
|
||||
state.playing = transport.state;
|
||||
state.position = transport.pos.frame() as usize;
|
||||
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_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);
|
||||
draw_bpm(buf, x + 33, y, state.timebase.tempo());
|
||||
draw_timer(buf, x + width - 1, y, &state.timebase, state.position);
|
||||
//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 ";
|
||||
hide.blit(buf, width - hide.len() as u16, height - 1, style);
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
fn draw_bpm (buf: &mut Buffer, x: u16, y: u16, tempo: usize) {
|
||||
let style = Style::default().not_dim();
|
||||
"BPM"
|
||||
.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;
|
||||
let (beats, ticks) = (tick / timebase.ppq(), tick % timebase.ppq());
|
||||
let (bars, beats) = (beats / 4, beats % 4);
|
||||
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_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);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
if let Some(track) = state.tracks.get(state.col().saturating_sub(1)) {
|
||||
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(
|
||||
&state, buf, x, y + 1, match view {
|
||||
LauncherView::Sequencer => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim(),
|
||||
}
|
||||
);
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
fn draw_highlight (buf: &mut Buffer, highlight: &Option<Rect>, style: Style) {
|
||||
if let Some(area) = highlight {
|
||||
draw_box_styled(buf, *area, Some(style));
|
||||
}
|
||||
}
|
||||
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 => {
|
||||
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()
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue