mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
Compare commits
15 commits
87cd6099ad
...
ccf21cbdfe
| Author | SHA1 | Date | |
|---|---|---|---|
| ccf21cbdfe | |||
| 5fa5a875b7 | |||
| 9e8572ae0f | |||
| 5c74ffd916 | |||
| 4394e03352 | |||
| b69a89aac9 | |||
| 65d054a03b | |||
| b433688f22 | |||
| e684415c2f | |||
| 75f8fd8746 | |||
| 419a07de8c | |||
| bfa0ea1163 | |||
| ec5e2a982b | |||
| 0cb259b7b5 | |||
| 6d4a629311 |
23 changed files with 244 additions and 212 deletions
10
README.md
10
README.md
|
|
@ -25,17 +25,17 @@ or [**matrix** `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.or
|
|||
## keymaps
|
||||
|
||||
* Arranger:
|
||||
* [x] `arrows/wasd`: navigate
|
||||
* [x] `tab`: enter editor
|
||||
* [x] arrows: navigate
|
||||
* [x] tab: enter editor
|
||||
* [x] `q`: enqueue clip
|
||||
* [x] space: play/pause
|
||||
* Editor:
|
||||
* [x] `arrows/wasd`: navigate
|
||||
* [x] arrows: navigate
|
||||
* [x] `,` / `.`: change note length
|
||||
* [x] `enter`: write note
|
||||
* [x] enter: write note
|
||||
* [x] `-` / `=`: zoom midi editor
|
||||
* [ ] `z`: zoom lock/unlock
|
||||
* [ ] `del`: delete
|
||||
* [ ] del: delete
|
||||
* Global:
|
||||
* [x] esc: options menu
|
||||
* [x] f1: help/command list
|
||||
|
|
|
|||
|
|
@ -3,25 +3,25 @@
|
|||
(info "A session grid.")
|
||||
|
||||
(view
|
||||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(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 :pool))
|
||||
:arranger))))))
|
||||
(fill/xy (align/e :view-pool))
|
||||
:view-arranger))))))
|
||||
|
||||
(keys
|
||||
(layer-if :mode-message "./keys_message.edn")
|
||||
(layer-if :mode-device-add "./keys_device_add.edn")
|
||||
(layer-if :mode-pool-import "./keys_pool_file.edn")
|
||||
(layer-if :mode-pool-export "./keys_pool_file.edn")
|
||||
(layer-if :mode-pool-rename "./keys_clip_rename.edn")
|
||||
(layer-if :mode-pool-length "./keys_clip_length.edn")
|
||||
(layer "./keys_global.edn")
|
||||
(layer-if :mode-editor "./keys_editor.edn")
|
||||
(layer-if :mode-clip "./keys_clip.edn")
|
||||
(layer-if :mode-track "./keys_track.edn")
|
||||
(layer-if :mode-scene "./keys_scene.edn")
|
||||
(layer-if :mode-mix "./keys_mix.edn")
|
||||
(layer "./keys_clock.edn")
|
||||
(layer "./keys_arranger.edn"))
|
||||
(layer-if :focus-message "./keys_message.edn")
|
||||
(layer-if :focus-device-add "./keys_device_add.edn")
|
||||
(layer-if :focus-pool-import "./keys_pool_file.edn")
|
||||
(layer-if :focus-pool-export "./keys_pool_file.edn")
|
||||
(layer-if :focus-pool-rename "./keys_clip_rename.edn")
|
||||
(layer-if :focus-pool-length "./keys_clip_length.edn")
|
||||
(layer "./keys_global.edn")
|
||||
(layer-if :focus-editor "./keys_editor.edn")
|
||||
(layer-if :focus-clip "./keys_clip.edn")
|
||||
(layer-if :focus-track "./keys_track.edn")
|
||||
(layer-if :focus-scene "./keys_scene.edn")
|
||||
(layer-if :focus-mix "./keys_mix.edn")
|
||||
(layer "./keys_clock.edn")
|
||||
(layer "./keys_arranger.edn"))
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
(name "Arranger")
|
||||
(name "Groovebox")
|
||||
|
||||
(info "A sequencer with built-in sampler.")
|
||||
|
||||
(view
|
||||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(bsp/n (fixed/y 5 :sample-viewer)
|
||||
(bsp/w (fixed/x :w-sidebar :pool)
|
||||
(bsp/e :samples-keys
|
||||
(fill/y :editor))))))))
|
||||
(bsp/a :view-dialog
|
||||
(bsp/s (fixed/y 1 :view-transport)
|
||||
(bsp/n (fixed/y 1 :view-status)
|
||||
(bsp/n (fixed/y 5 :view-sample-viewer)
|
||||
(bsp/w (fixed/x :w-sidebar :view-pool)
|
||||
(bsp/e :view-samples-keys
|
||||
(fill/y :view-editor))))))))
|
||||
|
||||
(keys
|
||||
(layer-if :mode-pool-import "./keys_pool_file.edn")
|
||||
(layer-if :mode-pool-export "./keys_pool_file.edn")
|
||||
(layer-if :mode-pool-rename "./keys_clip_rename.edn")
|
||||
(layer-if :mode-pool-length "./keys_clip_length.edn")
|
||||
(layer "./keys_global.edn")
|
||||
(layer-if :mode-editor "./keys_editor.edn")
|
||||
(layer "./keys_clock.edn")
|
||||
(layer "./keys_arranger.edn"))
|
||||
(layer-if :focus-pool-import "./keys_pool_file.edn")
|
||||
(layer-if :focus-pool-export "./keys_pool_file.edn")
|
||||
(layer-if :focus-pool-rename "./keys_clip_rename.edn")
|
||||
(layer-if :focus-pool-length "./keys_clip_length.edn")
|
||||
(layer "./keys_global.edn")
|
||||
(layer "./keys_clock.edn")
|
||||
(layer "./keys_editor.edn")
|
||||
(layer "./keys_sampler.edn"))
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
(info "A sampling soundboard.")
|
||||
|
||||
(view
|
||||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(fill/xy :samples-grid)))))
|
||||
(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_global.edn")
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
(info "A MIDI sequencer.")
|
||||
|
||||
(view
|
||||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(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 :pool))
|
||||
:editor)))))
|
||||
(fill/xy (align/e :view-pool))
|
||||
:view-editor)))))
|
||||
|
||||
(keys
|
||||
(layer-if :mode-pool-import "./keys_pool_file.edn")
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
(info "A JACK transport controller.")
|
||||
|
||||
(view :transport)
|
||||
(view :view-transport)
|
||||
|
||||
(keys
|
||||
(layer "./keys_global.edn")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(@c color)
|
||||
(@q launch)
|
||||
(@t select :track 0)
|
||||
(@t select :select-track-header)
|
||||
(@tab edit :clip)
|
||||
(@shift-I input add)
|
||||
(@shift-O output add)
|
||||
|
|
@ -8,11 +8,7 @@
|
|||
(@shift-T track add)
|
||||
(@shift-Z device picker)
|
||||
|
||||
(@up select :scene-prev)
|
||||
(@w select :scene-prev)
|
||||
(@down select :scene-next)
|
||||
(@s select :scene-next)
|
||||
(@left select :track-prev)
|
||||
(@a select :track-prev)
|
||||
(@right select :track-next)
|
||||
(@d select :track-next)
|
||||
(@up select :select-scene-prev)
|
||||
(@down select :select-scene-next)
|
||||
(@left select :select-track-prev)
|
||||
(@right select :select-track-next)
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
(@space clock toggle)
|
||||
(@shift-space clock toggle 0)
|
||||
(@space clock toggle-playback 0)
|
||||
(@shift-space clock toggle-playback 0)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,25 @@
|
|||
(@up editor note/pos :note-pos-next)
|
||||
(@w editor note/pos :note-pos-next)
|
||||
(@down editor note/pos :note-pos-prev)
|
||||
(@s editor note/pos :note-pos-prev)
|
||||
(@up editor note-pos :note-pos-next)
|
||||
(@down editor note-pos :note-pos-prev)
|
||||
(@pgup editor note-pos :note-pos-next-octave)
|
||||
(@pgdn editor note-pos :note-pos-prev-octave)
|
||||
|
||||
(@pgup editor note/pos :note-pos-next-octave)
|
||||
(@pgdn editor note/pos :note-pos-prev-octave)
|
||||
(@comma editor note-len :note-len-prev)
|
||||
(@period editor note-len :note-len-next)
|
||||
(@lt editor note-len :note-len-prev)
|
||||
(@gt editor note-len :note-len-next)
|
||||
|
||||
(@comma editor note/len :note-len-prev)
|
||||
(@period editor note/len :note-len-next)
|
||||
(@lt editor note/len :note-len-prev)
|
||||
(@gt editor note/len :note-len-next)
|
||||
(@plus editor note-range :note-range-next)
|
||||
(@underscore editor note-range :note-range-prev)
|
||||
|
||||
(@plus editor note/range :note-range-next)
|
||||
(@underscore editor note/range :note-range-prev)
|
||||
(@left editor time-pos :time-pos-prev)
|
||||
(@right editor time-pos :time-pos-next)
|
||||
|
||||
(@left editor time/pos :time-pos-prev)
|
||||
(@a editor time/pos :time-pos-prev)
|
||||
(@right editor time/pos :time-pos-next)
|
||||
(@d editor time/pos :time-pos-next)
|
||||
(@equal editor time-zoom :time-zoom-prev)
|
||||
(@minus editor time-zoom :time-zoom-next)
|
||||
|
||||
(@equal editor time/zoom :time-zoom-prev)
|
||||
(@minus editor time/zoom :time-zoom-next)
|
||||
(@z editor time-lock)
|
||||
|
||||
(@z editor time/lock)
|
||||
|
||||
(@enter editor note/put)
|
||||
(@shift-enter editor note/append)
|
||||
(@del editor note/del)
|
||||
(@shift-del editor note/del)
|
||||
(@enter editor note-put)
|
||||
(@shift-enter editor note-append)
|
||||
(@del editor note-del)
|
||||
(@shift-del editor note-del)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
(@up sampler select :sample-up)
|
||||
(@w sampler select :sample-up)
|
||||
|
||||
(@down sampler select :sample-down)
|
||||
(@s sampler select :sample-down)
|
||||
|
||||
(@left sampler select :sample-left)
|
||||
(@a sampler select :sample-left)
|
||||
|
||||
(@right sampler select :sample-right)
|
||||
(@d sampler select :sample-right)
|
||||
|
||||
(@r sampler record/toggle :sample)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ impl HasClips for ExampleClips {
|
|||
}
|
||||
}
|
||||
fn main () -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut clips = ExampleClips(Arc::new(vec![].into()));
|
||||
PoolClipCommand::Import(0, std::path::PathBuf::from("./example.mid")).execute(&mut clips)?;
|
||||
let mut clips = MidiPool::default();//ExampleClips(Arc::new(vec![].into()));
|
||||
PoolClipCommand::Import {
|
||||
index: 0,
|
||||
path: std::path::PathBuf::from("./example.mid")
|
||||
}.execute(&mut clips)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
}));
|
||||
|
||||
#[tengri_proc::expose] impl App {
|
||||
fn _todo_isize_stub (&self) -> isize {
|
||||
todo!()
|
||||
}
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme {
|
||||
todo!()
|
||||
}
|
||||
fn focus_editor (&self) -> bool {
|
||||
self.is_editing()
|
||||
}
|
||||
|
|
@ -113,22 +119,25 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
fn scene_selected (&self) -> Option<usize> {
|
||||
self.selected.scene()
|
||||
}
|
||||
fn scene_select_next (&self) -> Selection {
|
||||
self.selected.scene_next(self.scenes.len())
|
||||
}
|
||||
fn scene_select_prev (&self) -> Selection {
|
||||
self.selected.scene_prev()
|
||||
}
|
||||
fn track_count (&self) -> usize {
|
||||
self.tracks.len()
|
||||
}
|
||||
fn track_selected (&self) -> Option<usize> {
|
||||
self.selected.track()
|
||||
}
|
||||
fn track_select_next (&self) -> Selection {
|
||||
fn select_scene_next (&self) -> Selection {
|
||||
self.selected.scene_next(self.scenes.len())
|
||||
}
|
||||
fn select_scene_prev (&self) -> Selection {
|
||||
self.selected.scene_prev()
|
||||
}
|
||||
fn select_track_header (&self) -> Selection {
|
||||
self.selected.track_header(self.tracks.len())
|
||||
}
|
||||
fn select_track_next (&self) -> Selection {
|
||||
self.selected.track_next(self.tracks.len())
|
||||
}
|
||||
fn track_select_prev (&self) -> Selection {
|
||||
fn select_track_prev (&self) -> Selection {
|
||||
self.selected.track_prev()
|
||||
}
|
||||
fn clip_selected (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
|
|
@ -161,6 +170,15 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
}
|
||||
|
||||
#[tengri_proc::expose] impl MidiPool {
|
||||
fn _todo_bool_stub (&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn _todo_path_buf_stub (&self) -> PathBuf {
|
||||
todo!()
|
||||
}
|
||||
fn _todo_arc_str_stub (&self) -> Arc<str> {
|
||||
todo!()
|
||||
}
|
||||
fn clip_new (&self) -> MidiClip {
|
||||
self.new_clip()
|
||||
}
|
||||
|
|
@ -185,6 +203,9 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
}
|
||||
|
||||
#[tengri_proc::expose] impl MidiEditor {
|
||||
fn _todo_opt_clip_stub (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
todo!()
|
||||
}
|
||||
fn time_lock (&self) -> bool {
|
||||
self.get_time_lock()
|
||||
}
|
||||
|
|
@ -289,6 +310,35 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
None
|
||||
})
|
||||
}
|
||||
fn color (app: &mut App, theme: ItemTheme) -> Perhaps<Self> {
|
||||
Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme}))
|
||||
}
|
||||
fn enqueue (app: &mut App, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn history (app: &mut App, delta: isize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn zoom (app: &mut App, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn launch (app: &mut App) -> Perhaps<Self> {
|
||||
app.launch();
|
||||
Ok(None)
|
||||
}
|
||||
fn select (app: &mut App, selection: Selection) -> Perhaps<Self> {
|
||||
app.select(selection);
|
||||
Ok(None)
|
||||
//("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) {
|
||||
//(0, 0) => Self::Select(Selection::Mix),
|
||||
//(t, 0) => Self::Select(Selection::Track(t)),
|
||||
//(0, s) => Self::Select(Selection::Scene(s)),
|
||||
//(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) })))
|
||||
}
|
||||
fn stop_all (app: &mut App) -> Perhaps<Self> {
|
||||
app.stop_all();
|
||||
Ok(None)
|
||||
}
|
||||
fn sampler (app: &mut App, command: SamplerCommand) -> Perhaps<Self> {
|
||||
Ok(app.sampler_mut()
|
||||
.map(|s|command.delegate(s, |command|Self::Sampler{command}))
|
||||
|
|
@ -319,34 +369,29 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
|||
fn message (app: &mut App, command: MessageCommand) -> Perhaps<Self> {
|
||||
Ok(command.delegate(app, |command|Self::Message{command})?)
|
||||
}
|
||||
fn color (app: &mut App, theme: ItemTheme) -> Perhaps<Self> {
|
||||
Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme}))
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, ClockCommand> for App {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ClockCommand> {
|
||||
Context::get(&self.clock, iter)
|
||||
}
|
||||
fn enqueue (app: &mut App, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, MidiEditCommand> for App {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<MidiEditCommand> {
|
||||
self.editor().map(|e|Context::get(e, iter)).flatten()
|
||||
}
|
||||
fn history (app: &mut App, delta: isize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, PoolCommand> for App {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<PoolCommand> {
|
||||
self.pool().map(|p|Context::get(p, iter)).flatten()
|
||||
}
|
||||
fn zoom (app: &mut App, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn launch (app: &mut App) -> Perhaps<Self> {
|
||||
app.launch();
|
||||
Ok(None)
|
||||
}
|
||||
fn select (app: &mut App, selection: Selection) -> Perhaps<Self> {
|
||||
app.select(selection);
|
||||
Ok(None)
|
||||
//("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) {
|
||||
//(0, 0) => Self::Select(Selection::Mix),
|
||||
//(t, 0) => Self::Select(Selection::Track(t)),
|
||||
//(0, s) => Self::Select(Selection::Scene(s)),
|
||||
//(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) })))
|
||||
}
|
||||
fn stop_all (app: &mut App) -> Perhaps<Self> {
|
||||
app.stop_all();
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, SamplerCommand> for App {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<SamplerCommand> {
|
||||
self.sampler().map(|p|Context::get(p, iter)).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,8 +122,7 @@ impl Configuration {
|
|||
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
||||
};
|
||||
|
||||
let next = exp.next();
|
||||
if let Some(Token { value: Value::Str(path), .. }) = next {
|
||||
if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
||||
let path = base.as_ref().parent().unwrap().join(unquote(path));
|
||||
if !std::fs::exists(&path)? {
|
||||
return Err(format!("(e5) not found: {path:?}").into())
|
||||
|
|
@ -133,8 +132,9 @@ impl Configuration {
|
|||
println!("ok");
|
||||
let cond = cond.unwrap();
|
||||
map.add_layer_if(
|
||||
Box::new(|state|{
|
||||
Context::get(state, &Value::Sym(cond)).unwrap_or(false)
|
||||
Box::new(move |state|{
|
||||
let mut exp = exp.clone();
|
||||
Context::get(state, &mut exp).unwrap_or(false)
|
||||
}),
|
||||
keys
|
||||
);
|
||||
|
|
|
|||
|
|
@ -42,33 +42,29 @@ mod config; pub use self::config::*;
|
|||
mod model; pub use self::model::*;
|
||||
mod view; pub use self::view::*;
|
||||
|
||||
#[cfg(test)] #[test] fn test_model () {
|
||||
#[cfg(test)] #[test] fn test_model () -> Usually<()> {
|
||||
let mut app = App::default();
|
||||
let _ = app.clip();
|
||||
let _ = app.toggle_loop();
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_model_scene () {
|
||||
let mut app = App::default();
|
||||
//let _ = app.tracks_add(8, None, &[], &[])?;
|
||||
//let _ = app.track_add_focus()?;
|
||||
let _ = app.scene_longest();
|
||||
let _ = app.scene();
|
||||
let _ = app.scene_mut();
|
||||
let _ = app.scene_add(None, None);
|
||||
app.scene_del(0);
|
||||
let _ = app.scene_add(None, None)?;
|
||||
let _ = app.scene_add_focus()?;
|
||||
let scene = app.scene_del(0);
|
||||
let scene = Scene::default();
|
||||
let _ = scene.pulses();
|
||||
let _ = scene.is_playing(&[]);
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_view_clock () {
|
||||
let _ = button_play_pause(true);
|
||||
let mut app = App::default();
|
||||
let _ = app.view_transport();
|
||||
let _ = app.view_status();
|
||||
let _ = app.update_clock();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_view_layout () {
|
||||
let _ = button_play_pause(true);
|
||||
let _ = button_2("", "", true);
|
||||
let _ = button_2("", "", false);
|
||||
let _ = button_3("", "", "", true);
|
||||
|
|
@ -122,3 +118,29 @@ mod view; pub use self::view::*;
|
|||
let _ = app.h_outputs();
|
||||
let _ = app.h_scenes();
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_midi_edit () {
|
||||
let editor = MidiEditor::default();
|
||||
let mut editor = MidiEditor {
|
||||
mode: PianoHorizontal::new(Some(&Arc::new(RwLock::new(MidiClip::stop_all())))),
|
||||
size: Default::default(),
|
||||
//keys: Default::default(),
|
||||
};
|
||||
let _ = editor.put_note(true);
|
||||
let _ = editor.put_note(false);
|
||||
let _ = editor.clip_status();
|
||||
let _ = editor.edit_status();
|
||||
struct TestEditorHost(Option<MidiEditor>);
|
||||
has_editor!(|self: TestEditorHost|{
|
||||
editor = self.0;
|
||||
editor_w = 0;
|
||||
editor_h = 0;
|
||||
is_editing = false;
|
||||
});
|
||||
let mut host = TestEditorHost(Some(editor));
|
||||
let _ = host.editor();
|
||||
let _ = host.editor_mut();
|
||||
let _ = host.is_editing();
|
||||
let _ = host.editor_w();
|
||||
let _ = host.editor_h();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,13 +46,16 @@ pub trait HasScenes: HasSelection + HasEditor + Send + Sync {
|
|||
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
fn scene (&self) -> Option<&Scene> {
|
||||
self.selected().scene().and_then(|s|self.scenes().get(s))
|
||||
self.selected().scene()
|
||||
.and_then(|s|self.scenes().get(s))
|
||||
}
|
||||
fn scene_mut (&mut self) -> Option<&mut Scene> {
|
||||
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
|
||||
self.selected().scene()
|
||||
.and_then(|s|self.scenes_mut().get_mut(s))
|
||||
}
|
||||
fn scene_del (&mut self, index: usize) {
|
||||
self.selected().scene().and_then(|s|Some(self.scenes_mut().remove(index)));
|
||||
fn scene_del (&mut self, index: usize) -> Option<Scene> {
|
||||
self.selected().scene()
|
||||
.and_then(|s|Some(self.scenes_mut().remove(index)))
|
||||
}
|
||||
/// Set the color of a scene, returning the previous one.
|
||||
fn scene_set_color (&mut self, index: usize, color: ItemTheme) -> ItemTheme {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,16 @@ impl Selection {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn track_header (&self, track_count: usize) -> Self {
|
||||
use Selection::*;
|
||||
match self {
|
||||
Mix => Track(0),
|
||||
Scene(_) => Mix,
|
||||
Track(t) => Track((t + 1) % track_count),
|
||||
TrackClip { track, .. } => Track(*track),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
pub fn track_next (&self, len: usize) -> Self {
|
||||
use Selection::*;
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ pub(crate) use ::tengri::tui::ratatui::prelude::Position;
|
|||
|
||||
#[tengri_proc::view(TuiOut)]
|
||||
impl App {
|
||||
fn view_nil (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_nil (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
"nil"
|
||||
}
|
||||
fn view_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
view_status(
|
||||
|
|
@ -15,7 +15,7 @@ impl App {
|
|||
cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone(),
|
||||
)
|
||||
}
|
||||
fn view_transport (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_transport (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let cache = self.view_cache.read().unwrap();
|
||||
view_transport(
|
||||
|
|
@ -23,25 +23,25 @@ impl App {
|
|||
cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone(),
|
||||
)
|
||||
}
|
||||
fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
ArrangerView::new(self)
|
||||
}
|
||||
fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.pool().map(|p|Fixed::x(self.w_sidebar(), PoolView(self.is_editing(), p)))
|
||||
}
|
||||
fn view_editor (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_editor (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.editor().map(|e|Bsp::n(Bsp::e(e.clip_status(), e.edit_status()), e))
|
||||
}
|
||||
fn view_samples_keys (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_samples_keys (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.sampler().map(|s|s.view_list(false, self.editor().unwrap()))
|
||||
}
|
||||
fn view_samples_grid (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_samples_grid (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.sampler().map(|s|s.view_grid())
|
||||
}
|
||||
fn view_sample_viewer (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_sample_viewer (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos()))
|
||||
}
|
||||
fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
When::new(self.dialog.is_some(), Bsp::b(
|
||||
Fill::xy(Tui::fg_bg(Rgb(64,64,64), Rgb(32,32,32), "")),
|
||||
Fixed::xy(30, 15, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b(
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ impl ClockCommand {
|
|||
state.pause_at(position)?;
|
||||
Ok(None)
|
||||
}
|
||||
fn toggle_playback (state: &mut Clock, position: Option<u32>) -> Perhaps<Self> {
|
||||
fn toggle_playback (state: &mut Clock, position: u32) -> Perhaps<Self> {
|
||||
if state.is_rolling() {
|
||||
state.pause_at(position)?;
|
||||
state.pause_at(Some(position))?;
|
||||
} else {
|
||||
state.play_from(position)?;
|
||||
state.play_from(Some(position))?;
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,4 @@ mod lv2_model; pub use self::lv2_model::*;
|
|||
mod lv2_audio; pub use self::lv2_audio::*;
|
||||
mod lv2_gui; pub use self::lv2_gui::*;
|
||||
mod lv2_tui; pub use self::lv2_tui::*;
|
||||
|
||||
pub(self) use std::thread::JoinHandle;
|
||||
|
||||
pub(self) use ::livi::{
|
||||
World,
|
||||
Instance,
|
||||
Plugin as LiviPlugin,
|
||||
Features,
|
||||
FeaturesBuilder,
|
||||
Port as LiviPort,
|
||||
event::LV2AtomSequence,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ mod seq_view; pub use self::seq_view::*;
|
|||
let mut clip = MidiClip::new("clip", true, 1, None, None);
|
||||
clip.set_length(96);
|
||||
clip.toggle_loop();
|
||||
clip.record_event(12, midly::MidiMessage::NoteOn { key: 36.into(), vel: 100.into() });
|
||||
clip.record_event(12, MidiMessage::NoteOn { key: 36.into(), vel: 100.into() });
|
||||
assert!(clip.contains_note_on(36.into(), 6, 18));
|
||||
assert_eq!(&clip.notes, &clip.duplicate().notes);
|
||||
|
||||
|
|
@ -25,29 +25,3 @@ mod seq_view; pub use self::seq_view::*;
|
|||
let player = MidiPlayer::default();
|
||||
println!("{player:?}");
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_midi_edit () {
|
||||
let editor = MidiEditor::default();
|
||||
let mut editor = MidiEditor {
|
||||
mode: PianoHorizontal::new(Some(&Arc::new(RwLock::new(MidiClip::stop_all())))),
|
||||
size: Default::default(),
|
||||
keys: Default::default(),
|
||||
};
|
||||
let _ = editor.put_note(true);
|
||||
let _ = editor.put_note(false);
|
||||
let _ = editor.clip_status();
|
||||
let _ = editor.edit_status();
|
||||
struct TestEditorHost(Option<MidiEditor>);
|
||||
has_editor!(|self: TestEditorHost|{
|
||||
editor = self.0;
|
||||
editor_w = 0;
|
||||
editor_h = 0;
|
||||
is_editing = false;
|
||||
});
|
||||
let mut host = TestEditorHost(Some(editor));
|
||||
let _ = host.editor();
|
||||
let _ = host.editor_mut();
|
||||
let _ = host.is_editing();
|
||||
let _ = host.editor_w();
|
||||
let _ = host.editor_h();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,11 @@ impl Jack {
|
|||
// implements audio and MIDI input and output on a realtime basis.
|
||||
ClosureProcessHandler::new(Box::new({
|
||||
let app = app.clone();
|
||||
move|c: &_, s: &_|app.write().unwrap().process(c, s)
|
||||
move|c: &_, s: &_|if let Ok(mut app) = app.write() {
|
||||
app.process(c, s)
|
||||
} else {
|
||||
Control::Quit
|
||||
}
|
||||
}) as BoxedAudioHandler<'j>),
|
||||
)?;
|
||||
*self.state.write().unwrap() = Active(client);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ mod note; pub use self::note::*;
|
|||
pub mod jack; pub use self::jack::*;
|
||||
pub mod midi; pub use self::midi::*;
|
||||
|
||||
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, AtomicBool, Ordering::Relaxed}};
|
||||
pub(crate) use std::path::PathBuf;
|
||||
pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering::Relaxed}};
|
||||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::ops::{Add, Sub, Mul, Div, Rem};
|
||||
|
||||
|
|
@ -59,14 +58,14 @@ impl InteriorMutable<usize> for AtomicUsize {
|
|||
#[cfg(test)] #[test] fn test_midi_range () {
|
||||
let model = MidiRangeModel::from((1, false));
|
||||
|
||||
let _ = model.time_len();
|
||||
let _ = model.time_zoom();
|
||||
let _ = model.time_lock();
|
||||
let _ = model.time_start();
|
||||
let _ = model.time_axis();
|
||||
let _ = model.time_end();
|
||||
let _ = model.get_time_len();
|
||||
let _ = model.get_time_zoom();
|
||||
let _ = model.get_time_lock();
|
||||
let _ = model.get_time_start();
|
||||
let _ = model.get_time_axis();
|
||||
let _ = model.get_time_end();
|
||||
|
||||
let _ = model.note_lo();
|
||||
let _ = model.note_axis();
|
||||
let _ = model.note_hi();
|
||||
let _ = model.get_note_lo();
|
||||
let _ = model.get_note_axis();
|
||||
let _ = model.get_note_hi();
|
||||
}
|
||||
|
|
|
|||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 22d63eed9c9cb5bed5016d851f90773e0f60280d
|
||||
Subproject commit cb8fd26922fd1cfad4ceadeb89e48544531a178e
|
||||
Loading…
Add table
Add a link
Reference in a new issue