From 2e5462c4e7fc62b858740db1d400ca0a5afaa7ef Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 30 Jul 2025 19:12:45 +0300 Subject: [PATCH] wip: unify default configs the definitions are unified alright. it's just not supported yet :D the idea being that tek offers to write out the default configs to ~/.config/tek-v0 where the user can customize them. --- {crates/app/examples => .old}/midi_import.rs | 0 Cargo.lock | 7 + Cargo.toml | 1 + config/bindings.edn | 167 +++++++++++++++ config/config_arranger.edn | 31 --- config/config_groovebox.edn | 28 --- config/config_sampler.edn | 13 -- config/config_sequencer.edn | 19 -- config/config_transport.edn | 9 - config/keys_arranger.edn | 17 -- config/keys_arranger_clip.edn | 1 - config/keys_arranger_device.edn | 0 config/keys_arranger_input.rs | 0 config/keys_arranger_output.rs | 0 config/keys_arranger_scene.edn | 0 config/keys_arranger_track.rs | 0 config/keys_browser.edn | 8 - config/keys_clip.edn | 8 - config/keys_clock.edn | 2 - config/keys_device_add.edn | 2 - config/keys_dialog.edn | 0 config/keys_editor.edn | 26 --- config/keys_global.edn | 9 - config/keys_groovebox.edn | 3 - config/keys_length.edn | 6 - config/keys_message.edn | 2 - config/keys_mix.edn | 0 config/keys_pool.edn | 12 -- config/keys_pool_file.edn | 8 - config/keys_rename.edn | 4 - config/keys_sampler.edn | 12 -- config/keys_scene.edn | 7 - config/keys_sequencer.edn | 4 - config/keys_track.edn | 11 - config/templates.edn | 73 +++++++ crates/app/Cargo.toml | 1 + crates/app/src/api.rs | 4 +- crates/app/src/config.rs | 112 ++++++++++ crates/app/src/lib.rs | 1 + crates/app/src/model.rs | 202 +++++-------------- crates/app/src/view.rs | 8 +- crates/cli/tek.rs | 19 +- 42 files changed, 426 insertions(+), 411 deletions(-) rename {crates/app/examples => .old}/midi_import.rs (100%) create mode 100644 config/bindings.edn delete mode 100644 config/config_arranger.edn delete mode 100644 config/config_groovebox.edn delete mode 100644 config/config_sampler.edn delete mode 100644 config/config_sequencer.edn delete mode 100644 config/config_transport.edn delete mode 100644 config/keys_arranger.edn delete mode 100644 config/keys_arranger_clip.edn delete mode 100644 config/keys_arranger_device.edn delete mode 100644 config/keys_arranger_input.rs delete mode 100644 config/keys_arranger_output.rs delete mode 100644 config/keys_arranger_scene.edn delete mode 100644 config/keys_arranger_track.rs delete mode 100644 config/keys_browser.edn delete mode 100644 config/keys_clip.edn delete mode 100644 config/keys_clock.edn delete mode 100644 config/keys_device_add.edn delete mode 100644 config/keys_dialog.edn delete mode 100644 config/keys_editor.edn delete mode 100644 config/keys_global.edn delete mode 100644 config/keys_groovebox.edn delete mode 100644 config/keys_length.edn delete mode 100644 config/keys_message.edn delete mode 100644 config/keys_mix.edn delete mode 100644 config/keys_pool.edn delete mode 100644 config/keys_pool_file.edn delete mode 100644 config/keys_rename.edn delete mode 100644 config/keys_sampler.edn delete mode 100644 config/keys_scene.edn delete mode 100644 config/keys_sequencer.edn delete mode 100644 config/keys_track.edn create mode 100644 config/templates.edn create mode 100644 crates/app/src/config.rs diff --git a/crates/app/examples/midi_import.rs b/.old/midi_import.rs similarity index 100% rename from crates/app/examples/midi_import.rs rename to .old/midi_import.rs diff --git a/Cargo.lock b/Cargo.lock index 1b09411b..f0a12eed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2395,6 +2395,7 @@ dependencies = [ "tengri", "tengri_proc", "toml", + "xdg", ] [[package]] @@ -3408,6 +3409,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +[[package]] +name = "xdg" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" + [[package]] name = "xkbcommon-dl" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index 1c1ef56f..35b9e4ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ toml = { version = "0.9.2" } uuid = { version = "1.10.0", features = [ "v4" ] } wavers = { version = "1.4.3" } winit = { version = "0.30.4", features = [ "x11" ] } +xdg = { version = "3.0.0" } #once_cell = "1.19.0" #no_deadlocks = "1.3.2" #suil-rs = { path = "../suil" } diff --git a/config/bindings.edn b/config/bindings.edn new file mode 100644 index 00000000..1fff3dca --- /dev/null +++ b/config/bindings.edn @@ -0,0 +1,167 @@ +(module "global.edn" + (@u history/undo 1) + (@r history/redo 1) + (@esc dialog/hide) + (@f1 dialog/show :dialog-help) + (@f6 dialog/show :dialog-save) + (@f8 dialog/show :dialog-options) + (@f9 dialog/show :dialog-load) + (@f10 dialog/show :dialog-quit)) + +(module "clock.edn" + (@space clock/toggle 0) + (@shift-space clock/toggle 0)) + +(module "arranger.edn" + (@c color) + (@q launch) + (@tab project/edit) + (@enter project/edit) + (@escape project/home) + (@shift-I project/input-add) + (@shift-O project/output-add) + (@shift-S project/scene-add) + (@shift-T project/track-add) + (@shift-D dialog/show :dialog-device) + (@up select :select-scene-prev) + (@down select :select-scene-next) + (@left select :select-track-prev) + (@right select :select-track-next) + (@t select :select-track) + (@s select :select-scene)) + +(module "track.edn" + (@delete track/delete :track) + (@q track/launch :track) + (@c track/color :track) + (@comma track/prev) + (@period track/next) + (@lt track/swap-prev) + (@gt track/swap-next) + (@r track/rec) + (@m track/mon) + (@p track/play) + (@P track/solo)) + +(module "scene.edn" + (@delete scene/delete :scene) + (@q scene/launch :scene) + (@c scene/color :scene) + (@comma scene/prev) + (@period scene/next) + (@lt scene/swap-prev) + (@gt scene/swap-next)) + +(module "clip.edn" + (@g clip/get) + (@p clip/put) + (@delete clip/del) + (@comma clip/prev) + (@period clip/next) + (@lt clip/swap-prev) + (@gt clip/swap-next) + (@l clip/loop-toggle)) + +(module "browser.edn" + (@escape browser/cancel) + (@return browser/confirm) + (@up browser/cursor/set :browser-cursor-prev) + (@down browser/cursor/set :browser-cursor-next) + (@right browser/address/set :browser-address-selected) + (@left browser/address/set :browser-address-parent) + (:char browser/filter/append :char) + (@backspace browser/filter/delete :last)) + +(module "device_add.edn" + (@up dialog :dialog-device-prev) + (@down dialog :dialog-device-next)) + +(module "editor.edn" + (@left editor/time/set :time-pos-prev) + (@shift-left editor/time/set :time-pos-prev-fine) + (@right editor/time/set :time-pos-next) + (@shift-right editor/time/set :time-pos-next-fine) + (@equal editor/zoom/set :time-zoom-prev) + (@minus editor/zoom/set :time-zoom-next) + (@plus editor/zoom/set :time-zoom-next-fine) + (@underscore editor/zoom/set :time-zoom-prev-fine) + (@z editor/lock/set) + (@comma editor/length/set :note-len-prev) + (@period editor/length/set :note-len-next) + (@lt editor/length/set :note-len-prev) + (@gt editor/length/set :note-len-next) + (@up editor/pitch/set :note-pos-next) + (@down editor/pitch/set :note-pos-prev) + (@pgup editor/pitch/set :note-pos-next-octave) + (@pgdn editor/pitch/set :note-pos-prev-octave) + (@a editor/append :true) + (@enter editor/append :false) + (@del editor/delete-note) + (@shift-del editor/delete-note)) + +(module "groovebox.edn" + (@r sampler/record/toggle :sample) + (@tab focus/next) + (@shift-tab focus/prev)) + +(module "length.edn" + (@up inc) + (@down dec) + (@right next) + (@left prev) + (@return set :length) + (@escape cancel)) + +(module "message.edn" + (@esc message/dismiss) + (@enter message/dismiss)) + +(module "pool.edn" + (@n rename/begin) + (@t length/begin) + (@m import/begin) + (@x export/begin) + (@c clip/color :clip :random-color) + (@openbracket select :clip-prev) + (@closebracket select :clip-next) + (@lt swap :clip :clip-prev) + (@gt swap :clip :clip-next) + (@delete clip/delete :clip) + (@shift-A clip/add :after :new-clip) + (@shift-D clip/add :after :cloned-clip)) + +(module "pool_file.edn" + (@up select :prev) + (@down select :next) + (@right chdir :selected) + (@left chdir :parent) + (@return confirm) + (@escape cancel) + (:char append :char) + (@backspace delete :last)) + +(module "rename.edn" + (:char append :char) + (@backspace delete :last) + (@return confirm) + (@escape cancel)) + +(module "sampler.edn" + (@up sampler/select :sample-above) + (@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-export-sample) + (@shift-f9 dialog :dialog-import-sample)) + +(module "sequencer.edn" + (@c color) + (@q launch) + (@shift-I input/add) + (@shift-O output/add)) diff --git a/config/config_arranger.edn b/config/config_arranger.edn deleted file mode 100644 index 36577c97..00000000 --- a/config/config_arranger.edn +++ /dev/null @@ -1,31 +0,0 @@ -(name - "Arranger") -(info - "A grid of launchable clips arranged by track and scene.") -(keys - (cond :focus-editor (load "./keys_editor.edn")) - (cond :focus-dialog (load "./keys_dialog.edn")) - (cond :focus-message (load "./keys_message.edn")) - (cond :focus-device-add (load "./keys_device_add.edn")) - (cond :focus-browser (load "./keys_browser.edn")) - (cond :focus-pool-rename (load "./keys_rename.edn")) - (cond :focus-pool-length (load "./keys_length.edn")) - (cond :focus-clip (load "./keys_clip.edn")) - (cond :focus-track (load "./keys_track.edn")) - (cond :focus-scene (load "./keys_scene.edn")) - (cond :focus-mix (load "./keys_mix.edn")) - (load "./keys_clock.edn") - (load "./keys_arranger.edn") - (load "./keys_global.edn")) -(view - (bsp/a :view-dialog - (bsp/w :view-meters-output - (bsp/e :view-meters-input - (bsp/n (fixed/y 2 :view-status-h2) - (bsp/n :view-tracks-inputs - (bsp/s :view-tracks-devices - (bsp/s :view-tracks-outputs - (bsp/s :view-tracks-names - (fill/xy (either :focus-editor - (bsp/e :view-scenes-names :view-editor) - :view-scenes))))))))))) diff --git a/config/config_groovebox.edn b/config/config_groovebox.edn deleted file mode 100644 index 136b216a..00000000 --- a/config/config_groovebox.edn +++ /dev/null @@ -1,28 +0,0 @@ -(name "Groovebox") - -(info "A sequencer with built-in sampler.") - -(keys - (layer-if :focus-browser "./keys_browser.edn") - (layer-if :focus-pool-rename "./keys_rename.edn") - (layer-if :focus-pool-length "./keys_length.edn") - (layer "./keys_clock.edn") - (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/w - (fill/y (align/n - (bsp/s :view-midi-ins-status - (bsp/s :view-midi-outs-status - (bsp/s :view-audio-ins-status - (bsp/s :view-audio-outs-status - :view-pool)))))) - (bsp/n - (fixed/y :h-sample-detail - (bsp/e (fill/y (fixed/x 20 (align/nw :view-sample-status))) - :view-sample-viewer)) - (bsp/e - (fill/y (align/n (bsp/s :view-status-v :view-editor-status))) - (bsp/e :view-samples-keys :view-editor)))))))) diff --git a/config/config_sampler.edn b/config/config_sampler.edn deleted file mode 100644 index 2739e312..00000000 --- a/config/config_sampler.edn +++ /dev/null @@ -1,13 +0,0 @@ -(name "Sampler") - -(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))))) diff --git a/config/config_sequencer.edn b/config/config_sequencer.edn deleted file mode 100644 index 868bc0ba..00000000 --- a/config/config_sequencer.edn +++ /dev/null @@ -1,19 +0,0 @@ -(name "Sequencer") - -(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) - (bsp/n (fixed/y 1 :view-status) - (fill/xy (bsp/a - (fill/xy (align/e :view-pool)) - :view-editor))))) diff --git a/config/config_transport.edn b/config/config_transport.edn deleted file mode 100644 index 23ca4f6e..00000000 --- a/config/config_transport.edn +++ /dev/null @@ -1,9 +0,0 @@ -(name "Transport") - -(info "A JACK transport controller.") - -(keys - (layer "./keys_clock.edn") - (layer "./keys_global.edn")) - -(view :view-transport) diff --git a/config/keys_arranger.edn b/config/keys_arranger.edn deleted file mode 100644 index e3949d3f..00000000 --- a/config/keys_arranger.edn +++ /dev/null @@ -1,17 +0,0 @@ -(@c color) -(@q launch) -(@tab project edit) -(@enter project edit) -(@escape project home) -(@shift-I project input-add) -(@shift-O project output-add) -(@shift-S project scene-add) -(@shift-T project track-add) -(@shift-D dialog open :dialog-device) - -(@up select :select-scene-prev) -(@down select :select-scene-next) -(@left select :select-track-prev) -(@right select :select-track-next) -(@t select :select-track) -(@s select :select-scene) diff --git a/config/keys_arranger_clip.edn b/config/keys_arranger_clip.edn deleted file mode 100644 index 8b137891..00000000 --- a/config/keys_arranger_clip.edn +++ /dev/null @@ -1 +0,0 @@ - diff --git a/config/keys_arranger_device.edn b/config/keys_arranger_device.edn deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_arranger_input.rs b/config/keys_arranger_input.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_arranger_output.rs b/config/keys_arranger_output.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_arranger_scene.edn b/config/keys_arranger_scene.edn deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_arranger_track.rs b/config/keys_arranger_track.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_browser.edn b/config/keys_browser.edn deleted file mode 100644 index b950e989..00000000 --- a/config/keys_browser.edn +++ /dev/null @@ -1,8 +0,0 @@ -(@escape browser cancel) -(@return browser confirm) -(@up browser set-cursor :browser-cursor-prev) -(@down browser set-cursor :browser-cursor-next) -(@right browser set-address :browser-address-selected) -(@left browser set-address :browser-address-parent) -(:char browser append-to-search ;char) -(@backspace browser delete-from-search :last) diff --git a/config/keys_clip.edn b/config/keys_clip.edn deleted file mode 100644 index aa39a5e7..00000000 --- a/config/keys_clip.edn +++ /dev/null @@ -1,8 +0,0 @@ -(@g clip get) -(@p clip put) -(@delete clip del) -(@comma clip prev) -(@period clip next) -(@lt clip swap-prev) -(@gt clip swap-next) -(@l clip loop-toggle) diff --git a/config/keys_clock.edn b/config/keys_clock.edn deleted file mode 100644 index 3f707559..00000000 --- a/config/keys_clock.edn +++ /dev/null @@ -1,2 +0,0 @@ -(@space clock toggle-playback 0) -(@shift-space clock toggle-playback 0) diff --git a/config/keys_device_add.edn b/config/keys_device_add.edn deleted file mode 100644 index 9ad4b197..00000000 --- a/config/keys_device_add.edn +++ /dev/null @@ -1,2 +0,0 @@ -(@up toggle-dialog :dialog-device-prev) -(@down toggle-dialog :dialog-device-next) diff --git a/config/keys_dialog.edn b/config/keys_dialog.edn deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_editor.edn b/config/keys_editor.edn deleted file mode 100644 index a99ec7e5..00000000 --- a/config/keys_editor.edn +++ /dev/null @@ -1,26 +0,0 @@ -(@left editor set-time-pos :time-pos-prev) -(@shift-left editor set-time-pos :time-pos-prev-fine) -(@right editor set-time-pos :time-pos-next) -(@shift-right editor set-time-pos :time-pos-next-fine) - -(@equal editor set-time-zoom :time-zoom-prev) -(@minus editor set-time-zoom :time-zoom-next) -(@plus editor set-time-zoom :time-zoom-next-fine) -(@underscore editor set-time-zoom :time-zoom-prev-fine) - -(@z editor set-time-lock) - -(@up editor set-note-pos :note-pos-next) -(@down editor set-note-pos :note-pos-prev) -(@pgup editor set-note-pos :note-pos-next-octave) -(@pgdn editor set-note-pos :note-pos-prev-octave) - -(@comma editor set-note-len :note-len-prev) -(@period editor set-note-len :note-len-next) -(@lt editor set-note-len :note-len-prev) -(@gt editor set-note-len :note-len-next) - -(@a editor append-note :true) -(@enter editor append-note :false) -(@del editor delete-note) -(@shift-del editor delete-note) diff --git a/config/keys_global.edn b/config/keys_global.edn deleted file mode 100644 index 8e9f4233..00000000 --- a/config/keys_global.edn +++ /dev/null @@ -1,9 +0,0 @@ -(@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_groovebox.edn b/config/keys_groovebox.edn deleted file mode 100644 index 4aa17d4a..00000000 --- a/config/keys_groovebox.edn +++ /dev/null @@ -1,3 +0,0 @@ -(@r sampler record/toggle :sample) -(@tab focus-next) -(@shift-tab focus-prev) diff --git a/config/keys_length.edn b/config/keys_length.edn deleted file mode 100644 index faec020c..00000000 --- a/config/keys_length.edn +++ /dev/null @@ -1,6 +0,0 @@ -(@up inc) -(@down dec) -(@right next) -(@left prev) -(@return set :length) -(@escape cancel) diff --git a/config/keys_message.edn b/config/keys_message.edn deleted file mode 100644 index 90ae7960..00000000 --- a/config/keys_message.edn +++ /dev/null @@ -1,2 +0,0 @@ -(@esc message dismiss) -(@enter message dismiss) diff --git a/config/keys_mix.edn b/config/keys_mix.edn deleted file mode 100644 index e69de29b..00000000 diff --git a/config/keys_pool.edn b/config/keys_pool.edn deleted file mode 100644 index b06a4b17..00000000 --- a/config/keys_pool.edn +++ /dev/null @@ -1,12 +0,0 @@ -(@n rename begin) -(@t length begin) -(@m import begin) -(@x export begin) -(@c clip color :clip :random-color) -(@openbracket select :clip-prev) -(@closebracket select :clip-next) -(@lt swap :clip :clip-prev) -(@gt swap :clip :clip-next) -(@delete clip/delete :clip) -(@shift-A clip/add :after :new-clip) -(@shift-D clip/add :after :cloned-clip) diff --git a/config/keys_pool_file.edn b/config/keys_pool_file.edn deleted file mode 100644 index a7b405ad..00000000 --- a/config/keys_pool_file.edn +++ /dev/null @@ -1,8 +0,0 @@ -(@up select :prev) -(@down select :next) -(@right chdir :selected) -(@left chdir :parent) -(@return confirm) -(@escape cancel) -(:char append :char) -(@backspace delete :last) diff --git a/config/keys_rename.edn b/config/keys_rename.edn deleted file mode 100644 index c4c0df84..00000000 --- a/config/keys_rename.edn +++ /dev/null @@ -1,4 +0,0 @@ -(:char append :char) -(@backspace delete :last) -(@return confirm) -(@escape cancel) diff --git a/config/keys_sampler.edn b/config/keys_sampler.edn deleted file mode 100644 index 596671c4..00000000 --- a/config/keys_sampler.edn +++ /dev/null @@ -1,12 +0,0 @@ -(@up sampler select :sample-above) -(@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-export-sample) -(@shift-f9 dialog :dialog-import-sample) diff --git a/config/keys_scene.edn b/config/keys_scene.edn deleted file mode 100644 index f3265cec..00000000 --- a/config/keys_scene.edn +++ /dev/null @@ -1,7 +0,0 @@ -(@delete scene delete :scene) -(@q scene launch :scene) -(@c scene color :scene) -(@comma scene prev) -(@period scene next) -(@lt scene swap-prev) -(@gt scene swap-next) diff --git a/config/keys_sequencer.edn b/config/keys_sequencer.edn deleted file mode 100644 index eb8eba70..00000000 --- a/config/keys_sequencer.edn +++ /dev/null @@ -1,4 +0,0 @@ -(@c color) -(@q launch) -(@shift-I input add) -(@shift-O output add) diff --git a/config/keys_track.edn b/config/keys_track.edn deleted file mode 100644 index 3ffbb9a9..00000000 --- a/config/keys_track.edn +++ /dev/null @@ -1,11 +0,0 @@ -(@delete track delete :track) -(@q track launch :track) -(@c track color :track) -(@comma track prev) -(@period track next) -(@lt track swap-prev) -(@gt track swap-next) -(@r track rec) -(@m track mon) -(@p track play) -(@P track solo) diff --git a/config/templates.edn b/config/templates.edn new file mode 100644 index 00000000..11d87e59 --- /dev/null +++ b/config/templates.edn @@ -0,0 +1,73 @@ +(module "transport.edn" + (name "Transport") + (info "A JACK transport controller.") + (bind "keys/clock.edn") + (bind "keys/global.edn") + :view/transport) + +(module "arranger.edn" + (name "Arranger") + (info "A grid of launchable clips arranged by track and scene.") + (bind "keys/editor.edn" :focused/editor) + (bind "keys/dialog.edn" :focused/dialog) + (bind "keys/message.edn" :focused/message) + (bind "keys/device_add.edn" :focused/device/add) + (bind "keys/browser.edn" :focused/browser) + (bind "keys/rename.edn" :focused/pool/rename) + (bind "keys/length.edn" :focused/pool/length) + (bind "keys/clip.edn" :focused/clip) + (bind "keys/track.edn" :focused/track) + (bind "keys/scene.edn" :focused/scene) + (bind "keys/mix.edn" :focused/mix) + (bind "keys/clock.edn") + (bind "keys/arranger.edn") + (bind "keys/global.edn") + :view/dialog + (bsp/w :view/meters/output (bsp/e :view/meters/input (stack/n + (fixed/y 2 :view/status/h2) :view/tracks/inputs + (stack/s :view/tracks/devices :view/tracks/outputs :view/tracks/names + (fill/xy (either :focused/editor (bsp/e :view/scenes/names :view/editor) :view/scenes))))))) + +(module "groovebox.edn" + (name "Groovebox") + (info "A sequencer with built-in sampler.") + (bind "keys/browser.edn" :focused/browser) + (bind "keys/rename.edn" :focused/pool/rename) + (bind "keys/length.edn" :focused/pool/length) + (bind "keys/clock.edn") + (bind "keys/editor.edn") + (bind "keys/sampler.edn") + (bind "keys/global.edn") + :view/dialog + (bsp/w :view/meters/output (bsp/e :view/meters/input (bsp/w + (fill/y (align/n (stack/s :view/midi-ins/status :view/midi-outs/status + :view/audio-ins/status :view/audio-outs/status + :view/pool))) + (bsp/n (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :view/sample/status))) + :view/sample/viewer)) + (stack/e (fill/y (align/n (bsp/s :view/status/v :view/editor/status))) + :view/samples/keys :view/editor)))))) + +(module "sampler.edn" + (name "Sampler") + (info "A sampling soundboard.") + (bind "keys/sampler.edn") + (bind "keys/global.edn") + :view/dialog + (bsp/s (fixed/y 1 :view/transport) (bsp/n (fixed/y 1 :view/status) + (fill/xy :view/samples/grid)))) + +(module "sequencer.edn" + (name "Sequencer") + (info "A MIDI sequencer.") + (bind "keys/browser.edn" :focused/browser) + (bind "keys/rename.edn" :mode/pool-rename) + (bind "keys/length.edn" :mode/pool-length) + (bind "keys/editor.edn") + (bind "keys/clock.edn") + (bind "keys/global.edn") + :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/editor))))) diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 9635da64..0746934e 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -15,6 +15,7 @@ clap = { workspace = true, optional = true } palette = { workspace = true } rand = { workspace = true } toml = { workspace = true } +xdg = { workspace = true } [dev-dependencies] proptest = { workspace = true } diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index bb1d067c..0807ac89 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -7,7 +7,9 @@ macro_rules! cmd_todo { ($msg:literal) => {{ println!($msg); None }}; } handle!(TuiIn: |self: App, input|self.handle_tui_key_with_history(input)); impl App { fn handle_tui_key_with_history (&mut self, input: &TuiIn) -> Perhaps { - Ok(if let Some(binding) = self.config.keys.dispatch(input.event()) { + Ok(if let Some(binding) = self.configs.current.as_ref() + .map(|c|c.keys.dispatch(input.event())).flatten() + { let binding = binding.clone(); let undo = binding.command.clone().execute(self)?; // FIXME failed commands are not persisted in undo history diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs new file mode 100644 index 00000000..d95f5ac9 --- /dev/null +++ b/crates/app/src/config.rs @@ -0,0 +1,112 @@ +use crate::*; +use xdg::BaseDirectories; + +/// Configurations +#[derive(Default, Debug)] +pub struct Configurations { + pub dirs: BaseDirectories, + pub modules: Arc, Arc>>>, + pub current: Option +} + +/// Configuration +#[derive(Default, Debug)] +pub struct Configuration { + /// Path of configuration entrypoint + pub path: std::path::PathBuf, + /// Name of configuration + pub name: Option>, + /// Description of configuration + pub info: Option>, + /// View definition + pub view: Arc, + // Input keymap + pub keys: EventMap, +} + +macro_rules! dsl_for_each (($dsl:expr => $body:expr)=>{ + let mut dsl: Arc = $dsl.src().into(); + let mut head: Option> = dsl.head()?.map(Into::into); + let mut tail: Option> = dsl.tail()?.map(Into::into); + loop { + $body; + if let Some(next) = tail { + head = next.head()?.map(Into::into); + tail = next.tail()?.map(Into::into); + } else { + break + } + } +}); + +impl Configurations { + pub fn init () -> Usually { + let mut directories = BaseDirectories::with_profile("tek", "v0"); + let mut configurations = Self { dirs: directories, ..Default::default() }; + configurations.init_templates()?; + configurations.init_bindings()?; + Ok(configurations) + } + + const DEFAULT_TEMPLATES: &'static str = include_str!("../../../config/templates.edn"); + fn init_templates (&mut self) -> Usually<()> { + if let Some(path) = self.dirs.find_config_file("templates.edn") { + todo!() + } else { + let path = self.dirs.place_config_file("templates.edn")?; + dsl_for_each!(Self::DEFAULT_TEMPLATES => todo!()); + Ok(()) + } + } + + const DEFAULT_BINDINGS: &'static str = include_str!("../../../config/bindings.edn"); + fn init_bindings (&mut self) -> Usually<()> { + if let Some(path) = self.dirs.find_config_file("bindings.edn") { + todo!() + } else { + let path = self.dirs.place_config_file("bindings.edn")?; + dsl_for_each!(Self::DEFAULT_BINDINGS => todo!()); + Ok(()) + } + } +} + +impl Configuration { + fn load_template (&mut self, dsl: impl Dsl) -> Usually<&mut Self> { + dsl_for_each!(dsl => match () { + _ if let Some(exp) = dsl.exp()? => match exp.head()?.key()? { + Some("name") => match exp.tail()?.text()? { + Some(name) => self.name = Some(name.into()), + _ => return Err(format!("missing name definition").into()) + }, + Some("info") => match exp.tail()?.text()? { + Some(info) => self.info = Some(info.into()), + _ => return Err(format!("missing info definition").into()) + }, + Some("bind") => match exp.tail()? { + Some(keys) => self.keys = EventMap::from_dsl(&mut &keys)?, + _ => return Err(format!("missing keys definition").into()) + }, + Some("view") => match exp.tail()? { + Some(tail) => self.view = tail.src().into(), + _ => return Err(format!("missing view definition").into()) + }, + dsl => return Err(format!("unexpected: {dsl:?}").into()) + }, + + _ => return Err(format!("unexpected: {dsl:?}").into()) + }); + Ok(self) + } + fn load_binding (&mut self, dsl: impl Dsl) -> Usually<&mut Self> { + todo!(); + Ok(self) + } +} + +fn unquote (x: &str) -> &str { + let mut chars = x.chars(); + chars.next(); + //chars.next_back(); + chars.as_str() +} diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs index cc811d61..ee5f836d 100644 --- a/crates/app/src/lib.rs +++ b/crates/app/src/lib.rs @@ -36,6 +36,7 @@ pub(crate) use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed}; mod api; pub use self::api::*; mod audio; pub use self::audio::*; +mod config; pub use self::config::*; mod model; pub use self::model::*; mod view; pub use self::view::*; diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index d757b431..10b73a3f 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -1,66 +1,28 @@ use crate::*; use std::path::PathBuf; use std::error::Error; + #[derive(Default, Debug)] pub struct App { /// Must not be dropped for the duration of the process - pub jack: Jack<'static>, + pub jack: Jack<'static>, /// Display size - pub size: Measure, + pub size: Measure, /// Performance counter - pub perf: PerfModel, - // View and input definition - pub config: Configuration, + pub perf: PerfModel, + /// Available view definitions and input bindings + pub configs: Configurations, /// Contains all recently created clips. - pub pool: Pool, + pub pool: Pool, /// Contains the currently edited musical arrangement pub project: Arrangement, /// Undo history pub history: Vec<(AppCommand, Option)>, // Dialog overlay - pub dialog: Option, + pub dialog: Option, /// Base color. - pub color: ItemTheme, + pub color: ItemTheme, } -has!(Jack<'static>: |self: App|self.jack); -has!(Pool: |self: App|self.pool); -has!(Option: |self: App|self.dialog); -has!(Clock: |self: App|self.project.clock); -has!(Option: |self: App|self.project.editor); -has!(Selection: |self: App|self.project.selection); -has!(Vec: |self: App|self.project.midi_ins); -has!(Vec: |self: App|self.project.midi_outs); -has!(Vec: |self: App|self.project.scenes); -has!(Vec: |self: App|self.project.tracks); -has!(Measure: |self: App|self.size); -maybe_has!(Track: |self: App| - { MaybeHas::::get(&self.project) }; - { MaybeHas::::get_mut(&mut self.project) }); -impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } } -maybe_has!(Scene: |self: App| - { MaybeHas::::get(&self.project) }; - { MaybeHas::::get_mut(&mut self.project) }); -impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } } -has_clips!(|self: App|self.pool.clips); -impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self.project.inner_size } } -//take!(ClockCommand |state: App, iter|Take::take(state.clock(), iter)); -//take!(MidiEditCommand |state: App, iter|Ok(state.editor().map(|x|Take::take(x, iter)).transpose()?.flatten())); -//take!(PoolCommand |state: App, iter|Take::take(&state.pool, iter)); -//take!(SamplerCommand |state: App, iter|Ok(state.project.sampler().map(|x|Take::take(x, iter)).transpose()?.flatten())); -//take!(ArrangementCommand |state: App, iter|Take::take(&state.project, iter)); -//take!(DialogCommand |state: App, iter|Take::take(&state.dialog, iter)); -//has_editor!(|self: App|{ - //editor = self.editor; - //editor_w = { - //let size = self.size.w(); - //let editor = self.editor.as_ref().expect("missing editor"); - //let time_len = editor.time_len().get(); - //let time_zoom = editor.time_zoom().get().max(1); - //(5 + (time_len / time_zoom)).min(size.saturating_sub(20)).max(16) - //}; - //editor_h = 15; - //is_editing = self.editor.is_some(); -//}); impl App { pub fn update_clock (&self) { @@ -190,20 +152,16 @@ impl App { self.browser().is_some() } fn focus_clip (&self) -> bool { - !self.focus_editor() && matches!(self.selection(), - Selection::TrackClip{..}) + !self.focus_editor() && matches!(self.selection(), Selection::TrackClip{..}) } fn focus_track (&self) -> bool { - !self.focus_editor() && matches!(self.selection(), - Selection::Track(..)) + !self.focus_editor() && matches!(self.selection(), Selection::Track(..)) } fn focus_scene (&self) -> bool { - !self.focus_editor() && matches!(self.selection(), - Selection::Scene(..)) + !self.focus_editor() && matches!(self.selection(), Selection::Scene(..)) } fn focus_mix (&self) -> bool { - !self.focus_editor() && matches!(self.selection(), - Selection::Mix) + !self.focus_editor() && matches!(self.selection(), Selection::Mix) } fn focus_pool_import (&self) -> bool { matches!(self.pool.mode, Some(PoolMode::Import(..))) @@ -318,99 +276,43 @@ impl App { } } -/// Configuration -#[derive(Default, Debug)] -pub struct Configuration { - /// Path of configuration entrypoint - pub path: PathBuf, - /// Name of configuration - pub name: Option>, - /// Description of configuration - pub info: Option>, - /// View definition - pub view: Arc, - // Input keymap - pub keys: EventMap, -} +has!(Jack<'static>: |self: App|self.jack); +has!(Pool: |self: App|self.pool); +has!(Option: |self: App|self.dialog); +has!(Clock: |self: App|self.project.clock); +has!(Option: |self: App|self.project.editor); +has!(Selection: |self: App|self.project.selection); +has!(Vec: |self: App|self.project.midi_ins); +has!(Vec: |self: App|self.project.midi_outs); +has!(Vec: |self: App|self.project.scenes); +has!(Vec: |self: App|self.project.tracks); +has!(Measure: |self: App|self.size); +maybe_has!(Track: |self: App| + { MaybeHas::::get(&self.project) }; + { MaybeHas::::get_mut(&mut self.project) }); +impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } } +maybe_has!(Scene: |self: App| + { MaybeHas::::get(&self.project) }; + { MaybeHas::::get_mut(&mut self.project) }); +impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } } +has_clips!(|self: App|self.pool.clips); +impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self.project.inner_size } } -impl Configuration { - pub fn from_path (path: &impl AsRef, _watch: bool) -> Usually { - let mut config = Self { path: path.as_ref().into(), ..Default::default() }; - let mut dsl = read_and_leak(path.as_ref())?; - let mut head: Option> = dsl.head()?.map(Into::into); - let mut tail: Option> = dsl.tail()?.map(Into::into); - loop { - if let Some(exp) = head.exp()? { - match exp.head()?.key()? { - Some("name") => match exp.tail()?.text()? { - Some(name) => config.name = Some(name.into()), - _ => return Err(format!("missing name definition").into()) - }, - Some("info") => match exp.tail()?.text()? { - Some(info) => config.info = Some(info.into()), - _ => return Err(format!("missing info definition").into()) - }, - Some("keys") => match exp.tail()? { - Some(keys) => config.keys = EventMap::from_dsl(&mut &keys)?, - _ => return Err(format!("missing keys definition").into()) - }, - Some("view") => match exp.tail()? { - Some(tail) => config.view = tail.src().into(), - _ => return Err(format!("missing view definition").into()) - }, - Some(k) => return Err(format!("(e3) unexpected key {k:?} in {exp:?}").into()), - None => return Err(format!("(e2) unexpected exp {exp:?}").into()), - } - } else { - break - } - if let Some(next) = tail { - head = next.head()?.map(Into::into); - tail = next.tail()?.map(Into::into); - } else { - break - } - } - Ok(config) - } -} - -fn read_and_leak (path: impl AsRef) -> Usually<&'static str> { - Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?)) -} - -fn leak (x: impl AsRef) -> &'static str { - Box::leak(x.as_ref().into()) -} - -fn unquote (x: &str) -> &str { - let mut chars = x.chars(); - chars.next(); - //chars.next_back(); - chars.as_str() -} - -macro_rules! default_config { ($path:literal) => { ($path, include_str!($path)) }; } -pub const DEFAULT_CONFIGS: &'static [(&'static str, &'static str)] = &[ - default_config!("../../../config/config_arranger.edn"), - default_config!("../../../config/config_groovebox.edn"), - default_config!("../../../config/config_sampler.edn"), - default_config!("../../../config/config_sequencer.edn"), - default_config!("../../../config/config_transport.edn"), - - default_config!("../../../config/keys_arranger.edn"), - default_config!("../../../config/keys_clip.edn"), - default_config!("../../../config/keys_clock.edn"), - default_config!("../../../config/keys_editor.edn"), - default_config!("../../../config/keys_global.edn"), - default_config!("../../../config/keys_groovebox.edn"), - default_config!("../../../config/keys_length.edn"), - default_config!("../../../config/keys_mix.edn"), - default_config!("../../../config/keys_pool.edn"), - default_config!("../../../config/keys_pool_file.edn"), - default_config!("../../../config/keys_rename.edn"), - default_config!("../../../config/keys_sampler.edn"), - default_config!("../../../config/keys_scene.edn"), - default_config!("../../../config/keys_sequencer.edn"), - default_config!("../../../config/keys_track.edn"), -]; +//take!(ClockCommand |state: App, iter|Take::take(state.clock(), iter)); +//take!(MidiEditCommand |state: App, iter|Ok(state.editor().map(|x|Take::take(x, iter)).transpose()?.flatten())); +//take!(PoolCommand |state: App, iter|Take::take(&state.pool, iter)); +//take!(SamplerCommand |state: App, iter|Ok(state.project.sampler().map(|x|Take::take(x, iter)).transpose()?.flatten())); +//take!(ArrangementCommand |state: App, iter|Take::take(&state.project, iter)); +//take!(DialogCommand |state: App, iter|Take::take(&state.dialog, iter)); +//has_editor!(|self: App|{ + //editor = self.editor; + //editor_w = { + //let size = self.size.w(); + //let editor = self.editor.as_ref().expect("missing editor"); + //let time_len = editor.time_len().get(); + //let time_zoom = editor.time_zoom().get().max(1); + //(5 + (time_len / time_zoom)).min(size.saturating_sub(20)).max(16) + //}; + //editor_h = 15; + //is_editing = self.editor.is_some(); +//}); diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 75fef228..e31fe771 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -23,14 +23,14 @@ impl> Content for ErrorBoundary { } impl App { - pub fn view (model: &Self) -> impl Content + '_ { - ErrorBoundary::new(Ok(Some(Tui::bg(Black, model.view_menu())))) + pub fn view (&self) -> impl Content + '_ { + ErrorBoundary::new(Ok(Some(Tui::bg(Black, self.view_menu())))) //ErrorBoundary::new(Take::take(model, &mut model.config.view.clone())) //ErrorBoundary::new(Give::give(model, &mut model.config.view.clone())) } } -content!(TuiOut: |self: App| ErrorBoundary::new(Ok(Some(Tui::bg(Black, self.view_nil()))))); +content!(TuiOut: |self: App| ErrorBoundary::new(Ok(Some(Tui::bg(Black, self.view()))))); #[tengri_proc::view(TuiOut)] impl App { @@ -39,7 +39,7 @@ impl App { } pub fn view_menu (&self) -> impl Content + use<'_> { Stack::south(|add: &mut dyn FnMut(&dyn Render)|{ - add(&Tui::bold(true, "tek")); + add(&Tui::bold(true, "tek 0.3.0-rc0")); add(&""); add(&"+ new session"); }) diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index d2fde313..17a31c99 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -86,9 +86,7 @@ impl Cli { let right_tos = Connect::collect(&self.right_to, empty, empty); let audio_froms = &[left_froms.as_slice(), right_froms.as_slice()]; let audio_tos = &[left_tos.as_slice(), right_tos.as_slice()]; - let clip = Arc::new(RwLock::new(MidiClip::new( - "Clip", true, 384usize, None, Some(ItemColor::random().into())), - )); + let clip = Arc::new(RwLock::new(MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into())))); Tui::new()?.run(&Jack::new_run(&name, move|jack|{ for (index, connect) in midi_froms.iter().enumerate() { midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?); @@ -96,14 +94,7 @@ impl Cli { for (index, connect) in midi_tos.iter().enumerate() { midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?); }; - let config = Configuration::from_path(&match self.mode { - LaunchMode::Clock => "config/config_transport.edn", - LaunchMode::Sequencer => "config/config_sequencer.edn", - LaunchMode::Groovebox => "config/config_groovebox.edn", - LaunchMode::Arranger { .. } => "config/config_arranger.edn", - LaunchMode::Sampler => "config/config_sampler.edn", - _ => todo!("{:?}", self.mode), - }, false)?; + let configs = Configurations::init(); let clock = Clock::new(&jack, self.bpm)?; match self.mode { LaunchMode::Sequencer => tracks.push(Track::new( @@ -117,9 +108,9 @@ impl Cli { _ => {} } let mut app = App { - jack: jack.clone(), - config, - color: ItemTheme::random(), + jack: jack.clone(), + configs: Configurations::init()?, + color: ItemTheme::random(), pool: match self.mode { LaunchMode::Sequencer | LaunchMode::Groovebox => (&clip).into(), _ => Default::default()