use crate::prelude::*; mod transport; mod chain; mod sequencer; mod sampler; mod mixer; mod looper; mod plugin; mod launcher; pub use self::transport::Transport; pub use self::chain::Chain; pub use self::sequencer::Sequencer; pub use self::sampler::Sampler; pub use self::mixer::Mixer; pub use self::looper::Looper; pub use self::plugin::Plugin; pub use self::launcher::Launcher; use crossterm::event; use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client}; pub trait Device: Render + Handle + PortList + Send + Sync { fn boxed (self) -> Box where Self: Sized + 'static { Box::new(self) } } pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box> { let device = Arc::new(Mutex::new(device)); let exited = Arc::new(AtomicBool::new(false)); let _input_thread = { let poll = std::time::Duration::from_millis(100); let exited = exited.clone(); let device = device.clone(); spawn(move || loop { // Exit if flag is set if exited.fetch_and(true, Ordering::Relaxed) { break } // Listen for events and send them to the main thread if event::poll(poll).is_ok() { let event = event::read().unwrap(); match event { Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. }) => { exited.store(true, Ordering::Relaxed); }, _ => if device.lock().unwrap().handle(&AppEvent::Input(event)).is_err() { break } } } }) }; stdout().execute(EnterAlternateScreen)?; enable_raw_mode()?; let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; //better_panic::install(); let better_panic_handler = better_panic::Settings::auto() .verbosity(better_panic::Verbosity::Full) .create_panic_handler(); std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{ stdout() .execute(crossterm::terminal::LeaveAlternateScreen) .unwrap(); crossterm::terminal::disable_raw_mode() .unwrap(); better_panic_handler(info); //writeln!(std::io::stderr(), "{}", info) //.unwrap(); //writeln!(std::io::stderr(), "{:?}", ::backtrace::Backtrace::new()) //.unwrap(); })); let sleep = std::time::Duration::from_millis(16); loop { terminal.draw(|frame|{ let area = frame.size(); let buffer = frame.buffer_mut(); device.lock().unwrap().render(buffer, area).expect("Failed to render content."); }).expect("Failed to render frame"); if exited.fetch_and(true, Ordering::Relaxed) { break } std::thread::sleep(sleep); }; //render_thread.join().expect("Failed to join render thread"); stdout() .queue(crossterm::terminal::LeaveAlternateScreen)? .flush()?; crossterm::terminal::disable_raw_mode()?; Ok(()) } impl Device for T {} pub trait Handle { // Handle an input event. // Returns Ok(true) if the device handled the event. // This is the mechanism which allows nesting of components;. fn handle (&mut self, _e: &AppEvent) -> Usually { Ok(false) } } pub trait Render { // Render something to an area of the buffer. // Returns area used by component. // This is insufficient but for the most basic dynamic layout algorithms. fn render (&self, _b: &mut Buffer, _a: Rect) -> Usually { Ok(Rect { x: 0, y: 0, width: 0, height: 0 }) } } pub trait Blit { // Render something to X, Y coordinates in a buffer, ignoring width/height. fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option