mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 20:26:42 +01:00
perf counter for render
This commit is contained in:
parent
c9677c87d8
commit
1b7f0e0b93
17 changed files with 331 additions and 358 deletions
|
|
@ -1,10 +1,7 @@
|
|||
use crate::*;
|
||||
use EdnItem::*;
|
||||
|
||||
pub trait HasClock: Send + Sync {
|
||||
fn clock (&self) -> &Clock;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_clock {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? {
|
||||
|
|
@ -12,17 +9,6 @@ pub trait HasClock: Send + Sync {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hosts the JACK callback for updating the temporal pointer and playback status.
|
||||
pub struct ClockAudio<'a, T: HasClock>(pub &'a mut T);
|
||||
|
||||
impl<T: HasClock> Audio for ClockAudio<'_, T> {
|
||||
#[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
self.0.clock().update_from_scope(scope).unwrap();
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ClockCommand {
|
||||
Play(Option<u32>),
|
||||
|
|
@ -77,25 +63,25 @@ pub struct Clock {
|
|||
/// Size of buffer in samples
|
||||
pub chunk: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
from!(|jack: &Arc<RwLock<JackConnection>>| Clock = {
|
||||
let jack = jack.read().unwrap();
|
||||
let chunk = jack.client().buffer_size();
|
||||
let transport = jack.client().transport();
|
||||
let timebase = Arc::new(Timebase::default());
|
||||
Self {
|
||||
quant: Arc::new(24.into()),
|
||||
sync: Arc::new(384.into()),
|
||||
transport: Arc::new(Some(transport)),
|
||||
chunk: Arc::new((chunk as usize).into()),
|
||||
global: Arc::new(Moment::zero(&timebase)),
|
||||
playhead: Arc::new(Moment::zero(&timebase)),
|
||||
offset: Arc::new(Moment::zero(&timebase)),
|
||||
started: RwLock::new(None).into(),
|
||||
timebase,
|
||||
impl From<&Arc<RwLock<JackConnection>>> for Clock {
|
||||
fn from (jack: &Arc<RwLock<JackConnection>>) -> Self {
|
||||
let jack = jack.read().unwrap();
|
||||
let chunk = jack.client().buffer_size();
|
||||
let transport = jack.client().transport();
|
||||
let timebase = Arc::new(Timebase::default());
|
||||
Self {
|
||||
quant: Arc::new(24.into()),
|
||||
sync: Arc::new(384.into()),
|
||||
transport: Arc::new(Some(transport)),
|
||||
chunk: Arc::new((chunk as usize).into()),
|
||||
global: Arc::new(Moment::zero(&timebase)),
|
||||
playhead: Arc::new(Moment::zero(&timebase)),
|
||||
offset: Arc::new(Moment::zero(&timebase)),
|
||||
started: RwLock::new(None).into(),
|
||||
timebase,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
impl std::fmt::Debug for Clock {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("Clock")
|
||||
|
|
@ -109,8 +95,14 @@ impl std::fmt::Debug for Clock {
|
|||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
pub fn new (jack: &Arc<RwLock<JackConnection>>, bpm: Option<f64>) -> Self {
|
||||
let clock = Self::from(jack);
|
||||
if let Some(bpm) = bpm {
|
||||
clock.timebase.bpm.set(bpm);
|
||||
}
|
||||
clock
|
||||
}
|
||||
pub fn timebase (&self) -> &Arc<Timebase> {
|
||||
&self.timebase
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
use crate::*;
|
||||
use KeyCode::*;
|
||||
use ClockCommand::{Play, Pause};
|
||||
/// Transport clock app.
|
||||
pub struct ClockTui { pub jack: Arc<RwLock<JackConnection>>, pub clock: Clock, }
|
||||
handle!(TuiIn: |self: ClockTui, input|ClockCommand::execute_with_state(self, input.event()));
|
||||
keymap!(TRANSPORT_KEYS = |state: ClockTui, input: Event| ClockCommand {
|
||||
key(Char(' ')) =>
|
||||
if state.clock().is_stopped() { Play(None) } else { Pause(None) },
|
||||
shift(key(Char(' '))) =>
|
||||
if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }
|
||||
});
|
||||
has_clock!(|self: ClockTui|&self.clock);
|
||||
audio!(|self: ClockTui, client, scope|ClockAudio(self).process(client, scope));
|
||||
content!(TuiOut:|self: ClockTui|ClockView { compact: false, clock: &self.clock });
|
||||
pub struct ClockView<'a> { pub compact: bool, pub clock: &'a Clock }
|
||||
content!(TuiOut: |self: ClockView<'a>| Outer(Style::default().fg(TuiTheme::g(255))).enclose(row!(
|
||||
OutputStats::new(self.compact, self.clock),
|
||||
" ",
|
||||
PlayPause { compact: false, playing: self.clock.is_rolling() },
|
||||
" ",
|
||||
BeatStats::new(self.compact, self.clock),
|
||||
)));
|
||||
impl<'a> ClockView<'a> {
|
||||
pub fn new (compact: bool, clock: &'a Clock) -> Self { Self { compact, clock } }
|
||||
}
|
||||
pub struct PlayPause { pub compact: bool, pub playing: bool }
|
||||
content!(TuiOut: |self: PlayPause| Tui::bg(
|
||||
if self.playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||
Either::new(self.compact,
|
||||
Thunk::new(||Fixed::x(9, Either::new(self.playing,
|
||||
Tui::fg(Color::Rgb(0, 255, 0), " PLAYING "),
|
||||
Tui::fg(Color::Rgb(255, 128, 0), " STOPPED ")))),
|
||||
Thunk::new(||Fixed::x(5, Either::new(self.playing,
|
||||
Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
|
||||
Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",))))))));
|
||||
pub struct BeatStats { compact: bool, bpm: Arc<str>, beat: Arc<str>, time: Arc<str>, }
|
||||
content!(TuiOut: |self: BeatStats| Either::new(self.compact,
|
||||
row!(FieldV(TuiTheme::g(128).into(), "BPM", &self.bpm),
|
||||
FieldV(TuiTheme::g(128).into(), "Beat", &self.beat),
|
||||
FieldV(TuiTheme::g(128).into(), "Time", &self.time),),
|
||||
col!(Bsp::e(Tui::fg(TuiTheme::g(255), &self.bpm), " BPM"),
|
||||
Bsp::e("Beat ", Tui::fg(TuiTheme::g(255), &self.beat)),
|
||||
Bsp::e("Time ", Tui::fg(TuiTheme::g(255), &self.time)))));
|
||||
impl BeatStats {
|
||||
fn new (compact: bool, clock: &Clock) -> Self {
|
||||
let bpm = format!("{:.3}", clock.timebase.bpm.get()).into();
|
||||
let (beat, time) = if let Some(started) = clock.started.read().unwrap().as_ref() {
|
||||
let now = clock.global.usec.get() - started.usec.get();
|
||||
(
|
||||
clock.timebase.format_beats_1(clock.timebase.usecs_to_pulse(now)).into(),
|
||||
format!("{:.3}s", now/1000000.).into()
|
||||
)
|
||||
} else {
|
||||
("-.-.--".to_string().into(), "-.---s".to_string().into())
|
||||
};
|
||||
Self { compact, bpm, beat, time }
|
||||
}
|
||||
}
|
||||
pub struct OutputStats { compact: bool, sample_rate: Arc<str>, buffer_size: Arc<str>, latency: Arc<str>, }
|
||||
content!(TuiOut: |self: OutputStats| Either::new(self.compact,
|
||||
row!(FieldV(TuiTheme::g(128).into(), "SR", &self.sample_rate),
|
||||
FieldV(TuiTheme::g(128).into(), "Buf", &self.buffer_size),
|
||||
FieldV(TuiTheme::g(128).into(), "Lat", &self.latency)),
|
||||
col!(Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.sample_rate)), " sample rate"),
|
||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", self.buffer_size)), " sample buffer"),
|
||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"))));
|
||||
impl OutputStats {
|
||||
fn new (compact: bool, clock: &Clock) -> Self {
|
||||
let rate = clock.timebase.sr.get();
|
||||
let chunk = clock.chunk.load(Relaxed);
|
||||
let sr = if compact {format!("{:.1}kHz", rate / 1000.)} else {format!("{:.0}Hz", rate)};
|
||||
Self {
|
||||
compact,
|
||||
sample_rate: sr.into(),
|
||||
buffer_size: format!("{chunk}").into(),
|
||||
latency: format!("{:.1}ms", chunk as f64 / rate * 1000.).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
//keymap!(TRANSPORT_BPM_KEYS = |state: Clock, input: Event| ClockCommand {
|
||||
//key(Char(',')) => SetBpm(state.bpm().get() - 1.0),
|
||||
//key(Char('.')) => SetBpm(state.bpm().get() + 1.0),
|
||||
//key(Char('<')) => SetBpm(state.bpm().get() - 0.001),
|
||||
//key(Char('>')) => SetBpm(state.bpm().get() + 0.001),
|
||||
//});
|
||||
//keymap!(TRANSPORT_QUANT_KEYS = |state: Clock, input: Event| ClockCommand {
|
||||
//key(Char(',')) => SetQuant(state.quant.prev()),
|
||||
//key(Char('.')) => SetQuant(state.quant.next()),
|
||||
//key(Char('<')) => SetQuant(state.quant.prev()),
|
||||
//key(Char('>')) => SetQuant(state.quant.next()),
|
||||
//});
|
||||
//keymap!(TRANSPORT_SYNC_KEYS = |sync: Clock, input: Event | ClockCommand {
|
||||
//key(Char(',')) => SetSync(state.sync.prev()),
|
||||
//key(Char('.')) => SetSync(state.sync.next()),
|
||||
//key(Char('<')) => SetSync(state.sync.prev()),
|
||||
//key(Char('>')) => SetSync(state.sync.next()),
|
||||
//});
|
||||
//keymap!(TRANSPORT_SEEK_KEYS = |state: Clock, input: Event| ClockCommand {
|
||||
//key(Char(',')) => todo!("transport seek bar"),
|
||||
//key(Char('.')) => todo!("transport seek bar"),
|
||||
//key(Char('<')) => todo!("transport seek beat"),
|
||||
//key(Char('>')) => todo!("transport seek beat"),
|
||||
//});
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
mod clock; pub use self::clock::*;
|
||||
mod clock_tui; pub use self::clock_tui::*;
|
||||
mod microsecond; pub use self::microsecond::*;
|
||||
mod moment; pub use self::moment::*;
|
||||
mod note_duration; pub use self::note_duration::*;
|
||||
|
|
@ -11,17 +10,47 @@ mod timebase; pub use self::timebase::*;
|
|||
mod unit; pub use self::unit::*;
|
||||
|
||||
pub(crate) use ::tek_jack::{*, jack::{*, contrib::*}};
|
||||
pub(crate) use std::sync::{Arc, Mutex, RwLock, atomic::{AtomicUsize, Ordering::*}};
|
||||
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, Ordering::*}};
|
||||
pub(crate) use std::ops::{Add, Sub, Mul, Div, Rem};
|
||||
pub use ::atomic_float; pub(crate) use atomic_float::*;
|
||||
pub(crate) use ::tek_tui::{
|
||||
*,
|
||||
tek_output::*,
|
||||
tek_input::*,
|
||||
tek_edn::*,
|
||||
ratatui::prelude::*,
|
||||
crossterm::event::*,
|
||||
};
|
||||
pub(crate) use ::tek_edn::*;
|
||||
pub(crate) use ::tek_input::*;
|
||||
|
||||
/// Standard result type.
|
||||
pub(crate) type Usually<T> = Result<T, Box<dyn std::error::Error>>;
|
||||
/// Standard optional result type.
|
||||
pub(crate) type Perhaps<T> = Result<Option<T>, Box<dyn std::error::Error>>;
|
||||
|
||||
pub trait Gettable<T> {
|
||||
/// Returns current value
|
||||
fn get (&self) -> T;
|
||||
}
|
||||
|
||||
pub trait Mutable<T>: Gettable<T> {
|
||||
/// Sets new value, returns old
|
||||
fn set (&mut self, value: T) -> T;
|
||||
}
|
||||
|
||||
pub trait InteriorMutable<T>: Gettable<T> {
|
||||
/// Sets new value, returns old
|
||||
fn set (&self, value: T) -> T;
|
||||
}
|
||||
|
||||
impl Gettable<bool> for AtomicBool {
|
||||
fn get (&self) -> bool { self.load(Relaxed) }
|
||||
}
|
||||
|
||||
impl InteriorMutable<bool> for AtomicBool {
|
||||
fn set (&self, value: bool) -> bool { self.swap(value, Relaxed) }
|
||||
}
|
||||
|
||||
impl Gettable<usize> for AtomicUsize {
|
||||
fn get (&self) -> usize { self.load(Relaxed) }
|
||||
}
|
||||
|
||||
impl InteriorMutable<usize> for AtomicUsize {
|
||||
fn set (&self, value: usize) -> usize { self.swap(value, Relaxed) }
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_time () -> Usually<()> {
|
||||
// TODO!
|
||||
|
|
|
|||
|
|
@ -34,6 +34,17 @@ impl PerfModel {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn get_t1 (&self, t0: Option<u64>) -> Option<std::time::Duration> {
|
||||
if let Some(t0) = t0 {
|
||||
if self.enabled {
|
||||
Some(self.clock.delta(t0, self.clock.raw()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn update (&self, t0: Option<u64>, scope: &ProcessScope) {
|
||||
if let Some(t0) = t0 {
|
||||
let t1 = self.clock.raw();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue