axis config: bring failure under test

This commit is contained in:
i do not exist 2026-10-04 23:10:23 +03:00
parent 5aaa0f3e4e
commit a355e482c7
16 changed files with 1328 additions and 1149 deletions

View file

@ -1,5 +1,43 @@
#![allow(clippy::unit_arg)]
impl_as_ref!(Config: |self: App|self.config.config.as_ref());
impl_as_mut!(Option<MidiEditor>: |self: App|self.project.as_mut());
impl_as_mut!(Option<Project>: |self: App|todo!());
impl_as_ref!(Option<MidiEditor>: |self: App|self.project.as_ref());
impl_as_ref!(Option<Project>: |self: App|todo!());
impl_as_mut_opt!(Scene: |self: App| self.project.as_mut_opt());
impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt());
impl_as_ref_opt!(Scene: |self: App| self.project.as_ref_opt());
impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt());
impl_audio!(App: tek_jack_process, tek_jack_event);
impl_has!(Clock: |self: App|self.project.clock);
impl_has!(Dialog: |self: App|self.dialog);
impl_has!(Jack<'static>: |self: App|self.jack);
impl_has!(Pool<MidiClip>: |self: App|self.project.clips);
impl_has!(Pool<Sample>: |self: App|self.project.samples);
impl_has!(Selection: |self: App|self.project.selection);
impl_has!(Sizer: |self: App|self.size);
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x });
impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x });
impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x });
impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x });
impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x });
impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x });
impl_from!(AppCmd: |x: TrackCmd| AppCmd::Track { command: x });
impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
primitive!(isize: try_to_isize);
primitive!(u16: try_to_u16);
primitive!(u8: try_to_u8);
primitive!(usize: try_to_usize);
impl_debug!(MenuItem: |self, w| { write!(w, "{}", &self.0) });
impl_default!(MenuItem: Self::new("", []));
/// Define a type alias for iterators of sized items (columns).
macro_rules! def_sizes_iter {
($Type:ident => $($Item:ty),+) => {
@ -8,34 +46,36 @@ macro_rules! def_sizes_iter {
}
}
mod bind; pub use self::bind::*;
mod cli; pub use self::cli::*;
mod conf; pub use self::conf::*;
mod deps; pub use self::deps::*;
mod mixer; pub use self::mixer::*;
mod mode; pub use self::mode::*;
mod pool; pub use self::pool::*;
mod project; pub use self::project::*;
mod sample; pub use self::sample::*;
mod sequence; pub use self::sequence::*;
mod view; pub use self::view::*;
#[cfg(feature = "lv2")] mod lv2;
#[cfg(feature = "lv2")] pub use self::lv2::*;
pub use self::device::*;
mod device {
mod mixer; pub use self::mixer::*;
mod sample; pub use self::sample::*;
#[cfg(feature = "lv2_gui")] mod lv2_gui;
#[cfg(feature = "lv2_gui")] pub use self::lv2_gui::*;
#[cfg(feature = "lv2")] mod lv2;
#[cfg(feature = "lv2")] pub use self::lv2::*;
#[cfg(feature = "vst2")] mod vst2;
#[cfg(feature = "vst2")] pub use self::vst2::*;
#[cfg(feature = "lv2_gui")] mod lv2_gui;
#[cfg(feature = "lv2_gui")] pub use self::lv2_gui::*;
#[cfg(feature = "vst2")] mod vst2;
#[cfg(feature = "vst2")] pub use self::vst2::*;
}
/// Command-line entrypoint.
#[allow(unused)]
#[hotpath::main]
fn main () -> Usually<()> {
tengri::Tui::setup_panic();
#[cfg(feature = "cli")] let outcome = Config::watched(run_with_config);
#[cfg(not(feature = "cli"))] let outcome = Config::watched(run_new_plain);
#[cfg(not(feature = "cli"))]
let outcome = ConfigWatcher::run_watched(main_plain, None);
#[cfg(feature = "cli")]
let outcome = ConfigWatcher::run_watched(main_cli, None);
if let Err(e) = outcome {
println!("{e:#?}");
std::process::exit(1);
@ -44,7 +84,7 @@ fn main () -> Usually<()> {
}
#[cfg(not(feature = "cli"))]
fn run_new_plain (config: Config) -> Usually<()> {
fn main_plain (config: Arc<ConfigWatcher>) -> Usually<()> {
let name = "tek";
tengri::Tui::run_main(Jack::new_run(name, move|jack|{
let mode = "Menu";
@ -53,7 +93,7 @@ fn run_new_plain (config: Config) -> Usually<()> {
let clock = Clock::new(&jack, Some(bpm))?;
let tracks = [];
let scenes = [];
let project = Project::new(
let project = Project::create(
&jack,
title.into(),
clock,
@ -72,6 +112,119 @@ fn run_new_plain (config: Config) -> Usually<()> {
})?)
}
#[cfg(feature = "cli")] pub use self::cli::*;
#[cfg(feature = "cli")] mod cli {
use super::*;
/// Banner.
pub(crate) const HEADER: &'static str = r#"
~ █▀█▀█ █▀▀█ █ █ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
█ █▀ █▀▀▄ ~ heatwave is the new darkwave ~
~ ▀ █▀▀█ ▀ ▀ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;
pub fn show_version () {
println!("versions aint real man");
}
pub fn main_cli (config: Arc<ConfigWatcher>) -> Usually<()> {
Cli::parse().run(Some(config.into()))
}
/// The command-line interface descriptor.
///
/// ```
/// let cli: tek::Cli = Default::default();
///
/// use clap::CommandFactory;
/// tek::Cli::command().debug_assert();
/// ```
#[derive(Parser, Debug, Default)]
#[command(name = "tek", version, about = Some(HEADER), long_about = Some(HEADER))]
pub struct Cli {
/// Pre-defined configuration modes.
///
/// TODO: Replace these with scripted configurations.
#[command(subcommand)]
pub action: Action,
/// Record data for performance flamegraph.
#[arg(long)]
trace: bool,
}
/// Command-line configuration.
impl Cli {
pub fn run (&self, mut config: Option<Arc<ConfigWatcher>>) -> Usually<()> {
self.action.run(
config.unwrap_or_else(||Arc::new(ConfigWatcher::new(None))),
self.trace
)
}
}
/// Application modes that can be passed to the mommand line interface.
///
/// ```
/// let action: tek::Action = Default::default();
/// ```
#[derive(Debug, Clone, Subcommand, Default)]
pub enum Action {
/// Continue where you left off
#[default] Resume,
/// Run headlessly in current session.
Headless,
/// Show status of current session.
Status,
/// List known sessions.
List,
/// Continue work in a copy of the current session.
Fork,
/// Create a new empty session.
New(ProjectInit),
/// Import media as new session.
Import,
/// Show configuration.
Config,
/// Show version.
Version,
}
impl Action {
fn run (&self, config: Arc<ConfigWatcher>, _trace: bool) -> Usually<()> {
use Action::*;
match self {
Version => show_version(),
Config => config.config_print(),
Resume => todo!("resume session"),
List => todo!("list sessions"),
New(sesh) => Exit::run(|exit|Tui::run_main(
exit.clone(),
{
#[allow(unused_mut)]
let mut app = App::new(Some(exit), sesh.init()?, config, "Menu");
app.dialog = Dialog::welcome();
#[cfg(feature = "prof2")] {
if trace {
app.guard = Some({
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
//let tracy = tracing_tracy::TracyLayer::default();
let (flame, _guard) = tracing_flame::FlameLayer::with_file("./tracing.folded").unwrap();
//let registry = tracing_subscriber::registry().with(flame).init();
//tracing::subscriber::set_global_default(registry)?;
tracing::subscriber::set_global_default(tracing_subscriber::registry().with(flame))?;
_guard
})
}
}
Arc::new(RwLock::new(app))
})).map(|_|())?,
_ => todo!()
}
Ok(())
}
}
}
/// Total application state.
///
/// ```
@ -96,34 +249,34 @@ fn run_new_plain (config: Config) -> Usually<()> {
/// let _ = app.project.h_scenes();
/// ```
#[derive(Default, Debug)]
#[namespace(Arc<[Connect]> get_arc_array_connect)]
#[namespace(Arc<str> get_arc_str)]
#[namespace(Color get_color)]
#[namespace(ControlAxis ControlAxis::get_axis)]
#[namespace(Dialog Dialog::get_dialog)]
#[namespace(Arc<[Connect]> get_arc_array_connect)]
#[namespace(Arc<str> get_arc_str)]
#[namespace(Color get_color)]
#[namespace(ControlAxis ControlAxis::get_axis)]
#[namespace(Dialog Dialog::get_dialog)]
#[namespace(ItemColor)]
#[namespace(ItemTheme)]
#[namespace(MidiClip)]
#[namespace(ClipSlot ClipSlot::eval)]
#[namespace(ClipPool ClipPool::eval)]
#[namespace(SampleSlot SampleSlot::eval)]
#[namespace(SamplePool SamplePool::eval)]
#[namespace(Option<Arc<[Connect]>> get_opt_arc_array_connect)]
#[namespace(Option<Arc<str>> get_opt_arc_str)]
#[namespace(Option<ItemTheme> get_opt_itemtheme)]
#[namespace(Option<bool> get_opt_bool)]
#[namespace(Option<u16> get_opt_u16)]
#[namespace(Option<u32> get_opt_u32)]
#[namespace(Option<usize> get_opt_usize)]
#[namespace(PathBuf get_path_buf)]
#[namespace(Vec<Arc<str>> get_vec_arc_str)]
#[namespace(bool get_bool)]
#[namespace(f64 get_f64)]
#[namespace(ClipSlot ClipSlot::eval)]
#[namespace(ClipPool ClipPool::eval)]
#[namespace(SampleSlot SampleSlot::eval)]
#[namespace(SamplePool SamplePool::eval)]
#[namespace(Option<Arc<[Connect]>> get_opt_arc_array_connect)]
#[namespace(Option<Arc<str>> get_opt_arc_str)]
#[namespace(Option<ItemTheme> get_opt_itemtheme)]
#[namespace(Option<bool> get_opt_bool)]
#[namespace(Option<u16> get_opt_u16)]
#[namespace(Option<u32> get_opt_u32)]
#[namespace(Option<usize> get_opt_usize)]
#[namespace(PathBuf get_path_buf)]
#[namespace(Vec<Arc<str>> get_vec_arc_str)]
#[namespace(bool get_bool)]
#[namespace(f64 get_f64)]
#[namespace(isize)]
#[namespace(u16 get_u16)]
#[namespace(u16 get_u16)]
#[namespace(u32)]
#[namespace(u8)]
#[namespace(usize get_usize)]
#[namespace(usize get_usize)]
pub struct App {
/// Exit flag
pub exit: Exit,
@ -136,7 +289,7 @@ pub struct App {
/// Performance counter
pub perf: PerfModel,
/// Available view modes and input bindings
pub config: Arc<Config>,
pub config: Arc<ConfigWatcher>,
/// Currently selected mode
pub modes: Arc<RwLock<Vec<Arc<str>>>>,
/// Undo history
@ -155,16 +308,29 @@ impl App {
/// Create a new application instance from a backend, project, config, and mode
///
/// ```
/// let mut proj = tek::Project::default();
/// proj.jack = tek::tengri::Jack::new(&"test_tek").expect("failed to connect to jack");
/// let config = "(mode hi (name Hello) (info World))";
/// let mut conf = tek::Config::load(tek::Config::default(), config).unwrap();
/// let tek = tek::App::new(None, proj, conf.into(), "hello");
/// # use ::tek::{*, tengri::Jack};
/// let proj = Project::new_jack(&"test_tek").expect("failed to connect to jack");
/// let tek = App::new(None, proj, None, "hello");
/// config_reload(tek, stringify!(
/// (mode Arranger (name Arranger) (info Launch grid.)
/// (axis X Track)
/// (axis Y Scene)
/// (bind Global Clock Tracks Scenes Clip Launch Color History)
/// (view (bar/n 2 StatusBar (bar/s 8 (bar/e 16 (align/nw Transport)
/// (bar/w 20 (bar/s 2 (bar/w 4 TracksOutputsAdd TracksOutputsCount)
/// (bar/s 2 (bar/w 4 TracksDevicesAdd TracksDevicesCount)
/// (bar/s 2 (bar/w 4 TracksNamesAdd TracksNamesCount)
/// (bar/w 4 TracksInputsAdd TracksInputsCount))))
/// (align/x (bar/s 2 TracksOutputsCells
/// (bar/s 2 TracksDevicesCells
/// (bar/s 2 TracksNamesCells TracksInputsCells))))))
/// (bar/w 20 (text) (bar/e 16 ScenesNames ScenesClips))))))
/// )).unwrap();
/// ```
pub fn new (
exit: Option<Exit>,
project: Project,
config: Arc<Config>,
config: impl Into<Option<Arc<ConfigWatcher>>>,
mode: impl AsRef<str>
) -> Self {
App {
@ -173,7 +339,7 @@ impl App {
color: ItemTheme::random(),
dialog: Dialog::welcome(),
modes: Arc::new(RwLock::new(vec![mode.as_ref().into()])),
config,
config: config.into().unwrap_or_default(),
project,
..Default::default()
}
@ -186,7 +352,6 @@ impl HasJack<'static> for App {
}
}
impl_audio!(App: tek_jack_process, tek_jack_event);
fn tek_jack_process (state: &mut App, client: &Client, scope: &ProcessScope) -> Control {
let t0 = state.perf.get_t0();
@ -388,27 +553,9 @@ fn get_vec_arc_str <'a> (_: &App, _: impl Language) -> Perhaps<Vec<Arc<str>>> {
todo!()
}
primitive!(u8: try_to_u8);
primitive!(u16: try_to_u16);
primitive!(usize: try_to_usize);
primitive!(isize: try_to_isize);
impl_has!(Clock: |self: App|self.project.clock);
impl_has!(Dialog: |self: App|self.dialog);
impl_has!(Jack<'static>: |self: App|self.jack);
impl_has!(Pool<MidiClip>: |self: App|self.project.clips);
impl_has!(Pool<Sample>: |self: App|self.project.samples);
impl_has!(Selection: |self: App|self.project.selection);
impl_has!(Sizer: |self: App|self.size);
impl_as_ref!(Option<Project>: |self: App|todo!());
impl_as_mut!(Option<Project>: |self: App|todo!());
impl_as_ref!(Option<MidiEditor>: |self: App|self.project.as_ref());
impl_as_mut!(Option<MidiEditor>: |self: App|self.project.as_mut());
impl HasModeStack for App {
fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>> {
self.modes.read()
}
fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec<Arc<str>>> {
self.modes.write()
fn mode_stack (&self) -> &RwLock<Vec<Arc<str>>> {
&self.modes
}
}
impl HasClip for App {
@ -633,8 +780,6 @@ pub const fn wrap_dec (index: usize, count: usize) -> usize {
/// ```
#[derive(Clone)] pub struct MenuItem(pub Arc<str>, pub Arc<[AppCmd]>);
impl_debug!(MenuItem: |self, w| { write!(w, "{}", &self.0) });
impl_default!(MenuItem: Self::new("", []));
impl PartialEq for MenuItem {
fn eq (&self, other: &Self) -> bool {
@ -753,12 +898,126 @@ impl App {
}
}
pub trait HasError<T> {
fn error (&self) -> &RwLock<Option<T>>;
fn error_set (&self, e: Option<T>) {
*self.error().write() = e;
}
}
pub trait HasConfig {
fn config (&self) -> &Config;
fn config_mut (&mut self) -> &mut Config;
/// Print the configuration.
fn config_print (&self) {
let config = self.config();
print_config_views(config);
print_config_binds(config);
print_config_modes(config);
}
/// Make this configuration empty.
fn config_clear (&self) {
let config = self.config();
config.modes_clear();
config.views_clear();
config.binds_clear();
config.stamp.store(quanta::Clock::new().raw(), Relaxed);
}
}
pub trait HasModes {
fn modes (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Mode>>>;
/// Register a mode.
fn modes_add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("mode: missing name")?;
let body = expr.tail()?.ok_or("mode: missing body")?;
let mut mode = Mode::default();
body.each(&mut mode, |c, s|c.add(s))?;
self.modes().write().insert(name.into(), Arc::new(mode));
Ok(())
}
/// Get a mode by name.
fn modes_get (&self, name: impl AsRef<str>) -> Option<Arc<Mode>> {
self.modes().read().get(name.as_ref()).cloned()
}
/// Run something for each mode.
fn modes_for_each <T> (&self, mut ator: impl FnMut(&str, &Mode)->T) {
for (k, v) in self.modes().read().iter() {
let _ = ator(k.as_ref(), v.as_ref());
}
}
/// Count modes.
fn modes_count (&self) -> usize {
self.modes().read().len()
}
/// Clear modes.
fn modes_clear (&self) {
*self.modes().write() = Default::default();
}
}
pub trait HasBinds {
fn binds_keys (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Bind<TuiEvent, Arc<str>>>>>;
fn binds_axis (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Axis<Arc<str>>>>>;
fn binds_clear (&self) {
*self.binds_keys().write() = Default::default();
*self.binds_axis().write() = Default::default();
}
fn binds_find (&self, id: impl AsRef<str>) -> Option<Arc<Bind<TuiEvent, Arc<str>>>> {
self.binds_keys().read().get(id.as_ref()).cloned()
}
fn binds_for_each <T> (
&self, mut cb: impl FnMut(&Arc<str>, &Bind<TuiEvent, Arc<str>>)->T
) {
for (k, v) in self.binds_keys().read().iter() {
cb(k, v);
}
}
fn binds_def_keys (&self, expr: impl Language) -> Usually<()> {
self.binds_keys().write().insert(
expr.head()?.ok_or("bind: missing name")?.into(),
Bind::new(expr.tail()?.unwrap_or(""))?.into()
);
Ok(())
}
fn binds_def_axis (&self, expr: impl Language) -> Usually<()> {
self.binds_axis().write().insert(
expr.head()?.ok_or("axis: missing name")?.into(),
Axis::new(expr.tail()?.unwrap_or(""))?.into()
);
Ok(())
}
}
pub trait HasViews {
fn views (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<View<Tui, App>>>>;
/// Load custom view definition.
fn views_add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
self.views().write().insert(
expr.head()?.ok_or("view: missing name")?.into(),
View::new(expr.tail()?.ok_or("view: missing body")?)?.into()
);
Ok(())
}
fn views_clear (&self) {
*self.views().write() = Default::default();
}
fn views_get (&self, name: impl AsRef<str>) -> Option<Arc<View<Tui, App>>> {
self.views().read().get(name.as_ref()).cloned()
}
fn views_for_each <T> (&self, cb: impl Fn(&Arc<str>, &Arc<View<Tui, App>>)->T) -> () {
for (k, v) in self.views().read().iter() {
cb(k, v);
}
}
}
/// Has a stack of currently active interaction modes.
pub trait HasModeStack {
/// Readable mode stack.
fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>>;
/// Writable mode stack.
fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec<Arc<str>>>;
/// Stack of active modes.
fn mode_stack (&self) -> &RwLock<Vec<Arc<str>>>;
}
/// Has a MIDI editor.
@ -979,26 +1238,28 @@ impl App {
Ok(())
}
/// ```
/// let app = tek::App::default();
/// tek::Config::load(&app.config, stringify!(
/// (axis Y @down @up)
/// (mode Menu (axis Y prev next) (bind (@enter confirm)))
/// # use ::{std::sync::Arc, tek::*};
/// let app = App::default();
/// config_reload(&app, stringify!(
/// (axis Y @up @down)
/// (mode Menu
/// (axis Y prev next)
/// (bind (@enter confirm)))
/// )).unwrap();
/// panic!("{app:#?}");
/// assert!(app.input_cmds_find(&"left".into()).is_none());
/// assert!(app.input_cmds_find(&"enter".into()).is_some());
/// assert!(app.input_cmds_find(&"up".into()).is_some());
/// assert!(app.input_cmds_find(&"down".into()).is_some());
/// assert!(app.input_cmds_find(&"up".into()).is_some());
/// ```
pub fn input_cmds_find (&self, input: &TuiEvent) -> Option<Arc<[Binding<Arc<str>>]>> {
for name in self.modes.read().iter().rev() {
let mode = self.config.get_mode(name).as_ref().map(Arc::clone);
let mode = self.modes_get(name).as_ref().map(Arc::clone);
if let Some(mode) = mode {
for (ctrl, (dec, inc)) in mode.axis.iter() {
panic!("{ctrl:?} {dec:?} {inc:?}");
}
for id in mode.keys.iter() {
if let Some(event_map) = self.config.binds.find(id)
if let Some(event_map) = self.config.binds_find(id)
&& let Some(bindings) = event_map.query(input) {
return Some(bindings.into())
}
@ -1033,15 +1294,6 @@ impl App {
}
}
impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x });
impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x });
impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x });
impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x });
impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x });
impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x });
impl_from!(AppCmd: |x: TrackCmd| AppCmd::Track { command: x });
impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
#[tek_proc::commands(AppCmd)] impl App {
/// Exit the application.
#[command(Exit = "exit")] pub fn cmd_exit (&mut self) -> Perhaps<AppCmd> {
@ -1050,12 +1302,12 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
}
/// Switch to another set of contextual modes.
#[command(Mode = "mode")] pub fn cmd_mode (&mut self, modes: Vec<Arc<str>>) -> Perhaps<AppCmd> {
*self.modes_mut() = modes.into();
*self.mode_stack().write() = modes.into();
Ok(None)
}
/// Enter a contextual mode.
#[command(Enter = "enter")] pub fn enter_mode (&mut self, mode: Arc<str>) -> Perhaps<AppCmd> {
let mut modes = self.modes_mut();
let mut modes = self.mode_stack().write();
Ok(if modes.last() != Some(&mode) {
modes.push(mode);
Some(AppCmd::Leave)
@ -1065,7 +1317,7 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
}
/// Leave a contextual mode.
#[command(Leave = "leave")] fn leave_mode (&mut self) -> Perhaps<AppCmd> {
let mut modes = self.modes_mut();
let mut modes = self.mode_stack().write();
if modes.len() > 1 {
let mode = modes.pop();
Ok(mode.map(|mode|AppCmd::Enter { mode }))
@ -1771,243 +2023,32 @@ pub enum MidiOutputCmd {
Connect(Arc<str>),
}
//impl<T: HasPool
//+ Namespace<bool>
//+ Namespace<usize>
//+ Namespace<Arc<str>>
//+ Namespace<PathBuf>
//+ Namespace<MidiClip>
//+ Namespace<ItemColor>
//+ Namespace<BrowseCmd>
//> PoolControl for T {}
//#[tek_proc::commands(PolCmd = "pool")] pub trait PoolControl: HasPool
//+ Namespace<bool>
//+ Namespace<usize>
//+ Namespace<Arc<str>>
//+ Namespace<PathBuf>
//+ Namespace<MidiClip>
//+ Namespace<ItemColor>
//+ Namespace<BrowseCmd>
//{
///// Toggle visibility of pool
//#[command(Show = "show")] fn show (&mut self, visible: bool) -> Perhaps<PoolCmd> {
//self.pool_mut().visible = visible;
//Ok(Some(PoolCmd::Show { visible: !visible }))
//}
///// Set selected clip
//#[command(Select = "select")] fn select (&mut self, index: usize) -> Perhaps<PoolCmd> {
//self.pool_mut().set_clip_index(index);
//Ok(None)
//}
///// Delete a clip from the pool
//#[command(Delete = "delete")] fn delete (&mut self, index: usize) -> Perhaps<PoolCmd> {
//let clip = self.pool_mut().remove(index).read().clone();
//Ok(Some(PoolCmd::Add { index, clip }))
//}
///// Switch places of two clips in pool
//#[command(Swap = "swap")] fn swap (&mut self, index: usize, other: usize) -> Perhaps<PoolCmd> {
//self.pool_mut().swap(index, other);
//Ok(Some(PoolCmd::Swap { index, other }))
//}
///// Add clip to pool
//#[command(Add = "add")] fn clip_add (&mut self, index: usize, clip: MidiClip) -> Perhaps<PoolCmd> {
//let mut index = index;
//let clip = Arc::new(RwLock::new(clip.clone()));
//let mut clips = self.pool_mut().clips_mut();
//if index >= clips.len() {
//index = clips.len();
//clips.push(clip)
//} else {
//clips.insert(index, clip);
//}
//Ok(Some(PoolCmd::Delete { index }))
//}
///// Set name of clip
//#[command(SetName = "set-name")] fn clip_set_name (&mut self, index: usize, name: Arc<str>) -> Perhaps<PoolCmd> {
//Ok(self.pool_mut().clips.read().get(index).map(|clip|{
//let old_name = clip.read().name.clone();
//clip.write().name = name.clone();
//PoolCmd::SetName { index, name: old_name }
//}))
//}
///// Set length of clip
//#[command(SetLength = "set-length")] fn clip_set_length (&mut self, index: usize, length: usize) -> Perhaps<PoolCmd> {
//Ok(self.pool_mut().clips.read().get(index).map(|clip|{
//let old_len = clip.read().length;
//clip.write().length = length;
//PoolCmd::SetLength { index, length: old_len }
//}))
//}
///// Set color of clip
//#[command(SetColor = "set-color")] fn clip_set_color (&mut self, index: usize, color: ItemColor) -> Perhaps<PoolCmd> {
//let mut color = ItemTheme::from(color);
//std::mem::swap(&mut color, &mut self.pool().clips.read()[index].write().color);
//Ok(Some(PoolCmd::SetColor { index, color: color.base }))
//}
///// Enter crop mode
//#[command(CropBegin = "crop/begin")] fn crop_begin (&mut self) -> Perhaps<PoolCmd> {
//let index = self.pool().clip_index();
//let length = self.pool().clips.read()[index].read().length;
//*self.pool_mut().mode_mut() = Some(PoolMode::Length(index, length, ClipLengthFocus::Bar));
//Ok(None)
//}
///// Exit crop mode, discard
//#[command(CropCancel = "crop/cancel")] fn crop_cancel (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(..)) = self.pool_mut().mode_mut().clone() {
//*self.pool_mut().mode_mut() = None;
//}
//Ok(None)
//}
//#[command(CropSet = "crop/set")] fn crop_set (&mut self, _length: usize) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(clip, ref mut length, ref mut _focus))
//= self.pool_mut().mode_mut().clone()
//{
//let old_length;
//{
//let clip = self.pool().clips.read()[clip].clone();//.write();
//old_length = Some(clip.read().length);
//clip.write().length = *length;
//}
//*self.pool_mut().mode_mut() = None;
//return Ok(old_length.map(|l|PoolCmd::CropSet { _length: l }))
//}
//Ok(None)
//}
//#[command(CropNext = "crop/next")] fn crop_next (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(
//_clip, ref mut _length, ref mut focus
//)) = self.pool_mut().mode_mut().clone() {
//focus.next()
//};
//Ok(None)
//}
//#[command(CropPrev = "crop/prev")] fn crop_prev (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(
//_clip, ref mut _length, ref mut focus
//)) = self.pool_mut().mode_mut().clone() {
//focus.prev()
//};
//Ok(None)
//}
//#[command(CropInc = "crop/inc")] fn crop_inc (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(
//_clip, ref mut length, ref mut focus
//)) = self.pool_mut().mode_mut().clone() {
//match focus {
//ClipLengthFocus::Bar => { *length += 4 * PPQ },
//ClipLengthFocus::Beat => { *length += PPQ },
//ClipLengthFocus::Tick => { *length += 1 },
//}
//}
//Ok(None)
//}
//#[command(CropDec = "crop/dec")] fn crop_dec (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Length(
//_clip, ref mut length, ref mut focus
//)) = self.pool_mut().mode_mut().clone() {
//match focus {
//ClipLengthFocus::Bar => { *length = length.saturating_sub(4 * PPQ) },
//ClipLengthFocus::Beat => { *length = length.saturating_sub(PPQ) },
//ClipLengthFocus::Tick => { *length = length.saturating_sub(1) },
//}
//}
//Ok(None)
//}
///// Enter rename mode
//#[command(RenameBegin = "rename/begin")] fn rename_begin (&mut self) -> Perhaps<PoolCmd> {
//let index = self.pool().clip_index();
//let name = self.pool().clips.read()[index].read().name.clone();
//*self.pool_mut().mode_mut() = Some(PoolMode::Rename(index, name));
//Ok(None)
//}
///// Exit rename mode, discard
//#[command(RenameCancel = "rename/cancel")] fn rename_cancel (&mut self) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Rename(clip, ref mut old_name)) = self.pool_mut().mode_mut().clone() {
//self.pool().clips.read()[clip].write().name = old_name.clone().into();
//}
//Ok(None)
//}
///// Exit rename mode, update name
//#[command(RenameConfirm = "rename/confirm")] fn rename_confirm (&mut self) -> Perhaps<PoolCmd> {
//Ok(if let Some(PoolMode::Rename(_clip, ref mut old_name)) = self.pool_mut().mode_mut().clone() {
//let old_name = old_name.clone();
//*self.pool_mut().mode_mut() = None;
//Some(PoolCmd::RenameSet { value: old_name })
//} else {
//None
//})
//}
//#[command(RenameSet = "rename/set")] fn rename_set (&mut self, value: Arc<str>) -> Perhaps<PoolCmd> {
//if let Some(PoolMode::Rename(clip, ref mut _old_name)) = self.pool_mut().mode_mut().clone() {
//self.pool().clips.read()[clip].write().name = value.clone();
//}
//Ok(None)
//}
/////// Import from file
////#[command(Browse = "browse")]
////fn browse (&mut self, command: BrowseCmd) -> Perhaps<PoolCmd> {
////Ok(if let Some(browse) = self.pool_mut().browse.as_mut() {
////command.act(browse)?.map(|command|PoolCmd::Browse{command})
////} else {
////None
////})
////}
///// Import clip to pool from file
//#[command(Import = "import")]
//fn clip_import (&mut self, index: usize, path: PathBuf) -> Perhaps<PoolCmd>
//where Self: Sized
//{
//let bytes = std::fs::read(&path)?;
//let smf = Smf::parse(bytes.as_slice())?;
//let mut t = 0u32;
//let mut events = vec![];
//for track in smf.tracks.iter() {
//for event in track.iter() {
//t += event.delta.as_int();
//if let TrackEventKind::Midi { channel, message } = event.kind {
//events.push((t, channel.as_int(), message));
//}
//}
//}
//let mut clip = MidiClip::new("imported", true, t as usize + 1, None, None);
//for event in events.iter() {
//clip.notes[event.0 as usize].push(event.2);
//}
//Ok(PoolCmd::Add { index, clip }.dispatch(self)?)
//}
/////// Export to file
////#[command(Export = "export")]
////fn export (&mut self, command: BrowseCmd) -> Perhaps<PoolCmd> {
////Ok(if let Some(browse) = self.pool_mut().browse.as_mut() {
////command.act(browse)?.map(|command|PoolCmd::Export{command})
////} else {
////None
////})
////}
//}
//pub(crate) struct EntriesIterator<'a, S: Screen> {
//pub browser: &'a Browse,
//pub offset: usize,
//pub length: usize,
//pub index: usize,
//_screen: std::marker::PhantomData<S>
//}
// Commands supported by [Browse]
//#[derive(Debug, Clone, PartialEq)]
//pub enum BrowseCommand {
//Begin,
//Cancel,
//Confirm,
//Select(usize),
//Chdir(PathBuf),
//Filter(Arc<str>),
//}
//impl AsRef<Arc<[MenuItem]>> for MenuItems {
//fn as_ref (&self) -> &Arc<[MenuItem]> {
//&self.0
//}
//}
pub fn print_status (project: &Project) {
println!("Name: {:?}", &project.name);
println!("JACK: {:?}", &project.jack);
println!("Buffer: {:?}", &project.clock.chunk);
println!("Sample rate: {:?}", &project.clock.timebase.sr);
println!("MIDI PPQ: {:?}", &project.clock.timebase.ppq);
println!("Tempo: {:?}", &project.clock.timebase.bpm);
println!("Quantize: {:?}", &project.clock.quant);
println!("Launch: {:?}", &project.clock.sync);
println!("Playhead: {:?}us", &project.clock.playhead.usec);
println!("Playhead: {:?}s", &project.clock.playhead.sample);
println!("Playhead: {:?}p", &project.clock.playhead.pulse);
println!("Started: {:?}", &project.clock.started);
println!("Tracks:");
for (i, t) in project.tracks.iter().enumerate() {
println!(" Track {i}: {} {} {:?} {:?}", t.name, t.width,
&t.sequencer.play_clip, &t.sequencer.next_clip);
}
println!("Scenes:");
for (i, t) in project.scenes.iter().enumerate() {
println!(" Scene {i}: {} {:?}", &t.name, &t.clips);
}
println!("MIDI Ins: {:?}", &project.midi_ins);
println!("MIDI Outs: {:?}", &project.midi_outs);
println!("Audio Ins: {:?}", &project.audio_ins);
println!("Audio Outs: {:?}", &project.audio_outs);
// TODO git integration
// TODO dawvert integration
}