diff --git a/config/config_arranger.edn b/config/config_arranger.edn index 07f0d65a..51932ec3 100644 --- a/config/config_arranger.edn +++ b/config/config_arranger.edn @@ -2,6 +2,14 @@ (info "A session grid.") +(view + (bsp/a :view-dialog + (bsp/s (fixed/y 1 :view-transport) + (bsp/n (fixed/y 1 :view-status) + (fill/xy (bsp/a + (fill/xy (align/e :view-pool)) + :view-arranger)))))) + (keys (layer-if :focus-message "./keys_message.edn") (layer-if :focus-device-add "./keys_device_add.edn") @@ -16,17 +24,3 @@ (layer "./keys_clock.edn") (layer "./keys_arranger.edn") (layer "./keys_global.edn")) - -(view (bsp/a :view-dialog - (bsp/s - (fixed/y 8 (bsp/e - (fixed/x 20 (fill/y (align/n (bsp/s :view-status-v - (bsp/s :view-audio-ports-status :view-editor-status))))) - (fill/xy (align/n (bsp/s :view-arranger-track-names - (bsp/s :view-arranger-track-outputs - (bsp/s :view-arranger-track-devices :view-arranger-track-inputs))))))) - (bsp/w - (fixed/x 20 :view-pool) - (bsp/e - (fixed/x 20 (fill/xy (align/n :view-arranger-scene-names))) - (fill/xy (align/n :view-arranger-scene-clips))))))) diff --git a/config/config_groovebox.edn b/config/config_groovebox.edn index 3afc185b..c313319f 100644 --- a/config/config_groovebox.edn +++ b/config/config_groovebox.edn @@ -2,6 +2,18 @@ (info "A sequencer with built-in sampler.") +(view + (bsp/a :view-dialog + (bsp/s (fixed/y 1 :view-transport) + (bsp/n (fixed/y 1 :view-status) + (bsp/w :view-meters-output + (bsp/e :view-meters-input + (bsp/n :view-sample-info + (bsp/n (fixed/y 5 :view-sample-viewer) + (bsp/w :view-pool + (bsp/e :view-samples-keys + (fill/y :view-editor))))))))))) + (keys (layer-if :focus-browser "./keys_browser.edn") (layer-if :focus-pool-rename "./keys_rename.edn") @@ -10,11 +22,3 @@ (layer "./keys_editor.edn") (layer "./keys_sampler.edn") (layer "./keys_global.edn")) - -(view (bsp/a :view-dialog (bsp/w :view-meters-output (bsp/e :view-meters-input - (bsp/n (fixed/y 6 (bsp/e :view-sample-status :view-sample-viewer)) - (bsp/e - (fill/y (align/n (bsp/s :view-status-v (bsp/s - (bsp/s :view-midi-ports-status :view-audio-ports-status) - (bsp/n :view-editor-status :view-pool))))) - (bsp/e :view-samples-keys :view-editor))))))) diff --git a/config/config_sampler.edn b/config/config_sampler.edn index 2739e312..c2486385 100644 --- a/config/config_sampler.edn +++ b/config/config_sampler.edn @@ -2,12 +2,12 @@ (info "A sampling soundboard.") -(keys - (layer "./keys_sampler.edn") - (layer "./keys_global.edn")) - (view (bsp/a :view-dialog (bsp/s (fixed/y 1 :view-transport) (bsp/n (fixed/y 1 :view-status) (fill/xy :view-samples-grid))))) + +(keys + (layer "./keys_sampler.edn") + (layer "./keys_global.edn")) diff --git a/config/config_sequencer.edn b/config/config_sequencer.edn index 868bc0ba..a267cebd 100644 --- a/config/config_sequencer.edn +++ b/config/config_sequencer.edn @@ -2,14 +2,6 @@ (info "A MIDI sequencer.") -(keys - (layer-if :focus-browser "./keys_browser.edn") - (layer-if :mode-pool-rename "./keys_rename.edn") - (layer-if :mode-pool-length "./keys_length.edn") - (layer "./keys_editor.edn") - (layer "./keys_clock.edn") - (layer "./keys_global.edn")) - (view (bsp/a :view-dialog (bsp/s (fixed/y 1 :view-transport) @@ -17,3 +9,11 @@ (fill/xy (bsp/a (fill/xy (align/e :view-pool)) :view-editor))))) + +(keys + (layer-if :focus-browser "./keys_browser.edn") + (layer-if :mode-pool-rename "./keys_rename.edn") + (layer-if :mode-pool-length "./keys_length.edn") + (layer "./keys_editor.edn") + (layer "./keys_clock.edn") + (layer "./keys_global.edn")) diff --git a/config/config_transport.edn b/config/config_transport.edn index 23ca4f6e..47324e7b 100644 --- a/config/config_transport.edn +++ b/config/config_transport.edn @@ -2,8 +2,8 @@ (info "A JACK transport controller.") +(view :view-transport) + (keys (layer "./keys_clock.edn") (layer "./keys_global.edn")) - -(view :view-transport) diff --git a/config/keys_global.edn b/config/keys_global.edn index 6ea6d748..8e9f4233 100644 --- a/config/keys_global.edn +++ b/config/keys_global.edn @@ -1,9 +1,9 @@ -(@esc dialog :dialog-none) -(@f1 dialog :dialog-help) -(@f6 dialog :dialog-save) -(@f8 dialog :dialog-options) -(@f9 dialog :dialog-load) -(@f10 dialog :dialog-quit) +(@esc dialog :dialog-none) +(@f1 dialog :dialog-help) +(@f6 dialog :dialog-save) +(@f8 dialog :dialog-options) +(@f9 dialog :dialog-load) +(@f10 dialog :dialog-quit) (@u undo 1) (@r redo 1) diff --git a/config/keys_sampler.edn b/config/keys_sampler.edn index bb4b5b57..0e8aa127 100644 --- a/config/keys_sampler.edn +++ b/config/keys_sampler.edn @@ -2,11 +2,7 @@ (@down sampler select :sample-below) (@left sampler select :sample-to-left) (@right sampler select :sample-to-right) - (@r sampler record-toggle :sample-selected) (@shift-R sampler record-cancel) (@p sampler play-sample :sample-selected) (@P sampler stop-sample :sample-selected) - -(@shift-f6 dialog :dialog-import-sample) -(@shift-f9 dialog :dialog-export-sample) diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index 4948c2c1..5a459295 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -67,7 +67,7 @@ impl App { } pub fn browser (&self) -> Option<&Browser> { self.dialog.as_ref().and_then(|dialog|match dialog { - Dialog::Browser(_, b) => Some(b), + Dialog::Save(b) | Dialog::Load(b) => Some(b), _ => None }) } @@ -156,20 +156,11 @@ pub enum Dialog { Menu(usize), Device(usize), Message(Message), - Browser(BrowserTarget, Browser), + Save(Browser), + Load(Browser), Options, } -#[derive(Clone, Debug)] -pub enum BrowserTarget { - SaveProject, - LoadProject, - ImportSample(Arc>>), - ExportSample(Arc>>), - ImportClip(Arc>>), - ExportClip(Arc>>), -} - /// Various possible messages #[derive(PartialEq, Clone, Copy, Debug)] pub enum Message { @@ -244,22 +235,10 @@ impl App { Some(Dialog::Menu(0)) } fn dialog_save (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::SaveProject, Browser::new(None).unwrap())) + Some(Dialog::Save(Default::default())) } fn dialog_load (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::LoadProject, Browser::new(None).unwrap())) - } - fn dialog_import_clip (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::ImportClip(Default::default()), Browser::new(None).unwrap())) - } - fn dialog_export_clip (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::ExportClip(Default::default()), Browser::new(None).unwrap())) - } - fn dialog_import_sample (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::ImportSample(Default::default()), Browser::new(None).unwrap())) - } - fn dialog_export_sample (&self) -> Option { - Some(Dialog::Browser(BrowserTarget::ExportSample(Default::default()), Browser::new(None).unwrap())) + Some(Dialog::Load(Default::default())) } fn dialog_options (&self) -> Option { Some(Dialog::Options) diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 2d347a3b..3ffbd439 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -14,36 +14,6 @@ impl App { pub fn view_nil (&self) -> impl Content + use<'_> { "nil" } - pub fn view_status_v (&self) -> impl Content + use<'_> { - self.update_clock(); - let cache = self.view_cache.read().unwrap(); - let theme = self.color; - let playing = self.clock().is_rolling(); - Tui::bg(theme.darkest.rgb, Fixed::xy(20, 6, col!( - Fill::x(Align::w(Bsp::e( - Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, - Either::new(false, // TODO - Thunk::new(move||Fixed::x(9, Either::new(playing, - Tui::fg(Rgb(0, 255, 0), " PLAYING "), - Tui::fg(Rgb(255, 128, 0), " STOPPED "))) - ), - Thunk::new(move||Fixed::x(5, Either::new(playing, - Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), - Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) - ) - ) - )), - Bsp::s( - FieldH(theme, "Beat", cache.beat.view.clone()), - FieldH(theme, "Time", cache.time.view.clone()), - ), - ))), - Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), - Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), - ))) - } pub fn view_status (&self) -> impl Content + use<'_> { self.update_clock(); let cache = self.view_cache.read().unwrap(); @@ -57,172 +27,16 @@ impl App { cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone()) } pub fn view_editor (&self) -> impl Content + use<'_> { - self.editor() - } - pub fn view_editor_status (&self) -> impl Content + use<'_> { - self.editor().map(|e|Bsp::s(e.clip_status(), e.edit_status())) - } - pub fn view_midi_ports_status (&self) -> impl Content + use<'_> { - self.project.get_track().map(|track|Bsp::s( - Fixed::xy(20, 1 + track.sequencer.midi_ins.len() as u16, FieldV(self.color, - format!("MIDI ins: "), - Map::south(1, ||track.sequencer.midi_ins.iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))), - Fixed::xy(20, 1 + track.sequencer.midi_outs.len() as u16, FieldV( - self.color, - format!("MIDI outs: "), - Map::south(1, ||track.sequencer.midi_outs.iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))) - )) - } - pub fn view_audio_ports_status (&self) -> impl Content + use<'_> { - self.project.get_track().map(|track|Bsp::s( - track.devices.get(0).map(|device| - Fixed::xy(20, 1 + device.audio_ins().len() as u16, FieldV(self.color, - format!("Audio ins: "), - Map::south(1, ||device.audio_ins().iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))), - track.devices.last().map(|device| - Fixed::xy(20, 1 + device.audio_outs().len() as u16, FieldV(self.color, - format!("Audio outs:"), - Map::south(1, ||device.audio_outs().iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))), - )) + self.editor().map(|e|Bsp::n(Bsp::e(e.clip_status(), e.edit_status()), e)) } pub fn view_arranger (&self) -> impl Content + use<'_> { ArrangerView::new(&self.project, self.editor.as_ref()) } - pub fn view_arranger_scene_names <'a> (&'a self) -> impl Content + use<'a> { - let h = self.project.scenes.len() as u16 * 2; - let bg = self.color.darker.rgb; - Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( - ||self.project.scenes.iter().skip(self.project.scene_scroll), - move|scene: &Scene, index| - Push::y(index as u16 * 2u16, Fixed::xy(20, 2, - Tui::bg(scene.color.dark.rgb, Align::nw(Bsp::e( - format!(" {index:2} "), - Tui::fg(Rgb(255, 255, 255), - Tui::bold(true, format!("{}", scene.name))))))))))))) - } - pub fn view_arranger_scene_clips <'a> (&'a self) -> impl Content + use<'a> { - let h = self.project.scenes.len() as u16 * 2; - let bg = self.color.darker.rgb; - Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( - ||self.project.scenes.iter().skip(self.project.scene_scroll), - move|scene: &'a Scene, index1| - Push::y(index1 as u16 * 2u16, Fixed::xy(20, 2, - Map::new( - move||scene.clips.iter().skip(self.project.track_scroll), - move|clip: &'a Option>>, index2|{ - let (theme, text) = if let Some(clip) = clip { - let clip = clip.read().unwrap(); - (clip.color, clip.name.clone()) - } else { - (scene.color, Default::default()) - }; - Push::x(index2 as u16 * 14, Tui::bg(theme.dark.rgb, Bsp::e( - format!(" {index1:2} {index2:2} "), - Tui::fg(Rgb(255, 255, 255), - Tui::bold(true, format!("{}", text)))))) - })))))))) - } - pub fn view_arranger_track_names (&self) -> impl Content + use<'_> { - let mut max_outputs = 0u16; - for track in self.project.tracks.iter() { - max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[t]rack"), "[T] Add"))), - Align::w(Fixed::y(max_outputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &Track, usize, usize), _| - Push::x(index as u16 * 14, Fixed::xy(track.width as u16, max_outputs + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s( - Tui::fg(Rgb(255, 255, 255), Tui::bold(true, format!("{}", track.name))), - format!("{index} {x1} {x2}"))))))))))))) - } - pub fn view_arranger_track_outputs <'a> (&'a self) -> impl Content + use<'a> { - let mut max_outputs = 0u16; - for track in self.project.tracks.iter() { - max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[o]utput"), "[O] Add"))), - Align::w(Fixed::y(max_outputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Tui::bg(track.color.dark.rgb, Fixed::xy( - track.width as u16, - max_outputs + 1, - Align::nw(Bsp::s( - format!("[mut] [sol]"), - Map::south(1, ||track.sequencer.midi_outs.iter(), - |port, index|Tui::fg(Rgb(255, 255, 255), - format!("{index}: {}", port.name()))))))))))))))) - } - pub fn view_arranger_track_inputs <'a> (&'a self) -> impl Content + use<'a> { - let mut max_inputs = 0u16; - for track in self.project.tracks.iter() { - max_inputs = max_inputs.max(track.sequencer.midi_ins.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[i]nputs"), "[I] Add"))), - Fill::x(Align::w(Fixed::y(max_inputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Fixed::xy(track.width as u16, max_inputs + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s( - format!("[rec] [mon]"), - Map::south(1, ||track.sequencer.midi_ins.iter(), - |port, index|Tui::fg(Rgb(255, 255, 255), - format!("{index}: {}", port.name())))))))))))))))) - } - pub fn view_arranger_track_devices <'a> (&'a self) -> impl Content + use<'a> { - let mut max_devices = 2u16; - for track in self.project.tracks.iter() { - max_devices = max_devices.max(track.devices.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[d]evice"), "[D] Add"))), - Fixed::y(max_devices, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Fixed::xy(track.width as u16, max_devices + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Map::south(1, move||0..max_devices, - |_, index|format!("{index}: {}", "--------")))))))))))) - } - pub fn view_arranger_track_scenes <'a> (&'a self) -> impl Content + use<'a> { - let mut max_devices = 0u16; - for track in self.project.tracks.iter() { - max_devices = max_devices.max(track.devices.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "Devices"), "[d] Select", "[D] Add"))), - Fixed::y(max_devices + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Fixed::xy(track.width as u16, max_devices + 1, - Align::nw(Map::south(1, ||track.devices.iter(), - |device, index|format!("{index}: {}", device.name()))))))))))) - } pub fn view_pool (&self) -> impl Content + use<'_> { - Fixed::x(20, Bsp::s( - Fill::x(Align::w(FieldH(self.color, "Clip pool:", ""))), - Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), PoolView(&self.project.pool)))), - )) + PoolView(&self.project.pool) } pub fn view_samples_keys (&self) -> impl Content + use<'_> { - self.project.sampler().map(|s|s.view_list(true, self.editor().unwrap())) + self.project.sampler().map(|s|s.view_list(false, self.editor().unwrap())) } pub fn view_samples_grid (&self) -> impl Content + use<'_> { self.project.sampler().map(|s|s.view_grid()) @@ -233,9 +47,6 @@ impl App { pub fn view_sample_info (&self) -> impl Content + use<'_> { self.project.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos())) } - pub fn view_sample_status (&self) -> impl Content + use<'_> { - self.project.sampler().map(|s|s.view_sample_status(self.editor().unwrap().get_note_pos())) - } pub fn view_meters_input (&self) -> impl Content + use<'_> { self.project.sampler().map(|s|s.view_meters_input()) } @@ -251,8 +62,10 @@ impl App { self.view_dialog_menu().boxed(), Dialog::Help(offset) => self.view_dialog_help(*offset).boxed(), - Dialog::Browser(target, browser) => - self.view_dialog_browser(target, browser).boxed(), + Dialog::Save(browser) => + self.view_dialog_save().boxed(), + Dialog::Load(browser) => + self.view_dialog_load().boxed(), Dialog::Options => self.view_dialog_options().boxed(), Dialog::Device(index) => @@ -295,45 +108,21 @@ impl App { pub fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content + use<'a> { Bsp::s(message, Bsp::s("", "[ OK ]")) } - pub fn view_dialog_browser <'a> (&'a self, target: &BrowserTarget, browser: &'a Browser) -> impl Content + use<'a> { + pub fn view_dialog_save <'a> (&'a self) -> impl Content + use<'a> { Bsp::s( - Padding::xy(3, 1, Fill::x(Align::w(FieldV( - self.color, - match target { - BrowserTarget::SaveProject => "Save project:", - BrowserTarget::LoadProject => "Load project:", - BrowserTarget::ImportSample(_) => "Import sample:", - BrowserTarget::ExportSample(_) => "Export sample:", - BrowserTarget::ImportClip(_) => "Import clip:", - BrowserTarget::ExportClip(_) => "Export clip:", - }, - Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))), + Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( + Tui::bold(true, " Save project: "), + Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))), Outer(true, Style::default().fg(Tui::g(96))) - .enclose(Fill::xy(browser))) + .enclose(Fill::xy("todo file browser"))) } - pub fn view_dialog_load <'a> (&'a self, browser: &'a Browser) -> impl Content + use<'a> { + pub fn view_dialog_load <'a> (&'a self) -> impl Content + use<'a> { Bsp::s( Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( Tui::bold(true, " Load project: "), Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))), Outer(true, Style::default().fg(Tui::g(96))) - .enclose(Fill::xy(browser))) - } - pub fn view_dialog_export <'a> (&'a self, browser: &'a Browser) -> impl Content + use<'a> { - Bsp::s( - Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( - Tui::bold(true, " Export: "), - Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))), - Outer(true, Style::default().fg(Tui::g(96))) - .enclose(Fill::xy(browser))) - } - pub fn view_dialog_import <'a> (&'a self, browser: &'a Browser) -> impl Content + use<'a> { - Bsp::s( - Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( - Tui::bold(true, " Import: "), - Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))), - Outer(true, Style::default().fg(Tui::g(96))) - .enclose(Fill::xy(browser))) + .enclose(Fill::xy("todo file browser"))) } pub fn view_dialog_options <'a> (&'a self) -> impl Content + use<'a> { "TODO" diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index 97a7f499..1d407373 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -60,7 +60,7 @@ pub enum LaunchMode { /// Number of tracks #[arg(short = 'x', long, default_value_t = 4)] tracks: usize, /// Width of tracks - #[arg(short = 'w', long, default_value_t = 14)] track_width: usize, + #[arg(short = 'w', long, default_value_t = 12)] track_width: usize, }, /// TODO: A MIDI-controlled audio mixer Mixer, diff --git a/crates/device/src/arranger.rs b/crates/device/src/arranger.rs index 38f5b313..2939b23e 100644 --- a/crates/device/src/arranger.rs +++ b/crates/device/src/arranger.rs @@ -3,7 +3,7 @@ use crate::*; /// Define a type alias for iterators of sized items (columns). macro_rules! def_sizes_iter { ($Type:ident => $($Item:ty),+) => { - pub trait $Type<'a> = + pub(crate) trait $Type<'a> = Iterator + Send + Sync + 'a; } } diff --git a/crates/device/src/arranger/arranger_model.rs b/crates/device/src/arranger/arranger_model.rs index a34134d7..46687ab1 100644 --- a/crates/device/src/arranger/arranger_model.rs +++ b/crates/device/src/arranger/arranger_model.rs @@ -64,12 +64,27 @@ impl Arrangement { } /// Width available to display tracks. pub fn w_tracks_area (&self, is_editing: bool) -> u16 { - self.w().saturating_sub(self.w_sidebar(is_editing)) + self.w().saturating_sub(2 * self.w_sidebar(is_editing)) } /// Height of display pub fn h (&self) -> u16 { self.size.h() as u16 } + /// Height available to display track headers. + pub fn h_tracks_area (&self) -> u16 { + 5 // FIXME + //self.h().saturating_sub(self.h_inputs() + self.h_outputs()) + } + /// Height available to display tracks. + pub fn h_scenes_area (&self) -> u16 { + //15 + self.h().saturating_sub( + self.h_inputs() + + self.h_outputs() + + self.h_devices() + + 13 // FIXME + ) + } /// Height taken by all scenes. pub fn h_scenes (&self, is_editing: bool) -> u16 { self.scenes_with_sizes( @@ -96,27 +111,27 @@ impl Arrangement { //1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } /// Get the active track - pub fn get_track (&self) -> Option<&Track> { + fn get_track (&self) -> Option<&Track> { let index = self.selection().track()?; Has::>::get(self).get(index) } /// Get a mutable reference to the active track - pub fn get_track_mut (&mut self) -> Option<&mut Track> { + fn get_track_mut (&mut self) -> Option<&mut Track> { let index = self.selection().track()?; Has::>::get_mut(self).get_mut(index) } /// Get the active scene - pub fn get_scene (&self) -> Option<&Scene> { + fn get_scene (&self) -> Option<&Scene> { let index = self.selection().scene()?; Has::>::get(self).get(index) } /// Get a mutable reference to the active scene - pub fn get_scene_mut (&mut self) -> Option<&mut Scene> { + fn get_scene_mut (&mut self) -> Option<&mut Scene> { let index = self.selection().scene()?; Has::>::get_mut(self).get_mut(index) } /// Get the active clip - pub fn get_clip (&self) -> Option>> { + fn get_clip (&self) -> Option>> { self.get_scene()?.clips.get(self.selection().track()?)?.clone() } /// Put a clip in a slot diff --git a/crates/device/src/arranger/arranger_port.rs b/crates/device/src/arranger/arranger_port.rs index a5a1a586..152edc73 100644 --- a/crates/device/src/arranger/arranger_port.rs +++ b/crates/device/src/arranger/arranger_port.rs @@ -3,7 +3,7 @@ use crate::*; impl<'a> ArrangerView<'a> { pub(crate) fn input_routes (&'a self) -> impl Content + 'a { - Tryptich::top(self.arrangement.h_inputs()) + Tryptich::top(self.inputs_height) .left(self.width_side, io_ports(Tui::g(224), Tui::g(32), ||self.arrangement.midi_ins_with_sizes())) .middle(self.width_mid, @@ -127,7 +127,7 @@ impl<'a> ArrangerView<'a> { } pub(crate) fn output_conns (&'a self) -> impl Content + 'a { - Tryptich::top(self.arrangement.h_outputs()) + Tryptich::top(self.outputs_height) .left(self.width_side, io_ports( Tui::g(224), Tui::g(32), ||self.arrangement.midi_outs_with_sizes())) .middle(self.width_mid, per_track_top( diff --git a/crates/device/src/arranger/arranger_view.rs b/crates/device/src/arranger/arranger_view.rs index cbd3a4b1..a903d26f 100644 --- a/crates/device/src/arranger/arranger_view.rs +++ b/crates/device/src/arranger/arranger_view.rs @@ -2,19 +2,26 @@ use crate::*; pub struct ArrangerView<'a> { pub arrangement: &'a Arrangement, + pub is_editing: bool, + pub width: u16, pub width_mid: u16, pub width_side: u16, + + pub inputs_height: u16, + pub outputs_height: u16, + pub scene_last: usize, pub scene_scroll: Fill>, pub scene_selected: Option, - /// Height available to display scene/track content. pub scenes_height: u16, + pub track_scroll: Fill>, pub track_selected: Option, - /// Height available to display track headers. pub tracks_height: u16, + + pub show_debug_info: bool, } impl<'a> ArrangerView<'a> { @@ -23,15 +30,20 @@ impl<'a> ArrangerView<'a> { editor: Option<&'a MidiEditor> ) -> Self { let is_editing = editor.is_some(); - let h_tracks_area = 5; - let h_scenes_area = (arrangement.height() as u16).saturating_sub(20); + let h_tracks_area = arrangement.h_tracks_area(); + let h_scenes_area = arrangement.h_scenes_area(); let h_scenes = arrangement.h_scenes(is_editing); Self { arrangement, is_editing, - width: arrangement.w_tracks_area(is_editing), - width_mid: arrangement.w_tracks_area(is_editing).saturating_sub(20), - width_side: 20, + + width: arrangement.w(), + width_mid: arrangement.w_tracks_area(is_editing), + width_side: arrangement.w_sidebar(is_editing), + + inputs_height: arrangement.h_inputs(), + outputs_height: arrangement.h_outputs(), + scenes_height: h_scenes_area, scene_selected: arrangement.selection().scene(), scene_last: arrangement.scenes.len().saturating_sub(1), @@ -40,6 +52,7 @@ impl<'a> ArrangerView<'a> { length: h_scenes_area as usize, total: h_scenes as usize, })), + tracks_height: h_tracks_area, track_selected: arrangement.selection().track(), track_scroll: Fill::x(Fixed::y(1, ScrollbarH { @@ -47,6 +60,8 @@ impl<'a> ArrangerView<'a> { length: h_tracks_area as usize, total: h_scenes as usize, })), + + show_debug_info: false } } } @@ -60,7 +75,7 @@ impl<'a> Content for ArrangerView<'a> { let bg = |x|Tui::bg(Reset, x); //let track_scroll = |x|Bsp::s(&self.track_scroll, x); //let scene_scroll = |x|Bsp::e(&self.scene_scroll, x); - self.arrangement.size.of(outs(tracks(devices(ins(bg(self.scenes(&None))))))) + outs(tracks(devices(ins(bg(self.scenes(&None)))))) } } @@ -133,6 +148,7 @@ impl<'a> ArrangerView<'a> { scenes_height, scene_last, scene_selected, track_selected, is_editing, .. } = self; + let selection = Has::::get(self.arrangement); let selected_track = selection.track(); let selected_scene = selection.scene(); diff --git a/crates/device/src/browser/browser_view.rs b/crates/device/src/browser/browser_view.rs index 45f42863..ae02f3c3 100644 --- a/crates/device/src/browser/browser_view.rs +++ b/crates/device/src/browser/browser_view.rs @@ -1,33 +1,19 @@ use crate::*; -content!(TuiOut: |self: Browser|Map::south(1, ||EntriesIterator { - offset: 0, - index: 0, - length: self.dirs.len() + self.files.len(), - browser: self, -}, |entry, index|Fill::x(Align::w(entry)))); - -struct EntriesIterator<'a> { - browser: &'a Browser, - offset: usize, - length: usize, - index: usize, -} - -impl<'a> Iterator for EntriesIterator<'a> { - type Item = Modify<&'a str>; - fn next (&mut self) -> Option { - let dirs = self.browser.dirs.len(); - let files = self.browser.files.len(); - let index = self.index; - if self.index < dirs { - self.index += 1; - Some(Tui::bold(true, self.browser.dirs[index].1.as_str())) - } else if self.index < dirs + files { - self.index += 1; - Some(Tui::bold(false, self.browser.files[index - dirs].1.as_str())) - } else { - None +content!(TuiOut: |self: Browser| /*Stack::down(|add|{ + let mut i = 0; + for (_, name) in self.dirs.iter() { + if i >= self.scroll { + add(&Tui::bold(i == self.index, name.as_str()))?; } + i += 1; } -} + for (_, name) in self.files.iter() { + if i >= self.scroll { + add(&Tui::bold(i == self.index, name.as_str()))?; + } + i += 1; + } + add(&format!("{}/{i}", self.index))?; + Ok(()) +})*/"todo"); diff --git a/crates/device/src/device.rs b/crates/device/src/device.rs index 0512e686..91e45fa0 100644 --- a/crates/device/src/device.rs +++ b/crates/device/src/device.rs @@ -37,30 +37,6 @@ impl Device { _ => todo!(), } } - pub fn midi_ins (&self) -> &[JackMidiIn] { - match self { - //Self::Sampler(Sampler { midi_in, .. }) => &[midi_in], - _ => todo!() - } - } - pub fn midi_outs (&self) -> &[JackMidiOut] { - match self { - Self::Sampler(_) => &[], - _ => todo!() - } - } - pub fn audio_ins (&self) -> &[JackAudioIn] { - match self { - Self::Sampler(Sampler { audio_ins, .. }) => audio_ins.as_slice(), - _ => todo!() - } - } - pub fn audio_outs (&self) -> &[JackAudioOut] { - match self { - Self::Sampler(Sampler { audio_outs, .. }) => audio_outs.as_slice(), - _ => todo!() - } - } } pub struct DeviceAudio<'a>(pub &'a mut Device); diff --git a/crates/device/src/editor/editor_api.rs b/crates/device/src/editor/editor_api.rs index 8a588208..3ef47b0b 100644 --- a/crates/device/src/editor/editor_api.rs +++ b/crates/device/src/editor/editor_api.rs @@ -95,36 +95,34 @@ use crate::*; #[tengri_proc::command(MidiEditor)] impl MidiEditCommand { fn append_note (editor: &mut MidiEditor, advance: bool) -> Perhaps { editor.put_note(advance); - editor.redraw(); Ok(None) } - fn delete_note (editor: &mut MidiEditor) -> Perhaps { - editor.redraw(); + fn delete_note (_editor: &mut MidiEditor) -> Perhaps { todo!() } fn set_note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps { editor.set_note_pos(pos.min(127)); - editor.redraw(); Ok(None) } fn set_note_len (editor: &mut MidiEditor, value: usize) -> Perhaps { + //let note_len = editor.get_note_len(); + //let time_zoom = editor.get_time_zoom(); editor.set_note_len(value); - editor.redraw(); + //if note_len / time_zoom != x / time_zoom { + editor.redraw(); + //} Ok(None) } fn set_note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_note_lo(value.min(127)); - editor.redraw(); Ok(None) } fn set_time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_pos(value); - editor.redraw(); Ok(None) } fn set_time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_start(value); - editor.redraw(); Ok(None) } fn set_time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps { @@ -134,12 +132,10 @@ use crate::*; } fn set_time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps { editor.set_time_lock(value); - editor.redraw(); Ok(None) } fn show (editor: &mut MidiEditor, clip: Option>>) -> Perhaps { editor.set_clip(clip.as_ref()); - editor.redraw(); Ok(None) } // TODO: 1-9 seek markers that by default start every 8th of the clip diff --git a/crates/device/src/editor/editor_view.rs b/crates/device/src/editor/editor_view.rs index 454290a9..c91de260 100644 --- a/crates/device/src/editor/editor_view.rs +++ b/crates/device/src/editor/editor_view.rs @@ -12,11 +12,10 @@ impl MidiEditor { let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.name.clone(), clip.length, clip.looped) } else { (ItemTheme::G[64], String::new().into(), 0, false) }; - Fixed::x(20, col!( - Fill::x(Align::w(FieldV(color, "Clip ", format!("{name}")))), - Fill::x(Align::w(FieldH(color, "Length", format!("{length}")))), - Fill::x(Align::w(FieldH(color, "Loop ", looped.to_string()))), - )) + Bsp::e( + FieldH(color, "Edit", format!("{name} ({length})")), + FieldH(color, "Loop", looped.to_string()) + ) } pub fn edit_status (&self) -> impl Content + '_ { @@ -30,11 +29,10 @@ impl MidiEditor { let note_name = format!("{:4}", Note::pitch_to_name(note_pos)); let note_pos = format!("{:>3}", note_pos); let note_len = format!("{:>4}", self.get_note_len()); - Fixed::x(20, col!( - Fill::x(Align::w(FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos}")))), - Fill::x(Align::w(FieldH(color, "Lock", format!("{time_lock}")))), - Fill::x(Align::w(FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")))), - )) + Bsp::e( + FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")), + FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")), + ) } } diff --git a/crates/device/src/editor/editor_view_h.rs b/crates/device/src/editor/editor_view_h.rs index 1223d57e..35f430bd 100644 --- a/crates/device/src/editor/editor_view_h.rs +++ b/crates/device/src/editor/editor_view_h.rs @@ -64,22 +64,11 @@ impl PianoHorizontal { /// Draw the piano roll background. /// /// This mode uses full blocks on note on and half blocks on legato: █▄ █▄ █▄ - fn draw_bg ( - buf: &mut BigBuffer, - clip: &MidiClip, - zoom: usize, - note_len: usize, - note_point: usize, - time_point: usize, - ) { + fn draw_bg (buf: &mut BigBuffer, clip: &MidiClip, zoom: usize, note_len: usize) { for (y, note) in (0..=127).rev().enumerate() { for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) { let cell = buf.get_mut(x, y).unwrap(); - if note == (127-note_point) || time == time_point { - cell.set_bg(Rgb(0,0,0)); - } else { - cell.set_bg(clip.color.darkest.rgb); - } + cell.set_bg(clip.color.darkest.rgb); if time % 384 == 0 { cell.set_fg(clip.color.darker.rgb); cell.set_char('│'); @@ -278,10 +267,10 @@ impl MidiViewer for PianoHorizontal { let clip = clip.read().unwrap(); let buf_size = self.buffer_size(&clip); let mut buffer = BigBuffer::from(buf_size); + let note_len = self.get_note_len(); let time_zoom = self.get_time_zoom(); self.time_len().set(clip.length); - PianoHorizontal::draw_bg(&mut buffer, &clip, time_zoom, - self.get_note_len(), self.get_note_pos(), self.get_time_pos()); + PianoHorizontal::draw_bg(&mut buffer, &clip, time_zoom, note_len); PianoHorizontal::draw_fg(&mut buffer, &clip, time_zoom); buffer } else { diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index a9f2686e..5079ccd0 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -1,7 +1,6 @@ #![feature(let_chains)] #![feature(trait_alias)] #![feature(if_let_guard)] -#![feature(impl_trait_in_assoc_type)] pub(crate) use std::cmp::Ord; pub(crate) use std::fmt::{Debug, Formatter}; diff --git a/crates/device/src/meter.rs b/crates/device/src/meter.rs index 106975c7..13e63c5a 100644 --- a/crates/device/src/meter.rs +++ b/crates/device/src/meter.rs @@ -53,7 +53,7 @@ pub fn to_rms (samples: &[f32]) -> f32 { (sum / samples.len() as f32).sqrt() } -pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Content + 'a { +pub (crate) fn view_meter <'a> (label: &'a str, value: f32) -> impl Content + 'a { col!( FieldH(ItemTheme::G[128], label, format!("{:>+9.3}", value)), Fixed::xy(if value >= 0.0 { 13 } @@ -74,7 +74,7 @@ pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Content + 'a else { Green }, ()))) } -pub fn view_meters (values: &[f32;2]) -> impl Content + use<'_> { +pub(crate) fn view_meters (values: &[f32;2]) -> impl Content + use<'_> { let left = format!("L/{:>+9.3}", values[0]); let right = format!("R/{:>+9.3}", values[1]); Bsp::s(left, right) diff --git a/crates/device/src/pool/pool_view.rs b/crates/device/src/pool/pool_view.rs index 39d868aa..29e4aa01 100644 --- a/crates/device/src/pool/pool_view.rs +++ b/crates/device/src/pool/pool_view.rs @@ -8,7 +8,7 @@ content!(TuiOut: |self: PoolView<'a>| { //let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x)); //let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x); let height = pool.clips.read().unwrap().len() as u16; - Fixed::x(20, Fill::y(Align::n(Map::new( + Fixed::x(20, Fill::y(Align::c(Map::new( ||pool.clips().clone().into_iter(), move|clip: Arc>, i: usize|{ let item_height = 1; diff --git a/crates/device/src/sampler/sampler_midi.rs b/crates/device/src/sampler/sampler_midi.rs index f8f3f3d8..7a875e2e 100644 --- a/crates/device/src/sampler/sampler_midi.rs +++ b/crates/device/src/sampler/sampler_midi.rs @@ -7,18 +7,20 @@ impl Sampler { /// Create [Voice]s from [Sample]s in response to MIDI input. pub fn process_midi_in (&mut self, scope: &ProcessScope) { let Sampler { midi_in, mapped, voices, .. } = self; - for RawMidi { time, bytes } in midi_in.port().iter(scope) { - if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { - match message { - MidiMessage::NoteOn { ref key, ref vel } => { - if let Some(ref sample) = mapped[key.as_int() as usize] { - voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); + if let Some(ref midi_in) = midi_in { + for RawMidi { time, bytes } in midi_in.port().iter(scope) { + if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { + match message { + MidiMessage::NoteOn { ref key, ref vel } => { + if let Some(ref sample) = mapped[key.as_int() as usize] { + voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); + } + }, + MidiMessage::Controller { controller: _, value: _ } => { + // TODO } - }, - MidiMessage::Controller { controller: _, value: _ } => { - // TODO + _ => {} } - _ => {} } } } diff --git a/crates/device/src/sampler/sampler_model.rs b/crates/device/src/sampler/sampler_model.rs index fcd5d0ac..bf5c624d 100644 --- a/crates/device/src/sampler/sampler_model.rs +++ b/crates/device/src/sampler/sampler_model.rs @@ -22,7 +22,7 @@ pub struct Sampler { /// Sample currently being edited. pub editing: Option>>, /// MIDI input port. Triggers sample playback. - pub midi_in: JackMidiIn, + pub midi_in: Option, /// Collection of currently playing instances of samples. pub voices: Arc>>, /// Audio output ports. Voices get played here. @@ -47,28 +47,15 @@ pub struct Sampler { pub cursor: (AtomicUsize, AtomicUsize), } -impl Sampler { - pub fn new ( - jack: &Jack, - name: impl AsRef, - midi_from: &[PortConnect], - audio_from: &[&[PortConnect];2], - audio_to: &[&[PortConnect];2], - ) -> Usually { - let name = name.as_ref(); - Ok(Self { - name: name.into(), - midi_in: JackMidiIn::new(jack, format!("M/{name}"), midi_from)?, - audio_ins: vec![ - JackAudioIn::new(jack, &format!("L/{name}"), audio_from[0])?, - JackAudioIn::new(jack, &format!("R/{name}"), audio_from[1])?, - ], - audio_outs: vec![ - JackAudioOut::new(jack, &format!("{name}/L"), audio_to[0])?, - JackAudioOut::new(jack, &format!("{name}/R"), audio_to[1])?, - ], +impl Default for Sampler { + fn default () -> Self { + Self { + midi_in: None, + audio_ins: vec![], input_meters: vec![0.0;2], output_meters: vec![0.0;2], + audio_outs: vec![], + name: "tek_sampler".into(), mapped: [const { None };128], unmapped: vec![], voices: Arc::new(RwLock::new(vec![])), @@ -84,6 +71,31 @@ impl Sampler { color: Default::default(), mixing_mode: Default::default(), metering_mode: Default::default(), + } + } +} + +impl Sampler { + pub fn new ( + jack: &Jack, + name: impl AsRef, + midi_from: &[PortConnect], + audio_from: &[&[PortConnect];2], + audio_to: &[&[PortConnect];2], + ) -> Usually { + let name = name.as_ref(); + Ok(Self { + name: name.into(), + midi_in: Some(JackMidiIn::new(jack, format!("M/{name}"), midi_from)?), + audio_ins: vec![ + JackAudioIn::new(jack, &format!("L/{name}"), audio_from[0])?, + JackAudioIn::new(jack, &format!("R/{name}"), audio_from[1])?, + ], + audio_outs: vec![ + JackAudioOut::new(jack, &format!("{name}/L"), audio_to[0])?, + JackAudioOut::new(jack, &format!("{name}/R"), audio_to[1])?, + ], + ..Default::default() }) } /// Value of cursor @@ -134,20 +146,11 @@ pub struct Sample { pub channels: Vec>, pub rate: Option, pub gain: f32, - pub color: ItemTheme, } impl Sample { pub fn new (name: impl AsRef, start: usize, end: usize, channels: Vec>) -> Self { - Self { - name: name.as_ref().into(), - start, - end, - channels, - rate: None, - gain: 1.0, - color: ItemTheme::random(), - } + Self { name: name.as_ref().into(), start, end, channels, rate: None, gain: 1.0 } } pub fn play (sample: &Arc>, after: usize, velocity: &u7) -> Voice { Voice { diff --git a/crates/device/src/sampler/sampler_view.rs b/crates/device/src/sampler/sampler_view.rs index 7b21233d..ee67807b 100644 --- a/crates/device/src/sampler/sampler_view.rs +++ b/crates/device/src/sampler/sampler_view.rs @@ -59,25 +59,17 @@ impl Sampler { let note_lo = editor.get_note_lo(); let note_pt = editor.get_note_pos(); let note_hi = editor.get_note_hi(); - Fixed::x(if compact { 4 } else { 12 }, Map::south( + Fixed::x(12, Map::south( 1, move||(note_lo..=note_hi).rev(), move|note, _index| { //let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a)))); let mut bg = if note == note_pt { Tui::g(64) } else { Color::Reset }; let mut fg = Tui::g(160); - if let Some(mapped) = &self.mapped[note] { - let sample = mapped.read().unwrap(); - fg = if note == note_pt { - sample.color.lightest.rgb - } else { - Tui::g(224) - }; - bg = if note == note_pt { - sample.color.light.rgb - } else { - sample.color.base.rgb - }; + let mapped: &Option>> = &self.mapped[note]; + if mapped.is_some() { + fg = Tui::g(224); + bg = Color::Rgb(0, if note == note_pt { 96 } else { 64 }, 0); } if let Some((index, _)) = self.recording { if note == index { @@ -118,16 +110,6 @@ impl Sampler { }))) } - pub fn view_sample_status (&self, note_pt: usize) -> impl Content + use<'_> { - Fixed::x(20, draw_info_v(if let Some((_, sample)) = &self.recording { - Some(sample) - } else if let Some(sample) = &self.mapped[note_pt] { - Some(sample) - } else { - None - })) - } - pub fn view_status (&self, index: usize) -> impl Content { draw_status(self.mapped[index].as_ref()) } @@ -207,11 +189,11 @@ fn draw_viewer (sample: Option<&Arc>>) -> impl Content + .y_bounds([0.0, height as f64]) .paint(|ctx| { let text = "press record to begin sampling"; - //ctx.print( - //(width - text.len() as u16) as f64 / 2.0, - //height as f64 / 2.0, - //text.red() - //); + ctx.print( + (width - text.len() as u16) as f64 / 2.0, + height as f64 / 2.0, + text.red() + ); }) .render(area, &mut to.buffer); } @@ -221,37 +203,18 @@ fn draw_viewer (sample: Option<&Arc>>) -> impl Content + fn draw_info (sample: Option<&Arc>>) -> impl Content + use<'_> { When(sample.is_some(), Thunk::new(move||{ let sample = sample.unwrap().read().unwrap(); - let theme = sample.color; + let theme = ItemTheme::G[96]; row!( - FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), - FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), - FieldH(theme, "Start", format!("{:<8}", sample.start)), - FieldH(theme, "End", format!("{:<8}", sample.end)), - FieldH(theme, "Trans", "0"), - FieldH(theme, "Gain", format!("{}", sample.gain)), + FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), + FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), + FieldH(theme, "Start", format!("{:<8}", sample.start)), + FieldH(theme, "End", format!("{:<8}", sample.end)), + FieldH(theme, "Transpose", " 0 "), + FieldH(theme, "Gain", format!("{}", sample.gain)), ) })) } -fn draw_info_v (sample: Option<&Arc>>) -> impl Content + use<'_> { - Either(sample.is_some(), Thunk::new(move||{ - let sample = sample.unwrap().read().unwrap(); - let theme = sample.color; - Fixed::x(20, col!( - Fill::x(Align::w(FieldH(theme, "Name ", format!("{:<10}", sample.name.clone())))), - Fill::x(Align::w(FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())))), - Fill::x(Align::w(FieldH(theme, "Start ", format!("{:<8}", sample.start)))), - Fill::x(Align::w(FieldH(theme, "End ", format!("{:<8}", sample.end)))), - Fill::x(Align::w(FieldH(theme, "Trans ", "0"))), - Fill::x(Align::w(FieldH(theme, "Gain ", format!("{}", sample.gain)))), - )) - }), Thunk::new(move||Tui::fg(Red, col!( - Tui::bold(true, "× No sample."), - "[r] record", - "[Shift-F9] import", - )))) -} - fn draw_status (sample: Option<&Arc>>) -> impl Content { Tui::bold(true, Tui::fg(Tui::g(224), sample .map(|sample|{ diff --git a/crates/engine/src/jack/jack_device.rs b/crates/engine/src/jack/jack_device.rs index 7aa3c188..c415f36e 100644 --- a/crates/engine/src/jack/jack_device.rs +++ b/crates/engine/src/jack/jack_device.rs @@ -36,16 +36,16 @@ impl Handle for JackDevice { } impl Ports for JackDevice { - fn audio_ins (&self) -> Usually>> { + fn audio_ins(&self) -> Usually>> { Ok(self.ports.audio_ins.values().collect()) } - fn audio_outs (&self) -> Usually>> { + fn audio_outs(&self) -> Usually>> { Ok(self.ports.audio_outs.values().collect()) } - fn midi_ins (&self) -> Usually>> { + fn midi_ins(&self) -> Usually>> { Ok(self.ports.midi_ins.values().collect()) } - fn midi_outs (&self) -> Usually>> { + fn midi_outs(&self) -> Usually>> { Ok(self.ports.midi_outs.values().collect()) } } diff --git a/deps/tengri b/deps/tengri index c954965a..8bfd1a23 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit c954965ae125136286291e5f0d4532edf98f46ad +Subproject commit 8bfd1a23a1f880a1d2fb104a158fc51f244acd6e