mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: compiles again, after extensive jack rework
Some checks are pending
/ build (push) Waiting to run
Some checks are pending
/ build (push) Waiting to run
This commit is contained in:
parent
cb7e4f7a95
commit
0192d85a19
18 changed files with 526 additions and 525 deletions
|
|
@ -1,5 +1,9 @@
|
|||
use crate::*;
|
||||
|
||||
impl HasJack<'static> for App {
|
||||
fn jack (&self) -> &Jack<'static> {
|
||||
&self.jack
|
||||
}
|
||||
}
|
||||
audio!(
|
||||
|self: App, client, scope|{
|
||||
let t0 = self.perf.get_t0();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
use crate::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct App {
|
||||
/// Must not be dropped for the duration of the process
|
||||
pub jack: Jack<'static>,
|
||||
/// Port handles
|
||||
pub ports: std::collections::BTreeMap<u32, Port<Unowned>>,
|
||||
/// Display size
|
||||
pub size: Measure<TuiOut>,
|
||||
/// Performance counter
|
||||
|
|
@ -21,12 +18,9 @@ pub struct App {
|
|||
pub history: Vec<(AppCommand, Option<AppCommand>)>,
|
||||
// Dialog overlay
|
||||
pub dialog: Option<Dialog>,
|
||||
// Cache of formatted strings
|
||||
pub view_cache: Arc<RwLock<ViewCache>>,
|
||||
/// Base color.
|
||||
pub color: ItemTheme,
|
||||
}
|
||||
|
||||
has!(Jack<'static>: |self: App|self.jack);
|
||||
has!(Pool: |self: App|self.pool);
|
||||
has!(Option<Dialog>: |self: App|self.dialog);
|
||||
|
|
@ -69,7 +63,7 @@ from_dsl!(DialogCommand: |state: App, iter|Namespace::take_from(&state.dial
|
|||
|
||||
impl App {
|
||||
pub fn update_clock (&self) {
|
||||
ViewCache::update_clock(&self.view_cache, self.clock(), self.size.w() > 80)
|
||||
ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80)
|
||||
}
|
||||
pub fn toggle_dialog (&mut self, mut dialog: Option<Dialog>) -> Option<Dialog> {
|
||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||
|
|
@ -106,10 +100,10 @@ impl App {
|
|||
}
|
||||
fn device_add_sampler (&mut self) -> Usually<()> {
|
||||
let name = self.jack.with_client(|c|c.name().to_string());
|
||||
let midi = self.project.track().expect("no active track").sequencer.midi_outs[0].name();
|
||||
let midi = self.project.track().expect("no active track").sequencer.midi_outs[0].port_name();
|
||||
let track = self.track().expect("no active track");
|
||||
let port = format!("{}/Sampler", &track.name);
|
||||
let connect = Connect::exact(format!("{name}:{midi}"));
|
||||
let connect = Connect::exact(format!("{name}:{midi}"));
|
||||
let sampler = if let Ok(sampler) = Sampler::new(
|
||||
&self.jack, &port, &[connect], &[&[], &[]], &[&[], &[]]
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ impl App {
|
|||
)));
|
||||
add(&" ");
|
||||
{
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
let cache = self.project.clock.view_cache.read().unwrap();
|
||||
add(&Fixed::x(15, Align::w(Bsp::s(
|
||||
FieldH(theme, "Beat", cache.beat.view.clone()),
|
||||
FieldH(theme, "Time", cache.time.view.clone()),
|
||||
|
|
@ -90,7 +90,7 @@ impl App {
|
|||
}
|
||||
pub fn view_status_v (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
let cache = self.project.clock.view_cache.read().unwrap();
|
||||
let theme = self.color;
|
||||
let playing = self.clock().is_rolling();
|
||||
Tui::bg(theme.darker.rgb, Fixed::xy(20, 5, Outer(true, Style::default().fg(Tui::g(96))).enclose(
|
||||
|
|
@ -120,13 +120,13 @@ impl App {
|
|||
}
|
||||
pub fn view_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
let cache = self.project.clock.view_cache.read().unwrap();
|
||||
view_status(Some(self.project.selection.describe(self.tracks(), self.scenes())),
|
||||
cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone())
|
||||
}
|
||||
pub fn view_transport (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
let cache = self.project.clock.view_cache.read().unwrap();
|
||||
view_transport(self.project.clock.is_rolling(),
|
||||
cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone())
|
||||
}
|
||||
|
|
@ -225,122 +225,3 @@ impl ScenesView for App {
|
|||
(self.width() as u16).saturating_sub(self.w_side())
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a pre-allocated buffer, then write into it.
|
||||
#[macro_export] macro_rules! rewrite {
|
||||
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } }
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)] pub(crate) struct ViewMemo<T, U> {
|
||||
pub(crate) value: T,
|
||||
pub(crate) view: Arc<RwLock<U>>
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> ViewMemo<T, U> {
|
||||
fn new (value: T, view: U) -> Self {
|
||||
Self { value, view: Arc::new(view.into()) }
|
||||
}
|
||||
pub(crate) fn update <R> (
|
||||
&mut self,
|
||||
newval: T,
|
||||
render: impl Fn(&mut U, &T, &T)->R
|
||||
) -> Option<R> {
|
||||
if newval != self.value {
|
||||
let result = render(&mut*self.view.write().unwrap(), &newval, &self.value);
|
||||
self.value = newval;
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)] pub struct ViewCache {
|
||||
pub(crate) sr: ViewMemo<Option<(bool, f64)>, String>,
|
||||
pub(crate) buf: ViewMemo<Option<f64>, String>,
|
||||
pub(crate) lat: ViewMemo<Option<f64>, String>,
|
||||
pub(crate) bpm: ViewMemo<Option<f64>, String>,
|
||||
pub(crate) beat: ViewMemo<Option<f64>, String>,
|
||||
pub(crate) time: ViewMemo<Option<f64>, String>,
|
||||
pub(crate) scns: ViewMemo<Option<(usize, usize)>, String>,
|
||||
pub(crate) trks: ViewMemo<Option<(usize, usize)>, String>,
|
||||
pub(crate) stop: Arc<str>,
|
||||
pub(crate) edit: Arc<str>,
|
||||
}
|
||||
|
||||
impl Default for ViewCache {
|
||||
fn default () -> Self {
|
||||
let mut beat = String::with_capacity(16);
|
||||
write!(beat, "{}", Self::BEAT_EMPTY);
|
||||
let mut time = String::with_capacity(16);
|
||||
write!(time, "{}", Self::TIME_EMPTY);
|
||||
let mut bpm = String::with_capacity(16);
|
||||
write!(bpm, "{}", Self::BPM_EMPTY);
|
||||
Self {
|
||||
beat: ViewMemo::new(None, beat),
|
||||
time: ViewMemo::new(None, time),
|
||||
bpm: ViewMemo::new(None, bpm),
|
||||
sr: ViewMemo::new(None, String::with_capacity(16)),
|
||||
buf: ViewMemo::new(None, String::with_capacity(16)),
|
||||
lat: ViewMemo::new(None, String::with_capacity(16)),
|
||||
scns: ViewMemo::new(None, String::with_capacity(16)),
|
||||
trks: ViewMemo::new(None, String::with_capacity(16)),
|
||||
stop: "⏹".into(),
|
||||
edit: "edit".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewCache {
|
||||
pub const BEAT_EMPTY: &'static str = "-.-.--";
|
||||
pub const TIME_EMPTY: &'static str = "-.---s";
|
||||
pub const BPM_EMPTY: &'static str = "---.---";
|
||||
|
||||
pub fn track_counter (cache: &Arc<RwLock<Self>>, track: usize, tracks: usize)
|
||||
-> Arc<RwLock<String>>
|
||||
{
|
||||
let data = (track, tracks);
|
||||
cache.write().unwrap().trks.update(Some(data), rewrite!(buf, "{}/{}", data.0, data.1));
|
||||
cache.read().unwrap().trks.view.clone()
|
||||
}
|
||||
|
||||
pub fn scene_add (cache: &Arc<RwLock<Self>>, scene: usize, scenes: usize, is_editing: bool)
|
||||
-> impl Content<TuiOut>
|
||||
{
|
||||
let data = (scene, scenes);
|
||||
cache.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1));
|
||||
button_3("S", "add scene", cache.read().unwrap().scns.view.clone(), is_editing)
|
||||
}
|
||||
|
||||
pub fn update_clock (cache: &Arc<RwLock<Self>>, clock: &Clock, compact: bool) {
|
||||
let rate = clock.timebase.sr.get();
|
||||
let chunk = clock.chunk.load(Relaxed) as f64;
|
||||
let lat = chunk / rate * 1000.;
|
||||
let delta = |start: &Moment|clock.global.usec.get() - start.usec.get();
|
||||
let mut cache = cache.write().unwrap();
|
||||
cache.buf.update(Some(chunk), rewrite!(buf, "{chunk}"));
|
||||
cache.lat.update(Some(lat), rewrite!(buf, "{lat:.1}ms"));
|
||||
cache.sr.update(Some((compact, rate)), |buf,_,_|{
|
||||
buf.clear();
|
||||
if compact {
|
||||
write!(buf, "{:.1}kHz", rate / 1000.)
|
||||
} else {
|
||||
write!(buf, "{:.0}Hz", rate)
|
||||
}
|
||||
});
|
||||
if let Some(now) = clock.started.read().unwrap().as_ref().map(delta) {
|
||||
let pulse = clock.timebase.usecs_to_pulse(now);
|
||||
let time = now/1000000.;
|
||||
let bpm = clock.timebase.bpm.get();
|
||||
cache.beat.update(Some(pulse), |buf, _, _|{
|
||||
buf.clear();
|
||||
clock.timebase.format_beats_1_to(buf, pulse)
|
||||
});
|
||||
cache.time.update(Some(time), rewrite!(buf, "{:.3}s", time));
|
||||
cache.bpm.update(Some(bpm), rewrite!(buf, "{:.3}", bpm));
|
||||
} else {
|
||||
cache.beat.update(None, rewrite!(buf, "{}", ViewCache::BEAT_EMPTY));
|
||||
cache.time.update(None, rewrite!(buf, "{}", ViewCache::TIME_EMPTY));
|
||||
cache.bpm.update(None, rewrite!(buf, "{}", ViewCache::BPM_EMPTY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue