mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
simplify MidiView and midi_note
This commit is contained in:
parent
c1da3fac13
commit
66e8acc811
9 changed files with 171 additions and 184 deletions
|
|
@ -78,19 +78,19 @@ pub const KEYMAP_GLOBAL: &'static [KeyBinding<App>] = keymap!(App {
|
|||
Ok(true)
|
||||
}],
|
||||
[Char('+'), NONE, "quant_inc", "quantize coarser", |app: &mut App| {
|
||||
app.transport.quant = next_note_length(app.transport.quant);
|
||||
app.transport.quant = Note::next(app.transport.quant);
|
||||
Ok(true)
|
||||
}],
|
||||
[Char('_'), NONE, "quant_dec", "quantize finer", |app: &mut App| {
|
||||
app.transport.quant = prev_note_length(app.transport.quant);
|
||||
app.transport.quant = Note::prev(app.transport.quant);
|
||||
Ok(true)
|
||||
}],
|
||||
[Char('='), NONE, "zoom_in", "show fewer ticks per block", |app: &mut App| {
|
||||
app.arranger.sequencer_mut().map(|s|s.time_axis.scale_mut(&prev_note_length));
|
||||
app.arranger.sequencer_mut().map(|s|s.time_axis.scale_mut(&Note::prev));
|
||||
Ok(true)
|
||||
}],
|
||||
[Char('-'), NONE, "zoom_out", "show more ticks per block", |app: &mut App| {
|
||||
app.arranger.sequencer_mut().map(|s|s.time_axis.scale_mut(&next_note_length));
|
||||
app.arranger.sequencer_mut().map(|s|s.time_axis.scale_mut(&Note::next));
|
||||
Ok(true)
|
||||
}],
|
||||
[Char('x'), NONE, "extend", "double the current clip", |app: &mut App| {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,26 @@ pub(crate) mod focus; pub(crate) use focus::*;
|
|||
pub(crate) mod input; pub(crate) use input::*;
|
||||
pub(crate) mod output; pub(crate) use output::*;
|
||||
|
||||
pub(crate) use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize};
|
||||
pub(crate) use Ordering::Relaxed;
|
||||
|
||||
pub use self::{
|
||||
engine::Engine,
|
||||
input::Handle,
|
||||
output::Render
|
||||
};
|
||||
|
||||
/// Standard result type.
|
||||
pub type Usually<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
/// Standard optional result type.
|
||||
pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
||||
|
||||
/// Define test modules.
|
||||
#[macro_export] macro_rules! testmod {
|
||||
($($name:ident)*) => { $(#[cfg(test)] mod $name;)* };
|
||||
}
|
||||
|
||||
/// Prototypal case of implementor macro.
|
||||
/// Saves 4loc per data pats.
|
||||
#[macro_export] macro_rules! from {
|
||||
|
|
@ -38,13 +52,15 @@ pub trait InteriorMutable<T>: Gettable<T> {
|
|||
fn set (&self, value: T) -> T;
|
||||
}
|
||||
|
||||
/// Standard result type.
|
||||
pub type Usually<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
/// Standard optional result type.
|
||||
pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
||||
|
||||
/// Define test modules.
|
||||
#[macro_export] macro_rules! testmod {
|
||||
($($name:ident)*) => { $(#[cfg(test)] mod $name;)* };
|
||||
impl Gettable<bool> for AtomicBool {
|
||||
fn get (&self) -> bool { self.load(Ordering::Relaxed) }
|
||||
}
|
||||
impl InteriorMutable<bool> for AtomicBool {
|
||||
fn set (&self, value: bool) -> bool { self.swap(value, Ordering::Relaxed) }
|
||||
}
|
||||
impl Gettable<usize> for AtomicUsize {
|
||||
fn get (&self) -> usize { self.load(Ordering::Relaxed) }
|
||||
}
|
||||
impl InteriorMutable<usize> for AtomicUsize {
|
||||
fn set (&self, value: usize) -> usize { self.swap(value, Ordering::Relaxed) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,65 @@
|
|||
use crate::*;
|
||||
use Ordering::Relaxed;
|
||||
pub trait MidiViewport<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
||||
pub struct Note;
|
||||
impl Note {
|
||||
/// (pulses, name), assuming 96 PPQ
|
||||
pub const DURATIONS: [(usize, &str);26] = [
|
||||
(1, "1/384"), (2, "1/192"),
|
||||
(3, "1/128"), (4, "1/96"),
|
||||
(6, "1/64"), (8, "1/48"),
|
||||
(12, "1/32"), (16, "1/24"),
|
||||
(24, "1/16"), (32, "1/12"),
|
||||
(48, "1/8"), (64, "1/6"),
|
||||
(96, "1/4"), (128, "1/3"),
|
||||
(192, "1/2"), (256, "2/3"),
|
||||
(384, "1/1"), (512, "4/3"),
|
||||
(576, "3/2"), (768, "2/1"),
|
||||
(1152, "3/1"), (1536, "4/1"),
|
||||
(2304, "6/1"), (3072, "8/1"),
|
||||
(3456, "9/1"), (6144, "16/1"),
|
||||
];
|
||||
/// Returns the next shorter length
|
||||
pub fn prev (pulses: usize) -> usize {
|
||||
for i in 1..=16 { let length = Note::DURATIONS[16-i].0; if length < pulses { return length } }
|
||||
pulses
|
||||
}
|
||||
/// Returns the next longer length
|
||||
pub fn next (pulses: usize) -> usize {
|
||||
for (length, _) in &Note::DURATIONS { if *length > pulses { return *length } }
|
||||
pulses
|
||||
}
|
||||
pub fn pulses_to_name (pulses: usize) -> &'static str {
|
||||
for (length, name) in &Note::DURATIONS { if *length == pulses { return name } }
|
||||
""
|
||||
}
|
||||
}
|
||||
pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
||||
/// Make sure cursor is within range
|
||||
fn autoscroll (&self) {
|
||||
let note_lo = self.note_lo();
|
||||
let note_axis = self.note_axis();
|
||||
let note_hi = self.note_hi();
|
||||
let note_point = self.note_point().min(127);
|
||||
let note_lo = self.note_lo().get();
|
||||
let note_hi = self.note_hi();
|
||||
if note_point < note_lo {
|
||||
self.set_note_lo(note_point);
|
||||
self.note_lo().set(note_point);
|
||||
} else if note_point > note_hi {
|
||||
self.set_note_lo((note_lo + note_point).saturating_sub(note_hi));
|
||||
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
|
||||
}
|
||||
}
|
||||
/// Make sure best usage of screen space is achieved by default
|
||||
/// Make sure range is within display
|
||||
fn autozoom (&self) {
|
||||
}
|
||||
fn autozoom_n (&self) {
|
||||
}
|
||||
fn autozoom_t (&self) {
|
||||
let time_len = self.time_len().get();
|
||||
let time_axis = self.time_axis().get();
|
||||
let mut time_zoom = self.time_zoom().get();
|
||||
//while time_len.div_ceil(time_zoom) > time_axis {
|
||||
//println!("\r{time_len} {time_zoom} {time_axis}");
|
||||
//time_zoom = Note::next(time_zoom);
|
||||
//}
|
||||
//self.time_zoom().set(time_zoom);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MidiRangeModel {
|
||||
pub time_len: Arc<AtomicUsize>,
|
||||
/// Length of visible time axis
|
||||
pub time_axis: Arc<AtomicUsize>,
|
||||
/// Earliest time displayed
|
||||
|
|
@ -37,6 +74,7 @@ pub struct MidiRangeModel {
|
|||
pub note_lo: Arc<AtomicUsize>,
|
||||
}
|
||||
from!(|data:(usize, bool)|MidiRangeModel = Self {
|
||||
time_len: Arc::new(0.into()),
|
||||
note_axis: Arc::new(0.into()),
|
||||
note_lo: Arc::new(0.into()),
|
||||
time_axis: Arc::new(0.into()),
|
||||
|
|
@ -45,30 +83,28 @@ from!(|data:(usize, bool)|MidiRangeModel = Self {
|
|||
time_lock: Arc::new(data.1.into()),
|
||||
});
|
||||
pub trait MidiRange {
|
||||
fn time_zoom (&self) -> usize;
|
||||
fn set_time_zoom (&mut self, x: usize);
|
||||
fn time_lock (&self) -> bool;
|
||||
fn set_time_lock (&self, x: bool);
|
||||
fn time_start (&self) -> usize;
|
||||
fn set_time_start (&self, x: usize);
|
||||
fn note_lo (&self) -> usize;
|
||||
fn set_note_lo (&self, x: usize);
|
||||
fn note_axis (&self) -> usize;
|
||||
fn time_axis (&self) -> usize;
|
||||
fn note_hi (&self) -> usize { (self.note_lo() + self.note_axis().saturating_sub(1)).min(127) }
|
||||
fn time_end (&self) -> usize { self.time_start() + self.time_axis() * self.time_zoom() }
|
||||
fn time_len (&self) -> &AtomicUsize;
|
||||
fn time_zoom (&self) -> &AtomicUsize;
|
||||
fn time_lock (&self) -> &AtomicBool;
|
||||
fn time_start (&self) -> &AtomicUsize;
|
||||
fn note_lo (&self) -> &AtomicUsize;
|
||||
fn note_axis (&self) -> &AtomicUsize;
|
||||
fn time_axis (&self) -> &AtomicUsize;
|
||||
fn note_hi (&self) -> usize {
|
||||
(self.note_lo().get() + self.note_axis().get().saturating_sub(1)).min(127)
|
||||
}
|
||||
fn time_end (&self) -> usize {
|
||||
self.time_start().get() + self.time_axis().get() * self.time_zoom().get()
|
||||
}
|
||||
}
|
||||
impl MidiRange for MidiRangeModel {
|
||||
fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) }
|
||||
fn set_time_zoom (&mut self, x: usize) { self.time_zoom.store(x, Relaxed); }
|
||||
fn time_lock (&self) -> bool { self.time_lock.load(Relaxed) }
|
||||
fn set_time_lock (&self, x: bool) { self.time_lock.store(x, Relaxed); }
|
||||
fn time_start (&self) -> usize { self.time_start.load(Relaxed) }
|
||||
fn set_time_start (&self, x: usize) { self.time_start.store(x, Relaxed); }
|
||||
fn note_lo (&self) -> usize { self.note_lo.load(Relaxed).min(127) }
|
||||
fn set_note_lo (&self, x: usize) { self.note_lo.store(x.min(127), Relaxed); }
|
||||
fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) }
|
||||
fn time_axis (&self) -> usize { self.time_axis.load(Relaxed) }
|
||||
fn time_len (&self) -> &AtomicUsize { &self.time_len }
|
||||
fn time_zoom (&self) -> &AtomicUsize { &self.time_zoom }
|
||||
fn time_lock (&self) -> &AtomicBool { &self.time_lock }
|
||||
fn time_start (&self) -> &AtomicUsize { &self.time_start }
|
||||
fn note_lo (&self) -> &AtomicUsize { &self.note_lo }
|
||||
fn note_axis (&self) -> &AtomicUsize { &self.note_axis }
|
||||
fn time_axis (&self) -> &AtomicUsize { &self.time_axis }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -5,51 +5,6 @@ pub(crate) mod pulse; pub(crate) use pulse::*;
|
|||
pub(crate) mod sr; pub(crate) use sr::*;
|
||||
pub(crate) mod unit; pub(crate) use unit::*;
|
||||
|
||||
/// (pulses, name), assuming 96 PPQ
|
||||
pub const NOTE_DURATIONS: [(usize, &str);26] = [
|
||||
(1, "1/384"),
|
||||
(2, "1/192"),
|
||||
(3, "1/128"),
|
||||
(4, "1/96"),
|
||||
(6, "1/64"),
|
||||
(8, "1/48"),
|
||||
(12, "1/32"),
|
||||
(16, "1/24"),
|
||||
(24, "1/16"),
|
||||
(32, "1/12"),
|
||||
(48, "1/8"),
|
||||
(64, "1/6"),
|
||||
(96, "1/4"),
|
||||
(128, "1/3"),
|
||||
(192, "1/2"),
|
||||
(256, "2/3"),
|
||||
(384, "1/1"),
|
||||
(512, "4/3"),
|
||||
(576, "3/2"),
|
||||
(768, "2/1"),
|
||||
(1152, "3/1"),
|
||||
(1536, "4/1"),
|
||||
(2304, "6/1"),
|
||||
(3072, "8/1"),
|
||||
(3456, "9/1"),
|
||||
(6144, "16/1"),
|
||||
];
|
||||
|
||||
/// Returns the next shorter length
|
||||
pub fn prev_note_length (pulses: usize) -> usize {
|
||||
for i in 1..=16 { let length = NOTE_DURATIONS[16-i].0; if length < pulses { return length } }
|
||||
pulses
|
||||
}
|
||||
/// Returns the next longer length
|
||||
pub fn next_note_length (pulses: usize) -> usize {
|
||||
for (length, _) in &NOTE_DURATIONS { if *length > pulses { return *length } }
|
||||
pulses
|
||||
}
|
||||
pub fn pulses_to_name (pulses: usize) -> &'static str {
|
||||
for (length, name) in &NOTE_DURATIONS { if *length == pulses { return name } }
|
||||
""
|
||||
}
|
||||
|
||||
//#[cfg(test)]
|
||||
//mod test {
|
||||
//use super::*;
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ impl_time_unit!(BeatsPerMinute);
|
|||
impl_time_unit!(LaunchSync);
|
||||
impl LaunchSync {
|
||||
pub fn next (&self) -> f64 {
|
||||
next_note_length(self.get() as usize) as f64
|
||||
Note::next(self.get() as usize) as f64
|
||||
}
|
||||
pub fn prev (&self) -> f64 {
|
||||
prev_note_length(self.get() as usize) as f64
|
||||
Note::prev(self.get() as usize) as f64
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ impl LaunchSync {
|
|||
impl_time_unit!(Quantize);
|
||||
impl Quantize {
|
||||
pub fn next (&self) -> f64 {
|
||||
next_note_length(self.get() as usize) as f64
|
||||
Note::next(self.get() as usize) as f64
|
||||
}
|
||||
pub fn prev (&self) -> f64 {
|
||||
prev_note_length(self.get() as usize) as f64
|
||||
Note::prev(self.get() as usize) as f64
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ from_jack!(|jack|SequencerTui {
|
|||
phrases: PhraseListModel::from(&phrase),
|
||||
editor: PhraseEditorModel::from(&phrase),
|
||||
player: PhrasePlayerModel::from((&clock, &phrase)),
|
||||
clock,
|
||||
size: Measure::new(),
|
||||
midi_buf: vec![vec![];65536],
|
||||
note_buf: vec![],
|
||||
perf: PerfModel::default(),
|
||||
show_pool: true,
|
||||
status: true,
|
||||
clock,
|
||||
}
|
||||
});
|
||||
render!(<Tui>|self: SequencerTui|{
|
||||
|
|
@ -91,11 +91,13 @@ handle!(<Tui>|self:SequencerTui,input|SequencerCommand::execute_with_state(self,
|
|||
}
|
||||
input_to_command!(SequencerCommand: <Tui>|state:SequencerTui,input|match input.event() {
|
||||
// Transport: Play/pause
|
||||
key_pat!(Char(' ')) => Clock(if state.clock().is_stopped() {
|
||||
Play(None) } else { Pause(None) }),
|
||||
key_pat!(Char(' ')) => Clock(
|
||||
if state.clock().is_stopped() { Play(None) } else { Pause(None) }
|
||||
),
|
||||
// Transport: Play from start or rewind to start
|
||||
key_pat!(Shift-Char(' ')) => Clock(if state.clock().is_stopped() {
|
||||
Play(Some(0)) } else { Pause(Some(0)) }),
|
||||
key_pat!(Shift-Char(' ')) => Clock(
|
||||
if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }
|
||||
),
|
||||
// TODO: u: undo
|
||||
key_pat!(Char('u')) => { todo!("undo") },
|
||||
// TODO: Shift-U: redo
|
||||
|
|
@ -151,13 +153,13 @@ command!(|self: SequencerCommand, state: SequencerTui|match self {
|
|||
_ => default(cmd)?
|
||||
}
|
||||
},
|
||||
Self::Editor(cmd) => {
|
||||
Self::Editor(cmd) => {
|
||||
let default = ||cmd.execute(&mut state.editor).map(|x|x.map(Editor));
|
||||
match cmd {
|
||||
_ => default()?
|
||||
}
|
||||
},
|
||||
Self::Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||
Self::Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||
Self::Enqueue(phrase) => {
|
||||
state.player.enqueue_next(phrase.as_ref());
|
||||
None
|
||||
|
|
|
|||
|
|
@ -1,19 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
const HEADER_H: u16 = 3;
|
||||
const SCENES_W_OFFSET: u16 = 3;
|
||||
|
||||
fn tracks_with_widths (tracks: &[ArrangerTrack])
|
||||
-> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
|
||||
{
|
||||
let mut x = 0;
|
||||
tracks.iter().enumerate().map(move |(index, track)|{
|
||||
let data = (index, track, x, x + track.width);
|
||||
x += track.width;
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ArrangerVHead<'a> {
|
||||
scenes_w: u16,
|
||||
timebase: &'a Arc<Timebase>,
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@ pub enum PhraseCommand {
|
|||
impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
||||
fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option<Self> {
|
||||
let length = ||state.phrase().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||
let note_lo = ||state.note_lo();
|
||||
let time_start = ||state.time_start();
|
||||
let time_zoom = ||state.time_zoom();
|
||||
let time_lock = ||state.time_lock();
|
||||
|
||||
let note_lo = ||state.note_lo().get();
|
||||
let time_start = ||state.time_start().get();
|
||||
let time_zoom = ||state.time_zoom().get();
|
||||
let time_lock = ||state.time_lock().get();
|
||||
|
||||
let note_point = ||state.note_point();
|
||||
let time_point = ||state.time_point();
|
||||
let note_len = ||state.note_len();
|
||||
|
|
@ -59,16 +61,16 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
|||
key_pat!(Right) => SetTimeCursor((time_point() + note_len()) % length()),
|
||||
key_pat!(Char('`')) => ToggleDirection,
|
||||
key_pat!(Char('z')) => SetTimeLock(!time_lock()),
|
||||
key_pat!(Char('-')) => SetTimeZoom(next_note_length(time_zoom())),
|
||||
key_pat!(Char('_')) => SetTimeZoom(next_note_length(time_zoom())),
|
||||
key_pat!(Char('=')) => SetTimeZoom(prev_note_length(time_zoom())),
|
||||
key_pat!(Char('+')) => SetTimeZoom(prev_note_length(time_zoom())),
|
||||
key_pat!(Char('-')) => SetTimeZoom(Note::next(time_zoom())),
|
||||
key_pat!(Char('_')) => SetTimeZoom(Note::next(time_zoom())),
|
||||
key_pat!(Char('=')) => SetTimeZoom(Note::prev(time_zoom())),
|
||||
key_pat!(Char('+')) => SetTimeZoom(Note::prev(time_zoom())),
|
||||
key_pat!(Enter) => PutNote,
|
||||
key_pat!(Ctrl-Enter) => AppendNote,
|
||||
key_pat!(Char(',')) => SetNoteLength(prev_note_length(note_len())), // TODO: no 3plet
|
||||
key_pat!(Char('.')) => SetNoteLength(next_note_length(note_len())),
|
||||
key_pat!(Char('<')) => SetNoteLength(prev_note_length(note_len())), // TODO: 3plet
|
||||
key_pat!(Char('>')) => SetNoteLength(next_note_length(note_len())),
|
||||
key_pat!(Char(',')) => SetNoteLength(Note::prev(note_len())), // TODO: no 3plet
|
||||
key_pat!(Char('.')) => SetNoteLength(Note::next(note_len())),
|
||||
key_pat!(Char('<')) => SetNoteLength(Note::prev(note_len())), // TODO: 3plet
|
||||
key_pat!(Char('>')) => SetNoteLength(Note::next(note_len())),
|
||||
// TODO: key_pat!(Char('/')) => // toggle 3plet
|
||||
// TODO: key_pat!(Char('?')) => // toggle dotted
|
||||
_ => return None
|
||||
|
|
@ -80,20 +82,16 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
|||
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
|
||||
use PhraseCommand::*;
|
||||
match self {
|
||||
Show(phrase) => { state.set_phrase(phrase.as_ref()); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(x) => { state.set_time_zoom(x); },
|
||||
SetTimeLock(x) => { state.set_time_lock(x); },
|
||||
SetTimeScroll(x) => { state.set_time_start(x); },
|
||||
SetNoteScroll(x) => { state.set_note_lo(x.min(127)); },
|
||||
SetNoteLength(x) => { state.set_note_len(x); },
|
||||
SetTimeCursor(x) => {
|
||||
state.set_time_point(x);
|
||||
},
|
||||
SetNoteCursor(note) => {
|
||||
state.set_note_point(note.min(127));
|
||||
},
|
||||
Show(phrase) => { state.set_phrase(phrase.as_ref()); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
|
||||
SetTimeLock(x) => { state.time_lock().set(x); },
|
||||
SetTimeScroll(x) => { state.time_start().set(x); },
|
||||
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
|
||||
SetNoteLength(x) => { state.set_note_len(x); },
|
||||
SetTimeCursor(x) => { state.set_time_point(x); },
|
||||
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
|
||||
_ => todo!("{:?}", self)
|
||||
}
|
||||
Ok(None)
|
||||
|
|
@ -118,6 +116,7 @@ impl Default for PhraseEditorModel {
|
|||
has_size!(<Tui>|self:PhraseEditorModel|&self.size);
|
||||
render!(<Tui>|self: PhraseEditorModel|{
|
||||
self.autoscroll();
|
||||
self.autozoom();
|
||||
&self.mode
|
||||
});
|
||||
//render!(<Tui>|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
|
||||
|
|
@ -133,19 +132,16 @@ pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + D
|
|||
}
|
||||
}
|
||||
|
||||
impl MidiViewport<Tui> for PhraseEditorModel {}
|
||||
impl MidiView<Tui> for PhraseEditorModel {}
|
||||
|
||||
impl MidiRange for PhraseEditorModel {
|
||||
fn time_zoom (&self) -> usize { self.mode.time_zoom() }
|
||||
fn set_time_zoom (&mut self, x: usize) { self.mode.set_time_zoom(x); }
|
||||
fn time_lock (&self) -> bool { self.mode.time_lock() }
|
||||
fn set_time_lock (&self, x: bool) { self.mode.set_time_lock(x); }
|
||||
fn time_start (&self) -> usize { self.mode.time_start() }
|
||||
fn set_time_start (&self, x: usize) { self.mode.set_time_start(x); }
|
||||
fn set_note_lo (&self, x: usize) { self.mode.set_note_lo(x); }
|
||||
fn note_lo (&self) -> usize { self.mode.note_lo() }
|
||||
fn note_axis (&self) -> usize { self.mode.note_axis() }
|
||||
fn time_axis (&self) -> usize { self.mode.time_axis() }
|
||||
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
||||
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
||||
fn time_lock (&self) -> &AtomicBool { self.mode.time_lock() }
|
||||
fn time_start (&self) -> &AtomicUsize { self.mode.time_start() }
|
||||
fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() }
|
||||
fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() }
|
||||
fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() }
|
||||
}
|
||||
|
||||
impl MidiPoint for PhraseEditorModel {
|
||||
|
|
@ -253,10 +249,10 @@ render!(<Tui>|self:PhraseEditStatus<'a>|row!(|add|{
|
|||
field(" Time", format!("{}",
|
||||
self.0.time_point())),
|
||||
field(" View", format!("{}-{} ({}*{})",
|
||||
self.0.time_start(),
|
||||
self.0.time_start().get(),
|
||||
self.0.time_end(),
|
||||
self.0.time_axis(),
|
||||
self.0.time_zoom()))
|
||||
self.0.time_axis().get(),
|
||||
self.0.time_zoom().get()))
|
||||
])))?;
|
||||
add(&Fixed::wh(25, 3, col!(![
|
||||
field(" Note", format!("{:4} ({:3}) {:4}",
|
||||
|
|
@ -264,12 +260,12 @@ render!(<Tui>|self:PhraseEditStatus<'a>|row!(|add|{
|
|||
self.0.note_point(),
|
||||
self.0.note_len())),
|
||||
field(" View", format!("{}-{} ({})",
|
||||
to_note_name(self.0.note_lo()),
|
||||
to_note_name(self.0.note_lo().get()),
|
||||
to_note_name(self.0.note_hi()),
|
||||
self.0.note_axis()))
|
||||
self.0.note_axis().get()))
|
||||
])))?;
|
||||
add(&Fixed::wh(16, 3, col!(![
|
||||
row!(!["TimeLock ", Tui::bold(true, format!("{}", self.0.time_lock()))])])))?;
|
||||
row!(!["TimeLock ", Tui::bold(true, format!("{}", self.0.time_lock().get()))])])))?;
|
||||
Ok(())
|
||||
}))))
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ pub struct PianoHorizontal {
|
|||
|
||||
impl PianoHorizontal {
|
||||
pub fn new (phrase: Option<&Arc<RwLock<Phrase>>>) -> Self {
|
||||
let size = Measure::new();
|
||||
let mut range = MidiRangeModel::from((24, true));
|
||||
let size = Measure::new();
|
||||
let mut range = MidiRangeModel::from((24, true));
|
||||
range.time_axis = size.x.clone();
|
||||
range.note_axis = size.y.clone();
|
||||
let phrase = phrase.map(|p|p.clone());
|
||||
|
|
@ -80,7 +80,7 @@ render!(<Tui>|self: PianoHorizontalTimeline<'a>|render(|to|{
|
|||
let style = Some(Style::default().dim());
|
||||
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {
|
||||
let t = area_x as usize * self.0.time_zoom();
|
||||
let t = area_x as usize * self.0.time_zoom().get();
|
||||
if t < length {
|
||||
to.blit(&"|", screen_x, y, style);
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ render!(<Tui>|self: PianoHorizontalTimeline<'a>|render(|to|{
|
|||
pub struct PianoHorizontalKeys<'a>(&'a PianoHorizontal);
|
||||
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok({
|
||||
let color = self.0.color;
|
||||
let note_lo = self.0.note_lo();
|
||||
let note_lo = self.0.note_lo().get();
|
||||
let note_hi = self.0.note_hi();
|
||||
let note_point = self.0.note_point();
|
||||
let [x, y0, w, h] = to.area().xywh();
|
||||
|
|
@ -130,11 +130,11 @@ pub struct PianoHorizontalCursor<'a>(&'a PianoHorizontal);
|
|||
render!(<Tui>|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
||||
let note_hi = self.0.note_hi();
|
||||
let note_len = self.0.note_len();
|
||||
let note_lo = self.0.note_lo();
|
||||
let note_lo = self.0.note_lo().get();
|
||||
let note_point = self.0.note_point();
|
||||
let time_point = self.0.time_point();
|
||||
let time_start = self.0.time_start();
|
||||
let time_zoom = self.0.time_zoom();
|
||||
let time_start = self.0.time_start().get();
|
||||
let time_zoom = self.0.time_zoom().get();
|
||||
let [x0, y0, w, _] = to.area().xywh();
|
||||
let style = Some(Style::default().fg(Color::Rgb(0,255,0)));
|
||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
|
|
@ -159,13 +159,13 @@ render!(<Tui>|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
|||
|
||||
pub struct PianoHorizontalNotes<'a>(&'a PianoHorizontal);
|
||||
render!(<Tui>|self: PianoHorizontalNotes<'a>|render(|to|Ok({
|
||||
let time_start = self.0.time_start();
|
||||
let time_start = self.0.time_start().get();
|
||||
let note_lo = self.0.note_lo().get();
|
||||
let note_hi = self.0.note_hi();
|
||||
let note_lo = self.0.note_lo();
|
||||
let note_point = self.0.note_point();
|
||||
let source = &self.0.buffer;
|
||||
let [x0, y0, w, h] = to.area().xywh();
|
||||
if h as usize != self.0.note_axis() {
|
||||
if h as usize != self.0.note_axis().get() {
|
||||
panic!("area height mismatch");
|
||||
}
|
||||
for (area_x, screen_x) in (x0..x0+w).enumerate() {
|
||||
|
|
@ -264,19 +264,13 @@ impl PianoHorizontal {
|
|||
has_size!(<Tui>|self:PianoHorizontal|&self.size);
|
||||
|
||||
impl MidiRange for PianoHorizontal {
|
||||
fn time_zoom (&self) -> usize { self.range.time_zoom() }
|
||||
fn set_time_zoom (&mut self, x: usize) {
|
||||
self.range.set_time_zoom(x);
|
||||
self.redraw();
|
||||
}
|
||||
fn time_lock (&self) -> bool { self.range.time_lock() }
|
||||
fn set_time_lock (&self, x: bool) { self.range.set_time_lock(x); }
|
||||
fn time_start (&self) -> usize { self.range.time_start() }
|
||||
fn set_time_start (&self, x: usize) { self.range.set_time_start(x); }
|
||||
fn set_note_lo (&self, x: usize) { self.range.set_note_lo(x); }
|
||||
fn note_lo (&self) -> usize { self.range.note_lo() }
|
||||
fn note_axis (&self) -> usize { self.range.note_axis() }
|
||||
fn time_axis (&self) -> usize { self.range.time_axis() }
|
||||
fn time_len (&self) -> &AtomicUsize { self.range.time_len() }
|
||||
fn time_zoom (&self) -> &AtomicUsize { self.range.time_zoom() }
|
||||
fn time_lock (&self) -> &AtomicBool { self.range.time_lock() }
|
||||
fn time_start (&self) -> &AtomicUsize { self.range.time_start() }
|
||||
fn note_lo (&self) -> &AtomicUsize { self.range.note_lo() }
|
||||
fn note_axis (&self) -> &AtomicUsize { self.range.note_axis() }
|
||||
fn time_axis (&self) -> &AtomicUsize { self.range.time_axis() }
|
||||
}
|
||||
impl MidiPoint for PianoHorizontal {
|
||||
fn note_len (&self) -> usize { self.point.note_len()}
|
||||
|
|
@ -295,15 +289,16 @@ impl PhraseViewMode for PianoHorizontal {
|
|||
}
|
||||
/// Determine the required space to render the phrase.
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||
(phrase.length / self.range.time_zoom(), 128)
|
||||
(phrase.length / self.range.time_zoom().get(), 128)
|
||||
}
|
||||
fn redraw (&mut self) {
|
||||
let buffer = if let Some(phrase) = self.phrase.as_ref() {
|
||||
let phrase = phrase.read().unwrap();
|
||||
let buf_size = self.buffer_size(&phrase);
|
||||
let mut buffer = BigBuffer::from(buf_size);
|
||||
let note_len = self.point.note_len();
|
||||
let time_zoom = self.range.time_zoom();
|
||||
let note_len = self.note_len();
|
||||
let time_zoom = self.time_zoom().get();
|
||||
self.time_len().set(phrase.length);
|
||||
PianoHorizontal::draw_bg(&mut buffer, &phrase, time_zoom, note_len);
|
||||
PianoHorizontal::draw_fg(&mut buffer, &phrase, time_zoom);
|
||||
buffer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue