reenable global play/pause

This commit is contained in:
🪞👃🪞 2024-11-25 16:10:21 +01:00
parent 86f37fb278
commit 340919830a
12 changed files with 208 additions and 165 deletions

View file

@ -2,8 +2,8 @@ use crate::*;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ClockCommand { pub enum ClockCommand {
Play(Option<usize>), Play(Option<u32>),
Pause(Option<usize>), Pause(Option<u32>),
SeekUsec(f64), SeekUsec(f64),
SeekSample(f64), SeekSample(f64),
SeekPulse(f64), SeekPulse(f64),
@ -15,73 +15,35 @@ pub enum ClockCommand {
impl<T: ClockApi> Command<T> for ClockCommand { impl<T: ClockApi> Command<T> for ClockCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> { fn execute (self, state: &mut T) -> Perhaps<Self> {
use ClockCommand::*; use ClockCommand::*;
Ok(Some(match self { match self {
Play(_start) => { Play(start) => state.play_from(start)?,
todo!() Pause(pause) => state.pause_at(pause)?,
}, SeekUsec(usec) => state.current().update_from_usec(usec),
Pause(_start) => { SeekSample(sample) => state.current().update_from_sample(sample),
todo!() SeekPulse(pulse) => state.current().update_from_pulse(pulse),
}, SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))),
SeekUsec(usec) => { SetQuant(quant) => return Ok(Some(SetQuant(state.quant().set(quant)))),
state.current().update_from_usec(usec); SetSync(sync) => return Ok(Some(SetSync(state.sync().set(sync)))),
return Ok(None) };
}, Ok(None)
SeekSample(sample) => {
state.current().update_from_sample(sample);
return Ok(None)
},
SeekPulse(pulse) => {
state.current().update_from_pulse(pulse);
return Ok(None)
},
SetBpm(bpm) => SetBpm(state.timebase().bpm.set(bpm)),
SetQuant(quant) => SetQuant(state.quant().set(quant)),
SetSync(sync) => SetSync(state.sync().set(sync)),
}))
} }
} }
pub trait ClockApi: Send + Sync { pub trait ClockApi: Send + Sync {
/// Current moment in time
fn current (&self) -> &Arc<Instant>;
/// Temporal resolution in all units
fn timebase (&self) -> &Arc<Timebase> {
&self.current().timebase
}
fn sr (&self) -> &SampleRate {
&self.timebase().sr
}
fn bpm (&self) -> &BeatsPerMinute {
&self.timebase().bpm
}
fn ppq (&self) -> &PulsesPerQuaver {
&self.timebase().ppq
}
/// Note quantization factor /// Note quantization factor
fn quant (&self) -> &Arc<Quantize>; fn quant (&self) -> &Arc<Quantize>;
fn next_quant (&self) -> f64 {
next_note_length(self.quant().get() as usize) as f64
}
fn prev_quant (&self) -> f64 {
prev_note_length(self.quant().get() as usize) as f64
}
/// Launch quantization factor /// Launch quantization factor
fn sync (&self) -> &Arc<LaunchSync>; fn sync (&self) -> &Arc<LaunchSync>;
fn next_sync (&self) -> f64 { /// Current moment in time
next_note_length(self.sync().get() as usize) as f64 fn current (&self) -> &Arc<Instant>;
} /// Current temporal resolutions
fn prev_sync (&self) -> f64 { fn timebase (&self) -> &Arc<Timebase> { &self.current().timebase }
prev_note_length(self.sync().get() as usize) as f64 /// Current sample rate
} fn sr (&self) -> &SampleRate { &self.timebase().sr }
/// Current tempo
fn next_launch_pulse (&self) -> usize { fn bpm (&self) -> &BeatsPerMinute { &self.timebase().bpm }
let sync = self.sync().get() as usize; /// Current MIDI resolution
let pulse = self.current().pulse.get() as usize; fn ppq (&self) -> &PulsesPerQuaver { &self.timebase().ppq }
if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync }
}
/// Handle to JACK transport /// Handle to JACK transport
fn transport_handle (&self) -> &Arc<Transport>; fn transport_handle (&self) -> &Arc<Transport>;
/// Playback state /// Playback state
@ -89,12 +51,39 @@ pub trait ClockApi: Send + Sync {
/// Global sample and usec at which playback started /// Global sample and usec at which playback started
fn transport_offset (&self) -> &Arc<RwLock<Option<(usize, usize)>>>; fn transport_offset (&self) -> &Arc<RwLock<Option<(usize, usize)>>>;
fn is_stopped (&self) -> bool { fn next_launch_pulse (&self) -> usize {
*self.transport_state().read().unwrap() == Some(TransportState::Stopped) let sync = self.sync().get() as usize;
let pulse = self.current().pulse.get() as usize;
if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync }
}
fn play_from (&mut self, start: Option<u32>) -> Usually<()> {
if let Some(start) = start {
self.transport_handle().locate(start)?;
}
self.transport_handle().start()?;
self.update_transport_state()
} }
fn is_rolling (&self) -> bool { fn is_rolling (&self) -> bool {
*self.transport_state().read().unwrap() == Some(TransportState::Rolling) *self.transport_state().read().unwrap() == Some(TransportState::Rolling)
} }
fn pause_at (&mut self, pause: Option<u32>) -> Usually<()> {
self.transport_handle().stop()?;
if let Some(pause) = pause {
self.transport_handle().locate(pause)?;
}
self.update_transport_state()
}
fn is_stopped (&self) -> bool {
*self.transport_state().read().unwrap() == Some(TransportState::Stopped)
}
fn update_transport_state (&self) -> Usually<()> {
*self.transport_state().write().unwrap() = Some(self.transport_handle().query_state()?);
Ok(())
}
fn toggle_play (&self) -> Usually<()> { fn toggle_play (&self) -> Usually<()> {
let playing = self.transport_state().read().unwrap().expect("1st sample has not been processed yet"); let playing = self.transport_state().read().unwrap().expect("1st sample has not been processed yet");
let playing = match playing { let playing = match playing {

View file

@ -1,4 +1,4 @@
pub use tek_core::*; pub(crate) use tek_core::*;
pub(crate) use tek_core::midly::{*, live::LiveEvent, num::u7}; pub(crate) use tek_core::midly::{*, live::LiveEvent, num::u7};
pub(crate) use std::thread::JoinHandle; pub(crate) use std::thread::JoinHandle;
pub(crate) use std::fmt::{Debug, Formatter, Error}; pub(crate) use std::fmt::{Debug, Formatter, Error};

View file

@ -1,6 +1,5 @@
use crate::*; use crate::*;
use rand::{thread_rng, distributions::uniform::UniformSampler}; use rand::{thread_rng, distributions::uniform::UniformSampler};
pub use palette::{*, convert::*, okhsl::*};
/// A color in OKHSL and RGB representations. /// A color in OKHSL and RGB representations.
#[derive(Debug, Default, Copy, Clone, PartialEq)] #[derive(Debug, Default, Copy, Clone, PartialEq)]

View file

@ -15,6 +15,7 @@ pub(crate) use std::io::{stdout};
pub(crate) use std::thread::{spawn, JoinHandle}; pub(crate) use std::thread::{spawn, JoinHandle};
pub(crate) use std::time::Duration; pub(crate) use std::time::Duration;
pub(crate) use atomic_float::*; pub(crate) use atomic_float::*;
pub(crate) use palette::{*, convert::*, okhsl::*};
use better_panic::{Settings, Verbosity}; use better_panic::{Settings, Verbosity};
use std::ops::{Add, Sub, Mul, Div, Rem}; use std::ops::{Add, Sub, Mul, Div, Rem};
use std::cmp::{Ord, Eq, PartialEq}; use std::cmp::{Ord, Eq, PartialEq};

View file

@ -116,10 +116,26 @@ impl_time_unit!(Pulse);
/// Quantization setting for launching clips /// Quantization setting for launching clips
#[derive(Debug, Default)] pub struct LaunchSync(AtomicF64); #[derive(Debug, Default)] pub struct LaunchSync(AtomicF64);
impl_time_unit!(LaunchSync); impl_time_unit!(LaunchSync);
impl LaunchSync {
pub fn next (&self) -> f64 {
next_note_length(self.get() as usize) as f64
}
pub fn prev (&self) -> f64 {
prev_note_length(self.get() as usize) as f64
}
}
/// Quantization setting for notes /// Quantization setting for notes
#[derive(Debug, Default)] pub struct Quantize(AtomicF64); #[derive(Debug, Default)] pub struct Quantize(AtomicF64);
impl_time_unit!(Quantize); impl_time_unit!(Quantize);
impl Quantize {
pub fn next (&self) -> f64 {
next_note_length(self.get() as usize) as f64
}
pub fn prev (&self) -> f64 {
prev_note_length(self.get() as usize) as f64
}
}
/// Temporal resolutions: sample rate, tempo, MIDI pulses per quaver (beat) /// Temporal resolutions: sample rate, tempo, MIDI pulses per quaver (beat)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,6 +1,6 @@
pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers}; pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers};
pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage}; pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage};
pub(crate) use tek_core::jack::*; pub(crate) use tek_core::{*, jack::*};
pub(crate) use tek_api::*; pub(crate) use tek_api::*;
pub(crate) use std::collections::BTreeMap; pub(crate) use std::collections::BTreeMap;

View file

@ -21,7 +21,7 @@ impl<T: TransportControl> Command<T> for TransportCommand {
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug)]
pub enum SequencerCommand { pub enum SequencerCommand {
Focus(FocusCommand), Focus(FocusCommand),
Undo, Undo,
@ -62,29 +62,24 @@ pub enum ArrangerCommand {
Zoom(usize), Zoom(usize),
Phrases(PhrasesCommand), Phrases(PhrasesCommand),
Editor(PhraseCommand), Editor(PhraseCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
} }
impl Command<ArrangerTui> for ArrangerCommand { impl Command<ArrangerTui> for ArrangerCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> { fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
use ArrangerCommand::*; use ArrangerCommand::*;
Ok(match self { Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus), Focus(cmd) => cmd.execute(state)?.map(Focus),
Scene(cmd) => cmd.execute(state)?.map(Scene), Scene(cmd) => cmd.execute(state)?.map(Scene),
Track(cmd) => cmd.execute(state)?.map(Track), Track(cmd) => cmd.execute(state)?.map(Track),
Clip(cmd) => cmd.execute(state)?.map(Clip), Clip(cmd) => cmd.execute(state)?.map(Clip),
Phrases(cmd) => cmd.execute(state)?.map(Phrases), Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor), Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock), Clock(cmd) => cmd.execute(state)?.map(Clock),
Zoom(zoom) => { todo!(); }, Zoom(zoom) => { todo!(); },
Select(selected) => { Select(selected) => {
*state.selected_mut() = selected; *state.selected_mut() = selected;
None None
}, },
EditPhrase(phrase) => {
state.edit_phrase(&phrase);
None
},
_ => { todo!() } _ => { todo!() }
}) })
} }
@ -309,7 +304,7 @@ impl<T: PhrasesControl> Command<T> for FileBrowserCommand {
} }
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, Debug)]
pub enum PhraseCommand { pub enum PhraseCommand {
// TODO: 1-9 seek markers that by default start every 8th of the phrase // TODO: 1-9 seek markers that by default start every 8th of the phrase
ToggleDirection, ToggleDirection,
@ -323,15 +318,26 @@ pub enum PhraseCommand {
TimeCursorSet(Option<usize>), TimeCursorSet(Option<usize>),
TimeScrollSet(usize), TimeScrollSet(usize),
TimeZoomSet(usize), TimeZoomSet(usize),
Show(Option<Arc<RwLock<Phrase>>>),
} }
impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand { impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> { fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseCommand::*; use PhraseCommand::*;
Ok(match self { Ok(match self {
Show(phrase) => {
state.edit_phrase(phrase);
None
},
ToggleDirection => { todo!() }, ToggleDirection => { todo!() },
EnterEditMode => { state.focus_enter(); None }, EnterEditMode => {
ExitEditMode => { state.focus_exit(); None }, state.focus_enter();
None
},
ExitEditMode => {
state.focus_exit();
None
},
NoteAppend => { NoteAppend => {
if state.phrase_editor_entered() { if state.phrase_editor_entered() {
state.put_note(); state.put_note();

View file

@ -9,7 +9,7 @@ pub trait SequencerControl: TransportControl {}
pub trait ArrangerControl: TransportControl { pub trait ArrangerControl: TransportControl {
fn selected (&self) -> ArrangerSelection; fn selected (&self) -> ArrangerSelection;
fn selected_mut (&mut self) -> &mut ArrangerSelection; fn selected_mut (&mut self) -> &mut ArrangerSelection;
fn activate (&mut self); fn activate (&mut self) -> Usually<()>;
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>; fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>;
fn toggle_loop (&mut self); fn toggle_loop (&mut self);
fn randomize_color (&mut self); fn randomize_color (&mut self);
@ -54,7 +54,7 @@ impl ArrangerControl for ArrangerTui {
fn selected_mut (&mut self) -> &mut ArrangerSelection { fn selected_mut (&mut self) -> &mut ArrangerSelection {
&mut self.selected &mut self.selected
} }
fn activate (&mut self) { fn activate (&mut self) -> Usually<()> {
if let ArrangerSelection::Scene(s) = self.selected { if let ArrangerSelection::Scene(s) = self.selected {
for (t, track) in self.tracks.iter_mut().enumerate() { for (t, track) in self.tracks.iter_mut().enumerate() {
let phrase = self.scenes[s].clips[t].clone(); let phrase = self.scenes[s].clips[t].clone();
@ -62,15 +62,14 @@ impl ArrangerControl for ArrangerTui {
track.enqueue_next(phrase.as_ref()); track.enqueue_next(phrase.as_ref());
} }
} }
// TODO make transport available here, so that if self.is_stopped() {
// activating a scene when stopped starts playback self.play_from(Some(0))?;
//if self.is_stopped() { }
//self.transport.toggle_play()
//}
} else if let ArrangerSelection::Clip(t, s) = self.selected { } else if let ArrangerSelection::Clip(t, s) = self.selected {
let phrase = self.scenes()[s].clips[t].clone(); let phrase = self.scenes()[s].clips[t].clone();
self.tracks_mut()[t].enqueue_next(phrase.as_ref()); self.tracks_mut()[t].enqueue_next(phrase.as_ref());
}; };
Ok(())
} }
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> { fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
self.selected_scene()?.clips.get(self.selected.track()?)?.clone() self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
@ -114,8 +113,9 @@ pub trait PhrasesControl: HasPhrases {
} }
pub trait PhraseEditorControl: HasFocus { pub trait PhraseEditorControl: HasFocus {
fn edit_phrase (&self, phrase: &Option<Arc<RwLock<Phrase>>>); fn edit_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>);
fn editing_phrase (&self) -> &Option<Arc<RwLock<Phrase>>>; fn phrase_to_edit (&self) -> Option<Arc<RwLock<Phrase>>>;
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>;
fn phrase_editor_entered (&self) -> bool; fn phrase_editor_entered (&self) -> bool;
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>; fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>; fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
@ -124,7 +124,7 @@ pub trait PhraseEditorControl: HasFocus {
fn put_note (&mut self); fn put_note (&mut self);
fn time_cursor_advance (&self) { fn time_cursor_advance (&self) {
let point = self.time_axis().read().unwrap().point; let point = self.time_axis().read().unwrap().point;
let length = self.editing_phrase().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); let length = self.phrase_editing().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
let forward = |time|(time + self.note_len()) % length; let forward = |time|(time + self.note_len()) % length;
self.time_axis().write().unwrap().point = point.map(forward); self.time_axis().write().unwrap().point = point.map(forward);
} }

View file

@ -137,17 +137,26 @@ macro_rules! impl_phrases_control {
} }
macro_rules! impl_phrase_editor_control { macro_rules! impl_phrase_editor_control {
($Struct:ident $(:: $field:ident)* [$Focus:expr]) => { (
$Struct:ident $(:: $field:ident)*
[$Focus:expr]
[$self1:ident: $phrase_to_edit:expr]
[$self2:ident, $phrase:ident: $edit_phrase:expr]
) => {
impl PhraseEditorControl for $Struct { impl PhraseEditorControl for $Struct {
fn edit_phrase (&self, phrase: &Option<Arc<RwLock<Phrase>>>) { fn phrase_to_edit (&$self1) -> Option<Arc<RwLock<Phrase>>> {
$phrase_to_edit
}
fn edit_phrase (&mut $self2, $phrase: Option<Arc<RwLock<Phrase>>>) {
$edit_phrase
//self.editor.show(self.selected_phrase().as_ref()); //self.editor.show(self.selected_phrase().as_ref());
//state.editor.phrase = phrase.clone(); //state.editor.phrase = phrase.clone();
//state.focus(ArrangerFocus::PhraseEditor); //state.focus(ArrangerFocus::PhraseEditor);
//state.focus_enter(); //state.focus_enter();
todo!("edit_phrase") //todo!("edit_phrase")
} }
fn editing_phrase (&self) -> &Option<Arc<RwLock<Phrase>>> { fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>> {
todo!("editing_phrase") todo!("phrase_editing")
} }
fn phrase_editor_entered (&self) -> bool { fn phrase_editor_entered (&self) -> bool {
self.entered && self.focused() == $Focus self.entered && self.focused() == $Focus
@ -192,9 +201,13 @@ impl_midi_player!(PhrasePlayerModel);
impl_phrases_control!(SequencerTui); impl_phrases_control!(SequencerTui);
impl_phrases_control!(ArrangerTui); impl_phrases_control!(ArrangerTui);
impl_phrase_editor_control!(SequencerTui [ impl_phrase_editor_control!(SequencerTui
AppFocus::Content(SequencerFocus::PhraseEditor) [AppFocus::Content(SequencerFocus::PhraseEditor)]
]); [self: Some(self.phrases.phrases[self.phrases.phrase.load(Ordering::Relaxed)].clone())]
impl_phrase_editor_control!(ArrangerTui [ [self, phrase: self.editor.show(phrase)]
AppFocus::Content(ArrangerFocus::PhraseEditor) );
]); impl_phrase_editor_control!(ArrangerTui
[AppFocus::Content(ArrangerFocus::PhraseEditor)]
[self: todo!()]
[self, phrase: todo!()]
);

View file

@ -59,17 +59,17 @@ where
_ => return None, _ => return None,
}, },
TransportFocus::Quant => match input.event() { TransportFocus::Quant => match input.event() {
key!(Char(',')) => Clock(SetQuant(state.prev_quant())), key!(Char(',')) => Clock(SetQuant(state.quant().prev())),
key!(Char('.')) => Clock(SetQuant(state.next_quant())), key!(Char('.')) => Clock(SetQuant(state.quant().next())),
key!(Char('<')) => Clock(SetQuant(state.prev_quant())), key!(Char('<')) => Clock(SetQuant(state.quant().prev())),
key!(Char('>')) => Clock(SetQuant(state.next_quant())), key!(Char('>')) => Clock(SetQuant(state.quant().next())),
_ => return None, _ => return None,
}, },
TransportFocus::Sync => match input.event() { TransportFocus::Sync => match input.event() {
key!(Char(',')) => Clock(SetSync(state.prev_sync())), key!(Char(',')) => Clock(SetSync(state.sync().prev())),
key!(Char('.')) => Clock(SetSync(state.next_sync())), key!(Char('.')) => Clock(SetSync(state.sync().next())),
key!(Char('<')) => Clock(SetSync(state.prev_sync())), key!(Char('<')) => Clock(SetSync(state.sync().prev())),
key!(Char('>')) => Clock(SetSync(state.next_sync())), key!(Char('>')) => Clock(SetSync(state.sync().next())),
_ => return None, _ => return None,
}, },
TransportFocus::Clock => match input.event() { TransportFocus::Clock => match input.event() {
@ -90,70 +90,89 @@ where
fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<SequencerCommand> { fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<SequencerCommand> {
use SequencerCommand::*; use SequencerCommand::*;
use KeyCode::Char;
if !state.entered() { if !state.entered() {
return None return None
} }
Some(match state.focused() { Some(match input.event() {
AppFocus::Menu => { todo!() }, key!(Char('e')) => Editor(
AppFocus::Content(focused) => match focused { PhraseCommand::Show(state.phrase_to_edit().clone())
SequencerFocus::Phrases => Phrases(PhrasesCommand::input_to_command(state, input)?), ),
SequencerFocus::PhraseEditor => Editor(PhraseCommand::input_to_command(state, input)?), key!(Char(' ')) => Clock(
SequencerFocus::Transport(_) => { if let Some(TransportState::Stopped) = *state.clock.playing.read().unwrap() {
match TransportCommand::input_to_command(state, input)? { ClockCommand::Play(None)
TransportCommand::Clock(_) => { todo!() }, } else {
TransportCommand::Focus(command) => Focus(command), ClockCommand::Pause(None)
} }
}, ),
key!(Shift-Char(' ')) => Clock(
if let Some(TransportState::Stopped) = *state.clock.playing.read().unwrap() {
ClockCommand::Play(Some(0))
} else {
ClockCommand::Pause(Some(0))
}
),
_ => match state.focused() {
AppFocus::Menu => { todo!() },
AppFocus::Content(focused) => match focused {
SequencerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(_) => { todo!() },
TransportCommand::Focus(command) => Focus(command),
}
},
SequencerFocus::Phrases => Phrases(
PhrasesCommand::input_to_command(state, input)?
),
SequencerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(state, input)?
),
}
} }
}) })
} }
fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<ArrangerCommand> { fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<ArrangerCommand> {
use ArrangerCommand as Cmd; use ArrangerCommand as Cmd;
use KeyCode::Char;
if !state.entered() { if !state.entered() {
return None return None
} }
Some(match state.focused() { Some(match input.event() {
AppFocus::Menu => { key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(state.phrase_to_edit().clone())),
todo!() _ => match state.focused() {
}, AppFocus::Menu => { todo!() },
AppFocus::Content(focused) => match focused { AppFocus::Content(focused) => match focused {
ArrangerFocus::Transport(_) => { ArrangerFocus::Transport(_) => {
use TransportCommand::{Clock, Focus}; use TransportCommand::{Clock, Focus};
match TransportCommand::input_to_command(state, input)? { match TransportCommand::input_to_command(state, input)? {
Clock(_) => { todo!() }, Clock(_) => { todo!() },
Focus(command) => Cmd::Focus(command) Focus(command) => Cmd::Focus(command)
} }
},
ArrangerFocus::PhraseEditor => {
Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
},
ArrangerFocus::Phrases => match input.event() {
key!(KeyCode::Char('e')) => {
Cmd::EditPhrase(state.phrase_editing().clone())
}, },
_ => { ArrangerFocus::PhraseEditor => {
Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
},
ArrangerFocus::Phrases => {
Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?) Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?)
} },
}, ArrangerFocus::Arranger => {
ArrangerFocus::Arranger => { use ArrangerSelection::*;
use ArrangerSelection::*; match input.event() {
use KeyCode::Char; key!(Char('l')) => Cmd::Clip(ArrangerClipCommand::SetLoop(false)),
match input.event() { key!(Char('+')) => Cmd::Zoom(0), // TODO
key!(Char('e')) => Cmd::EditPhrase(state.phrase_editing().clone()), key!(Char('=')) => Cmd::Zoom(0), // TODO
key!(Char('l')) => Cmd::Clip(ArrangerClipCommand::SetLoop(false)), key!(Char('_')) => Cmd::Zoom(0), // TODO
key!(Char('+')) => Cmd::Zoom(0), // TODO key!(Char('-')) => Cmd::Zoom(0), // TODO
key!(Char('=')) => Cmd::Zoom(0), // TODO key!(Char('`')) => { todo!("toggle state mode") },
key!(Char('_')) => Cmd::Zoom(0), // TODO key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add),
key!(Char('-')) => Cmd::Zoom(0), // TODO key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add),
key!(Char('`')) => { todo!("toggle state mode") }, _ => match state.selected() {
key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add), Mix => to_arranger_mix_command(input)?,
key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add), Track(t) => to_arranger_track_command(input, t)?,
_ => match state.selected() { Scene(s) => to_arranger_scene_command(input, s)?,
Mix => to_arranger_mix_command(input)?, Clip(t, s) => to_arranger_clip_command(input, t, s)?,
Track(t) => to_arranger_track_command(input, t)?, }
Scene(s) => to_arranger_scene_command(input, s)?,
Clip(t, s) => to_arranger_clip_command(input, t, s)?,
} }
} }
} }

View file

@ -49,9 +49,9 @@ pub struct PhrasePlayerModel {
/// Send all notes off /// Send all notes off
pub(crate) reset: bool, // TODO?: after Some(nframes) pub(crate) reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence. /// Record from MIDI ports to current sequence.
pub midi_ins: Vec<Port<MidiIn>>, pub midi_ins: Vec<Port<MidiIn>>,
/// Play from current sequence to MIDI ports /// Play from current sequence to MIDI ports
pub midi_outs: Vec<Port<MidiOut>>, pub midi_outs: Vec<Port<MidiOut>>,
/// Notes currently held at input /// Notes currently held at input
pub(crate) notes_in: Arc<RwLock<[bool; 128]>>, pub(crate) notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output /// Notes currently held at output

View file

@ -616,7 +616,7 @@ impl PhraseEditorModel {
} }
} }
/// Select which pattern to display. This pre-renders it to the buffer at full resolution. /// Select which pattern to display. This pre-renders it to the buffer at full resolution.
pub fn show (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) { pub fn show (&mut self, phrase: Option<Arc<RwLock<Phrase>>>) {
if let Some(phrase) = phrase { if let Some(phrase) = phrase {
self.phrase = Some(phrase.clone()); self.phrase = Some(phrase.clone());
self.time_axis.write().unwrap().clamp = Some(phrase.read().unwrap().length); self.time_axis.write().unwrap().clamp = Some(phrase.read().unwrap().length);