add clock to sequencer tracks

This commit is contained in:
🪞👃🪞 2024-10-26 18:46:21 +03:00
parent 67a5ea3a2b
commit 4f53058742
5 changed files with 42 additions and 22 deletions

View file

@ -46,6 +46,8 @@ pub enum ArrangerStatusBar {
}
/// Represents the tracks and scenes of the composition.
pub struct Arrangement<E: Engine> {
/// Global timebase
pub clock: Arc<TransportTime>,
/// Name of arranger
pub name: Arc<RwLock<String>>,
/// Collection of phrases.
@ -218,8 +220,9 @@ impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
}
/// General methods for arrangement
impl<E: Engine> Arrangement<E> {
pub fn new (name: &str, phrases: &Arc<RwLock<PhrasePool<E>>>) -> Self {
pub fn new (clock: &Arc<TransportTime>, name: &str, phrases: &Arc<RwLock<PhrasePool<E>>>) -> Self {
Self {
clock: clock.clone(),
name: Arc::new(RwLock::new(name.into())),
mode: ArrangementViewMode::Vertical(2),
selected: ArrangementFocus::Clip(0, 0),
@ -391,8 +394,8 @@ impl<E: Engine> Arrangement<E> {
&mut self, name: Option<&str>, color: Option<Color>
) -> Usually<&mut ArrangementTrack<E>> {
self.tracks.push(name.map_or_else(
|| ArrangementTrack::new(&self.track_default_name(), color),
|name| ArrangementTrack::new(name, color),
|| ArrangementTrack::new(&self.clock, &self.track_default_name(), color),
|name| ArrangementTrack::new(&self.clock, name, color),
));
let index = self.tracks.len() - 1;
Ok(&mut self.tracks[index])
@ -519,11 +522,11 @@ impl<E: Engine> Arrangement<E> {
}
}
impl<E: Engine> ArrangementTrack<E> {
pub fn new (name: &str, color: Option<Color>) -> Self {
pub fn new (clock: &Arc<TransportTime>, name: &str, color: Option<Color>) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
inputs: vec![],
player: PhrasePlayer::new(),
player: PhrasePlayer::new(clock),
outputs: vec![],
width: name.len() + 2,
color: color.unwrap_or_else(random_color)

View file

@ -21,9 +21,10 @@ impl ArrangerCli {
fn run (&self) -> Usually<()> {
let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
let jack = JackClient::Inactive(jack);
let transport = Arc::new(RwLock::new(TransportToolbar::new(None, Some(jack.transport()))));
let transport = TransportToolbar::new(None, Some(jack.transport()));
let phrases = Arc::new(RwLock::new(PhrasePool::new()));
let mut arrangement = Arrangement::new("", &phrases);
let mut arrangement = Arrangement::new(&transport.clock, "", &phrases);
let transport = Arc::new(RwLock::new(transport));
if let Some(name) = self.name.as_ref() {
*arrangement.name.write().unwrap() = name.clone();
}

View file

@ -122,7 +122,6 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let rows = Scene::ppqs(state.scenes.as_slice(), *factor);
let tracks = state.tracks.as_ref() as &[ArrangementTrack<Tui>];
let scenes = state.scenes.as_ref();
//panic!("{scenes:#?} {rows:#?}");
let offset = 3 + Scene::longest_name(scenes) as u16; // x of 1st track
let bg = state.color;
let clip_bg = Color::Rgb(40, 50, 30);
@ -131,6 +130,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let border_lo = Color::Rgb(70, 80, 50);
let border_fg = if self.0.focused { border_hi } else { border_lo };
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
let title_h = 3u16;
Layers::new(move |add|{
let rows: &[(usize, usize)] = rows.as_ref();
let cols: &[(usize, usize)] = cols.as_ref();
@ -167,8 +167,15 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let track_titles = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0))=>{
let name = track.name.read().unwrap();
let max_w = w.saturating_sub(1).min(name.len()).max(2);
(&format!("{}", &name[0..max_w]).as_str())
.min_xy(w as u16, 2)
let name = format!("{}", &name[0..max_w]);
let time1 = track.player.phrase.as_ref()
.map(|_|format!("{:>}", track.player.now.load(Ordering::Relaxed)))
.unwrap_or(String::from(""));
let time2 = track.player.switch_at.as_ref()
.map(|t|format!("{:>}", t.load(Ordering::Relaxed)))
.unwrap_or(String::from(""));
col!(name, time1, time2)
.min_xy(w as u16, title_h)
.bg(track.color)
.push_x(offset - 1)
});
@ -218,16 +225,22 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let focused = state.focused;
let selected = state.selected;
let get_track_area = |t: usize| [
offset + area.x() + cols[t].1 as u16 - 1, area.y(),
cols[t].0 as u16, area.h(),
offset + area.x() + cols[t].1 as u16 - 1,
area.y(),
cols[t].0 as u16,
area.h(),
];
let get_scene_area = |s: usize| [
area.x(), 2 + area.y() + (rows[s].1 / PPQ) as u16,
area.w(), (rows[s].0 / PPQ) as u16
area.x(),
title_h + area.y() + (rows[s].1 / PPQ) as u16,
area.w(),
(rows[s].0 / PPQ) as u16
];
let get_clip_area = |t: usize, s: usize| [
offset+area.x()+cols[t].1 as u16 - 1, 2+area.y()+(rows[s].1/PPQ) as u16,
cols[t].0 as u16, (rows[s].0 / PPQ) as u16
offset+area.x()+cols[t].1 as u16 - 1,
title_h + area.y() + (rows[s].1/PPQ) as u16,
cols[t].0 as u16,
(rows[s].0 / PPQ) as u16
];
let mut track_area: Option<[u16;4]> = None;
let mut scene_area: Option<[u16;4]> = None;

View file

@ -113,6 +113,8 @@ pub struct PhraseEditor<E: Engine> {
/// Phrase player.
pub struct PhrasePlayer<E: Engine> {
_engine: PhantomData<E>,
/// Global timebase
pub clock: Arc<TransportTime>,
/// Phrase being played
pub phrase: Option<Arc<RwLock<Phrase>>>,
/// Next phrase
@ -328,9 +330,10 @@ impl std::cmp::PartialEq for Phrase {
}
impl Eq for Phrase {}
impl<E: Engine> PhrasePlayer<E> {
pub fn new () -> Self {
pub fn new (clock: &Arc<TransportTime>) -> Self {
Self {
_engine: Default::default(),
clock: clock.clone(),
phrase: None,
next_phrase: None,
now: Arc::new(0.into()),

View file

@ -76,7 +76,6 @@ impl<E: Engine> TransportToolbar<E> {
clock: Option<&Arc<TransportTime>>,
transport: Option<Transport>,
) -> Self {
let timebase = Timebase::default();
Self {
_engine: Default::default(),
focused: false,
@ -88,6 +87,7 @@ impl<E: Engine> TransportToolbar<E> {
clock: if let Some(clock) = clock {
clock.clone()
} else {
let timebase = Timebase::default();
Arc::new(TransportTime {
playing: Some(TransportState::Stopped).into(),
quant: 24.into(),