wip(p61,e38)

This commit is contained in:
🪞👃🪞 2024-11-21 00:25:54 +01:00
parent 9d4fcaa32b
commit 76da19d9c6
12 changed files with 172 additions and 141 deletions

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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
}, },

View file

@ -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();

View file

@ -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>> {

View file

@ -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) {

View file

@ -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> {

View file

@ -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()),
}) })
} }
} }

View file

@ -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)),
}, },

View file

@ -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

View file

@ -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
} }
} }