mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
traits MIDIRange and MIDIPoint
This commit is contained in:
parent
d06b95df2c
commit
8f0decbe4d
7 changed files with 236 additions and 235 deletions
|
|
@ -2,6 +2,7 @@ mod phrase; pub(crate) use phrase::*;
|
||||||
mod jack; pub(crate) use self::jack::*;
|
mod jack; pub(crate) use self::jack::*;
|
||||||
mod clip; pub(crate) use clip::*;
|
mod clip; pub(crate) use clip::*;
|
||||||
mod clock; pub(crate) use clock::*;
|
mod clock; pub(crate) use clock::*;
|
||||||
|
mod note; pub(crate) use note::*;
|
||||||
mod player; pub(crate) use player::*;
|
mod player; pub(crate) use player::*;
|
||||||
mod scene; pub(crate) use scene::*;
|
mod scene; pub(crate) use scene::*;
|
||||||
mod track; pub(crate) use track::*;
|
mod track; pub(crate) use track::*;
|
||||||
|
|
|
||||||
94
crates/tek/src/api/note.rs
Normal file
94
crates/tek/src/api/note.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use crate::*;
|
||||||
|
use Ordering::Relaxed;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MIDIRangeModel {
|
||||||
|
/// Length of visible time axis
|
||||||
|
pub time_axis: Arc<AtomicUsize>,
|
||||||
|
/// Earliest time displayed
|
||||||
|
pub time_start: Arc<AtomicUsize>,
|
||||||
|
/// Time step
|
||||||
|
pub time_zoom: Arc<AtomicUsize>,
|
||||||
|
/// Auto rezoom to fit in time axis
|
||||||
|
pub time_lock: Arc<AtomicBool>,
|
||||||
|
/// Length of visible note axis
|
||||||
|
pub note_axis: Arc<AtomicUsize>,
|
||||||
|
// Lowest note displayed
|
||||||
|
pub note_lo: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(usize, bool)> for MIDIRangeModel {
|
||||||
|
fn from ((time_zoom, time_lock): (usize, bool)) -> Self {
|
||||||
|
Self {
|
||||||
|
note_axis: Arc::new(0.into()),
|
||||||
|
note_lo: Arc::new(0.into()),
|
||||||
|
time_axis: Arc::new(0.into()),
|
||||||
|
time_start: Arc::new(0.into()),
|
||||||
|
time_zoom: Arc::new(time_zoom.into()),
|
||||||
|
time_lock: Arc::new(time_lock.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MIDIPointModel {
|
||||||
|
/// Time coordinate of cursor
|
||||||
|
pub time_point: Arc<AtomicUsize>,
|
||||||
|
/// Note coordinate of cursor
|
||||||
|
pub note_point: Arc<AtomicUsize>,
|
||||||
|
/// Length of note that will be inserted, in pulses
|
||||||
|
pub note_len: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MIDIPointModel {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self {
|
||||||
|
time_point: Arc::new(0.into()),
|
||||||
|
note_point: Arc::new(0.into()),
|
||||||
|
note_len: Arc::new(24.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MIDIRange {
|
||||||
|
fn time_zoom (&self) -> usize;
|
||||||
|
fn set_time_zoom (&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 note_hi (&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MIDIPoint {
|
||||||
|
fn note_len (&self) -> usize;
|
||||||
|
fn set_note_len (&self, x: usize);
|
||||||
|
fn note_point (&self) -> usize;
|
||||||
|
fn set_note_point (&self, x: usize);
|
||||||
|
fn time_point (&self) -> usize;
|
||||||
|
fn set_time_point (&self, x: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MIDIRange for MIDIRangeModel {
|
||||||
|
fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) }
|
||||||
|
fn set_time_zoom (&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 set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); }
|
||||||
|
fn note_lo (&self) -> usize { self.note_lo.load(Relaxed) }
|
||||||
|
fn note_axis (&self) -> usize { self.note_lo.load(Relaxed) }
|
||||||
|
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
|
||||||
|
}
|
||||||
|
impl MIDIPoint for MIDIPointModel {
|
||||||
|
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
|
||||||
|
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
|
||||||
|
fn note_point (&self) -> usize { self.note_point.load(Relaxed) }
|
||||||
|
fn set_note_point (&self, x: usize) { self.note_point.store(x, Relaxed) }
|
||||||
|
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
|
||||||
|
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ mod engine; pub(crate) use engine::*;
|
||||||
mod focus; pub(crate) use focus::*;
|
mod focus; pub(crate) use focus::*;
|
||||||
mod input; pub(crate) use input::*;
|
mod input; pub(crate) use input::*;
|
||||||
mod output; pub(crate) use output::*;
|
mod output; pub(crate) use output::*;
|
||||||
|
mod perf; pub(crate) use perf::*;
|
||||||
mod pitch; pub(crate) use pitch::*;
|
mod pitch; pub(crate) use pitch::*;
|
||||||
mod space; pub(crate) use space::*;
|
mod space; pub(crate) use space::*;
|
||||||
mod time; pub(crate) use time::*;
|
mod time; pub(crate) use time::*;
|
||||||
|
|
|
||||||
54
crates/tek/src/core/perf.rs
Normal file
54
crates/tek/src/core/perf.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Performance counter
|
||||||
|
pub struct PerfModel {
|
||||||
|
pub enabled: bool,
|
||||||
|
clock: quanta::Clock,
|
||||||
|
// In nanoseconds
|
||||||
|
used: AtomicF64,
|
||||||
|
// In microseconds
|
||||||
|
period: AtomicF64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PerfModel {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
clock: quanta::Clock::new(),
|
||||||
|
used: Default::default(),
|
||||||
|
period: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerfModel {
|
||||||
|
pub fn get_t0 (&self) -> Option<u64> {
|
||||||
|
if self.enabled {
|
||||||
|
Some(self.clock.raw())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn update (&self, t0: Option<u64>, scope: &jack::ProcessScope) {
|
||||||
|
if let Some(t0) = t0 {
|
||||||
|
let t1 = self.clock.raw();
|
||||||
|
self.used.store(
|
||||||
|
self.clock.delta_as_nanos(t0, t1) as f64,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
self.period.store(
|
||||||
|
scope.cycle_times().unwrap().period_usecs as f64,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn percentage (&self) -> Option<f64> {
|
||||||
|
let period = self.period.load(Ordering::Relaxed) * 1000.0;
|
||||||
|
if period > 0.0 {
|
||||||
|
let used = self.used.load(Ordering::Relaxed);
|
||||||
|
Some(100.0 * used / period)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -386,59 +386,6 @@ pub fn pulses_to_name (pulses: usize) -> &'static str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performance counter
|
|
||||||
pub struct PerfModel {
|
|
||||||
pub enabled: bool,
|
|
||||||
clock: quanta::Clock,
|
|
||||||
// In nanoseconds
|
|
||||||
used: AtomicF64,
|
|
||||||
// In microseconds
|
|
||||||
period: AtomicF64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PerfModel {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: true,
|
|
||||||
clock: quanta::Clock::new(),
|
|
||||||
used: Default::default(),
|
|
||||||
period: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PerfModel {
|
|
||||||
pub fn get_t0 (&self) -> Option<u64> {
|
|
||||||
if self.enabled {
|
|
||||||
Some(self.clock.raw())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn update (&self, t0: Option<u64>, scope: &jack::ProcessScope) {
|
|
||||||
if let Some(t0) = t0 {
|
|
||||||
let t1 = self.clock.raw();
|
|
||||||
self.used.store(
|
|
||||||
self.clock.delta_as_nanos(t0, t1) as f64,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
);
|
|
||||||
self.period.store(
|
|
||||||
scope.cycle_times().unwrap().period_usecs as f64,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn percentage (&self) -> Option<f64> {
|
|
||||||
let period = self.period.load(Ordering::Relaxed) * 1000.0;
|
|
||||||
if period > 0.0 {
|
|
||||||
let used = self.used.load(Ordering::Relaxed);
|
|
||||||
Some(100.0 * used / period)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#[cfg(test)]
|
//#[cfg(test)]
|
||||||
//mod test {
|
//mod test {
|
||||||
//use super::*;
|
//use super::*;
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,13 @@ pub enum PhraseCommand {
|
||||||
impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
||||||
fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option<Self> {
|
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 length = ||state.phrase().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||||
let range = state.range();
|
let note_lo = ||state.note_lo();
|
||||||
let note_lo = ||range.note_lo.load(Relaxed);
|
let time_start = ||state.time_start();
|
||||||
let time_start = ||range.time_start.load(Relaxed);
|
let time_zoom = ||state.time_zoom();
|
||||||
let time_zoom = ||range.time_zoom();
|
let time_lock = ||state.time_lock();
|
||||||
let point = state.point();
|
let note_point = ||state.note_point();
|
||||||
let note_point = ||point.note_point();
|
let time_point = ||state.time_point();
|
||||||
let time_point = ||point.time_point();
|
let note_len = ||state.note_len();
|
||||||
let note_len = ||point.note_len();
|
|
||||||
Some(match from.event() {
|
Some(match from.event() {
|
||||||
key_pat!(Ctrl-Alt-Up) => SetNoteScroll(note_point() + 3),
|
key_pat!(Ctrl-Alt-Up) => SetNoteScroll(note_point() + 3),
|
||||||
key_pat!(Ctrl-Alt-Down) => SetNoteScroll(note_point().saturating_sub(3)),
|
key_pat!(Ctrl-Alt-Down) => SetNoteScroll(note_point().saturating_sub(3)),
|
||||||
|
|
@ -54,7 +53,7 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
||||||
key_pat!(Left) => SetTimeCursor(time_point().saturating_sub(note_len())),
|
key_pat!(Left) => SetTimeCursor(time_point().saturating_sub(note_len())),
|
||||||
key_pat!(Right) => SetTimeCursor((time_point() + note_len()) % length()),
|
key_pat!(Right) => SetTimeCursor((time_point() + note_len()) % length()),
|
||||||
key_pat!(Char('`')) => ToggleDirection,
|
key_pat!(Char('`')) => ToggleDirection,
|
||||||
key_pat!(Char('z')) => SetTimeLock(!state.range().time_lock()),
|
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(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())),
|
||||||
|
|
@ -75,27 +74,24 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
||||||
impl Command<PhraseEditorModel> for PhraseCommand {
|
impl Command<PhraseEditorModel> for PhraseCommand {
|
||||||
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
|
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
let range = state.range();
|
|
||||||
let point = state.point();
|
|
||||||
match self {
|
match self {
|
||||||
Show(phrase) => { state.set_phrase(phrase.as_ref()); },
|
Show(phrase) => { state.set_phrase(phrase.as_ref()); },
|
||||||
PutNote => { state.put_note(false); },
|
PutNote => { state.put_note(false); },
|
||||||
AppendNote => { state.put_note(true); },
|
AppendNote => { state.put_note(true); },
|
||||||
SetTimeZoom(x) => { range.set_time_zoom(x); },
|
SetTimeZoom(x) => { state.set_time_zoom(x); },
|
||||||
SetTimeLock(x) => { range.set_time_lock(x); },
|
SetTimeLock(x) => { state.set_time_lock(x); },
|
||||||
SetTimeScroll(x) => { range.set_time_start(x); },
|
SetTimeScroll(x) => { state.set_time_start(x); },
|
||||||
SetNoteScroll(x) => { range.set_note_lo(x); },
|
SetNoteScroll(x) => { state.set_note_lo(x); },
|
||||||
SetNoteLength(x) => { point.set_note_len(x); },
|
SetNoteLength(x) => { state.set_note_len(x); },
|
||||||
SetTimeCursor(x) => { point.set_time_point(x); },
|
SetTimeCursor(x) => { state.set_time_point(x); },
|
||||||
SetNoteCursor(note) => {
|
SetNoteCursor(note) => {
|
||||||
let note = 127.min(note);
|
let note = 127.min(note);
|
||||||
let start = range.note_lo.load(Relaxed);
|
let start = state.note_lo();
|
||||||
point.note_point.store(note, Relaxed);
|
state.set_note_point(note);
|
||||||
if note < start {
|
if note < start {
|
||||||
range.note_lo.store(note, Relaxed);
|
state.set_note_lo(note)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => todo!("{:?}", self)
|
_ => todo!("{:?}", self)
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -116,9 +112,7 @@ impl Default for PhraseEditorModel {
|
||||||
|
|
||||||
render!(|self: PhraseEditorModel|self.mode);
|
render!(|self: PhraseEditorModel|self.mode);
|
||||||
|
|
||||||
pub trait PhraseViewMode: Render<Tui> + Debug + Send + Sync {
|
pub trait PhraseViewMode: Render<Tui> + MIDIRange + MIDIPoint + Debug + Send + Sync {
|
||||||
fn range (&self) -> &PhraseEditorRange;
|
|
||||||
fn point (&self) -> &PhraseEditorPoint;
|
|
||||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
||||||
fn redraw (&mut self);
|
fn redraw (&mut self);
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||||
|
|
@ -129,13 +123,27 @@ pub trait PhraseViewMode: Render<Tui> + Debug + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MIDIRange for PhraseEditorModel {
|
||||||
|
fn time_zoom (&self) -> usize { self.mode.time_zoom() }
|
||||||
|
fn set_time_zoom (&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_lo() }
|
||||||
|
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
|
||||||
|
}
|
||||||
|
impl MIDIPoint for PhraseEditorModel {
|
||||||
|
fn note_len (&self) -> usize { self.mode.note_len()}
|
||||||
|
fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) }
|
||||||
|
fn note_point (&self) -> usize { self.mode.note_point() }
|
||||||
|
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
|
||||||
|
fn time_point (&self) -> usize { self.mode.time_point() }
|
||||||
|
fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) }
|
||||||
|
}
|
||||||
impl PhraseViewMode for PhraseEditorModel {
|
impl PhraseViewMode for PhraseEditorModel {
|
||||||
fn range (&self) -> &PhraseEditorRange {
|
|
||||||
self.mode.range()
|
|
||||||
}
|
|
||||||
fn point (&self) -> &PhraseEditorPoint {
|
|
||||||
self.mode.point()
|
|
||||||
}
|
|
||||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||||
self.mode.buffer_size(phrase)
|
self.mode.buffer_size(phrase)
|
||||||
}
|
}
|
||||||
|
|
@ -153,113 +161,15 @@ impl PhraseViewMode for PhraseEditorModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PhraseEditorRange {
|
|
||||||
/// Length of visible time axis
|
|
||||||
pub time_axis: Arc<AtomicUsize>,
|
|
||||||
/// Earliest time displayed
|
|
||||||
pub time_start: Arc<AtomicUsize>,
|
|
||||||
/// Time step
|
|
||||||
pub time_zoom: Arc<AtomicUsize>,
|
|
||||||
/// Auto rezoom to fit in time axis
|
|
||||||
pub time_lock: Arc<AtomicBool>,
|
|
||||||
/// Length of visible note axis
|
|
||||||
pub note_axis: Arc<AtomicUsize>,
|
|
||||||
// Lowest note displayed
|
|
||||||
pub note_lo: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PhraseEditorRange {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self {
|
|
||||||
time_axis: Arc::new(0.into()),
|
|
||||||
time_start: Arc::new(0.into()),
|
|
||||||
time_zoom: Arc::new(24.into()),
|
|
||||||
time_lock: Arc::new(true.into()),
|
|
||||||
note_axis: Arc::new(0.into()),
|
|
||||||
note_lo: Arc::new(0.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PhraseEditorRange {
|
|
||||||
pub fn time_zoom (&self) -> usize {
|
|
||||||
self.time_zoom.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn set_time_zoom (&self, x: usize) {
|
|
||||||
self.time_zoom.store(x, Relaxed);
|
|
||||||
}
|
|
||||||
pub fn time_lock (&self) -> bool {
|
|
||||||
self.time_lock.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn set_time_lock (&self, x: bool) {
|
|
||||||
self.time_lock.store(x, Relaxed);
|
|
||||||
}
|
|
||||||
pub fn time_start (&self) -> usize {
|
|
||||||
self.time_start.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn set_time_start (&self, x: usize) {
|
|
||||||
self.time_start.store(x, Relaxed);
|
|
||||||
}
|
|
||||||
pub fn set_note_lo (&self, x: usize) {
|
|
||||||
self.note_lo.store(x, Relaxed);
|
|
||||||
}
|
|
||||||
pub fn note_lo (&self) -> usize {
|
|
||||||
self.note_lo.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn note_axis (&self) -> usize {
|
|
||||||
self.note_lo.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn note_hi (&self) -> usize {
|
|
||||||
self.note_lo() + self.note_axis()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PhraseEditorPoint {
|
|
||||||
/// Time coordinate of cursor
|
|
||||||
pub time_point: Arc<AtomicUsize>,
|
|
||||||
/// Note coordinate of cursor
|
|
||||||
pub note_point: Arc<AtomicUsize>,
|
|
||||||
/// Length of note that will be inserted, in pulses
|
|
||||||
pub note_len: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PhraseEditorPoint {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self {
|
|
||||||
time_point: Arc::new(0.into()),
|
|
||||||
note_point: Arc::new(0.into()),
|
|
||||||
note_len: Arc::new(24.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PhraseEditorPoint {
|
|
||||||
pub fn note_len (&self) -> usize {
|
|
||||||
self.note_len.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn set_note_len (&self, x: usize) {
|
|
||||||
self.note_len.store(x, Relaxed)
|
|
||||||
}
|
|
||||||
pub fn note_point (&self) -> usize {
|
|
||||||
self.note_point.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn time_point (&self) -> usize {
|
|
||||||
self.time_point.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn set_time_point (&self, x: usize) {
|
|
||||||
self.time_point.store(x, Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseEditorModel {
|
impl PhraseEditorModel {
|
||||||
/// Put note at current position
|
/// Put note at current position
|
||||||
pub fn put_note (&mut self, advance: bool) {
|
pub fn put_note (&mut self, advance: bool) {
|
||||||
let mut redraw = false;
|
let mut redraw = false;
|
||||||
if let Some(phrase) = self.phrase() {
|
if let Some(phrase) = self.phrase() {
|
||||||
let mut phrase = phrase.write().unwrap();
|
let mut phrase = phrase.write().unwrap();
|
||||||
let note_start = self.point().time_point();
|
let note_start = self.time_point();
|
||||||
let note_point = self.point().note_point();
|
let note_point = self.note_point();
|
||||||
let note_len = self.point().note_len();
|
let note_len = self.note_len();
|
||||||
let note_end = note_start + note_len;
|
let note_end = note_start + note_len;
|
||||||
let key: u7 = u7::from(note_point as u8);
|
let key: u7 = u7::from(note_point as u8);
|
||||||
let vel: u7 = 100.into();
|
let vel: u7 = 100.into();
|
||||||
|
|
@ -274,7 +184,7 @@ impl PhraseEditorModel {
|
||||||
phrase.notes[note_end].push(note_off);
|
phrase.notes[note_end].push(note_off);
|
||||||
}
|
}
|
||||||
if advance {
|
if advance {
|
||||||
self.point().set_time_point(note_end);
|
self.set_time_point(note_end);
|
||||||
}
|
}
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +192,25 @@ impl PhraseEditorModel {
|
||||||
self.mode.redraw();
|
self.mode.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Make sure cursor is within range
|
||||||
|
fn autoscroll (
|
||||||
|
range: &impl MIDIRange,
|
||||||
|
point: &impl MIDIPoint,
|
||||||
|
height: usize
|
||||||
|
) -> (usize, (usize, usize)) {
|
||||||
|
let note_point = point.note_point();
|
||||||
|
let mut note_lo = range.note_lo();
|
||||||
|
let mut note_hi = 127.min((note_lo + height).saturating_sub(2));
|
||||||
|
if note_point > note_hi {
|
||||||
|
note_lo += note_point - note_hi;
|
||||||
|
note_hi = note_point;
|
||||||
|
range.set_note_lo(note_lo);
|
||||||
|
}
|
||||||
|
(note_point, (note_lo, note_hi))
|
||||||
|
}
|
||||||
|
/// Make sure best usage of screen space is achieved by default
|
||||||
|
fn autozoom (&self) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Arc<RwLock<Phrase>>> for PhraseEditorModel {
|
impl From<&Arc<RwLock<Phrase>>> for PhraseEditorModel {
|
||||||
|
|
@ -301,46 +230,7 @@ impl From<Option<Arc<RwLock<Phrase>>>> for PhraseEditorModel {
|
||||||
impl std::fmt::Debug for PhraseEditorModel {
|
impl std::fmt::Debug for PhraseEditorModel {
|
||||||
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("PhraseEditorModel")
|
f.debug_struct("PhraseEditorModel")
|
||||||
.field("range", &self.range())
|
.field("point", &self)
|
||||||
.field("point", &self.point())
|
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn autoscroll_notes (
|
|
||||||
range: &PhraseEditorRange, point: &PhraseEditorPoint, height: usize
|
|
||||||
) -> (usize, (usize, usize)) {
|
|
||||||
let note_point = point.note_point.load(Relaxed);
|
|
||||||
let mut note_lo = range.note_lo.load(Relaxed);
|
|
||||||
let mut note_hi = 127.min((note_lo + height).saturating_sub(2));
|
|
||||||
if note_point > note_hi {
|
|
||||||
note_lo += note_point - note_hi;
|
|
||||||
note_hi = note_point;
|
|
||||||
range.note_lo.store(note_lo, Relaxed);
|
|
||||||
}
|
|
||||||
(note_point, (note_lo, note_hi))
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
|
|
||||||
//fn from (state: &'a T) -> Self {
|
|
||||||
//let editor = state.editor();
|
|
||||||
//let (note_point, note_range) = autoscroll_notes(
|
|
||||||
//&editor.range,
|
|
||||||
//&editor.point,
|
|
||||||
//editor.size.h()
|
|
||||||
//);
|
|
||||||
//Self {
|
|
||||||
//note_point,
|
|
||||||
//note_range,
|
|
||||||
//time_start: editor.range.time_start.load(Relaxed),
|
|
||||||
//time_point: editor.point.time_point.load(Relaxed),
|
|
||||||
//note_len: editor.point.note_len.load(Relaxed),
|
|
||||||
//phrase: editor.phrase.clone(),
|
|
||||||
//mode: &editor.mode,
|
|
||||||
//size: &editor.size,
|
|
||||||
//now: &editor.now,
|
|
||||||
//focused: state.editor_focused(),
|
|
||||||
//entered: state.editor_entered(),
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ pub struct PianoHorizontal {
|
||||||
/// Width and height of notes area at last render
|
/// Width and height of notes area at last render
|
||||||
size: Measure<Tui>,
|
size: Measure<Tui>,
|
||||||
/// The display window
|
/// The display window
|
||||||
range: PhraseEditorRange,
|
range: MIDIRangeModel,
|
||||||
/// The note cursor
|
/// The note cursor
|
||||||
point: PhraseEditorPoint,
|
point: MIDIPointModel,
|
||||||
/// The highlight color palette
|
/// The highlight color palette
|
||||||
color: ItemPalette,
|
color: ItemPalette,
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct PianoHorizontal {
|
||||||
impl PianoHorizontal {
|
impl PianoHorizontal {
|
||||||
pub fn new (phrase: Option<&Arc<RwLock<Phrase>>>) -> Self {
|
pub fn new (phrase: Option<&Arc<RwLock<Phrase>>>) -> Self {
|
||||||
let size = Measure::new();
|
let size = Measure::new();
|
||||||
let mut range = PhraseEditorRange::default();
|
let mut range = MIDIRangeModel::from((24, true));
|
||||||
range.time_axis = size.x.clone();
|
range.time_axis = size.x.clone();
|
||||||
range.note_axis = size.y.clone();
|
range.note_axis = size.y.clone();
|
||||||
let phrase = phrase.map(|p|p.clone());
|
let phrase = phrase.map(|p|p.clone());
|
||||||
|
|
@ -28,7 +28,7 @@ impl PianoHorizontal {
|
||||||
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
||||||
Self {
|
Self {
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
point: PhraseEditorPoint::default(),
|
point: MIDIPointModel::default(),
|
||||||
size,
|
size,
|
||||||
range,
|
range,
|
||||||
phrase,
|
phrase,
|
||||||
|
|
@ -241,6 +241,26 @@ impl PianoHorizontal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MIDIRange for PianoHorizontal {
|
||||||
|
fn time_zoom (&self) -> usize { self.range.time_zoom() }
|
||||||
|
fn set_time_zoom (&self, x: usize) { self.range.set_time_zoom(x); }
|
||||||
|
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_lo() }
|
||||||
|
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
|
||||||
|
}
|
||||||
|
impl MIDIPoint for PianoHorizontal {
|
||||||
|
fn note_len (&self) -> usize { self.point.note_len()}
|
||||||
|
fn set_note_len (&self, x: usize) { self.point.set_note_len(x) }
|
||||||
|
fn note_point (&self) -> usize { self.point.note_point() }
|
||||||
|
fn set_note_point (&self, x: usize) { self.point.set_note_point(x) }
|
||||||
|
fn time_point (&self) -> usize { self.point.time_point() }
|
||||||
|
fn set_time_point (&self, x: usize) { self.point.set_time_point(x) }
|
||||||
|
}
|
||||||
impl PhraseViewMode for PianoHorizontal {
|
impl PhraseViewMode for PianoHorizontal {
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
&self.phrase
|
&self.phrase
|
||||||
|
|
@ -248,12 +268,6 @@ impl PhraseViewMode for PianoHorizontal {
|
||||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<Phrase>>> {
|
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<Phrase>>> {
|
||||||
&mut self.phrase
|
&mut self.phrase
|
||||||
}
|
}
|
||||||
fn range (&self) -> &PhraseEditorRange {
|
|
||||||
&self.range
|
|
||||||
}
|
|
||||||
fn point (&self) -> &PhraseEditorPoint {
|
|
||||||
&self.point
|
|
||||||
}
|
|
||||||
/// Determine the required space to render the phrase.
|
/// Determine the required space to render the phrase.
|
||||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||||
(phrase.length / self.range.time_zoom(), 128)
|
(phrase.length / self.range.time_zoom(), 128)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue