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