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

View file

@ -19,11 +19,12 @@ pub struct ArrangerCli {
impl ArrangerCli { impl ArrangerCli {
/// Run the arranger TUI from CLI arguments. /// Run the arranger TUI from CLI arguments.
fn run (&self) -> Usually<()> { fn run (&self) -> Usually<()> {
let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0; let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
let jack = JackClient::Inactive(jack); 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 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() { if let Some(name) = self.name.as_ref() {
*arrangement.name.write().unwrap() = name.clone(); *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 rows = Scene::ppqs(state.scenes.as_slice(), *factor);
let tracks = state.tracks.as_ref() as &[ArrangementTrack<Tui>]; let tracks = state.tracks.as_ref() as &[ArrangementTrack<Tui>];
let scenes = state.scenes.as_ref(); let scenes = state.scenes.as_ref();
//panic!("{scenes:#?} {rows:#?}");
let offset = 3 + Scene::longest_name(scenes) as u16; // x of 1st track let offset = 3 + Scene::longest_name(scenes) as u16; // x of 1st track
let bg = state.color; let bg = state.color;
let clip_bg = Color::Rgb(40, 50, 30); 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_lo = Color::Rgb(70, 80, 50);
let border_fg = if self.0.focused { border_hi } else { border_lo }; let border_fg = if self.0.focused { border_hi } else { border_lo };
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
let title_h = 3u16;
Layers::new(move |add|{ Layers::new(move |add|{
let rows: &[(usize, usize)] = rows.as_ref(); let rows: &[(usize, usize)] = rows.as_ref();
let cols: &[(usize, usize)] = cols.as_ref(); let cols: &[(usize, usize)] = cols.as_ref();
@ -165,10 +165,17 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
// track titles // track titles
let track_titles = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0))=>{ let track_titles = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0))=>{
let name = track.name.read().unwrap(); let name = track.name.read().unwrap();
let max_w = w.saturating_sub(1).min(name.len()).max(2); let max_w = w.saturating_sub(1).min(name.len()).max(2);
(&format!("{}", &name[0..max_w]).as_str()) let name = format!("{}", &name[0..max_w]);
.min_xy(w as u16, 2) 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) .bg(track.color)
.push_x(offset - 1) .push_x(offset - 1)
}); });
@ -218,16 +225,22 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let focused = state.focused; let focused = state.focused;
let selected = state.selected; let selected = state.selected;
let get_track_area = |t: usize| [ let get_track_area = |t: usize| [
offset + area.x() + cols[t].1 as u16 - 1, area.y(), offset + area.x() + cols[t].1 as u16 - 1,
cols[t].0 as u16, area.h(), area.y(),
cols[t].0 as u16,
area.h(),
]; ];
let get_scene_area = |s: usize| [ let get_scene_area = |s: usize| [
area.x(), 2 + area.y() + (rows[s].1 / PPQ) as u16, area.x(),
area.w(), (rows[s].0 / PPQ) as u16 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| [ 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, offset+area.x()+cols[t].1 as u16 - 1,
cols[t].0 as u16, (rows[s].0 / PPQ) as u16 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 track_area: Option<[u16;4]> = None;
let mut scene_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. /// Phrase player.
pub struct PhrasePlayer<E: Engine> { pub struct PhrasePlayer<E: Engine> {
_engine: PhantomData<E>, _engine: PhantomData<E>,
/// Global timebase
pub clock: Arc<TransportTime>,
/// Phrase being played /// Phrase being played
pub phrase: Option<Arc<RwLock<Phrase>>>, pub phrase: Option<Arc<RwLock<Phrase>>>,
/// Next phrase /// Next phrase
@ -328,9 +330,10 @@ impl std::cmp::PartialEq for Phrase {
} }
impl Eq for Phrase {} impl Eq for Phrase {}
impl<E: Engine> PhrasePlayer<E> { impl<E: Engine> PhrasePlayer<E> {
pub fn new () -> Self { pub fn new (clock: &Arc<TransportTime>) -> Self {
Self { Self {
_engine: Default::default(), _engine: Default::default(),
clock: clock.clone(),
phrase: None, phrase: None,
next_phrase: None, next_phrase: None,
now: Arc::new(0.into()), now: Arc::new(0.into()),

View file

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