mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
This commit is contained in:
parent
3deef0641d
commit
d495d97516
16 changed files with 462 additions and 425 deletions
268
src/tek.rs
268
src/tek.rs
|
|
@ -2,31 +2,12 @@
|
|||
//#![feature(impl_trait_in_assoc_type)] // Used by EntriesIterator; TODO remove
|
||||
mod deps; pub use self::deps::*;
|
||||
|
||||
/// Banner.
|
||||
pub(crate) const HEADER: &'static str = r#"
|
||||
~ █▀█▀█ █▀▀█ █ █ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
|
||||
█ █▀ █▀▀▄ ~ heatwave is the new darkwave ~
|
||||
~ ▀ █▀▀█ ▀ ▀ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;
|
||||
|
||||
pub fn show_version () {
|
||||
println!("versions aint real man");
|
||||
}
|
||||
|
||||
/// Command-line entrypoint.
|
||||
#[allow(unused)]
|
||||
#[hotpath::main]
|
||||
fn main () -> Usually<()> {
|
||||
#[cfg(feature = "prof")] let _flame_guard = {
|
||||
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
|
||||
};
|
||||
tengri::Tui::setup_panic();
|
||||
#[cfg(feature = "cli")] let outcome = Config::watched(crate::cli::run_with_config);
|
||||
#[cfg(feature = "cli")] let outcome = Config::watched(run_with_config);
|
||||
#[cfg(not(feature = "cli"))] let outcome = Config::watched(run_new_plain);
|
||||
if let Err(e) = outcome {
|
||||
println!("{e:#?}");
|
||||
|
|
@ -35,209 +16,8 @@ fn main () -> Usually<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
fn run_new_plain (config: Config) -> Usually<()> {
|
||||
let name = "tek";
|
||||
tengri::Tui::run_main(Jack::new_run(name, move|jack|{
|
||||
let mode = ":menu";
|
||||
let title = "untitled!";
|
||||
let bpm = 74.;
|
||||
let clock = Clock::new(&jack, Some(bpm))?;
|
||||
let tracks = [];
|
||||
let scenes = [];
|
||||
Ok(App::new(None, 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()),
|
||||
), config, mode))
|
||||
})?)
|
||||
}
|
||||
|
||||
//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.try_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)
|
||||
//})?)?
|
||||
//}
|
||||
|
||||
#[cfg(feature = "cli")] pub mod cli {
|
||||
use crate::*;
|
||||
|
||||
pub fn run_with_config (config: Arc<Config>) -> Usually<()> {
|
||||
Cli::parse().run(Some(config))
|
||||
}
|
||||
|
||||
/// Command-line configuration.
|
||||
impl Cli {
|
||||
pub fn run (&self, mut config: Option<Arc<Config>>) -> Usually<()> {
|
||||
if config.is_none() {
|
||||
config = Some(Config::init_new(None)?);
|
||||
}
|
||||
self.action.run(config.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// The command-line interface descriptor.
|
||||
///
|
||||
/// ```
|
||||
/// let cli: tek::cli::Cli = Default::default();
|
||||
///
|
||||
/// use clap::CommandFactory;
|
||||
/// tek::cli::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,
|
||||
}
|
||||
impl Action {
|
||||
fn run (&self, config: Arc<Config>) -> Usually<()> {
|
||||
use Action::*;
|
||||
match self {
|
||||
Version => show_version(),
|
||||
Config => config.print(),
|
||||
Resume => todo!("resume session"),
|
||||
List => todo!("list sessions"),
|
||||
New(sesh) => Exit::run(|exit|Tui::run_main(
|
||||
exit.clone(),
|
||||
Arc::new(RwLock::new(App::new(
|
||||
Some(exit),
|
||||
sesh.init()?,
|
||||
config,
|
||||
":menu"
|
||||
))))).map(|_|())?,
|
||||
_ => todo!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Application modes that can be passed to the mommand line interface.
|
||||
///
|
||||
/// ```
|
||||
/// let action: tek::cli::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,
|
||||
}
|
||||
#[derive(Debug, Clone, Parser, Default)]
|
||||
pub struct ProjectInit {
|
||||
/// 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>,
|
||||
}
|
||||
impl ProjectInit {
|
||||
pub fn init (&self) -> Usually<Arrangement> {
|
||||
let Self {
|
||||
name, bpm, tracks, scenes,
|
||||
sync_lead: _, sync_follow: _,
|
||||
left_from, right_from, midi_from, midi_from_re,
|
||||
left_to, right_to, midi_to, midi_to_re,
|
||||
..
|
||||
} = self;
|
||||
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, *bpm)?,
|
||||
[].into_iter(),
|
||||
[].into_iter(),
|
||||
connect_midi_ins(
|
||||
&jack, &"M".to_string(), midi_from.as_ref(), Some(midi_from_re)
|
||||
)?.into_iter(),
|
||||
connect_midi_outs(
|
||||
&jack, &"M".to_string(), midi_to.as_ref(), Some(midi_to_re)
|
||||
)?.into_iter(),
|
||||
[].into_iter()
|
||||
.chain(
|
||||
connect_audio_ins(&jack, &"L".to_string(), &left_from, None)?.into_iter()
|
||||
)
|
||||
.chain(
|
||||
connect_audio_ins(&jack, &"R".to_string(), &right_from, None)?.into_iter()
|
||||
),
|
||||
[].into_iter()
|
||||
.chain(
|
||||
connect_audio_outs(&jack, &"L".to_string(), &left_to, None)?.into_iter()
|
||||
)
|
||||
.chain(
|
||||
connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()
|
||||
));
|
||||
proj.tracks_add_many(tracks.unwrap_or(0), None, [].into(), [].into())?;
|
||||
proj.scenes_add_many(scenes.unwrap_or(0))?;
|
||||
Ok(proj)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use self::cli::*;
|
||||
mod cli;
|
||||
|
||||
pub use self::config::*;
|
||||
mod config;
|
||||
|
|
@ -326,7 +106,11 @@ mod app {
|
|||
/// Contains the currently edited musical arrangement
|
||||
pub project: Arrangement,
|
||||
/// Error, if any
|
||||
pub error: Arc<RwLock<Option<Arc<str>>>>
|
||||
pub error: Arc<RwLock<Option<Arc<str>>>>,
|
||||
|
||||
#[cfg(feature = "prof2")]
|
||||
/// Tracing guard
|
||||
pub guard: Option<tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -501,7 +285,7 @@ mod bind {
|
|||
use crate::*;
|
||||
|
||||
tui_keys!(self: App, input {
|
||||
#[cfg(feature = "prof")] profiling::scope!("App::tui_keys!");
|
||||
#[cfg(feature = "prof2")] profiling::scope!("App::tui_keys!");
|
||||
let name = self.mode.as_ref();
|
||||
let mode = name.and_then(|m|self.config.get_mode(m)).as_ref().map(Arc::clone);
|
||||
if let Some(mode) = mode {
|
||||
|
|
@ -907,12 +691,12 @@ mod draw {
|
|||
/// Then, every top-level form of the DSL description is rendered.
|
||||
impl Draw<Tui> for App {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
#[cfg(feature = "prof")] profiling::scope!("App::draw");
|
||||
#[cfg(feature = "prof2")] profiling::scope!("App::draw");
|
||||
//self.perf.cycle(&mut |_|{
|
||||
self.draw_error(to)?;
|
||||
self.draw_modes(to)?;
|
||||
self.draw_debug(to)?;
|
||||
#[cfg(feature = "prof")] profiling::finish_frame!();
|
||||
#[cfg(feature = "prof2")] profiling::finish_frame!();
|
||||
Ok(Some(to.area().into()))
|
||||
//})
|
||||
}
|
||||
|
|
@ -1116,33 +900,3 @@ mod draw {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue