mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
add clock to sequencer tracks
This commit is contained in:
parent
67a5ea3a2b
commit
4f53058742
5 changed files with 42 additions and 22 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue