diff --git a/Justfile b/Justfile index 19f8e663..611f5fe2 100644 --- a/Justfile +++ b/Justfile @@ -1,9 +1,11 @@ default: just -l + status: cargo c cloc --by-file src/ git status + push: git push -u codeberg main git push -u origin main @@ -16,18 +18,35 @@ fpush: ftpush: git push --tags -fu codeberg git push --tags -fu origin + transport: reset cargo run --bin tek_transport +transport-release: + reset + cargo run --release --bin tek_transport + arranger: reset cargo run --bin tek_arranger +arranger-release: + reset + cargo run --release --bin tek_arranger + +groovebox: + reset + cargo run --bin tek_groovebox +groovebox-release: + reset + cargo run --release --bin tek_groovebox + sequencer: reset cargo run --bin tek_sequencer sequencer-release: reset cargo run --release --bin tek_sequencer + mixer: reset cargo run --bin tek_mixer diff --git a/crates/tek/Cargo.toml b/crates/tek/Cargo.toml index 77967f73..349ca090 100644 --- a/crates/tek/Cargo.toml +++ b/crates/tek/Cargo.toml @@ -39,6 +39,10 @@ path = "src/cli/cli_arranger.rs" name = "tek_sequencer" path = "src/cli/cli_sequencer.rs" +[[bin]] +name = "tek_groovebox" +path = "src/cli/cli_groovebox.rs" + [[bin]] name = "tek_transport" path = "src/cli/cli_transport.rs" diff --git a/crates/tek/src/cli/cli_groovebox.rs b/crates/tek/src/cli/cli_groovebox.rs index e69de29b..77bac1bc 100644 --- a/crates/tek/src/cli/cli_groovebox.rs +++ b/crates/tek/src/cli/cli_groovebox.rs @@ -0,0 +1,25 @@ +include!("../lib.rs"); +pub fn main () -> Usually<()> { + GrooveboxCli::parse().run() +} + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +pub struct GrooveboxCli; + +impl GrooveboxCli { + fn run (&self) -> Usually<()> { + Tui::run(JackClient::new("tek_groovebox")?.activate_with(|jack|{ + let midi_in_1 = jack.read().unwrap().register_port("in1", MidiIn::default())?; + let midi_out = jack.read().unwrap().register_port("out", MidiOut::default())?; + let midi_in_2 = jack.read().unwrap().register_port("in2", MidiIn::default())?; + let audio_in_1 = jack.read().unwrap().register_port("inL", AudioIn::default())?; + let audio_in_2 = jack.read().unwrap().register_port("inR", AudioIn::default())?; + let audio_out_1 = jack.read().unwrap().register_port("out1", AudioOut::default())?; + let audio_out_2 = jack.read().unwrap().register_port("out2", AudioOut::default())?; + let mut app = GrooveboxTui::try_from(jack)?; + Ok(app) + })?)?; + Ok(()) + } +} diff --git a/crates/tek/src/core/input.rs b/crates/tek/src/core/input.rs index e9388793..19c42b3b 100644 --- a/crates/tek/src/core/input.rs +++ b/crates/tek/src/core/input.rs @@ -17,6 +17,16 @@ pub trait Handle: Send + Sync { fn handle (&mut self, context: &E::Input) -> Perhaps; } +#[macro_export] macro_rules! handle { + (<$E:ty>|$self:ident:$Struct:ty,$input:ident|$handler:expr) => { + impl Handle<$E> for $Struct { + fn handle (&mut $self, $input: &<$E as Engine>::Input) -> Perhaps<<$E as Engine>::Handled> { + $handler + } + } + } +} + impl> Handle for &mut H { fn handle (&mut self, context: &E::Input) -> Perhaps { (*self).handle(context) diff --git a/crates/tek/src/lib.rs b/crates/tek/src/lib.rs index f13af271..84ad431d 100644 --- a/crates/tek/src/lib.rs +++ b/crates/tek/src/lib.rs @@ -18,10 +18,10 @@ pub(crate) use ratatui::{ pub(crate) use jack; pub(crate) use jack::{ - Client, ProcessScope, Control, CycleTimes, - Port, PortSpec, MidiIn, MidiOut, AudioOut, Unowned, - Transport, TransportState, MidiIter, RawMidi, contrib::ClosureProcessHandler, + Client, ProcessScope, Control, CycleTimes, + Port, PortSpec, MidiIn, MidiOut, AudioIn, AudioOut, Unowned, + Transport, TransportState, MidiIter, RawMidi, }; pub(crate) use midly; diff --git a/crates/tek/src/tui/_todo_tui_mixer.rs b/crates/tek/src/tui/_todo_tui_mixer.rs index c8203770..111d5cb2 100644 --- a/crates/tek/src/tui/_todo_tui_mixer.rs +++ b/crates/tek/src/tui/_todo_tui_mixer.rs @@ -164,88 +164,85 @@ impl Content for Track { } } -impl Handle for Mixer { - fn handle (&mut self, engine: &TuiInput) -> Perhaps { - if let TuiEvent::Input(crossterm::event::Event::Key(event)) = engine.event() { +handle!(|self:Mixer,engine|{ + if let TuiEvent::Input(crossterm::event::Event::Key(event)) = engine.event() { - match event.code { - //KeyCode::Char('c') => { - //if event.modifiers == KeyModifiers::CONTROL { - //self.exit(); - //} - //}, - KeyCode::Down => { - self.selected_track = (self.selected_track + 1) % self.tracks.len(); - println!("{}", self.selected_track); - return Ok(Some(true)) - }, - KeyCode::Up => { - if self.selected_track == 0 { - self.selected_track = self.tracks.len() - 1; - } else { - self.selected_track -= 1; - } - println!("{}", self.selected_track); - return Ok(Some(true)) - }, - KeyCode::Left => { - if self.selected_column == 0 { - self.selected_column = 6 - } else { - self.selected_column -= 1; - } - return Ok(Some(true)) - }, - KeyCode::Right => { - if self.selected_column == 6 { - self.selected_column = 0 - } else { - self.selected_column += 1; - } - return Ok(Some(true)) - }, - _ => { - println!("\n{event:?}"); + match event.code { + //KeyCode::Char('c') => { + //if event.modifiers == KeyModifiers::CONTROL { + //self.exit(); + //} + //}, + KeyCode::Down => { + self.selected_track = (self.selected_track + 1) % self.tracks.len(); + println!("{}", self.selected_track); + return Ok(Some(true)) + }, + KeyCode::Up => { + if self.selected_track == 0 { + self.selected_track = self.tracks.len() - 1; + } else { + self.selected_track -= 1; } + println!("{}", self.selected_track); + return Ok(Some(true)) + }, + KeyCode::Left => { + if self.selected_column == 0 { + self.selected_column = 6 + } else { + self.selected_column -= 1; + } + return Ok(Some(true)) + }, + KeyCode::Right => { + if self.selected_column == 6 { + self.selected_column = 0 + } else { + self.selected_column += 1; + } + return Ok(Some(true)) + }, + _ => { + println!("\n{event:?}"); } + } - } - Ok(None) } -} -impl Handle for Track { - fn handle (&mut self, from: &TuiInput) -> Perhaps { - match from.event() { - //, NONE, "chain_cursor_up", "move cursor up", || { - key!(KeyCode::Up) => { - Ok(Some(true)) - }, - // , NONE, "chain_cursor_down", "move cursor down", || { - key!(KeyCode::Down) => { - Ok(Some(true)) - }, - // Left, NONE, "chain_cursor_left", "move cursor left", || { - key!(KeyCode::Left) => { - //if let Some(track) = app.arranger.track_mut() { - //track.device = track.device.saturating_sub(1); - //return Ok(true) - //} - Ok(Some(true)) - }, - // , NONE, "chain_cursor_right", "move cursor right", || { - key!(KeyCode::Right) => { - //if let Some(track) = app.arranger.track_mut() { - //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); - //return Ok(true) - //} - Ok(Some(true)) - }, - // , NONE, "chain_mode_switch", "switch the display mode", || { - key!(KeyCode::Char('`')) => { - //app.chain_mode = !app.chain_mode; - Ok(Some(true)) - }, - _ => Ok(None) - } + Ok(None) +}); + +handle!(|self:Track,from|{ + match from.event() { + //, NONE, "chain_cursor_up", "move cursor up", || { + key!(KeyCode::Up) => { + Ok(Some(true)) + }, + // , NONE, "chain_cursor_down", "move cursor down", || { + key!(KeyCode::Down) => { + Ok(Some(true)) + }, + // Left, NONE, "chain_cursor_left", "move cursor left", || { + key!(KeyCode::Left) => { + //if let Some(track) = app.arranger.track_mut() { + //track.device = track.device.saturating_sub(1); + //return Ok(true) + //} + Ok(Some(true)) + }, + // , NONE, "chain_cursor_right", "move cursor right", || { + key!(KeyCode::Right) => { + //if let Some(track) = app.arranger.track_mut() { + //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); + //return Ok(true) + //} + Ok(Some(true)) + }, + // , NONE, "chain_mode_switch", "switch the display mode", || { + key!(KeyCode::Char('`')) => { + //app.chain_mode = !app.chain_mode; + Ok(Some(true)) + }, + _ => Ok(None) } -} +}); diff --git a/crates/tek/src/tui/_todo_tui_plugin.rs b/crates/tek/src/tui/_todo_tui_plugin.rs index 54cf0704..8f4eddfd 100644 --- a/crates/tek/src/tui/_todo_tui_plugin.rs +++ b/crates/tek/src/tui/_todo_tui_plugin.rs @@ -83,66 +83,65 @@ fn draw_header (state: &Plugin, to: &mut TuiOutput, x: u16, y: u16, w: u1 Ok(Rect { x, y, width: w, height: 1 }) } -impl Handle for Plugin { - fn handle (&mut self, from: &TuiInput) -> Perhaps { - match from.event() { - key!(KeyCode::Up) => { - self.selected = self.selected.saturating_sub(1); - Ok(Some(true)) - }, - key!(KeyCode::Down) => { - self.selected = (self.selected + 1).min(match &self.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, - _ => unimplemented!() - }); - Ok(Some(true)) - }, - key!(KeyCode::PageUp) => { - self.selected = self.selected.saturating_sub(8); - Ok(Some(true)) - }, - key!(KeyCode::PageDown) => { - self.selected = (self.selected + 10).min(match &self.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, - _ => unimplemented!() - }); - Ok(Some(true)) - }, - key!(KeyCode::Char(',')) => { - match self.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[self.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value - 0.01); - } - }, - _ => {} - } - Ok(Some(true)) - }, - key!(KeyCode::Char('.')) => { - match self.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[self.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value + 0.01); - } - }, - _ => {} - } - Ok(Some(true)) - }, - key!(KeyCode::Char('g')) => { - match self.plugin { - Some(PluginKind::LV2(ref mut plugin)) => { - plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); - }, - Some(_) => unreachable!(), - None => {} - } - Ok(Some(true)) - }, - _ => Ok(None) - } +handle!(|self:Plugin,from|{ + match from.event() { + key!(KeyCode::Up) => { + self.selected = self.selected.saturating_sub(1); + Ok(Some(true)) + }, + key!(KeyCode::Down) => { + self.selected = (self.selected + 1).min(match &self.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, + _ => unimplemented!() + }); + Ok(Some(true)) + }, + key!(KeyCode::PageUp) => { + self.selected = self.selected.saturating_sub(8); + Ok(Some(true)) + }, + key!(KeyCode::PageDown) => { + self.selected = (self.selected + 10).min(match &self.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, + _ => unimplemented!() + }); + Ok(Some(true)) + }, + key!(KeyCode::Char(',')) => { + match self.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[self.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value - 0.01); + } + }, + _ => {} + } + Ok(Some(true)) + }, + key!(KeyCode::Char('.')) => { + match self.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[self.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value + 0.01); + } + }, + _ => {} + } + Ok(Some(true)) + }, + key!(KeyCode::Char('g')) => { + match self.plugin { + Some(PluginKind::LV2(ref mut plugin)) => { + plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); + }, + Some(_) => unreachable!(), + None => {} + } + Ok(Some(true)) + }, + _ => Ok(None) } +}); } diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 8347f172..858647b4 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -56,9 +56,94 @@ pub struct ArrangerTui { pub perf: PerfModel, } -impl Handle for ArrangerTui { - fn handle (&mut self, i: &TuiInput) -> Perhaps { - ArrangerCommand::execute_with_state(self, i) +has_clock!(|self:ArrangerTui|&self.clock); +has_phrases!(|self:ArrangerTui|self.phrases.phrases); +has_editor!(|self:ArrangerTui|self.editor); +handle!(|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input)); +render!(|self: ArrangerTui|{ + let arranger_focused = self.arranger_focused(); + let transport_focused = if let ArrangerFocus::Transport(_) = self.focus.inner() { + true + } else { + false + }; + let transport = TransportView::from((self, None, transport_focused)); + let with_transport = move|x|col!([transport, x]); + let border = Lozenge(Style::default() + .bg(TuiTheme::border_bg()) + .fg(TuiTheme::border_fg(arranger_focused))); + let arranger = move||border.wrap(Tui::grow_y(1, lay!(|add|{ + match self.mode { + ArrangerMode::Horizontal => add(&arranger_content_horizontal(self))?, + ArrangerMode::Vertical(factor) => add(&arranger_content_vertical(self, factor))? + }; + add(&self.size) + }))); + with_transport(col!([ + Tui::fixed_y(self.splits[0], lay!([ + arranger(), + Tui::push_x(1, Tui::fg( + TuiTheme::title_fg(arranger_focused), + format!("[{}] Arranger", if self.entered { + "■" + } else { + " " + }) + )) + ])), + Split::right(false, self.splits[1], PhraseListView(&self.phrases), &self.editor), + ])) +}); +audio!(|self: ArrangerTui, client, scope|{ + // Start profiling cycle + let t0 = self.perf.get_t0(); + // Update transport clock + if ClockAudio(self).process(client, scope) == Control::Quit { + return Control::Quit + } + // Update MIDI sequencers + let tracks = &mut self.tracks; + let note_buf = &mut self.note_buf; + let midi_buf = &mut self.midi_buf; + if TracksAudio(tracks, note_buf, midi_buf, Default::default()) + .process(client, scope) == Control::Quit { + return Control::Quit + } + // FIXME: one of these per playing track + //self.now.set(0.); + //if let ArrangerSelection::Clip(t, s) = self.selected { + //let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); + //if let Some(Some(Some(phrase))) = phrase { + //if let Some(track) = self.tracks().get(t) { + //if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { + //let phrase = phrase.read().unwrap(); + //if *playing.read().unwrap() == *phrase { + //let pulse = self.current().pulse.get(); + //let start = started_at.pulse.get(); + //let now = (pulse - start) % phrase.length as f64; + //self.now.set(now); + //} + //} + //} + //} + //} + // End profiling cycle + self.perf.update(t0, scope); + return Control::Continue +}); + +impl HasPhraseList for ArrangerTui { + fn phrases_focused (&self) -> bool { + self.focused() == ArrangerFocus::Phrases + } + fn phrases_entered (&self) -> bool { + self.entered() && self.phrases_focused() + } + fn phrases_mode (&self) -> &Option { + &self.phrases.mode + } + fn phrase_index (&self) -> usize { + self.phrases.phrase.load(Ordering::Relaxed) } } @@ -320,97 +405,8 @@ impl TransportControl for ArrangerTui { } } } -has_clock!(|self:ArrangerTui|&self.clock); has_clock!(|self:ArrangerTrack|self.player.clock()); -has_phrases!(|self:ArrangerTui|self.phrases.phrases); -has_editor!(|self:ArrangerTui|self.editor); has_player!(|self:ArrangerTrack|self.player); -render!(|self: ArrangerTui|{ - let arranger_focused = self.arranger_focused(); - let transport_focused = if let ArrangerFocus::Transport(_) = self.focus.inner() { - true - } else { - false - }; - let transport = TransportView::from((self, None, transport_focused)); - let with_transport = move|x|col!([transport, x]); - let border = Lozenge(Style::default() - .bg(TuiTheme::border_bg()) - .fg(TuiTheme::border_fg(arranger_focused))); - let arranger = move||border.wrap(Tui::grow_y(1, lay!(|add|{ - match self.mode { - ArrangerMode::Horizontal => add(&arranger_content_horizontal(self))?, - ArrangerMode::Vertical(factor) => add(&arranger_content_vertical(self, factor))? - }; - add(&self.size) - }))); - with_transport(col!([ - Tui::fixed_y(self.splits[0], lay!([ - arranger(), - Tui::push_x(1, Tui::fg( - TuiTheme::title_fg(arranger_focused), - format!("[{}] Arranger", if self.entered { - "■" - } else { - " " - }) - )) - ])), - Split::right(false, self.splits[1], PhraseListView(&self.phrases), &self.editor), - ])) -}); -audio!(|self: ArrangerTui, client, scope|{ - // Start profiling cycle - let t0 = self.perf.get_t0(); - // Update transport clock - if ClockAudio(self).process(client, scope) == Control::Quit { - return Control::Quit - } - // Update MIDI sequencers - let tracks = &mut self.tracks; - let note_buf = &mut self.note_buf; - let midi_buf = &mut self.midi_buf; - if TracksAudio(tracks, note_buf, midi_buf, Default::default()) - .process(client, scope) == Control::Quit { - return Control::Quit - } - // FIXME: one of these per playing track - //self.now.set(0.); - //if let ArrangerSelection::Clip(t, s) = self.selected { - //let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); - //if let Some(Some(Some(phrase))) = phrase { - //if let Some(track) = self.tracks().get(t) { - //if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { - //let phrase = phrase.read().unwrap(); - //if *playing.read().unwrap() == *phrase { - //let pulse = self.current().pulse.get(); - //let start = started_at.pulse.get(); - //let now = (pulse - start) % phrase.length as f64; - //self.now.set(now); - //} - //} - //} - //} - //} - // End profiling cycle - self.perf.update(t0, scope); - return Control::Continue -}); - -impl HasPhraseList for ArrangerTui { - fn phrases_focused (&self) -> bool { - self.focused() == ArrangerFocus::Phrases - } - fn phrases_entered (&self) -> bool { - self.entered() && self.phrases_focused() - } - fn phrases_mode (&self) -> &Option { - &self.phrases.mode - } - fn phrase_index (&self) -> usize { - self.phrases.phrase.load(Ordering::Relaxed) - } -} /// Sections in the arranger app that may be focused #[derive(Copy, Clone, PartialEq, Eq, Debug)] diff --git a/crates/tek/src/tui/app_groovebox.rs b/crates/tek/src/tui/app_groovebox.rs index efd4f1fb..43a45923 100644 --- a/crates/tek/src/tui/app_groovebox.rs +++ b/crates/tek/src/tui/app_groovebox.rs @@ -12,7 +12,7 @@ impl TryFrom<&Arc>> for GrooveboxTui { } } -struct GrooveboxTui { +pub struct GrooveboxTui { pub sequencer: SequencerTui, pub sampler: SamplerTui, pub focus: GrooveboxFocus, @@ -30,3 +30,7 @@ pub enum GrooveboxFocus { /// The sample player is focused Sampler } + +render!(|self:GrooveboxTui|"are we groovy yet?"); +audio!(|self:GrooveboxTui,_client,_process|Control::Continue); +handle!(|self:GrooveboxTui,input|Ok(None)); diff --git a/crates/tek/src/tui/app_sampler.rs b/crates/tek/src/tui/app_sampler.rs index c626e38e..9501d125 100644 --- a/crates/tek/src/tui/app_sampler.rs +++ b/crates/tek/src/tui/app_sampler.rs @@ -221,42 +221,40 @@ fn read_sample_data (_: &str) -> Usually<(usize, Vec>)> { todo!(); } -impl Handle for SamplerTui { - fn handle (&mut self, from: &TuiInput) -> Perhaps { - let cursor = &mut self.cursor; - let unmapped = &mut self.state.unmapped; - let mapped = &self.state.mapped; - let voices = &self.state.voices; - match from.event() { - key_pat!(KeyCode::Up) => cursor.0 = if cursor.0 == 0 { - mapped.len() + unmapped.len() - 1 - } else { - cursor.0 - 1 - }, - key_pat!(KeyCode::Down) => { - cursor.0 = (cursor.0 + 1) % (mapped.len() + unmapped.len()); - }, - key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { - voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); - }, - key_pat!(KeyCode::Char('a')) => { - let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); - *self.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - unmapped.push(sample); - }, - key_pat!(KeyCode::Char('r')) => if let Some(sample) = self.sample() { - *self.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - }, - key_pat!(KeyCode::Enter) => if let Some(sample) = self.sample() { - self.editing = Some(sample.clone()); - }, - _ => { - return Ok(None) - } +handle!(|self:SamplerTui,from|{ + let cursor = &mut self.cursor; + let unmapped = &mut self.state.unmapped; + let mapped = &self.state.mapped; + let voices = &self.state.voices; + match from.event() { + key_pat!(KeyCode::Up) => cursor.0 = if cursor.0 == 0 { + mapped.len() + unmapped.len() - 1 + } else { + cursor.0 - 1 + }, + key_pat!(KeyCode::Down) => { + cursor.0 = (cursor.0 + 1) % (mapped.len() + unmapped.len()); + }, + key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { + voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); + }, + key_pat!(KeyCode::Char('a')) => { + let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); + *self.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); + unmapped.push(sample); + }, + key_pat!(KeyCode::Char('r')) => if let Some(sample) = self.sample() { + *self.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); + }, + key_pat!(KeyCode::Enter) => if let Some(sample) = self.sample() { + self.editing = Some(sample.clone()); + }, + _ => { + return Ok(None) } - Ok(Some(true)) } -} + Ok(Some(true)) +}); fn scan (dir: &PathBuf) -> Usually<(Vec, Vec)> { let (mut subdirs, mut files) = std::fs::read_dir(dir)? diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 94679b86..a17aa161 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -144,24 +144,11 @@ impl InputToCommand for SequencerCommand { } } -audio!(|self:SequencerTui, client, scope|{ - // Start profiling cycle - let t0 = self.perf.get_t0(); - // Update transport clock - if Control::Quit == ClockAudio(self).process(client, scope) { - return Control::Quit - } - // Update MIDI sequencer - if Control::Quit == PlayerAudio( - &mut self.player, &mut self.note_buf, &mut self.midi_buf - ).process(client, scope) { - return Control::Quit - } - // End profiling cycle - self.perf.update(t0, scope); - Control::Continue -}); - +has_size!(|self:SequencerTui|&self.size); +has_clock!(|self:SequencerTui|&self.clock); +has_phrases!(|self:SequencerTui|self.phrases.phrases); +has_editor!(|self:SequencerTui|self.editor); +handle!(|self:SequencerTui,i|SequencerCommand::execute_with_state(self, i)); render!(|self: SequencerTui|{ let w = self.size.w(); let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; @@ -181,11 +168,23 @@ render!(|self: SequencerTui|{ ]), transport]); with_size(with_status(col!([ toolbar, editor, ]))) }); - -has_size!(|self:SequencerTui|&self.size); -has_clock!(|self:SequencerTui|&self.clock); -has_phrases!(|self:SequencerTui|self.phrases.phrases); -has_editor!(|self:SequencerTui|self.editor); +audio!(|self:SequencerTui, client, scope|{ + // Start profiling cycle + let t0 = self.perf.get_t0(); + // Update transport clock + if Control::Quit == ClockAudio(self).process(client, scope) { + return Control::Quit + } + // Update MIDI sequencer + if Control::Quit == PlayerAudio( + &mut self.player, &mut self.note_buf, &mut self.midi_buf + ).process(client, scope) { + return Control::Quit + } + // End profiling cycle + self.perf.update(t0, scope); + Control::Continue +}); impl HasPhraseList for SequencerTui { fn phrases_focused (&self) -> bool { @@ -202,12 +201,6 @@ impl HasPhraseList for SequencerTui { } } -impl Handle for SequencerTui { - fn handle (&mut self, i: &TuiInput) -> Perhaps { - SequencerCommand::execute_with_state(self, i) - } -} - /// Status bar for sequencer app #[derive(Clone)] pub struct SequencerStatusBar { diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index fe8a4316..bc15dddf 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -39,6 +39,7 @@ impl std::fmt::Debug for TransportTui { has_clock!(|self:TransportTui|&self.clock); audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope)); +handle!(|self:TransportTui,from|TransportCommand::execute_with_state(self, from)); render!(|self: TransportTui|TransportView::from((self, None, true))); pub struct TransportView { @@ -201,12 +202,6 @@ impl StatusBar for TransportStatusBar { render!(|self: TransportStatusBar|"todo"); -impl Handle for TransportTui { - fn handle (&mut self, from: &TuiInput) -> Perhaps { - TransportCommand::execute_with_state(self, from) - } -} - pub trait TransportControl: HasClock + { fn transport_focused (&self) -> Option; } diff --git a/crates/tek/src/tui/piano_horizontal.rs b/crates/tek/src/tui/piano_horizontal.rs index 38ac132f..a28f9877 100644 --- a/crates/tek/src/tui/piano_horizontal.rs +++ b/crates/tek/src/tui/piano_horizontal.rs @@ -239,10 +239,11 @@ impl PianoHorizontal { for (x, time_start) in (0..phrase.length).step_by(zoom).enumerate() { for (y, note) in (0..127).rev().enumerate() { - let cell = buf.get_mut(x, note).unwrap(); - if notes_on[note] { - cell.set_char('▂'); - cell.set_style(style); + if let Some(cell) = buf.get_mut(x, note) { + if notes_on[note] { + cell.set_char('▂'); + cell.set_style(style); + } } }