mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
transport compact mode
This commit is contained in:
parent
6776e2ec55
commit
92459b5f82
6 changed files with 134 additions and 83 deletions
|
|
@ -105,14 +105,13 @@ impl ArrangerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: ArrangerTui) => {
|
render!(Tui: (self: ArrangerTui) => {
|
||||||
let play = PlayPause(self.clock.is_rolling());
|
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
|
||||||
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
|
let with_pool = |x|Bsp::w(Fixed::x(pool_size, PoolView(&self.phrases)), x);
|
||||||
let with_pool = |x|Bsp::w(Fixed::x(pool_size, PoolView(&self.phrases)), x);
|
let status = ArrangerStatus::from(self);
|
||||||
let status = ArrangerStatus::from(self);
|
let with_editbar = |x|Bsp::n(Fixed::y(1, MidiEditStatus(&self.editor)), x);
|
||||||
let with_editbar = |x|Bsp::n(Fixed::y(1, MidiEditStatus(&self.editor)), x);
|
let with_status = |x|Bsp::n(Fixed::y(2, status), x);
|
||||||
let with_status = |x|Bsp::n(Fixed::y(2, status), x);
|
let with_size = |x|lay!(&self.size, x);
|
||||||
let with_size = |x|lay!(&self.size, x);
|
let arranger = ||{
|
||||||
let arranger = ||{
|
|
||||||
let color = self.color;
|
let color = self.color;
|
||||||
lay!(
|
lay!(
|
||||||
Fill::xy(Tui::bg(color.darkest.rgb, "")),
|
Fill::xy(Tui::bg(color.darkest.rgb, "")),
|
||||||
|
|
@ -121,7 +120,7 @@ render!(Tui: (self: ArrangerTui) => {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
with_size(with_status(with_editbar(with_pool(col!(
|
with_size(with_status(with_editbar(with_pool(col!(
|
||||||
TransportView(&self.clock),
|
TransportView::new(true, &self.clock),
|
||||||
Fill::x(Fixed::y(20, arranger())),
|
Fill::x(Fixed::y(20, arranger())),
|
||||||
Fill::xy(&self.editor),
|
Fill::xy(&self.editor),
|
||||||
)))))
|
)))))
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ pub struct TransportTui {
|
||||||
has_clock!(|self: TransportTui|&self.clock);
|
has_clock!(|self: TransportTui|&self.clock);
|
||||||
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
|
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
|
||||||
handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
|
handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
|
||||||
render!(Tui: (self: TransportTui) => TransportView(&self.clock));
|
render!(Tui: (self: TransportTui) => TransportView {
|
||||||
|
compact: false,
|
||||||
|
clock: &self.clock
|
||||||
|
});
|
||||||
impl TransportTui {
|
impl TransportTui {
|
||||||
pub fn new (jack: &Arc<RwLock<JackConnection>>) -> Usually<Self> {
|
pub fn new (jack: &Arc<RwLock<JackConnection>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -21,25 +24,43 @@ impl TransportTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TransportView<'a>(pub &'a Clock);
|
pub struct TransportView<'a> { pub compact: bool, pub clock: &'a Clock }
|
||||||
|
impl<'a> TransportView<'a> {
|
||||||
|
pub fn new (compact: bool, clock: &'a Clock) -> Self {
|
||||||
|
Self { compact, clock }
|
||||||
|
}
|
||||||
|
}
|
||||||
render!(Tui: (self: TransportView<'a>) => Outer(
|
render!(Tui: (self: TransportView<'a>) => Outer(
|
||||||
Style::default().fg(TuiTheme::g(255)).bg(TuiTheme::g(0))
|
Style::default().fg(TuiTheme::g(255)).bg(TuiTheme::g(0))
|
||||||
).enclose(row!(
|
).enclose(row!(
|
||||||
BeatStats::new(self.0), " ",
|
BeatStats::new(self.compact, self.clock), " ",
|
||||||
PlayPause(self.0.is_rolling()), " ",
|
PlayPause { compact: self.compact, playing: self.clock.is_rolling() }, " ",
|
||||||
OutputStats::new(self.0),
|
OutputStats::new(self.compact, self.clock),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
pub struct PlayPause(pub bool);
|
struct Field<'a>(ItemPalette, &'a str, &'a str);
|
||||||
render!(Tui: (self: PlayPause) => Tui::bg(
|
render!(Tui: (self: Field<'a>) => row!(
|
||||||
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "▐")),
|
||||||
Fixed::x(5, Tui::either(self.0,
|
Tui::bg(self.0.darker.rgb, Tui::fg(self.0.lighter.rgb,
|
||||||
Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
|
Tui::bold(true, format!("{}", self.1)))),
|
||||||
Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",))))));
|
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "▌")),
|
||||||
|
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.lightest.rgb,
|
||||||
|
Tui::bold(true, format!("{} ", self.2))))));
|
||||||
|
|
||||||
pub struct BeatStats { bpm: String, beat: String, time: String, }
|
pub struct PlayPause { pub compact: bool, pub playing: bool }
|
||||||
|
render!(Tui: (self: PlayPause) => Tui::bg(
|
||||||
|
if self.playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||||
|
Tui::either(self.compact,
|
||||||
|
Thunk::new(||Fixed::x(9, Tui::either(self.playing,
|
||||||
|
Tui::fg(Color::Rgb(0, 255, 0), " PLAYING "),
|
||||||
|
Tui::fg(Color::Rgb(255, 128, 0), " STOPPED ")))),
|
||||||
|
Thunk::new(||Fixed::x(5, Tui::either(self.playing,
|
||||||
|
Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
|
||||||
|
Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",))))))));
|
||||||
|
|
||||||
|
pub struct BeatStats { compact: bool, bpm: String, beat: String, time: String, }
|
||||||
impl BeatStats {
|
impl BeatStats {
|
||||||
fn new (clock: &Clock) -> Self {
|
fn new (compact: bool, clock: &Clock) -> Self {
|
||||||
let (beat, time) = clock.started.read().unwrap().as_ref().map(|started|{
|
let (beat, time) = clock.started.read().unwrap().as_ref().map(|started|{
|
||||||
let current_usec = clock.global.usec.get() - started.usec.get();
|
let current_usec = clock.global.usec.get() - started.usec.get();
|
||||||
(
|
(
|
||||||
|
|
@ -47,32 +68,49 @@ impl BeatStats {
|
||||||
format!("{:.3}s", current_usec/1000000.)
|
format!("{:.3}s", current_usec/1000000.)
|
||||||
)
|
)
|
||||||
}).unwrap_or_else(||("-.-.--".to_string(), "-.---s".to_string()));
|
}).unwrap_or_else(||("-.-.--".to_string(), "-.---s".to_string()));
|
||||||
Self { bpm: format!("{:.3}", clock.timebase.bpm.get()), beat, time }
|
Self { compact, bpm: format!("{:.3}", clock.timebase.bpm.get()), beat, time }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: BeatStats) => col!(
|
render!(Tui: (self: BeatStats) => Tui::either(self.compact,
|
||||||
Bsp::e(Tui::fg(TuiTheme::g(255), &self.bpm), " BPM"),
|
row!(
|
||||||
Bsp::e("Beat ", Tui::fg(TuiTheme::g(255), &self.beat)),
|
Field(TuiTheme::g(128).into(), "BPM", &self.bpm),
|
||||||
Bsp::e("Time ", Tui::fg(TuiTheme::g(255), &self.time)),
|
Field(TuiTheme::g(128).into(), "Beat", &self.beat),
|
||||||
));
|
Field(TuiTheme::g(128).into(), "Time", &self.time),
|
||||||
|
),
|
||||||
|
col!(
|
||||||
|
Bsp::e(Tui::fg(TuiTheme::g(255), &self.bpm), " BPM"),
|
||||||
|
Bsp::e("Beat ", Tui::fg(TuiTheme::g(255), &self.beat)),
|
||||||
|
Bsp::e("Time ", Tui::fg(TuiTheme::g(255), &self.time)),
|
||||||
|
)));
|
||||||
|
|
||||||
pub struct OutputStats { sample_rate: String, buffer_size: String, latency: String, }
|
pub struct OutputStats { compact: bool, sample_rate: String, buffer_size: String, latency: String, }
|
||||||
impl OutputStats {
|
impl OutputStats {
|
||||||
fn new (clock: &Clock) -> Self {
|
fn new (compact: bool, clock: &Clock) -> Self {
|
||||||
let rate = clock.timebase.sr.get();
|
let rate = clock.timebase.sr.get();
|
||||||
let chunk = clock.chunk.load(Relaxed);
|
let chunk = clock.chunk.load(Relaxed);
|
||||||
Self {
|
Self {
|
||||||
sample_rate: format!("{:.1}Hz", rate),
|
compact,
|
||||||
|
sample_rate: if compact {
|
||||||
|
format!("{:.1}kHz", rate / 1000.)
|
||||||
|
} else {
|
||||||
|
format!("{:.0}Hz", rate)
|
||||||
|
},
|
||||||
buffer_size: format!("{chunk}"),
|
buffer_size: format!("{chunk}"),
|
||||||
latency: format!("{}", chunk as f64 / rate * 1000.),
|
latency: format!("{}", chunk as f64 / rate * 1000.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: OutputStats) => col!(
|
render!(Tui: (self: OutputStats) => Tui::either(self.compact,
|
||||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.sample_rate)), " sample rate"),
|
row!(
|
||||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.buffer_size)), " sample buffer"),
|
Field(TuiTheme::g(128).into(), "SR", &self.sample_rate),
|
||||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"),
|
Field(TuiTheme::g(128).into(), "Buf", &self.buffer_size),
|
||||||
));
|
Field(TuiTheme::g(128).into(), "Lat", &self.latency),
|
||||||
|
),
|
||||||
|
col!(
|
||||||
|
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.sample_rate)), " sample rate"),
|
||||||
|
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.buffer_size)), " sample buffer"),
|
||||||
|
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"),
|
||||||
|
)));
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum TransportCommand {
|
pub enum TransportCommand {
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,16 @@ render!(Tui: (self: Groovebox) => {
|
||||||
let color = self.player.play_phrase().as_ref()
|
let color = self.player.play_phrase().as_ref()
|
||||||
.and_then(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color))
|
.and_then(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color))
|
||||||
.clone();
|
.clone();
|
||||||
|
let sampler = Align::w(Fill::y(SampleList::new(&self.sampler, &self.editor)));
|
||||||
|
let selector = Bsp::e(PhraseSelector::play_phrase(&self.player), PhraseSelector::next_phrase(&self.player));
|
||||||
self.size.of(Bsp::s(
|
self.size.of(Bsp::s(
|
||||||
Fill::x(Fixed::y(3, lay!(
|
Fill::x(Fixed::y(if self.pool.visible { 3 } else { 1 }, lay!(
|
||||||
Align::w(Meter("L/", self.sampler.input_meter[0])),
|
Align::w(Meter("L/", self.sampler.input_meter[0])),
|
||||||
Align::x(TransportView(&self.player.clock)),
|
|
||||||
Align::e(Meter("R/", self.sampler.input_meter[1])),
|
Align::e(Meter("R/", self.sampler.input_meter[1])),
|
||||||
|
Align::x(Tui::bg(TuiTheme::g(32), TransportView::new(
|
||||||
|
!self.pool.visible,
|
||||||
|
&self.player.clock
|
||||||
|
))),
|
||||||
))),
|
))),
|
||||||
Bsp::n(
|
Bsp::n(
|
||||||
Bsp::s(
|
Bsp::s(
|
||||||
|
|
@ -135,12 +140,9 @@ render!(Tui: (self: Groovebox) => {
|
||||||
Bsp::w(
|
Bsp::w(
|
||||||
Fixed::x(pool_w, Align::e(Fill::y(PoolView(&self.pool)))),
|
Fixed::x(pool_w, Align::e(Fill::y(PoolView(&self.pool)))),
|
||||||
Fill::xy(Bsp::e(
|
Fill::xy(Bsp::e(
|
||||||
Fixed::x(sampler_w, Align::w(Fill::y(GrooveboxSamples(self)))),
|
Fixed::x(sampler_w, sampler),
|
||||||
Bsp::s(
|
Bsp::s(
|
||||||
Fill::x(Align::c(Bsp::e(
|
selector,
|
||||||
PhraseSelector::play_phrase(&self.player),
|
|
||||||
PhraseSelector::next_phrase(&self.player),
|
|
||||||
))),
|
|
||||||
Bsp::n(
|
Bsp::n(
|
||||||
MidiEditStatus(&self.editor),
|
MidiEditStatus(&self.editor),
|
||||||
&self.editor,
|
&self.editor,
|
||||||
|
|
@ -152,32 +154,6 @@ render!(Tui: (self: Groovebox) => {
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO move this to sampler
|
|
||||||
struct GrooveboxSamples<'a>(&'a Groovebox);
|
|
||||||
render!(Tui: (self: GrooveboxSamples<'a>) => {
|
|
||||||
let note_lo = self.0.editor.note_lo().load(Relaxed);
|
|
||||||
let note_pt = self.0.editor.note_point();
|
|
||||||
let note_hi = self.0.editor.note_hi();
|
|
||||||
Fill::xy(Tui::map(move||(note_lo..=note_hi).rev(), move|note, i| {
|
|
||||||
let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset };
|
|
||||||
let mut fg = TuiTheme::g(160);
|
|
||||||
if let Some((index, _)) = self.0.sampler.recording {
|
|
||||||
if note == index {
|
|
||||||
bg = Color::Rgb(64,16,0);
|
|
||||||
fg = Color::Rgb(224,64,32)
|
|
||||||
}
|
|
||||||
} else if self.0.sampler.mapped[note].is_some() {
|
|
||||||
fg = TuiTheme::g(224);
|
|
||||||
}
|
|
||||||
let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a))));
|
|
||||||
offset(Tui::bg(bg, if let Some(sample) = &self.0.sampler.mapped[note] {
|
|
||||||
Tui::fg(fg, format!("{note:3} ?????? "))
|
|
||||||
} else {
|
|
||||||
Tui::fg(fg, format!("{note:3} (none) "))
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
pub enum GrooveboxCommand {
|
pub enum GrooveboxCommand {
|
||||||
History(isize),
|
History(isize),
|
||||||
Clock(ClockCommand),
|
Clock(ClockCommand),
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ pub use self::sampler_tui::SamplerTui;
|
||||||
pub mod sample_import;
|
pub mod sample_import;
|
||||||
pub(crate) use self::sample_import::*;
|
pub(crate) use self::sample_import::*;
|
||||||
|
|
||||||
|
pub mod sample_list;
|
||||||
|
pub(crate) use self::sample_list::*;
|
||||||
|
pub use self::sample_list::SampleList;
|
||||||
|
|
||||||
pub mod sample_viewer;
|
pub mod sample_viewer;
|
||||||
pub(crate) use self::sample_viewer::*;
|
pub(crate) use self::sample_viewer::*;
|
||||||
pub use self::sample_viewer::SampleViewer;
|
pub use self::sample_viewer::SampleViewer;
|
||||||
|
|
|
||||||
34
src/sampler/sample_list.rs
Normal file
34
src/sampler/sample_list.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub struct SampleList<'a>(&'a Sampler, &'a MidiEditor);
|
||||||
|
|
||||||
|
impl<'a> SampleList<'a> {
|
||||||
|
pub fn new (sampler: &'a Sampler, editor: &'a MidiEditor) -> Self {
|
||||||
|
Self(sampler, editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render!(Tui: (self: SampleList<'a>) => {
|
||||||
|
let Self(sampler, editor) = self;
|
||||||
|
let note_lo = editor.note_lo().load(Relaxed);
|
||||||
|
let note_pt = editor.note_point();
|
||||||
|
let note_hi = editor.note_hi();
|
||||||
|
Fill::xy(Tui::map(move||(note_lo..=note_hi).rev(), move|note, i| {
|
||||||
|
let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset };
|
||||||
|
let mut fg = TuiTheme::g(160);
|
||||||
|
if let Some((index, _)) = sampler.recording {
|
||||||
|
if note == index {
|
||||||
|
bg = Color::Rgb(64,16,0);
|
||||||
|
fg = Color::Rgb(224,64,32)
|
||||||
|
}
|
||||||
|
} else if sampler.mapped[note].is_some() {
|
||||||
|
fg = TuiTheme::g(224);
|
||||||
|
}
|
||||||
|
let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a))));
|
||||||
|
offset(Tui::bg(bg, if let Some(sample) = &sampler.mapped[note] {
|
||||||
|
Tui::fg(fg, format!("{note:3} ?????? "))
|
||||||
|
} else {
|
||||||
|
Tui::fg(fg, format!("{note:3} (none) "))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct SequencerTui {
|
||||||
pub transport: bool,
|
pub transport: bool,
|
||||||
pub selectors: bool,
|
pub selectors: bool,
|
||||||
pub clock: Clock,
|
pub clock: Clock,
|
||||||
pub phrases: PoolModel,
|
pub pool: PoolModel,
|
||||||
pub player: MidiPlayer,
|
pub player: MidiPlayer,
|
||||||
pub editor: MidiEditor,
|
pub editor: MidiEditor,
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<Tui>,
|
||||||
|
|
@ -29,7 +29,7 @@ from_jack!(|jack|SequencerTui {
|
||||||
_jack: jack.clone(),
|
_jack: jack.clone(),
|
||||||
transport: true,
|
transport: true,
|
||||||
selectors: true,
|
selectors: true,
|
||||||
phrases: PoolModel::from(&phrase),
|
pool: PoolModel::from(&phrase),
|
||||||
editor: MidiEditor::from(&phrase),
|
editor: MidiEditor::from(&phrase),
|
||||||
player: MidiPlayer::from((&clock, &phrase)),
|
player: MidiPlayer::from((&clock, &phrase)),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
|
|
@ -43,8 +43,8 @@ from_jack!(|jack|SequencerTui {
|
||||||
render!(Tui: (self: SequencerTui) => {
|
render!(Tui: (self: SequencerTui) => {
|
||||||
let w = self.size.w();
|
let w = self.size.w();
|
||||||
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||||
let pool_w = if self.phrases.visible { phrase_w } else { 0 };
|
let pool_w = if self.pool.visible { phrase_w } else { 0 };
|
||||||
let pool = Pull::y(1, Fill::y(Align::e(PoolView(&self.phrases))));
|
let pool = Pull::y(1, Fill::y(Align::e(PoolView(&self.pool))));
|
||||||
let with_pool = move|x|Bsp::w(Fixed::x(pool_w, pool), x);
|
let with_pool = move|x|Bsp::w(Fixed::x(pool_w, pool), x);
|
||||||
let status = SequencerStatus::from(self);
|
let status = SequencerStatus::from(self);
|
||||||
let with_status = |x|Bsp::n(Fixed::x(if self.status { 2 } else { 0 }, status), x);
|
let with_status = |x|Bsp::n(Fixed::x(if self.status { 2 } else { 0 }, status), x);
|
||||||
|
|
@ -56,7 +56,7 @@ render!(Tui: (self: SequencerTui) => {
|
||||||
p.as_ref().map(|p|p.read().unwrap().color)
|
p.as_ref().map(|p|p.read().unwrap().color)
|
||||||
).flatten().clone();
|
).flatten().clone();
|
||||||
|
|
||||||
let toolbar = Tui::when(self.transport, TransportView(&self.clock));
|
let toolbar = Tui::when(self.transport, TransportView::new(true, &self.clock));
|
||||||
|
|
||||||
let play_queue = Tui::when(self.selectors, row!(
|
let play_queue = Tui::when(self.selectors, row!(
|
||||||
PhraseSelector::play_phrase(&self.player),
|
PhraseSelector::play_phrase(&self.player),
|
||||||
|
|
@ -88,7 +88,7 @@ audio!(|self:SequencerTui, client, scope|{
|
||||||
});
|
});
|
||||||
has_size!(<Tui>|self:SequencerTui|&self.size);
|
has_size!(<Tui>|self:SequencerTui|&self.size);
|
||||||
has_clock!(|self:SequencerTui|&self.clock);
|
has_clock!(|self:SequencerTui|&self.clock);
|
||||||
has_phrases!(|self:SequencerTui|self.phrases.phrases);
|
has_phrases!(|self:SequencerTui|self.pool.phrases);
|
||||||
has_editor!(|self:SequencerTui|self.editor);
|
has_editor!(|self:SequencerTui|self.editor);
|
||||||
handle!(<Tui>|self:SequencerTui,input|SequencerCommand::execute_with_state(self, input));
|
handle!(<Tui>|self:SequencerTui,input|SequencerCommand::execute_with_state(self, input));
|
||||||
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
||||||
|
|
@ -114,15 +114,15 @@ input_to_command!(SequencerCommand: <Tui>|state: SequencerTui, input|match input
|
||||||
// Shift-U: redo
|
// Shift-U: redo
|
||||||
key_pat!(Char('U')) => Cmd::History( 1),
|
key_pat!(Char('U')) => Cmd::History( 1),
|
||||||
// Tab: Toggle visibility of phrase pool column
|
// Tab: Toggle visibility of phrase pool column
|
||||||
key_pat!(Tab) => Cmd::Pool(PoolCommand::Show(!state.phrases.visible)),
|
key_pat!(Tab) => Cmd::Pool(PoolCommand::Show(!state.pool.visible)),
|
||||||
// q: Enqueue currently edited phrase
|
// q: Enqueue currently edited phrase
|
||||||
key_pat!(Char('q')) => Cmd::Enqueue(Some(state.phrases.phrase().clone())),
|
key_pat!(Char('q')) => Cmd::Enqueue(Some(state.pool.phrase().clone())),
|
||||||
// 0: Enqueue phrase 0 (stop all)
|
// 0: Enqueue phrase 0 (stop all)
|
||||||
key_pat!(Char('0')) => Cmd::Enqueue(Some(state.phrases.phrases()[0].clone())),
|
key_pat!(Char('0')) => Cmd::Enqueue(Some(state.phrases()[0].clone())),
|
||||||
// e: Toggle between editing currently playing or other phrase
|
// e: Toggle between editing currently playing or other phrase
|
||||||
key_pat!(Char('e')) => if let Some((_, Some(playing))) = state.player.play_phrase() {
|
key_pat!(Char('e')) => if let Some((_, Some(playing))) = state.player.play_phrase() {
|
||||||
let editing = state.editor.phrase().as_ref().map(|p|p.read().unwrap().clone());
|
let editing = state.editor.phrase().as_ref().map(|p|p.read().unwrap().clone());
|
||||||
let selected = state.phrases.phrase().clone();
|
let selected = state.pool.phrase().clone();
|
||||||
Cmd::Editor(Show(Some(if Some(selected.read().unwrap().clone()) != editing {
|
Cmd::Editor(Show(Some(if Some(selected.read().unwrap().clone()) != editing {
|
||||||
selected
|
selected
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -135,7 +135,7 @@ input_to_command!(SequencerCommand: <Tui>|state: SequencerTui, input|match input
|
||||||
// The ones defined above supersede them.
|
// The ones defined above supersede them.
|
||||||
_ => if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) {
|
_ => if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) {
|
||||||
Cmd::Editor(command)
|
Cmd::Editor(command)
|
||||||
} else if let Some(command) = PoolCommand::input_to_command(&state.phrases, input) {
|
} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) {
|
||||||
Cmd::Pool(command)
|
Cmd::Pool(command)
|
||||||
} else {
|
} else {
|
||||||
return None
|
return None
|
||||||
|
|
@ -144,19 +144,19 @@ input_to_command!(SequencerCommand: <Tui>|state: SequencerTui, input|match input
|
||||||
command!(|self: SequencerCommand, state: SequencerTui|match self {
|
command!(|self: SequencerCommand, state: SequencerTui|match self {
|
||||||
Self::Pool(cmd) => {
|
Self::Pool(cmd) => {
|
||||||
let mut default = |cmd: PoolCommand|cmd
|
let mut default = |cmd: PoolCommand|cmd
|
||||||
.execute(&mut state.phrases)
|
.execute(&mut state.pool)
|
||||||
.map(|x|x.map(Cmd::Pool));
|
.map(|x|x.map(Cmd::Pool));
|
||||||
match cmd {
|
match cmd {
|
||||||
// autoselect: automatically load selected phrase in editor
|
// autoselect: automatically load selected phrase in editor
|
||||||
PoolCommand::Select(_) => {
|
PoolCommand::Select(_) => {
|
||||||
let undo = default(cmd)?;
|
let undo = default(cmd)?;
|
||||||
state.editor.set_phrase(Some(state.phrases.phrase()));
|
state.editor.set_phrase(Some(state.pool.phrase()));
|
||||||
undo
|
undo
|
||||||
},
|
},
|
||||||
// update color in all places simultaneously
|
// update color in all places simultaneously
|
||||||
PoolCommand::Phrase(SetColor(index, _)) => {
|
PoolCommand::Phrase(SetColor(index, _)) => {
|
||||||
let undo = default(cmd)?;
|
let undo = default(cmd)?;
|
||||||
state.editor.set_phrase(Some(state.phrases.phrase()));
|
state.editor.set_phrase(Some(state.pool.phrase()));
|
||||||
undo
|
undo
|
||||||
},
|
},
|
||||||
_ => default(cmd)?
|
_ => default(cmd)?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue