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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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