mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
flip it puside down
This commit is contained in:
parent
5550631254
commit
761ec78282
4 changed files with 42 additions and 53 deletions
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue