mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
wip(p61,e38)
This commit is contained in:
parent
9d4fcaa32b
commit
76da19d9c6
12 changed files with 172 additions and 141 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait HasPlayer: JackApi {
|
pub trait HasPlayer: JackApi {
|
||||||
fn player (&self) -> &impl PlayerApi;
|
fn player (&self) -> &impl MidiPlayerApi;
|
||||||
fn player_mut (&mut self) -> &mut impl PlayerApi;
|
fn player_mut (&mut self) -> &mut impl MidiPlayerApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlayerApi: MidiInputApi + MidiOutputApi + Send + Sync {}
|
pub trait MidiPlayerApi: MidiInputApi + MidiOutputApi + Send + Sync {}
|
||||||
|
|
||||||
pub trait HasPhrase: PlayheadApi {
|
pub trait HasPhrase: PlayheadApi {
|
||||||
fn reset (&self) -> bool;
|
fn reset (&self) -> bool;
|
||||||
|
|
@ -293,7 +293,7 @@ pub fn update_keys (keys: &mut[bool;128], message: &MidiMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hosts the JACK callback for a single MIDI player
|
/// Hosts the JACK callback for a single MIDI player
|
||||||
pub struct PlayerAudio<'a, T: PlayerApi>(
|
pub struct PlayerAudio<'a, T: MidiPlayerApi>(
|
||||||
/// Player
|
/// Player
|
||||||
pub &'a mut T,
|
pub &'a mut T,
|
||||||
/// Note buffer
|
/// Note buffer
|
||||||
|
|
@ -303,7 +303,7 @@ pub struct PlayerAudio<'a, T: PlayerApi>(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// JACK process callback for a sequencer's phrase player/recorder.
|
/// JACK process callback for a sequencer's phrase player/recorder.
|
||||||
impl<'a, T: PlayerApi> Audio for PlayerAudio<'a, T> {
|
impl<'a, T: MidiPlayerApi> Audio for PlayerAudio<'a, T> {
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
let model = &mut self.0;
|
let model = &mut self.0;
|
||||||
let note_buf = &mut self.1;
|
let note_buf = &mut self.1;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub enum ArrangerTrackCommand {
|
||||||
SetZoom(usize),
|
SetZoom(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArrangerTrackApi: PlayerApi + Send + Sync + Sized {
|
pub trait ArrangerTrackApi: MidiPlayerApi + Send + Sync + Sized {
|
||||||
/// Name of track
|
/// Name of track
|
||||||
fn name (&self) -> &Arc<RwLock<String>>;
|
fn name (&self) -> &Arc<RwLock<String>>;
|
||||||
/// Preferred width of track column
|
/// Preferred width of track column
|
||||||
|
|
|
||||||
|
|
@ -50,34 +50,41 @@ pub struct ScaledAxis<T> {
|
||||||
|
|
||||||
macro_rules! impl_axis_common { ($A:ident $T:ty) => {
|
macro_rules! impl_axis_common { ($A:ident $T:ty) => {
|
||||||
impl $A<$T> {
|
impl $A<$T> {
|
||||||
#[inline] pub fn start_plus (&mut self, n: $T) -> $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))
|
(self.start + n).min(self.clamp.unwrap_or(<$T>::MAX))
|
||||||
}
|
}
|
||||||
#[inline] pub fn start_inc (&mut self, n: $T) -> $T {
|
#[inline] pub fn start_inc (&mut self, n: $T) -> $T {
|
||||||
self.start = self.start_plus(n);
|
self.start_set(self.start_plus(n))
|
||||||
self.start
|
|
||||||
}
|
}
|
||||||
#[inline] pub fn start_minus (&mut self, n: $T) -> $T {
|
#[inline] pub fn start_minus (&self, n: $T) -> $T {
|
||||||
self.start.saturating_sub(n)
|
self.start.saturating_sub(n)
|
||||||
}
|
}
|
||||||
#[inline] pub fn start_dec (&mut self, n: $T) -> $T {
|
#[inline] pub fn start_dec (&mut self, n: $T) -> $T {
|
||||||
self.start = self.start_minus(n);
|
self.start = self.start_minus(n);
|
||||||
self.start
|
self.start
|
||||||
}
|
}
|
||||||
#[inline] pub fn point_plus (&mut self, n: $T) -> Option<$T> {
|
|
||||||
|
#[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)))
|
self.point.map(|p|(p + n).min(self.clamp.unwrap_or(<$T>::MAX)))
|
||||||
}
|
}
|
||||||
#[inline] pub fn point_inc (&mut self, n: $T) -> Option<$T> {
|
#[inline] pub fn point_inc (&mut self, n: $T) -> Option<$T> {
|
||||||
self.point = self.point_plus(n);
|
self.point_set(self.point_plus(n))
|
||||||
self.point
|
|
||||||
}
|
}
|
||||||
#[inline] pub fn point_minus (&mut self, n: $T) -> Option<$T> {
|
#[inline] pub fn point_minus (&self, n: $T) -> Option<$T> {
|
||||||
self.point.map(|p|p.saturating_sub(n))
|
self.point.map(|p|p.saturating_sub(n))
|
||||||
}
|
}
|
||||||
#[inline] pub fn point_dec (&mut self, n: $T) -> Option<$T> {
|
#[inline] pub fn point_dec (&mut self, n: $T) -> Option<$T> {
|
||||||
self.point = self.point_minus(n);
|
self.point_set(self.point_minus(n))
|
||||||
self.point
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
impl_axis_common!(FixedAxis u16);
|
impl_axis_common!(FixedAxis u16);
|
||||||
|
|
@ -85,6 +92,13 @@ impl_axis_common!(FixedAxis usize);
|
||||||
impl_axis_common!(ScaledAxis u16);
|
impl_axis_common!(ScaledAxis u16);
|
||||||
impl_axis_common!(ScaledAxis usize);
|
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]
|
// TODO: return impl Point and impl Size instead of [N;x]
|
||||||
// to disambiguate between usage of 2-"tuple"s
|
// to disambiguate between usage of 2-"tuple"s
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pub enum SequencerCommand {
|
||||||
|
|
||||||
impl<T> Command<T> for SequencerCommand
|
impl<T> Command<T> for SequencerCommand
|
||||||
where
|
where
|
||||||
T: PhrasesControl + PhraseControl + PlayheadApi
|
T: PhrasesControl + PhraseEditorControl + PlayheadApi
|
||||||
+ FocusGrid<Item = SequencerFocus> + FocusEnter<Item = SequencerFocus>
|
+ FocusGrid<Item = SequencerFocus> + FocusEnter<Item = SequencerFocus>
|
||||||
{
|
{
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
|
|
@ -93,9 +93,10 @@ impl Command<ArrangerTui> for ArrangerCommand {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
EditPhrase(phrase) => {
|
EditPhrase(phrase) => {
|
||||||
state.show_phrase(phrase);
|
state.edit_phrase(&phrase);
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
|
_ => { todo!() }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +186,7 @@ impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
|
||||||
Tick => { *length = length.saturating_sub(1) },
|
Tick => { *length = length.saturating_sub(1) },
|
||||||
},
|
},
|
||||||
Self::Set(length) => {
|
Self::Set(length) => {
|
||||||
let mut phrase = state.phrases[phrase].write().unwrap();
|
let mut phrase = state.phrases()[*phrase].write().unwrap();
|
||||||
let old_length = phrase.length;
|
let old_length = phrase.length;
|
||||||
phrase.length = length;
|
phrase.length = length;
|
||||||
*mode = None;
|
*mode = None;
|
||||||
|
|
@ -217,10 +218,13 @@ where
|
||||||
{
|
{
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseRenameCommand::*;
|
use PhraseRenameCommand::*;
|
||||||
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.phrases_mode_mut() {
|
if let Some(PhrasesMode::Rename(
|
||||||
|
phrase,
|
||||||
|
ref mut old_name
|
||||||
|
)) = state.phrases_mode_mut().clone() {
|
||||||
match self {
|
match self {
|
||||||
Set(s) => {
|
Set(s) => {
|
||||||
state.phrases()[*phrase].write().unwrap().name = s.into();
|
state.phrases()[phrase].write().unwrap().name = s.into();
|
||||||
return Ok(Some(Self::Set(old_name.clone())))
|
return Ok(Some(Self::Set(old_name.clone())))
|
||||||
},
|
},
|
||||||
Confirm => {
|
Confirm => {
|
||||||
|
|
@ -229,7 +233,7 @@ where
|
||||||
return Ok(Some(Self::Set(old_name)))
|
return Ok(Some(Self::Set(old_name)))
|
||||||
},
|
},
|
||||||
Cancel => {
|
Cancel => {
|
||||||
state.phrases()[*phrase].write().unwrap().name = old_name.clone();
|
state.phrases()[phrase].write().unwrap().name = old_name.clone();
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
|
|
@ -251,17 +255,17 @@ pub enum PhraseCommand {
|
||||||
ExitEditMode,
|
ExitEditMode,
|
||||||
NoteAppend,
|
NoteAppend,
|
||||||
NoteSet,
|
NoteSet,
|
||||||
NoteCursorSet(usize),
|
NoteCursorSet(Option<usize>),
|
||||||
NoteLengthSet(usize),
|
NoteLengthSet(usize),
|
||||||
NoteScrollSet(usize),
|
NoteScrollSet(usize),
|
||||||
TimeCursorSet(usize),
|
TimeCursorSet(Option<usize>),
|
||||||
TimeScrollSet(usize),
|
TimeScrollSet(usize),
|
||||||
TimeZoomSet(usize),
|
TimeZoomSet(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Command<T> for PhraseCommand
|
impl<T> Command<T> for PhraseCommand
|
||||||
where
|
where
|
||||||
T: PhraseControl + FocusEnter
|
T: PhraseEditorControl + FocusEnter
|
||||||
{
|
{
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
|
|
@ -270,24 +274,28 @@ where
|
||||||
EnterEditMode => { state.focus_enter(); None },
|
EnterEditMode => { state.focus_enter(); None },
|
||||||
ExitEditMode => { state.focus_exit(); None },
|
ExitEditMode => { state.focus_exit(); None },
|
||||||
NoteAppend => {
|
NoteAppend => {
|
||||||
if state.phrase_entered() {
|
if state.phrase_editor_entered() {
|
||||||
state.put_note();
|
state.put_note();
|
||||||
state.time_cursor_advance();
|
state.time_cursor_advance();
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
NoteSet => { if state.phrase_entered() { state.put_note(); } None },
|
NoteSet => { if state.phrase_editor_entered() { state.put_note(); } None },
|
||||||
TimeCursorSet(time) => { state.time_axis().write().unwrap().point_set(time); None },
|
TimeCursorSet(time) => { state.time_axis().write().unwrap().point_set(time); None },
|
||||||
TimeScrollSet(time) => { state.time_axis().write().unwrap().start_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 },
|
TimeZoomSet(zoom) => { state.time_axis().write().unwrap().scale_set(zoom); None },
|
||||||
NoteScrollSet(note) => { state.note_axis().write().unwrap().start_set(note); None },
|
NoteScrollSet(note) => { state.note_axis().write().unwrap().start_set(note); None },
|
||||||
NoteLengthSet(time) => { *state.note_len_mut() = time; None },
|
NoteLengthSet(time) => { *state.note_len_mut() = time; None },
|
||||||
NoteCursorSet(note) => {
|
NoteCursorSet(note) => {
|
||||||
let axis = state.note_axis().write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.point_set(note);
|
axis.point_set(note);
|
||||||
if let Some(point) = axis.point {
|
if let Some(point) = axis.point {
|
||||||
if point > 73 { axis.point = Some(73); }
|
if point > 73 {
|
||||||
if point < axis.start { axis.start = (point / 2) * 2; }
|
axis.point = Some(73);
|
||||||
|
}
|
||||||
|
if point < axis.start {
|
||||||
|
axis.start = (point / 2) * 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> {
|
||||||
let entered = self.0.entered();
|
let entered = self.0.entered();
|
||||||
let phrases = self.0.phrases();
|
let phrases = self.0.phrases();
|
||||||
let selected_phrase = self.0.phrase();
|
let selected_phrase = self.0.phrase();
|
||||||
let mode = self.0.mode();
|
let mode = self.0.phrase_mode();
|
||||||
let content = col!(
|
let content = col!(
|
||||||
(i, phrase) in phrases.iter().enumerate() => Layers::new(|add|{
|
(i, phrase) in phrases.iter().enumerate() => Layers::new(|add|{
|
||||||
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ pub trait SequencerControl: TransportControl {}
|
||||||
pub trait ArrangerControl: TransportControl {
|
pub trait ArrangerControl: TransportControl {
|
||||||
fn selected (&self) -> ArrangerSelection;
|
fn selected (&self) -> ArrangerSelection;
|
||||||
fn selected_mut (&mut self) -> &mut ArrangerSelection;
|
fn selected_mut (&mut self) -> &mut ArrangerSelection;
|
||||||
fn show_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>);
|
|
||||||
fn activate (&mut self);
|
fn activate (&mut self);
|
||||||
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>;
|
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>;
|
||||||
fn toggle_loop (&mut self);
|
fn toggle_loop (&mut self);
|
||||||
|
|
@ -51,13 +50,10 @@ pub trait PhrasesControl: HasPhrases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasPhrasesModel: HasPhrases {
|
pub trait PhraseEditorControl {
|
||||||
fn phrases_model (&self) -> &PhrasesModel;
|
fn edit_phrase (&self, phrase: &Option<Arc<RwLock<Phrase>>>);
|
||||||
fn phrases_model_mut (&mut self) -> &mut PhrasesModel;
|
fn editing_phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||||
}
|
fn phrase_editor_entered (&self) -> bool;
|
||||||
|
|
||||||
pub trait PhraseControl {
|
|
||||||
fn phrase_entered (&self) -> bool;
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
||||||
fn note_len (&self) -> usize;
|
fn note_len (&self) -> usize;
|
||||||
|
|
@ -65,7 +61,7 @@ pub trait PhraseControl {
|
||||||
fn put_note (&mut self);
|
fn put_note (&mut self);
|
||||||
fn time_cursor_advance (&self) {
|
fn time_cursor_advance (&self) {
|
||||||
let point = self.time_axis().read().unwrap().point;
|
let point = self.time_axis().read().unwrap().point;
|
||||||
let length = self.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
let length = self.editing_phrase().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||||
let forward = |time|(time + self.note_len()) % length;
|
let forward = |time|(time + self.note_len()) % length;
|
||||||
self.time_axis().write().unwrap().point = point.map(forward);
|
self.time_axis().write().unwrap().point = point.map(forward);
|
||||||
}
|
}
|
||||||
|
|
@ -86,17 +82,11 @@ impl ArrangerControl for ArrangerTui {
|
||||||
fn selected_mut (&mut self) -> &mut ArrangerSelection {
|
fn selected_mut (&mut self) -> &mut ArrangerSelection {
|
||||||
&mut self.selected
|
&mut self.selected
|
||||||
}
|
}
|
||||||
fn show_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>) {
|
|
||||||
self.editor.show(self.selected_phrase().as_ref());
|
|
||||||
//state.editor.phrase = phrase.clone();
|
|
||||||
//state.focus(ArrangerFocus::PhraseEditor);
|
|
||||||
//state.focus_enter();
|
|
||||||
}
|
|
||||||
fn activate (&mut self) {
|
fn activate (&mut self) {
|
||||||
if let ArrangerSelection::Scene(s) = self.selected {
|
if let ArrangerSelection::Scene(s) = self.selected {
|
||||||
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
|
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
|
||||||
let clip = self.scenes()[s].clips[t].as_ref();
|
let clip = self.scenes()[s].clips[t].as_ref();
|
||||||
if track.play_phrase.is_some() || clip.is_some() {
|
if track.player.play_phrase.is_some() || clip.is_some() {
|
||||||
track.enqueue_next(clip);
|
track.enqueue_next(clip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,41 +127,48 @@ impl ArrangerControl for ArrangerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasPhrasesModel for ArrangerTui {
|
impl PhrasesControl for SequencerTui {
|
||||||
fn phrases_model (&self) -> &PhrasesModel {
|
|
||||||
&self.phrases
|
|
||||||
}
|
|
||||||
fn phrases_model_mut (&mut self) -> &mut PhrasesModel {
|
|
||||||
&mut self.phrases
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasPhrasesModel for SequencerTui {
|
|
||||||
fn phrases_model (&self) -> &PhrasesModel {
|
|
||||||
&self.phrases
|
|
||||||
}
|
|
||||||
fn phrases_model_mut (&mut self) -> &mut PhrasesModel {
|
|
||||||
&mut self.phrases
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: HasPhrasesModel> PhrasesControl for T {
|
|
||||||
fn phrase_index (&self) -> usize {
|
fn phrase_index (&self) -> usize {
|
||||||
self.phrases_model().phrase.load(Ordering::Relaxed)
|
self.phrases.phrase.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
fn set_phrase_index (&self, value: usize) {
|
fn set_phrase_index (&self, value: usize) {
|
||||||
self.phrases_model().phrase.store(value, Ordering::Relaxed);
|
self.phrases.phrase.store(value, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.phrases_model().mode
|
&self.phrases.mode
|
||||||
}
|
}
|
||||||
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
||||||
&mut self.phrases_model_mut().mode
|
&mut self.phrases.mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseControl for SequencerTui {
|
impl PhrasesControl for ArrangerTui {
|
||||||
fn phrase_entered (&self) -> bool {
|
fn phrase_index (&self) -> usize {
|
||||||
|
self.phrases.phrase.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
fn set_phrase_index (&self, value: usize) {
|
||||||
|
self.phrases.phrase.store(value, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
||||||
|
&self.phrases.mode
|
||||||
|
}
|
||||||
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
||||||
|
&mut self.phrases.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhraseEditorControl for SequencerTui {
|
||||||
|
fn edit_phrase (&self, phrase: &Option<Arc<RwLock<Phrase>>>) {
|
||||||
|
//self.editor.show(self.selected_phrase().as_ref());
|
||||||
|
//state.editor.phrase = phrase.clone();
|
||||||
|
//state.focus(ArrangerFocus::PhraseEditor);
|
||||||
|
//state.focus_enter();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn editing_phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrase_editor_entered (&self) -> bool {
|
||||||
self.entered && self.focused() == SequencerFocus::PhraseEditor
|
self.entered && self.focused() == SequencerFocus::PhraseEditor
|
||||||
}
|
}
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
|
|
@ -191,8 +188,14 @@ impl PhraseControl for SequencerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseControl for ArrangerTui {
|
impl PhraseEditorControl for ArrangerTui {
|
||||||
fn phrase_entered (&self) -> bool {
|
fn edit_phrase (&self, phrase: &Option<Arc<RwLock<Phrase>>>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn editing_phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrase_editor_entered (&self) -> bool {
|
||||||
self.entered && self.focused() == ArrangerFocus::PhraseEditor
|
self.entered && self.focused() == ArrangerFocus::PhraseEditor
|
||||||
}
|
}
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub enum SequencerFocus {
|
||||||
/// The transport (toolbar) is focused
|
/// The transport (toolbar) is focused
|
||||||
Transport,
|
Transport,
|
||||||
/// The phrase list (pool) is focused
|
/// The phrase list (pool) is focused
|
||||||
PhrasePool,
|
Phrases,
|
||||||
/// The phrase editor (sequencer) is focused
|
/// The phrase editor (sequencer) is focused
|
||||||
PhraseEditor,
|
PhraseEditor,
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ pub enum ArrangerFocus {
|
||||||
/// The arrangement (grid) is focused
|
/// The arrangement (grid) is focused
|
||||||
Arranger,
|
Arranger,
|
||||||
/// The phrase list (pool) is focused
|
/// The phrase list (pool) is focused
|
||||||
PhrasePool,
|
Phrases,
|
||||||
/// The phrase editor (sequencer) is focused
|
/// The phrase editor (sequencer) is focused
|
||||||
PhraseEditor,
|
PhraseEditor,
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +150,7 @@ impl FocusGrid for SequencerTui {
|
||||||
&[
|
&[
|
||||||
&[Menu, Menu ],
|
&[Menu, Menu ],
|
||||||
&[Transport, Transport ],
|
&[Transport, Transport ],
|
||||||
&[PhrasePool, PhraseEditor],
|
&[Phrases, PhraseEditor],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
fn focus_update (&mut self) {
|
fn focus_update (&mut self) {
|
||||||
|
|
@ -161,20 +161,22 @@ impl FocusGrid for SequencerTui {
|
||||||
impl FocusEnter for ArrangerTui {
|
impl FocusEnter for ArrangerTui {
|
||||||
type Item = ArrangerFocus;
|
type Item = ArrangerFocus;
|
||||||
fn focus_enter (&mut self) {
|
fn focus_enter (&mut self) {
|
||||||
use ArrangerFocus::*;
|
self.entered = true;
|
||||||
let focused = self.focused();
|
//use ArrangerFocus::*;
|
||||||
if !self.entered {
|
//let focused = self.focused();
|
||||||
self.entered = focused == Arranger;
|
//if !self.entered {
|
||||||
self.editor.entered = focused == PhraseEditor;
|
//self.entered = focused == Arranger;
|
||||||
self.phrases.entered = focused == PhrasePool;
|
//self.editor.entered = focused == PhraseEditor;
|
||||||
}
|
//self.phrases.entered = focused == Phrases;
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
fn focus_exit (&mut self) {
|
fn focus_exit (&mut self) {
|
||||||
if self.entered {
|
self.entered = false;
|
||||||
self.entered = false;
|
//if self.entered {
|
||||||
self.editor.entered = false;
|
//self.entered = false;
|
||||||
self.phrases.entered = false;
|
//self.editor.entered = false;
|
||||||
}
|
//self.phrases.entered = false;
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
fn focus_entered (&self) -> Option<Self::Item> {
|
fn focus_entered (&self) -> Option<Self::Item> {
|
||||||
if self.entered {
|
if self.entered {
|
||||||
|
|
@ -200,7 +202,7 @@ impl FocusGrid for ArrangerTui {
|
||||||
&[Menu, Menu ],
|
&[Menu, Menu ],
|
||||||
&[Transport, Transport ],
|
&[Transport, Transport ],
|
||||||
&[Arranger, Arranger ],
|
&[Arranger, Arranger ],
|
||||||
&[PhrasePool, PhraseEditor],
|
&[Phrases, PhraseEditor],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
fn focus_update (&mut self) {
|
fn focus_update (&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ impl_has_phrases!(ArrangerTui::phrases);
|
||||||
impl_has_phrase!(SequencerTui::player);
|
impl_has_phrase!(SequencerTui::player);
|
||||||
impl_has_phrase!(ArrangerTrack::player);
|
impl_has_phrase!(ArrangerTrack::player);
|
||||||
impl_has_phrase!(PhrasePlayerModel);
|
impl_has_phrase!(PhrasePlayerModel);
|
||||||
|
impl_midi_player!(ArrangerTrack);
|
||||||
|
|
||||||
impl HasScenes<ArrangerScene> for ArrangerTui {
|
impl HasScenes<ArrangerScene> for ArrangerTui {
|
||||||
fn scenes (&self) -> &Vec<ArrangerScene> {
|
fn scenes (&self) -> &Vec<ArrangerScene> {
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cursor: (0, 0),
|
|
||||||
state: TransportModel::from(jack.read().unwrap().transport()),
|
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
|
state: TransportModel::from(jack.read().unwrap().transport()),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
|
cursor: (0, 0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,17 +17,15 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cursor: (0, 0),
|
|
||||||
entered: false,
|
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
phrases: vec![],
|
|
||||||
phrases_mode: None,
|
|
||||||
size: Measure::new(),
|
|
||||||
split: 20,
|
|
||||||
view_phrase: 0,
|
|
||||||
transport: TransportModel::from(jack.read().unwrap().transport()),
|
transport: TransportModel::from(jack.read().unwrap().transport()),
|
||||||
|
phrases: PhrasesModel::default(),
|
||||||
player: PhrasePlayerModel::default(),
|
player: PhrasePlayerModel::default(),
|
||||||
editor: PhraseEditorModel::default(),
|
editor: PhraseEditorModel::default(),
|
||||||
|
size: Measure::new(),
|
||||||
|
cursor: (0, 0),
|
||||||
|
entered: false,
|
||||||
|
split: 20,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,26 +34,24 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
color: Color::Rgb(28, 35, 25).into(),
|
|
||||||
cursor: (0, 0),
|
|
||||||
entered: false,
|
|
||||||
history: vec![],
|
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
menu_bar: None,
|
transport: TransportModel::from(jack.read().unwrap().transport()),
|
||||||
|
phrases: PhrasesModel::default(),
|
||||||
|
selected: ArrangerSelection::Clip(0, 0),
|
||||||
|
scenes: vec![],
|
||||||
|
tracks: vec![],
|
||||||
|
color: Color::Rgb(28, 35, 25).into(),
|
||||||
|
history: vec![],
|
||||||
midi_buf: vec![],
|
midi_buf: vec![],
|
||||||
|
note_buf: vec![],
|
||||||
mode: ArrangerMode::Vertical(2),
|
mode: ArrangerMode::Vertical(2),
|
||||||
name: Arc::new(RwLock::new(String::new())),
|
name: Arc::new(RwLock::new(String::new())),
|
||||||
note_buf: vec![],
|
|
||||||
phrase: 0,
|
|
||||||
phrases: vec![],
|
|
||||||
phrases_mode: None,
|
|
||||||
scenes: vec![],
|
|
||||||
selected: ArrangerSelection::Clip(0, 0),
|
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
|
cursor: (0, 0),
|
||||||
splits: [20, 20],
|
splits: [20, 20],
|
||||||
|
entered: false,
|
||||||
|
menu_bar: None,
|
||||||
status_bar: None,
|
status_bar: None,
|
||||||
tracks: vec![],
|
|
||||||
transport: TransportModel::from(jack.read().unwrap().transport()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ where
|
||||||
|
|
||||||
impl<T> InputToCommand<Tui, T> for SequencerCommand
|
impl<T> InputToCommand<Tui, T> for SequencerCommand
|
||||||
where
|
where
|
||||||
T: SequencerControl + TransportControl + PhrasesControl + PhraseControl + PlayheadApi
|
T: SequencerControl + TransportControl + PhrasesControl + PhraseEditorControl + PlayheadApi
|
||||||
+ HasFocus<Item = SequencerFocus>
|
+ HasFocus<Item = SequencerFocus>
|
||||||
+ FocusGrid<Item = SequencerFocus>
|
+ FocusGrid<Item = SequencerFocus>
|
||||||
+ FocusEnter<Item = SequencerFocus>
|
+ FocusEnter<Item = SequencerFocus>
|
||||||
|
|
@ -83,12 +83,10 @@ where
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SequencerFocus::Phrases => {
|
SequencerFocus::Phrases =>
|
||||||
PhrasesCommand::input_to_command(state, input).map(Phrases)
|
Phrases(PhrasesCommand::input_to_command(state, input)?),
|
||||||
},
|
SequencerFocus::PhraseEditor =>
|
||||||
SequencerFocus::PhraseEditor => {
|
Editor(PhraseCommand::input_to_command(state, input)?),
|
||||||
PhraseCommand::input_to_command(state, input).map(Editor)
|
|
||||||
},
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +94,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
||||||
fn input_to_command (state: &ArrangerCommand, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &ArrangerTui, input: &TuiInput) -> Option<Self> {
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
Some(match input.event() {
|
Some(match input.event() {
|
||||||
|
|
@ -126,13 +124,9 @@ impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
||||||
ArrangerFocus::PhraseEditor => Editor(
|
ArrangerFocus::PhraseEditor => Editor(
|
||||||
PhraseCommand::input_to_command(state, input)?
|
PhraseCommand::input_to_command(state, input)?
|
||||||
),
|
),
|
||||||
ArrangerFocus::PhrasePool => match input.event() {
|
ArrangerFocus::Phrases => match input.event() {
|
||||||
key!(KeyCode::Char('e')) => EditPhrase(
|
key!(KeyCode::Char('e')) => EditPhrase(Some(state.phrase().clone())),
|
||||||
Some(state.phrase().clone())
|
_ => Phrases(PhrasePoolCommand::input_to_command(state, input)?)
|
||||||
),
|
|
||||||
_ => Phrases(
|
|
||||||
PhrasePoolCommand::input_to_command(state, input)?
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
ArrangerFocus::Arranger => {
|
ArrangerFocus::Arranger => {
|
||||||
use ArrangerSelection as Select;
|
use ArrangerSelection as Select;
|
||||||
|
|
@ -369,7 +363,7 @@ impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseRenameCommand {
|
||||||
|
|
||||||
impl<T> InputToCommand<Tui, T> for PhraseCommand
|
impl<T> InputToCommand<Tui, T> for PhraseCommand
|
||||||
where
|
where
|
||||||
T: PhraseControl + FocusEnter
|
T: PhraseEditorControl + FocusEnter
|
||||||
{
|
{
|
||||||
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
|
|
@ -385,27 +379,27 @@ where
|
||||||
key!(KeyCode::Char('_')) => TimeZoomSet(next_note_length(state.time_axis().read().unwrap().scale)),
|
key!(KeyCode::Char('_')) => TimeZoomSet(next_note_length(state.time_axis().read().unwrap().scale)),
|
||||||
key!(KeyCode::Char('=')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
|
key!(KeyCode::Char('=')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
|
||||||
key!(KeyCode::Char('+')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
|
key!(KeyCode::Char('+')) => TimeZoomSet(prev_note_length(state.time_axis().read().unwrap().scale)),
|
||||||
key!(KeyCode::Up) => match state.phrase_entered() {
|
key!(KeyCode::Up) => match state.phrase_editor_entered() {
|
||||||
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(1)),
|
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(1)),
|
||||||
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(1)),
|
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(1)),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Down) => match state.phrase_entered() {
|
key!(KeyCode::Down) => match state.phrase_editor_entered() {
|
||||||
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(1)),
|
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(1)),
|
||||||
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(1)),
|
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(1)),
|
||||||
},
|
},
|
||||||
key!(KeyCode::PageUp) => match state.phrase_entered() {
|
key!(KeyCode::PageUp) => match state.phrase_editor_entered() {
|
||||||
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(3)),
|
true => NoteCursorSet(state.note_axis().write().unwrap().point_plus(3)),
|
||||||
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(3)),
|
false => NoteScrollSet(state.note_axis().write().unwrap().start_plus(3)),
|
||||||
},
|
},
|
||||||
key!(KeyCode::PageDown) => match state.phrase_entered() {
|
key!(KeyCode::PageDown) => match state.phrase_editor_entered() {
|
||||||
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(3)),
|
true => NoteCursorSet(state.note_axis().write().unwrap().point_minus(3)),
|
||||||
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(3)),
|
false => NoteScrollSet(state.note_axis().write().unwrap().start_minus(3)),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Left) => match state.phrase_entered() {
|
key!(KeyCode::Left) => match state.phrase_editor_entered() {
|
||||||
true => TimeCursorSet(state.note_axis().write().unwrap().point_minus(1)),
|
true => TimeCursorSet(state.note_axis().write().unwrap().point_minus(1)),
|
||||||
false => TimeScrollSet(state.note_axis().write().unwrap().start_minus(1)),
|
false => TimeScrollSet(state.note_axis().write().unwrap().start_minus(1)),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Right) => match state.phrase_entered() {
|
key!(KeyCode::Right) => match state.phrase_editor_entered() {
|
||||||
true => TimeCursorSet(state.note_axis().write().unwrap().point_plus(1)),
|
true => TimeCursorSet(state.note_axis().write().unwrap().point_plus(1)),
|
||||||
false => TimeScrollSet(state.note_axis().write().unwrap().start_plus(1)),
|
false => TimeScrollSet(state.note_axis().write().unwrap().start_plus(1)),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,19 @@ pub struct PhrasesModel {
|
||||||
pub(crate) entered: bool,
|
pub(crate) entered: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for PhrasesModel {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self {
|
||||||
|
phrases: vec![RwLock::new(Phrase::default()).into()],
|
||||||
|
phrase: 0.into(),
|
||||||
|
scroll: 0,
|
||||||
|
mode: None,
|
||||||
|
focused: false,
|
||||||
|
entered: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct ArrangerScene {
|
pub struct ArrangerScene {
|
||||||
/// Name of scene
|
/// Name of scene
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub trait PhrasesViewState: Send + Sync {
|
||||||
fn entered (&self) -> bool;
|
fn entered (&self) -> bool;
|
||||||
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>>;
|
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>>;
|
||||||
fn phrase (&self) -> usize;
|
fn phrase (&self) -> usize;
|
||||||
fn mode (&self) -> &Option<PhrasesMode>;
|
fn phrase_mode (&self) -> &Option<PhrasesMode>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PhraseViewState: Send + Sync {
|
pub trait PhraseViewState: Send + Sync {
|
||||||
|
|
@ -105,7 +105,7 @@ impl PhrasesViewState for PhrasesModel {
|
||||||
fn phrase (&self) -> usize {
|
fn phrase (&self) -> usize {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn mode (&self) -> &Option<PhrasesMode> {
|
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.mode
|
&self.mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,8 +123,8 @@ impl PhrasesViewState for SequencerTui {
|
||||||
fn phrase (&self) -> usize {
|
fn phrase (&self) -> usize {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn mode (&self) -> &Option<PhrasesMode> {
|
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.phrases_mode
|
&self.phrases.mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,8 +141,8 @@ impl PhrasesViewState for ArrangerTui {
|
||||||
fn phrase (&self) -> usize {
|
fn phrase (&self) -> usize {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn mode (&self) -> &Option<PhrasesMode> {
|
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.phrases_mode
|
&self.phrases.mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue