diff --git a/crates/tek.old/src/control.rs b/crates/tek.old/src/control.rs index 46bd349f..ff45ff11 100644 --- a/crates/tek.old/src/control.rs +++ b/crates/tek.old/src/control.rs @@ -78,19 +78,19 @@ pub const KEYMAP_GLOBAL: &'static [KeyBinding] = 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| { diff --git a/crates/tek/src/core.rs b/crates/tek/src/core.rs index 6c4c98df..ab3ba20a 100644 --- a/crates/tek/src/core.rs +++ b/crates/tek/src/core.rs @@ -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 = Result>; + +/// Standard optional result type. +pub type Perhaps = Result, Box>; + +/// 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: Gettable { fn set (&self, value: T) -> T; } -/// Standard result type. -pub type Usually = Result>; - -/// Standard optional result type. -pub type Perhaps = Result, Box>; - -/// Define test modules. -#[macro_export] macro_rules! testmod { - ($($name:ident)*) => { $(#[cfg(test)] mod $name;)* }; +impl Gettable for AtomicBool { + fn get (&self) -> bool { self.load(Ordering::Relaxed) } +} +impl InteriorMutable for AtomicBool { + fn set (&self, value: bool) -> bool { self.swap(value, Ordering::Relaxed) } +} +impl Gettable for AtomicUsize { + fn get (&self) -> usize { self.load(Ordering::Relaxed) } +} +impl InteriorMutable for AtomicUsize { + fn set (&self, value: usize) -> usize { self.swap(value, Ordering::Relaxed) } } diff --git a/crates/tek/src/midi/midi_note.rs b/crates/tek/src/midi/midi_note.rs index 408bfddb..84474557 100644 --- a/crates/tek/src/midi/midi_note.rs +++ b/crates/tek/src/midi/midi_note.rs @@ -1,28 +1,65 @@ use crate::*; use Ordering::Relaxed; -pub trait MidiViewport: MidiRange + MidiPoint + HasSize { +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: MidiRange + MidiPoint + HasSize { /// 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, /// Length of visible time axis pub time_axis: Arc, /// Earliest time displayed @@ -37,6 +74,7 @@ pub struct MidiRangeModel { pub note_lo: Arc, } 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)] diff --git a/crates/tek/src/time.rs b/crates/tek/src/time.rs index 0ef9a5db..4cc89706 100644 --- a/crates/tek/src/time.rs +++ b/crates/tek/src/time.rs @@ -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::*; diff --git a/crates/tek/src/time/pulse.rs b/crates/tek/src/time/pulse.rs index 52096580..7f8c9097 100644 --- a/crates/tek/src/time/pulse.rs +++ b/crates/tek/src/time/pulse.rs @@ -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 } } diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 1bddb4eb..c566d672 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -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!(|self: SequencerTui|{ @@ -91,11 +91,13 @@ handle!(|self:SequencerTui,input|SequencerCommand::execute_with_state(self, } input_to_command!(SequencerCommand: |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 diff --git a/crates/tek/src/tui/arranger_mode_v.rs b/crates/tek/src/tui/arranger_mode_v.rs index 35b27563..44aa0538 100644 --- a/crates/tek/src/tui/arranger_mode_v.rs +++ b/crates/tek/src/tui/arranger_mode_v.rs @@ -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 -{ - 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, diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index c3fd87d7..9c325d54 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -33,10 +33,12 @@ pub enum PhraseCommand { impl InputToCommand for PhraseCommand { fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option { 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 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 for PhraseCommand { fn execute (self, state: &mut PhraseEditorModel) -> Perhaps { 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!(|self:PhraseEditorModel|&self.size); render!(|self: PhraseEditorModel|{ self.autoscroll(); + self.autozoom(); &self.mode }); //render!(|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks @@ -133,19 +132,16 @@ pub trait PhraseViewMode: Render + HasSize + MidiRange + MidiPoint + D } } -impl MidiViewport for PhraseEditorModel {} +impl MidiView 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!(|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!(|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(()) })))) })); diff --git a/crates/tek/src/tui/piano_horizontal.rs b/crates/tek/src/tui/piano_horizontal.rs index e3ed1ec1..04bd82ed 100644 --- a/crates/tek/src/tui/piano_horizontal.rs +++ b/crates/tek/src/tui/piano_horizontal.rs @@ -40,8 +40,8 @@ pub struct PianoHorizontal { impl PianoHorizontal { pub fn new (phrase: Option<&Arc>>) -> 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!(|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!(|self: PianoHorizontalTimeline<'a>|render(|to|{ pub struct PianoHorizontalKeys<'a>(&'a PianoHorizontal); render!(|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!(|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!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({ pub struct PianoHorizontalNotes<'a>(&'a PianoHorizontal); render!(|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!(|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