move port and midi to tengri

This commit is contained in:
i do not exist 2026-07-26 21:50:00 +03:00
parent 711e4fe6a9
commit 8144d75abe
26 changed files with 1520 additions and 1977 deletions

View file

@ -6,37 +6,295 @@
trait_alias,
type_changing_struct_update
)]
pub extern crate atomic_float;
pub extern crate xdg;
pub extern crate midly;
pub extern crate tengri;
/// Command-line entrypoint.
#[cfg(feature = "cli")] pub fn main () -> Usually<()> {
tui_setup_panic();
Config::watch(|config|{
tui_run_main(Jack::new_run("tek", move|jack|{
let clock = Clock::new(&jack, Some(51.))?;
let project = Arrangement::new(&jack, None, clock, vec![], vec![], vec![], vec![]);
let app = App::new(&jack, project, config, ":menu");
Ok(app)
})?)
}).map(|_|())
tengri::Tui::setup_panic();
let name = "tek";
let mode = ":menu";
let title = "untitled!";
let bpm = 74.;
Config::watch(|config|tengri::Tui::run_main(Jack::new_run(name, move|jack|{
let clock = Clock::new(&jack, Some(bpm))?;
let tracks = [];
let scenes = [];
let project = Arrangement::new(
&jack, title.into(), clock,
tracks.into_iter(),
scenes.into_iter(),
Connect::midi_ins(&jack, &"M", &[], None)?.into_iter(),
Connect::midi_outs(&jack, &"M", &[], None)?.into_iter(),
[].into_iter()
.chain(Connect::audio_ins(&jack, &"L", &[], None)?.into_iter())
.chain(Connect::audio_ins(&jack, &"R", &[], None)?.into_iter()),
[].into_iter()
.chain(Connect::audio_outs(&jack, &"L", &[], None)?.into_iter())
.chain(Connect::audio_outs(&jack, &"R", &[], None)?.into_iter()),
);
Ok(App::new(&jack, project, config, mode))
})?)).map(|_|())
}
pub mod deps;
pub(crate) use self::deps::*;
pub mod device;
pub use self::device::*;
pub mod app;
pub use self::app::*;
/// CLI banner.
pub(crate) const HEADER: &'static str = r#"
~ █▀█▀█ █▀▀█ █ █ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
█ █▀ █▀▀▄ ~ v0.4.0, 2026 heatwave edition ~
~ ▀ █▀▀█ ▀ ▀ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;
#[cfg(feature = "cli")]
mod cli {
use crate::*;
/// The command-line interface descriptor.
///
/// ```
/// let cli: tek::Cli = Default::default();
///
/// use clap::CommandFactory;
/// tek::Cli::command().debug_assert();
/// ```
#[derive(Parser)]
#[command(name = "tek", version, about = Some(HEADER), long_about = Some(HEADER))]
#[derive(Debug, Default)] pub struct Cli {
/// Pre-defined configuration modes.
///
/// TODO: Replace these with scripted configurations.
#[command(subcommand)] pub action: Action,
}
/// 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 {
/// Name of JACK client
#[arg(short='n', long)] name: Option<String>,
/// Whether to attempt to become transport master
#[arg(short='Y', long, default_value_t = false)] sync_lead: bool,
/// Whether to sync to external transport master
#[arg(short='y', long, default_value_t = true)] sync_follow: bool,
/// Initial tempo in beats per minute
#[arg(short='b', long, default_value = None)] bpm: Option<f64>,
/// Whether to include a transport toolbar (default: true)
#[arg(short='c', long, default_value_t = true)] show_clock: bool,
/// MIDI outs to connect to (multiple instances accepted)
#[arg(short='I', long)] midi_from: Vec<String>,
/// MIDI outs to connect to (multiple instances accepted)
#[arg(short='i', long)] midi_from_re: Vec<String>,
/// MIDI ins to connect to (multiple instances accepted)
#[arg(short='O', long)] midi_to: Vec<String>,
/// MIDI ins to connect to (multiple instances accepted)
#[arg(short='o', long)] midi_to_re: Vec<String>,
/// Audio outs to connect to left input
#[arg(short='l', long)] left_from: Vec<String>,
/// Audio outs to connect to right input
#[arg(short='r', long)] right_from: Vec<String>,
/// Audio ins to connect from left output
#[arg(short='L', long)] left_to: Vec<String>,
/// Audio ins to connect from right output
#[arg(short='R', long)] right_to: Vec<String>,
/// Tracks to creat
#[arg(short='t', long)] tracks: Option<usize>,
/// Scenes to create
#[arg(short='s', long)] scenes: Option<usize>,
},
/// Import media as new session.
Import,
/// Show configuration.
Config,
/// Show version.
Version,
}
/// Command-line configuration.
#[cfg(feature = "cli")]
impl Cli {
pub fn run (&self) -> Usually<()> {
self.action.run(Config::init_new(None)?)
}
}
#[cfg(feature = "cli")]
impl Action {
fn run (&self, config: Config) -> Usually<()> {
use Action::*;
match self {
Version => show_version(),
Config => print_config(&config),
List => todo!("list sessions"),
Resume => todo!("resume session"),
New {
name, bpm, tracks, scenes,
sync_lead, sync_follow,
midi_from: mf, midi_from_re: mfr, midi_to: mt, midi_to_re: mtr,
left_from: lf, right_from: rf, left_to: lt, right_to: rt, ..
} => {
let name = name.as_ref().map_or("tek", |x|x.as_str());
let jack = Jack::new(&name)?;
let mut proj = Arrangement::new(
&jack,
name.into(),
Clock::new(&jack, None)?,
[].into_iter(),
[].into_iter(),
Connect::midi_ins(&jack, &"M", &[], None)?.into_iter(),
Connect::midi_outs(&jack, &"M", &[], None)?.into_iter(),
[].into_iter()
.chain(Connect::audio_ins(&jack, &"L", &[], None)?.into_iter())
.chain(Connect::audio_ins(&jack, &"R", &[], None)?.into_iter()),
[].into_iter()
.chain(Connect::audio_outs(&jack, &"L", &[], None)?.into_iter())
.chain(Connect::audio_outs(&jack, &"R", &[], None)?.into_iter()),
);
//&jack, Clock::new(&jack, *bpm)?, &lf, &lt, &rf, &rt, &mf, &mt, &mfr, &mtr)?;
proj.tracks_add(tracks.unwrap_or(0), None, &[], &[])?;
proj.scenes_add(scenes.unwrap_or(0))?;
//if matches!(self, Action::Status) {
//// Show status and exit
//tek_print_status(&proj);
//return Ok(())
//}
// Initialize the app state
let app = Arc::new(RwLock::new(App::new(&jack, proj, config, ":menu")));
//if matches!(self, Action::Headless) {
//// TODO: Headless mode (daemon + client over IPC, then over network...)
//println!("todo headless");
//return Ok(())
//}
let (_keyboard, _terminal) = Exit::run(|exited|Tui::io(
exited.as_ref(),
&app,
Duration::from_millis(100),
Duration::from_millis(10),
std::io::stdout()
))?;
},
_ => todo!()
}
Ok(())
}
}
//pub fn tui (
//app: Arc<RwLock<App>>,
//jack: Jack,
//sync_lead: &bool,
//sync_follow: &bool,
//) -> Usually<()> {
//// Run the [Tui] and [Jack] threads with the [App] state.
//Tui::run_main(&jack.run(move|jack|{
//// Between jack init and app's first cycle:
////jack.sync_lead(*sync_lead, |mut state|{
////let clock = app.write().unwrap().clock();
////clock.playhead.update_from_sample(state.position.frame() as f64);
////state.position.bbt = Some(clock.bbt());
////state.position
////})?;
////jack.sync_follow(*sync_follow)?;
//// FIXME: They don't work properly.
//Ok(app)
//})?)?
//}
pub fn show_version () {
println!("todo version");
}
pub fn print_config (config: &Config) {
use ::ansi_term::Color::*;
println!("{:?}", config.dirs);
for (k, v) in config.views.read().unwrap().iter() {
println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}")));
}
for (k, v) in config.binds.read().unwrap().iter() {
println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}")));
for (k, v) in v.0.iter() {
print!("{} ", &Yellow.paint(match &k.0 {
Event::Key(KeyEvent { modifiers, .. }) =>
format!("{:>16}", format!("{modifiers}")),
_ => unimplemented!()
}));
print!("{}", &Yellow.bold().paint(match &k.0 {
Event::Key(KeyEvent { code, .. }) =>
format!("{:<10}", format!("{code}")),
_ => unimplemented!()
}));
for v in v.iter() {
print!(" => {:?}", v.commands);
print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default());
println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default());
//println!(" {:?}", v.source);
}
}
}
config.modes.for_each(|k, v|{
println!();
for v in v.name.iter() { print!("{}", Green.bold().paint(format!("{v} "))); }
for v in v.info.iter() { print!("\n{}", Green.paint(format!("{v}"))); }
print!("\n{} {}", Blue.paint("TOOL"), Green.bold().paint(format!("{k:<16}")));
print!("\n{}", Blue.paint("KEYS"));
for v in v.keys.iter() { print!("{}", Green.paint(format!(" {v}"))); }
println!();
v.modes.for_each(|k, v|{
print!("{} {} {:?}", Blue.paint("MODE"), Green.bold().paint(format!("{k:<16}")), v.name);
print!( " INFO={:?}", v.info);
print!( " VIEW={:?}", v.view);
println!(" KEYS={:?}", v.keys);
});
print!("{}", Blue.paint("VIEW"));
for v in v.view.iter() { print!("{}", Green.paint(format!(" {v}"))); }
println!();
});
}
pub fn print_status (project: &Arrangement) {
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
}
}