mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: zoom lock
This commit is contained in:
parent
94491a323a
commit
44c28183de
11 changed files with 107 additions and 89 deletions
|
|
@ -33,8 +33,10 @@ impl<'a> TransportView<'a> {
|
||||||
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.compact, self.clock), " ",
|
BeatStats::new(self.compact, self.clock),
|
||||||
PlayPause { compact: self.compact, playing: self.clock.is_rolling() }, " ",
|
" ",
|
||||||
|
PlayPause { compact: self.compact, playing: self.clock.is_rolling() },
|
||||||
|
" ",
|
||||||
OutputStats::new(self.compact, self.clock),
|
OutputStats::new(self.compact, self.clock),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
@ -87,7 +89,7 @@ impl OutputStats {
|
||||||
format!("{:.0}Hz", rate)
|
format!("{:.0}Hz", rate)
|
||||||
},
|
},
|
||||||
buffer_size: format!("{chunk}"),
|
buffer_size: format!("{chunk}"),
|
||||||
latency: format!("{:.3}ms", chunk as f64 / rate * 1000.),
|
latency: format!("{:.1}ms", chunk as f64 / rate * 1000.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,8 @@ render!(Tui: (self: Groovebox) => {
|
||||||
.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 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));
|
let selectors = Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player));
|
||||||
|
let edit_clip = MidiEditClip(&self.editor);
|
||||||
self.size.of(Bsp::s(
|
self.size.of(Bsp::s(
|
||||||
Fill::x(Fixed::y(if self.pool.visible { 3 } else { 1 }, 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])),
|
||||||
|
|
@ -139,7 +140,10 @@ render!(Tui: (self: Groovebox) => {
|
||||||
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, Push::y(3, sampler)),
|
Fixed::x(sampler_w, Push::y(3, sampler)),
|
||||||
Bsp::s(selector, &self.editor),
|
Bsp::s(
|
||||||
|
lay!(Align::w(edit_clip), Align::e(selectors)),
|
||||||
|
&self.editor
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -232,13 +236,7 @@ command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
||||||
_ => cmd.delegate(&mut state.pool, Self::Pool)?
|
_ => cmd.delegate(&mut state.pool, Self::Pool)?
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::Editor(cmd) => {
|
Self::Sampler(cmd) => cmd.delegate(&mut state.sampler, Self::Sampler)?,
|
||||||
cmd.delegate(&mut state.editor, Self::Editor)?
|
Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?,
|
||||||
},
|
Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?,
|
||||||
Self::Clock(cmd) => {
|
|
||||||
cmd.delegate(state, Self::Clock)?
|
|
||||||
},
|
|
||||||
Self::Sampler(cmd) => {
|
|
||||||
cmd.delegate(&mut state.sampler, Self::Sampler)?
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,6 @@ render!(Tui: (self: MidiEditor) => {
|
||||||
Fill::xy(Bsp::b(&self.size, &self.mode))
|
Fill::xy(Bsp::b(&self.size, &self.mode))
|
||||||
});
|
});
|
||||||
|
|
||||||
impl MidiView<Tui> for MidiEditor {}
|
|
||||||
|
|
||||||
impl TimeRange for MidiEditor {
|
impl TimeRange for MidiEditor {
|
||||||
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
||||||
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
||||||
|
|
@ -79,7 +77,7 @@ impl MidiViewMode for MidiEditor {
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
||||||
self.mode.buffer_size(phrase)
|
self.mode.buffer_size(phrase)
|
||||||
}
|
}
|
||||||
fn redraw (&mut self) {
|
fn redraw (&self) {
|
||||||
self.mode.redraw()
|
self.mode.redraw()
|
||||||
}
|
}
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||||
|
|
@ -126,23 +124,6 @@ impl MidiEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MidiViewMode: HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
|
||||||
fn redraw (&mut self);
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
|
||||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
|
||||||
*self.phrase_mut() = phrase.cloned();
|
|
||||||
self.redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content<Tui> for Box<dyn MidiViewMode> {
|
|
||||||
fn content (&self) -> impl Content<Tui> {
|
|
||||||
Some(&(*self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for MidiEditor {
|
impl std::fmt::Debug for MidiEditor {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("MidiEditor")
|
f.debug_struct("MidiEditor")
|
||||||
|
|
@ -191,10 +172,10 @@ impl MidiEditor {
|
||||||
(kexp!(Right), &|s: &Self|SetTimeCursor((s.time_point() + s.note_len()) % s.phrase_length())),
|
(kexp!(Right), &|s: &Self|SetTimeCursor((s.time_point() + s.note_len()) % s.phrase_length())),
|
||||||
(kexp!(Char('d')), &|s: &Self|SetTimeCursor((s.time_point() + s.note_len()) % s.phrase_length())),
|
(kexp!(Char('d')), &|s: &Self|SetTimeCursor((s.time_point() + s.note_len()) % s.phrase_length())),
|
||||||
(kexp!(Char('z')), &|s: &Self|SetTimeLock(!s.time_lock().get())),
|
(kexp!(Char('z')), &|s: &Self|SetTimeLock(!s.time_lock().get())),
|
||||||
(kexp!(Char('-')), &|s: &Self|SetTimeZoom(Note::next(s.time_zoom().get()))),
|
(kexp!(Char('-')), &|s: &Self|SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { Note::next(s.time_zoom().get()) })),
|
||||||
(kexp!(Char('_')), &|s: &Self|SetTimeZoom(Note::next(s.time_zoom().get()))),
|
(kexp!(Char('_')), &|s: &Self|SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { Note::next(s.time_zoom().get()) })),
|
||||||
(kexp!(Char('=')), &|s: &Self|SetTimeZoom(Note::prev(s.time_zoom().get()))),
|
(kexp!(Char('=')), &|s: &Self|SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { Note::prev(s.time_zoom().get()) })),
|
||||||
(kexp!(Char('+')), &|s: &Self|SetTimeZoom(Note::prev(s.time_zoom().get()))),
|
(kexp!(Char('+')), &|s: &Self|SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { Note::prev(s.time_zoom().get()) })),
|
||||||
(kexp!(Enter), &|s: &Self|PutNote),
|
(kexp!(Enter), &|s: &Self|PutNote),
|
||||||
(kexp!(Ctrl-Enter), &|s: &Self|AppendNote),
|
(kexp!(Ctrl-Enter), &|s: &Self|AppendNote),
|
||||||
(kexp!(Char(',')), &|s: &Self|SetNoteLength(Note::prev(s.note_len()))), // TODO: no 3plet
|
(kexp!(Char(',')), &|s: &Self|SetNoteLength(Note::prev(s.note_len()))), // TODO: no 3plet
|
||||||
|
|
|
||||||
|
|
@ -41,16 +41,16 @@ impl Note {
|
||||||
];
|
];
|
||||||
/// Returns the next shorter length
|
/// Returns the next shorter length
|
||||||
pub fn prev (pulses: usize) -> usize {
|
pub fn prev (pulses: usize) -> usize {
|
||||||
for i in 1..=16 { let length = Note::DURATIONS[16-i].0; if length < pulses { return length } }
|
for (length, _) in Self::DURATIONS.iter().rev() { if *length < pulses { return *length } }
|
||||||
pulses
|
pulses
|
||||||
}
|
}
|
||||||
/// Returns the next longer length
|
/// Returns the next longer length
|
||||||
pub fn next (pulses: usize) -> usize {
|
pub fn next (pulses: usize) -> usize {
|
||||||
for (length, _) in &Note::DURATIONS { if *length > pulses { return *length } }
|
for (length, _) in Self::DURATIONS.iter() { if *length > pulses { return *length } }
|
||||||
pulses
|
pulses
|
||||||
}
|
}
|
||||||
pub fn pulses_to_name (pulses: usize) -> &'static str {
|
pub fn pulses_to_name (pulses: usize) -> &'static str {
|
||||||
for (length, name) in &Note::DURATIONS { if *length == pulses { return name } }
|
for (length, name) in Self::DURATIONS.iter() { if *length == pulses { return name } }
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
pub struct MidiEditClip<'a>(pub &'a MidiEditor);
|
||||||
|
render!(Tui: (self: MidiEditClip<'a>) => {
|
||||||
|
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||||
|
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
||||||
|
} else {
|
||||||
|
(ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false)
|
||||||
|
};
|
||||||
|
Fixed::y(1, row!(
|
||||||
|
Field(color, "Edit", name.to_string()),
|
||||||
|
Field(color, "Length", length.to_string()),
|
||||||
|
Field(color, "Loop", looped.to_string())
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
|
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
|
||||||
render!(Tui: (self: MidiEditStatus<'a>) => {
|
render!(Tui: (self: MidiEditStatus<'a>) => {
|
||||||
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
pub trait MidiViewMode: HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||||
/// Make sure cursor is within range
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
||||||
|
fn redraw (&self);
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
|
*self.phrase_mut() = phrase.cloned();
|
||||||
|
self.redraw();
|
||||||
|
}
|
||||||
|
/// Make sure cursor is within note range
|
||||||
fn autoscroll (&self) {
|
fn autoscroll (&self) {
|
||||||
let note_point = self.note_point().min(127);
|
let note_point = self.note_point().min(127);
|
||||||
let note_lo = self.note_lo().get();
|
let note_lo = self.note_lo().get();
|
||||||
|
|
@ -12,11 +20,43 @@ pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
||||||
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
|
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Make sure range is within display
|
/// Make sure time range is within display
|
||||||
fn autozoom (&self) {
|
fn autozoom (&self) {
|
||||||
let time_len = self.time_len().get();
|
if self.time_lock().get() {
|
||||||
let time_axis = self.time_axis().get();
|
let time_len = self.time_len().get();
|
||||||
let time_zoom = self.time_zoom().get();
|
let time_axis = self.time_axis().get();
|
||||||
|
let time_zoom = self.time_zoom().get();
|
||||||
|
loop {
|
||||||
|
let time_zoom = self.time_zoom().get();
|
||||||
|
let time_area = time_axis * time_zoom;
|
||||||
|
if time_area > time_len {
|
||||||
|
let next_time_zoom = Note::prev(time_zoom);
|
||||||
|
if next_time_zoom <= 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let next_time_area = time_axis * next_time_zoom;
|
||||||
|
if next_time_area >= time_len {
|
||||||
|
self.time_zoom().set(next_time_zoom);
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if time_area < time_len {
|
||||||
|
let prev_time_zoom = Note::next(time_zoom);
|
||||||
|
if prev_time_zoom > 384 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let prev_time_area = time_axis * prev_time_zoom;
|
||||||
|
if prev_time_area <= time_len {
|
||||||
|
self.time_zoom().set(prev_time_zoom);
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if time_zoom != self.time_zoom().get() {
|
||||||
|
self.redraw()
|
||||||
|
}
|
||||||
|
}
|
||||||
//while time_len.div_ceil(time_zoom) > time_axis {
|
//while time_len.div_ceil(time_zoom) > time_axis {
|
||||||
//println!("\r{time_len} {time_zoom} {time_axis}");
|
//println!("\r{time_len} {time_zoom} {time_axis}");
|
||||||
//time_zoom = Note::next(time_zoom);
|
//time_zoom = Note::next(time_zoom);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ mod piano_h_time; pub(crate) use self::piano_h_time::*;
|
||||||
pub struct PianoHorizontal {
|
pub struct PianoHorizontal {
|
||||||
phrase: Option<Arc<RwLock<MidiClip>>>,
|
phrase: Option<Arc<RwLock<MidiClip>>>,
|
||||||
/// Buffer where the whole phrase is rerendered on change
|
/// Buffer where the whole phrase is rerendered on change
|
||||||
buffer: BigBuffer,
|
buffer: Arc<RwLock<BigBuffer>>,
|
||||||
/// Size of actual notes area
|
/// Size of actual notes area
|
||||||
size: Measure<Tui>,
|
size: Measure<Tui>,
|
||||||
/// The display window
|
/// The display window
|
||||||
|
|
@ -34,7 +34,7 @@ impl PianoHorizontal {
|
||||||
keys_width: 5,
|
keys_width: 5,
|
||||||
size,
|
size,
|
||||||
range,
|
range,
|
||||||
buffer: Default::default(),
|
buffer: RwLock::new(Default::default()).into(),
|
||||||
point: MidiPointModel::default(),
|
point: MidiPointModel::default(),
|
||||||
phrase: phrase.cloned(),
|
phrase: phrase.cloned(),
|
||||||
color: phrase.as_ref()
|
color: phrase.as_ref()
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,19 @@ pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iter
|
||||||
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: PianoHorizontal) => {
|
render!(Tui: (self: PianoHorizontal) => Bsp::s(
|
||||||
let (color, name, length, looped) = if let Some(phrase) = self.phrase().as_ref().map(|p|p.read().unwrap()) {
|
Fixed::y(1, Bsp::e(
|
||||||
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
Fixed::x(self.keys_width, ""),
|
||||||
} else {
|
Fill::x(PianoHorizontalTimeline(self)),
|
||||||
(ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false)
|
)),
|
||||||
};
|
Fill::xy(Bsp::e(
|
||||||
let field = move|x, y|row!(
|
Fixed::x(self.keys_width, PianoHorizontalKeys(self)),
|
||||||
Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)),
|
Fill::xy(self.size.of(lay!(
|
||||||
Tui::fg_bg(color.lightest.rgb, color.dark.rgb, format!(" {y} ")),
|
Fill::xy(PianoHorizontalNotes(self)),
|
||||||
);
|
Fill::xy(PianoHorizontalCursor(self)),
|
||||||
Bsp::s(
|
))),
|
||||||
Fixed::y(1, row!(
|
)),
|
||||||
field(" Edit ", name.to_string()), " ",
|
));
|
||||||
field(" Length ", length.to_string()), " ",
|
|
||||||
field(" Loop ", looped.to_string())
|
|
||||||
)),
|
|
||||||
Bsp::s(
|
|
||||||
Fixed::y(1, Bsp::e(
|
|
||||||
Fixed::x(self.keys_width, ""),
|
|
||||||
Fill::x(PianoHorizontalTimeline(self)),
|
|
||||||
)),
|
|
||||||
Fill::xy(Bsp::e(
|
|
||||||
Fixed::x(self.keys_width, PianoHorizontalKeys(self)),
|
|
||||||
Fill::xy(self.size.of(lay!(
|
|
||||||
Fill::xy(PianoHorizontalNotes(self)),
|
|
||||||
Fill::xy(PianoHorizontalCursor(self)),
|
|
||||||
))),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
impl PianoHorizontal {
|
impl PianoHorizontal {
|
||||||
/// Draw the piano roll foreground using full blocks on note on and half blocks on legato: █▄ █▄ █▄
|
/// Draw the piano roll foreground using full blocks on note on and half blocks on legato: █▄ █▄ █▄
|
||||||
|
|
@ -131,7 +113,7 @@ impl MidiViewMode for PianoHorizontal {
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
||||||
(phrase.length / self.range.time_zoom().get(), 128)
|
(phrase.length / self.range.time_zoom().get(), 128)
|
||||||
}
|
}
|
||||||
fn redraw (&mut self) {
|
fn redraw (&self) {
|
||||||
let buffer = if let Some(phrase) = self.phrase.as_ref() {
|
let buffer = if let Some(phrase) = self.phrase.as_ref() {
|
||||||
let phrase = phrase.read().unwrap();
|
let phrase = phrase.read().unwrap();
|
||||||
let buf_size = self.buffer_size(&phrase);
|
let buf_size = self.buffer_size(&phrase);
|
||||||
|
|
@ -145,7 +127,7 @@ impl MidiViewMode for PianoHorizontal {
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
self.buffer = buffer
|
*self.buffer.write().unwrap() = buffer
|
||||||
}
|
}
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
*self.phrase_mut() = phrase.cloned();
|
*self.phrase_mut() = phrase.cloned();
|
||||||
|
|
@ -157,9 +139,10 @@ impl MidiViewMode for PianoHorizontal {
|
||||||
|
|
||||||
impl std::fmt::Debug for PianoHorizontal {
|
impl std::fmt::Debug for PianoHorizontal {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
let buffer = self.buffer.read().unwrap();
|
||||||
f.debug_struct("PianoHorizontal")
|
f.debug_struct("PianoHorizontal")
|
||||||
.field("time_zoom", &self.range.time_zoom)
|
.field("time_zoom", &self.range.time_zoom)
|
||||||
.field("buffer", &format!("{}x{}", self.buffer.width, self.buffer.height))
|
.field("buffer", &format!("{}x{}", buffer.width, buffer.height))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ render!(Tui: |self: PianoHorizontalNotes<'a>, render|{
|
||||||
let note_lo = self.0.note_lo().get();
|
let note_lo = self.0.note_lo().get();
|
||||||
let note_hi = self.0.note_hi();
|
let note_hi = self.0.note_hi();
|
||||||
let note_point = self.0.note_point();
|
let note_point = self.0.note_point();
|
||||||
let source = &self.0.buffer;
|
let source = self.0.buffer.read().unwrap();
|
||||||
let [x0, y0, w, h] = render.area().xywh();
|
let [x0, y0, w, h] = render.area().xywh();
|
||||||
if h as usize != note_axis {
|
if h as usize != note_axis {
|
||||||
panic!("area height mismatch: {h} <> {note_axis}");
|
panic!("area height mismatch: {h} <> {note_axis}");
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct PhraseSelector {
|
pub struct ClipSelected {
|
||||||
pub(crate) title: &'static str,
|
pub(crate) title: &'static str,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) color: ItemPalette,
|
pub(crate) color: ItemPalette,
|
||||||
pub(crate) time: String,
|
pub(crate) time: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: PhraseSelector) =>
|
render!(Tui: (self: ClipSelected) =>
|
||||||
Field(self.color, self.title, format!("{} {}", self.time, self.name)));
|
Field(self.color, self.title, format!("{} {}", self.time, self.name)));
|
||||||
|
|
||||||
impl PhraseSelector {
|
impl ClipSelected {
|
||||||
|
|
||||||
// beats elapsed
|
// beats elapsed
|
||||||
pub fn play_phrase <T: HasPlayPhrase + HasClock> (state: &T) -> Self {
|
pub fn play_phrase <T: HasPlayPhrase + HasClock> (state: &T) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ render!(Tui: (self: SequencerTui) => {
|
||||||
let toolbar = Tui::when(self.transport, TransportView::new(true, &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),
|
ClipSelected::play_phrase(&self.player),
|
||||||
PhraseSelector::next_phrase(&self.player),
|
ClipSelected::next_phrase(&self.player),
|
||||||
));
|
));
|
||||||
|
|
||||||
Min::y(15, with_size(with_status(col!(
|
Min::y(15, with_size(with_status(col!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue