mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: p.58, e=57
This commit is contained in:
parent
0c94c2af8f
commit
e95230a340
5 changed files with 173 additions and 174 deletions
|
|
@ -25,8 +25,9 @@ impl PhraseTui {
|
||||||
entered: false,
|
entered: false,
|
||||||
mode: false,
|
mode: false,
|
||||||
now: Arc::new(0.into()),
|
now: Arc::new(0.into()),
|
||||||
width: 0.into(),
|
size: Measure::default(),
|
||||||
height: 0.into(),
|
//width: 0.into(),
|
||||||
|
//height: 0.into(),
|
||||||
note_axis: RwLock::new(FixedAxis {
|
note_axis: RwLock::new(FixedAxis {
|
||||||
start: 12,
|
start: 12,
|
||||||
point: Some(36),
|
point: Some(36),
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,21 @@ pub enum SequencerCommand {
|
||||||
|
|
||||||
impl<T> Command<T> for SequencerCommand
|
impl<T> Command<T> for SequencerCommand
|
||||||
where
|
where
|
||||||
T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi
|
T: PhrasesControl + PhraseControl + ClockApi + PlayheadApi
|
||||||
|
+ FocusGrid<Item = SequencerFocus> + FocusEnter<Item = SequencerFocus>
|
||||||
{
|
{
|
||||||
fn execute (self, state: T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use SequencerCommand::*;
|
use SequencerCommand::*;
|
||||||
match self {
|
Ok(match self {
|
||||||
Focus(cmd) => delegate(cmd, Focus, state),
|
Focus(cmd) => cmd.execute(state)?.map(Focus),
|
||||||
Phrases(cmd) => delegate(cmd, Phrases, state),
|
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
|
||||||
Editor(cmd) => delegate(cmd, Editor, state),
|
Editor(cmd) => cmd.execute(state)?.map(Editor),
|
||||||
Clock(cmd) => delegate(cmd, Clock, state),
|
Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||||
Playhead(cmd) => delegate(cmd, Playhead, state)
|
Playhead(cmd) => cmd.execute(state)?.map(Playhead),
|
||||||
}
|
Undo => { todo!() },
|
||||||
|
Redo => { todo!() },
|
||||||
|
Clear => { todo!() },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,28 +76,32 @@ pub enum ArrangerCommand {
|
||||||
|
|
||||||
impl<T> Command<T> for ArrangerCommand
|
impl<T> Command<T> for ArrangerCommand
|
||||||
where
|
where
|
||||||
T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi
|
T: ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi
|
||||||
|
+ FocusGrid<Item = ArrangerFocus> + FocusEnter<Item = ArrangerFocus>
|
||||||
{
|
{
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
match self {
|
Ok(match self {
|
||||||
Focus(cmd) => { delegate(cmd, Focus, &mut state) },
|
Focus(cmd) => cmd.execute(state)?.map(Focus),
|
||||||
Scene(cmd) => { delegate(cmd, Scene, &mut state) },
|
Scene(cmd) => cmd.execute(state)?.map(Scene),
|
||||||
Track(cmd) => { delegate(cmd, Track, &mut state) },
|
Track(cmd) => cmd.execute(state)?.map(Track),
|
||||||
Clip(cmd) => { delegate(cmd, Clip, &mut state) },
|
Clip(cmd) => cmd.execute(state)?.map(Clip),
|
||||||
Phrases(cmd) => { delegate(cmd, Phrases, &mut state) },
|
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
|
||||||
Editor(cmd) => { delegate(cmd, Editor, &mut state) },
|
Editor(cmd) => cmd.execute(state)?.map(Editor),
|
||||||
Clock(cmd) => { delegate(cmd, Clock, &mut state) },
|
Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||||
Playhead(cmd) => { delegate(cmd, Playhead, &mut state) },
|
Playhead(cmd) => cmd.execute(state)?.map(Playhead),
|
||||||
Zoom(zoom) => { todo!(); },
|
Zoom(zoom) => { todo!(); },
|
||||||
Select(selected) => { state.selected = selected; Ok(None) },
|
Select(selected) => {
|
||||||
|
state.selected = selected;
|
||||||
|
None
|
||||||
|
},
|
||||||
EditPhrase(phrase) => {
|
EditPhrase(phrase) => {
|
||||||
state.editor.phrase = phrase.clone();
|
state.editor.phrase = phrase.clone();
|
||||||
state.focus(ArrangerFocus::PhraseEditor);
|
state.focus(ArrangerFocus::PhraseEditor);
|
||||||
state.focus_enter();
|
state.focus_enter();
|
||||||
Ok(None)
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,31 +132,21 @@ pub enum PhrasesCommand {
|
||||||
Phrase(PhrasePoolCommand),
|
Phrase(PhrasePoolCommand),
|
||||||
Rename(PhraseRenameCommand),
|
Rename(PhraseRenameCommand),
|
||||||
Length(PhraseLengthCommand),
|
Length(PhraseLengthCommand),
|
||||||
MoveUp,
|
|
||||||
MoveDown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
|
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseRenameCommand as Rename;
|
use PhraseRenameCommand as Rename;
|
||||||
use PhraseLengthCommand as Length;
|
use PhraseLengthCommand as Length;
|
||||||
match self {
|
Ok(match self {
|
||||||
|
Self::Phrase(command) => command.execute(state)?.map(Self::Phrase),
|
||||||
|
Self::Rename(command) => command.execute(state)?.map(Self::Rename),
|
||||||
|
Self::Length(command) => command.execute(state)?.map(Self::Length),
|
||||||
Self::Select(phrase) => {
|
Self::Select(phrase) => {
|
||||||
state.phrase = phrase
|
state.phrase = phrase;
|
||||||
|
None
|
||||||
},
|
},
|
||||||
Self::Phrase(command) => {
|
})
|
||||||
return Ok(command.execute(&mut state)?.map(Self::Phrase))
|
|
||||||
}
|
|
||||||
Self::Rename(command) => match command {
|
|
||||||
Rename::Begin => self.phrases_rename_begin(),
|
|
||||||
_ => return Ok(command.execute(state)?.map(Self::Rename)),
|
|
||||||
},
|
|
||||||
Self::Length(command) => match command {
|
|
||||||
Length::Begin => self.phrases_length_begin(),
|
|
||||||
_ => return Ok(command.execute(state)?.map(Self::Length)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +197,7 @@ impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if self == Begin {
|
} else if self == Begin {
|
||||||
self.phrases_length_begin();
|
state.phrase_length_begin();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -239,7 +237,7 @@ where
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if self == Begin {
|
} else if self == Begin {
|
||||||
self.phrase_rename_begin();
|
state.phrase_rename_begin();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -279,81 +277,107 @@ where
|
||||||
//}
|
//}
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
match self.translate(state) {
|
Ok(match self {
|
||||||
ToggleDirection => {
|
ToggleDirection => {
|
||||||
state.mode = !state.mode;
|
state.mode = !state.mode;
|
||||||
|
None
|
||||||
},
|
},
|
||||||
EnterEditMode => {
|
EnterEditMode => {
|
||||||
state.focus_enter();
|
state.focus_enter();
|
||||||
|
None
|
||||||
},
|
},
|
||||||
ExitEditMode => {
|
ExitEditMode => {
|
||||||
state.focus_exit();
|
state.focus_exit();
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeZoomOut => {
|
TimeZoomOut => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().scale = next_note_length(scale)
|
state.time_axis().write().unwrap().scale = next_note_length(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeZoomIn => {
|
TimeZoomIn => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().scale = prev_note_length(scale)
|
state.time_axis().write().unwrap().scale = prev_note_length(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeCursorDec => {
|
TimeCursorDec => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().point_dec(scale);
|
state.time_axis().write().unwrap().point_dec(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeCursorInc => {
|
TimeCursorInc => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().point_inc(scale);
|
state.time_axis().write().unwrap().point_inc(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeScrollDec => {
|
TimeScrollDec => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().start_dec(scale);
|
state.time_axis().write().unwrap().start_dec(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
TimeScrollInc => {
|
TimeScrollInc => {
|
||||||
let scale = state.time_axis().read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis().write().unwrap().start_inc(scale);
|
state.time_axis().write().unwrap().start_inc(scale);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteCursorDec => {
|
NoteCursorDec => {
|
||||||
let mut axis = state.note_axis().write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.point_inc(1);
|
axis.point_inc(1);
|
||||||
if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } }
|
if let Some(point) = axis.point {
|
||||||
|
if point > 73 { axis.point = Some(73); }
|
||||||
|
}
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteCursorInc => {
|
NoteCursorInc => {
|
||||||
let mut axis = state.note_axis().write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.point_dec(1);
|
axis.point_dec(1);
|
||||||
if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } }
|
if let Some(point) = axis.point {
|
||||||
|
if point < axis.start { axis.start = (point / 2) * 2; }
|
||||||
|
}
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteScrollDec => {
|
NoteScrollDec => {
|
||||||
state.note_axis().write().unwrap().start_inc(1);
|
state.note_axis().write().unwrap().start_inc(1);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteScrollInc => {
|
NoteScrollInc => {
|
||||||
state.note_axis().write().unwrap().start_dec(1);
|
state.note_axis().write().unwrap().start_dec(1);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteLengthDec => {
|
NoteLengthDec => {
|
||||||
*state.note_len_mut() = prev_note_length(state.note_len())
|
*state.note_len_mut() = prev_note_length(state.note_len());
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteLengthInc => {
|
NoteLengthInc => {
|
||||||
*state.note_len_mut() = next_note_length(state.note_len())
|
*state.note_len_mut() = next_note_length(state.note_len());
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NotePageUp => {
|
NotePageUp => {
|
||||||
let mut axis = state.note_axis().write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.start_dec(3);
|
axis.start_dec(3);
|
||||||
axis.point_dec(3);
|
axis.point_dec(3);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NotePageDown => {
|
NotePageDown => {
|
||||||
let mut axis = state.note_axis().write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.start_inc(3);
|
axis.start_inc(3);
|
||||||
axis.point_inc(3);
|
axis.point_inc(3);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteAppend => if state.focus_entered() {
|
NoteAppend => if state.focus_entered() {
|
||||||
state.put();
|
state.put();
|
||||||
state.time_cursor_advance();
|
state.time_cursor_advance();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
None
|
||||||
},
|
},
|
||||||
NoteSet => if state.focus_entered() {
|
NoteSet => if state.focus_entered() {
|
||||||
state.put();
|
state.put();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
None
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
})
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ where
|
||||||
|
|
||||||
impl<T> InputToCommand<Tui, T> for SequencerCommand
|
impl<T> InputToCommand<Tui, T> for SequencerCommand
|
||||||
where
|
where
|
||||||
T: SequencerControl + TransportControl + PhrasesControl + PhraseControl + HasFocus<Item = SequencerFocus>
|
T: SequencerControl + TransportControl + PhrasesControl + PhraseControl + PlayheadApi
|
||||||
|
+ HasFocus<Item = SequencerFocus> + FocusGrid<Item = SequencerFocus>
|
||||||
{
|
{
|
||||||
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
|
|
@ -92,7 +93,8 @@ where
|
||||||
|
|
||||||
impl<T> InputToCommand<Tui, T> for ArrangerCommand
|
impl<T> InputToCommand<Tui, T> for ArrangerCommand
|
||||||
where
|
where
|
||||||
T: ArrangerControl + TransportControl + PhrasesControl + PhraseControl + HasFocus<Item = ArrangerFocus>
|
T: ArrangerControl + TransportControl + PhrasesControl + PhraseControl + PlayheadApi
|
||||||
|
+ HasFocus<Item = ArrangerFocus> + FocusGrid<Item = ArrangerFocus>
|
||||||
{
|
{
|
||||||
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,29 @@ impl HasScenes<ArrangerScene> for ArrangerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Display mode of arranger
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum ArrangerMode {
|
||||||
|
/// Tracks are rows
|
||||||
|
Horizontal,
|
||||||
|
/// Tracks are columns
|
||||||
|
Vertical(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arranger display mode can be cycled
|
||||||
|
impl ArrangerMode {
|
||||||
|
/// Cycle arranger display mode
|
||||||
|
pub fn to_next (&mut self) {
|
||||||
|
*self = match self {
|
||||||
|
Self::Horizontal => Self::Vertical(1),
|
||||||
|
Self::Vertical(1) => Self::Vertical(2),
|
||||||
|
Self::Vertical(2) => Self::Vertical(2),
|
||||||
|
Self::Vertical(0) => Self::Horizontal,
|
||||||
|
Self::Vertical(_) => Self::Vertical(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct ArrangerScene {
|
pub struct ArrangerScene {
|
||||||
/// Name of scene
|
/// Name of scene
|
||||||
|
|
@ -425,6 +448,42 @@ pub enum PhrasesMode {
|
||||||
Length(usize, usize, PhraseLengthFocus),
|
Length(usize, usize, PhraseLengthFocus),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Displays and edits phrase length.
|
||||||
|
pub struct PhraseLength {
|
||||||
|
/// Pulses per beat (quaver)
|
||||||
|
pub ppq: usize,
|
||||||
|
/// Beats per bar
|
||||||
|
pub bpb: usize,
|
||||||
|
/// Length of phrase in pulses
|
||||||
|
pub pulses: usize,
|
||||||
|
/// Selected subdivision
|
||||||
|
pub focus: Option<PhraseLengthFocus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhraseLength {
|
||||||
|
pub fn new (pulses: usize, focus: Option<PhraseLengthFocus>) -> Self {
|
||||||
|
Self { ppq: PPQ, bpb: 4, pulses, focus }
|
||||||
|
}
|
||||||
|
pub fn bars (&self) -> usize {
|
||||||
|
self.pulses / (self.bpb * self.ppq)
|
||||||
|
}
|
||||||
|
pub fn beats (&self) -> usize {
|
||||||
|
(self.pulses % (self.bpb * self.ppq)) / self.ppq
|
||||||
|
}
|
||||||
|
pub fn ticks (&self) -> usize {
|
||||||
|
self.pulses % self.ppq
|
||||||
|
}
|
||||||
|
pub fn bars_string (&self) -> String {
|
||||||
|
format!("{}", self.bars())
|
||||||
|
}
|
||||||
|
pub fn beats_string (&self) -> String {
|
||||||
|
format!("{}", self.beats())
|
||||||
|
}
|
||||||
|
pub fn ticks_string (&self) -> String {
|
||||||
|
format!("{:>02}", self.ticks())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Contains state for viewing and editing a phrase
|
/// Contains state for viewing and editing a phrase
|
||||||
pub struct PhraseTui {
|
pub struct PhraseTui {
|
||||||
/// Phrase being played
|
/// Phrase being played
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,10 @@ use crate::*;
|
||||||
|
|
||||||
pub struct TransportView<'a, T: TransportViewState>(pub &'a T);
|
pub struct TransportView<'a, T: TransportViewState>(pub &'a T);
|
||||||
|
|
||||||
pub trait TransportViewState: Send + Sync {
|
pub trait TransportViewState: ClockApi + PlayheadApi + Send + Sync {
|
||||||
fn transport_selected (&self) -> TransportFocus;
|
fn transport_selected (&self) -> TransportFocus;
|
||||||
fn transport_focused (&self) -> bool;
|
fn transport_focused (&self) -> bool;
|
||||||
fn transport_state (&self) -> Option<TransportState>;
|
fn transport_state (&self) -> Option<TransportState>;
|
||||||
fn bpm_value (&self) -> f64;
|
|
||||||
fn sync_value (&self) -> f64;
|
|
||||||
fn format_beat (&self) -> String;
|
|
||||||
fn format_msu (&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransportViewState for TransportTui {
|
|
||||||
fn transport_selected (&self) -> TransportFocus {
|
|
||||||
self.focus
|
|
||||||
}
|
|
||||||
fn transport_focused (&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
|
||||||
*self.playing().read().unwrap()
|
|
||||||
}
|
|
||||||
fn bpm_value (&self) -> f64 {
|
fn bpm_value (&self) -> f64 {
|
||||||
self.bpm().get()
|
self.bpm().get()
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +20,18 @@ impl TransportViewState for TransportTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TransportViewState for TransportTui {
|
||||||
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
|
self.focus
|
||||||
|
}
|
||||||
|
fn transport_focused (&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
|
*self.playing().read().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TransportViewState for SequencerTui {
|
impl TransportViewState for SequencerTui {
|
||||||
fn transport_selected (&self) -> TransportFocus {
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
self.focus
|
self.focus
|
||||||
|
|
@ -46,18 +42,6 @@ impl TransportViewState for SequencerTui {
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
*self.playing().read().unwrap()
|
*self.playing().read().unwrap()
|
||||||
}
|
}
|
||||||
fn bpm_value (&self) -> f64 {
|
|
||||||
self.bpm().get()
|
|
||||||
}
|
|
||||||
fn sync_value (&self) -> f64 {
|
|
||||||
self.sync().get()
|
|
||||||
}
|
|
||||||
fn format_beat (&self) -> String {
|
|
||||||
self.current().format_beat()
|
|
||||||
}
|
|
||||||
fn format_msu (&self) -> String {
|
|
||||||
self.current().usec.format_msu()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportViewState for ArrangerTui {
|
impl TransportViewState for ArrangerTui {
|
||||||
|
|
@ -70,18 +54,6 @@ impl TransportViewState for ArrangerTui {
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
*self.playing().read().unwrap()
|
*self.playing().read().unwrap()
|
||||||
}
|
}
|
||||||
fn bpm_value (&self) -> f64 {
|
|
||||||
self.bpm().get()
|
|
||||||
}
|
|
||||||
fn sync_value (&self) -> f64 {
|
|
||||||
self.sync().get()
|
|
||||||
}
|
|
||||||
fn format_beat (&self) -> String {
|
|
||||||
self.current().format_beat()
|
|
||||||
}
|
|
||||||
fn format_msu (&self) -> String {
|
|
||||||
self.current().usec.format_msu()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArrangerViewState {
|
pub trait ArrangerViewState {
|
||||||
|
|
@ -161,31 +133,34 @@ impl PhrasesViewState for ArrangerTui {
|
||||||
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
|
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
|
||||||
|
|
||||||
pub trait PhraseViewState: Send + Sync {
|
pub trait PhraseViewState: Send + Sync {
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||||
fn phrase_focused (&self) -> bool;
|
fn phrase_focused (&self) -> bool;
|
||||||
|
fn phrase_editor_size (&self) -> &Measure<Tui>;
|
||||||
fn entered (&self) -> bool;
|
fn entered (&self) -> bool;
|
||||||
fn keys (&self) -> &Buffer;
|
fn keys (&self) -> &Buffer;
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
|
||||||
fn buffer (&self) -> &BigBuffer;
|
fn buffer (&self) -> &BigBuffer;
|
||||||
fn note_len (&self) -> usize;
|
fn note_len (&self) -> usize;
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
||||||
fn size (&self) -> &Measure<Tui>;
|
|
||||||
fn now (&self) -> &Arc<Pulse>;
|
fn now (&self) -> &Arc<Pulse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for PhraseTui {
|
impl PhraseViewState for PhraseTui {
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
|
&self.phrase
|
||||||
|
}
|
||||||
fn phrase_focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
self.focused
|
self.focused
|
||||||
}
|
}
|
||||||
|
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
self.entered
|
self.entered
|
||||||
}
|
}
|
||||||
fn keys (&self) -> &Buffer {
|
fn keys (&self) -> &Buffer {
|
||||||
&self.keys
|
&self.keys
|
||||||
}
|
}
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
&self.phrase
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
fn buffer (&self) -> &BigBuffer {
|
||||||
&self.buffer
|
&self.buffer
|
||||||
}
|
}
|
||||||
|
|
@ -198,27 +173,27 @@ impl PhraseViewState for PhraseTui {
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
&self.time_axis
|
&self.time_axis
|
||||||
}
|
}
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
&self.size
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
fn now (&self) -> &Arc<Pulse> {
|
||||||
&self.now
|
&self.now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for SequencerTui {
|
impl PhraseViewState for SequencerTui {
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
fn phrase_focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn keys (&self) -> &Buffer {
|
fn keys (&self) -> &Buffer {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
fn buffer (&self) -> &BigBuffer {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -231,27 +206,27 @@ impl PhraseViewState for SequencerTui {
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
fn now (&self) -> &Arc<Pulse> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for ArrangerTui {
|
impl PhraseViewState for ArrangerTui {
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
fn phrase_focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn keys (&self) -> &Buffer {
|
fn keys (&self) -> &Buffer {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
fn buffer (&self) -> &BigBuffer {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -264,73 +239,11 @@ impl PhraseViewState for ArrangerTui {
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
fn now (&self) -> &Arc<Pulse> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays and edits phrase length.
|
|
||||||
pub struct PhraseLength {
|
|
||||||
/// Pulses per beat (quaver)
|
|
||||||
pub ppq: usize,
|
|
||||||
/// Beats per bar
|
|
||||||
pub bpb: usize,
|
|
||||||
/// Length of phrase in pulses
|
|
||||||
pub pulses: usize,
|
|
||||||
/// Selected subdivision
|
|
||||||
pub focus: Option<PhraseLengthFocus>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseLength {
|
|
||||||
pub fn new (pulses: usize, focus: Option<PhraseLengthFocus>) -> Self {
|
|
||||||
Self { ppq: PPQ, bpb: 4, pulses, focus }
|
|
||||||
}
|
|
||||||
pub fn bars (&self) -> usize {
|
|
||||||
self.pulses / (self.bpb * self.ppq)
|
|
||||||
}
|
|
||||||
pub fn beats (&self) -> usize {
|
|
||||||
(self.pulses % (self.bpb * self.ppq)) / self.ppq
|
|
||||||
}
|
|
||||||
pub fn ticks (&self) -> usize {
|
|
||||||
self.pulses % self.ppq
|
|
||||||
}
|
|
||||||
pub fn bars_string (&self) -> String {
|
|
||||||
format!("{}", self.bars())
|
|
||||||
}
|
|
||||||
pub fn beats_string (&self) -> String {
|
|
||||||
format!("{}", self.beats())
|
|
||||||
}
|
|
||||||
pub fn ticks_string (&self) -> String {
|
|
||||||
format!("{:>02}", self.ticks())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Display mode of arranger
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub enum ArrangerMode {
|
|
||||||
/// Tracks are rows
|
|
||||||
Horizontal,
|
|
||||||
/// Tracks are columns
|
|
||||||
Vertical(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Arranger display mode can be cycled
|
|
||||||
impl ArrangerMode {
|
|
||||||
/// Cycle arranger display mode
|
|
||||||
pub fn to_next (&mut self) {
|
|
||||||
*self = match self {
|
|
||||||
Self::Horizontal => Self::Vertical(1),
|
|
||||||
Self::Vertical(1) => Self::Vertical(2),
|
|
||||||
Self::Vertical(2) => Self::Vertical(2),
|
|
||||||
Self::Vertical(0) => Self::Horizontal,
|
|
||||||
Self::Vertical(_) => Self::Vertical(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||||
let mut widths = vec![];
|
let mut widths = vec![];
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue