flip it puside down

This commit is contained in:
🪞👃🪞 2024-12-10 20:16:06 +01:00
parent 5550631254
commit 761ec78282
4 changed files with 42 additions and 53 deletions

View file

@ -33,23 +33,6 @@ impl<T: HasClock> Command<T> for ClockCommand {
} }
} }
#[derive(Clone)]
pub struct Timeline {
pub timebase: Arc<Timebase>,
pub started: Arc<RwLock<Option<Moment>>>,
pub loopback: Arc<RwLock<Option<Moment>>>,
}
impl Default for Timeline {
fn default () -> Self {
Self {
timebase: Arc::new(Timebase::default()),
started: RwLock::new(None).into(),
loopback: RwLock::new(None).into(),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct ClockModel { pub struct ClockModel {
/// JACK transport handle. /// JACK transport handle.
@ -60,6 +43,8 @@ pub struct ClockModel {
pub global: Arc<Moment>, pub global: Arc<Moment>,
/// Global sample and usec at which playback started /// Global sample and usec at which playback started
pub started: Arc<RwLock<Option<Moment>>>, pub started: Arc<RwLock<Option<Moment>>>,
/// Playback offset (when playing not from start)
pub offset: Arc<Moment>,
/// Current playhead position /// Current playhead position
pub playhead: Arc<Moment>, pub playhead: Arc<Moment>,
/// Note quantization factor /// Note quantization factor
@ -83,6 +68,7 @@ impl From<&Arc<RwLock<JackClient>>> for ClockModel {
chunk: Arc::new((chunk as usize).into()), chunk: Arc::new((chunk as usize).into()),
global: Arc::new(Moment::zero(&timebase)), global: Arc::new(Moment::zero(&timebase)),
playhead: Arc::new(Moment::zero(&timebase)), playhead: Arc::new(Moment::zero(&timebase)),
offset: Arc::new(Moment::zero(&timebase)),
started: RwLock::new(None).into(), started: RwLock::new(None).into(),
timebase, timebase,
} }
@ -158,31 +144,34 @@ impl ClockModel {
self.chunk.store(n_frames, Ordering::Relaxed); self.chunk.store(n_frames, Ordering::Relaxed);
} }
pub fn update_from_scope (&self, scope: &ProcessScope) -> Usually<()> { pub fn update_from_scope (&self, scope: &ProcessScope) -> Usually<()> {
// Store buffer length
self.set_chunk(scope.n_frames() as usize); self.set_chunk(scope.n_frames() as usize);
// Store reported global frame and usec
let CycleTimes { current_frames, current_usecs, .. } = scope.cycle_times()?; let CycleTimes { current_frames, current_usecs, .. } = scope.cycle_times()?;
self.global.sample.set(current_frames as f64); self.global.sample.set(current_frames as f64);
self.global.usec.set(current_usecs as f64); self.global.usec.set(current_usecs as f64);
// If transport has just started or just stopped,
// update starting point:
let mut started = self.started.write().unwrap(); let mut started = self.started.write().unwrap();
match self.transport.query_state()? { match (self.transport.query_state()?, started.as_ref()) {
TransportState::Rolling => { (TransportState::Rolling, None) => {
if started.is_none() { let moment = Moment::zero(&self.timebase);
let moment = Moment::zero(&self.timebase); moment.sample.set(current_frames as f64);
moment.sample.set(current_frames as f64); moment.usec.set(current_usecs as f64);
moment.usec.set(current_usecs as f64); *started = Some(moment);
*started = Some(moment);
}
}, },
TransportState::Stopped => { (TransportState::Stopped, Some(_)) => {
if started.is_some() { *started = None;
*started = None;
}
}, },
_ => {} _ => {}
}; };
self.playhead.update_from_sample(match *started {
Some(ref instant) => current_frames as f64 - instant.sample.get(), self.playhead.update_from_sample(started.as_ref()
None => 0. .map(|started|current_frames as f64 - started.sample.get())
}); .unwrap_or(0.));
Ok(()) Ok(())
} }
} }

View file

@ -159,7 +159,7 @@ impl Phrase {
impl Default for Phrase { impl Default for Phrase {
fn default () -> Self { fn default () -> Self {
Self::new("(empty)", false, 0, None, Some(ItemColor::from(Color::Rgb(0, 0, 0)).into())) Self::new("null", false, 0, None, Some(ItemColor::from(Color::Rgb(0, 0, 0)).into()))
} }
} }

View file

@ -107,38 +107,38 @@ impl Audio for SequencerTui {
} }
} }
render!(|self: SequencerTui|lay!([ render!(|self: SequencerTui|lay!([self.size, Tui::split_up(1,
self.size, Tui::fill_xy(Tui::at_s(SequencerStatusBar::from(self))),
Tui::shrink_y(1, col!([ Tui::split_up(2,
TransportView::from((self, if let SequencerFocus::Transport(_) = self.focus.inner() { TransportView::from((self, if let SequencerFocus::Transport(_) = self.focus.inner() {
true true
} else { } else {
false false
})), })),
row!([ row!([
Tui::fixed_x(20, Tui::split_up(2, PhraseSelector::edit_phrase( Tui::fixed_x(20, Tui::split_up(4, col!([
&self.editor.phrase,
self.focused() == SequencerFocus::PhraseEditor,
self.entered()
), col!([
PhraseSelector::play_phrase(
&self.player,
self.focused() == SequencerFocus::PhrasePlay,
self.entered()
),
PhraseSelector::next_phrase( PhraseSelector::next_phrase(
&self.player, &self.player,
self.focused() == SequencerFocus::PhraseNext, self.focused() == SequencerFocus::PhraseNext,
self.entered() self.entered()
), ),
PhraseSelector::play_phrase(
&self.player,
self.focused() == SequencerFocus::PhrasePlay,
self.entered()
),
]), col!([
PhraseSelector::edit_phrase(
&self.editor.phrase,
self.focused() == SequencerFocus::PhraseEditor,
self.entered()
),
PhraseListView::from(self), PhraseListView::from(self),
]))), ]))),
PhraseView::from(self) PhraseView::from(self)
]) ])
])), )
Tui::fill_xy(Tui::at_s(SequencerStatusBar::from(self))), )]));
]));
impl HasClock for SequencerTui { impl HasClock for SequencerTui {
fn clock (&self) -> &ClockModel { fn clock (&self) -> &ClockModel {

View file

@ -270,13 +270,13 @@ fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option<Phra
key!(Down) => Cmd::Select( key!(Down) => Cmd::Select(
index.saturating_add(1) % state.phrases().len() index.saturating_add(1) % state.phrases().len()
), ),
key!(Char(',')) => if index > 1 { key!(Char('<')) => if index > 1 {
state.set_phrase_index(state.phrase_index().saturating_sub(1)); state.set_phrase_index(state.phrase_index().saturating_sub(1));
Cmd::Phrase(Pool::Swap(index - 1, index)) Cmd::Phrase(Pool::Swap(index - 1, index))
} else { } else {
return None return None
}, },
key!(Char('.')) => if index < count.saturating_sub(1) { key!(Char('>')) => if index < count.saturating_sub(1) {
state.set_phrase_index(state.phrase_index() + 1); state.set_phrase_index(state.phrase_index() + 1);
Cmd::Phrase(Pool::Swap(index + 1, index)) Cmd::Phrase(Pool::Swap(index + 1, index))
} else { } else {