remove FixedAxis and ScaledAxis

This commit is contained in:
🪞👃🪞 2024-11-29 00:28:53 +01:00
parent 54057afad8
commit 286dec0f40
6 changed files with 223 additions and 290 deletions

View file

@ -33,72 +33,6 @@ impl<T> Coordinate for T where T: Send + Sync + Copy
+ Into<f64>
{}
#[derive(Debug)]
pub struct FixedAxis<T> {
pub start: T,
pub point: Option<T>,
pub clamp: Option<T>,
}
#[derive(Debug)]
pub struct ScaledAxis<T> {
pub start: T,
pub scale: T,
pub point: Option<T>,
pub clamp: Option<T>,
}
macro_rules! impl_axis_common { ($A:ident $T:ty) => {
impl $A<$T> {
#[inline] pub fn start_set (&mut self, n: $T) -> $T {
self.start = n;
self.start
}
#[inline] pub fn start_plus (&self, n: $T) -> $T {
(self.start + n).min(self.clamp.unwrap_or(<$T>::MAX))
}
#[inline] pub fn start_inc (&mut self, n: $T) -> $T {
self.start_set(self.start_plus(n))
}
#[inline] pub fn start_minus (&self, n: $T) -> $T {
self.start.saturating_sub(n)
}
#[inline] pub fn start_dec (&mut self, n: $T) -> $T {
self.start = self.start_minus(n);
self.start
}
#[inline] pub fn point_set (&mut self, n: Option<$T>) -> Option<$T> {
self.point = n;
self.point
}
#[inline] pub fn point_plus (&self, n: $T) -> Option<$T> {
self.point.map(|p|(p + n).min(self.clamp.unwrap_or(<$T>::MAX)))
}
#[inline] pub fn point_inc (&mut self, n: $T) -> Option<$T> {
self.point_set(self.point_plus(n))
}
#[inline] pub fn point_minus (&self, n: $T) -> Option<$T> {
self.point.map(|p|p.saturating_sub(n))
}
#[inline] pub fn point_dec (&mut self, n: $T) -> Option<$T> {
self.point_set(self.point_minus(n))
}
}
} }
impl_axis_common!(FixedAxis u16);
impl_axis_common!(FixedAxis usize);
impl_axis_common!(ScaledAxis u16);
impl_axis_common!(ScaledAxis usize);
impl<T: Copy> ScaledAxis<T> {
#[inline] pub fn scale_set (&mut self, n: T) -> T {
self.scale = n;
n
}
}
// TODO: return impl Point and impl Size instead of [N;x]
// to disambiguate between usage of 2-"tuple"s

View file

@ -32,7 +32,7 @@ impl Command<ArrangerTui> for ArrangerCommand {
Track(cmd) => cmd.execute(state)?.map(Track),
Clip(cmd) => cmd.execute(state)?.map(Clip),
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Zoom(zoom) => { todo!(); },
Select(selected) => {
@ -140,7 +140,9 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arrange
return None
}
Some(match input.event() {
key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(state.phrase_to_edit().clone())),
key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(Some(
state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()
))),
_ => match state.focused() {
ArrangerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
@ -149,7 +151,7 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arrange
}
},
ArrangerFocus::PhraseEditor => {
Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
Cmd::Editor(PhraseCommand::input_to_command(&state.editor, input)?)
},
ArrangerFocus::Phrases => {
Cmd::Phrases(PhrasesCommand::input_to_command(&state.phrases, input)?)

View file

@ -3,105 +3,141 @@ use crate::*;
#[derive(Clone, Debug)]
pub enum PhraseCommand {
// TODO: 1-9 seek markers that by default start every 8th of the phrase
ToggleDirection,
EnterEditMode,
ExitEditMode,
NoteAppend,
NoteSet,
NoteCursorSet(Option<usize>),
NoteLengthSet(usize),
NoteScrollSet(usize),
TimeCursorSet(Option<usize>),
TimeScrollSet(usize),
TimeZoomSet(usize),
AppendNote,
PutNote,
SetNoteCursor(usize),
SetNoteLength(usize),
SetNoteScroll(usize),
SetTimeCursor(usize),
SetTimeScroll(usize),
SetTimeZoom(usize),
Show(Option<Arc<RwLock<Phrase>>>),
SetEditMode(PhraseEditMode),
ToggleDirection,
}
impl<T: PhraseEditorControl + HasEnter> InputToCommand<Tui, T> for PhraseCommand {
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option<Self> {
use PhraseCommand::*;
use KeyCode::{Char, Enter, Esc, Up, Down, PageUp, PageDown, Left, Right};
Some(match from.event() {
key!(Char('`')) => ToggleDirection,
key!(Enter) => EnterEditMode,
key!(Esc) => ExitEditMode,
key!(Char('a')) => NoteAppend,
key!(Char('s')) => NoteSet,
key!(Char('[')) => NoteLengthSet(prev_note_length(state.note_len())),
key!(Char(']')) => NoteLengthSet(next_note_length(state.note_len())),
key!(Char('-')) => TimeZoomSet(next_note_length(state.time_axis().read().unwrap().scale)),
key!(Char('_')) => TimeZoomSet(next_note_length(state.time_axis().read().unwrap().scale)),
key!(Char('=')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
key!(Char('+')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
key!(Up) => match state.phrase_editor_entered() {
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(1)),
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(1)),
},
key!(Down) => match state.phrase_editor_entered() {
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(1)),
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(1)),
},
key!(PageUp) => match state.phrase_editor_entered() {
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(3)),
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(3)),
},
key!(PageDown) => match state.phrase_editor_entered() {
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(3)),
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(3)),
},
key!(Left) => match state.phrase_editor_entered() {
true => TimeCursorSet(state.note_axis().write().unwrap().point_minus(1)),
false => TimeScrollSet(state.note_axis().write().unwrap().start_minus(1)),
},
key!(Right) => match state.phrase_editor_entered() {
true => TimeCursorSet(state.note_axis().write().unwrap().point_plus(1)),
false => TimeScrollSet(state.note_axis().write().unwrap().start_plus(1)),
},
key!(Enter) => SetEditMode(PhraseEditMode::Note),
key!(Esc) => SetEditMode(PhraseEditMode::Scroll),
key!(Char('-')) => SetTimeZoom(
next_note_length(state.time_scale.load(Ordering::Relaxed))
),
key!(Char('_')) => SetTimeZoom(
next_note_length(state.time_scale.load(Ordering::Relaxed))
),
key!(Char('=')) => SetTimeZoom(
prev_note_length(state.time_scale.load(Ordering::Relaxed))
),
key!(Char('+')) => SetTimeZoom(
prev_note_length(state.time_scale.load(Ordering::Relaxed))
),
_ => match state.edit_mode {
PhraseEditMode::Scroll => match from.event() {
key!(Up) => SetNoteCursor(
state.note_point.load(Ordering::Relaxed) + 1
),
key!(Down) => SetNoteCursor(
state.note_point.load(Ordering::Relaxed).saturating_sub(1)
),
key!(PageUp) => SetNoteCursor(
state.note_point.load(Ordering::Relaxed) + 3
),
key!(PageDown) => SetNoteCursor(
state.note_point.load(Ordering::Relaxed).saturating_sub(3)
),
key!(Left) => SetTimeCursor(
state.note_point.load(Ordering::Relaxed).saturating_sub(1)
),
key!(Right) => SetTimeCursor(
state.note_point.load(Ordering::Relaxed) + 1
),
_ => return None
},
PhraseEditMode::Note => match from.event() {
key!(Up) => SetNoteScroll(
state.note_start.load(Ordering::Relaxed) + 1
),
key!(Down) => SetNoteScroll(
state.note_start.load(Ordering::Relaxed).saturating_sub(1)
),
key!(PageUp) => SetNoteScroll(
state.note_start.load(Ordering::Relaxed) + 3
),
key!(PageDown) => SetNoteScroll(
state.note_start.load(Ordering::Relaxed).saturating_sub(3)
),
key!(Left) => SetTimeScroll(
state.note_start.load(Ordering::Relaxed).saturating_sub(1)
),
key!(Right) => SetTimeScroll(
state.note_start.load(Ordering::Relaxed) + 1
),
key!(Char('a')) => AppendNote,
key!(Char('s')) => PutNote,
key!(Char('[')) => SetNoteLength(prev_note_length(state.note_len)),
key!(Char(']')) => SetNoteLength(next_note_length(state.note_len)),
_ => return None
},
}
_ => return None
})
}
}
impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
impl Command<PhraseEditorModel> for PhraseCommand {
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
use PhraseCommand::*;
Ok(match self {
Show(phrase) => {
state.edit_phrase(phrase);
state.phrase = phrase;
None
},
ToggleDirection => { todo!() },
EnterEditMode => {
state.focus_enter();
ToggleDirection => {
todo!()
},
SetEditMode(mode) => {
state.edit_mode = mode;
None
}
AppendNote => {
state.put_note();
state.time_cursor_advance();
None
},
ExitEditMode => {
state.focus_exit();
PutNote => {
state.put_note();
None
},
NoteAppend => {
if state.phrase_editor_entered() {
state.put_note();
state.time_cursor_advance();
}
SetTimeCursor(time) => {
state.time_point.store(time, Ordering::Relaxed);
None
},
NoteSet => { if state.phrase_editor_entered() { state.put_note(); } None },
TimeCursorSet(time) => { state.time_axis().write().unwrap().point_set(time); None },
TimeScrollSet(time) => { state.time_axis().write().unwrap().start_set(time); None },
TimeZoomSet(zoom) => { state.time_axis().write().unwrap().scale_set(zoom); None },
NoteScrollSet(note) => { state.note_axis().write().unwrap().start_set(note); None },
NoteLengthSet(time) => { *state.note_len_mut() = time; None },
NoteCursorSet(note) => {
let mut axis = state.note_axis().write().unwrap();
axis.point_set(note);
if let Some(point) = axis.point {
if point > 73 {
axis.point = Some(73);
}
if point < axis.start {
axis.start = (point / 2) * 2;
}
SetTimeScroll(time) => {
state.time_start.store(time, Ordering::Relaxed);
None
},
SetTimeZoom(zoom) => {
state.time_scale.store(zoom, Ordering::Relaxed);
None
},
SetNoteScroll(note) => {
state.note_start.store(note, Ordering::Relaxed);
None
},
SetNoteLength(time) => {
state.note_len = time;
None
},
SetNoteCursor(note) => {
let start = state.note_start.load(Ordering::Relaxed);
state.note_point.store(note, Ordering::Relaxed);
if note < start {
state.note_start.store((note / 2) * 2, Ordering::Relaxed);
}
None
},
@ -109,75 +145,3 @@ impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand {
})
}
}
pub trait PhraseEditorControl {
fn edit_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>);
fn phrase_to_edit (&self) -> Option<Arc<RwLock<Phrase>>>;
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>;
fn phrase_editor_entered (&self) -> bool;
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
fn note_len (&self) -> usize;
fn note_len_mut (&mut self) -> &mut usize;
fn put_note (&mut self);
fn time_cursor_advance (&self) {
let point = self.time_axis().read().unwrap().point;
let length = self.phrase_editing().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
let forward = |time|(time + self.note_len()) % length;
self.time_axis().write().unwrap().point = point.map(forward);
}
}
macro_rules! impl_phrase_editor_control {
(
$Struct:ident $(:: $field:ident)*
[$Focus:expr]
[$self1:ident: $phrase_to_edit:expr]
[$self2:ident, $phrase:ident: $edit_phrase:expr]
) => {
impl PhraseEditorControl for $Struct {
fn phrase_to_edit (&$self1) -> Option<Arc<RwLock<Phrase>>> {
$phrase_to_edit
}
fn edit_phrase (&mut $self2, $phrase: Option<Arc<RwLock<Phrase>>>) {
$edit_phrase
//self.editor.show(self.selected_phrase().as_ref());
//state.editor.phrase = phrase.clone();
//state.focus(ArrangerFocus::PhraseEditor);
//state.focus_enter();
//todo!("edit_phrase")
}
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>> {
todo!("phrase_editing")
}
fn phrase_editor_entered (&self) -> bool {
self.entered && self.focused() == $Focus
}
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
&self.editor.time_axis
}
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
&self.editor.note_axis
}
fn note_len (&self) -> usize {
self.editor.note_len
}
fn note_len_mut (&mut self) -> &mut usize {
&mut self.editor.note_len
}
fn put_note (&mut self) {
todo!("put_note")
}
}
}
}
impl_phrase_editor_control!(SequencerTui
[SequencerFocus::PhraseEditor]
[self: Some(self.phrases.phrases[self.phrases.phrase.load(Ordering::Relaxed)].clone())]
[self, phrase: self.editor.show(phrase)]
);
impl_phrase_editor_control!(ArrangerTui
[ArrangerFocus::PhraseEditor]
[self: todo!()]
[self, phrase: todo!()]
);

View file

@ -24,7 +24,7 @@ impl Command<SequencerTui> for SequencerCommand {
Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus),
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Enqueue(phrase) => {
state.player.enqueue_next(phrase.as_ref());
@ -78,7 +78,7 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
_ => return None,
},
SequencerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(state, input)?
PhraseCommand::input_to_command(&state.editor, input)?
),
SequencerFocus::PhraseList => match input.event() {
key!(Enter) => Enqueue(Some(

View file

@ -10,12 +10,6 @@ pub struct PhraseEditorModel {
pub(crate) keys: Buffer,
/// The full piano roll is rendered to this buffer
pub(crate) buffer: BigBuffer,
/// Cursor/scroll/zoom in pitch axis
pub(crate) note_axis: RwLock<FixedAxis<usize>>,
/// Cursor/scroll/zoom in time axis
pub(crate) time_axis: RwLock<ScaledAxis<usize>>,
/// Display mode
pub(crate) mode: bool,
/// Notes currently held at input
pub(crate) notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output
@ -23,14 +17,34 @@ pub struct PhraseEditorModel {
/// Current position of global playhead
pub(crate) now: Arc<Pulse>,
/// Width and height of notes area at last render
pub(crate) size: Measure<Tui>
pub(crate) size: Measure<Tui>,
pub(crate) note_start: AtomicUsize,
pub(crate) note_point: AtomicUsize,
pub(crate) note_clamp: AtomicUsize,
pub(crate) time_start: AtomicUsize,
pub(crate) time_point: AtomicUsize,
pub(crate) time_clamp: AtomicUsize,
pub(crate) time_scale: AtomicUsize,
pub(crate) edit_mode: PhraseEditMode,
}
impl std::fmt::Debug for PhraseEditorModel {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("PhraseEditorModel")
.field("note_axis", &self.time_axis)
.field("time_axis", &self.note_axis)
.field("note_axis", &format!("{} {} {}",
self.note_start.load(Ordering::Relaxed),
self.note_point.load(Ordering::Relaxed),
self.note_clamp.load(Ordering::Relaxed),
))
.field("time_axis", &format!("{} {} {} {}",
self.time_start.load(Ordering::Relaxed),
self.time_point.load(Ordering::Relaxed),
self.time_clamp.load(Ordering::Relaxed),
self.time_scale.load(Ordering::Relaxed),
))
.finish()
}
}
@ -38,30 +52,44 @@ impl std::fmt::Debug for PhraseEditorModel {
impl Default for PhraseEditorModel {
fn default () -> Self {
Self {
phrase: None,
note_len: 24,
keys: keys_vert(),
buffer: Default::default(),
mode: false,
notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(),
now: Pulse::default().into(),
size: Measure::new(),
note_axis: RwLock::new(FixedAxis {
start: 12,
point: Some(36),
clamp: Some(127)
}),
time_axis: RwLock::new(ScaledAxis {
start: 00,
point: Some(00),
clamp: Some(000),
scale: 24
}),
phrase: None,
note_len: 24,
keys: keys_vert(),
buffer: Default::default(),
notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(),
now: Pulse::default().into(),
size: Measure::new(),
edit_mode: PhraseEditMode::Scroll,
note_start: 12.into(),
note_point: 36.into(),
note_clamp: 127.into(),
time_start: 0.into(),
time_point: 0.into(),
time_clamp: 0.into(),
time_scale: 24.into(),
}
}
}
impl PhraseEditorModel {
pub fn put_note (&mut self) {
todo!("put_note")
}
pub fn time_cursor_advance (&self) {
let point = self.time_point.load(Ordering::Relaxed);
let length = self.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
let forward = |time|(time + self.note_len) % length;
self.time_point.store(forward(point), Ordering::Relaxed);
}
}
#[derive(Copy, Clone, Debug)]
pub enum PhraseEditMode {
Note,
Scroll,
}
pub trait HasEditor {
fn editor (&self) -> &PhraseEditorModel;
fn editor_focused (&self) -> bool;

View file

@ -8,24 +8,36 @@ pub struct PhraseView<'a> {
pub(crate) keys: &'a Buffer,
pub(crate) buffer: &'a BigBuffer,
pub(crate) note_len: usize,
pub(crate) note_axis: &'a RwLock<FixedAxis<usize>>,
pub(crate) time_axis: &'a RwLock<ScaledAxis<usize>>,
pub(crate) now: &'a Arc<Pulse>,
pub(crate) note_start: &'a AtomicUsize,
pub(crate) note_point: &'a AtomicUsize,
pub(crate) note_clamp: &'a AtomicUsize,
pub(crate) time_start: &'a AtomicUsize,
pub(crate) time_point: &'a AtomicUsize,
pub(crate) time_clamp: &'a AtomicUsize,
pub(crate) time_scale: &'a AtomicUsize,
}
impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
fn from (state: &'a T) -> Self {
Self {
focused: state.editor_focused(),
entered: state.editor_entered(),
note_len: state.editor().note_len,
phrase: &state.editor().phrase,
size: &state.editor().size,
keys: &state.editor().keys,
buffer: &state.editor().buffer,
note_axis: &state.editor().note_axis,
time_axis: &state.editor().time_axis,
now: &state.editor().now
focused: state.editor_focused(),
entered: state.editor_entered(),
note_len: state.editor().note_len,
phrase: &state.editor().phrase,
size: &state.editor().size,
keys: &state.editor().keys,
buffer: &state.editor().buffer,
now: &state.editor().now,
note_start: &state.editor().note_start,
note_point: &state.editor().note_point,
note_clamp: &state.editor().note_clamp,
time_start: &state.editor().time_start,
time_point: &state.editor().time_point,
time_clamp: &state.editor().time_clamp,
time_scale: &state.editor().time_scale,
}
}
}
@ -33,13 +45,14 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
impl<'a> Content for PhraseView<'a> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let Self {
focused, entered, phrase, size, keys, buffer, note_len, note_axis, time_axis, now
} = self;
let FixedAxis { start: note_start, point: note_point, clamp: note_clamp }
= *note_axis.read().unwrap();
let ScaledAxis { start: time_start, point: time_point, clamp: time_clamp, scale: time_scale }
= *time_axis.read().unwrap();
let Self { focused, entered, phrase, size, keys, buffer, note_len, now, .. } = self;
let note_start = self.note_start.load(Ordering::Relaxed);
let note_point = self.note_point.load(Ordering::Relaxed);
let note_clamp = self.note_clamp.load(Ordering::Relaxed);
let time_start = self.time_start.load(Ordering::Relaxed);
let time_point = self.time_point.load(Ordering::Relaxed);
let time_clamp = self.time_clamp.load(Ordering::Relaxed);
let time_scale = self.time_scale.load(Ordering::Relaxed);
//let color = Color::Rgb(0,255,0);
//let color = phrase.as_ref().map(|p|p.read().unwrap().color.base.rgb).unwrap_or(color);
let keys = CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(5))), move|to: &mut TuiOutput|{
@ -57,11 +70,8 @@ impl<'a> Content for PhraseView<'a> {
let area = to.area();
let h = area.h() as usize;
size.set_wh(area.w(), h);
let mut axis = note_axis.write().unwrap();
if let Some(point) = axis.point {
if point.saturating_sub(axis.start) > (h * 2).saturating_sub(1) {
axis.start += 2;
}
if note_point.saturating_sub(note_start) > (h * 2).saturating_sub(1) {
self.note_start.store(note_start + 2, Ordering::Relaxed);
}
Ok(if to.area().h() >= 2 {
let area = to.area();
@ -82,14 +92,12 @@ impl<'a> Content for PhraseView<'a> {
let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
Ok(if *focused && *entered {
let area = to.area();
if let (Some(time), Some(note)) = (time_point, note_point) {
let x1 = area.x() + (time / time_scale) as u16;
let x2 = x1 + (note_len / time_scale) as u16;
let y = area.y() + note.saturating_sub(note_start) as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" };
for x in x1..x2 {
to.blit(&c, x, y, Some(Style::default().fg(Color::Rgb(0,255,0))));
}
let x1 = area.x() + (time_point / time_scale) as u16;
let x2 = x1 + (note_len / time_scale) as u16;
let y = area.y() + note_point.saturating_sub(note_start) as u16 / 2;
let c = if note_point % 2 == 0 { "" } else { "" };
for x in x1..x2 {
to.blit(&c, x, y, Some(Style::default().fg(Color::Rgb(0,255,0))));
}
})
});
@ -100,8 +108,7 @@ impl<'a> Content for PhraseView<'a> {
move|to: &mut TuiOutput|{
if let Some(_) = phrase {
let now = now.get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length;
let time_clamp = time_clamp
.expect("time_axis of sequencer expected to be clamped");
let time_clamp = time_clamp;
for x in 0..(time_clamp/time_scale).saturating_sub(time_start) {
let this_step = time_start + (x + 0) * time_scale;
let next_step = time_start + (x + 1) * time_scale;
@ -136,7 +143,7 @@ impl<'a> Content for PhraseView<'a> {
//);
if *focused && *entered {
lower_right = format!("┤Note: {} {}├─{lower_right}",
note_axis.read().unwrap().point.unwrap(),
note_point,
pulses_to_name(*note_len));
//lower_right = format!("Note: {} (+{}:{}|{}) {upper_right}",
//pulses_to_name(*note_len),
@ -206,11 +213,9 @@ const NTH_OCTAVE: [&'static str; 11] = [
impl PhraseEditorModel {
pub fn put (&mut self) {
if let (Some(phrase), Some(time), Some(note)) = (
&self.phrase,
self.time_axis.read().unwrap().point,
self.note_axis.read().unwrap().point,
) {
if let Some(phrase) = &self.phrase {
let time = self.time_point.load(Ordering::Relaxed);
let note = self.note_point.load(Ordering::Relaxed);
let mut phrase = phrase.write().unwrap();
let key: u7 = u7::from((127 - note) as u8);
let vel: u7 = 100.into();
@ -225,11 +230,11 @@ impl PhraseEditorModel {
pub fn show (&mut self, phrase: Option<Arc<RwLock<Phrase>>>) {
if let Some(phrase) = phrase {
self.phrase = Some(phrase.clone());
self.time_axis.write().unwrap().clamp = Some(phrase.read().unwrap().length);
self.time_clamp.store(phrase.read().unwrap().length, Ordering::Relaxed);
self.buffer = Self::redraw(&*phrase.read().unwrap());
} else {
self.phrase = None;
self.time_axis.write().unwrap().clamp = Some(0);
self.time_clamp.store(0, Ordering::Relaxed);
self.buffer = Default::default();
}
}