diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf8aa09cf..8f92bd812 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: - artifact: windows-x86_64 os: windows-latest toolchain-suffix: x86_64-pc-windows-msvc - lib-file-name: realearn.dll + lib-file-name: helgobox.dll extension-file-name: reaper_helgobox.dll target: x86_64-pc-windows-msvc use-cross: false @@ -25,7 +25,7 @@ jobs: - artifact: windows-i686 os: windows-latest toolchain-suffix: i686-pc-windows-msvc - lib-file-name: realearn.dll + lib-file-name: helgobox.dll extension-file-name: reaper_helgobox.dll target: i686-pc-windows-msvc use-cross: false @@ -34,7 +34,7 @@ jobs: - artifact: macos-x86_64 os: macos-latest toolchain-suffix: x86_64-apple-darwin - lib-file-name: librealearn.dylib + lib-file-name: libhelgobox.dylib extension-file-name: libreaper_helgobox.dylib target: x86_64-apple-darwin use-cross: false @@ -43,7 +43,7 @@ jobs: - artifact: macos-aarch64 os: macos-latest toolchain-suffix: x86_64-apple-darwin - lib-file-name: librealearn.dylib + lib-file-name: libhelgobox.dylib extension-file-name: libreaper_helgobox.dylib target: aarch64-apple-darwin use-cross: false @@ -52,7 +52,7 @@ jobs: - artifact: linux-x86_64 os: ubuntu-20.04 toolchain-suffix: x86_64-unknown-linux-gnu - lib-file-name: librealearn.so + lib-file-name: libhelgobox.so extension-file-name: "" target: x86_64-unknown-linux-gnu use-cross: false @@ -62,7 +62,7 @@ jobs: # - artifact: linux-aarch64 # os: ubuntu-22.04 # toolchain-suffix: x86_64-unknown-linux-gnu - # lib-file-name: librealearn.so + # lib-file-name: libhelgobox.so # extension-file-name: "" # target: aarch64-unknown-linux-gnu # use-cross: true @@ -72,7 +72,7 @@ jobs: # - artifact: linux-armv7 # os: ubuntu-22.04 # toolchain-suffix: x86_64-unknown-linux-gnu - # lib-file-name: librealearn.so + # lib-file-name: libhelgobox.so # extension-file-name: "" # target: armv7-unknown-linux-gnueabihf # use-cross: true @@ -143,7 +143,7 @@ jobs: - name: Strip debug symbols from Linux binary if: startsWith(matrix.os, 'ubuntu-') run: | - cp target/${{ matrix.target }}/${{ matrix.profile }}/${{ matrix.lib-file-name }} target/${{ matrix.target }}/${{ matrix.profile }}/librealearn-debug.so + cp target/${{ matrix.target }}/${{ matrix.profile }}/${{ matrix.lib-file-name }} target/${{ matrix.target }}/${{ matrix.profile }}/libhelgobox-debug.so ${{ matrix.strip-cmd }} target/${{ matrix.target }}/${{ matrix.profile }}/${{ matrix.lib-file-name }} - name: Strip debug symbols from macOS binary if: matrix.os == 'macos-latest' @@ -168,19 +168,19 @@ jobs: uses: actions/upload-artifact@v2 with: name: ${{ matrix.artifact }} - path: target/${{ matrix.target }}/${{ matrix.profile }}/realearn.pdb + path: target/${{ matrix.target }}/${{ matrix.profile }}/helgobox.pdb - name: Upload macOS debug symbols to artifact if: matrix.os == 'macos-latest' uses: actions/upload-artifact@v2 with: name: ${{ matrix.artifact }} - path: target/${{ matrix.target }}/${{ matrix.profile }}/deps/librealearn.dylib.dSYM/ + path: target/${{ matrix.target }}/${{ matrix.profile }}/deps/libhelgobox.dylib.dSYM/ - name: Upload Linux debug symbols to artifact if: startsWith(matrix.os, 'ubuntu-') uses: actions/upload-artifact@v2 with: name: ${{ matrix.artifact }} - path: target/${{ matrix.target }}/${{ matrix.profile }}/librealearn-debug.so + path: target/${{ matrix.target }}/${{ matrix.profile }}/libhelgobox-debug.so create-release: name: Publish release @@ -209,8 +209,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./windows-x86_64/realearn.dll - asset_name: realearn-windows-x86_64.dll + asset_path: ./windows-x86_64/helgobox.dll + asset_name: helgobox-windows-x86_64.dll asset_content_type: application/octet-stream - name: Upload windows-x86_64 release artifact 2 uses: actions/upload-release-asset@v1 @@ -228,8 +228,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./windows-i686/realearn.dll - asset_name: realearn-windows-i686.dll + asset_path: ./windows-i686/helgobox.dll + asset_name: helgobox-windows-i686.dll asset_content_type: application/octet-stream - name: Upload windows-i686 release artifact 2 uses: actions/upload-release-asset@v1 @@ -247,8 +247,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./macos-x86_64/librealearn.dylib - asset_name: realearn-macos-x86_64.vst.dylib + asset_path: ./macos-x86_64/libhelgobox.dylib + asset_name: helgobox-macos-x86_64.vst.dylib asset_content_type: application/octet-stream - name: Upload macos-x86_64 release artifact 2 uses: actions/upload-release-asset@v1 @@ -266,8 +266,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./macos-aarch64/librealearn.dylib - asset_name: realearn-macos-aarch64.vst.dylib + asset_path: ./macos-aarch64/libhelgobox.dylib + asset_name: helgobox-macos-aarch64.vst.dylib asset_content_type: application/octet-stream - name: Upload macos-aarch64 release artifact 2 uses: actions/upload-release-asset@v1 @@ -285,8 +285,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-x86_64/librealearn.so - asset_name: realearn-linux-x86_64.so + asset_path: ./linux-x86_64/libhelgobox.so + asset_name: helgobox-linux-x86_64.so asset_content_type: application/octet-stream # Upload Linux aarch64 artifacts # - name: Upload linux-aarch64 release artifact @@ -295,8 +295,8 @@ jobs: # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # with: # upload_url: ${{ steps.create_release.outputs.upload_url }} - # asset_path: ./linux-aarch64/librealearn.so - # asset_name: realearn-linux-aarch64.so + # asset_path: ./linux-aarch64/libhelgobox.so + # asset_name: helgobox-linux-aarch64.so # asset_content_type: application/octet-stream # Upload Linux armv7 artifacts # - name: Upload linux-armv7 release artifact @@ -305,6 +305,6 @@ jobs: # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # with: # upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: ./linux-armv7/librealearn.so -# asset_name: realearn-linux-armv7.so +# asset_path: ./linux-armv7/libhelgobox.so +# asset_name: helgobox-linux-armv7.so # asset_content_type: application/octet-stream \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 9f623a3cd..fde8d3442 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,7 +26,7 @@ "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [ - {"name": "REALEARN_LOG", "value": "debug,vst=info,hyper=trace"} + {"name": "HELGOBOX_LOG", "value": "debug,vst=info,hyper=trace"} // {"name": "CONTROL_SURFACE_METRICS", "value": "true"} ] }, diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 54183f76b..a5db2e98c 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -77,7 +77,7 @@ Luau language bindings are generated from Rust code by executing tests: [source,shell] ---- -RUST_MIN_STACK=5242880 cargo test --package realearn-api --lib bindings::luau::export_luau +RUST_MIN_STACK=5242880 cargo test --package helgobox-api --lib bindings::luau::export_luau ---- Artwork such as toolbar icons is generated by running a crate: @@ -132,7 +132,7 @@ mv playtime-clip-engine-placeholder playtime-clip-engine # ONLY For users who HAVE access to the private submodules git submodule update --init ---- -. Build ReaLearn (after that you should have a `realearn.dll` in `target\debug`) +. Build ReaLearn (after that you should have a `helgobox.dll` in `target\debug`) + [source,shell] ---- @@ -258,7 +258,7 @@ In future, it would be nice to run this integration test during continuous integ == Log -It's possible to make ReaLearn output log messages to `stdout` by setting the `REALEARN_LOG` environment variable, e.g. to `debug,vst=info`. +It's possible to make ReaLearn output log messages to `stdout` by setting the `HELGOBOX_LOG` environment variable, e.g. to `debug,vst=info`. It follows https://docs.rs/env_logger/0.8.2/env_logger/index.html[this] format. Beware that e.g. on Windows, `stdout` is not shown, not even when executing REAPER from the command line. One way to make it visible is to execute REAPER with a debugger. @@ -288,7 +288,7 @@ Prometheus is usually available at http://localhost:9090/. === ReaLearn metrics -- You can turn on ReaLearn metrics by setting the environment variable `REALEARN_METRICS` (value doesn't matter). +- You can turn on ReaLearn metrics by setting the environment variable `HELGOBOX_METRICS` (value doesn't matter). - If this environment variable is set (value doesn't matter), ReaLearn will record some metrics and expose them on the Prometheus endpoint mentioned above. - If ReaLearn is built with the Playtime Clip Engine, this flag will also enable Clip Engine metrics. This can negatively effect clip playing performance because many clip engine metrics are captured in real-time threads. @@ -345,7 +345,7 @@ To be used like this: All documentation is written in AsciiDoc: -- link:doc/user-guide.adoc[User guide] +- link:doc/realearn-user-guide.adoc[User guide] - link:ARCHITECTURE.adoc[Architecture] Some SVGs embedded in the architecture documentation are generated via link:https://nodejs.org/[NodeJS] / link:https://svgjs.dev/[SVG.js] in link:doc/svg-gen/index.js[]. diff --git a/Cargo.lock b/Cargo.lock index 7e4c75442..933df7405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -606,12 +606,12 @@ dependencies = [ "either", "enigo", "futures-timer", - "helgoboss-allocator", + "helgobox-allocator", + "helgobox-api", "indexmap 2.1.0", "logos 0.13.0", "metrics", "once_cell", - "realearn-api", "reaper-high", "reaper-low", "reaper-medium", @@ -2733,14 +2733,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "helgoboss-allocator" -version = "0.1.0" -dependencies = [ - "backtrace", - "once_cell", -] - [[package]] name = "helgoboss-learn" version = "0.1.0" @@ -2810,6 +2802,163 @@ dependencies = [ "serde_repr", ] +[[package]] +name = "helgobox" +version = "2.16.0-pre.18" +dependencies = [ + "anyhow", + "approx", + "arboard", + "ascii", + "askama", + "async-channel 2.1.1", + "auto_impl", + "axum", + "axum-server", + "backtrace", + "base", + "baseview", + "bindgen", + "built", + "bytesize", + "c_str_macro", + "camino", + "cc 1.0.73", + "chrono", + "crossbeam-channel", + "derivative", + "derive_more", + "dns-lookup", + "edit", + "egui", + "egui-baseview", + "egui_extras", + "either", + "embed-resource", + "enum-map", + "enum_dispatch", + "enumflags2", + "enumset", + "env_logger", + "fasteval", + "fragile", + "futures", + "helgoboss-learn", + "helgoboss-license-api", + "helgoboss-midi", + "helgobox-allocator", + "helgobox-api", + "helgobox-dialogs", + "hex", + "hostname", + "image", + "include_dir", + "indexmap 2.1.0", + "itertools 0.12.0", + "lazycell", + "libloading 0.8.0", + "maplit", + "metrics", + "metrics-exporter-prometheus", + "mlua", + "nanoid", + "nom", + "num_enum 0.7.2", + "once_cell", + "open", + "palette", + "percent-encoding", + "playtime-api", + "playtime-clip-engine", + "pot", + "pot-browser", + "prost", + "qrcode", + "raw-window-handle 0.4.3", + "rcgen", + "reaper-common-types", + "reaper-high", + "reaper-low", + "reaper-macros", + "reaper-medium", + "reaper-rx", + "regex", + "rosc", + "runas", + "rust-ini", + "rx-util", + "rxrust", + "scopeguard", + "semver 1.0.17", + "serde", + "serde_ini", + "serde_json", + "serde_plain", + "serde_repr", + "serde_with", + "serde_yaml", + "slug", + "smallvec", + "strum", + "swell-ui", + "sys-info", + "sysinfo", + "tar", + "tempfile", + "tinyvec", + "tokio", + "tokio-stream", + "tonic", + "tower", + "tower-http", + "tracing", + "tracing-core", + "tracing-subscriber", + "tts", + "url", + "uuid 1.7.0", + "vst", + "walkdir", + "webbrowser", + "whoami", + "wildmatch", + "winapi", + "xxhash-rust", + "zstd", +] + +[[package]] +name = "helgobox-allocator" +version = "0.1.0" +dependencies = [ + "backtrace", + "once_cell", +] + +[[package]] +name = "helgobox-api" +version = "0.1.0" +dependencies = [ + "anyhow", + "darling 0.20.3", + "derive_more", + "enum-map", + "enumset", + "heck", + "helgoboss-license-api", + "helgobox-macros", + "mlua", + "num_enum 0.7.2", + "playtime-api", + "reaper-low", + "semver 1.0.17", + "serde", + "serde_json", + "strum", + "stylua", + "syn 2.0.48", +] + [[package]] name = "helgobox-artwork-processor" version = "0.1.0" @@ -2818,6 +2967,14 @@ dependencies = [ "resvg", ] +[[package]] +name = "helgobox-dialogs" +version = "0.1.0" +dependencies = [ + "derive_more", + "indexmap 2.1.0", +] + [[package]] name = "helgobox-extension" version = "0.1.0" @@ -2831,6 +2988,10 @@ dependencies = [ "reaper-medium", ] +[[package]] +name = "helgobox-macros" +version = "0.1.0" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -4443,9 +4604,9 @@ dependencies = [ "camino", "chrono", "derive_more", + "helgobox-macros", "nanoid", "num_enum 0.7.2", - "realearn-macros", "reaper-common-types", "reaper-low", "rmp-serde", @@ -4477,11 +4638,11 @@ dependencies = [ "fragile", "function_name", "glidesort", - "helgoboss-allocator", "helgoboss-learn", "helgoboss-license-api", "helgoboss-license-processor", "helgoboss-midi", + "helgobox-allocator", "indexmap 2.1.0", "itertools 0.12.0", "libc", @@ -4574,13 +4735,13 @@ dependencies = [ "enumset", "fallible-iterator", "futures", - "helgoboss-allocator", + "helgobox-allocator", + "helgobox-api", "indexmap 2.1.0", "itertools 0.12.0", "lexical-sort", "nanoid", "once_cell", - "realearn-api", "reaper-high", "reaper-medium", "regex", @@ -4615,11 +4776,11 @@ dependencies = [ "egui", "egui-toast", "egui_extras", + "helgobox-api", "lru", "opener", "pot", "pulldown-cmark", - "realearn-api", "reaper-high", "reaper-medium", "rfd", @@ -4889,155 +5050,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be105c72a1e6a5a1198acee3d5b506a15676b74a02ecd78060042a447f408d94" -[[package]] -name = "realearn" -version = "2.16.0-pre.18" -dependencies = [ - "anyhow", - "approx", - "arboard", - "ascii", - "askama", - "async-channel 2.1.1", - "auto_impl", - "axum", - "axum-server", - "backtrace", - "base", - "baseview", - "bindgen", - "built", - "bytesize", - "c_str_macro", - "camino", - "cc 1.0.73", - "chrono", - "crossbeam-channel", - "derivative", - "derive_more", - "dns-lookup", - "edit", - "egui", - "egui-baseview", - "egui_extras", - "either", - "embed-resource", - "enum-map", - "enum_dispatch", - "enumflags2", - "enumset", - "env_logger", - "fasteval", - "fragile", - "futures", - "helgoboss-allocator", - "helgoboss-learn", - "helgoboss-license-api", - "helgoboss-midi", - "hex", - "hostname", - "image", - "include_dir", - "indexmap 2.1.0", - "itertools 0.12.0", - "lazycell", - "libloading 0.8.0", - "maplit", - "metrics", - "metrics-exporter-prometheus", - "mlua", - "nanoid", - "nom", - "num_enum 0.7.2", - "once_cell", - "open", - "palette", - "percent-encoding", - "playtime-api", - "playtime-clip-engine", - "pot", - "pot-browser", - "prost", - "qrcode", - "raw-window-handle 0.4.3", - "rcgen", - "realearn-api", - "realearn-dialogs", - "reaper-common-types", - "reaper-high", - "reaper-low", - "reaper-macros", - "reaper-medium", - "reaper-rx", - "regex", - "rosc", - "runas", - "rust-ini", - "rx-util", - "rxrust", - "scopeguard", - "semver 1.0.17", - "serde", - "serde_ini", - "serde_json", - "serde_plain", - "serde_repr", - "serde_with", - "serde_yaml", - "slug", - "smallvec", - "strum", - "swell-ui", - "sys-info", - "sysinfo", - "tar", - "tempfile", - "tinyvec", - "tokio", - "tokio-stream", - "tonic", - "tower", - "tower-http", - "tracing", - "tracing-core", - "tracing-subscriber", - "tts", - "url", - "uuid 1.7.0", - "vst", - "walkdir", - "webbrowser", - "whoami", - "wildmatch", - "winapi", - "xxhash-rust", - "zstd", -] - -[[package]] -name = "realearn-api" -version = "0.1.0" -dependencies = [ - "anyhow", - "darling 0.20.3", - "derive_more", - "enum-map", - "enumset", - "heck", - "helgoboss-license-api", - "mlua", - "num_enum 0.7.2", - "playtime-api", - "realearn-macros", - "reaper-low", - "semver 1.0.17", - "serde", - "serde_json", - "strum", - "stylua", - "syn 2.0.48", -] - [[package]] name = "realearn-csi" version = "0.1.0" @@ -5045,22 +5057,10 @@ dependencies = [ "base", "derive_more", "helgoboss-midi", + "helgobox-api", "nom", - "realearn-api", ] -[[package]] -name = "realearn-dialogs" -version = "0.1.0" -dependencies = [ - "derive_more", - "indexmap 2.1.0", -] - -[[package]] -name = "realearn-macros" -version = "0.1.0" - [[package]] name = "reaper-common-types" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b92722715..057447378 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,9 @@ rppxml-parser = { git = "https://github.com/helgoboss/reaper-rs.git", branch = " swell-ui = { path = "swell-ui" } rx-util = { path = "rx-util" } playtime-clip-engine = { path = "playtime-clip-engine" } -realearn-api = { path = "api" } -realearn-macros = { path = "macros" } -helgoboss-allocator = { path = "allocator" } +helgobox-api = { path = "api" } +helgobox-macros = { path = "macros" } +helgobox-allocator = { path = "allocator" } playtime-api = { path = "playtime-api" } realearn-csi = { path = "csi" } helgoboss-learn = { path = "main/lib/helgoboss-learn", features = ["reaper-low"] } diff --git a/README.adoc b/README.adoc index 61b8a25bd..161b36641 100644 --- a/README.adoc +++ b/README.adoc @@ -100,7 +100,7 @@ impression and stay tuned if you are interested in the details. ReaLearn features a large and complete user guide. -- https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc[Read it right here on GitHub] (preferred, best readability) +- https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc[Read it right here on GitHub] (preferred, best readability) - https://github.com/helgoboss/realearn/releases/latest/download/realearn-user-guide.pdf[Download user guide as PDF file] TIP: The main focus of ReaLearn's user guide is being comprehensive, so it's more like a reference manual and can get quite detailed and technical. If you prefer a simple hands-on approach, the <> are a better choice. diff --git a/allocator/Cargo.toml b/allocator/Cargo.toml index c5e2aff70..5346d7872 100644 --- a/allocator/Cargo.toml +++ b/allocator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "helgoboss-allocator" +name = "helgobox-allocator" version = "0.1.0" authors = ["Benjamin Klum "] edition = "2021" diff --git a/api/Cargo.toml b/api/Cargo.toml index 5b6e5681b..0131b53e4 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "realearn-api" +name = "helgobox-api" version = "0.1.0" authors = ["Benjamin Klum "] edition = "2021" @@ -10,7 +10,7 @@ default = [] [dependencies] # For being able to use the API macro -realearn-macros.workspace = true +helgobox-macros.workspace = true reaper-low.workspace = true serde.workspace = true semver.workspace = true diff --git a/api/src/runtime/reaper.rs b/api/src/runtime/reaper.rs index f4583a0f6..0ee025d87 100644 --- a/api/src/runtime/reaper.rs +++ b/api/src/runtime/reaper.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case)] -use realearn_macros::reaper_api; +use helgobox_macros::reaper_api; reaper_api![ HelgoboxApi, HelgoboxApiPointers, HelgoboxApiSession, register_helgobox_api diff --git a/base/Cargo.toml b/base/Cargo.toml index 24d8eb7a2..0dfb5ddb8 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -11,8 +11,8 @@ reaper-high.workspace = true reaper-medium.workspace = true reaper-low.workspace = true reaper-rx.workspace = true -realearn-api.workspace = true -helgoboss-allocator.workspace = true +helgobox-api.workspace = true +helgobox-allocator.workspace = true # 3rd-party serde.workspace = true diff --git a/base/src/metrics_util.rs b/base/src/metrics_util.rs index 82f0a051b..6d8066ee0 100644 --- a/base/src/metrics_util.rs +++ b/base/src/metrics_util.rs @@ -26,7 +26,7 @@ impl Drop for MetricsHook { } impl MetricsHook { - /// Initializes metrics recording if the env variable `REALEARN_METRICS` is set. + /// Initializes metrics recording if the env variable `HELGOBOX_METRICS` is set. /// /// This starts a dedicated metrics recording thread, which is responsible for actually /// recording certain metrics (e.g. durations), which is especially important when measuring @@ -41,14 +41,14 @@ impl MetricsHook { /// The returned metrics hook must be dropped before the library is unloaded, otherwise the /// metrics thread sticks around and that can't be good. pub fn init() -> Option { - std::env::var("REALEARN_METRICS").ok()?; + std::env::var("HELGOBOX_METRICS").ok()?; let (sender, receiver) = std::sync::mpsc::sync_channel(5000); thread::Builder::new() - .name(String::from("ReaLearn metrics")) + .name(String::from("Helgobox metrics")) .spawn(move || { keep_recording_metrics(receiver); }) - .expect("ReaLearn metrics thread couldn't be created"); + .expect("Helgobox metrics thread couldn't be created"); METRICS_SENDER .set(sender.clone()) .expect("attempting to initializing metrics hook more than once"); diff --git a/base/src/mouse/api.rs b/base/src/mouse/api.rs index d9a83c35d..a57958ff2 100644 --- a/base/src/mouse/api.rs +++ b/base/src/mouse/api.rs @@ -1,4 +1,4 @@ -use realearn_api::persistence::{Axis, MouseButton}; +use helgobox_api::persistence::{Axis, MouseButton}; pub trait Mouse { fn axis_size(&self, axis: Axis) -> u32; diff --git a/base/src/mouse/enigo.rs b/base/src/mouse/enigo.rs index fb1b76543..26dd87a42 100644 --- a/base/src/mouse/enigo.rs +++ b/base/src/mouse/enigo.rs @@ -1,7 +1,7 @@ use crate::{Mouse, MouseCursorPosition}; use device_query::DeviceState; use enigo::{Enigo, MouseControllable}; -use realearn_api::persistence::{Axis, MouseButton}; +use helgobox_api::persistence::{Axis, MouseButton}; use std::fmt::Debug; #[derive(Debug, Default)] diff --git a/csi/Cargo.toml b/csi/Cargo.toml index 253a85ab8..d455d08b6 100644 --- a/csi/Cargo.toml +++ b/csi/Cargo.toml @@ -9,5 +9,5 @@ publish = false nom.workspace = true helgoboss-midi.workspace = true base.workspace = true -realearn-api.workspace = true +helgobox-api.workspace = true derive_more.workspace = true \ No newline at end of file diff --git a/csi/src/lib.rs b/csi/src/lib.rs index ea81f0686..dfc73e36c 100644 --- a/csi/src/lib.rs +++ b/csi/src/lib.rs @@ -1,7 +1,7 @@ use base::hash_util::NonCryptoHashSet; use derive_more::Display; use helgoboss_midi::{RawShortMessage, ShortMessage, StructuredShortMessage, U14, U7}; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ ApiObject, ButtonFilter, Compartment, Envelope, Glue, Interval, MackieLcdSource, MackieSevenSegmentDisplayScope, MackieSevenSegmentDisplaySource, Mapping, MidiChannelPressureAmountSource, MidiControlChangeValueSource, MidiNoteVelocitySource, diff --git a/dialogs/Cargo.toml b/dialogs/Cargo.toml index a077a9a67..2ab168fe8 100644 --- a/dialogs/Cargo.toml +++ b/dialogs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "realearn-dialogs" +name = "helgobox-dialogs" version = "0.1.0" authors = ["Benjamin Klum "] edition = "2021" diff --git a/dialogs/src/rc_file_header.txt b/dialogs/src/rc_file_header.txt index af602be74..b133300b6 100644 --- a/dialogs/src/rc_file_header.txt +++ b/dialogs/src/rc_file_header.txt @@ -1,4 +1,4 @@ -// Resource script generated by realearn-dialogs. +// Resource script generated by helgobox-dialogs. // #include "resource.h" #include "windows.h" diff --git a/doc/controllers.adoc b/doc/controllers.adoc index bfa275a51..851b40650 100644 --- a/doc/controllers.adoc +++ b/doc/controllers.adoc @@ -514,7 +514,7 @@ All presets assume "Live" mode. === Novation SL MkIII -The screens of this device are extremely flexible. That's why I decided to not add built-in support but provide a link:https://gist.github.com/helgoboss/902d78137248da1683791e19d435be06[Lua script] for ReaLearn's link:https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#midi-script[MIDI script source]. +The screens of this device are extremely flexible. That's why I decided to not add built-in support but provide a link:https://gist.github.com/helgoboss/902d78137248da1683791e19d435be06[Lua script] for ReaLearn's link:https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#midi-script[MIDI script source]. You can adjust this script to your needs. It allows you to leverage all of the possibilities this keyboard gives you in terms of screen and LED control. Watch link:https://youtu.be/aTCuN_iysKo[this video] to get an idea of what you can do with it. diff --git a/doc/playtime-clip-engine.adoc b/doc/playtime-clip-engine.adoc index 12005a280..62e2e8c71 100644 --- a/doc/playtime-clip-engine.adoc +++ b/doc/playtime-clip-engine.adoc @@ -28,7 +28,7 @@ The Clip Engine is built into the REAPER plug-in ReaLearn, so you first need to IMPORTANT: Make absolutely sure to install the latest *pre-release* (>= v2.13.0-pre.4), otherwise it will not work! Pre-releases can be installed by right-clicking the package in ReaPack. -TIP: First things first: If you are new to ReaLearn, it's helpful to get a basic feel for it and make sure that your controller generally works, so I strongly suggest walking through the link:https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#quick-start[Quick start] section of its user guide before diving into the template project! It doesn't take long. +TIP: First things first: If you are new to ReaLearn, it's helpful to get a basic feel for it and make sure that your controller generally works, so I strongly suggest walking through the link:https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#quick-start[Quick start] section of its user guide before diving into the template project! It doesn't take long. === Step 2: Optimize the REAPER preferences @@ -140,7 +140,7 @@ In order to see which knob/button has which effect, you can use ReaLearn's _Proj This is not the typical grid controller, so my main preset is quite opinionated. Nevertheless, due to its grid-like alignment of the push encoders and rich visual feedback options, it's quite suitable for recording and launching clips. It can even show the current position within the clip. -. Set up the Twister exactly as described in the link:https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#dj-techtools-midi-fighter-twister[corresponding section] in ReaLearn's user guide, subsection "Preparation". +. Set up the Twister exactly as described in the link:https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#dj-techtools-midi-fighter-twister[corresponding section] in ReaLearn's user guide, subsection "Preparation". + IMPORTANT: Existing ReaLearn users, watch out. I've added new instructions! + @@ -174,7 +174,7 @@ The Mackie Control controller preset doesn't define any Projection layout, so yo There are plenty of OSC apps out there and even more layouts. Unless you are satisfied with my super simplistic TouchOSC Mk2 preset, you'll need to adjust the main preset Lua code. Recording is not implemented at the moment because it would need some tweaking of the OSC layout to get proper visual feedback for it (red color). Feel free to adjust it to your needs. -. Set up your TouchOSC device globally in ReaLearn (not in REAPER!) by following link:https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#hexler-touchosc-the-recent-version[these instructions]. +. Set up your TouchOSC device globally in ReaLearn (not in REAPER!) by following link:https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#hexler-touchosc-the-recent-version[these instructions]. . Select the device as both _Control input_ and _Feedback output_. . In TouchOSC: Choose the built-in "Simple Mk2" layout and open the tab "Matrix". . Import the main preset diff --git a/doc/playtime.adoc b/doc/playtime.adoc index 9f0bca52f..d269869e6 100644 --- a/doc/playtime.adoc +++ b/doc/playtime.adoc @@ -3,12 +3,3 @@ :toclevels: 2 == Notes for later - -=== High buffer sizes - -* Mention in "troubleshooting" section that one has to be careful with high buffer sizes -* At the moment, Playtime 2 is a live instrument, so it's intended to be used with buffer sizes that don't introduce more than 10 or 11ms of latency. -* When using it with higher buffer sizes, the following effects may occur: -** When triggering audio clips on a track that contains FX which introduces latency, the audio will be delayed -** Triggering MIDI clips will lead to unpleasant delays if the virtual instrument FX introduces latency -* Behavior with high buffer sizes might improve in future \ No newline at end of file diff --git a/doc/realearn-user-guide.adoc b/doc/realearn-user-guide.adoc new file mode 100644 index 000000000..d941b18c8 --- /dev/null +++ b/doc/realearn-user-guide.adoc @@ -0,0 +1,4751 @@ += ReaLearn user guide +:experimental: +:toc: +:toclevels: 5 +:sectnums: +:sectnumlevels: 2 + +// Reusable text snippets +:osc_host_instruction: Enter the IP address of the computer running ReaLearn. You can easily find it by pressing the "Projection" button in ReaLearn and scrolling down a bit. It's the value next to "Host" and should start with "192.168.". +:osc_port_instruction: Choose a random port number greater than 1024, preferably 7879. This number must not be in use yet by other OSC applications, not even by REAPER's native OSC! +:osc_preset_content: There are no ReaLearn controller presets for OSC layouts yet. Although technically possible in exactly the same way as with controller presets for MIDI devices, OSC layouts are very custom, so I'm not sure if it would make much sense to create presets. Time will show. + +|=== +|Last update of text: |`2023-06-05 (v2.16.0-pre.1)` +|Last update of relevant screenshots: |`2021-04-27 (v2.8.0)` +|=== + +== Quick start + +Here's a step-by-step guide to help you get started with ReaLearn and a MIDI controller: + +. Start REAPER. +. If you haven't already done it, https://github.com/helgoboss/realearn#installation[install ReaLearn via ReaPack]. +. Make sure your MIDI controller is enabled in _Options → Preferences… → Audio → MIDI Devices_ + * For the MIDI input device (control), tick _Enable input from this device_ and untick + _Enable input for control messages_. + * For the MIDI output device (feedback), tick both _Enable output to this device_ and _Do not send reset messages_. ++ +[NOTE] +==== +The option _Do not send reset messages_ isn't available in older REAPER versions. If you don't use any external hardware synths, you can untick the global options _Reset on: Play_ and _Reset on: Stop_ instead! +==== +. Make sure the MIDI device is *not* in use as a REAPER control surface (in _Options → Preferences... → Control/OSC/web_). +. Check if there's an existing controller preset for your MIDI controller (this is optional but can make things easier). + * Extensions → ReaPack → Browse packages… + * Type "realearn controller" in the _Filter_ field. + * You should see a list of ReaLearn controller presets. + * If you find your controller in the list, right-click it, choose install and press OK. +. Fire up an instance of ReaLearn + * If you want your mappings to be specific to a particular project, create a new REAPER project or open an existing one. Right-click the track control panel and choose "Insert virtual instrument on new track…". + * If you want your mappings to be automatically available in each of your projects, open REAPER's global monitoring FX chain (View → Monitoring FX) instead. + * Then choose "VSTi: ReaLearn (Helgoboss)" +. Configure the ReaLearn instance + * Select your controller's MIDI device as _Input_ and _Output_ (if you have a controller + that supports MIDI feedback). + * If you have downloaded a controller preset: + - Switch to _Controller compartment_ and select the desired controller preset below. + - This should fill the list below with so-called _controller mappings_. + - When you are done, switch back to the _Main compartment_. +. Add and learn your first mapping + * Add a first mapping by pressing the _Add one_ button. + - A mapping appears that doesn't do anything yet because it just has a dummy target. + * Press _Learn source_ and move a control element on your MIDI controller. + * Press _Learn target_ and move e.g. the volume fader of a track. + * Now your control element should control the track volume. + +If you want to get the most out of your controller and learn about all of ReaLearn's cool features, please read on. + +== Introduction + +=== What is ReaLearn? + +Probably you know already that ReaLearn is a sort of improvement over REAPER's built-in MIDI/OSC learn. But what is it +exactly? Let me put it this way: + +____ + +ReaLearn is an instrument. It allows you to take whatever MIDI/OSC controller you have, be it a +keyboard or some fader box, plug it in and play … but instead of playing notes, you "play" +REAPER itself! + +And because ReaLearn supports MIDI/OSC feedback, you can also do the reverse: Let REAPER "play" your +controller. + +____ + +While this is still pretty vague, I think it captures the essence of ReaLearn. From a technical +viewpoint it's a VSTi plug-in, so it is an instrument, quite literally. That's one thing that sets +it immediately apart from the more conventional control surface feature in REAPER and 3rd-party +efforts such as https://forum.cockos.com/showthread.php?t=183143[CSI] or +http://www.mossgrabers.de/Software/Reaper/Reaper.html[DrivenByMoss]. The goal of the +latter-mentioned is to equip REAPER with support for specific controllers, typically +dedicated DAW controllers such as +https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/Mackie_Control_Universal.jpg/1600px-Mackie_Control_Universal.jpg[Mackie MCU] +that are tailored to control a DAW just like a hardware mixer. And I suppose they do a pretty good +job at that. + +ReaLearn's approach is quite different: It gives you total control over which control element operates which REAPER +parameter and provides you with a _learn_ function that allows you build your own control mappings quickly +and intuitively without writing configuration files. All of that on a _per-instance_ basis. That's right, by default, the mappings +are saved as part of the ReaLearn instance and therefore as part of your REAPER project. No need to pollute your global +control mappings just for the needs of one project! + +Nevertheless, since version 2, ReaLearn is also a great choice for setting up global mappings for usage across multiple projects. Just add ReaLearn to the monitoring FX chain of REAPER (View → Monitoring FX) and ReaLearn will be instantly available in all of your REAPER sessions without having to add it to a project first. In addition, ReaLearn provides a simple yet powerful preset system to make a set of mappings reusable in whatever project you want. + +ReaLearn is designed to get the most out of general-purpose MIDI controllers, which - compared to the big +and bulky DAW consoles - usually have the advantage of being small, USB-powered, more versatile and easier on the +budget. ReaLearn doesn't impose many requirements on your controller. Thanks to features like <> and <>, it can turn even the cheapest MIDI controller into a powerhouse for controlling +your DAW. + +The usual ReaLearn workflow for a single mapping goes like this: + +. Add a mapping +. Hit _Learn source_ and touch some knob on your controller. +. Hit _Learn target_ and touch some target parameter. +. Done. + +If you want to learn multiple mappings in one go, this gets even easier via the _Learn many_ button which can save you a lot of clicks. + +The result are mappings that you can customize as you desire, for example by setting a target value +range. All of that with MIDI/OSC feedback support, which was previously only available in the less +dynamic, more global control surface world. + +*Summary:* _ReaLearn is a sort of instrument for controlling REAPER._ + +=== Videos + +If you want to get a first impression of ReaLearn, a video is surely a good way. + +Here's a list of official ReaLearn videos: + +* https://www.youtube.com/playlist?list=PL0bFMT0iEtAgKY2BUSyjEO1I4s20lZa5G[The ReaLearn Tutorials]: A series of ReaLearn tutorials. +* https://youtu.be/dUPyqYaIkYA[Introduction to ReaLearn 2]: An in-depth introduction to ReaLearn 2, the sophisticated MIDI-learn plug-in for REAPER. + +Here's a short, non-complete list of user-made videos. Please note that at the moment all of them relate to older +ReaLearn versions and therefore might be partially outdated: + +* https://www.youtube.com/watch?v=WKF2LmIueY8[How To: ReaLearn and MIDI Controller for Track Sends in REAPER - Tutorial] +* https://www.youtube.com/watch?v=UrYrAxnB19I[using ReaLearn to assign MIDI controllers to (VST) plugin parameters in Cockos Reaper] + +=== Usage scenarios + +Ultimately, ReaLearn gains whatever purpose you can come up with. Because it is a VSTi plug-in and +provides many MIDI routing options, it's very flexible in how it can be used. You can "inject" it +wherever you want or need it (limitation: using it in a take FX chain is not possible yet): + +* *Input FX chain for live-only use:* Put it on a track's input FX chain in order to use it only + for incoming "live" MIDI and let it control a parameter of an effect that's on the normal FX + chain, right below a synthesizer. It will be active only if the track is armed for recording. + All MIDI messages that are used for parameter control will _automatically_ be filtered by default + and won't reach the controlled instrument, which is usually exactly what you need. +* *Grid controller for song switching:* Use some grid controller like the + https://thumbs.static-thomann.de/thumb/thumb250x220/pics/prod/339386.jpg[AKAI APC Key 25] to + arm/disarm various tracks (effectively enabling/disabling certain sound setups) by pressing the + grid buttons - with the LEDs of the buttons indicating which setup is currently active. +* *Combination with other MIDI FX for interesting effects:* Slap it on a track FX chain, right + between a MIDI arpeggiator and a synthesizer to arpeggiate the cutoff parameter of that + synthesizer. +* *Monitoring FX for project-spanning setups:* Put it on the monitoring FX chain to have some + control mappings available globally in all projects (similar to conventional control surface + stuff). +* *Unusual settings for experimental stuff:* Create a track volume mapping with only feedback + turned on. Choose "<FX output>" as MIDI output and play the synthesizer one + position below in the FX chain by moving the track volume slider (whatever that might be good for + …). +* *Rotary encoders for avoiding parameter jumps:* How about a refreshingly "normal" use case? Let + your rotary endless encoder control a track send volume without parameter jumps and restrict the + value range to volumes below 0dB. +* *VST presets for easy reuse:* Save a bunch of commonly used mappings globally as FX presets. +* *Switching controller and main presets separately:* Maintain controller and main presets and switch + between them as you like. Easily switch your controller without adjusting your FX presets. +* *Combination of multiple instances:* Use one ReaLearn instance to arm or disarm tracks that + contain other ReaLearn instances to enable/disable different mapping groups. Group mappings and + activate/deactivate them group-wise simply by instantiating multiple ReaLearn instances and + enabling/disabling them as desired in the FX chain window. + +… the possibilities are endless. It's all up to you! Use your creativity. + +All of that makes ReaLearn especially well-suited for performers, people who use REAPER as a +platform for live playing. It might be less interesting to people who are satisfied with a control surface setup off the shelf. But even so, as long as you have some general-purpose MIDI controller and you want a fine-tuned mapping to DAW parameters +of all sorts, give ReaLearn a try. It might be just what you need. More so if the controller supports feedback +(e.g. motorized faders, LEDs or LCDs). + +*Summary:* _ReaLearn is tailored to usage scenarios typically desired by performers._ + +== Basics + +=== Control + +After installing ReaLearn, you can fire it up just like any other VST instrument in REAPER: By +adding it to an FX chain. + +. Right click in the track header area and choose "Insert virtual instrument on new track…" +. Choose "VSTi: ReaLearn (Helgoboss)" + +After that you should see ReaLearn's main panel (unlike this screenshot, it wouldn't contain any +mappings yet): + +image:images/screenshot-main-panel-annotated.svg[Main panel] + +On the very top you see the _header panel_ for changing settings or doing things that affect +this complete instance of ReaLearn. Below that there's the _mapping rows panel_ which displays all +main mappings in this instance of ReaLearn. There can be very many of them. On the very bottom you see some information about the version of ReaLearn that you are +running. + +It can be useful to route all keyboard input to ReaLearn, so you can enter spaces in the "Search" field: + +. Right click ReaLearn FX in the FX chain. +. Enable "Send all keyboard input to plug-in". + +[discrete] +==== Adding a mapping + +*Let's see how to add and use our first MIDI mapping:* + +. Press the "Add one" button. +* A new mapping called "1" should appear in the mapping rows panel. +* For now, it doesn't have any effect. The default target is a + <> target which basically does nothing. +. Press the "Learn source" button of that new mapping. +* Its label will change to "Stop". +. Touch some control element on your MIDI controller (knob, encoder, fader, button, key, pitch + bend, mod wheel, …). For this example it's best to use something continuous, not a button or + key. +* If your MIDI is set up correctly, the button label should jump back to "Learn source" and the + touched control element should appear in the _source label_. See below if this doesn't happen. +. Press the "Learn target" button. +* Its label will change to "Stop". +. Touch the volume fader of your newly created REAPER track. +* The button label should jump back to "Learn target" and "Track: Set volume" should appear in the + _target label_. +. Now you should be able to control the touched target with your control element. + +[discrete] +[#troubleshooting] +==== Troubleshooting + +[discrete] +==== ReaLearn doesn't appear in the list of plug-ins + +- Make sure you look in the *VSTi* section (ReaLearn is an instrument). +- If REAPER crashes when scanning for plug-ins and the crash message shows something like `reaper_host64` or `reaper_host32`, you either have a 32/64-bit version mismatch or you have _Preferences → Plug-ins → Compatibility → VST bridging/firewalling_ set to "In separate plug-in process" or "In dedicated process per plug-in". Please see the https://github.com/helgoboss/realearn#installation[installation instructions on the +project website] for hints how to fix this. + +[discrete] +==== ReaLearn doesn't learn MIDI messages + +If the label remains at "Stop" at step 3, that means ReaLearn doesn't see the incoming MIDI messages. You need to have a look at your MIDI setup. + +* Make sure the MIDI device is *not* installed as REAPER control surface (in _Preferences → Control/OSC/web_). +* Make sure *Enable input from this device* is checked for your controller MIDI input device in + the REAPER preferences. ++ +[NOTE] +==== +_Enable input for control messages_ is totally irrelevant for ReaLearn. This is +only used for REAPER's built-in MIDI learn, which uses the so-called _control MIDI path_. + +With ReaLearn, you use the same MIDI path for controlling and playing, which is one reason why it is so flexible. It provides local and global MIDI message filtering, so you still don't need to worry about messages that are intended for control but suddenly cause your synthesizer to play MIDI notes. +==== ++ +* Make sure your audio hardware is not stuck (playback in REAPER should work). +* Make sure the track is armed for recording and has the appropriate MIDI device input. ++ +NOTE: This is necessary only if _Input_ is set to __, which is the default. If you capture MIDI from a specific device, the track doesn't have to be armed. +* Some controllers, especially DAW controllers, are able to work with several protocols (MCU, HUI, MIDI, …). +** For this simple test, it's probably the best to make your controller enter a specific MIDI operation mode. +** Although MCU and HUI is also just MIDI under the hood, these operation modes are more specialized and therefore need a bit of special attention. ReaLearn conveniently handles these modes when using the _Mackie Control_ controller preset. +** In any case, please consult the <> section, maybe you will find some information about your controller. + +When you read this the first time, you might get the impression that this is a lot of work for +setting up one simple control mapping. It's not. Learning mappings is a matter of a few secs after +you got the hang of it. ReaLearn also provides the "Learn many" button and a bunch of REAPER actions +for one-click learning straight from a REAPER toolbar or triggered by a button on your controller. +More about that later. + +At this point: Congratulations! You have successfully made your first baby steps with ReaLearn. + +[discrete] +==== Some words about MIDI routing + +If you think that what we saw until now is not more than what REAPER's built-in MIDI learn already +offers, I can't blame you. First, don't worry, there's more to come, this was just the beginning. +Second, there _is_ a difference. For some folks, this is an insignificant difference, for others +it's a game changer, it depends on the usage scenario. The key to understand this difference is to +understand the MIDI _routing_: In above example, _Input_ was set to `<FX input>`. That means +we used normal track MIDI messages to control a parameter in REAPER - let's call it _track MIDI path_. +This is different from REAPER's built-in MIDI learn, which uses the totally separate _control MIDI path_. + +Using the track MIDI path means it's completely up to you to decide what MIDI messages flow into +ReaLearn. You decide that by using REAPER's powerful routing capabilities. For example, you can +simply "disable" the mapping by disarming your track, a feature that is very desirable if you use +REAPER as live instrument. Or you can preprocess incoming MIDI (although that should rarely be +necessary given ReaLearn's mapping customization possibilities). + +Instead of using `<FX input>`, you can also pick the MIDI device of your choice directly, in which case ReaLearn will +ignore track MIDI messages and capture MIDI messages directly from the already open MIDI device. + +Another thing worth to point out which is different from built-in MIDI learn is that we didn't use +the action "Track: Set volume for track 01". Benefit: ReaLearn will let you control the volume of +the track even if you move that track to another position. The track's position is irrelevant! + +=== Feedback + +In ReaLearn, every mapping has 2 directions: _control_ (controller to REAPER) and _feedback_ (REAPER +to controller). So far we have talked about the _control_ direction only: When you move a knob on +your controller, something will happen in REAPER. But if your controller supports it, the other +direction is possible, too! + +Imagine you would use a MIDI-controllable motorized fader as control element to change the track +volume. ReaLearn is capable of making that fader move whenever your track volume in REAPER changes - +no matter if that change happens through automation or through dragging the fader with your mouse. +Motorized faders are quite fancy. Another form of feedback visualisation are rotary encoders with +LEDs that indicate the current parameter value. + +How to set this up? Often it's just a matter of choosing the correct feedback device: + +. Make sure *Enable output to this device* and *Do not send reset messages* is checked for your controller MIDI output device in the REAPER preferences. ++ +[NOTE] +==== +The option _Do not send reset messages_ isn't available in some older REAPER versions. If you don't use any external hardware synths, you can untick the global options *Reset on: Play* and *Reset on: Stop* instead! +==== +. In ReaLearn's header panel, select your controller as _MIDI output_. + +That should be it! + +If it doesn't work and you have ruled out MIDI connection issues, here are some possible causes: + +. *Your controller is not capable of feedback via MIDI messages.* +* Some controllers _do_ support feedback, but not via MIDI. +* If they support feedback via OSC, you are lucky because ReaLearn supports that, too. This is discussed + in another section. +* If it's another protocol, you are out of luck. Reverse engineering proprietary protocols is out of + ReaLearn's scope. +* Recommendation: Maybe you are able to find some bridge driver for your controller that is + capable of translating generic MIDI messages to the proprietary protocol. Then it could work. +* Examples: Akai Advance keyboards, Native Instruments Kontrol keyboards +. *Your controller doesn't support feedback via generic MIDI messages but via MIDI SysEx.* +* In this case, MIDI feedback is probably still achievable because since version 2.6.0 ReaLearn also supports + feedback via MIDI system-exclusive messages. However, it's not going to be straightforward. + Unless you find an existing controller preset for your controller, you'll have to read the MIDI specification + of your controller (hopefully there is one) … or you need to experiment a lot. +* Examples: Arturia MiniLab mkII (but we have a controller preset for this one!) +. *Your controller has multiple modes and currently is in the wrong one.* +* Some controllers, especially DAW controllers, are able to work with several protocols. +* Recommendation: Consult your controller's manual and take the necessary steps to put it into + something like a "generic MIDI" mode. +* Example: Presonus Faderport +. *Your controller expects feedback via messages that are different from the control MIDI messages.* +* Usually, controllers with feedback support are kind of symmetric. Here's an example what I mean + by that: Let's assume your motorized fader _emits_ CC 18 MIDI messages when you move it. That + same motorized fader starts to move when it _receives_ CC 18 MIDI messages (messages of exactly + the same type). That's what I call symmetric. E.g. it's not symmetric if it emits CC 18 but + reacts when receiving CC 19. +* ReaLearn assumes that your controller is symmetric. If it's not, you will observe non-working + or mixed-up feedback. +* Recommendation: Consult your controller's manual and try to find out which MIDI messages need + to be sent to the controller to deliver feedback to the control element in question. Then, + split your mapping into two, making the first one a control-only and the second one a + feedback-only mapping. Adjust the source of the feedback-only mapping accordingly. In the next + section you'll learn how to do that. +* Example: Presonus Faderport + +TIP: Have a look into the section <>. Maybe your controller is listed there along with some tips. + +=== Editing a mapping + +When you press the _Edit_ button of a mapping row, a so-called _mapping panel_ appears, which lets +you look at the corresponding mapping in detail and modify it: + +image:images/screenshot-mapping-panel.png[Mapping panel] + +This panel has 4 sections: + +* *Mapping:* Allows to change the name and other general settings related to this mapping. +* *Source:* Allows to edit the _source_ of the mapping. In most cases, a source represents a + particular control element on your controller (e.g. a fader). +* *Target:* Allows to edit the _target_ of the mapping and optionally some target-related + activation conditions. A target essentially is the parameter in REAPER that should be controlled. +* *Glue:* Allows to change in detail how your source and target will be glued together. This + defines _how_ incoming control values from the source should be + applied to the target (and vice versa, if feedback is used). This is where it gets interesting. + Whereas REAPER's built-in MIDI learn provides just some basic modes like Absolute or Toggle, ReaLearn + allows you to customize many more aspects of a mapping. + +By design, source, glue and target are independent concepts in ReaLearn. They can be combined +freely - although there are some combinations that don't make too much sense. + +Changes in the mapping panel are applied immediately. Pressing the _OK_ button just closes the +panel. + +*Tip:* It is possible to have up to 4 mapping panels open at the same time. + +=== Controller setup + +In order to get the most out of your controller in combination with ReaLearn, you should consider +the general hints given in the section <>. + +[#automation-and-rendering] +=== Automation and rendering + +Similarly to control surfaces, ReaLearn is primarily meant to be used for controlling targets "live". If you +want to _persist_ the resulting target value changes, you can do so by writing automation. Just as +with any other automation, it will be included when you render your project. + +It _is_ possible to feed ReaLearn with track MIDI items instead of live MIDI data. This results +in a kind of _pseudo automation_. Some users call this _MIDI CC based automation_. This feature can be quite interesting and appealing to MIDI fans. + +[CAUTION] +==== + +*Support for rendering pseudo automation is limited!* + +Let's say you finally want to render your project. If you don't watch out, your pseudo automation will simply be ignored! + +Pseudo automation will only be rendered if you follow some very distinct rules: + +- The target must be <> (all other targets will most likely be ignored). +- The targeted FX must be on the same track as the ReaLearn instance itself. +- This only works in REAPER versions >= 6.52+dev0324. + +I remember that *Online Render* used to respect all kinds of pseudo automation. However, this must have stopped working at some point (or it works only under particular circumstances or with certain settings, not sure). Anyway, now you need to follow the same rules as with offline rendering to make pseudo automation work. +==== + + + +[#companion-app] +== Companion app + +This section is about the _ReaLearn Companion_ app, which is a separate software that powers ReaLearn's <> feature. + +At the moment it comes as https://play.google.com/store/apps/details?id=org.helgoboss.realearn_companion[Android app] +and https://realearn.helgoboss.org/[web app]. The iOS app has not been published yet. +The source code is available https://github.com/helgoboss/realearn-companion[here at GitHub]. + +=== Connecting to ReaLearn + +The start screen lets you connect to a specific ReaLearn instance by scanning the QR code that pops up when +pressing ReaLearn's <>. It's also possible to enter the connection data manually, in +case your device doesn't have a camera or in case you are using the web app (in which QR code scanning often doesn't +work so well). If you are experiencing issues, follow the instructions given by the app and the setup guide which is +displayed when pressing the <> button! + +Please note, if all you want is to open the web app on the computer that also runs REAPER/ReaLearn, you don't need to +bother with QR codes or connection data at all. Just follow the link that is displayed in the setup guide. + +ReaLearn allows many Companion apps to connect to it simultaneously, there's no artificial limit. + +=== Viewing the controller projection + +As soon as you have connected, you should be able to see the controller projection, which consists of both the +controller layout and the current mapping of its control elements. If not, the app will give you a hint what's missing. +The control element labels will reflect the labels of your main mappings. + +You can tap the screen to make the app bar disappear or reappear. There's a menu on the right side of the app bar +which let's you change various aspects of the appearance. Just give it a try! Dark mode combined with high-contrast is +especially nice on devices with OLED displays! All of these settings will be saved on your device, not in ReaLearn's +controller preset. + +Another thing you can do here is applying two-finger gestures in order to zoom/pinch. + +=== Editing the controller layout + +Pressing the pencil button in the app bar let's you enter edit mode. As soon as you do that, the control element labels +will reflect the labels of your controller mappings and a palette will appear on the side of the screen. + +==== Editing basics + +You can drag the controls freely from the palette onto the scene and back. Pressing a control element opens a panel +which lets you change its appearance. The two labels mentioned there are used in the following way: + +. If the control element is a composite control element (see below, e.g. push encoder), the first label represents the +mapping of the first inner control element (e.g. the encoder) and the second label represents the mapping of the +second inner control element (e.g. the button). See the _Midi Fighter Twister_ <> for a real-world usage of this feature. +. If the control element is just a normal control element, the second label is usually empty. Except this control +element has more than one main mapping assigned: In that case the second label shows the second main mapping. + +Whenever you press the save button (floppy disk) in the app bar, the layout is saved - not on your specific device +but as part of ReaLearn's controller preset! So this same layout will automatically be available to all other +connected Companion apps. + +You can leave the edit mode by pressing the pencil button again. This gives you a preview of your current changes. + +*Attention:* If you leave the controller projection view (e.g. by pressing the upper left arrow) or if you change your +controller preset from within ReaLearn, all non-saved controller layout changes will be lost! So it's a good idea to +save often. Once saved, there's no undo though. You can back up temporary states by copying the corresponding controller +preset files (on the computer running ReaLearn) to a temporary backup location (see _Save as…_ button documentation +in the <> section). + +==== Composite control elements + +If you want one visual control element to contain 2 logical control elements (e.g. a push encoder = encoder + button), +just move one control element onto another one - and they will merge into a composite control element. If you want to +undo this merging, move the merged control element back on the palette - they will split up and you can drag them onto +the scene again. + +==== Batch-editing control elements + +Sometimes it's a bit tedious to edit each control element separately. As soon as you long-press one control element, +the Companion app will enter multi-edit mode and you can start adding/removing other control elements to/from the +selection by just tapping them. When you move one element of the selection, all others will also be moved. You can open +the control element appearance panel by long-pressing an element. All changes made in the panel will immediately be +applied to all selected elements. + +You can leave multi-edit mode either by unselecting all elements or by (temporarily) leaving the edit mode. + +_Known issue:_ In the web app, multi-edit mode currently doesn't work, there's a graphical glitch. + +==== Dealing with the grid + +You can hide the grid using the app bar menu. The grid will still have an effect though. + +One way to get more fine-grained positioning is by decreasing the grid size. However, it doesn't go below a certain +minimum and changing the grid size after already having positioned lots of elements might not be the best idea. +Usually, the better way is to just expand the scene. Don't worry, your layout will always fit on the screen, no matter +how large the scene actually is in terms of grid squares! + +You can enlarge the scene by slightly moving a control element out of the scene. Do so in small steps and you will +automatically have more space at your disposal. The scene will always be as big as the imaginary rectangle from the +top-left control element to the bottom-right control element! + +[#tutorials] +== Tutorials + +The screenshots in this section are slightly out of date. If you feel like contributing to the project, this is an +area where you could help. + +=== Using conditional activation to implement banks/pages + +Users often ask if it's possible to do control surface bank-style mapping in order to switch to a completely +different set of mappings with the press of a button. Yes, it is! It's done using the _conditional activation_ feature +with the activation mode "When bank selected". + +TIP: ReaLearn >= 2.11.0 provides an alternative and probably more straightforward way to implement banks: The <> target. + +I'll show you a minimal example but in great detail. Once you understand this example, you should be able to progress to +bigger things. So let's assume you have 2 knobs and 2 buttons on your controller and you want to map some controls +to parameters of the https://vital.audio/[Vital synth]. Here's our goal: + +* *Knob K1:* Controls decay of ENV X +* *Knob K2:* Controls frequency of LFO X +* *Button B1:* Sets X to 1 +* *Button B2:* Sets X to 2 + +[discrete] +==== Step 1: Add all desired mappings + +First, it's important to understand that conditional activation does one thing only: It switches mappings on or off. +It doesn't magically change the target of a mapping or anything like that. Just on or off! Thus, the first thing you +should do is adding all the knob mappings (for example by using "Learn many"). Here's the result: + +image:images/tutorial-1-step-1.jpg[Step 1] + +Note: As you can see, I gave the mappings friendly names, which is nice in general but really pays off once you use the +projection feature. Also note that I used my Midi Fighter Twister preset and renamed the relevant encoders to K1 and K2. + +At this point, all those mappings are always active, so moving K1 will affect both ENV 1 and ENV 2 decay whereas moving +K2 will affect both LFO 1 and LFO 2 frequency! We need activation conditions to make sure that not all mappings are +active at the same time. + +[discrete] +==== Step 2: Assign mappings to groups + +Now we could shoot ahead and directly set the activation condition of each mapping individually. *But* usually it's +much better to activate/deactivate complete _groups_ of mappings. When you press button B1, you want to have the +"ENV 1 Decay" and "LFO 1 Freq" mappings active (= "Group 1"). When you press button B2, you want "ENV 2 Decay" and +"LFO 2 Freq" to be active instead (= "Group 2"). And this is just a minimal example. You will probably have many more +mappings in one group in the end. + +Turns out, ReaLearn has something made exactly for that: Mapping groups. Using them will make your life way easier. +We will create those 2 groups and distribute our knob mappings into both groups. + +. Right to "Mapping group", press "Add" and enter the name "Group 1". Repeat the same for "Group 2". +. Select mapping group `<Default>` again. +. Now move every mapping to its corresponding group by right-clicking the mapping row and choosing the desired group. + +Here's how "Group 1" looks like after this: + +image:images/tutorial-1-step-2.jpg[Step 2] + +Please note that until now, this is purely cosmetic. It hasn't changed in any way how the mappings work. + +[discrete] +==== Step 3: Set group activation conditions + +Now let's set the activation conditions. First for "Group 1": + +. Select mapping group "Group 1". +. Press "Edit". +. In the "Active" dropdown, choose "When bank selected". Make sure that "Parameter" is set to "1. Parameter 1" and +"Bank" to 0. + +Repeat the same for "Group 2", but set "Bank" to 1. Should look like this: + +image:images/tutorial-1-step-3.jpg[Step 3] + +Did you see how the mappings in "Group 2" turned grey? That means they became inactive! At this point, moving the knobs +should affect ENV 1 and LFO 1 only. + +[discrete] +==== Step 4: Understand "Parameter" and "Bank" + +In the previous step, we have set "Parameter" to "Parameter 1". It's important to understand that we are talking about +ReaLearn's own VST parameters. Each ReaLearn instance has 200 free parameters (100 per compartment) which don't do anything by default. +One easy way to make them visible is by pressing the "UI" button at the top right of the FX window to switch to the +parameter view: + +image:images/tutorial-1-step-4.jpg[Step 4] + +See "Parameter 1" at the top? That's the one we used in our activation condition! Hence, once we change the value of +this parameter, mappings will get activated or deactivated. You can try it! Move the parameter slider a bit to the right +and you will observe that "Group 1" turned inactive. "Group 1" will be active when the slider is on the very left. +"Group 2" will be active when the slider is pushed _slightly_ more to the right. If you push it even more to the right, +none of the mappings will be active. Enough! Press "UI" again to go back to the ReaLearn user interface. + +Now that we know that the value of ReaLearn's internal "Parameter 1" is the key to activate/deactivate our mappings, +the next step should be obvious: We need to map our buttons to it! + +[discrete] +==== Step 5: Map buttons to bank parameter + +We are going to map the buttons to "Parameter 1". Button B1 will set its value to 0 and button B2 will set its value +to 1. Remember how we defined these two numbers in the activation conditions … they are the "Bank" numbers! + +. Select mapping group `<Default>`. +. Map the two buttons. The easiest way is to use "Learn many", switch to the parameter view once again and move the +"Parameter 1" slider whenever ReaLearn asks you to touch the target. +** Before you continue, make sure your screen looks similar to this (take note how I've given the mappings friendly +names again): image:images/tutorial-1-step-5a.jpg[Step 5a] +. Edit the mapping for button B1 and set both Target Min/Max to 0 (this causes the button to always set the fixed +value 0). +** If you have a controller that is capable of feedback (button has LED), also set "Out-of-range behavior" to "Min". +This makes sure that the LED lights up whenever this bank is selected but switches off otherwise. +. Edit the mapping for button B2 and set both Target Min/Max to 1. +** Here's how the mapping panel for button B2 looks afterwards: image:images/tutorial-1-step-5b.jpg[Step 5b] +** If feedback is desired, set "Out-of-range behavior" as described in the previous step. + +That's it, the goal is achieved! Press the buttons and move the knobs to test it. + +You might wonder why ReaLearn has been designed to use this particular mechanism for activating/deactivating mappings, +in particular why it uses generic parameters to do the job. The answer is: This mechanism is insanely powerful. If you +take the time and digest this for a while, you will realize that you can do almost anything with a clever combination of +the "Mapping", "Parameter" and "Activation condition" concepts. This scenario is just one of many. Just see the next +tutorial to understand why. + +=== The same but with previous/next buttons + +Now let's assume you don't want 2 buttons where each button should activate one particular bank but you want +previous/next buttons to switch between the banks. Do everything as in tutorial 1 with the exception of step 5. + +TIP: ReaLearn >= 2.11.0 provides an alternative and probably more straightforward way to implement cycling through banks with previous/next buttons: By combining multiple mappings with <> target (for defining the banks) with one <> targets (for cycling). + +[discrete] +==== Step 5: Map buttons to bank parameter + +. As in tutorial 1. +. As in tutorial 1. +. Edit the mapping for button B2 ("Next group") and set mode to "Incremental button" +. Edit the mapping for button B1 ("Previous group"), set mode to "Incremental button" _and_ check the "Reverse" box +(because you want to go in the other direction). + +The "Previous group" mapping then looks like this: + +image:images/tutorial-2-step-5.jpg[Step 5] + +=== Using "Auto-load" to control whatever plug-in is currently in focus + +This one seems to be a very popular use case: To create a dedicated set of mappings for a specific FX plug-in and load +these mappings whenever focusing that plug-in on the screen. The easiest way to do this is to use the "Auto-load" +feature. + +To have a nice example, let's assume you want to build a first set of mappings for the VSTi plug-in +https://vital.audio/[Vital]. The procedure for other plug-ins is the same. + +[discrete] +==== Step 1: Activate the correct controller preset + +Before you start, I strongly recommend downloading a ReaLearn controller preset for your specific controller from ReaPack and activate it in the _controller compartment_. You will need to right-click the header panel and choose +<> to make a newly downloaded controller preset appear in the preset list. If there's no +suitable preset for your controller available on ReaPack or in the https://github.com/helgoboss/realearn/tree/master/resources/controller-presets/unofficial[list of unofficial controller presets], build your own. + +This step is completely optional, but it gives you many advantages, both in the short and long run. Please see +section <> for details. + +[discrete] +==== Step 2: Create mappings for your FX plug-in + +In this step you will tell ReaLearn which control element on your controller should control which parameter of your FX +plug-in: + +. Add Vital VSTi and a new _empty_ ReaLearn instance, both as track FX (**not as monitoring FX**), preferably +side-by-side so that you can see both. +** It's also possible to use monitoring FX but for this particular tutorial it's important to use track FX (otherwise +ReaLearn will not ask you if it should make your mappings project-independent in step 3). +. In ReaLearn, press _Learn many_. +. Move a control element on your controller, change a Vital parameter, move another control element, change another +Vital parameter … until you are finished! +. Press _Stop_. + +[discrete] +==== Step 3: Save mappings as main preset and link it to the FX type + +Now let's save your newly created set of mappings as preset and link the preset to the Vital VSTi plug-in: + +. Make sure the _main_ compartment is shown. +. Press _Save as…_ (next to _Preset_). +** ReaLearn will ask you if it should make your mappings project-independent. Answer with _Yes_ (important). +. Enter a descriptive preset name, e.g. "Vital". +. Right-click ReaLearn's header panel → `Global FX-to-preset links` → `<Add link from FX "Vital.dll" to...>` and choose +the previously created "Vital" preset. +** The name `Vital.dll` can vary, depending on your operating system. +** If it doesn't mention _Vital_ but another VST plug-in, focus your Vital VSTi plug-in instance for a moment and +then go directly to ReaLearn and right-click the header panel. + +[discrete] +==== Step 4: Activate "Auto-load" + +Now you just have to set _Auto-load_ to _Based on instance FX_. Since the <> is by default the currently focused FX, ReaLearn will from now on activate your "Vital" preset +whenever Vital VSTi plug-in has focus. If you want this in all projects without having to add ReaLearn to each +project manually, add a dedicated ReaLearn instance to REAPER's monitoring FX chain (REAPER → View → Monitoring FX). + +== FAQ + +=== How many instances and where to put them? + +Since ReaLearn is a VST instrument, you can have many instances of it, not just one. A question that comes up pretty +often is how many is right and where to put them? + +There's no definitive answer to that, it all depends on what you want. Here are some basic rules of thumb: + +. You need at least one ReaLearn instance per controller. +. For mappings that shall be available in any existing or new project automatically, create a ReaLearn instance on the +monitoring FX chain +** If you want to use multiple controllers, simply add multiple instances to the monitoring FX chain. +. For in-project mappings which control arbitrary parameters on arbitrary tracks, you are totally free to choose where +to put ReaLearn, there shouldn't be any differences in behavior. +** Putting it on the master FX chain is a good choice if you want it out of your way and usually not be visible. +** However, in order to be reminded that I use ReaLearn, I usually add some track at the very top and put all +ReaLearn instances on its FX chain. +. Let's say you have a VST instrument that you want to activate/deactivate for live playing by arming/disarming the +corresponding track. And now you want to use ReaLearn as a kind of "Insert" effect for controlling parameters of that +VST instrument (or other track-local parameters such as the track volume), only when it's active. Then put ReaLearn +exactly on that track, somewhere _above_ your VST instrument and select MIDI input `<FX input>`. +** That way your VST instrument won't receive MIDI that's already processed by ReaLearn, which is cool. +** If you want ReaLearn to only receive MIDI messages that originate from live playing and ignore MIDI that +originates from track MIDI items, put it on the input FX chain of that track. + +=== Should I use conditional activation or the "Enable/disable mapping" target? + +ReaLearn 2.11.0 introduces an alternative to <>: The ability to tag mappings and enable/disable them via the <> target. In general, one can say that conditional activation is slightly more powerful but that the enable/disable target is easier to use and enough in most common use cases. + +You strictly need to use conditional activation if you ... + +* ... want to activate/deactivate mappings using REAPER automation envelopes. +* ... want to sync the active/inactive state of a mapping with a <> expression. +* ... want to activate/deactivate mappings in another ReaLearn instance. +* ... want to activate/deactivate mappings in another compartment within the same ReaLearn instance. ++ +NOTE: This particular limitation of the enable/disable target might disappear in the future. + +You might prefer conditional activation if you ... + +* ... have a modifier use case (not a bank switching use case). +** It can be quite intuitive to think of modifiers as parameters that you define once and refer to them in the mapping (to be activated or deactivated) itself. +* ... have a modifier use case in which you want to combine multiple modifiers (e.g. activate a mapping only if the _Shift_ and _Control_ button is pressed at the same time) +** This is really much easier to achieve using conditional activation, think about it. +* ... prefer that the mapping itself defines when it should be active or not (instead of dictating it via tags). + +In all other circumstances the enable/disable target should be fine. + +==== More technical explanation + +Conditional activation introduces a level of indirection. It allows you to look at the act of enabling/disabling mappings as _two different concerns_ by introducing an intermediate concept called a "modifier" (respectively a "bank"). + +In particular, it separates the following two concerns: + +1. Switching a modifier on/off (respectively activating a bank) +2. Let other mappings follow the on/off state of the modifier (respectively the active bank) + +You define these two concerns in different mappings: + +1. "Modifier/bank-changing mappings" +2. "Modifier/bank-dependent mappings" (as activation condition) + +If you use the alternative to conditional activation, <>, you throw both of these concerns into one mapping! + +The advantage of separating these two concerns is that you can change *1* (the modifier/bank-changing mappings, e.g. which button controls the modifier and how) without having to touch *2* (the dependent mappings)! And vice versa. That can make complex setups easier to understand and reason about! + +The disadvantage is that it makes simple setups a bit harder to understand than necessary since you need at least two mappings instead of only one. + +As always: Choose the right tool for the job and consider starting off with the easiest tool. + + +[#tested-controllers] +== Controller support + +=== Explanation + +ReaLearn strives to support any general-purpose MIDI/OSC controller out there. However, there are some things you should know: + +. Not every controller works out of the box. +** There are controllers which might need some initial preparation in order to work optimally with ReaLearn. +** Don't fear this initial effort, it can pay off very quickly. ReaLearn is designed to get the most out of +your controller and make it work the way _you_ want it, not some company that wants to impose a certain type +of workflow on you. +** The versatility of a controller is a weakness but at the same time a strength. Taking some time to +get to know and prepare your controller can make you end up with a tool that is much better suited for +what you are trying to do than some bling-bling off-the-shelf solution. +. Some controllers don't work perfectly, especially when it comes to the _feedback_ direction. +** Among those controllers that support MIDI feedback, not all of them handle the feedback messages flawlessly. +** Depending on the nature of the particular problem, it might be possible to fix it in future ReaLearn versions. +Therefore, if you encounter a problem in this area, feel free to +https://github.com/helgoboss/realearn/issues[raise an issue]. +. Some controllers might have unique features that you can only use if you bring a bit of MIDI know-how and are ready +to use advanced ReaLearn features. +** Example: A controller might offer a way to change the appearance of an LED ring, but only via system-exclusive +MIDI messages. +** First, have a look if there's a controller preset already. Maybe it supports those advanced features already. +** If not, ReaLearn offers the following features for such scenarios: +*** <> (e.g. for sending MIDI sys-ex data on mapping +activation) +*** <> (for sending MIDI sys-ex data in response to target value changes) +*** <> (same but for more complex scenarios) +*** <> target (for sending MIDI sys-ex data triggered by a source) + +So even ReaLearn is made for any controller, it's still useful to have a list of specific controllers and how they work in combination with ReaLearn. This list is available link:https://github.com/helgoboss/realearn/tree/master/doc/controllers.adoc[here] + +=== General tips regarding controller setup and usage + +The following basic setup hints are usually valid, no matter the specific controller: + +* Put your controller's buttons into momentary mode, _not_ toggle mode. +* If you are in the lucky situation of owning a controller with endless rotary encoders, by all +means, configure them to transmit relative values, not absolute ones! +** Otherwise, you can't take advantage of ReaLearn's advanced features for sources emitting +relative values, such as the "Step size" or "Speed" setting. +** Also, preventing parameter jumps can never be as effective in absolute mode as in relative mode. +* If there are issues, consult the <> section in the first part of this guide. + +Consider the following general usage hints: + +- If the device supports visual feedback and different LED colors, the LED color often depends on the target value and +can be manually adjusted using "Source Min/Max" in the "Glue" section of the mapping. + +[IMPORTANT] +==== +Make sure to watch out for dedicated controller presets on the Helgoboss ReaPack repository and https://github.com/helgoboss/realearn/tree/master/resources/controller-presets/unofficial[list of unofficial controller presets]! + +Using an existing preset might save you a lot of mapping work (and possibly also layout work, if you want to use the projection feature). +==== + +=== List of tested controllers + +The list of tested controllers is now available as link:https://github.com/helgoboss/realearn/blob/master/doc/controllers.adoc[separate document]. + +== Available presets + +The lists of currently available controller and main presets are available here: + +- link:https://github.com/helgoboss/realearn/tree/master/resources/controller-presets[Controller presets] +- link:https://github.com/helgoboss/realearn/tree/master/resources/main-presets[Main presets] + + + +== Reference + +So far we've covered the basics. Now let's look into everything in detail. + +=== Main panel + +[#header-panel] +==== Header panel + +The header panel provides the following user interface elements, no matter if the _main_ or +_controller_ compartment is shown: + +[#control-input] +===== Input + +By default, ReaLearn captures MIDI events from _<FX input>_, which + consists of all MIDI messages that flow into this ReaLearn VSTi FX instance (= track MIDI path). + Alternatively, ReaLearn can capture events from a MIDI device directly, from an OSC device or from your computer keyboard. Be aware that MIDI will only work if _Enable input + from this device_ is checked for the selected MIDI input device in REAPER's MIDI preferences. + +[#feedback-output] +===== Output + +Here you can choose if and where ReaLearn should send MIDI/OSC feedback. By + default it's set to __ for no feedback. If you want to enable feedback, pick a MIDI or OSC + output device here. Keep in mind that for MIDI, _Enable output to this device_ must be checked in REAPER's + MIDI preferences. As an alternative, you can send feedback to _<FX output>_, which makes + feedback MIDI events stream down to the next FX in the chain or to the track's hardware MIDI output. + +[WARNING] +==== +Please note that sending MIDI feedback + to the FX output has some drawbacks. First, it doesn't participate in ReaLearn's multi-instance feedback + orchestration. That means you might experience LEDs/faders misbehaving when using multiple instances. Second, it + doesn't work if ReaLearn FX is suspended, e.g. in the following cases: + +* ReaLearn FX is disabled. +* Project is paused and ReaLearn track is not armed. +* ReaLearn FX is on input FX chain and track is not armed. +==== + +===== Menu + +This opens ReaLearn's main menu. It's also accessible via right-click on Windows and Linux and control-click +on macOS. It provides the following entries. + +====== Copy listed mappings + +Copies all mappings that are visible in the current mapping list to the clipboard +(respecting group, search field and filters). You can insert them by opening the context menu in the row panel. + +====== Paste mappings (replace all in group) + +Replaces all mappings in the current group with the mappings in the +clipboard. + +====== Auto-name listed mappings + +Clears the names of all listed mappings so ReaLearn's dynamic auto-naming mechanism can kick in. + +====== Name listed mappings after source + +Sets the names of each listed mapping to the first line of its source label. + +====== Make sources of all main mappings virtual + +Attempts to make the sources in the main compartment virtual by matching them with the sources in the controller compartment. This is useful if you already learned a bunch of sources in the main compartment, just to find out later that you would like to have used a controller preset. + +====== Make targets of listed mappings sticky + +Changes the targets of all currently listed mappings to use "sticky" object selectors by attempting to resolve the objects from non-sticky selectors. We call object selectors _sticky_ if they refer to a very particular object (e.g. a track). + +* Sticky selectors: ``, ``, `Particular` +* Non-sticky selectors: ``, ``, ``, ``, `, `At position`, `Named`, `All named` + + +====== Move listed mappings to group + +Lets you move all currently listed mappings to the specified group. Perfect in combination with the textual search! + +====== Advanced + +Provides access to expert features. + +* *Copy listed mappings as Lua:* Like _Copy listed mappings_ but generates Lua code instead. +* *Copy listed mappings as Lua (include default values):* Generates Lua code that contains even those properties that correspond to ReaLearn's defaults. +* *Paste from Lua (replace all in group):* Like _Paste mappings (replace all in group)_ but treats the clipboard content as Lua code. +* *Dry-run Lua script from clipboard*: Executes the Lua code in the clipboard and opens the returned data structure in a text editor. ++ +[NOTE] +==== +The way Lua import works in ReaLearn is: + +. ReaLearn executes the Lua script (from clipboard). +. ReaLearn attempts to interpret the return value as ReaLearn API object. +. ReaLearn loads the API object + +If step 1 fails, ReaLearn displays an error messages that hopefully contains a line number. + +If step 2 fails, ReaLearn shows a validation error message. + +The command _Dry-run Lua script from clipboard_ enables you to just execute step 1 and see the "expanded" result. This can help to make sense of a possible validation error message in step 2. +==== +* *Freeze clip matrix*: Don't use this, this feature is not ready yet! + +[#options] +====== Options + +* *Auto-correct settings:* By default, whenever you change something in ReaLearn, it tries to +figure out if your combination of settings makes sense. If not, it makes an adjustment. +This auto-correction is usually helpful. If for some reason you want to disable auto-correction, this +is your checkbox. +* *Send feedback only if track armed:* If input is set to _<FX input>_, +ReaLearn by default only sends feedback if the track is armed (unarming will naturally disable +control, so disabling feedback is just consequent). However, if input is set to a +MIDI or OSC device, _auto-correct settings_ will take care of unchecking this option in order to allow feedback +even when unarmed (same reasoning). You can override this behavior with this checkbox. At the moment, +it can only be unchecked if ReaLearn is on the normal FX chain. If it's on the input FX chain, unarming +naturally disables feedback because REAPER generally excludes input FX from audio/MIDI processing while a +track is unarmed (*this is subject to change in future!*). +* *Reset feedback when releasing source:* When using ReaLearn the normal way, it's usually desired that feedback is reset when the corresponding sources are not in use anymore (e.g. lights are switch off, displays are cleared, motor faders are pulled down). You can prevent this ReaLearn instance from doing this by disabling this option. This can be useful e.g. when using REAPER/ReaLearn to control a hardware device (essentially using ReaLearn the other way around, "controlling from target to source"). +* *Make instance superior:* If ticked, this instance is allowed to suspend other instances which share the same +input and/or output device (hardware devices only, not FX input or output!). With this you can easily let your +controller control e.g. the currently focused FX but fall back to your usual controls when it's closed. It's intended +to be used primarily on instances that use <>. ++ +TIP: Since ReaLearn 2.14.0, falling back to normal mappings when the FX loses focus in auto-load mode became much easier! One instance is enough. Your normal mappings will be memorized and reloaded once the FX loses focus. See <>. ++ +** By default, ReaLearn instances are not superior, just normal. This is often okay because ReaLearn instances +are friendly fellows and like sharing controllers with each other. +** For example, if 2 instances use the same input or output device and they use different control elements, they +can peacefully coexist. And even if they share a control element for the _control direction_, they are still +fine with it. The same control element will control 2 mappings, why not! +** Things start to get hairy as soon as 2 instances want to send _feedback_ to the same control elements at the +same time. You should avoid this. You should not even do this within one ReaLearn instance. This can't work. +** Sometimes you want one instance to suspend/cover/cancel/mute another one! You can do this by making this +instance _superior_. Then, whenever this instance has at least one active mapping, all non-superior instances +with the same control and/or feedback device will be disabled for control and/or feedback. +** You can have multiple superior instances. Make sure they get along with each other :) +* *Use instance-wide FX-to-preset links only:* By default, instance-specific links are applied _in addition_ to the global links and take precedence over the global ones. This checkbox makes sure that only instance-specific links are used. +* *Stay active when project in background:* Determines if and under which conditions this ReaLearn instance should stay active when the containing project tab is not the active one. Applies to in-project ReaLearn instances only, not to monitoring FX instances! +** *Never:* Will only be active when its project tab is active. +** *Only if background project is running:* Follows REAPER's project tab settings ("Run background projects" and "Run stopped background projects"). +** *Always (more or less):* Attempts to stay active no matter what. Please note that this is technically not always possible when using __ or __ when the background project is not running. + + +====== Server + +ReaLearn features a built-in server which allows the <> (and in future also the Playtime app) to connect to ReaLearn. The server runs globally, not per instance! + +* *Enable and start!:* This starts the server and makes sure it will automatically be started next time you use ReaLearn. +* *Disable and stop!:* This stops the server and makes sure it will not be started next time you use ReaLearn. +* *Add firewall rule:* Attempts to add a firewall rule for making the server accessible from other devices or +displays instructions how to do it. + +====== Open preset folder + +Opens the ReaLearn preset folder in a file manager. + +[#reload-all-presets] +====== Reload all presets from disk + +If you made direct changes to preset files or have downloaded presets via ReaPack, you should press this to reflect these changes in the preset lists of all open ReaLearn instances (reloads all preset files). + +[NOTE] +==== +This *will not* yet apply an adjusted preset, it will just reload the list. If you want to apply a preset that has been changed on disk, you need to select it in the preset dropdown once again! +==== + +[#pot-browser] +====== Open Pot Browser + +This will open Pot Browser, a modern preset browser. It's recommended to use this from a ReaLearn instance on the monitoring FX chain, that way you have the browser accessible from any project. + +TIP: Add a toolbar button which triggers the REAPER action "ReaLearn: Open first Pot Browser" to get quick and convenient access to the browser. + +Remarks: + +- Pot Browser is in an experimental stage, it doesn't save any of your settings! +- Each ReaLearn instance has its own so-called _Pot Unit_. Each Pot Unit has its own filter and preset state. When you open the Pot Browser from an instance, it connects to the Pot Unit of that instance. +- ReaLearn's "Pot" targets such as <> can be used to control the Pot Unit from any controller. + +[#osc-devices] +====== OSC devices + +Allows one to display and modify the list of (globally) configured OSC devices. + +* *:* Opens a window for adding a new OSC devices. +** *Name:* A descriptive name of the device, e.g. "TouchOSC on my Android phone". +** *Local port:* Required for control. The UDP port on which ReaLearn should listen for OSC control messages. +*** *Important:* This port must be reserved exclusively for ReaLearn. If you already use this port +in another application (e.g. in REAPER's own OSC control surface) it won't work and ReaLearn will bless +you with an "unable to connect" message in the "Input" dropdown. +** *Device host:* Required for feedback only. It's the IP address of the OSC device to which ReaLearn +should send feedback messages. This address is usually displayed on your OSC device (e.g. as "Local IP address"). When targeting an OSC software that runs on the same computer as REAPER and +ReaLearn, enter the special IP address `127.0.0.1` ("localhost"). ++ +[TIP] +==== +When you configure your OSC device, you must provide a _host_ as well. There you should enter the IP address of the computer which runs REAPER and ReaLearn. + +You can easily find it by pressing the <> button in ReaLearn and scrolling down a bit. It's the value next to *Host* and should start with `192.168.`. +==== +** *Device port:* Required for feedback only. The UDP port on which the OSC device listens for OSC feedback +messages. +** All OSC device configurations will be saved in the REAPER resource directory +(REAPER → Actions → Show action list… → Show REAPER resource path in explorer/finder) in the JSON file +`Helgoboss/ReaLearn/osc.json`. +* *_Some device_* +** *Edit:* Lets you edit an existing device (see _<New>_). +** *Remove:* Removes the device. This is a global action. As a consequence, all existing ReaLearn instances +which use this device will point to a device that doesn't exist anymore. +** *Enabled for control:* If you disable this, ReaLearn will stop listening to this device. This can save +resources, so you should do this with each device that is not in use (as an alternative for removing it +forever). +** *Enabled for feedback:* If you disable this, ReaLearn won't connect to this device. +** *Can deal with bundles:* By default, ReaLearn aggregates multiple OSC messages into so-called OSC bundles. +Some devices (e.g. from Behringer) can't deal with OSC bundles. Untick the checkbox in this case and ReaLearn +will send single OSC messages. + +[#compartment-parameters] +====== Compartment parameters + +This shows all parameters of the current compartment (you know, the ones that can be used +for conditional activation and __ selector expressions) and makes it possible to customize them. This is practical because it's completely up to you how to put these parameters to use. + +Perfect for preset authors: Parameter settings are saved together with the compartment preset. Parameter values will be reset whenever you load a preset (just the ones in that compartment). + +* *Param _x_ Name:* Changes the name of this parameter. +* *Value count:* By default, ReaLearn parameter values are continuous in nature: They are arbitrary decimal numbers between 0.0 and 1.0. Although that's very flexible, it's often easier to work with a discrete value range. Entering a value count turns the parameter into a discrete parameter with the given number of integer values. For example, a value count of 10 means that the parameter can represent exactly 10 values (0 to 9). + +[CAUTION] +==== +*Choose the value count wisely and think twice before changing it to a different value at a later point in time!* + +Reason: You probably want to refer to values of this parameter in certain parts of ReaLearn, e.g. in <>. If you do that and later change the value count, these value references will not be valid anymore. They will point to other integers than you intended to. So if you are not sure, better pick a large value count and stick to it! +==== + +[#logging] +====== Logging + +* **Log debug info (now):** Logs some information about ReaLearn's internal state. Can be interesting for +investigating bugs or understanding how this plug-in works. +* ** Log real control messages:** When enabled, all incoming MIDI messages, OSC messages or key pressed will be logged to the console. Each log entry contains the following information: +** Timestamp in seconds +** ReaLearn instance ID (a randomly assigned ID that uniquely identifies a particular instance, will change after +restart) +** Message purpose +*** *Real control:* A message used for controlling targets. +*** *Real learn:* A message used for learning a source. +** Actual message (MIDI messages will be shown as hexadecimal byte sequence, short MIDI messages also as +decimal byte sequence and decoded) +** Match result +*** *unmatched:* The message didn't match any mappings. +*** *matched:* The message matched at least one of the mappings. +*** *consumed:* Only for short MIDI messages. This short message is part of a (N)RPN or 14-bit CC message and +there's at least one active mapping that has a (N)RPN or 14-bit CC source. That means it will not be +processed. The complete (N)RPN or 14-bit CC message will be. +* **Log virtual control messages:** When enabled, all triggered virtual control elements and their values will be logged (see <>). +* **Log target control:** When enabled, all target invocations (parameter changes etc.) will be logged. +* **Log virtual feedback messages:** When enabled, all feedback events to virtual control elements will be logged (see <>). +* **Log real feedback messages:** When enabled, all outgoing MIDI or OSC messages will be logged to the console. The log entries look similar to the ones described above, with the following notable differences. +** Message purpose +*** *Feedback output:* A message sent to your controller as response to target value changes. +*** *Lifecycle output:* A message sent to your controller as response to mapping activation/deactivation +(see <>). +*** *Target output:* A message sent because of either the <> or +<> target. + +====== Send feedback now + +Usually ReaLearn sends feedback whenever something changed to keep the LEDs +or motorized faders of your controller in sync with REAPER at all times. There might be situations +where it doesn't work though. In this case you can send feedback manually using this button. + +===== Export to clipboard + +Pressing the export button allows you to copy ReaLearn's settings to the clipboard so you can import them in another instance or edit them in a text editor. + +* *Export session as JSON:* Copies a _complete_ dump of ReaLearn's current settings (including all mappings, even controller mappings) to the clipboard. The dump's data format is + https://www.json.org/json-en.html[JSON], a wide-spread data exchange format. It's a text format, + so if you are familiar with the search & replace feature of your favorite text editor, this is one way to do batch editing. However, recent versions of ReaLearn provide a much better way of doing that: _ReaLearn Script_. Read about the other export options for learning more about it. ++ +[TIP] +==== +You can also use the export for some very basic A/B testing: + +1. Choose _Export session as JSON_ +2. Change some settings and test them +3. Restore the old settings by pressing _Import from clipboard_. +==== ++ +[TIP] +==== +For the programmers and script junkies out there: It's perfectly possible to program ReaLearn from outside by passing it a snippet of JSON via https://www.reaper.fm/sdk/reascript/reascripthelp.html#TrackFX_SetNamedConfigParm[`TrackFX_SetNamedConfigParm()`]. Parameter name is `set-state`. This mechanism is implemented on ReaLearn side using https://www.reaper.fm/sdk/vst/vst_ext.php[REAPER's named parameter mechanism] (search for `named_parameter_name`). + +Example that assumes that the first FX of the first track is a ReaLearn instance: + +[source,lua] +---- +local track = reaper.GetTrack(0, 0) +local state = [[ +{ + "controlDeviceId": "62", + "feedbackDeviceId": "fx-output", + "mappings": [ + { + "name": "1", + "source": { + "type": 1, + "channel": 0, + "number": 64 + }, + "mode": {}, + "target": { + "type": 2 + } + } + ] +} +]] +reaper.TrackFX_SetNamedConfigParm(track, 0, "set-state", state) +---- +==== +* *Export main/controller compartment as JSON:* Copies a dump of the currently visible compartment to the clipboard. It contains about the same data that a compartment preset would contain. +* *Export main/controller compartment as Lua:* Copies a dump of the currently visible compartment to the clipboard as Lua code (ReaLearn Script). This form of Lua export skips properties that correspond to ReaLearn's default values, resulting in a minimal result. Perfect for pasting in a forum or programming ReaLearn with focus on only those properties that matter to you. +* *Export main/controller compartment as Lua (include default values):* This Lua export includes even those properties that correspond to ReaLearn's default values, resulting in more text. This gives you the perfect starting point if you want to extensively modify the current compartment (using the Lua programming language) or build a compartment from scratch, using even properties that you haven't touched yet in the user interface! + +===== Import from clipboard + +Pressing the import button does the opposite: It restores whatever ReaLearn dump is currently in the clipboard. + +====== "?" (Help) + +Provides helpful links to the user guide and other stuff. + + +[#projection] +===== Projection + +This is a quite unique feature that allows you to project a schematic representation + of your currently active controller to a mobile device (e.g. a tablet computer). You can put this device close + to your controller in order to see immediately which control element is mapped to which parameter. + This is an attempt to solve an inherent problem with generic controllers: That it's easy to forget which control + element is mapped to which target parameter. If you want to use this feature, just click this button + and you will see detailed instructions on how to set this up. In order to use this feature, you need the + _ReaLearn Companion_ app, which has a <> in this user guide. + +===== Let through + +ReaLearn by default "eats" incoming MIDI events for which there's at least one active mapping with that source. In other words, it doesn't forward MIDI events which are used to control a target parameter. However, unmatched MIDI events are forwarded! + +You can change this using these checkboxes. E.g. you can tick *Matched events* if you want to forward even matched events. The exact behavior differs depending on what you choose as <>: + +* If input is set to +** MIDI events arrive from ReaLearn's FX input. If they get forwarded, they get forwarded to the FX output, usually to the plug-in which is located right below ReaLearn FX. The default setting often makes much sense here, especially if you put ReaLearn right above another instrument plug-in. +* If input is set to a MIDI hardware device +** MIDI events arrive directly from the MIDI hardware device. If they get forwarded, they get forwarded to REAPER's tracks as they would usually do without ReaLearn. If they don't get forwarded, it means they get filtered and will never make it to the tracks. ReaLearn completely eats them, globally! That means, ReaLearn can act as global MIDI filter. +** Please note, with input set to a real MIDI device, MIDI events coming from _FX input_ are _always_ forwarded to the FX output. +** Also, MIDI events captured from a real MIDI device input are *never* forwarded to ReaLearn's FX output. ++ +TIP: This global MIDI filter feature is only available in REAPER v6.36+. +* If input is set to a OSC device +** You won't see the checkboxes because they don't make sense for OSC. +* If input is set to computer keyboard +** You can control whether key presses are forwarded to REAPER or not. +** For example, unticking both checkboxes makes sure that only keyboard hotkeys defined in ReaLearn have an effect. This can be interesting for live scenarios in which you temporarily want to lower the risk of pressing the wrong key and messing up the performance. Just unlock the keys you absolutely need. + +===== Show + +This lets you choose which mapping compartment is displayed. A compartment is basically a list of mappings + that can be saved as independent preset. Initially, ReaLearn shows the so-called "Main compartment" which contains + the so-called "Main mappings" - the bread and butter of ReaLearn. However, there's another interesting compartment, + the "Controller compartment". In a nutshell, this compartment lets you define which hardware controllers you have at + your disposal and which control elements they have. Learn more about that feature in section + <>. + +===== Controller preset / Main preset + +This is the list of available presets for that compartment. By default, it's set + to "<None>", which means that no particular preset is active. If you select a preset in this list, its + corresponding mappings will be loaded and immediately get active. In the _controller_ compartment, this list + will essentially represent the list of available hardware controller presets. A few are shipped with ReaLearn itself + (separately downloadable via ReaPack) but you can also define your own ones and add them to this list! + +* *Save:* If you made changes to a preset, you can save them by pressing this button. This works for built-in presets + as well but I would strongly recommend against changing them directly. Better use _Save as…_ and choose a custom + name. + +===== Save as… + +This allows you to save all currently visible mappings as a new preset. Please choose a descriptive + name. + +** Saving your mappings as a preset is optional. All controller mappings are saved together + with your current ReaLearn instance anyway, no worries. But as soon as you want to reuse these + mappings in other ReaLearn instances, it makes of course sense to save them as a preset! +** All of your presets end up in the REAPER resource directory + (REAPER → Actions → Show action list… → Show REAPER resource path in explorer/finder) at + `Data/helgoboss/realearn/presets`. They are JSON files and very similar to what you get when you press + _Export to clipboard_. +** Those files are usually in the root of that `presets` directory but can also reside in sub directories (one level only, sub/sub directories are not supported). Please note that the sub directory name becomes a part of the preset ID, so better don't move existing presets around if you want preset references of existing ReaLearn instances to stay intact. +** JSON files can also contain custom data sections. For example, the ReaLearn + Companion app adds a custom data section to controller presets in order to memorize the positions and shapes of all control elements. +** When pressing this button, ReaLearn might detect that your current mappings are referring to specific tracks and + FX instances _within the current project_. This would somehow defeat the purpose of presets because what good + are presets that are usable only within one project? That's why ReaLearn also offers you to automatically + convert such mappings to project-independent mappings by applying the following transformations: +*** FX targets are changed to refer to _current instance FX_* instead of a particular one. Their track is set to + *<This>* because it doesn't matter anyway. +*** Track targets are changed to refer to a track via its position instead of its ID. +** If this is not what you want, you can choose to say no and make modifications yourself. + +===== Delete + +This permanently deletes the currently chosen preset. You can also delete built-in presets. + However, if you use ReaPack for installation, it should restore them on next sync. + +===== Notes + +Allows you to save custom notes/comments for the current compartment. These notes are also included in compartment presets. + +===== Add one + +Adds a new mapping at the end of the current mapping list. + +===== Learn many + +Allows you to add and learn many new mappings in a convenient batch mode. Click this button and follow + the on-screen instructions. Click _Stop_ when you are finished with your bulk learning strike. +[#search] + +===== Search + +Enter some text here in order to display just mappings whose name matches the text. The search expression + also supports wildcards `*` and `?` for doing blurry searches. `*` stands for zero or more arbitrary characters and `?` stands for one arbitrary character. + +===== Filter source + +If you work with many mappings and you have problems memorizing them, you + will love this feature. When you press this button, ReaLearn will start listening to incoming MIDI/OSC + events and temporarily disable all target control. You can play around freely on your controller + without having to worry about messing up target parameters. Whenever ReaLearn detects a valid + source, it will filter the mapping list by showing only mappings which have that source. This is a + great way to find out what a specific knob/fader/button etc. is mapped to. Please note that the + list can end up empty (if no mapping has that source). As soon as you press _Stop_, the current + filter setting will get locked. This in turn is useful for temporarily focusing on mappings with a + particular source. When you are done and you want to see all mappings again, press the *X* + button to the right. _Tip:_ Before you freak out thinking that ReaLearn doesn't work anymore + because it won't let you control targets, have a quick look at this button. ReaLearn might still + be in "filter source" mode. Then just calm down and press _Stop_. It's easy to forget. + +===== Filter target + +If you want to find out what mappings exist for a particular target, + press this button and touch something in REAPER. As soon as you have touched a valid target, the + list will show all mappings with that target. Unlike _Filter source_, ReaLearn will + automatically stop learning as soon as a target was touched. Press the *X* button to remove the + filter and show all mappings again. + +[#bottom-panel] +==== Bottom panel + +At the bottom you can see: + +- The current scroll position. +- The session ID of this this ReaLearn instance. +- Tags assigned to this ReaLearn instance. +- Information about the current instance track and instance FX. +- Information whether control and/or feedback is currently inactive instance-wide. +- Information about what version of ReaLearn you have. + +===== Instance data... + +Press this button to change various key-value data of this ReaLearn instance as a whole. + +* *Session ID…:* This lets you customize the ID used to address this particular ReaLearn +instance when using the <> feature. +** By default, the session ID is a random cryptic string +which ensures that every instance is uniquely addressable. The result is that scanning the QR code +of this ReaLearn instance will let your mobile device connect for sure with this unique +instance, not with another one - remember, you can use many instances of ReaLearn in parallel. This +is usually what you want. +** But a side effect is that with every new ReaLearn instance that you create, +you first have to point your mobile device to it in order to see its +<> (by scanning the QR code). Let's assume you have in many of your projects exactly one ReaLearn instance +that lets your favorite MIDI controller control track volumes. By customizing the session ID, you basically can tell +your mobile device that it should always show the <> of this very ReaLearn instance - +no matter in which REAPER project you are and even if they control the volumes of totally +different tracks. +** You can achieve this by setting the session ID of each volume-controlling ReaLearn instance +to exactly the same value, in each project. Ideally it's a descriptive name without spaces, such as "track-volumes". +You have to do the pairing only once et voilà, you have a dedicated device for monitoring your volume control +ReaLearn instances in each project. ++ +[CAUTION] +==== +Make sure to not have more than one ReaLearn instance with the same session +ID active at the same time because then it's not clear to which your mobile device will connect! +==== +** *At the moment, the session ID is part of the ReaLearn preset!* That means, opening a preset, copying/cutting +a ReaLearn FX, importing from clipboard - all of that will overwrite the session ID. This might change in +future in favor of a more nuanced approach! +* *Tags:* Lets you assign tags to this instance (a comma-separated list). They are important if you want to dynamically enable or disable instances using the <> target. + +[#instance-track] +===== Instance track + +The second line of the bottom panel shows the current track chosen as **Instance track** for this instance of ReaLearn. This can be something like "Track 3" or "The currently selected track". Mappings in this ReaLearn instance can refer to this track by choosing the track selector <>. + +The instance track can be changed via target <>. + +[#instance-fx] +===== Instance FX + +The second line of the bottom panel also shows the current FX chosen as **Instance FX** for this instance of ReaLearn. This can be something like "FX 5 on track 3" or "The currently focused track". Mappings in this ReaLearn instance can refer to this FX by choosing the FX selector <>. + +The instance FX can be changed via target <>. + +==== Common compartment settings + +The header panel shows the following user interface elements, no matter if you are in the controller or main +compartment: + +* *Mapping group:* Mapping groups are part of the currently shown compartment and enable you to divide the list of +mappings into multiple groups. +** Groups can be useful … +*** To apply an activation condition to multiple mappings at once. +*** To enable/disable control/feedback for multiple mappings at once. +*** To keep track of mappings if there are many of them. +** This dropdown contains the following options: +*** *<All>:* Displays all mappings in the compartment, no matter to which group they belong. In this view, + you will see the name of the group on the right side of a mapping row. +*** *<Default>:* Displays mappings that belong to the _default_ group. This is where mappings + end up if you don't care about grouping. This is a special group that can't be removed. +*** *_Custom group_:* Displays all mappings in your custom group. +** You can move existing mappings between groups by opening the context menu (accessible via right-click on Windows + and Linux, control-click on macOS) of the corresponding mapping row and choosing "Move to group". +** Groups are saved as part of the project, VST plug-in preset and compartment preset. +* *Add:* Allows you to add a group and give it a specific name. +* *Remove:* Removes the currently displayed group. It will ask you if you want to remove all the mappings in that + group as well. Alternatively they will automatically be moved to the default group. +* *Edit:* Opens the group panel. This allows you to change the group name and change things that affect all mappings in this groups: Assigning tags, enabling/disabling control and/or feedback, setting an activation condition. The activation condition that you provide here is combined with the one that you provide in the mapping. Only if both, the group activation conditions and + the mapping activation condition are satisfied, the corresponding mapping will be active. Read more about + <> below in the section about the <>. + +image:images/screenshot-group-panel.png[Group panel] + +Since ReaLearn 2.10.0, mappings are processed from top to button, exactly in the order in which they are defined +within the corresponding compartment. This matters if you want to map multiple targets to one button and +the order of execution matters. + +*Important:* There's an exception. ReaLearn's processing of its own VST parameters is always deferred. + +- That means changing a ReaLearn parameter in one mapping and relying on it in the next + one (in terms of conditional activation or in a `<Dynamic>` expression), will not work! +- You can work around that by delaying execution of the next mapping via <> but + that's a dirty hack. ReaLearn's parameters are not supposed to be used that way! +- Imagine a railway: ReaLearn's targets can be considered as trains. Triggering a target means moving the train forward. + ReaLearn's parameters can be considered as railway switches. Changing a parameter means setting a course. + The course needs to be set in advance, at least one step before! Not at the same time as moving the train over the + switch. + +[#controller-compartment] +==== Controller compartment + +By default, ReaLearn shows the list of main mappings. If you switch to the _controller_ compartment, you will see the +list of controller mappings instead. Each controller mapping represents a control +element on your hardware controller, e.g. a button or fader. This view lets you describe your controller by - well - +by adding mappings. Almost everything in ReaLearn is a mapping :) + +Defining your own controllers can have a bunch of benefits: + +* You can use the awesome <> feature + to project your controller mapping to a smartphone or tablet (link:https://www.youtube.com/watch?v=omuYBznEShk&feature=youtu.be[watch video]). +* You can use controller presets made by other users and thereby save precious setup time. Or you can contribute them + yourself! +* You can make your main mappings independent of the actual controller that you use. This is done using so-called + _virtual_ sources and targets. +* It allows you to give your knobs, buttons etc. descriptive and friendly names instead of just e.g. "CC 15". +* You don't need to learn your control elements again and again. Although the process of learning an element is easy + in ReaLearn, it can take some time in case the source character is not guessed correctly + (e.g. absolute range element or relative encoder). Just do it once and be done with it! + +If you want to make ReaLearn "learn" about your nice controller device, all you need to do is to create a suitable +controller mapping for each of its control elements. + +Let's first look at the "slow" way to do this - adding and editing each controller mapping one by one: + +. Press the "Add one" button. +. Learn the source by pressing the "Learn source" button and touching the control element. +. Press the "Edit" button. +. Enter a descriptive name for the control element. +** _Hint:_ This name will appear in many places so you want it to be short, clear and unique! +. Assign a unique virtual target. +** At this point we don't want to assign a _concrete_ target yet. The point of controller presets is + to make them as reusable as possible, that's why we choose a so-called _virtual_ target. +** In the _Category_ dropdown, choose _Virtual_. +** As _Type_, choose _Button_ if your control element is a sort of button (something which you can press) + and _Multi_ in all other cases. +** Use for each control element a unique combination of _Type_ and _ID_, starting with number _1_ and counting. +*** Example: It's okay and desired to have one control element mapped to "Multi 1" and one to "Button 1". +** Just imagine the "8 generic knobs + 8 generic buttons" layout which is typical for lots of popular controllers. + You can easily model that by assigning 8 multis and 8 buttons. +** Maybe you have realized that the _Glue_ section is available for controller mappings as well! That opens up all + kinds of possibilities. You could for example restrict the target range for a certain control element. Or make + an encoder generally slower or faster. Or you could simulate a rotary encoder by making two buttons on your + controller act as -/+ buttons emitting relative values. This is possible by mapping them to the same "Multi" in + "Incremental button" mode. + +Before you go ahead and do that for each control element, you might want to check out what this is good for: Navigate +back to the _main_ compartment, learn the source of some main mapping and touch the control element that you +have just mapped: Take note how ReaLearn will assign a _virtual_ source this time, not a MIDI source! It will also +display the name of the control element as source label. Now, let's say at some point you swap your controller device +with another one that has a similar layout, all you need to do is switch the controller preset and you are golden! You +have decoupled your main mappings from the actual controller. Plus, as soon as you have saved your controller mappings +as a preset, you can take full advantage of the <> feature. + +All of this might be a bit of an effort but it's well worth it! Plus, there's a way to do this _a lot_ faster by +using _batch learning_: + +. Press the "Learn many" button. +. Choose whether you want to learn all the "Multis" on your controller or all the "Buttons". +. Simply touch all control elements in the desired order. +** ReaLearn will take care of automatically incrementing the virtual control element numbers. +. Press "Stop". +. Done! +** At this point it's recommended to recheck the learned mappings. +** ReaLearn's source character detection for MIDI CCs is naturally just a guess, so it can be wrong. If so, + just adjust the character in the corresponding mapping panel. + +You can share your preset with other users by sending them to link:mailto:info@helgoboss.org[info@helgoboss.org]. I will add it to https://github.com/helgoboss/realearn/tree/master/resources/controller-presets[this +list]. + +==== Main compartment + +The header panel for main mappings consists of a few more user interface elements: + +[#auto-load] +===== Auto-load + +If you switch this to _Based on instance FX_, ReaLearn will start to observe the <> of this ReaLearn instance. By default, the instance FX is set to ``, which means, it will reflect whatever FX is currently focused. Whenever the instance FX changes, it will check if you have linked a compartment preset + to it and will automatically load it. Whenever the instance FX switches to an unlinked FX or the FX loses focus, ReaLearn falls back to the mapping list defined before activating auto-load. Of course this makes sense only if you actually have linked some + presets. Read on! +The header context menu (accessible via right-click on Windows and Linux, control-click on macOS) for the main +compartment contains the missing piece of the puzzle: + +[#global-fx-to-preset-links] +====== Global FX-to-preset links + +Manage a global list of links from plug-ins to ReaLearn main compartment presets. + +* *Add link from last focused FX to preset:* This lets you link whatever FX window was focused before focusing + ReaLearn, to an arbitrary main compartment preset. Needless to say, this only works if an FX has been focused + before. +** All links will be saved _globally_, not just within this project! +** Location: REAPER resource directory (REAPER → Actions → Show action list… → Show REAPER resource path in + explorer/finder) at `Data/helgoboss/realearn/auto-load-configs/fx.json`. +* *_Arbitrary FX ID:_* If you have added a link already, you will see them here in a list. What you see, is the + so-called _FX ID_, which by default simply corresponds to the plug-in's original name (e.g. `VSTi: ReaSynth (Cockos)`). +** *<Edit FX ID…>:* With this, you can edit the FX ID manually. +*** _All fields_: All the fields below support wildcards. E.g. instead on relying on the original plug-in name you could match plug-ins with similar file names (e.g. VST2 +and VST3 at once): You can use `\*` for matching zero or arbitrary many characters and `?` for matching +exactly one arbitrary character. E.g. `Pianoteq 7 STAGE.*` would match both `Pianoteq 7 STAGE.dll` (VST2) +and `Pianoteq 7 STAGE.vst3` (VST3). +*** *FX name:* Allows you to adjust the (original) plug-in name that triggers the preset change. +*** *FX file name:* Allows you to adjust the plug-in file name that triggers the preset change. +*** *FX preset name:* Maybe the FX name or file name is not enough for you to decide which preset you want to load. + Good news: You can add a preset name as additional criteria! E.g. if you have use a sampler, you can load + different ReaLearn presets depending on which sample library is loaded into your sampler. Just add two + links with the same FX file name (e.g. `Kontakt 5.dll`) but different preset names. You can also use + wildcards here! +** *<Remove link>:* (Globally) this FX-to-preset link. +** *_Arbitrary main preset:_* The checkbox tells you to which main preset the FX ID is linked. You can change + the linked preset by clicking another one. + +====== Instance-wide FX-to-preset links + +This is like <> but saves the links as part of this ReaLearn instance. This is useful if you have 2 controllers (= and therefore 2 ReaLearn instances) and want them to auto-load different presets although the instance FX points to the same plug-in. + + +==== Mapping row + +The mapping, source and target labels of a mapping row should be greyed out whenever the mapping is _off_. A mapping is considered as _on_ only if the following is true: + +. The mapping is complete, that is, both source and target are completely specified. +. The mapping is enabled as a whole. +. The mapping has control and/or feedback enabled. +. The mapping is active (see _conditional activation_). + +If a mapping is _off_, it doesn't have any effect. + +* *✓:* This checkbox at the top left of the mapping row enables or disables the mapping as a whole. +* *●:* This indicator at the very left of the mapping row lights on incoming control messages whenever they match the mapping source. Attention: This doesn't necessarily mean that the message will reach the target (although it often does). There are certain settings in the <> section which allow you to filter messages even they matched the source (e.g. the _Source Min/Max_). +* *Up / Down:* Use these buttons to move this mapping up or down the list. +* *→ / ←:* Use these checkboxes to enable/disable control and/or feedback for this mapping. Disabling both has the same effect as disabling the mapping as a whole. +* *Edit:* Opens the mapping panel for this mapping. +* *Duplicate:* Creates a new mapping just like this one right below. +* *Remove:* Removes this mapping from the list. +* *Learn source:* Starts or stops learning the source of this mapping. +* *Learn target:* Starts or stops learning the target of this mapping. +** _Tip:_ Learning a target that is currently being automated is not possible at the moment because + ReaLearn can't know if the value change notification is coming from the automation or your touch + interaction. + +Each mapping row provides a context menu (accessible via right-click on Windows and Linux, control-click on macOS), +which allows you access to the following functionality: + +* *Copy:* Copies this mapping to the clipboard. +* *Paste (replace):* Replaces this mapping with the mapping in the clipboard. If the clipboard contains just + a part of a mapping (source, mode or target), then just this part gets replaced. +* *Paste (insert below):* Creates a new mapping that's like the mapping in the clipboard and places it below + this mapping. +* *Copy part:* Copies just a part of the mapping (activation condition, source, mode or target). +* *Move to group:* Lets you move this mapping to another mapping group. +* *Advanced:* Provides access to expert features. +** *Copy as Lua:* Copies this mapping as Lua code. This is an indispensable tool if you want to build your mappings in Lua because it gives you a readily executable code snippet that you can adjust as desired. +** *Copy as Lua (include default values):* Includes even default values. +** *Paste from Lua (replaces):* Like _Paste (replace)_ but treats the clipboard content as Lua code. +** *Paste from Lua (insert below):* Like _Paste (insert below)_ but treats the clipboard content as Lua code. +** *Log debug info (now):* Logs debug information about this particular mapping. + +[#mapping-panel] +=== Mapping panel + +At this point it's important to understand some basics about how ReaLearn processes incoming control +events. When there's an incoming control event that matches a particular source, one of the first +things ReaLearn does is to normalize it to a so-called _control value_. + +A control value can be either absolute or relative, depending on the source character: + +* *Source emits absolute values (e.g. faders)*: The control value will be absolute, which means + it's a 64-bit decimal number between 0.0 and 1.0. You can also think of it in terms of + percentages: Something between 0% and 100%. 0% means the minimum possible value of the source has + been emitted whereas 100% means the maximum. +* *Source emits relative values (e.g. rotary encoders)*: The control value will be relative, which + means it's a positive or negative integer that reflects the amount of the increment or decrement. + E.g. -2 means a decrement of 2. + +After having translated the incoming event to a control value, ReaLearn feeds it to the mapping's +glue section. The glue section is responsible for transforming control values before they reach the _target_. +This transformation can change the type of the control value, e.g. from relative to absolute - it depends +on the mapping's target character. The glue section can even "eat" control values so that they don't arrive +at the target at all. + +Finally, ReaLearn converts the transformed control value into some target instruction (e.g. "set +volume to -6.0 dB") and executes it. + +Feedback (from REAPER to controller) works in a similar fashion but is restricted to absolute +control values. Even if the source is relative (e.g. an encoder), ReaLearn will always emit absolute +feedback, because relative feedback doesn't make sense. + +[#mapping] +==== General mapping properties + +This section provides the following mapping-related settings and functions: + +* *Name:* Here you can enter a descriptive name for the mapping. This is especially useful in + combination with the search function if there are many mappings to keep track of. If you clear + the name, ReaLearn will name the mapping automatically based on its target. +* *Tags:* Use this to assign arbitrary tags to this mapping (comma-separated). These tags can be used to organize mappings in a way that is much more flexible than groups. +** Mapping tags are also displayed in mapping rows, including the ones inherited by groups. +** In the header panel <>, you can search for mappings that have a certain tag by entering the tag name prefixed with the hash character `#`. For example, you can search for all mappings tagged with the tag `mixing` by entering `#mixing`. +** Tags are not just something for people that love to keep things tidy! They also get meaning in combination with certain ReaLearn targets such as <>. +* *Control enabled / Feedback enabled:* Use these checkboxes to enable/disable control and/or + feedback for this mapping. +* *Active:* This dropdown controls so-called conditional activation of mappings. See the + <> section below. +* *Feedback:* +** *Normal:* Makes ReaLearn send feedback whenever the target value changes. This is the recommended + option in most cases. +** *Prevent echo feedback:* This option mainly exists for motorized faders that don't like + getting feedback while being moved. If checked, ReaLearn won't send feedback if the target value + change was caused by incoming source events of this mapping. However, it will still send feedback + if the target value change was caused by something else, e.g. a mouse action within REAPER itself. +** *Send feedback after control:* This checkbox mainly exists for "fixing" controllers which allow + their LEDs to be controlled via incoming MIDI/OSC _but at the same time_ insist on controlling these + LEDs themselves. For example, some Behringer X-Touch Compact buttons exhibit this behavior in MIDI mode. + This can lead to wrong LED states which don't reflect the actual state in REAPER. + If this option is not selected (the normal case and recommended for most controllers), ReaLearn + will send feedback to the controller _only_ if the target value has changed. For example, if you + use a button to toggle a target value on and off, the target value will change only when pressing + the button, not when releasing it. As a consequence, feedback will be sent only when pressing the + button, not when releasing it. However, if this option is selected, ReaLearn will send feedback + even after releasing the button - although the target value has not been changed by it. Another + case where this option comes in handy is if you use a target which doesn't support proper feedback + because REAPER doesn't notify ReaLearn about value changes (e.g. "Track FX all enable") and you have + "Poll for feedback" disabled. By choosing this option, ReaLearn will send feedback whenever the target value + change was caused by ReaLearn itself, which improves the situation at least a bit. +* *Show in projection:* When unticked, this mapping will not show up in the <>. + Useful e.g. for feedback-only mappings or buttons with multiple assignments. +* *Advanced settings:* This button is for experts. There are some advanced mapping-related settings in + ReaLearn that are not adjustable via its graphical user interface but only by writing text-based configuration. + Pressing this button should open a small window in which you can write the configuration for this mapping. + If the button label ends with a number, that number denotes the + number of top-level configuration properties set for that mapping. That way you can immediately see if a mapping + has advanced settings or not. You can learn more about the available properties in the section + <>. +** *Open in text editor (Windows and Linux only):* Opens the settings in the system text editor or whatever program is associated with + YAML files. It depends on your system setup if this works or not. If it does and if your text editor is good, + this can make editing larger YAML snippets more convenient (e.g. by providing syntax highlighting). As soon + as you save the file and close the editor, the text will automatically appear in the "Advanced settings" + text area. +** *Help:* Will open an online version of the user guide section that describes the available configuration + properties. +* *Find in mapping list:* Scrolls the mapping rows panel so that the corresponding mapping row for + this mapping gets visible. +* *Beep on success (checkbox on the bottom-left):* Makes the mapping play a sound whenever the target has been invoked successfully. Nice for trigger-like targets such as <> for which there's no other good way to know if it worked. +* *Previous/next buttons:* Allows you to jump to the previous or next mapping. Considers only mappings that are currently visible in the mapping rows panel. +* *Enabled (checkbox on the bottom-right):* Enables or disables the mapping as a whole. + +[#conditional-activation] +==== Conditional activation + +Conditional activation allows you to dynamically enable or disable this mapping based on the state of +ReaLearn's own plug-in parameters and since recently even on the state of arbitrary targets. This is a powerful feature. It is especially practical if your +controller has a limited amount of control elements and you want to give control elements several +responsibilities. It let's you easily implement use cases such as: + +* "This knob should control the track pan, but only when my sustain pedal is pressed, otherwise it + should control track volume!" (modifier use cases) +* "I want to have two buttons for switching between different banks where each bank represents + a group of mappings." (bank use cases) +* "I want to control the volume of this track only if it's not muted." (target-state based use cases) + +TIP: Since ReaLearn 2.11.0, <> provides a slightly less powerful but more straightforward way to implement use cases that were before only achievable with parameter-based conditional activation. + +There are 6 different activation modes: + +* *Always:* Mapping is always active (the default) +* *When modifiers on/off:* Mapping becomes active only if something is pressed / not pressed +* *When bank selected:* Allows you to step through different groups of mappings (sometimes also called "pages") +* *When EEL met* Let an EEL formula decide (total freedom) +* *When expression met:* Let an expression decide (total freedom) +* *When target value met:* Let the current value of the target of another mapping decide + +[NOTE] +==== +At this occasion, some words about ReaLearn's own freely assignable FX parameters. + +ReaLearn itself isn't just able to +control parameters of other FX, it also offers FX parameters itself. At the moment it offers 200 FX parameters, 100 for the main compartment and 100 for the controller compartment. You can control them just like parameters in other FX: + +- Via automation envelopes, +- via track controls, +- via REAPER's own MIDI/OSC learn +- … and of course via ReaLearn itself. + +Initially, they don't do anything at all. First, you need to give meaning to them by referring to them in activation conditions or `<Dynamic>` selector expressions. +==== + +[discrete] +===== When modifiers on/off + +This mode is comparable to modifier keys on a computer keyboard. For example, when you press `Ctrl+V` +for pasting text, `Ctrl` is a modifier because it modifies the meaning of the `V` key. When this +modifier is "on" (= pressed), it activates the "paste text" and deactivates the "write the letter V" +functionality of the `V` key. + +In ReaLearn, the modifier is one of the FX parameters. It's considered to be "on" if the parameter +has a value greater than 0 and "off" if the value is 0. + +You can choose up to 2 modifier parameters, "Modifier A" and "Modifier B". If you select "<None>", +the modifier gets disabled (it won't have any effect on activation). The checkbox to the right of +the dropdown lets you decide if the modifier must be "on" for the mapping to become active or "off". + +Example: The following setting means that this mapping becomes active _only_ if both "Parameter 1" +and "Parameter 2" are "on". + +* *Modifier A:* "Parameter 1" +* *Checkbox A:* Checked +* *Modifier B:* "Parameter 2" +* *Checkbox B:* Checked + +Now you just have to map 2 controller buttons to "Parameter 1" and "Parameter 2" via ReaLearn (by +creating 2 additional mappings - in the same ReaLearn instance or another one, up to you) et voilà, +it works. The beauty of this solution lies in how you can compose different ReaLearn features to +obtain exactly the result you want. For example, the _absolute mode_ of the mapping that controls the modifier +parameter decides if the modifier button is momentary (has to be pressed all the time) +or toggled (switches between on and off everytime you press it). You can also be more adventurous +and let the modifier on/off state change over time, using REAPER's automation envelopes. + +[discrete] +===== When bank selected + +This is the correct activation mode if you want control surface "bank-style" mapping. An in-depth tutorial how +to implement this can be found in the <> section, tutorial number 1. + +TIP: For this kind of use cases you should consider the new <> target, which is available since ReaLearn 2.11.0 as an alternative. It's slightly less powerful than conditional activation but probably easier to use, partly because you can dictate which mappings should be active "from outside", not from the perspective of the mapping itself. + +You can tell ReaLearn to only activate your mapping if a certain parameter has a particular value. +The particular value is called "Bank". Why? Let's +assume you mapped 2 buttons "Previous" and "Next" to increase/decrease the value of the parameter +(by using "Incremental button" mode, you will learn how to do that further below). And you have multiple +mappings where each one uses "When bank selected" with the same parameter but a different "Bank". +Then the result is that you can press "Previous" and "Next" and it will switch between different +mappings within that parameter. If you assign the same "Bank" to multiple mappings, it's like putting +those mappings into one group which can be activated/deactivated as a whole. + +Switching between different programs via "Previous" and "Next" buttons is just one possibility. +Here are some other ones: + +* *Browse banks using a rotary encoder:* Just map the rotary encoder + to the "Bank" parameter and restrict the target range as desired. +* *Activate each bank with a separate button:* Map each button to the "Bank" + parameter (with absolute mode "Normal") and set "Target Min/Max" to a distinct value. E.g. set button + 1 min/max both to 0% and button 2 min/max both to 1%. Then pressing button 1 + will activate bank 0 and pressing button 2 will activate bank 1. + +In previous versions of ReaLearn you could use other methods to achieve a similar behavior, but it always +involved using multiple ReaLearn instances: + +* *By enabling/disabling other ReaLearn instances:* You can use one main ReaLearn instance containing + a bunch of mappings with <> target in order to enable/disable other ReaLearn FX + instances. Then each of the other ReaLearn instances acts as one mapping bank/group. +* *By switching between presets of another ReaLearn instance:* You can use one main ReaLearn instance containing a + mapping with <> target in order to browse presets of + another ReaLearn FX instance. Then each preset in the other ReaLearn instance acts as one mapping bank/group. However, + that method is pretty limited and hard to maintain because presets are something global + (not saved together with your REAPER project). + +With _Conditional activation_ you can do the same (and more) within just one ReaLearn instance. + +TIP: If you want to adjust the number of banks and improve bank handling in general, set a discrete value count for the corresponding bank parameter (see <>). + +[discrete] +===== When EEL met + +This is for experts. It allows you to write a formula in https://www.cockos.com/EEL2/[EEL2] language +that determines if the mapping becomes active or not, based on potentially all parameter values. +This is the most flexible of all parameter-based activation modes. The other modes can be easily simulated. The example +modifier condition scenario mentioned above written as formula would be: + +---- +y = p1 > 0 && p2 > 0 +---- + +`y` represents the result. If `y` is greater than zero, the mapping will become active, otherwise +it will become inactive. `p1` to `p100` contain the current parameter values. Each of them has a +value between 0.0 (= 0%) and 1.0 (= 100%). + +This activation mode accounts for ReaLearn's philosophy to allow for great flexibility instead of just implementing +one particular use case. If you feel limited by the other activation modes, just use EEL. + +TIP: For most activation conditions which need this amount of freedom, the newer activation mode <> is a slightly better choice because it's easier to use and generally performs a bit better. + +[#expression-based-activation-condition] +[discrete] +===== When expression met + +This is very similar to the previous EEL activation mode. But instead of EEL, it lets you use the same expression language as used in <> to express the activation condition. + +The equivalent expression to above EEL example is: + +`p[0] > 0 && p[1] > 0` + +[#target-based-activation-condition] +[discrete] +===== When target value met + +This is different from all the other activation condition types in that it doesn't look at ReaLearn's internal parameter values. Instead, it looks at the target of another mapping (the so-called "lead mapping") and switches our mapping (the so-called "follow mapping") on or off depending on the target value of the lead mapping. + +It works like this: + +. Create the lead mapping and give it a target, e.g. <>. +* This lead mapping doesn't need to have a source. It can even be completely disabled! +. In the **Mapping** dropdown, pick this newly created mapping. +. In the **Expression** text field to the right, enter `y > 0`. +* This means you want the follow mapping to be active whenever the target value of the lead mapping is greater than 0.0. Or in other words, when it's "switched on". + +You can detect an inactive target by using `y == none`. + +[discrete] +===== Custom parameter names + +Because ReaLearn's parameters are freely assignable, they have very generic names by default. However, as soon as you +give them meaning by using them in a specific way, it can be helpful to give them a meaningful name. You can do that: + +. Switch to the compartment whose parameter names you want to change. +. Open the header panel context menu (accessible via right-click on Windows and Linux, control-click on macOS) + and open the _Compartment parameters_ submenu. +. Here you will find each of the 100 compartment parameters with their current names. Simply click the name to change + it. + +Parameter names are not global, they are always saved together with the REAPER project / FX preset / track template etc. +They will also be saved/restored as part of the compartment preset. + +[discrete] +===== Use case: Control A when a button is not pressed, control B when it is + +Here's how you would implement a typical use case. You want your rotary encoder to control target A when the button is +not pressed and control target B when it's pressed. + +. Create a mapping for the button +** As "Target", you need to choose ReaLearn itself (Type: <>, Track: `<This>`, FX: "… VSTi: ReaLearn (Helgoboss)"). As "Parameter", choose an arbitrary ReaLearn parameter, e.g. "Parameter 1". +** As "Mode", choose either "Absolute" (if you want to switch the encoder function just momentarily) or "Toggle" (if you want the button to toggle between the two encoder functions). +. Create a mapping with target A +** Set "Active" to "When modifiers on/off", "Modifier A" to "Parameter 1" and disable the checkbox beside it. Set "Modifier B" to `<None>`. +** This basically means "Hey, ReaLearn! Please activate this mapping only if ReaLearn Parameter 1 is *off*!" (remember, we control ReaLearn Parameter 1 using the button). +** At this point, turning your encoder should control target A, but only if you don't press the button! +. Create a mapping with target B +** Just as in step 2, set "Active" to "When modifiers on/off" and "Modifier A" to "Parameter 1". *But*: Now *enable* the checkbox beside it. Set "Modifier B" to `<None>`. +** This basically means "Hey, ReaLearn! Please activate this mapping only if ReaLearn Parameter 1 is *on*!" +** At this point, turning your encoder should control target A if you don't press the button and control target B if you press the button. + +==== Source + +As mentioned before, a source usually represents a single control element on your controller. +Sources share the following common settings and functions: + +* *Learn:* Starts or stops learning the source of this mapping. +* *Category:* Lets you choose the source category. +** *None:* A special kind of source that will never emit any events. It's intended to be used on mappings which are + not supposed to be controlled directly but only via <>. +** *MIDI:* Incoming MIDI events. +** *OSC:* Incoming OSC events. +** *REAPER:* Events that can occur within REAPER. +** *Virtual:* Invocations of virtual control elements (coming from virtual controller mappings). This source + category is available for main mappings only. +* *Type:* Let's you choose the source type. Available types depend on the selected category. + +All other UI elements in this section depend on the chosen category. + +===== Category "MIDI" + +All types in the MIDI category have the following UI elements in common: + +* *Channel:* Optionally restricts this source to messages from a certain MIDI channel. Only + available for sources that emit MIDI channel messages. + +The remaining UI elements in this section depend on the chosen source type. + +[#cc-value-source] +====== CC value + +This source reacts to incoming MIDI control-change messages. + +* *CC:* Optionally restricts this source to messages with a certain MIDI control-change controller + number. +* *Character:* MIDI control-change messages serve a very wide spectrum of MIDI + control use cases. Even though some control-change controller numbers have a special purpose + according to the MIDI specification (e.g. CC 7 = channel volume), nothing prevents one from using + them for totally different purposes. In practice that happens quite often, especially when using + general-purpose controllers. Also, there's no strict standard whatsoever that specifies how + relative values (increments/decrements) shall be emitted and which controller numbers emit them. + Therefore you explicitly need to tell ReaLearn about it by setting the _source character_. The + good news is: If you use "Learn source", ReaLearn will try to guess the source character for you + by looking at the emitted values. Naturally, the result is not always correct. The best guessing + result can be achieved by turning the knob or encoder quickly and "passionately" into clockwise + direction. Please note that guessing doesn't support encoder type 3. The possible values are: +* *Range element (knob, fader, etc.):* A control element that emits continuous absolute values. Examples: Fader, +knob, modulation wheel, pitch bend, ribbon controller. Would also include a endless rotary encoder +which is (maybe unknowingly) configured to transmit absolute values. +* *Button (momentary):* A control element that can be pressed and emits absolute values. It emits a > 0% +value when pressing it and optionally a 0% value when releasing it. Examples: Damper pedal. +* *Encoder (relative type _x_):* A control element that emits relative values, usually an endless rotary +encoder. The _x_ specifies _how_ the relative values are sent. This 1:1 corresponds to the +relative modes in REAPER's built-in MIDI learn: +** *Type 1*: +** 127 = decrement; 0 = none; 1 = increment +** 127 > value > 63 results in higher decrements (64 possible decrement amounts) +** 1 < value <= 63 results in higher increments (63 possible increment amounts) +** *Type 2*: +** 63 = decrement; 64 = none; 65 = increment +** 63 > value >= 0 results in higher decrements (64 possible decrement amounts) +** 65 < value <= 127 results in higher increments (63 possible increment amounts) +** *Type 3*: +** 65 = decrement; 0 = none; 1 = increment +** 65 < value <= 127 results in higher decrements (63 possible decrement amounts) +** 1 < value <= 64 results in higher increments (64 possible increment amounts) +* *Toggle-only button (avoid!):* A control element that can be pressed and emits absolute values. It emits a > 0% +value when pressing it, no value when releasing it and a 0% value when pressing it again. +** Hint: This is a workaround for controllers that don't have momentary buttons! You should only use this character +if there's absolutely no way to configure this control element as a momentary button. +** Background: ReaLearn can make a momentary hardware button work like a full-blown toggle button (ReaLearn's +toggle mode is inherently more powerful than your controller's built-in toggle mode!). However, the opposite is +not true. It can't make a toggle hardware button act like a momentary button. +** The way this character works: ReaLearn will simply emit 100%, no matter what the hardware sends. +** *Attention:* If you use the toggle-only source character in combination with mode "Incremental button", you +must leave source max at the (default) theoretical maximum value for that source (e.g. 127 for MIDI CC). Even if +your controller device only sends 0 and 1 and in all other mappings you would enter the controller's concrete +(instead of theoretically possible) maximum value. Otherwise, for this special case, a fixed +out-of-range-behavior will set in that will just ignore all button presses. +* *14-bit values:* If unchecked, this source reacts to MIDI control-change messages with 7-bit + resolution (usually the case). If checked, it reacts to MIDI control-change messages with 14-bit + resolution. This is not so common but sometimes used by controllers with high-precision faders. + +====== Note velocity + +This source reacts to incoming MIDI note-on and note-off messages. The higher the velocity of the +incoming note-on message, the higher the absolute control value. Note-off messages are always +translated to 0%, even if there's a note-off velocity. + +* *Note:* Optionally restricts this source to messages with a certain note number (note numbers + represent keys on the MIDI keyboard, e.g. 60 corresponds to C4). + +====== Note number + +This source reacts to incoming MIDI note-on messages. The higher the note number (= key on a MIDI +keyboard), the higher the absolute control value. + +This essentially turns your MIDI keyboard into a "huge fader" with the advantage that you can jump +to any value at any time. + +====== Pitch wheel + +This source reacts to incoming MIDI pitch-bend change messages. The higher the pitch-wheel position, +the higher the absolute control value. The center position corresponds to an absolute control value +of 50%. + +====== Channel after touch + +This source reacts to incoming MIDI channel-pressure messages. The higher the pressure, the higher +the absolute control value. + +====== Program change + +This source reacts to a range of incoming MIDI program-change messages. The higher the program number, the +higher the absolute control value. + +====== (N)RPN value + +This source reacts to incoming non-registered (NRPN) or registered (RPN) MIDI parameter-number +messages. The higher the emitted value, the higher the absolute control value. + +(N)RPN messages are not widely used. If they are, then mostly to take advantage of their ability to +transmit 14-bit values (up to 16384 different values instead of only 128), resulting in a higher +resolution. + +* *Number:* The number of the registered or unregistered parameter-number message. This is a value + between 0 and 16383. +* *RPN:* If unchecked, this source reacts to unregistered parameter-number messages (NRPN). If + checked, it reacts to registered ones (RPN). +* *14-bit values:* If unchecked, this source reacts to (N)RPN messages with 7-bit resolution, including increment/decrement messages. If + checked, it reacts to those with 14-bit resolution. In practice, this if often checked. +* *Character:* See <>. + +====== Polyphonic after touch + +This source reacts to incoming MIDI polyphonic-key-pressure messages. The higher the pressure, the +higher the absolute control value. + +* *Note:* Optionally restricts this source to messages with a certain note number. + +====== MIDI clock tempo + +This source reacts to incoming MIDI clock (MTC) tempo messages. These are metronome-beat-like +messages which can be regularly transmitted by some DAWs and MIDI devices. The frequency with which +this message is sent dictates the tempo. + +The higher the calculated tempo, the higher the absolute control value. A tempo of 1 bpm will be +translated to a control value of 0%, a tempo of 960 bpm to 100% (this corresponds to REAPER's +supported tempo range). + +This source can be used in combination with the <> target to obtain a "poor man's" tempo +synchronization. Be aware: MIDI clock naturally suffers from certain inaccuracies and latencies - +that's an issue inherent to the nature of the MIDI clock protocol itself. E.g. it's not really +suitable if you need super accurate and instant tempo synchronization. Additionally, ReaLearn's +algorithm for calculating the tempo could probably be improved (that's why this source is marked as +experimental). + +====== MIDI clock transport + +This source reacts to incoming MIDI clock (MTC) transport messages. These are simple start, continue +and stop messages which can be sent by some DAWs and MIDI devices. + +* *Message:* The specific transport message to which this source should react. + +[#raw-midi-source] +====== Raw MIDI / SysEx + +This source primarily deals with system-exclusive MIDI messages. Since ReaLearn v2.11.0, it supports both control and feedback direction! + +* *Pattern:* Pattern describing the raw MIDI message. + +*Pattern basics* + +In its most basic form, the pattern is a sequence of bytes notated as hexadecimal numbers. This is typical notation, +especially for system-exclusive MIDI messages. + +Example: + +---- +F0 00 20 6B 7F 42 02 00 10 77 00 F7 +---- + +If you enter this and ReaLearn receives this system-exclusive message from the input, it will fire a 100% value. If feedback is set up correctly, this message will be sent to the device whenever the target value changes. + +Remarks: + +- You can check if the correct feedback messages are sent to the device by <>. +- Each byte is written using 2 hexadecimal digits. +- Spaces between the bytes can be omitted. +- You can express all types of MIDI messages using this raw notation (e.g. pitch wheel), not just system-exclusive ones. If you do this, it will work as expected for the _feedback_ direction. Please note that it will not work for the _control_ direction at the moment (I don't think this is needed). +- If you want a system-exclusive MIDI message, you _must_ include its start (`F0`) and end status byte (`F7`)! + +*Binary notation* + +ReaLearn also supports binary notation of a byte. You need to enclose the binary digits of one byte in brackets. + +Example: + +---- +F0 00 20 [0110 1011] 7F 42 02 00 10 77 00 F7 +---- + +This is equivalent to the first example (`6B` in hexadecimal notation is the same as `0110 1011` in binary +notation). + +Remarks: + +- Between the brackets, each digit represents one bit. The left bit is the most significant one. +- Spaces between the two nibbles (4 bits) can be omitted. + +*Extracting and encoding a value* + +For the _feedback_ direction, the examples I've shown you so far aren't real-world examples, because there's no point in sending the same MIDI message to the device over and over again! If you really would want to send a constant MIDI message to the device, you would be +much better off using a <>, which allow you to send raw MIDI +messages once when a mapping is initialized, not on every target value change. + +But even for the _control_ direction, you might want to react to a whole _range_ of system-exclusive messages, not just a fixed one. One part of your message might represent a variable value. You might want to extract it and control the target with it. + +Fortunately, ReaLearn offers a uniform way to extract a variable value from the raw MIDI message (control) or encode the current target value as part of it (feedback). Bytes which contain a variable value (or a part of it) _must_ be expressed using binary notation. + +Example: + +---- +F0 00 20 6B 7F 42 02 00 10 77 [0000 dcba] F7 +---- + +The second nibble of the second last byte contains the lowercase letters `dcba`. This is the portion of the byte that +denotes the variable value. + +Each letter represents one bit of the variable value: + +* `a` - Bit 1 (least significant bit of the variable value) +* `b` - Bit 2 +* `c` - Bit 3 +* `d` - Bit 4 +* … +* `m` - Bit 13 +* `n` - Bit 14 +* `o` - Bit 15 +* `p` - Bit 16 (most significant bit of the variable value) + +The resolution of the variable value always corresponds to the letter in the whole pattern which represents the +highest bit number. In the example above, the resolution is 4 bit because there's no letter greater than `d` +in the pattern. + +In the following example, the resolution is 7 bit because `n` is the greatest letter in the whole pattern. + +---- +F0 00 20 6B 7F 42 02 00 10 [00nm lkji] [hgfe dcba] F7 +---- + +Remarks: + +- The highest resolution currently supported is 16 bit (= 65536 different values). +- You can put these letter bits anywhere in the pattern (but only within bytes that use binary notation). + +*Byte order* + +This form of notation is slightly unconventional but I think it's very flexible because it gives you much control over +the resulting MIDI message. This amount of control seems appropriate considering the many different ways +hardware manufacturers used and still use to encode their MIDI data. When a number is expressed within more than +one byte, manufacturers sometimes put the most significant byte first and sometimes the least significant one, +there's no rule. This notation supports both because you decide where the bits end up: + +Example for "most significant byte first": + +---- +F0 00 20 6B 7F 42 02 00 10 [ponm lkji] [hgfe dcba] F7 +---- + +Example for "least significant byte first": + +---- +F0 00 20 6B 7F 42 02 00 10 [hgfe dcba] [ponm lkji] F7 +---- + +*More examples* + +"Romeo and Juliet" bits (separated by 2 bytes): + +---- +F0 [1111 000b] [a101 0100] F7 +---- + +Simple on/off value (1 bit only): + +---- +F0 A0 [1111 010a] F7 +---- + +This behaves like pitch wheel (because the pattern describes exactly the way how pitch wheel messages are encoded): + +---- +E0 [0gfe dcba] [0nml kjih] +---- + +[#script-source] +====== MIDI Script + +This source is feedback-only and exists for enabling more complex feedback use cases such as controlling LCDs that are not yet supported by the <> source. It lets you write an EEL or Lua script that will be executed whenever ReaLearn "feels" like it needs to send some feedback to the MIDI device. + +* *Kind:* Whether to use the EEL or Lua language. +* *Script:* The script. Is disabled if the script contains more than one line. +* *…:* Opens the script in a separate window (for multi-line scripts). + +TIP: Prefer the <> source over this one whenever possible. It's easier to use. + +*General mechanics* + +* The script receives an input and must produce an output. +* *Script input* +** The main input is the current feedback value, which the script can access as a variable. +* *Script output* +** The main output that the script is supposed to return is the MIDI message to be sent to the MIDI device. +** Additionally, the script can provide a so-called _feedback address_, which is supposed to uniquely identify the LED, motor fader or display. It's important to provide an address if you want ReaLearn to handle feedback relay correctly, e.g. that it switches off the LED when not in use anymore and doesn't switch it off if another mapping "takes over" the same LED. By convention, the constant (non-variable) bytes of the MIDI message should be used as address. The examples below might help to understand. + +*EEL script specifics* + +* *Script input* +** EEL scripts can access numeric feedback values only. The current numeric feedback value is available as variable `y`, a floating point number between 0.0 and 1.0. This is essentially the current normalized target value after being processed by the "Glue" section of the mapping. +* *Script output* +** In order to provide the output MIDI message, you must assign the raw bytes of that message to subsequent slots of the EEL script's virtual local address space (by indexing via brackets) *and* +set the variable `msg_size` to the number of bytes to be sent. If you forget the latter step, nothing will be sent because that variable defaults to zero! +** In order to provide the address, simply assign an appropriate number to the `address` variable. +* *Examples* +** The following example creates a 3-byte MIDI message. ++ +[source,eel] +---- +address = 0x4bb0; +msg_size = 3; +0[] = 0xb0; +1[] = 0x4b; +2[] = y * 64; +---- + +*Lua script specifics* + +* *Script input* +** Lua scripts can access numeric, text and dynamic feedback values. +** Here's the list of input variables: +*** `y`: The feedback value, either numeric (`type(y) == "number"`) or text (`type(y) == "string")`. +*** `context.feedback_event.color`: The color as set in the <> section. Either the default color (`== nil`) or an RGB color (table with properties `r`, `g` and `b`). +*** `context.feedback_event.background_color`: The background color. +* *Script output* +** A Lua script can even generate multiple output messages. +** You need to return a table which contains the following keys: +*** `address`: The feedback address. +*** `messages`: An array containing all the messages, where each message itself is an array contaning the message bytes. +* *Examples* +** Creates a 3-byte MIDI message, assuming that `y` is a numeric normalized value. ++ +[source,lua] +---- +return { + address = 0x4bb0, + messages = { + { 0xb0, 0x4b, math.floor(y * 10) } + } +} +---- ++ +** Creates a MIDI sys-ex message that changes the color of some fictional device LED/display: ++ +[source,lua] +---- +local color = context.feedback_event.color +if color == nil then + -- This means no specific color is set. Choose whatever you need. + color = { r = 0, g = 0, b = 0 } +end +return { + address = 0x4b, + -- Whatever messages your device needs to set that color. + messages = { + { 0xf0, 0x02, 0x4b, color.r, color.g, color.b, 0xf7 } + } +} +---- +** Creates a 3-byte MIDI message, assuming that `y` is a text value. ++ +[source,lua] +---- +local lookup_table = { + playing = 5, + stopped = 6, + paused = 7, +} +return { + messages = { + { 0xb0, 0x4b, lookup_table[y] or 0 } + } +} +---- ++ +[TIP] +==== +Please note that this kind of simple mapping from text values to integer numbers doesn't need a script. You can use the `feedback_value_table` <> property instead, which can only be set via API though. Do a full-text search for `feedback_value_table` in directory `resources/controller-presets` of the link:https://github.com/helgoboss/realearn[ReaLearn source code] to find usage examples. +==== + +[#display-source] +====== Display + +This is a feedback-only source used to display text on MIDI-controllable hardware displays (LCDs, OLED displays, 7-segment displays, etc.). + +* *Protocol:* Lets you choose the display protocol, which tells ReaLearn how it should communicate with the hardware display and which options it supports. +** *Mackie LCD:* Use this for MCU-compatible LCDs. Depending on your particular control surface, there can be up to 8 LCDs, each of which has up to 2 lines. +** *Mackie XT LCD:* Use this to control the displays of MCU XT devices (= control surface extenders, which provide additional faders and displays). +** *X-Touch Mackie LCD:* Like _Mackie LCD_ but also supports colors on certain X-Touch devices. +** *X-Touch Mackie XT LCD:* Like _Mackie LCD XT_ but also supports colors on certain X-Touch devices. +** *Mackie 7-segment display:* Use this for MCU-compatible 7-segment displays (you know, the ones which only show digits). There's usually one small assignment display and a larger one for showing the time code. +** *SiniCon E24:* Use this with the https://www.sinicon.io/[SiniCon E24 controller]. +** *Launchpad Pro - Scrolling text:* Displays looped scrolling text on a Novation Launchpad Pro. Only seems to work if you set _Output_ to `MIDIOUT2 (Launchpad Pro)`. +** *Studiologic SL Keyboard display:* Displays text on the display of Studiologic SL keyboards (tested with SL88). +* *Display:* Choose the particular display or display portion to which you want to send text. +* *Line:* Choose the line number. + +CAUTION: For controllers with multiple displays and lines, ReaLearn allows you to spread your text over all available displays and lines. This is great if you need to display a lot of text but one display doesn't provide enough space. But be aware: Replacing feedback with other feedback ("feedback relay") doesn't work so nicely anymore if you make use of this feature. + +If you want to know how to define which text shall be sent to the displays, please see <> in the *Glue* section. + + +====== Specific program change + +This source reacts to MIDI program-change messages with a specific program. This is a trigger-only source, that means it always fires 100% (whenever the program number corresponds to the configured one). + +[#category-osc] +===== Category "OSC" + +OSC sources allow configuration of the following aspects: + +====== Address + +This needs to correspond exactly to the address of the corresponding control element on your OSC device. + Example: `/1/fader1`. You don't need to figure that out yourself, just use the _Learn_ function. + +====== Argument + +Each OSC message consists of an arbitrary number of arguments. In most cases, e.g. with faders, knobs or + buttons, it's just one argument. X/Y controls often send 2 arguments, one for each axis. There are rare cases in which messages have even more arguments. + +The first dropdown menu allows you to choose the number of the argument that ReaLearn should look at and process. `1` denotes the first argument, `2` the second one, and so on. + +The second dropdown menu lets you choose the argument type which ReaLearn should use to construct a proper feedback message. + +* This is usually the same type as the one used for control direction. For control direction, choosing an explicit type is irrelevant because ReaLearn handles whatever type arrives automatically in the best possible way. +* If you use _Learn_, the type is filled automatically. +* The value to be sent will be derived from the type (see <>): ++ +[cols="m,m"] +|=== +| Type | Property + +| Float | value.float +| Double | value.double +| Int | value.int +| Long | value.long +| Bool | value.bool +| Nil | nil +| Inf | inf +| String | value.string +| Color | style.color +|=== +* If you want more control over what feedback values are sent, use the <> field. + +====== Range + +Values of argument types _Float_ and _Double_ are by default interpreted as decimal values between 0.0 and 1.0. You can change that by entering a different value range here. Even negative numbers are allowed. + +Customizing the value range is especially important for argument types _Int_ and _Long_ because they don't have a standard value range. + +====== Is relative + +Some messages transmitted by OSC devices are meant to be interpreted as relative + increments/decrements instead of absolute values, e.g. jog wheels. When you enable this checkbox, ReaLearn will + treat each received _1_ value as an increment and _0_ value a decrement. + + +[#feedback-arguments] +====== Feedback arguments + +Allows you to define exactly which feedback value is sent at which argument position. If this field is non-empty, the _Type_ dropdown will be ignored. + +The format of this field is very simple: You enter feedback value property keys separated by spaces. Each entered property key corresponds to one argument position. E.g. if you want ReaLearn to send the current feedback value in text form at argument 1 and the color (see <>) as RRGGBB string at argument 2, you would enter: + +---- +value.string style.color.rrggbb +---- + +The following properties are available: + +[cols="m,m,1"] +|=== +| Property | Type | Description + +| +value.float +| +Float +| +Numeric feedback value interpreted as float + +| +value.double +| +Double +| +Numeric feedback value interpreted as double + +| +value.bool +| +Bool +| +Numeric feedback interpreted as bool (on/off only) + +| +value.string +| +String +| +Numeric or textual feedback value formatted as string + + +| +style.color.rrggbb +| +String +| +Feedback value color formatted as RRGGBB string + + +| +style.background_color.rrggbb +| +String +| +Feedback value background color formatted as RRGGBB string + + + +| +style.color +| +Color +| +Feedback value color as native OSC color + + +| +style.background_color +| +Color +| +Feedback value background color as native OSC color + + +| +nil +| +Nil +| +Nil value + +| +inf +| +Infinity +| +Infinity value +|=== + +===== Category "Keyboard" + +This source reacts to pressing or releasing a key on your computer keyboard. It emits a value of 100% when the key is pressed and 0% when released. + +Usage: + +* In order to set the key, simply click the *Learn* button and press the key of your choice. +* In addition to the key label, ReaLearn might show some warnings regarding the portability of your keystroke. +** This helps you to avoid keyboard shortcuts that don't reliably work cross-platform (in other operating systems) or on other keyboard layouts. +** You can ignore portability warnings if you use just this operating system and don't plan to share your keyboard presets with other users. + +Tips: + +* This only works if <> is set to *Computer keyboard*. +* If you hold a key, it will not keep firing. This is by design! Use <> instead. +* Key combinations are not supported. This is by design! Use <> instead. + +====== MIDI device changes + +===== Category "REAPER" + +====== MIDI device changes + +This source emits a value of 100% whenever any MIDI device is connected and 0% whenever any MIDI device is +disconnected. You can map this to the REAPER action "Reset all MIDI devices" to achieve true plug and play +of MIDI devices (provided the corresponding device has been enabled at least once in REAPER's MIDI device +preferences). + +====== ReaLearn instance start + +This source fires (emits a value of 100%) when ReaLearn starts. It can be used to execute an actions or restore certain states on REAPER startup or project load. + +====== Timer + +This source fires (emits a value of 100%) repeatedly every _n_ milliseconds. + +====== ReaLearn parameter + +This source fires whenever one of ReaLearn's <> is changed. + +One of many ways to use this is to create macro parameters which control multiple parameters of multiple other plug-ins. + +WARNING: At the moment, mappings with this source can't participate in rendering. So it's important to write down automation *before* rendering. This applies *in addition* the things pointed out in <>. + +====== Speech + +This source works for feedback only. It uses the native Windows or macOS text-to-speech engine to speak out any feedback value. + +[#virtual-source] +===== Category "Virtual" + +As pointed out before, _virtual_ sources exist in order to decouple your mappings from the actual +MIDI/OSC source. + +If you want to define a virtual source, you first need to choose among two types of virtual control elements: +"Multi" (control elements that support more than 2 values) and "Button" (simple on/off controls). It's sort of the +lowest common denominator among all possible control element types. This distinction is used by ReaLearn +to optimize its user interface. In future, it might be used for additional improvements. + +Both types are explained in detail below. They support the following settings: + +* *ID:* A number or name for uniquely identifying the control element. +** Numbers are especially suited for the 8-knobs/8-buttons layouts. In a row of 8 knobs one would typically assign + number 1 to the leftmost and number 8 to the rightmost one. It's your choice. +** For more advanced virtual control scenarios it can be useful to think in names instead of numbers. That's why + the IDs of virtual control elements are not limited to numbers only. You can use up to 32 alphanumeric and + punctuation characters (no exotic characters, e.g. no umlauts). +* *Pick:* Lets you conveniently pick out of predefined numbers and names. If you want your main preset to be + compatible with as many controller presets as possible, try to use predefined names instead of inventing your own + naming scheme. +** *DAW control:* The names you see here are heavily inspired by the wording used with Mackie Control devices. +** *Numbered:* Simply lets you pick among any number between 1 and 100. Wow, you can save up to 3 key presses!!! + +====== Multi + +Represents a control element that you can "move", that is, something that allows you to choose between more than 2 +values. Usually everything which is _not_ a simple on/off button :) Here's a list of typical _multis_: + +* Fader +* Knob +* Pitch wheel +* Mod wheel +* Endless encoder +* XY pad (1 axis) +* Touch strip +* (Endless) rotary encoder +* Velocity-sensitive pads or keys + +====== Button + +Represents a control element that distinguishes between two possible states only (e.g. on/off), or even just one +("trigger"). Usually it has the form factor of a button that you can "press". Here's a list of typical _buttons_: + +* Play button +* Switch +* Sustain pedal + +Please note that velocity-sensitive keys should be exposed as "Multi", not as "Button" - unless you know for sure that +you are not interested in the velocity sensitivity. + +[#target] +==== Target + +A target is a thing that is supposed to be controlled. + +===== Common target elements + +====== Learn + +Starts or stops learning the target of this mapping. + +====== Menu + +Opens a small menu related to the target section: + +- *Pick recently touched target (by type):* Gives you a list of recently touched parameters or executed actions in REAPER. When you click one of it, the target will be populated accordingly. It's an alternative to "Learn". Please note that not all targets can be picked that way, some have to be configured manually. +- *Go there (if supported):* If applicable, this makes the target of this mapping visible in REAPER. E.g. if the target is a track FX parameter, the corresponding track FX window will be displayed. + + + +====== Type + +* *Left dropdown:* Lets you choose the target category. +** *Real:* Targets that are about actually changing something "real", e.g. in REAPER or ReaLearn itself. +** *Virtual:* Targets that invoke virtual control elements. This source + category is available for controller mappings only. +* *Right dropdown:* Lets you choose a target type within that category. + +====== Value +Reflects the current value of this mapping target and lets you change it (either via slider and text field or via buttons, depending on the target character). + +* If the target can't be resolved at the moment, it will show "Target currently inactive!". + +====== Unit button + +On the right side of the current value you will see a button with a label such as `1. dB (%)`. + This button displays the currently selected unit which is used for displaying and entering target values. The + number in the parentheses denotes the unit which is used for displaying and entering target step sizes. Clicking + the button switches between the units. Currently there are two options: + +* *1. Use native target units*: Uses the target-specific unit, e.g. dB for volume targets. If the target + doesn't have any specific units, it will displayed as `1. - (-)`. +* *2. Use percentages*: Uses percentages for everything, which can be nice to get a uniform way of + displaying/entering values instead of having to deal with the sometimes clunky target-specific units. + +===== Common selectors + +Targets that need a track, FX, FX parameter or send/receive have dropdowns that let you choose how you want to _address_ these objects. Let's call them _object selectors_. Here's an explanation of commonly available object selectors. + +NOTE: The descriptions below are sometimes a bit tailored to _track_ objects but the same applies to all other objects that support it. + +[#instance-selector] +====== Selector "Instance" + +This selector makes the target work on the current <> or current <> of this particular ReaLearn instance. + +[#by-id] +====== Selector "Particular" + +Lets you pick a specific object (e.g. track) and refer to it by its unique ID. This is the default and + in most cases what you want. Choose this if you want ReaLearn to always control that very particular track even + in case you move it somewhere else or rename it. + +Please note that it's + _not possible_ with this setting to create a ReaLearn preset that is reusable among different projects. Because a + track ID is globally unique, even across projects. That also means it doesn't make sense to use this setting in a ReaLearn monitoring FX instance. + +[#by-position] +====== Selector "At position" + +This is the most straightforward selector. It lets you refer to a track by its position in the track list. This is great if you want to build a preset that you are going to reuse among multiple projects. + +However, this selector has the disadvantage that things fall apart if you reorder, insert or delete tracks. This is why it's not the default. + +Next to the dropdown you will find a text field. Here you should enter the position as number, starting with number `1`. + +[#by-name] +====== Selector "Named" + +Allows you to choose a track depending on its name. In case there are multiple tracks with the same + name, it will always prefer the first one. This will allow you to use one ReaLearn preset across multiple projects that have similar naming schemes, e.g. as monitoring FX. + +In the name field next to the dropdown, you can enter a name. If you don't want exact matching, you can use wildcards: + +* `*` for matching zero or arbitrary many characters +* `?` for matching exactly one arbitrary character. +* Example: `Violin *` would match `Violin 1` or `Violin 12` but not `12th Violin`. + +[#dynamic-selector] +====== Selector "Dynamic" + +This selector allows you to _calculate_ which object (e.g. track) you want to use. + +When you choose this option, a text field will appear next to it. This lets you enter a + mathematical expression whose result should be the object's _index_. E.g. the first track in the project has index 0, the master track -1. For your convenience, you will find a small text label next to the expression text field that always shows the current result of your formula (clamped to the target value range). + + +NOTE: Please note +that the expression language is _not EEL_ - this is a notable difference to ReaLearn's control/feedback +transformation text fields! The expression language used here just +provides very basic mathematical operations like addition (`+/-`), multiplication (`*`) etc. and it also +doesn't allow or need any assignment to an output variable. + +The dynamic selector is a very powerful tool because you can use some special variables: + + +[cols="m,1,1,3"] +|=== +| Variable | Type | Applicable to | Description + +| none | - | All selectors | +Special value that represents a "not set" value. See below for examples. + +| p | Array of floating-point numbers | All selectors | +Allows you to access the values of ReaLearn's internal parameters. E.g. you can get the number of the first parameter by writing `p[0]`. + +By default, parameter values are normalized floating point values, that means they are decimal numbers between 0.0 and 1.0. This can be changed by customizing the parameter with a specific integer value count (see <>). + +| p1 ... p100 | Floating-point numbers | All selectors | +Values of ReaLearn's internal parameters as single variables. + +_Deprecated_: Recent ReaLearn versions offer the `p` array instead. Better use that one. + + +| selected_track_index | Integer >= -1 | Track selectors | +Resolves to the zero-based index of the first currently selected track within the containing project. +If no track is selected, this resolves to `none`. If the master track is selected, `-1`. + +| selected_track_tcp_index | Integer >= -1 | Track selectors | +Like `selected_track_index` but counts only tracks that are visible in the track control panel. + +| selected_track_mcp_index | Integer >= -1 | Track selectors | +Like `selected_track_index` but counts only tracks that are visible in the mixer control panel. + +| selected_track_indexes | Array of integers >= -1 | Track selectors | +Lets you access the indexes of multiple selected tracks. + +E.g. if 2 tracks are selected, `selected_track_indexes[0]` gives you the index of the first selected track whereas `selected_track_indexes[1]` gives you the index of the second selected track. `selected_track_indexes[2]` would resolve to `none`. + +| this_track_index | Integer >= -1 | Track selectors | + +Resolves to the zero-based index of the track on which this ReaLearn instance is located. + +| instance_track_index | Integer >= -1 | Track selectors | + +Resolves to the zero-based index of the instance track of this ReaLearn instance. + +| instance_track_tcp_index | Integer >= -1 | Track selectors | + +Like `instance_track_index` but counts only tracks that are visible in the track control panel. + +| instance_track_mcp_index | Integer >= -1 | Track selectors | + +Like `instance_track_index` but counts only tracks that are visible in the mixer control panel. + +| this_fx_index | Integer >= 0 | FX selectors | + +Resolves to the zero-based index of this ReaLearn FX instance. + +| instance_fx_index | Integer >= 0 | FX selectors | + +Resolves to the zero-based index of the instance FX of this ReaLearn instance. + +| tcp_fx_indexes | Array of integers >= 0 | FX selectors | + +Lets you access the indexes of FXs in a track control panel. + +E.g. `tcp_fx_indexes[2]` will resolve to the index of the third FX displayed in the track control panel. + +| tcp_fx_parameter_indexes | Array of integers >= 0 | FX parameter selectors | + +Lets you access the indexes of FX parameters in a track control panel. + +E.g. `selected_fx_parameter_indexes[2]` will resolve to the index of the third FX parameter displayed in the track control panel. + +This only makes sense if used in conjunction with `tcp_fx_indexes`. + +|=== + +Examples of dynamic track expressions: + +* `p1 * 99` +** Will point to track with index 0 (first track) if "Parameter 1" is set to the minimum and to + track with index 99 (= track number 100) if it's set to the maximum. +** If you use a formula like that, + you should make sure that "Parameter 1" is controlled with a step size that allows for exactly 100 different + values. This conforms to ReaLearn's default step size 0.01 = 1%. +** Since ReaLearn 2.13, this is easier because it adds support for integer parameters: +*** Set the value count of "Parameter 1" to 100 (see <>) +*** You can now treat the parameter in the formula as an integer (just `p1` instead of `p1 * 99`). +*** Most importantly, ReaLearn will take care of using the correct step size automatically when setting up a mapping for +controlling that parameter. +* `p1 * 3 * 100 + p2 * 99` +** This will treat "Parameter 1" as a kind of bank selector that allows you + to choose between exactly 4 banks (0, 1, 2, 3) of 100 tracks each. "Parameter 2" will select the track + number within the bank. You see, this is very flexible. + +===== Common elements and selectors for track targets + +When choosing a track, the following additional elements and selectors are available: + + +====== Track must be selected + +If checked, this mapping will be active only if the track set in _Track_ is currently selected. + +====== Selection ganging + +If checked and if the track in question is selected, all other selected tracks will be adjusted as well. This uses REAPER's built-in selection-ganging feature and therefore should behave exactly like it. + +====== Respect grouping + +If checked, track grouping will be taken into account when adjusting the value. This uses REAPER's built-in track grouping feature and therefore should behave exactly like it. + +NOTE: In older REAPER versions (< 6.69+dev1102), this can only be enabled together with selection ganging when using it on volume, pan or width targets. + +====== Selector "" + +Track which hosts this ReaLearn instance. If ReaLearn is on the monitoring FX +chain, this resolves to the master track of the current project. + +[#selected-selector] +====== Selector "" + +Currently selected track. If multiple tracks are selected, refers only to the first one. + +====== Selector "" + +All currently selected tracks. This makes track targets (not FX target and not send +targets) do their job on _all_ selected tracks. The feedback value always corresponds to the highest value among all selected tracks. + +CAUTION: If you select many tracks, things can become quite slow! + +====== Selector "" + +Master track of the project which hosts this ReaLearn instance. + +* If ReaLearn is on the monitoring FX chain, this resolves to the master track of the current project. +* If you don't have ReaLearn on the monitoring FX chain but you want to control an FX on the monitoring FX +chain, this option is the right choice as well. Make sure to enable the "Monitoring FX" checkbox. + +====== Selector "All named" + +Allows you to use wildcards (see <>) to make track targets do their thing on +all matching tracks instead of only the first one. + +====== Selector "At TCP position" + +Like <> but just considers tracks that are visible in the track control panel. + +====== Selector "At MCP position" + +Like <> but just considers tracks that are visible in the mixer control panel. + +====== Selector "Dynamic (TCP)" + +Like <> but the result should be an index counting only tracks visible in the track control panel. + +====== Selector "Dynamic (MCP)" + +Like <> but the result should be an index counting only tracks visible in the mixer control panel. + +====== Selector "By ID or name (legacy)" + +This lets you refer to a track by its unique ID and name as fallback. This was the default +behavior for ReaLearn versions up to 1.11.0 and is just kept for compatibility reasons. + +_Deprecated_: You shouldn't use this selector anymore. + +===== Common elements for on/off targets + +Targets which control an on/off-style property of tracks (e.g. <>) additionally provide the following elements. + +====== Exclusive + +By default, this option is set to "No". + +* *No:* Makes the track target affect just this track. +* *Within project:* Switches the property on (off) for this track and off (on) for all other tracks in the project. +* *Within folder:* Switches the property on (off) for this track and off (on) for all other tracks in the same folder and same level. +* *Within project (on only):* Variation of _Within project_ that applies exclusivity only when switching the property on for this track. In other words, it never switches the property on for other tracks. +* *Within folder (on only):* Variation of _Within folder_ that applies exclusivity only when switching the property on for this track. In other words, it never switches the property on for other tracks. + + +===== Common elements for send targets + +Only available for targets that work on a send/receive: + +====== Kind + +The kind of send/receive that you want to control. + +* *Send:* Send from the track above to another track of your choice. If you choose <>, + ReaLearn will memorize the ID of the destination track. That way you will still control the correct send even + if you delete another send in that track. +* *Receive:* Receive from another track of your choice to the track above (opposite direction of send). If you + choose the <> selector, ReaLearn will memorize the ID of the source track. +* *Output:* Send from the track above to a hardware output. Please note that with hardware outputs, <> is the + same as <> because hardware outputs don't have unique IDs. + +====== Send/Receive/Output + +This lets you choose the actual send/receive/output. + +===== Common elements and selectors for FX targets + +The following elements and selectors are available for targets associated with a particular FX instance. + +====== FX + +The FX instance associated with this target. ReaLearn will search for the FX in the output or input FX chain of the above selected track. + +====== Selector "" + +Always points to the own ReaLearn instance. Perfect for changing own parameters, e.g. for + usage of the conditional activation or `` features (especially important if you want to create reusable + presets that make use of these features). + +====== Selector "" + +Currently focused FX. _Track_ and _Input FX_ settings are ignored. + +[#fx-by-id] +====== Selector "Particular" + +Lets you pick a specific FX in the FX chain. Renaming the FX or moving it within the FX chain is fine - ReaLearn will still keep controlling exactly this FX. Please note that this only makes sense if you address the containing track using <> as well. + +[#fx-by-name] +====== Selector "Named" + +Lets you address the FX by its name in the FX chain. Just as with tracks, you can use wildcards to have a blurry search. + +====== Selector "All named" + +Allows you to use wildcard3s (see <>) to make FX targets do their thing on all matching FX instances instead of only the first one. + +====== Selector "By ID or position (legacy)" + +This refers to the FX by its unique ID with its position as fallback. This was the default + behavior for ReaLearn versions up to 1.11.0 and is just kept for compatibility reasons. + +_Deprecated_: Don't use this selector anymore. + +====== Input FX + +If unchecked, the _FX_ dropdown will show FX instances in the track's normal FX + chain. If checked, it will show FX instances in the track's input FX chain. + +====== Monitoring FX + +This appears instead of the input FX checkbox if you select track `<Master>`. If you check this, + you can target FX instances on REAPER's global monitoring FX chain. + +WARNING: Because of a limitation in the REAPER API, learning and feedback for monitoring FX doesn't work + at the moment! + +====== FX must have focus + +If checked, this mapping will be active only if the FX instance set in + _FX_ is currently focused. + +If the FX instance is displayed in a floating window, _focused_ means + that the floating window is active. If it's displayed within the FX chain window, _focused_ means + that the FX chain window is currently open and the FX instance is the currently selected FX in + that FX chain. + +Of course, this flag doesn't have any effect if you chose _<Focused>_ FX. + +===== Common elements for pollable targets + +The following elements are available only for the few targets that might need polling (= regular value querying) in order to support automatic feedback in all cases. + +====== Poll for feedback + +This makes ReaLearn query the current target value every few milliseconds in order to send + up-to-date feedback to your controller at all times. + +This is not necessary for most targets because usually ReaLearn + takes advantage of REAPER's internal notification system to get notified about target value changes (which is great + for performance). For the few targets for which it is, this option is enabled by default in order to give + you the best feedback experience out-of-the-box. + +In the probably rare case that the polling causes performance issues, you can untick this checkbox. + +* For most targets, if you untick this checkbox, automatic feedback for that target will simply stop working. This + means you will not receive up-to-date feedback anymore whenever you change the target value within REAPER itself + (not using ReaLearn). +* The <> target is an exception. Automatic feedback will still work, even without _Poll for + feedback_ enabled. But in the following corner cases it might not: +** If the FX is on the monitoring FX chain. +** If you change a preset from within the FX GUI. + +===== Category "Real" + +[#global-last-touched] +====== Global: Last touched + +This will control whatever target has been last touched in REAPER. It's similar to the built-in REAPER action +"Adjust last touched FX parameter" but provides the following benefits: + +. It's applicable to all ReaLearn targets that are learnable, not just FX parameters. +. It offers feedback. +. It can distinguish between parameter modifications caused by ReaLearn (i.e. hardware control) and those caused in other ways (e.g. via mouse). + +- *Targets → Pick!:* This opens a window that lets you pick all considered target types and types of invocations (only macOS and Windows so far). Last-touched targets not checked in this window will be ignored. + +====== Global: Mouse + +This will control the mouse. + +* *Action* +** *Move cursor to:* Moves the mouse cursor on the given axis in an absolute manner. This is a good choice for absolute mouse movement, that is, if you want to position the mouse cursor to a specific screen position. Although it's also possible to move the mouse cursor relatively with this action by controlling the target with relative messages, it's usually better to use _Move cursor by_ instead. +** *Move cursor by:* Moves the mouse cursor on the given axis in a relative manner. This is a good choice if you want to move the cursor e.g. up a bit, starting from its current position. This only works with relative control elements such as encoders or features such as <>. +** *Press or release button:* Presses or releases a certain mouse button, depending on the incoming control value (0% = release, anything else = press). +** *Turn scroll wheel:* Simulates the scroll wheel. +* *Axis:* Determines the direction of movement or scrolling. +* *Button:* Determines which mouse button to use. + +TIP: One popular use of this target is to adjust the FX parameter under the mouse cursor. For this, it's usually best to set _Action_ to "Turn scroll wheel" and _Axis_ to "Y (vertical)". + +TIP: You can unfold the magic of this target by combining multiple mappings. E.g. one can simulate mouse dragging by using one mapping to press/release the left button and another mapping to move the cursor. link:https://raw.githubusercontent.com/helgoboss/realearn/master/resources/test-projects/issue-686-mouse-target.RPP[This example project] contains multiple examples (one per group). + +WARNING: Feedback for this target is not fully implemented. + +====== Global: Set automation mode override + +Sets the global automation mode override to the desired value if the incoming control value is greater than 0%, +otherwise removes the override. + +* *Behavior:* Lets you decide between not overriding anything, bypassing all envelopes or overriding with a specific + automation mode. +* *Mode:* Here you can pick the desired automation mode if _Behavior_ is _Override_. + +====== Project: Any on (solo/mute/...) + +This target is most useful in feedback direction. Map it to some LED on your controller and the LED will light up if at least one of the tracks in your project is e.g. mute (depending on the track parameter in question). + +If the control element is also a button, pressing the button will e.g. unmute all tracks in your project. + +* *Parameter:* The track parameter in question. + +[#project-invoke-reaper-action] +====== Project: Invoke REAPER action + +Triggers or sets the value of a particular REAPER action in the main section. + +* *Invoke:* Specifies _how_ the picked action is going to be controlled. +** *Trigger:* Invokes the action with the incoming absolute control value, but only if it's +greater than 0%. Most suitable for simple trigger-like actions that neither have an on/off state +nor are annotated with "(MIDI CC/OSC only)" or similar. +** *Absolute 14-bit:* Invokes the action with the incoming absolute control value, even if it's 0%. Most +suitable for actions which either have an on/off state or are annotated with "(MIDI CC/OSC +only)" or similar. The resolution of the invocation is 14-bit, no matter what's the resolution of your control element). +** *Absolute 7-bit:* Just like the previous invocation mode but uses 7-bit resolution. Might be necessary for actions provided by 3rd-party extensions which don't interpret 14-bit control values correctly. In all other circumstances, 14-bit is probably the better default choice. +** *Relative:* Invokes the action with the incoming relative control value (absolute ones are +ignored). Only works for actions that are annotated with ("MIDI CC relative only") or similar. +* *Pick!:* Opens REAPER's action dialog so you can select the desired action. +* *With track*: Allows you to choose a track which ReaLearn will select before executing the action. This makes it possible to combine ReaLearn's flexible track selection capabilities with the plethora of REAPER actions that work on the currently selected track. + +The particular action decides if toggling/feedback works completely, has limitations or is not possible at all. There +are multiple types of actions so it's not possible to settle with one invocation type and be done with it. The types +of actions can roughly be divided into: + +. Actions that take care of toggling themselves _and_ report on/off state. +** Example: "25. Track: Toggle record arm for track 01" +** If you want toggle behavior, you have 2 options: +*** a) Set Invoke to "Absolute" and Mode to "Toggle button" (preferred). +*** b) Set Invoke to "Trigger" and Mode to "Normal". +** Feedback is completely supported. +. Actions that take care of toggling themselves but _don't_ report on/off state. +** Example: "40175. Item properties: Toggle mute" +** Toggle behavior is achieved as described in (1) but support for toggling and feedback has limitations (explained + in (4)). +. Actions that don't take care of toggling themselves ("trigger only"). +** Example: "1007. Transport: Play" +** There's no way to make such an action toggle because the action is not designed to do so. +** If the action reports an on/off state, feedback is completely supported though, otherwise not at all! +. Actions that have a complete range of values as state. +** Example: "994. View: Adjust vertical zoom (MIDI CC/OSC only)" +** Since ReaLearn 2 and REAPER 6.20, there's special support for this type of actions. Starting from the first + time this action is triggered, ReaLearn will track its current value. +** That's why toggling is supported. Because ReaLearn itself takes care of toggling, you need to set _Invoke_ to + "Absolute" and Mode to "Toggle button". +** Feedback is also supported. +** Toggling/feedback for this type of actions comes with some inherent limitations that are related to the fact that + a) REAPER itself doesn't necessarily use actions to invoke its own functions and b) MIDI CC/OSC actions don't + have the concept of a "current value" (unlike e.g. toggle actions or FX parameters). +** The bottom line of these limitations is that toggling/feedback will only work if the action itself is used to + trigger the change and if the action is an absolute action (not relative). +** Limitations in detail: +... In most cases, feedback will not work when changing the value in REAPER directly (e.g. when adjusting + vertical zoom directly via the REAPER user interface). +... It will only work for actions that support some kind of absolute value range (usually the case for all + non-relative MIDI CC/OSC actions). +... When the action is invoked via ReaLearn, the feedback will only work if "Invoke" is "Trigger" or "Absolute". + It won't work with "Relative". +... When the action is invoked from ReaScript or other extensions, it will only work if the invocation was done + via `KBD_OnMainActionEx()` and an absolute value change. +... When the action is invoked via a native REAPER action mapping, it will only work if the invocation is done + using absolute MIDI CC/OSC (not relative). + +====== Project: Invoke transport action + +Invokes a transport-related action. + +* *Action:* Specifies which transport action should be invoked. +** *Play/stop:* Starts playing the containing project if the incoming absolute control value is greater than 0%, + otherwise invokes stop. +** *Play/pause:* Starts playing the containing project if the incoming absolute control value is greater than 0%, + otherwise invokes pause. +** *Stop:* Stops the containing project if the incoming absolute control value is greater than 0%. Useful for + distinguishing feedback between _paused_ and _stopped_ state. +** *Pause:* Pauses the containing project if the incoming absolute control value is greater than 0%. Useful for + distinguishing feedback between _paused_ and _stopped_ state. +** *Record:* Starts/enables recording for the current project if the incoming absolute control value is greater than + 0%, otherwise disables recording. +** *Repeat:* Enables repeat for the containing project if the incoming absolute control value is greater than 0%, + otherwise disables it. + +[#browse_tracks_target] +====== Project: Browse tracks + +Steps through tracks. To be used with endless rotary encoders or previous/next-style "Incremental buttons". + +* *Scroll TCP* and *Scroll mixer*: See <> target. +* *Scope:* Decides which tracks are considered and how. +** *All tracks:* Considers all tracks even those which are hidden. +** *Only tracks visible in TCP:* Considers only those tracks which are visible in the track control panel. +** *Only tracks visible in TCP (allow 2 selections):* Like "Only tracks visible in TCP" but makes it possible to have 2 selections. One for the MCP and one for the TCP. These selections can be moved independently. This can make sense if you have a bunch of tracks that you only show in the TCP and another separate bunch of tracks that you only show in the MCP. +** *Only tracks visible in MCP:* Considers only those tracks which are visible in the mixer control panel. +** *Only tracks visible in MCP (allow 2 selections):* See above. + +[#seek-target] +====== Project: Seek + +Allows you to use faders, knobs, encoders or incremental buttons to seek within portions of your project … +with feedback that indicates the current position! + +* *Feedback:* Determines how frequently ReaLearn captures feedback and sends it to your feedback output. +** *Beat:* Every beat. +** *Fast:* As fast as possible, thereby giving the satisfying feeling of continuity. This obviously uses some more + resources. No idea how far you can go with that. Try yourself. +* *Behavior:* Determines whether to use immediate or smooth seeking. +* *Seek play:* Doesn't just change the edit cursor but also changes the play position when the project is currently + being played. +* *Move view:* Allow to scroll / change viewport when seeking. + +The following options determine which time ranges will be taken into consideration as reference for seeking (control) +and feedback. + +. *Use time selection:* Can use the currently set time selection as reference. +. *Use loop points:* Can use the currently set loop points as reference. +. *Use regions:* Can use the current region as reference. +. *Use project:* Can use the complete project as reference, from start to end. + +If you don't tick any "Use" checkbox, ReaLearn will seek within the currently visible viewport. + +If you tick multiple options, this is the order of fallbacks: + +* If there's no time selection, the loop points will be used. +* If there are no loop points, the current region is used. +* If there's no current region, the project will be used. +* If the project is empty, the viewport will be used. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +|target.position.project_default | Position in the current transport time unit +|target.position.time | _minute:second.milli_ +|target.position.measures_beats_time | _measure.beat.milli_ +|target.position.measures_beats | _measure.beat.milli_ +|target.position.seconds | _second.milli_ +|target.position.samples | _sample_ +|target.position.hmsf | _hour:minute:second:milli_ +|target.position.absolute_frames | _frames_ +|target.position.project_default.mcu | Like `target.position.project_default` but tailored to Mackie Control timecode displays +|target.position.time.mcu | Like `target.position.time` but tailored to Mackie Control timecode displays +|target.position.measures_beats_time.mcu | Like `target.position.measures_beats_time` but tailored to Mackie Control timecode displays +|target.position.measures_beats.mcu | Like `target.position.measures_beats` but tailored to Mackie Control timecode displays +|target.position.seconds.mcu | Like `target.position.seconds` but tailored to Mackie Control timecode displays +|target.position.samples.mcu | Like `target.position.samples` but tailored to Mackie Control timecode displays +|target.position.hmsf.mcu | Like `target.position.hmsf` but tailored to Mackie Control timecode displays +|target.position.absolute_frames.mcu | Like `target.position.absolute_frames` but tailored to Mackie Control timecode displays +|=== + + +====== Project: Set playrate + +Sets REAPER's master playrate. + +*Attention:* This target doesn't currently work if the project containing ReaLearn is not the active project tab. + +[#project-set-tempo] +====== Project: Set tempo + +Sets REAPER's master tempo. + +This target is not learnable anymore via the "Learn target" button and also not eligible for +the <> target because it caused too many "false positives". + +[#marker-region-go-to] +====== Marker/region: Go to + +Navigates to a specific marker or region. Here's the behavior in detail: + +* Regions +** If the project is stopped, the editor cursor immediately jumps to the start position of the given region. +** If the project is playing, playback will continue with the given region as soon as the currently playing region + (or measure if not within a region) has finished playing. This is called "smooth seek". +** *Attention:* This currently doesn't work if the project containing ReaLearn is not the active project tab. +* Markers +** If the project is stopped, the editor cursor immediately jumps to the given marker. +** If the project is playing, playback will immediately be continued at the given marker. + +The cool thing about this target compared to REAPER's built-in actions is that it allows to target arbitrarily many +markers/regions (either by position or by ID) … and that it supports visual feedback! If you assign this target to a +button which has an LED, you will see which marker/region is currently playing just by looking at +your controller. + +Please note that this doesn't work when recording! + +User interface elements specific to this target: + +* *Marker/region:* +** *Left dropdown:* This dropdown lets you choose if you want to refer to a marker/region by its + user-assigned ID or by its position on the timeline. +** *Right dropdown:* This dropdown displays the markers or regions (depending on the _Regions_ checkbox state). +* *Now!:* This sets the target to the currently playing (or currently focused, if stopped) marker/region. +* *Behavior:* Determines whether to use immediate or smooth seeking. +* *Regions:* Switches between markers and regions. +* *Set loop points:* For regions, this will additionally set the loop points to the region start and end position. +* *Set time selection:* For regions, this will additionally set the time selection to the region start and end + position. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +|target.bookmark.id | (Numeric) ID of the bookmark +|target.bookmark.index | Index of the bookmark (counting both markers and regions) +|target.bookmark.index_within_type | Index of the bookmark (counting only markers or regions, respectively) +|target.bookmark.name | Name of the bookmark +|=== + +[#track-target] +====== Track + +A target that allows you to define a track. + +The setting **Act/Tags** stands for "Action / Instance tags" and decides what happens when a control messages arrives, e.g. a button press: + +- **None (feedback only):** With this setting, nothing will happen. It's suited very well as neutral target for textual feedback with an expression that contains a track property, e.g. `{{ target.track.name }}`. +- **Set (as instance track):** The button press will set the track defined in this target as <> _without resolving it before_. For example, if this target defines to use the currently selected track (<>), pressing the button will make the instance track dynamically reflect whatever track is selected. +- **Pin (as instance track):** The button press will resolve the track defined in this target and set the result as <>. For example, if this target defines to use the currently selected track, pressing the button will check which track is currently selected and set the instance track to exactly this track. It will stay that way even if the user selects another track. + +The text field to the right defines contains **Instance tags** of the ReaLearn instances whose instance track should be changed. If it's empty, the current instance will be affected. + +====== Track: Arm/disarm + +Arms the track for recording if the incoming absolute control value is greater than 0%, otherwise +disarms the track. This disables "Automatic record-arm when track selected". If you don't want that, +use the _Track: Select/unselect_ target instead. + +====== Track: Enable/disable all FX + +Enables all the track's FX instances if the incoming absolute control value is greater than +0%, otherwise disables them. + +====== Track: Enable/disable parent send + +Enables the parent send routing of the track if the incoming absolute control value is greater than 0%, otherwise disables it. + +====== Track: Mute/unmute + +Mutes the track if the incoming absolute control value is greater than 0%, otherwise unmutes the +track. + +====== Track: Peak + +This is a feedback-only target! It turns your feedback-capable controller into a VU meter by constantly reporting the +current volume of the configured track to it. + +In addition to connecting it with a LED ring or motor fader source (which should be obvious), it can also be used with +a single LED to build a clipping indicator: + +. Set _Target Min_ to the minimum dB value that should make your clipping LED turn on. Leave _Target Max_ at 12.00 dB. +. Make sure the _Out-of-range_ behavior is set to "Min or max". +. If you have an LED that supports multiple colors, you will probably see a rainbow of colors flashing up which can be + quite confusing. Use the feedback transformation formula `x = ceil(y)` to restrict the feedback to just two values: + Min (0%) or Max (100%). You can then use _Source Min_ and _Max_ to adjust the off/on LED colors. + +At the moment this target only reports peak volume, not RMS. + +====== Track: Phase invert/normal + +Inverts the track phase if the incoming absolute control value is greater than 0%, otherwise switches the track phase back to normal. + + +[#track-selectunselect] +====== Track: Select/unselect + +Selects the track if the incoming absolute control value is greater than 0%, otherwise unselects the +track. + +This target stops being learnable if you activate the REAPER preference +"Mouse click on volume/pan faders and track buttons changes track selection" (because this preference would generate +too many false positives). If you change the preference, ReaLearn will take it into consideration the next time +you restart REAPER. + +* *Scroll TCP:* Also scrolls the track control panel to the desired track. +* *Scroll mixer:* Also scrolls the mixer control panel to the desired track. + +====== Track: Set automation mode + +Sets the track to a specific automation mode if the incoming control value is greater than 0%, otherwise +sets it back to REAPER's default track automation mode "Trim/Read". + +* *Mode:* Here you can pick the desired automation mode. + +====== Track: Set monitoring mode + +Sets the track to a specific input monitoring mode if the incoming control value is greater than 0%, otherwise sets it back to "Off". + +* *Mode:* Here you can pick the desired monitoring mode. + +[#track-set-automation-touch-state] +====== Track: Set automation touch state + +When you use REAPER's "Touch" automation mode, REAPER needs a way to know if you are currently touching the control +element which is bound to the automation envelope or not. As long as you keep touching it, it will overwrite +existing automation. As soon as you release it, REAPER will leave the envelope untouched. + +Classical control surfaces implement this very intuitively by providing touch-sensitive faders. With this target, you +can easily reproduce exactly this behavior via ReaLearn. You do this by mapping the touch event (which is usually +nothing else than a MIDI note on/off message) to this target. The touch state is scoped to a particular track and +parameter type which you can choose in the **Type* dropdown. + +However, ReaLearn wouldn't be ReaLearn if it wouldn't allow you to let totally different sources take control of the +touch state. For example, if you have a push encoder, you could map the "push" event to the touch state, allowing you +to write automation only while you are touching the encoder. Or if you don't have a push encoder, you could just use +some spare button. + +====== Track: Set pan + +Sets the track's pan value. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +|target.pan.mcu | Pan value tailored to one line on a Mackie Control LCD +|=== + +====== Track: Set stereo pan width + +Sets the track's width value (applicable if the track is in stereo pan mode). + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +|target.width.mcu | Width value tailored to one line on a Mackie Control LCD +|=== + +====== Track: Set volume + +Sets the track's volume. + +====== Track: Show/hide + +Shows the track if the incoming absolute control value is greater than 0%, otherwise hides it. + +* *Area:* Lets you decide if you want it to show/hide in the track control panel or the mixer. + +[#track-solounsolo] +====== Track: Solo/unsolo + +Soloes the track if the incoming absolute control value is greater than 0%, otherwise unsoloes the +track. + +Provides the following additional settings: + +* *Behavior:* See the REAPER user guide for details. +** *Solo in place:* Soloes the track while respecting REAPER's routing. This is REAPER's default and since + ReaLearn v2.4.0 also ReaLearn's default. +** *Solo (ignore routing):* Soloes the track muting everything else, no matter the routing. +** *Use REAPER preference:* Follows whatever is set in the REAPER preferences. + +Learning this target by pressing the "Solo" button of the _master_ track is currently not possible but +of course you can just select it manually in the dropdown menu. + +====== FX chain: Browse FXs + +Steps through the FX instances in the FX chain by always having exactly one FX instance visible. +To be used with endless rotary encoders or previous/next-style "Incremental buttons". + +* *Display:* Here you can decide if you want to display the FX as part of the FX chain or in a dedicated floating + window. + + +[#fx-target] +====== FX + +A target that allows you to define an FX, in its basic variant perfect for acquiring feedback for a specific FX. + +The setting **Act/Tags** allows you to optionally set/pin the declared FX as <>. This works pretty much the same as described in target <>. + +[#fx-enabledisable] +====== FX: Enable/disable + +Enables the FX instance if the incoming absolute control value is greater than 0%, otherwise +disables it. + +====== FX: Set online/offline + +Sets the FX instance online if the incoming absolute control value is greater than 0%, otherwise +sets it offline. + +[#fx-load-snapshot] +====== FX: Load snapshot + +Restores a certain state of a particular FX. Before using this target, you need to take a snapshot of the desired FX +state using the _Take!_ button. This snapshot will be saved as part of ReaLearn's state itself and as a direct +consequence as a part of your project. This makes your project nicely self-contained. It's perfect for activating +particular FX presets because it will always restore the desired state, even if the preset list has changed. + +This target supports feedback, but only if the snapshot is loaded via ReaLearn itself. + +Please note that some plug-ins have _very large_ states. Therefore you should keep an eye on the snapshot size, which +will be displayed once you take the snapshot. ReaLearn's own state will grow with every new snapshot mapping, so this +can quickly add up and make REAPER/ReaLearn slow! + +[#fx-browse-presets] +====== FX: Browse presets + +Steps through FX presets. + +This target is suited for use with knobs, encoders and incremental buttons (previous/next) because it allows +you to step through the complete preset list. The minimum value always represents _No preset_ whereas the +maximum value always represents the last available preset. + +It's _not_ suited for activating a particular preset (e.g. by setting _Target Min_ and _Max_ to the same value), +because the preset list of an FX is usually not constant. As soon as you modify the preset list, this value will might +suddenly point to a completely different preset. Even worse, the actual preset might have been deleted. + +If you want to activate a particular preset, please use the <> target +instead. + +====== FX: Open/close + +Makes the FX instance visible if the incoming control value is greater than 0%, otherwise hides it. + +* *Display:* Here you can decide if you want to display the FX as part of the FX chain or in a dedicated floating + window. + +====== FX parameter: Set automation touch state + +This does the same as <> but for FX parameter value changes. + + +[#fx-set-parameter-value] +====== FX parameter: Set value + +Sets the value of a particular track FX parameter. + +* *Parameter:* The parameter to be controlled. Please note that both <> and <> address the FX by its position in the FX chain. The difference between the two is that <> shows a dropdown containing the available parameters and <> lets you enter the position as a number in a text field. Latter is useful if at the time of choosing the position, the FX is not available. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +| +target.fx_parameter.index +| +Zero-based index of the resolved FX parameter. + +| +target.fx_parameter.name +| +Name of the resolved FX parameter. + +| +target.fx_parameter.macro.name +| +Name of the corresponding Pot macro parameter. Only works if this parameter is part of a preset loaded via Pot. + +| +target.fx_parameter.macro.section.name +| +Name of the corresponding Pot macro parameter section. Only works if this parameter is part of a preset loaded via Pot. + +| +target.fx_parameter.macro.section.index +| +Zero-based index of the corresponding Pot macro parameter section (within the current bank). Only works if this parameter is part of a preset loaded via Pot. + +| +target.fx_parameter.macro.new_section.name +| +Name of the corresponding Pot macro parameter section, but only if this parameter marks the start of a new section. Only works if this parameter is part of a preset loaded via Pot. + +| +target.fx_parameter.macro.bank.name +| +Name of the corresponding Pot macro parameter bank. Only works if this parameter is part of a preset loaded via Pot. +|=== + +[#pot-browse-filter-items] +====== Pot: Browse filter items + +This target can be used to filter the potentially very large collection of presets in <>. The idea is to map this target to an endless rotary encoder or previous/next buttons (using <> mode) and then navigate within the available filter items, e.g. instruments or banks. + + +* *Kind:* Choose the kind of filter items that you want to browse. They correspond to the filters available in <>. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +| +target.item.name +| +Name of the filter item. + +| +target.item.parent.name +| +Name of the parent filter item if there's any. E.g. the instrument to which a bank belongs or the type to which a sub type belongs. +|=== + +[#pot-browse-presets] +====== Pot: Browse presets + +Use this target to browse a collection of presets. By default, this is the complete collection of presets available in all supported databases, so potentially thousands of presets. If you want to browse just a subset, see <>. + +The idea is to map this target to an endless rotary encoder or previous/next buttons (using <> mode) and then navigate within the available presets. Once you have selected a preset, you can audition it via <> (if it's a sound preset) and load it via <>. + +This target supports the following additional placeholders in textual feedback expressions: + +[cols="m,1"] +|=== +| +target.preset.name +| +Name of the preset. + +| +target.preset.product.name +| +Name of the product to which this preset belongs, if available. + +| +target.preset.file_ext +| +File extension of the preset, in case it's a file-based preset. + +| +target.preset.author +| +Name of the preset author, if available. + +| +target.preset.vendor +| +Name of the preset vendor, if available. + +| +target.preset.comment +| +Preset comment, if available. + +|=== + +[#pot-preview-preset] +====== Pot: Preview preset + +Auditions a preset selected via <>. Only works if it's a sound preset and a sound preview file is available. + +[#pot-load-preset] +====== Pot: Load preset + +Loads a preset selected via <>. + +NOTE: This needs at least REAPER version 6.69+dev1030! Also, it only works if you have the VST2/VST2i version of the corresponding plug-in installed. +- *NKS audio file presets:* Loading supported via ReaSamplOmatic5000 + +Settings: + +* *Track/FX:* You must tell the target at which FX slot to load the corresponding plug-in. The best idea is to use FX selector <>. Selectors such as <> or <> are not suited because the target might replace the plug-in with another one, in which the unique FX ID and the FX name can change. Then the target would turn inactive and stop working. + +This target supports the same additional placeholders for textual feedback expressions as <>. The only difference is that the ones in <> relate to the currently loaded preset, not the one that's selected in the preset browser. + +====== Send: Automation mode + +Sets the track send to a specific automation mode if the incoming control value is greater than 0%, otherwise sets it back to REAPER's default automation mode "Trim/Read". + +====== Send: Mono/stereo + +Sets the track send to mono or back to stereo. + +====== Send: Mute/unmute + +Mutes/unmutes the track send. + +====== Send: Phase invert/normal + +Inverts the track send phase or switches it back to normal. + +====== Send: Set automation touch state + +This does the same as <> but for send volume or pan adjustments. + + +====== Send: Set pan + +Sets the track send's pan value. + +====== Send: Set volume + +Sets the track send's volume. + +====== Clip: Invoke transport action + +CAUTION: Clips are a highly experimental feature of ReaLearn and still subject to many changes! Better don't rely on it at the moment! + +_Under construction_ + +====== Clip: Seek + +_Under construction_ + +====== Clip: Volume + +_Under construction_ + +[#midi-send-message] +====== MIDI: Send message + +Sends arbitrary MIDI messages (also sys-ex!) in response to incoming messages. This target turns ReaLearn into +a capable and convenient MIDI → MIDI and OSC → MIDI converter. + +* *Output:* Where to send the MIDI message. +** *FX output:* Sends the MIDI message to the output of this ReaLearn instance - which usually means it flows + into the FX below ReaLearn, e.g. a VST instrument. ++ +** *Feedback output:* Sends the MIDI message to the device which is set as _output_. +* *Pattern:* Defines the MIDI message to be sent as a sequence of bytes in hexadecimal notation. It also allows you + to encode the incoming _absolute_ control value as part of the message (after it has been processed by the glue + section). The syntax for doing this takes some getting used to but it's very flexible. It's exactly the same syntax as + used in the <>. + Please read about it there! +* *...* Primarily provides predefined patterns. Just pick one here, set the destination to "Feedback output" and + add a "ReaControlMIDI" FX below to see which messages ReaLearn sends. + +[NOTE] +==== +This target is a bit special in that it carries out its processing logic exclusively in the audio thread if it's controlled by a MIDI source. This has the big advantage that receiving and producing MIDI messages happens in one go (without inter-thread-communication latency), which is often important when using MIDI message conversion. + +However, this also means that the following things won't work when controlling this target using MIDI: + +* It can't take the lead in <>. +* If _output_ is set to ``, additional limitations apply: +** It can't act as a follower in <>, either. +** It can't participate in <>. +==== + +[#osc-send-message] +====== OSC: Send message + +Sends OSC messages with up to one argument in response to incoming messages. This target turns ReaLearn into +a capable and convenient MIDI → OSC and OSC → OSC converter. If an argument number is entered (e.g. `1`), +it will encode the incoming absolute control value as that argument (after it has been processed by the glue section). + +* *Output:* Where to send the OSC message. +** *<Feedback output>:* Sends the OSC message to the device which is set as _Output_. Of course this only + works if it's an OSC device. +** *_Specific device:_* Sends the OSC message to a specific device. +* *Address*, *Argument* and *Range*: These correspond to the identically named settings of <>. + Check that section for details. + +[#realearn-enable-disable-instances] +====== ReaLearn: Enable/disable instances + +This target allows you to flexibly enable or disable other ReaLearn instances based on their tags: + +* *Exclusivity* +** *Non-exclusive:* If the incoming control value is greater than 0%, all matching ReaLearn instances will be enabled (on top of the already enabled instances). If the value is 0%, all matching ReaLearn instances will be disabled. +** *Exclusive:* If the incoming control value is greater than 0%, all matching ReaLearn instances will be enabled and all non-matching ones will be disabled. If the value is 0%, it's exactly the opposite (react to button <> if you don't want this to happen). +** *Exclusive (on only):* Variation of _Exclusive_ that applies exclusivity only if the incoming control value is greater than 0%. +* *Tags:* A ReaLearn instance matches when it is tagged with any of the tags entered in this field (comma-separated). + +TIP: Use the main panel to assign tags to a ReaLearn instance. + +Please note: + +* This really affects other ReaLearn instances only, not _this_ instance. +* ReaLearn instances without tags won't be affected at all. +* Only affects instances in the same project. If _this_ ReaLearn instance is on the monitoring FX chain, it only affects other instances in the monitoring FX chain. + +TIP: This target is great for switching between completely different controller setups! + +[#realearn-dummy-target] +====== ReaLearn: Dummy target + +This target simply does nothing when invoked and also doesn't provide any meaningful feedback on its own. + +It's sometimes useful to have such a dummy target, e.g. combined with <>. Or if you want to use ReaLearn as a MIDI filter which just "eats" an incoming MIDI message. Or if you want to send some text feedback to a hardware display, if the text is just a constant string or uses a placeholder that doesn't need target context. + +[#realearn-enable-disable-mappings] +====== ReaLearn: Enable/disable mappings + +This target allows you to flexibly enable or disable other mappings in this instance based on their tags: + +* *Exclusivity* +** *Non-exclusive:* If the incoming control value is greater than 0%, all matching mappings will be enabled (on top of the already enabled mappings). If the value is 0%, all matching mappings will be disabled. +** *Exclusive:* If the incoming control value is greater than 0%, all matching mappings will be enabled and all non-matching ones will be disabled. If the value is 0%, it's exactly the opposite (react to button <> if you don't want this to happen). +** *Exclusive (on only):* Variation of _Exclusive_ that applies exclusivity only if the incoming control value is greater than 0%. +* *Tags:* A mapping matches when it is tagged with any of the tags entered in this field (comma-separated). + +TIP: Use the text field on the top of the mapping panel to assign tags to a mapping. + +Please note: + +* This really affects other mappings only, not _this_ mapping. +* Mappings without tags won't be affected at all. + +TIP: This target is a straightforward alternative to <>, especially when it comes to bank switching! + +[#realearn-load-mapping-snapshot] +====== ReaLearn: Load mapping snapshot + +Restores target values for all or certain mappings in this ReaLearn instances. + +* *Snapshot:* Choose the snapshot that you want to load. +** *:* Restores the initial target values for the mappings. +** *By ID:* Restores target values contained in a snapshot that was taken via <>. Simply enter the corresponding ID here. +* *Default:* Allows you to define a default target value to restore for each participating mapping whenever the snapshot either doesn't exist or doesn't contain a value for that mapping. If that participating mapping has <> checked, the inverse of the default value will be loaded. +* *Tags:* Allows you to restrict the set of mappings whose target values will be restored. +** If this field is empty, target values of all mappings will be restored. +** If this field contains tags (comma-separated), target values will be restored only for mappings that are tagged with any of these. +* *Active mappings only:* By default, even target values for inactive (but control-enabled) mappings are restored! If you don't like that, tick this checkbox. + +Please note: + +* Mappings for which control is not enabled, never participate in snapshotting. +* Some targets don't report values and therefore don't participate in snapshotting. +* Feedback of this target indicates whether the desired snapshot is the one which has last been loaded (for the given tags). + +====== ReaLearn: Modify mapping + +Triggers a modification of another ReaLearn mapping. + +* *Kind:* The kind of modification. +** *Learn target:* Switches "Learn target" on or off for the destination mapping. Use button kbd:[...] to pick the considered target types and invocations to be included in the learning process. +** *Set target to last touched:* Sets the target of the destination mapping to the last-touched target. Use button kbd:[...] to pick the considered target types and invocations. +* *Instance:* Allows you to pick another ReaLearn instance. +* *Mapping:* Allows you to pick the destination mapping. + +TIP: This target is great to "pin" targets to certain control elements on demand. + +[#realearn-take-mapping-snapshot] +====== ReaLearn: Take mapping snapshot + +Memorizes target values for all or certain mappings in this ReaLearn instances and saves them in a snapshot of your choice. + +* *Snapshot*: Choose the snapshot to which you want to save the mapping values. +** *:* Always chooses the snapshot which is currently active (was last loaded) for the given tags. ++ +TIP: Only works if tags are not empty and if all tags have the same last-loaded snapshot. So the best is if you always enter exactly one tag. +** *By ID:* Enter the unique ID of the snapshot, e.g. `scene_1`. +* *Tags:* Allows you to restrict the set of mappings whose target values will be memorized. +** If this field is empty, target values of all mappings will be memorized. +** If this field contains tags (comma-separated), target values will be memorized only for mappings that are tagged with any of these. +* *Active mappings only:* By default, even target values of inactive (but control-enabled) mappings end up in the snapshot! If you don't like that, tick this checkbox. + +[#realearn-browse-group-mappings] +====== ReaLearn: Browse group mappings + +This target lets you choose an arbitrary mapping group in this compartment and cycle through it with an encoder/fader/knob or incremental (previous/next) buttons. + +"Cycling through" means that you move from one mapping in the group to the next one by hitting the next mapping's target with the _Target Max_ value in its glue section (by default 100%). + +* *Group:* The group that you want to browse. +* *Exclusivity* +** *Non-exclusive:* Really just hits the target of the mapping which is next in the line and doesn't do anything with the other mappings. In many cases this is enough, e.g. if the targets of the mappings in the cycled group are the same and just "Target Max" is different. Or if the target itself already takes care of exclusivity. +** *Exclusive:* Doesn't just hit the target of the mapping which is next in the line but also hits the targets of all other mappings in the cycled group with their respective _Target Min_ value (by default 0%). Be careful with this, you often won't need it. + +Please note: + +- Inactive mappings are skipped. + +[TIP] +==== +Mapping lend themselves perfectly for defining things that should happen _in sequence_. This target allows you to take advantage of that! + +- Combine it with <> to browse different banks. +- Combine it with <> to browse completely different controller setups (or banks). +- Combine it with targets that don't provide a "Browse ..." variant themselves. +- Use it as an alternative to <> that allows you to have completely different targets within one sequence. +==== + +[#virtual-target] +===== Category "Virtual" + +This is exactly the counterpart of the possible <>. Choosing a virtual target here is like +placing cables between a control element and all corresponding main mappings that use this +virtual control element as source. + +[#glue] +==== Glue + +As mentioned before, the glue section defines the glue between a source and a target. It's divided into +several sub sections some of which make sense for all kinds of sources and others only for some. + +*At first something important to understand:* Since ReaLearn 2, a mapping can deal with both _absolute_ +and _relative_ values, no matter what's set as _Mode_! ReaLearn checks the type of each emitted source value +and interprets it correctly. The _Mode_ dropdown has been sort of "degraded" because now it only applies to +incoming _absolute_ values and determines how to handle them (see further below). This change has been made +to support virtual sources - because virtual sources can be either absolute or relative depending on the current +controller mappings. ReaLearn allows you to prepare your mapping for both cases by showing all possible settings. + +_Relative_ means that the current target value is relevant and the change of the target value is calculated in +terms of increments or decrements. Control elements that can emit relative values are rotary encoders and +virtual multis. + +Having so many settings available at the same time can be a bit daunting. ReaLearn helps you by hiding settings +that don't make sense in the current context. It shows or hides them based on the following criteria: + +* Is control and/or feedback enabled for the mapping? +* What are the characteristics of the source and target? +* What's the current setting of _Absolute mode_ and _Make absolute_? + +===== Reset to defaults + +Resets the settings to some sensible defaults. + +===== Reverse + +If checked, this inverses the direction of the change. E.g. the target value will + decrease when moving the fader upward and increase when moving it downward. + +[#target-min-max] +===== Target Min/Max + +The controlled range of absolute target values. This enables you to "squeeze" +target values into a specific value range. + +Example: If you set this to "-6 dB to 0 dB" for a _Track +volume_ target, the volume will always stay within that dB range if controlled via this mapping. +It wouldn't prevent the volume from exceeding that range if changed e.g. in REAPER itself. + +This +setting applies to targets which are controlled via absolute control values (= all targets with +the exception of the <> if invocation type is _Relative_). + +These are relevant for the control direction only: + +[#target-value-sequence] +===== Value sequence + +Allows you to define a specific sequence of target values. This is a very powerful feature because + you not only can enter single values but also ranges with customizable step sizes! Plus, it has support for true + relative control. All values are entered comma-separated and using the unit chosen in the <>. + Just click the unit button to switch between the native target unit and percentages. + +Examples: + +* `-20, -14, -12, -6, -3.5, 0`: Enter this sequence for a volume target with target unit switched to dB. When you + move your knob or rotary encoder or press a button using _Incremental button_ mode, ReaLearn will step through + the entered dB values for you. +* `10 - 30, 50 - 70 (5), 80 - 90 (2)`: Enter this sequence for a target with a continuous value range and target + unit switched to %. It will first step in 1% steps from 10% to 30%, then in 2% steps from 50% to 70% and finally + from 80% to 90% in 2% steps. At the moment it's important that the numbers and the range dash are separated by + spaces! +* `20, 10, 10, -5, 8`: When using absolute control, even duplicate values and direction changes are respected, as + seen in this value sequence. However, true relative control naturally supports continuous sequences only. So if + you have a rotary encoder that sends relative messages (hopefully!) or use incremental buttons, the sequence will + be stepped through in a continuous manner (-5, 8, 10, 20). The benefit as always: No parameter jumps! If you want + to use non-continuous sequences with encoders or incremental buttons, you can always use _Make absolute_! + +[#group-interaction] +===== Group interaction + +Lets you control not just _this_ mapping but also _all other mappings in the same mapping + group_. Very powerful feature! + +TIP: If you want to control _other_ mappings only and not _this_ mapping, just pick + a target that doesn't have any effect, for example the <> or an unused internal ReaLearn compartment parameter + (target <> with FX set to ``). + +* *None:* Switches group interaction off. This is the default. Incoming control events will just affect _this_ + mapping, not others. +* *Same control:* This will broadcast any incoming control value to all other mappings in the same group. The + glue section of this mapping will be ignored when controlling the other mappings. The glue sections of the + other mappings will be respected, including the source min/max setting. +* *Same target value:* This will set the target value of each other mapping in the same group to the target value + of this mapping. Nice: It will respect the target min/max setting of both this mapping and the other mappings. + All other settings of the glue section will not be processed. Needless to say, this kind of control is always + absolute, which means it can lead to parameter jumps. Therefore, it's most suited for on/off targets. If you don't + like this, choose _Same control_ instead. +* *Inverse control:* This is like _Same control_ but broadcasts the _inverse_ of the incoming control value. +* *Inverse target value:* This is like _Same target value_ but sets the target values of the other mappings to the + _inverse_ value. This is very useful in practice with buttons because it essentially gives you exclusivity within + one group. It's a great alternative to the _Exclusive_ setting which is available for some targets. Unlike the + latter, _Inverse target value_ allows for exclusivity between completely different target types and completely + custom groupings - independent of e.g. organization of tracks into folders. +* *Inverse target value (on only):* Variation of _Inverse target value_ that applies the inverse only when the target value is > 0%. +* *Inverse target value (off only):* Variation of _Inverse target value_ that applies the inverse only when the target value is 0%. + +===== Feedback type + +Determines whether to send numeric, textual or dynamic feedback to the source. + +====== Numeric feedback: EEL transformation + +Sends numeric feedback to the source. This is the default. + +The text field below allows you to specify an optional feedback transformation formula, which is much like <> but used for translating a target value back to a source value for feedback purposes. Be aware: Here `x` is the desired source value (= output value) and `y` is the current target value (= input value), so you must assign the desired source value to `x`. + + + +Example: `x = y * 2`. + +ReaLearn's feedback processing order is like this since version 2: +.. Apply target interval. +.. Apply reverse. +.. Apply transformation. +.. Apply source interval. + +[#textual-feedback] +====== Textual feedback: Text expression + +With this option, ReaLearn will send textual feedback values to the source. This only works with sources that are capable of displaying text: That is any <> with argument type _String_, <> and <>. + +The field below contains the _textual feedback expression_. Here you define which text is going to be sent to the source _whenever the target value changes_ and also - for your convenience - immediately at the moment of entering the text. Whatever text you enter here, will be sent verbatim to the source. + +Of course, entering a fixed text here is not very exciting. Most likely you want to display dynamic text such as the name of the currently selected track or the current target value, nicely formatted! You can do that by using placeholders, delimited by double braces. Example: `{{target.text_value}}`. + +Which placeholders are available, depends very much on the type of the mapping target. However, there are some which are available for (almost) any target: + +[cols="m,1"] +|=== +| +global.realearn.time +| +Time in milliseconds since ReaLearn has been loaded (the first instance). + +| +mapping.name +| +Name of the mapping. Contains the explicitly assigned mapping name, never an automatically generated one. + +| +target.text_value +| +Short text representing the current target value, including a possible unit. + +If the target value can be represented using some kind of name, this name is preferred over a possibly alternative numeric representation. Example: Let's assume the 4th track in our project is called "Guitar" and the mapping target is <>. Then `target.text_value` contains the text _Guitar_, not the text _4_. + +This is the default value shown if textual feedback is enabled and the textual feedback +expression is empty. + +| +target.available +| +A boolean value indicating whether the target is currently available or not. + +Most targets that are _active_ are also _available_. But some targets can be _active_ and _unavailable_. Example: <> is not _available_ if no preview is available for the preset currently selected in Pot browser. But the target is still considered _active_ in this case! + +Usually used together with <>, for example in order to display different things on displays depending on the target's availability. + +| +target.discrete_value +| +The current target value as zero-based integer. This only works for discrete targets. + +| +target.discrete_value_count +| +The number of possible values in the current target. This only works for discrete targets. + +| +target.numeric_value +| +The current target value as a "human-friendly" number without its unit. + +The purpose of this placeholder is to allow for more freedom in formatting numerical target values than +when using `target.text_value`. Future versions of ReaLearn might extend textual feedback +expressions in a way so the user can define how exactly the numerical value is formatted +(e.g. the number of digits after the decimal point). + +| +target.numeric_value.unit +| +Contains the unit of `target.numeric_value` (e.g. _dB_). + +| +target.normalized_value +| +The current target value as normalized number, that is, a value between 0.0 and 1.0 (the so-called unit interval). You can think of this number as a percentage, and indeed, it's currently always formatted as percentage. + +This value is available for most targets and good if you need a totally uniform and predictable representation of the target value that doesn't differ between target types. + +By default, this number is formatted as percentage _without_ the percent sign. Future versions of ReaLearn might offer user-defined +formatting. This will also be the preferred form to format on/off states in a +custom way (where 0% represents _off_). + +| +target.type.name +| +Short name representing the type of the mapping target. + +| +target.type.long_name +| +Long name representing the type of the mapping target. + +| +target.track.index +| +Zero-based index of the first resolved target track (if supported). + +| +target.track.name +| +Name of the first resolved target track (if supported). + +| +target.fx.index +| +Zero-based index of the first resolved target FX (if supported). + +| +target.fx.name +| +Name of the first resolved target FX (if supported). + +| +target.route.index +| +Zero-based index of the first resolved target send/receive/output (if supported). + +| +target.route.name +| +Name of the first resolved target send/receive/output (if supported). +|=== + +For target-specific placeholders, please look up the corresponding <> section. + +[#dynamic-feedback] +====== Dynamic feedback: Lua script + +This feedback type puts you fully in charge about which feedback to send to the source. It does so by letting you define a Lua script that builds numeric, textual or even arbitrarily structured feedback. + +*General mechanics* + +* ReaLearn executes your script whenever one of the ReaLearn-provided properties used in your script might have changed its value. +* The script receives an input and must produce an output. +* *Script input* +** The input is a function `context.prop` which you can use to query arbitrary properties, e.g. target or mapping properties. Those properties are the very same properties that you can use in <>. +** Here's an example how to use this function: ++ +[source,lua] +---- +local preset_name = context.prop("target.preset.name") +local param_name = context.prop("target.fx_parameter.name") +---- ++ +** Values returned by this function can be `nil`! E.g. target-related properties return a `nil` value whenever the mapping or target turns inactive, which is a very common situation. So it's important to prepare your Lua code for that, otherwise script execution fails and no feedback will be sent. One way to deal with a `nil` value returned by `context.prop` is to also return `nil` as `value` (see below). +* *Script output* +** The output that the script is supposed to return is a table which looks as in the following example: ++ +[source,lua] +---- +return { + feedback_event = { + -- The feedback value <1> + value = "Arbitrary text", + -- An optional color <2> + color = { r = 0, g = 255, b = 0 }, + -- An optional background color <3> + background_color = nil, + } +} +---- +<1> In this example it's a text value, but it can be anything! +<2> Has the same effect as color in <> +<3> Has the same effect as background color in <> ++ +** The most important thing here is `value`. It can either be ... +*** ... a string (ideal for display sources) +*** ... a number (ideal for LEDs and motor faders) +*** ... `nil` (which means "turn the source off", e.g. turn off the LED, turn down the motorfader, clear the display text) +*** ... or anything else (`true`, `false` or an arbitrary table ... at the moment, this is only useful for the <> source because other sources don't know how to deal with it) +* *Examples* +** Displays the number of milliseconds passed since ReaLearn was loaded: ++ +[source,lua] +---- +local millis = context.prop("global.realearn.time") +return { + feedback_event = { + value = "" .. millis .. "ms" + }, +} +---- ++ +** Creates an animation to make a long FX name visible on a tiny screen. ++ +[source,lua] +---- +function create_left_right_animation(global_millis, max_char_count, frame_length, text) + if text == nil then + return nil + end + if #text > max_char_count then + local frame_count = #text - max_char_count + local frame_index = math.floor(global_millis / frame_length) % (frame_count * 2) + local text_offset + if frame_index < frame_count then + text_offset = frame_index + else + local distance = frame_index - frame_count + text_offset = frame_count - distance + end + return text:sub(text_offset + 1, text_offset + max_char_count) + else + return text + end +end + +-- The maximum number of characters we want to display at once +local max_char_count = 10 +-- How many milliseconds to remain in one position +local frame_length = 150 +local millis = context.prop("global.realearn.time") +local fx_name = context.prop("target.fx.name") +local animation = create_left_right_animation(millis, 10, frame_length, fx_name) +return { + feedback_event = { + value = animation + }, +} +---- ++ +** Returns a structured feedback value ... ++ +[source,lua] +---- +return { + feedback_event = { + value = { + available = context.prop("target.available"), + more_info = { + index = context.prop("target.discrete_value"), + count = context.prop("target.discrete_value_count"), + }, + } + }, +} +---- +** ... which can then be processed by a <> source (this example is not realistic, it just shows how you can access the value table returned by the glue section feedback script): ++ +[source,lua] +---- +return { + address = 0x4bb0, + messages = { + { 0xb0, 0x4b, y.more_info.index, y.more_info.count } + } +} +---- + + + +[#feedback-style] +===== Feedback style + +The ... button provides options to change the _feedback style_. At the moment, it's all about setting colors. + +TIP: If you use <>, changes made here don't have any effect because you are supposed to provide style properties as part of the Lua script result (which is much more flexible). + +* *Color / Background color:* With this you can define the color and background color of the displayed text. Of course this will only work if the display source supports it! +** *:* Chooses the default color, that is the one which is preferred for the corresponding controller and display type. +** *:* Opens a color picker so you can choose the color of your choice. +** *_Property name_:* Maybe you don't want a fixed color but a dynamic one that changes whenever your target changes. Choose one of the following properties to make that happen: ++ +[cols="m,1"] +|=== +| +target.track.color +| +Custom color of the first resolved target track (if supported). + +| +target.bookmark.color +| +Custom color of the resolved marker or region. + +Only works with the <> target. +|=== + + +===== Source Min/Max + +The observed range of absolute source control values. By restricting that + range, you basically tell ReaLearn to react only to a sub range of a control element, e.g. only + the upper half of a fader or only the lower velocity layer of a key press. In relative mode, this + only has an effect on absolute source control values, not on relative ones. This range also + determines the minimum and maximum feedback value. + + +===== Out-of-range behavior + +This determines ReaLearn's behavior if the source value is not within + "Source Min/Max" or the target value not within "Target Min/Max". There are these variants: + +|=== +| | *Control direction (absolute mode only)* | *Feedback direction* +| *Min or max* | If the source value is < _Source Min_, ReaLearn will behave as if _Source Min_ was received (or 0% if _Source Min_ = _Source Max_). + +If the source value is > _Source Max_, ReaLearn will behave as if _Source Max_ was received (or 100% if _Source Min_ = _Source Max_). | If the target value is < _Target Min_, ReaLearn will behave as if _Target Min_ was detected (or 0% if _Target Min_ = _Target Max_). + +If the target value is > _Target Max_, ReaLearn will behave as if _Target Max_ was detected (or 100% if _Target Min_ = _Target Max_). + + | *Min* | ReaLearn will behave as if _Source Min_ was received (or 0% if _Source Min_ = _Source Max_). | ReaLearn will behave as if _Target Min_ was detected (or 0% if _Target Min_ = _Target Max_). Useful for getting radio-button-like feedback. + + | *Ignore* | Target value won't be touched. | No feedback will be sent. +|=== + + +===== Mode ("Absolute mode") + +Let's you choose an _absolute mode_, that is, the way incoming absolute source values are handled. + +TIP: Not all modes make sense at all times! It mostly depends on the character of the source. If a mode doesn't make sense given the current source, it will be marked as `NOT APPLICABLE`. In this case, you should choose another mode or change the source. + +====== Normal + +Takes and optionally transforms absolute source control values _the normal way_. _Normal_ means that +the current target value is irrelevant and the target will just be set to whatever absolute control value is +coming in (potentially transformed). + +[#incremental-button] +====== Incremental button + +With this you can "go relative" without having encoders, provided your control elements +are buttons. Let's assume you use the _MIDI Note velocity_ and select _Incremental button_ mode. +Then it works like this: Each time you press the key, the target value will increase, according to the mode's +settings. You can even make the amount of change velocity-sensitive! If you want the target value to decrease, +just check the _Reverse_ checkbox. + +====== Toggle button + +Toggle button mode is used to toggle a target between on and off states. It only makes sense for momentary buttons (which fire a value > 0 on each press). + +Here's how it works in detail: + +* If the current target value is within the first half of the target min/max range, it's considered as _off_ and will therefore be switched _on_ (set to _target max_). If it's within the second half, it's considered as _on_ and will therefore be switched _off_ (set to _target min_). +* It works a bit differently if _target min_ and _target max_ have the same value (which is a common technique to set the target to a specific value on the press of a button). Instead of toggling between _target min_ and _target max_, this mode now toggles between this specific value (= _target min_ = _target max_) and 0%. This is useful whenever you have a set of buttons each of which sets the same target to a different value, and you want them to toggle between the specified value and an initial value (0%). + +This mode is not supported for controller mappings that have a virtual target. + +[TIP] +==== +Sometimes the controller itself provides a toggle mode for buttons. *Don't use it!* + +Always set up your controller buttons to work in momentary mode! It's impossible for the controller +to know which state (on/off) a target currently has. Therefore, if you use the controller's built-in +toggle function, it's quite likely that it gets out of sync with the actual target state at some point. + +ReaLearn's own toggle mode has a clear advantage here. +==== + +[#make-relative] +====== Make relative + +This converts incoming absolute fader/knob movements into relative adjustments of the target value. It somewhat resembles takeover mode <> but has important differences: + +- It's guaranteed that a full fader/knob swipe from 0% to 100% always results in a swipe over the full target range (assuming the target was at 0% initially). +- It doesn't need to know the current target value. Which means it also works for mappings with <>. + +[#performance-control] +====== Performance control + +This mode emulates the behavior of a typical soft synth modulation matrix mapping: It uses the target value that has been set in REAPER (not via this ReaLearn mapping) as an offset and starts changing it from there. + +[#takeover-mode] +===== Takeover mode + +If you are not using motorized faders, absolute mode is inherently prone to +parameter jumps. A parameter jump occurs if you touch a control element (e.g. fader) whose +position in no way reflects the current target value. This can result in audible jumps because the +value is changed abruptly instead of continuously. You can deal with this by setting the right takeover mode. + +ReaLearn provides multiple takeover modes that decide how to deal with situations when a target parameter +jump would occur. + +====== Off + +The default settings: Jumps allowed. + +[#pick-up] +====== Pick up + +This is the same as "Soft takeover" in REAPER's built-in MIDI learn. It prevents jumps by not changing the target value until your control element reaches it. + +In certain cases, this mode can cause the target value to get stuck. This happens with faders/knobs that cause jumps themselves when moved very rapidly. If you don't like that, you might want to try <>. + +[#pick-up-tolerant] +====== Pick up (tolerant) + +This is like <> but makes extra sure that the target value doesn't get stuck. + +However, unlike <>, this mode will jump if you cause a jump on your controller! Imagine using a touch strip. This kind of control element allows you to jump to arbitrary values at any time. Tolerant mode will not prevent this kind of jumps! + + +====== Long time no see + +This is similar to <> with the difference that the current target value + will gradually "come your way". This results in seamless and fast reunification of control and target + value but it can feel weird because the target value can temporarily move in the opposite + direction of the fader movement. In older ReaLearn versions this was called "Slowly approach if jump too big". + +[#takeover-mode-parallel] +====== Parallel + +With this mode, the target will simply follow your fader moves, in exactly the same tempo - + without any scaling. Reunification only happens when both control and target value meet at the "borders". + + +====== Catch up + +This mode is sometimes called "Proportional" or "Value scaling" mode. It's like "Parallel" mode + but the target value is allowed to move slower than the control value - hence the control can catch up (converge) faster. + +[#control-transformation] +===== Control transformation (EEL) + +This feature allows you to write a formula that transforms incoming control values. + +While very powerful because it allows for arbitrary transformations (velocity curves, + random values - you name it), it's not everybody's cup of tea to write something like that. The formula must be written in the language https://www.cockos.com/EEL2/[EEL2]. Some REAPER power users might be familiar with it because REAPER's JSFX uses the same language. + +Luckily, ReaLearn has a fancy editor which visualizes the formula and has some predefined templates built-in (available on Windows and macOS only at the moment). Press the "*...*" button to open the editor. Code changes are applied immediately. + +The most simple + formula is `y = x`, which means there will be no transformation at all. `y = x / 2` means that + incoming control values will be halved. You get the idea: `y` represents the desired target + control value (= output value) and `x` the incoming source control value (= input value). Both are + 64-bit floating point numbers between 0.0 (0%) and 1.0 (100%). + +The script can be much more + complicated than the mentioned examples and make use of all built-in EEL2 language features. The + important thing is to assign the desired value to `y` at some point. + +The following variables/functions are available in the formula: + +[cols="m,1"] +|=== +| Variable | Description + +| +y +| +`y` initially contains the _current_ target value. You can use that value in order to calculate the new value. With this, you can essentially craft your own relative mode! + +| +y_last +| +This contains the last value of the target before it was affected by this particular mapping. + +Allows you to come up with a performance control mode typical for synth parameter mappings, just like the built-in <> mode but more customizable. Try this +for example: `y = y_last + x * (1 - y_last)` + +| +rel_time +a| +This contains the number of milliseconds since this mapping has last been triggered with a control message coming from the source. + +As soon as you use this and a control message comes in, ReaLearn will start invoking your formula _repeatedly_! That means, this variable is your entrance ticket to smooth transitions and continuous parameter modulation. + +A few examples: + +* Smooth transition from current value to control value: `rel_time; y = abs(x - y) < 0.05 ? stop : y + 0.1 * (x - y)` +* Sinus LFO: `y = (sin(rel_time / 500) + 1) / 2` +* Linear transition to control value (1 second): `y = abs(x - y) < 0.05 ? stop : x * min(rel_time / 500, 1)` +* 2 seconds chaos: `y = rel_time < 2000 ? rand(1) : stop` +* Setting a value with delay: `y = rel_time < 2000 ? none : stop(0.5)` + +| +stop and stop(...) +| + +In combination with `rel_time`, this stops repeated invocation of the formula until the mapping is triggered again. + +Good for building transitions with a defined end. + +Stopping the invocation at some point is also important if the same parameter should be controlled by other mappings as well. Otherwise, if multiple mappings continuously change the target parameter, only the last one wins. + +This also exists as a function, which lets you do both, returning a target value *and* stopping the transition. Pass the desired value in the parentheses, e.g. `stop(0.5)`. + +| +none +| +Usually, each repeated (see `rel_time`) invocation always results in a target invocation (unless the target is not retriggerable and already has the desired value). Sometimes this is not desired. In this case, one can return `none`, in which case the target will not be touched. + +Good for transitions that are not continuous, especially if other mappings want to control the parameter as well from time to time. +|=== + + + +ReaLearn's control processing order is like this: + +. Apply source interval +. Apply transformation +. Apply reverse +. Apply target interval +. Apply rounding + + +===== Step size Min/Max + +When you deal with relative adjustments of target values in terms of + increments/decrements, then you have great flexibility because you can influence the _amount_ of + those increments/decrements. This is done via the _Step size_ setting, which is available for all + _continuous_ targets. + +* _Step size Min_ specifies how much to increase/decrease the target value when an +increment/decrement is received. +* _Step size Max_ is used to limit the effect of acceleration (for rotary encoders +which support acceleration and virtual control elements that are mapped as "Incremental button" and have +a "Speed" > 1x) and changes in velocity (for velocity-sensitive buttons/keys that are used +as "Incremental button"). If +you set this to the same value as _Step size Min_, encoder acceleration or changes in velocity +will have absolutely no effect on the incrementation/decrementation amount. If you set it to +100%, the effect is maximized. + +===== Speed Min/Max + +When you choose a discrete target, the _Step size_ label will change into + _Speed_. _Discrete_ means there's a concrete number of possible values - it's the opposite of + _continuous_. If a target is discrete, it cannot have arbitrarily small step sizes. It rather has + one predefined atomic step size which never should be deceeded. Allowing arbitrary step size + adjustment wouldn't make sense. That's why _Speed_ allows you to _multiply_ (positive + numbers) or _"divide"_ (negative numbers) value increments with a factor instead. Negative numbers are + most useful for rotary encoders because they will essentially lower their sensitivity. Virtual targets + are always discrete. + +Example: + +* Let's assume you selected the discrete target <>, which is + considered discrete because an FX with for example 5 presets has 6 well-defined possible values (including the + <no preset> option), there's nothing inbetween. And let's also assume that you have a controller like Midi + Fighter Twister whose rotary encoders don't support built-in acceleration. Now you slightly move an encoder + clock-wise and your controller sends an increment +1. If the _Speed Min_ slider was at 1 (default), this will just + navigate to the next preset (+1). If the _Speed Min_ slider was at 2, this will jump to the 2nd-next preset (+2). + And so on. +* There are FX plug-ins out there which report their parameter as discrete with an insanely small step size (e.g. + some Native Instrument plug-ins). This kind of defeats the purpose of discrete parameters and one can argue that + those parameters should actually be continuous. In such a case, moving your rotary encoder might need _a lot_ of + turning even if you set _Speed_ to the apparent maximum of 100! In this case you will be happy to know that the + text field next to the slider allows you to enter values higher than 100. +* You can set the "Speed" slider to a negative value, e.g. -2. This is the opposite. It means you need to make your + encoder send 2 increments in order to move to the next preset. Or -5: You need to make your encoder send 5 + increments to move to the next preset. This is like slowing down the encoder movement. + +===== Encoder filter (dropdown) + +Allows you to react to clockwise or counter-clockwise encoder movements only, e.g. if + you want to invoke one action on clockwise movement and another one on counter-clockwise movement. Or if you want + to use different step sizes for different movements. + +* *Increment & decrement:* ReaLearn will process both increments and decrements. +* *Increment only:* ReaLearn will ignore decrements. +* *Decrement only:* ReaLearn will ignore increments. + + +===== Wrap + +If unchecked, the target value will not change anymore if there's an incoming + decrement but the target already reached its minimum value. If checked, the target value will jump + to its maximum value instead. It works analogously if there's an incoming increment and the target + already reached its maximum value. + +If this flag is enabled for controller mappings which have a virtual target, every main mapping controlled by + that virtual control element will _rotate_ - even if the main mapping itself doesn't have _rotate_ enabled. + + +===== Make absolute + +Check this box if you want to emulate an absolute control element with a relative encoder + or with -/+ (incremental) buttons. + +This is useful if you have configured your controller to be relative all the way (which is good!) but you want + to use a control transformation EEL formula - which is not possible if you change the target with relative + increments. It works by keeping an internal absolute value, incrementing or decrementing it accordingly and + then processing it just like normal absolute control values. + +By checking this box: + +* You lose the possibility to be perfectly free of parameter jumps (but you can try to mitigate that loss by + using the jump settings). +* You gain support for control-direction EEL transformation, non-continuous target value sequences and source range. +* You can still use some of the relative-only features: Step size and rotate! + +[#fire-mode] +===== Fire mode + +Normally, when a button gets pressed, it controls the target immediately. However, by using this dropdown +and by changing the values below it, you can change this behavior. This dropdown provides different fire modes that +decide how exactly ReaLearn should cope with button presses. + +====== Fire on press (or release if > 0 ms) + +This mode is essential in order to be able to distinguish between + different press durations. + +* *Min* and *Max* decide how long a button needs to be pressed to have an effect. +* By default, both min and max will be at 0 ms, which means that the duration doesn't matter and + both press (> 0%) and release (0%) will be instantly forwarded. If you change _Min_ to + e.g. 1000 ms and _Max_ to 5000 ms, it will behave as follows: +* If you press the control element and instantly release it, nothing will happen. +* If you press the control element, wait for a maximum of 5 seconds and then release it, the + control value of the press (> 0%) will be forwarded. +* It will never forward the control value of a release (0%), so this is probably only useful for + targets with trigger character. +* The main use case of this setting is to assign multiple functions to one control element, + depending on how long it has been pressed. For this, use settings like the following: +* Short press: 0 ms - 250 ms +* Long press: 250 ms - 5000 ms + +====== Fire after timeout + +This mode is more "satisfying" because it will let ReaLearn "fire" immediately once a certain + time has passed since the press of the button. However, obviously it doesn't have the concept of a "Maximum" + press duration, so it can't be used to execute different things depending on different press durations (or only as + the last part in the press duration chain, so to say). + +* *Timeout:* Sets the timeout in milliseconds. If this is zero, everything will behave as usual. + +[#fire-after-timeout-keep-firing] +====== Fire after timeout, keep firing (turbo) + +Welcome to turbo mode. It will keep hitting your target (always with + with the initial button press velocity) at a specific rate. Optionally with an initial delay. Epic! + +* *Timeout:* This is the initial delay before anything happens. Can be zero, then turbo stage is entered + instantly on press. +* *Rate:* This is how frequently the target will be hit once the timeout has passed. In practice it won't + happen more frequently than about 30 ms (REAPER's main thread loop frequency). + +====== Fire on double press + +This reacts to double presses of a button (analog to double clicks with the mouse). + +====== Fire after single press (if hold < Max ms) + +If you want to do something in response to a double press, chances are that + you want to do something _else_ in response to just a single press. The _Normal_ fire mode will fire no matter + what! That's why there's an additional _Single press_ mode that will not respond to double clicks. Needless to + say, the response happens _slightly_ delayed - because ReaLearn needs to wait a bit to see if it's going to be a + double press or not. + +* *Max:* It's even possible to distinguish between single, double _and_ long press. In order to do that, you + must set the _Max_ value of the _Single press_ mapping to a value that is lower than the _Timeout_ value of + your _After timeout_ mapping. That way you can use one button for 3 different actions! Example: +** Mapping 1 "Single press" with Max = 499ms +** Mapping 2 "Double press" +** Mapping 3 "After timeout" with Timeout = 500ms + + +===== Button filter (right dropdown) + +This allows you to easily ignore button presses or releases. + +* *Press & release:* ReaLearn will process both button presses (control value = 0%) and button releases (control + value > 0%). This is the default. +* [[press-only,press-only]] *Press only:* Makes ReaLearn ignore the release of the button. The same thing can be achieved by setting + _Source Min_ to 1. However, doing so would also affect the feedback direction, which is often undesirable + because it will mess with the button LED color or on/off state. +* *Release only:* Makes ReaLearn ignore the press of the button (just processing its release). Rare, but possible. + +==== Bottom section + +This section has two functions: + +- Providing context-sensitive help for the glue section +- Providing target control information and error reporting + +===== Target control information + +This shows information about how an incoming control value was handled and possible target control errors. + +NOTE: If the target supports MIDI real-time control and the source is a MIDI source, this currently only works if "Log target control" is enabled (see <>). + +===== Help + +Context-sensitive help for the glue section. Whenever you touch a setting in +the glue section, some text will appear which explains what this element does, both for the _control_ and for the +_feedback_ direction (if applicable). + +* *If source is a:* It often depends on the kind of source what effect a setting has. Therefore, this dropdown + always contains a list of sources. It only displays relevant kinds of sources. If a source kind is impossible + according to the current source settings or if it's not supported by the setting, it won't appear in the list. + +=== Provided REAPER actions + +ReaLearn provides some REAPER actions which become available as soon as at least one instance of ReaLearn +is loaded. It can be useful to put a ReaLearn instance on REAPER's monitoring FX chain in order to have access +to those actions at all times. + +In order to find these actions, open REAPER's _Actions_ menu, choose _Show action list…_ and simply search for +`realearn`. The most important actions: + +* *ReaLearn: Find first mapping by source:* This action will ask you to touch some control element. As soon as you + touch a control element which is mapped, it will open the mapping panel for the corresponding mapping. It will search + within all ReaLearn instances loaded in your current project as well as the ones on the monitoring FX chain. +* *ReaLearn: Find first mapping by target:* This action is similar to _Find first mapping by source_. It asks you + to touch some (learnable) REAPER parameter. As soon as you touch one that is mapped, it will open its mapping panel. +* *ReaLearn: Learn single mapping (reassigning source):* Asks you to touch a control element and target and adds a new + mapping in the first ReaLearn instance that it encounters. It prefers instances in the current project over + monitoring FX. It automatically chooses the instance with the correct MIDI/OSC input. If there's an instance which + already has that source assigned, it will be reassigned to the new target that you touched. +* *ReaLearn: Learn single mapping (reassigning source) and open it:* Like _Learn single mapping_ but additionally + opens the mapping panel after having learned the mapping. This is great for subsequent fine tuning. +* *ReaLearn: Learn source for last touched target (reassigning target):* This behaves similar to REAPER's built-in + MIDI learn in that it always relates to the target that has been touched last. +* *ReaLearn: Send feedback for all instances:* Makes each ReaLearn instance in all project tabs send feedback for all + mappings. That shouldn't be necessary most of the time because ReaLearn usually sends feedback automatically, but + there are situations when it might come in handy. + +[#advanced-settings] +=== Advanced settings + +This section describes the _Advanced settings_ feature of the mapping panel (see section <>) in more +detail. + +==== The YAML language + +This feature allows you enter text that +conforms to the so-called https://en.wikipedia.org/wiki/YAML[YAML] format. This is not a programming language, so you +can't write loops, conditions or anything like that. Instead, think of it as a language for writing configuration. Do +you know INI files? REAPER uses INI files to save configuration. YAML is a bit like that, just much more expressive +because it allows you to not only express flat key-value pairs (e.g. `edit_fontsize=29`) but also deeply nested +configuration data and lists. + +*Important thing 1:* YAML is indentation-sensitive, so indentation matters! The bright side of this is that it always +looks clean and nice. The dark side is that ReaLearn will refuse to save your settings if you messed up the indentation. +Therefore: Be consistent with your indentation (e.g. use always an indentation of 2 spaces for nesting) and have an utmost +attention to detail when doing copy and paste from the examples in this section! + +*Important thing 2:* When you close the text editor and ReaLearn saves your advanced settings as part of the mapping, +it will not save the text that you have entered _verbatim_. It will save a structural representation of what you +entered (and it will strip comments!). That means if you open the advanced settings again, your could text could look a +bit different, in particular it can have a different formatting. But don't worry, it _means_ exactly the same to +ReaLearn. + +[discrete] +===== Why the hell did you come up with something like that? + +Deciding for textual configuration and YAML in particular was a conscious decision with the goal to provide a +developer-friendly framework for rapidly extending ReaLearn with advanced features that don't urgently need a graphical +user interface. + +* *Why ask the user to enter text instead of providing a convenient graphical user interface?* +** That's mostly a tradeoff due to the fact that my time available for developing ReaLearn is limited. +** It's much work to develop a graphical user interface for every feature. In fact, programming the user interface + often takes most of the time whereas implementing the actual logic is not that much effort. +** It's true that some sorts of functionality really benefit from having a fancy graphical user interface. But + there's also functionality for which having it is not too important, e.g. functionality that is of configurational + nature and not used that often. +** Also, one of ReaLearn's goals is to give power users and programmers extra powers. Textual configuration can be + more powerful in many situations once the user knows how to go about it. +* *Why YAML?* +** YAML has the advantage of being popular among programmers, widely supported, highly structured and relatively + well readable/writable by both humans and machines. +** Many text editors offer built-in support for editing YAML. +** Makes it easy to provide data for even very complex features. +* *Why not a scripting language?* +** Using a scripting language would spoil any future possibility to add a graphical user interface on top of some of + the functionality. +** It wouldn't allow ReaLearn to apply future optimizations and improvements. ReaLearn is rather declarative + in nature and a scripting language would destroy this quality. +** It's hard to come up with a stable and good API. +** It's harder to use than a configuration language. +* *Why don't you save the text, just the structure?* +** Mostly because saving just the structure makes the entered data become a natural part of ReaLearn's main preset + format (JSON). +** However, this is something that might change in future, depending on how it proves itself in practice. +** Once we would start saving the actual text, it would be hard to go back. + +==== Supported configuration properties + +In this section you will find examples that cover all currently supported configuration properties. You can copy and +paste the stuff you need to the text editor, remove the parts that you don't need and adjust the rest. Comments (lines +starting with `#`) will be removed automatically. + +[#mapping-lifecycle-actions] +===== Mapping lifecycle actions + +ReaLearn allows you to define MIDI messages to be sent to the output whenever a mapping turns active or +inactive. + +Example use cases: + +* Accessing very device-specific features via system-exclusive MIDI messages. +* Choosing a different LED color/style depending on the active mapping. +* Initializing a sys-ex-controllable display with some mapping-specific text (more difficult). + +A mapping can change its active/inactive state based on the following factors: + +* *Preset loading/unloading:* A mapping can turn active when a ReaLearn instance or preset is loaded and turn + inactive when it's changed or unloaded. +* *<>:* A mapping can turn inactive when an activation condition + is not satisfied anymore and can change back to active as soon as it's satisfied again. +* *Target condition:* Targets can also have conditions (e.g. "Track must be selected"). They affect activation + state changes in the same way. +* *Target validity:* A mapping can turn inactive when the target is not valid anymore, e.g. when it's a target that's + based on the currently selected track but no track is currently selected. Analogously, it can turn active again once + a valid target can be resolved. +* *Feedback enabled checkbox (or mapping-enabled checkbox):* A mapping can turn inactive as soon as this checkbox is unticked and turn active + again when ticking it. This is also the best way to test your configuration. + +(Controller) mappings with virtual targets are always considered active as long as the feedback checkbox is ticked. +That's why they are perfectly suited for holding a bunch of controller initialization messages! This feature is +for example used in the "PreSonus FaderPort Classic" controller preset, which needs to be put in a specific mode +before being usable. ReaLearn does this automatically simply by sending some mapping on-activate MIDI messages. + +These are the available configuration properties: + +[source,yaml] +---- +# Contains stuff to be done whenever this mapping becomes active. +on_activate: + # A list of MIDI messages to be sent to the output when this mapping becomes active. + # + # At the moment, only messages of type "raw" are supported. Although this covers all possible types + # of MIDI messages, it's a bit hard to express e.g. simple NOTE ON or CC messages with this notation. + # In particular, you would need to know how MIDI messages are presented as byte sequences. Future ReaLearn + # versions will provide more convenient ways to describe simple MIDI messages. + send_midi_feedback: + # This is an example of a system-exclusive message ("SysEx"). It's usually expressed in hexadecimal string + # notation. Make sure to include the leading F0 and trailing F7, which is the begin and end marker of all + # system-exclusive messages! + - raw: F0 00 20 6B 7F 42 02 00 10 77 01 F7 + # Instead of above hexadecimal string notation, you could also use an array of decimal numbers to describe a raw + # message. The following is a NOTE ON of note 74 on channel 1 with velocity 100. + - raw: + # NOTE ON on channel 1 + - 144 + # Note number 74 + - 74 + # Note velocity 100 + - 100 + +# Contains stuff to be done whenever this mapping becomes inactive. +on_deactivate: + # A list of MIDI messages to be sent to the output when this mapping becomes inactive. + send_midi_feedback: + # Supports exactly the same kinds of messages as described above in "on_activate". + - raw: F0 00 20 6B 7F 42 02 00 10 77 14 F7 +---- + +Please remember that YAML comments (e.g. `# The following line does this and that`) _will not be saved_! In case you +want to explain something, you need to write it as YAML property, such as in the following example: + +[source,yaml] +---- +comment: "The following configuration makes the rightmost pad of the MiniLab mkII light up in red color." +on_activate: + send_midi_feedback: + - raw: F0 00 20 6B 7F 42 02 00 10 77 01 F7 +---- + +ReaLearn will ignore any unknown properties. + +TIP: If you use input `` and find that MIDI lifecycle messages aren't sent, no matter what, make sure "Send feedback only if track armed" is disabled (see <>)! + +WARNING: Disabling the complete ReaLearn instance will cause all mappings to deactivate. However, sending MIDI messages on deactivation in this case will only work if the output is a device! If it is ``, it will not send anything because REAPER will not give that ReaLearn instance any chance to output MIDI messages once it's disabled. Instead, the MIDI message will queue up and be sent once you enable that instance again ... which is probably not what you want. + +=== Configuration files + +ReaLearn creates and/or reads a few files in REAPER's resource directory. + + +[cols="m,1"] +|=== +| File | Description + +| Data/helgoboss | Directory which contains data such as presets or resources that need to be distributed via ReaPack + +| Data/helgoboss/auto-load-configs/fx.json | Contains global FX-to-preset links, see <> + +| Data/helgoboss/archives | Directory which contains archives e.g. the compressed app, distributed via ReaPack + +| Data/helgoboss/doc | Contains offline documentation, e.g. this guide as PDF + +| Data/helgoboss/presets/controller | Contains preset for the controller compartment + +| Data/helgoboss/presets/main | Contains preset for the main compartment + +| Helgoboss | Directory which contains rather personal or device-specific data, not touched via ReaPack + +| licensing.json | Contains license keys + +| Helgoboss/App | Contains the uncompressed App, if installed + +| Helgoboss/Pot/previews | Directory which contains previews recorded by <> + +| Helgoboss/ReaLearn/osc.json | Global OSC device configurations, see <> + +| Helgoboss/ReaLearn/realearn.ini | Very basic global configuration, currently mainly regarding ReaLearn's built-in server. + +Currently supported properties (subject to change): `server_enabled`, `server_http_port`, `server_https_port`, `server_grpc_port`, `companion_web_app_url` + +| Helgoboss/Server/certificates | Contains a list of certificates and corresponding private keys in order to allow encrypted communication with ReaLearn Companion and App. +|=== \ No newline at end of file diff --git a/doc/user-guide.adoc b/doc/user-guide.adoc index d941b18c8..e55b68817 100644 --- a/doc/user-guide.adoc +++ b/doc/user-guide.adoc @@ -1,4751 +1 @@ -= ReaLearn user guide -:experimental: -:toc: -:toclevels: 5 -:sectnums: -:sectnumlevels: 2 - -// Reusable text snippets -:osc_host_instruction: Enter the IP address of the computer running ReaLearn. You can easily find it by pressing the "Projection" button in ReaLearn and scrolling down a bit. It's the value next to "Host" and should start with "192.168.". -:osc_port_instruction: Choose a random port number greater than 1024, preferably 7879. This number must not be in use yet by other OSC applications, not even by REAPER's native OSC! -:osc_preset_content: There are no ReaLearn controller presets for OSC layouts yet. Although technically possible in exactly the same way as with controller presets for MIDI devices, OSC layouts are very custom, so I'm not sure if it would make much sense to create presets. Time will show. - -|=== -|Last update of text: |`2023-06-05 (v2.16.0-pre.1)` -|Last update of relevant screenshots: |`2021-04-27 (v2.8.0)` -|=== - -== Quick start - -Here's a step-by-step guide to help you get started with ReaLearn and a MIDI controller: - -. Start REAPER. -. If you haven't already done it, https://github.com/helgoboss/realearn#installation[install ReaLearn via ReaPack]. -. Make sure your MIDI controller is enabled in _Options → Preferences… → Audio → MIDI Devices_ - * For the MIDI input device (control), tick _Enable input from this device_ and untick - _Enable input for control messages_. - * For the MIDI output device (feedback), tick both _Enable output to this device_ and _Do not send reset messages_. -+ -[NOTE] -==== -The option _Do not send reset messages_ isn't available in older REAPER versions. If you don't use any external hardware synths, you can untick the global options _Reset on: Play_ and _Reset on: Stop_ instead! -==== -. Make sure the MIDI device is *not* in use as a REAPER control surface (in _Options → Preferences... → Control/OSC/web_). -. Check if there's an existing controller preset for your MIDI controller (this is optional but can make things easier). - * Extensions → ReaPack → Browse packages… - * Type "realearn controller" in the _Filter_ field. - * You should see a list of ReaLearn controller presets. - * If you find your controller in the list, right-click it, choose install and press OK. -. Fire up an instance of ReaLearn - * If you want your mappings to be specific to a particular project, create a new REAPER project or open an existing one. Right-click the track control panel and choose "Insert virtual instrument on new track…". - * If you want your mappings to be automatically available in each of your projects, open REAPER's global monitoring FX chain (View → Monitoring FX) instead. - * Then choose "VSTi: ReaLearn (Helgoboss)" -. Configure the ReaLearn instance - * Select your controller's MIDI device as _Input_ and _Output_ (if you have a controller - that supports MIDI feedback). - * If you have downloaded a controller preset: - - Switch to _Controller compartment_ and select the desired controller preset below. - - This should fill the list below with so-called _controller mappings_. - - When you are done, switch back to the _Main compartment_. -. Add and learn your first mapping - * Add a first mapping by pressing the _Add one_ button. - - A mapping appears that doesn't do anything yet because it just has a dummy target. - * Press _Learn source_ and move a control element on your MIDI controller. - * Press _Learn target_ and move e.g. the volume fader of a track. - * Now your control element should control the track volume. - -If you want to get the most out of your controller and learn about all of ReaLearn's cool features, please read on. - -== Introduction - -=== What is ReaLearn? - -Probably you know already that ReaLearn is a sort of improvement over REAPER's built-in MIDI/OSC learn. But what is it -exactly? Let me put it this way: - -____ - -ReaLearn is an instrument. It allows you to take whatever MIDI/OSC controller you have, be it a -keyboard or some fader box, plug it in and play … but instead of playing notes, you "play" -REAPER itself! - -And because ReaLearn supports MIDI/OSC feedback, you can also do the reverse: Let REAPER "play" your -controller. - -____ - -While this is still pretty vague, I think it captures the essence of ReaLearn. From a technical -viewpoint it's a VSTi plug-in, so it is an instrument, quite literally. That's one thing that sets -it immediately apart from the more conventional control surface feature in REAPER and 3rd-party -efforts such as https://forum.cockos.com/showthread.php?t=183143[CSI] or -http://www.mossgrabers.de/Software/Reaper/Reaper.html[DrivenByMoss]. The goal of the -latter-mentioned is to equip REAPER with support for specific controllers, typically -dedicated DAW controllers such as -https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/Mackie_Control_Universal.jpg/1600px-Mackie_Control_Universal.jpg[Mackie MCU] -that are tailored to control a DAW just like a hardware mixer. And I suppose they do a pretty good -job at that. - -ReaLearn's approach is quite different: It gives you total control over which control element operates which REAPER -parameter and provides you with a _learn_ function that allows you build your own control mappings quickly -and intuitively without writing configuration files. All of that on a _per-instance_ basis. That's right, by default, the mappings -are saved as part of the ReaLearn instance and therefore as part of your REAPER project. No need to pollute your global -control mappings just for the needs of one project! - -Nevertheless, since version 2, ReaLearn is also a great choice for setting up global mappings for usage across multiple projects. Just add ReaLearn to the monitoring FX chain of REAPER (View → Monitoring FX) and ReaLearn will be instantly available in all of your REAPER sessions without having to add it to a project first. In addition, ReaLearn provides a simple yet powerful preset system to make a set of mappings reusable in whatever project you want. - -ReaLearn is designed to get the most out of general-purpose MIDI controllers, which - compared to the big -and bulky DAW consoles - usually have the advantage of being small, USB-powered, more versatile and easier on the -budget. ReaLearn doesn't impose many requirements on your controller. Thanks to features like <> and <>, it can turn even the cheapest MIDI controller into a powerhouse for controlling -your DAW. - -The usual ReaLearn workflow for a single mapping goes like this: - -. Add a mapping -. Hit _Learn source_ and touch some knob on your controller. -. Hit _Learn target_ and touch some target parameter. -. Done. - -If you want to learn multiple mappings in one go, this gets even easier via the _Learn many_ button which can save you a lot of clicks. - -The result are mappings that you can customize as you desire, for example by setting a target value -range. All of that with MIDI/OSC feedback support, which was previously only available in the less -dynamic, more global control surface world. - -*Summary:* _ReaLearn is a sort of instrument for controlling REAPER._ - -=== Videos - -If you want to get a first impression of ReaLearn, a video is surely a good way. - -Here's a list of official ReaLearn videos: - -* https://www.youtube.com/playlist?list=PL0bFMT0iEtAgKY2BUSyjEO1I4s20lZa5G[The ReaLearn Tutorials]: A series of ReaLearn tutorials. -* https://youtu.be/dUPyqYaIkYA[Introduction to ReaLearn 2]: An in-depth introduction to ReaLearn 2, the sophisticated MIDI-learn plug-in for REAPER. - -Here's a short, non-complete list of user-made videos. Please note that at the moment all of them relate to older -ReaLearn versions and therefore might be partially outdated: - -* https://www.youtube.com/watch?v=WKF2LmIueY8[How To: ReaLearn and MIDI Controller for Track Sends in REAPER - Tutorial] -* https://www.youtube.com/watch?v=UrYrAxnB19I[using ReaLearn to assign MIDI controllers to (VST) plugin parameters in Cockos Reaper] - -=== Usage scenarios - -Ultimately, ReaLearn gains whatever purpose you can come up with. Because it is a VSTi plug-in and -provides many MIDI routing options, it's very flexible in how it can be used. You can "inject" it -wherever you want or need it (limitation: using it in a take FX chain is not possible yet): - -* *Input FX chain for live-only use:* Put it on a track's input FX chain in order to use it only - for incoming "live" MIDI and let it control a parameter of an effect that's on the normal FX - chain, right below a synthesizer. It will be active only if the track is armed for recording. - All MIDI messages that are used for parameter control will _automatically_ be filtered by default - and won't reach the controlled instrument, which is usually exactly what you need. -* *Grid controller for song switching:* Use some grid controller like the - https://thumbs.static-thomann.de/thumb/thumb250x220/pics/prod/339386.jpg[AKAI APC Key 25] to - arm/disarm various tracks (effectively enabling/disabling certain sound setups) by pressing the - grid buttons - with the LEDs of the buttons indicating which setup is currently active. -* *Combination with other MIDI FX for interesting effects:* Slap it on a track FX chain, right - between a MIDI arpeggiator and a synthesizer to arpeggiate the cutoff parameter of that - synthesizer. -* *Monitoring FX for project-spanning setups:* Put it on the monitoring FX chain to have some - control mappings available globally in all projects (similar to conventional control surface - stuff). -* *Unusual settings for experimental stuff:* Create a track volume mapping with only feedback - turned on. Choose "<FX output>" as MIDI output and play the synthesizer one - position below in the FX chain by moving the track volume slider (whatever that might be good for - …). -* *Rotary encoders for avoiding parameter jumps:* How about a refreshingly "normal" use case? Let - your rotary endless encoder control a track send volume without parameter jumps and restrict the - value range to volumes below 0dB. -* *VST presets for easy reuse:* Save a bunch of commonly used mappings globally as FX presets. -* *Switching controller and main presets separately:* Maintain controller and main presets and switch - between them as you like. Easily switch your controller without adjusting your FX presets. -* *Combination of multiple instances:* Use one ReaLearn instance to arm or disarm tracks that - contain other ReaLearn instances to enable/disable different mapping groups. Group mappings and - activate/deactivate them group-wise simply by instantiating multiple ReaLearn instances and - enabling/disabling them as desired in the FX chain window. - -… the possibilities are endless. It's all up to you! Use your creativity. - -All of that makes ReaLearn especially well-suited for performers, people who use REAPER as a -platform for live playing. It might be less interesting to people who are satisfied with a control surface setup off the shelf. But even so, as long as you have some general-purpose MIDI controller and you want a fine-tuned mapping to DAW parameters -of all sorts, give ReaLearn a try. It might be just what you need. More so if the controller supports feedback -(e.g. motorized faders, LEDs or LCDs). - -*Summary:* _ReaLearn is tailored to usage scenarios typically desired by performers._ - -== Basics - -=== Control - -After installing ReaLearn, you can fire it up just like any other VST instrument in REAPER: By -adding it to an FX chain. - -. Right click in the track header area and choose "Insert virtual instrument on new track…" -. Choose "VSTi: ReaLearn (Helgoboss)" - -After that you should see ReaLearn's main panel (unlike this screenshot, it wouldn't contain any -mappings yet): - -image:images/screenshot-main-panel-annotated.svg[Main panel] - -On the very top you see the _header panel_ for changing settings or doing things that affect -this complete instance of ReaLearn. Below that there's the _mapping rows panel_ which displays all -main mappings in this instance of ReaLearn. There can be very many of them. On the very bottom you see some information about the version of ReaLearn that you are -running. - -It can be useful to route all keyboard input to ReaLearn, so you can enter spaces in the "Search" field: - -. Right click ReaLearn FX in the FX chain. -. Enable "Send all keyboard input to plug-in". - -[discrete] -==== Adding a mapping - -*Let's see how to add and use our first MIDI mapping:* - -. Press the "Add one" button. -* A new mapping called "1" should appear in the mapping rows panel. -* For now, it doesn't have any effect. The default target is a - <> target which basically does nothing. -. Press the "Learn source" button of that new mapping. -* Its label will change to "Stop". -. Touch some control element on your MIDI controller (knob, encoder, fader, button, key, pitch - bend, mod wheel, …). For this example it's best to use something continuous, not a button or - key. -* If your MIDI is set up correctly, the button label should jump back to "Learn source" and the - touched control element should appear in the _source label_. See below if this doesn't happen. -. Press the "Learn target" button. -* Its label will change to "Stop". -. Touch the volume fader of your newly created REAPER track. -* The button label should jump back to "Learn target" and "Track: Set volume" should appear in the - _target label_. -. Now you should be able to control the touched target with your control element. - -[discrete] -[#troubleshooting] -==== Troubleshooting - -[discrete] -==== ReaLearn doesn't appear in the list of plug-ins - -- Make sure you look in the *VSTi* section (ReaLearn is an instrument). -- If REAPER crashes when scanning for plug-ins and the crash message shows something like `reaper_host64` or `reaper_host32`, you either have a 32/64-bit version mismatch or you have _Preferences → Plug-ins → Compatibility → VST bridging/firewalling_ set to "In separate plug-in process" or "In dedicated process per plug-in". Please see the https://github.com/helgoboss/realearn#installation[installation instructions on the -project website] for hints how to fix this. - -[discrete] -==== ReaLearn doesn't learn MIDI messages - -If the label remains at "Stop" at step 3, that means ReaLearn doesn't see the incoming MIDI messages. You need to have a look at your MIDI setup. - -* Make sure the MIDI device is *not* installed as REAPER control surface (in _Preferences → Control/OSC/web_). -* Make sure *Enable input from this device* is checked for your controller MIDI input device in - the REAPER preferences. -+ -[NOTE] -==== -_Enable input for control messages_ is totally irrelevant for ReaLearn. This is -only used for REAPER's built-in MIDI learn, which uses the so-called _control MIDI path_. - -With ReaLearn, you use the same MIDI path for controlling and playing, which is one reason why it is so flexible. It provides local and global MIDI message filtering, so you still don't need to worry about messages that are intended for control but suddenly cause your synthesizer to play MIDI notes. -==== -+ -* Make sure your audio hardware is not stuck (playback in REAPER should work). -* Make sure the track is armed for recording and has the appropriate MIDI device input. -+ -NOTE: This is necessary only if _Input_ is set to __, which is the default. If you capture MIDI from a specific device, the track doesn't have to be armed. -* Some controllers, especially DAW controllers, are able to work with several protocols (MCU, HUI, MIDI, …). -** For this simple test, it's probably the best to make your controller enter a specific MIDI operation mode. -** Although MCU and HUI is also just MIDI under the hood, these operation modes are more specialized and therefore need a bit of special attention. ReaLearn conveniently handles these modes when using the _Mackie Control_ controller preset. -** In any case, please consult the <> section, maybe you will find some information about your controller. - -When you read this the first time, you might get the impression that this is a lot of work for -setting up one simple control mapping. It's not. Learning mappings is a matter of a few secs after -you got the hang of it. ReaLearn also provides the "Learn many" button and a bunch of REAPER actions -for one-click learning straight from a REAPER toolbar or triggered by a button on your controller. -More about that later. - -At this point: Congratulations! You have successfully made your first baby steps with ReaLearn. - -[discrete] -==== Some words about MIDI routing - -If you think that what we saw until now is not more than what REAPER's built-in MIDI learn already -offers, I can't blame you. First, don't worry, there's more to come, this was just the beginning. -Second, there _is_ a difference. For some folks, this is an insignificant difference, for others -it's a game changer, it depends on the usage scenario. The key to understand this difference is to -understand the MIDI _routing_: In above example, _Input_ was set to `<FX input>`. That means -we used normal track MIDI messages to control a parameter in REAPER - let's call it _track MIDI path_. -This is different from REAPER's built-in MIDI learn, which uses the totally separate _control MIDI path_. - -Using the track MIDI path means it's completely up to you to decide what MIDI messages flow into -ReaLearn. You decide that by using REAPER's powerful routing capabilities. For example, you can -simply "disable" the mapping by disarming your track, a feature that is very desirable if you use -REAPER as live instrument. Or you can preprocess incoming MIDI (although that should rarely be -necessary given ReaLearn's mapping customization possibilities). - -Instead of using `<FX input>`, you can also pick the MIDI device of your choice directly, in which case ReaLearn will -ignore track MIDI messages and capture MIDI messages directly from the already open MIDI device. - -Another thing worth to point out which is different from built-in MIDI learn is that we didn't use -the action "Track: Set volume for track 01". Benefit: ReaLearn will let you control the volume of -the track even if you move that track to another position. The track's position is irrelevant! - -=== Feedback - -In ReaLearn, every mapping has 2 directions: _control_ (controller to REAPER) and _feedback_ (REAPER -to controller). So far we have talked about the _control_ direction only: When you move a knob on -your controller, something will happen in REAPER. But if your controller supports it, the other -direction is possible, too! - -Imagine you would use a MIDI-controllable motorized fader as control element to change the track -volume. ReaLearn is capable of making that fader move whenever your track volume in REAPER changes - -no matter if that change happens through automation or through dragging the fader with your mouse. -Motorized faders are quite fancy. Another form of feedback visualisation are rotary encoders with -LEDs that indicate the current parameter value. - -How to set this up? Often it's just a matter of choosing the correct feedback device: - -. Make sure *Enable output to this device* and *Do not send reset messages* is checked for your controller MIDI output device in the REAPER preferences. -+ -[NOTE] -==== -The option _Do not send reset messages_ isn't available in some older REAPER versions. If you don't use any external hardware synths, you can untick the global options *Reset on: Play* and *Reset on: Stop* instead! -==== -. In ReaLearn's header panel, select your controller as _MIDI output_. - -That should be it! - -If it doesn't work and you have ruled out MIDI connection issues, here are some possible causes: - -. *Your controller is not capable of feedback via MIDI messages.* -* Some controllers _do_ support feedback, but not via MIDI. -* If they support feedback via OSC, you are lucky because ReaLearn supports that, too. This is discussed - in another section. -* If it's another protocol, you are out of luck. Reverse engineering proprietary protocols is out of - ReaLearn's scope. -* Recommendation: Maybe you are able to find some bridge driver for your controller that is - capable of translating generic MIDI messages to the proprietary protocol. Then it could work. -* Examples: Akai Advance keyboards, Native Instruments Kontrol keyboards -. *Your controller doesn't support feedback via generic MIDI messages but via MIDI SysEx.* -* In this case, MIDI feedback is probably still achievable because since version 2.6.0 ReaLearn also supports - feedback via MIDI system-exclusive messages. However, it's not going to be straightforward. - Unless you find an existing controller preset for your controller, you'll have to read the MIDI specification - of your controller (hopefully there is one) … or you need to experiment a lot. -* Examples: Arturia MiniLab mkII (but we have a controller preset for this one!) -. *Your controller has multiple modes and currently is in the wrong one.* -* Some controllers, especially DAW controllers, are able to work with several protocols. -* Recommendation: Consult your controller's manual and take the necessary steps to put it into - something like a "generic MIDI" mode. -* Example: Presonus Faderport -. *Your controller expects feedback via messages that are different from the control MIDI messages.* -* Usually, controllers with feedback support are kind of symmetric. Here's an example what I mean - by that: Let's assume your motorized fader _emits_ CC 18 MIDI messages when you move it. That - same motorized fader starts to move when it _receives_ CC 18 MIDI messages (messages of exactly - the same type). That's what I call symmetric. E.g. it's not symmetric if it emits CC 18 but - reacts when receiving CC 19. -* ReaLearn assumes that your controller is symmetric. If it's not, you will observe non-working - or mixed-up feedback. -* Recommendation: Consult your controller's manual and try to find out which MIDI messages need - to be sent to the controller to deliver feedback to the control element in question. Then, - split your mapping into two, making the first one a control-only and the second one a - feedback-only mapping. Adjust the source of the feedback-only mapping accordingly. In the next - section you'll learn how to do that. -* Example: Presonus Faderport - -TIP: Have a look into the section <>. Maybe your controller is listed there along with some tips. - -=== Editing a mapping - -When you press the _Edit_ button of a mapping row, a so-called _mapping panel_ appears, which lets -you look at the corresponding mapping in detail and modify it: - -image:images/screenshot-mapping-panel.png[Mapping panel] - -This panel has 4 sections: - -* *Mapping:* Allows to change the name and other general settings related to this mapping. -* *Source:* Allows to edit the _source_ of the mapping. In most cases, a source represents a - particular control element on your controller (e.g. a fader). -* *Target:* Allows to edit the _target_ of the mapping and optionally some target-related - activation conditions. A target essentially is the parameter in REAPER that should be controlled. -* *Glue:* Allows to change in detail how your source and target will be glued together. This - defines _how_ incoming control values from the source should be - applied to the target (and vice versa, if feedback is used). This is where it gets interesting. - Whereas REAPER's built-in MIDI learn provides just some basic modes like Absolute or Toggle, ReaLearn - allows you to customize many more aspects of a mapping. - -By design, source, glue and target are independent concepts in ReaLearn. They can be combined -freely - although there are some combinations that don't make too much sense. - -Changes in the mapping panel are applied immediately. Pressing the _OK_ button just closes the -panel. - -*Tip:* It is possible to have up to 4 mapping panels open at the same time. - -=== Controller setup - -In order to get the most out of your controller in combination with ReaLearn, you should consider -the general hints given in the section <>. - -[#automation-and-rendering] -=== Automation and rendering - -Similarly to control surfaces, ReaLearn is primarily meant to be used for controlling targets "live". If you -want to _persist_ the resulting target value changes, you can do so by writing automation. Just as -with any other automation, it will be included when you render your project. - -It _is_ possible to feed ReaLearn with track MIDI items instead of live MIDI data. This results -in a kind of _pseudo automation_. Some users call this _MIDI CC based automation_. This feature can be quite interesting and appealing to MIDI fans. - -[CAUTION] -==== - -*Support for rendering pseudo automation is limited!* - -Let's say you finally want to render your project. If you don't watch out, your pseudo automation will simply be ignored! - -Pseudo automation will only be rendered if you follow some very distinct rules: - -- The target must be <> (all other targets will most likely be ignored). -- The targeted FX must be on the same track as the ReaLearn instance itself. -- This only works in REAPER versions >= 6.52+dev0324. - -I remember that *Online Render* used to respect all kinds of pseudo automation. However, this must have stopped working at some point (or it works only under particular circumstances or with certain settings, not sure). Anyway, now you need to follow the same rules as with offline rendering to make pseudo automation work. -==== - - - -[#companion-app] -== Companion app - -This section is about the _ReaLearn Companion_ app, which is a separate software that powers ReaLearn's <> feature. - -At the moment it comes as https://play.google.com/store/apps/details?id=org.helgoboss.realearn_companion[Android app] -and https://realearn.helgoboss.org/[web app]. The iOS app has not been published yet. -The source code is available https://github.com/helgoboss/realearn-companion[here at GitHub]. - -=== Connecting to ReaLearn - -The start screen lets you connect to a specific ReaLearn instance by scanning the QR code that pops up when -pressing ReaLearn's <>. It's also possible to enter the connection data manually, in -case your device doesn't have a camera or in case you are using the web app (in which QR code scanning often doesn't -work so well). If you are experiencing issues, follow the instructions given by the app and the setup guide which is -displayed when pressing the <> button! - -Please note, if all you want is to open the web app on the computer that also runs REAPER/ReaLearn, you don't need to -bother with QR codes or connection data at all. Just follow the link that is displayed in the setup guide. - -ReaLearn allows many Companion apps to connect to it simultaneously, there's no artificial limit. - -=== Viewing the controller projection - -As soon as you have connected, you should be able to see the controller projection, which consists of both the -controller layout and the current mapping of its control elements. If not, the app will give you a hint what's missing. -The control element labels will reflect the labels of your main mappings. - -You can tap the screen to make the app bar disappear or reappear. There's a menu on the right side of the app bar -which let's you change various aspects of the appearance. Just give it a try! Dark mode combined with high-contrast is -especially nice on devices with OLED displays! All of these settings will be saved on your device, not in ReaLearn's -controller preset. - -Another thing you can do here is applying two-finger gestures in order to zoom/pinch. - -=== Editing the controller layout - -Pressing the pencil button in the app bar let's you enter edit mode. As soon as you do that, the control element labels -will reflect the labels of your controller mappings and a palette will appear on the side of the screen. - -==== Editing basics - -You can drag the controls freely from the palette onto the scene and back. Pressing a control element opens a panel -which lets you change its appearance. The two labels mentioned there are used in the following way: - -. If the control element is a composite control element (see below, e.g. push encoder), the first label represents the -mapping of the first inner control element (e.g. the encoder) and the second label represents the mapping of the -second inner control element (e.g. the button). See the _Midi Fighter Twister_ <> for a real-world usage of this feature. -. If the control element is just a normal control element, the second label is usually empty. Except this control -element has more than one main mapping assigned: In that case the second label shows the second main mapping. - -Whenever you press the save button (floppy disk) in the app bar, the layout is saved - not on your specific device -but as part of ReaLearn's controller preset! So this same layout will automatically be available to all other -connected Companion apps. - -You can leave the edit mode by pressing the pencil button again. This gives you a preview of your current changes. - -*Attention:* If you leave the controller projection view (e.g. by pressing the upper left arrow) or if you change your -controller preset from within ReaLearn, all non-saved controller layout changes will be lost! So it's a good idea to -save often. Once saved, there's no undo though. You can back up temporary states by copying the corresponding controller -preset files (on the computer running ReaLearn) to a temporary backup location (see _Save as…_ button documentation -in the <> section). - -==== Composite control elements - -If you want one visual control element to contain 2 logical control elements (e.g. a push encoder = encoder + button), -just move one control element onto another one - and they will merge into a composite control element. If you want to -undo this merging, move the merged control element back on the palette - they will split up and you can drag them onto -the scene again. - -==== Batch-editing control elements - -Sometimes it's a bit tedious to edit each control element separately. As soon as you long-press one control element, -the Companion app will enter multi-edit mode and you can start adding/removing other control elements to/from the -selection by just tapping them. When you move one element of the selection, all others will also be moved. You can open -the control element appearance panel by long-pressing an element. All changes made in the panel will immediately be -applied to all selected elements. - -You can leave multi-edit mode either by unselecting all elements or by (temporarily) leaving the edit mode. - -_Known issue:_ In the web app, multi-edit mode currently doesn't work, there's a graphical glitch. - -==== Dealing with the grid - -You can hide the grid using the app bar menu. The grid will still have an effect though. - -One way to get more fine-grained positioning is by decreasing the grid size. However, it doesn't go below a certain -minimum and changing the grid size after already having positioned lots of elements might not be the best idea. -Usually, the better way is to just expand the scene. Don't worry, your layout will always fit on the screen, no matter -how large the scene actually is in terms of grid squares! - -You can enlarge the scene by slightly moving a control element out of the scene. Do so in small steps and you will -automatically have more space at your disposal. The scene will always be as big as the imaginary rectangle from the -top-left control element to the bottom-right control element! - -[#tutorials] -== Tutorials - -The screenshots in this section are slightly out of date. If you feel like contributing to the project, this is an -area where you could help. - -=== Using conditional activation to implement banks/pages - -Users often ask if it's possible to do control surface bank-style mapping in order to switch to a completely -different set of mappings with the press of a button. Yes, it is! It's done using the _conditional activation_ feature -with the activation mode "When bank selected". - -TIP: ReaLearn >= 2.11.0 provides an alternative and probably more straightforward way to implement banks: The <> target. - -I'll show you a minimal example but in great detail. Once you understand this example, you should be able to progress to -bigger things. So let's assume you have 2 knobs and 2 buttons on your controller and you want to map some controls -to parameters of the https://vital.audio/[Vital synth]. Here's our goal: - -* *Knob K1:* Controls decay of ENV X -* *Knob K2:* Controls frequency of LFO X -* *Button B1:* Sets X to 1 -* *Button B2:* Sets X to 2 - -[discrete] -==== Step 1: Add all desired mappings - -First, it's important to understand that conditional activation does one thing only: It switches mappings on or off. -It doesn't magically change the target of a mapping or anything like that. Just on or off! Thus, the first thing you -should do is adding all the knob mappings (for example by using "Learn many"). Here's the result: - -image:images/tutorial-1-step-1.jpg[Step 1] - -Note: As you can see, I gave the mappings friendly names, which is nice in general but really pays off once you use the -projection feature. Also note that I used my Midi Fighter Twister preset and renamed the relevant encoders to K1 and K2. - -At this point, all those mappings are always active, so moving K1 will affect both ENV 1 and ENV 2 decay whereas moving -K2 will affect both LFO 1 and LFO 2 frequency! We need activation conditions to make sure that not all mappings are -active at the same time. - -[discrete] -==== Step 2: Assign mappings to groups - -Now we could shoot ahead and directly set the activation condition of each mapping individually. *But* usually it's -much better to activate/deactivate complete _groups_ of mappings. When you press button B1, you want to have the -"ENV 1 Decay" and "LFO 1 Freq" mappings active (= "Group 1"). When you press button B2, you want "ENV 2 Decay" and -"LFO 2 Freq" to be active instead (= "Group 2"). And this is just a minimal example. You will probably have many more -mappings in one group in the end. - -Turns out, ReaLearn has something made exactly for that: Mapping groups. Using them will make your life way easier. -We will create those 2 groups and distribute our knob mappings into both groups. - -. Right to "Mapping group", press "Add" and enter the name "Group 1". Repeat the same for "Group 2". -. Select mapping group `<Default>` again. -. Now move every mapping to its corresponding group by right-clicking the mapping row and choosing the desired group. - -Here's how "Group 1" looks like after this: - -image:images/tutorial-1-step-2.jpg[Step 2] - -Please note that until now, this is purely cosmetic. It hasn't changed in any way how the mappings work. - -[discrete] -==== Step 3: Set group activation conditions - -Now let's set the activation conditions. First for "Group 1": - -. Select mapping group "Group 1". -. Press "Edit". -. In the "Active" dropdown, choose "When bank selected". Make sure that "Parameter" is set to "1. Parameter 1" and -"Bank" to 0. - -Repeat the same for "Group 2", but set "Bank" to 1. Should look like this: - -image:images/tutorial-1-step-3.jpg[Step 3] - -Did you see how the mappings in "Group 2" turned grey? That means they became inactive! At this point, moving the knobs -should affect ENV 1 and LFO 1 only. - -[discrete] -==== Step 4: Understand "Parameter" and "Bank" - -In the previous step, we have set "Parameter" to "Parameter 1". It's important to understand that we are talking about -ReaLearn's own VST parameters. Each ReaLearn instance has 200 free parameters (100 per compartment) which don't do anything by default. -One easy way to make them visible is by pressing the "UI" button at the top right of the FX window to switch to the -parameter view: - -image:images/tutorial-1-step-4.jpg[Step 4] - -See "Parameter 1" at the top? That's the one we used in our activation condition! Hence, once we change the value of -this parameter, mappings will get activated or deactivated. You can try it! Move the parameter slider a bit to the right -and you will observe that "Group 1" turned inactive. "Group 1" will be active when the slider is on the very left. -"Group 2" will be active when the slider is pushed _slightly_ more to the right. If you push it even more to the right, -none of the mappings will be active. Enough! Press "UI" again to go back to the ReaLearn user interface. - -Now that we know that the value of ReaLearn's internal "Parameter 1" is the key to activate/deactivate our mappings, -the next step should be obvious: We need to map our buttons to it! - -[discrete] -==== Step 5: Map buttons to bank parameter - -We are going to map the buttons to "Parameter 1". Button B1 will set its value to 0 and button B2 will set its value -to 1. Remember how we defined these two numbers in the activation conditions … they are the "Bank" numbers! - -. Select mapping group `<Default>`. -. Map the two buttons. The easiest way is to use "Learn many", switch to the parameter view once again and move the -"Parameter 1" slider whenever ReaLearn asks you to touch the target. -** Before you continue, make sure your screen looks similar to this (take note how I've given the mappings friendly -names again): image:images/tutorial-1-step-5a.jpg[Step 5a] -. Edit the mapping for button B1 and set both Target Min/Max to 0 (this causes the button to always set the fixed -value 0). -** If you have a controller that is capable of feedback (button has LED), also set "Out-of-range behavior" to "Min". -This makes sure that the LED lights up whenever this bank is selected but switches off otherwise. -. Edit the mapping for button B2 and set both Target Min/Max to 1. -** Here's how the mapping panel for button B2 looks afterwards: image:images/tutorial-1-step-5b.jpg[Step 5b] -** If feedback is desired, set "Out-of-range behavior" as described in the previous step. - -That's it, the goal is achieved! Press the buttons and move the knobs to test it. - -You might wonder why ReaLearn has been designed to use this particular mechanism for activating/deactivating mappings, -in particular why it uses generic parameters to do the job. The answer is: This mechanism is insanely powerful. If you -take the time and digest this for a while, you will realize that you can do almost anything with a clever combination of -the "Mapping", "Parameter" and "Activation condition" concepts. This scenario is just one of many. Just see the next -tutorial to understand why. - -=== The same but with previous/next buttons - -Now let's assume you don't want 2 buttons where each button should activate one particular bank but you want -previous/next buttons to switch between the banks. Do everything as in tutorial 1 with the exception of step 5. - -TIP: ReaLearn >= 2.11.0 provides an alternative and probably more straightforward way to implement cycling through banks with previous/next buttons: By combining multiple mappings with <> target (for defining the banks) with one <> targets (for cycling). - -[discrete] -==== Step 5: Map buttons to bank parameter - -. As in tutorial 1. -. As in tutorial 1. -. Edit the mapping for button B2 ("Next group") and set mode to "Incremental button" -. Edit the mapping for button B1 ("Previous group"), set mode to "Incremental button" _and_ check the "Reverse" box -(because you want to go in the other direction). - -The "Previous group" mapping then looks like this: - -image:images/tutorial-2-step-5.jpg[Step 5] - -=== Using "Auto-load" to control whatever plug-in is currently in focus - -This one seems to be a very popular use case: To create a dedicated set of mappings for a specific FX plug-in and load -these mappings whenever focusing that plug-in on the screen. The easiest way to do this is to use the "Auto-load" -feature. - -To have a nice example, let's assume you want to build a first set of mappings for the VSTi plug-in -https://vital.audio/[Vital]. The procedure for other plug-ins is the same. - -[discrete] -==== Step 1: Activate the correct controller preset - -Before you start, I strongly recommend downloading a ReaLearn controller preset for your specific controller from ReaPack and activate it in the _controller compartment_. You will need to right-click the header panel and choose -<> to make a newly downloaded controller preset appear in the preset list. If there's no -suitable preset for your controller available on ReaPack or in the https://github.com/helgoboss/realearn/tree/master/resources/controller-presets/unofficial[list of unofficial controller presets], build your own. - -This step is completely optional, but it gives you many advantages, both in the short and long run. Please see -section <> for details. - -[discrete] -==== Step 2: Create mappings for your FX plug-in - -In this step you will tell ReaLearn which control element on your controller should control which parameter of your FX -plug-in: - -. Add Vital VSTi and a new _empty_ ReaLearn instance, both as track FX (**not as monitoring FX**), preferably -side-by-side so that you can see both. -** It's also possible to use monitoring FX but for this particular tutorial it's important to use track FX (otherwise -ReaLearn will not ask you if it should make your mappings project-independent in step 3). -. In ReaLearn, press _Learn many_. -. Move a control element on your controller, change a Vital parameter, move another control element, change another -Vital parameter … until you are finished! -. Press _Stop_. - -[discrete] -==== Step 3: Save mappings as main preset and link it to the FX type - -Now let's save your newly created set of mappings as preset and link the preset to the Vital VSTi plug-in: - -. Make sure the _main_ compartment is shown. -. Press _Save as…_ (next to _Preset_). -** ReaLearn will ask you if it should make your mappings project-independent. Answer with _Yes_ (important). -. Enter a descriptive preset name, e.g. "Vital". -. Right-click ReaLearn's header panel → `Global FX-to-preset links` → `<Add link from FX "Vital.dll" to...>` and choose -the previously created "Vital" preset. -** The name `Vital.dll` can vary, depending on your operating system. -** If it doesn't mention _Vital_ but another VST plug-in, focus your Vital VSTi plug-in instance for a moment and -then go directly to ReaLearn and right-click the header panel. - -[discrete] -==== Step 4: Activate "Auto-load" - -Now you just have to set _Auto-load_ to _Based on instance FX_. Since the <> is by default the currently focused FX, ReaLearn will from now on activate your "Vital" preset -whenever Vital VSTi plug-in has focus. If you want this in all projects without having to add ReaLearn to each -project manually, add a dedicated ReaLearn instance to REAPER's monitoring FX chain (REAPER → View → Monitoring FX). - -== FAQ - -=== How many instances and where to put them? - -Since ReaLearn is a VST instrument, you can have many instances of it, not just one. A question that comes up pretty -often is how many is right and where to put them? - -There's no definitive answer to that, it all depends on what you want. Here are some basic rules of thumb: - -. You need at least one ReaLearn instance per controller. -. For mappings that shall be available in any existing or new project automatically, create a ReaLearn instance on the -monitoring FX chain -** If you want to use multiple controllers, simply add multiple instances to the monitoring FX chain. -. For in-project mappings which control arbitrary parameters on arbitrary tracks, you are totally free to choose where -to put ReaLearn, there shouldn't be any differences in behavior. -** Putting it on the master FX chain is a good choice if you want it out of your way and usually not be visible. -** However, in order to be reminded that I use ReaLearn, I usually add some track at the very top and put all -ReaLearn instances on its FX chain. -. Let's say you have a VST instrument that you want to activate/deactivate for live playing by arming/disarming the -corresponding track. And now you want to use ReaLearn as a kind of "Insert" effect for controlling parameters of that -VST instrument (or other track-local parameters such as the track volume), only when it's active. Then put ReaLearn -exactly on that track, somewhere _above_ your VST instrument and select MIDI input `<FX input>`. -** That way your VST instrument won't receive MIDI that's already processed by ReaLearn, which is cool. -** If you want ReaLearn to only receive MIDI messages that originate from live playing and ignore MIDI that -originates from track MIDI items, put it on the input FX chain of that track. - -=== Should I use conditional activation or the "Enable/disable mapping" target? - -ReaLearn 2.11.0 introduces an alternative to <>: The ability to tag mappings and enable/disable them via the <> target. In general, one can say that conditional activation is slightly more powerful but that the enable/disable target is easier to use and enough in most common use cases. - -You strictly need to use conditional activation if you ... - -* ... want to activate/deactivate mappings using REAPER automation envelopes. -* ... want to sync the active/inactive state of a mapping with a <> expression. -* ... want to activate/deactivate mappings in another ReaLearn instance. -* ... want to activate/deactivate mappings in another compartment within the same ReaLearn instance. -+ -NOTE: This particular limitation of the enable/disable target might disappear in the future. - -You might prefer conditional activation if you ... - -* ... have a modifier use case (not a bank switching use case). -** It can be quite intuitive to think of modifiers as parameters that you define once and refer to them in the mapping (to be activated or deactivated) itself. -* ... have a modifier use case in which you want to combine multiple modifiers (e.g. activate a mapping only if the _Shift_ and _Control_ button is pressed at the same time) -** This is really much easier to achieve using conditional activation, think about it. -* ... prefer that the mapping itself defines when it should be active or not (instead of dictating it via tags). - -In all other circumstances the enable/disable target should be fine. - -==== More technical explanation - -Conditional activation introduces a level of indirection. It allows you to look at the act of enabling/disabling mappings as _two different concerns_ by introducing an intermediate concept called a "modifier" (respectively a "bank"). - -In particular, it separates the following two concerns: - -1. Switching a modifier on/off (respectively activating a bank) -2. Let other mappings follow the on/off state of the modifier (respectively the active bank) - -You define these two concerns in different mappings: - -1. "Modifier/bank-changing mappings" -2. "Modifier/bank-dependent mappings" (as activation condition) - -If you use the alternative to conditional activation, <>, you throw both of these concerns into one mapping! - -The advantage of separating these two concerns is that you can change *1* (the modifier/bank-changing mappings, e.g. which button controls the modifier and how) without having to touch *2* (the dependent mappings)! And vice versa. That can make complex setups easier to understand and reason about! - -The disadvantage is that it makes simple setups a bit harder to understand than necessary since you need at least two mappings instead of only one. - -As always: Choose the right tool for the job and consider starting off with the easiest tool. - - -[#tested-controllers] -== Controller support - -=== Explanation - -ReaLearn strives to support any general-purpose MIDI/OSC controller out there. However, there are some things you should know: - -. Not every controller works out of the box. -** There are controllers which might need some initial preparation in order to work optimally with ReaLearn. -** Don't fear this initial effort, it can pay off very quickly. ReaLearn is designed to get the most out of -your controller and make it work the way _you_ want it, not some company that wants to impose a certain type -of workflow on you. -** The versatility of a controller is a weakness but at the same time a strength. Taking some time to -get to know and prepare your controller can make you end up with a tool that is much better suited for -what you are trying to do than some bling-bling off-the-shelf solution. -. Some controllers don't work perfectly, especially when it comes to the _feedback_ direction. -** Among those controllers that support MIDI feedback, not all of them handle the feedback messages flawlessly. -** Depending on the nature of the particular problem, it might be possible to fix it in future ReaLearn versions. -Therefore, if you encounter a problem in this area, feel free to -https://github.com/helgoboss/realearn/issues[raise an issue]. -. Some controllers might have unique features that you can only use if you bring a bit of MIDI know-how and are ready -to use advanced ReaLearn features. -** Example: A controller might offer a way to change the appearance of an LED ring, but only via system-exclusive -MIDI messages. -** First, have a look if there's a controller preset already. Maybe it supports those advanced features already. -** If not, ReaLearn offers the following features for such scenarios: -*** <> (e.g. for sending MIDI sys-ex data on mapping -activation) -*** <> (for sending MIDI sys-ex data in response to target value changes) -*** <> (same but for more complex scenarios) -*** <> target (for sending MIDI sys-ex data triggered by a source) - -So even ReaLearn is made for any controller, it's still useful to have a list of specific controllers and how they work in combination with ReaLearn. This list is available link:https://github.com/helgoboss/realearn/tree/master/doc/controllers.adoc[here] - -=== General tips regarding controller setup and usage - -The following basic setup hints are usually valid, no matter the specific controller: - -* Put your controller's buttons into momentary mode, _not_ toggle mode. -* If you are in the lucky situation of owning a controller with endless rotary encoders, by all -means, configure them to transmit relative values, not absolute ones! -** Otherwise, you can't take advantage of ReaLearn's advanced features for sources emitting -relative values, such as the "Step size" or "Speed" setting. -** Also, preventing parameter jumps can never be as effective in absolute mode as in relative mode. -* If there are issues, consult the <> section in the first part of this guide. - -Consider the following general usage hints: - -- If the device supports visual feedback and different LED colors, the LED color often depends on the target value and -can be manually adjusted using "Source Min/Max" in the "Glue" section of the mapping. - -[IMPORTANT] -==== -Make sure to watch out for dedicated controller presets on the Helgoboss ReaPack repository and https://github.com/helgoboss/realearn/tree/master/resources/controller-presets/unofficial[list of unofficial controller presets]! - -Using an existing preset might save you a lot of mapping work (and possibly also layout work, if you want to use the projection feature). -==== - -=== List of tested controllers - -The list of tested controllers is now available as link:https://github.com/helgoboss/realearn/blob/master/doc/controllers.adoc[separate document]. - -== Available presets - -The lists of currently available controller and main presets are available here: - -- link:https://github.com/helgoboss/realearn/tree/master/resources/controller-presets[Controller presets] -- link:https://github.com/helgoboss/realearn/tree/master/resources/main-presets[Main presets] - - - -== Reference - -So far we've covered the basics. Now let's look into everything in detail. - -=== Main panel - -[#header-panel] -==== Header panel - -The header panel provides the following user interface elements, no matter if the _main_ or -_controller_ compartment is shown: - -[#control-input] -===== Input - -By default, ReaLearn captures MIDI events from _<FX input>_, which - consists of all MIDI messages that flow into this ReaLearn VSTi FX instance (= track MIDI path). - Alternatively, ReaLearn can capture events from a MIDI device directly, from an OSC device or from your computer keyboard. Be aware that MIDI will only work if _Enable input - from this device_ is checked for the selected MIDI input device in REAPER's MIDI preferences. - -[#feedback-output] -===== Output - -Here you can choose if and where ReaLearn should send MIDI/OSC feedback. By - default it's set to __ for no feedback. If you want to enable feedback, pick a MIDI or OSC - output device here. Keep in mind that for MIDI, _Enable output to this device_ must be checked in REAPER's - MIDI preferences. As an alternative, you can send feedback to _<FX output>_, which makes - feedback MIDI events stream down to the next FX in the chain or to the track's hardware MIDI output. - -[WARNING] -==== -Please note that sending MIDI feedback - to the FX output has some drawbacks. First, it doesn't participate in ReaLearn's multi-instance feedback - orchestration. That means you might experience LEDs/faders misbehaving when using multiple instances. Second, it - doesn't work if ReaLearn FX is suspended, e.g. in the following cases: - -* ReaLearn FX is disabled. -* Project is paused and ReaLearn track is not armed. -* ReaLearn FX is on input FX chain and track is not armed. -==== - -===== Menu - -This opens ReaLearn's main menu. It's also accessible via right-click on Windows and Linux and control-click -on macOS. It provides the following entries. - -====== Copy listed mappings - -Copies all mappings that are visible in the current mapping list to the clipboard -(respecting group, search field and filters). You can insert them by opening the context menu in the row panel. - -====== Paste mappings (replace all in group) - -Replaces all mappings in the current group with the mappings in the -clipboard. - -====== Auto-name listed mappings - -Clears the names of all listed mappings so ReaLearn's dynamic auto-naming mechanism can kick in. - -====== Name listed mappings after source - -Sets the names of each listed mapping to the first line of its source label. - -====== Make sources of all main mappings virtual - -Attempts to make the sources in the main compartment virtual by matching them with the sources in the controller compartment. This is useful if you already learned a bunch of sources in the main compartment, just to find out later that you would like to have used a controller preset. - -====== Make targets of listed mappings sticky - -Changes the targets of all currently listed mappings to use "sticky" object selectors by attempting to resolve the objects from non-sticky selectors. We call object selectors _sticky_ if they refer to a very particular object (e.g. a track). - -* Sticky selectors: ``, ``, `Particular` -* Non-sticky selectors: ``, ``, ``, ``, `, `At position`, `Named`, `All named` - - -====== Move listed mappings to group - -Lets you move all currently listed mappings to the specified group. Perfect in combination with the textual search! - -====== Advanced - -Provides access to expert features. - -* *Copy listed mappings as Lua:* Like _Copy listed mappings_ but generates Lua code instead. -* *Copy listed mappings as Lua (include default values):* Generates Lua code that contains even those properties that correspond to ReaLearn's defaults. -* *Paste from Lua (replace all in group):* Like _Paste mappings (replace all in group)_ but treats the clipboard content as Lua code. -* *Dry-run Lua script from clipboard*: Executes the Lua code in the clipboard and opens the returned data structure in a text editor. -+ -[NOTE] -==== -The way Lua import works in ReaLearn is: - -. ReaLearn executes the Lua script (from clipboard). -. ReaLearn attempts to interpret the return value as ReaLearn API object. -. ReaLearn loads the API object - -If step 1 fails, ReaLearn displays an error messages that hopefully contains a line number. - -If step 2 fails, ReaLearn shows a validation error message. - -The command _Dry-run Lua script from clipboard_ enables you to just execute step 1 and see the "expanded" result. This can help to make sense of a possible validation error message in step 2. -==== -* *Freeze clip matrix*: Don't use this, this feature is not ready yet! - -[#options] -====== Options - -* *Auto-correct settings:* By default, whenever you change something in ReaLearn, it tries to -figure out if your combination of settings makes sense. If not, it makes an adjustment. -This auto-correction is usually helpful. If for some reason you want to disable auto-correction, this -is your checkbox. -* *Send feedback only if track armed:* If input is set to _<FX input>_, -ReaLearn by default only sends feedback if the track is armed (unarming will naturally disable -control, so disabling feedback is just consequent). However, if input is set to a -MIDI or OSC device, _auto-correct settings_ will take care of unchecking this option in order to allow feedback -even when unarmed (same reasoning). You can override this behavior with this checkbox. At the moment, -it can only be unchecked if ReaLearn is on the normal FX chain. If it's on the input FX chain, unarming -naturally disables feedback because REAPER generally excludes input FX from audio/MIDI processing while a -track is unarmed (*this is subject to change in future!*). -* *Reset feedback when releasing source:* When using ReaLearn the normal way, it's usually desired that feedback is reset when the corresponding sources are not in use anymore (e.g. lights are switch off, displays are cleared, motor faders are pulled down). You can prevent this ReaLearn instance from doing this by disabling this option. This can be useful e.g. when using REAPER/ReaLearn to control a hardware device (essentially using ReaLearn the other way around, "controlling from target to source"). -* *Make instance superior:* If ticked, this instance is allowed to suspend other instances which share the same -input and/or output device (hardware devices only, not FX input or output!). With this you can easily let your -controller control e.g. the currently focused FX but fall back to your usual controls when it's closed. It's intended -to be used primarily on instances that use <>. -+ -TIP: Since ReaLearn 2.14.0, falling back to normal mappings when the FX loses focus in auto-load mode became much easier! One instance is enough. Your normal mappings will be memorized and reloaded once the FX loses focus. See <>. -+ -** By default, ReaLearn instances are not superior, just normal. This is often okay because ReaLearn instances -are friendly fellows and like sharing controllers with each other. -** For example, if 2 instances use the same input or output device and they use different control elements, they -can peacefully coexist. And even if they share a control element for the _control direction_, they are still -fine with it. The same control element will control 2 mappings, why not! -** Things start to get hairy as soon as 2 instances want to send _feedback_ to the same control elements at the -same time. You should avoid this. You should not even do this within one ReaLearn instance. This can't work. -** Sometimes you want one instance to suspend/cover/cancel/mute another one! You can do this by making this -instance _superior_. Then, whenever this instance has at least one active mapping, all non-superior instances -with the same control and/or feedback device will be disabled for control and/or feedback. -** You can have multiple superior instances. Make sure they get along with each other :) -* *Use instance-wide FX-to-preset links only:* By default, instance-specific links are applied _in addition_ to the global links and take precedence over the global ones. This checkbox makes sure that only instance-specific links are used. -* *Stay active when project in background:* Determines if and under which conditions this ReaLearn instance should stay active when the containing project tab is not the active one. Applies to in-project ReaLearn instances only, not to monitoring FX instances! -** *Never:* Will only be active when its project tab is active. -** *Only if background project is running:* Follows REAPER's project tab settings ("Run background projects" and "Run stopped background projects"). -** *Always (more or less):* Attempts to stay active no matter what. Please note that this is technically not always possible when using __ or __ when the background project is not running. - - -====== Server - -ReaLearn features a built-in server which allows the <> (and in future also the Playtime app) to connect to ReaLearn. The server runs globally, not per instance! - -* *Enable and start!:* This starts the server and makes sure it will automatically be started next time you use ReaLearn. -* *Disable and stop!:* This stops the server and makes sure it will not be started next time you use ReaLearn. -* *Add firewall rule:* Attempts to add a firewall rule for making the server accessible from other devices or -displays instructions how to do it. - -====== Open preset folder - -Opens the ReaLearn preset folder in a file manager. - -[#reload-all-presets] -====== Reload all presets from disk - -If you made direct changes to preset files or have downloaded presets via ReaPack, you should press this to reflect these changes in the preset lists of all open ReaLearn instances (reloads all preset files). - -[NOTE] -==== -This *will not* yet apply an adjusted preset, it will just reload the list. If you want to apply a preset that has been changed on disk, you need to select it in the preset dropdown once again! -==== - -[#pot-browser] -====== Open Pot Browser - -This will open Pot Browser, a modern preset browser. It's recommended to use this from a ReaLearn instance on the monitoring FX chain, that way you have the browser accessible from any project. - -TIP: Add a toolbar button which triggers the REAPER action "ReaLearn: Open first Pot Browser" to get quick and convenient access to the browser. - -Remarks: - -- Pot Browser is in an experimental stage, it doesn't save any of your settings! -- Each ReaLearn instance has its own so-called _Pot Unit_. Each Pot Unit has its own filter and preset state. When you open the Pot Browser from an instance, it connects to the Pot Unit of that instance. -- ReaLearn's "Pot" targets such as <> can be used to control the Pot Unit from any controller. - -[#osc-devices] -====== OSC devices - -Allows one to display and modify the list of (globally) configured OSC devices. - -* *:* Opens a window for adding a new OSC devices. -** *Name:* A descriptive name of the device, e.g. "TouchOSC on my Android phone". -** *Local port:* Required for control. The UDP port on which ReaLearn should listen for OSC control messages. -*** *Important:* This port must be reserved exclusively for ReaLearn. If you already use this port -in another application (e.g. in REAPER's own OSC control surface) it won't work and ReaLearn will bless -you with an "unable to connect" message in the "Input" dropdown. -** *Device host:* Required for feedback only. It's the IP address of the OSC device to which ReaLearn -should send feedback messages. This address is usually displayed on your OSC device (e.g. as "Local IP address"). When targeting an OSC software that runs on the same computer as REAPER and -ReaLearn, enter the special IP address `127.0.0.1` ("localhost"). -+ -[TIP] -==== -When you configure your OSC device, you must provide a _host_ as well. There you should enter the IP address of the computer which runs REAPER and ReaLearn. - -You can easily find it by pressing the <> button in ReaLearn and scrolling down a bit. It's the value next to *Host* and should start with `192.168.`. -==== -** *Device port:* Required for feedback only. The UDP port on which the OSC device listens for OSC feedback -messages. -** All OSC device configurations will be saved in the REAPER resource directory -(REAPER → Actions → Show action list… → Show REAPER resource path in explorer/finder) in the JSON file -`Helgoboss/ReaLearn/osc.json`. -* *_Some device_* -** *Edit:* Lets you edit an existing device (see _<New>_). -** *Remove:* Removes the device. This is a global action. As a consequence, all existing ReaLearn instances -which use this device will point to a device that doesn't exist anymore. -** *Enabled for control:* If you disable this, ReaLearn will stop listening to this device. This can save -resources, so you should do this with each device that is not in use (as an alternative for removing it -forever). -** *Enabled for feedback:* If you disable this, ReaLearn won't connect to this device. -** *Can deal with bundles:* By default, ReaLearn aggregates multiple OSC messages into so-called OSC bundles. -Some devices (e.g. from Behringer) can't deal with OSC bundles. Untick the checkbox in this case and ReaLearn -will send single OSC messages. - -[#compartment-parameters] -====== Compartment parameters - -This shows all parameters of the current compartment (you know, the ones that can be used -for conditional activation and __ selector expressions) and makes it possible to customize them. This is practical because it's completely up to you how to put these parameters to use. - -Perfect for preset authors: Parameter settings are saved together with the compartment preset. Parameter values will be reset whenever you load a preset (just the ones in that compartment). - -* *Param _x_ Name:* Changes the name of this parameter. -* *Value count:* By default, ReaLearn parameter values are continuous in nature: They are arbitrary decimal numbers between 0.0 and 1.0. Although that's very flexible, it's often easier to work with a discrete value range. Entering a value count turns the parameter into a discrete parameter with the given number of integer values. For example, a value count of 10 means that the parameter can represent exactly 10 values (0 to 9). - -[CAUTION] -==== -*Choose the value count wisely and think twice before changing it to a different value at a later point in time!* - -Reason: You probably want to refer to values of this parameter in certain parts of ReaLearn, e.g. in <>. If you do that and later change the value count, these value references will not be valid anymore. They will point to other integers than you intended to. So if you are not sure, better pick a large value count and stick to it! -==== - -[#logging] -====== Logging - -* **Log debug info (now):** Logs some information about ReaLearn's internal state. Can be interesting for -investigating bugs or understanding how this plug-in works. -* ** Log real control messages:** When enabled, all incoming MIDI messages, OSC messages or key pressed will be logged to the console. Each log entry contains the following information: -** Timestamp in seconds -** ReaLearn instance ID (a randomly assigned ID that uniquely identifies a particular instance, will change after -restart) -** Message purpose -*** *Real control:* A message used for controlling targets. -*** *Real learn:* A message used for learning a source. -** Actual message (MIDI messages will be shown as hexadecimal byte sequence, short MIDI messages also as -decimal byte sequence and decoded) -** Match result -*** *unmatched:* The message didn't match any mappings. -*** *matched:* The message matched at least one of the mappings. -*** *consumed:* Only for short MIDI messages. This short message is part of a (N)RPN or 14-bit CC message and -there's at least one active mapping that has a (N)RPN or 14-bit CC source. That means it will not be -processed. The complete (N)RPN or 14-bit CC message will be. -* **Log virtual control messages:** When enabled, all triggered virtual control elements and their values will be logged (see <>). -* **Log target control:** When enabled, all target invocations (parameter changes etc.) will be logged. -* **Log virtual feedback messages:** When enabled, all feedback events to virtual control elements will be logged (see <>). -* **Log real feedback messages:** When enabled, all outgoing MIDI or OSC messages will be logged to the console. The log entries look similar to the ones described above, with the following notable differences. -** Message purpose -*** *Feedback output:* A message sent to your controller as response to target value changes. -*** *Lifecycle output:* A message sent to your controller as response to mapping activation/deactivation -(see <>). -*** *Target output:* A message sent because of either the <> or -<> target. - -====== Send feedback now - -Usually ReaLearn sends feedback whenever something changed to keep the LEDs -or motorized faders of your controller in sync with REAPER at all times. There might be situations -where it doesn't work though. In this case you can send feedback manually using this button. - -===== Export to clipboard - -Pressing the export button allows you to copy ReaLearn's settings to the clipboard so you can import them in another instance or edit them in a text editor. - -* *Export session as JSON:* Copies a _complete_ dump of ReaLearn's current settings (including all mappings, even controller mappings) to the clipboard. The dump's data format is - https://www.json.org/json-en.html[JSON], a wide-spread data exchange format. It's a text format, - so if you are familiar with the search & replace feature of your favorite text editor, this is one way to do batch editing. However, recent versions of ReaLearn provide a much better way of doing that: _ReaLearn Script_. Read about the other export options for learning more about it. -+ -[TIP] -==== -You can also use the export for some very basic A/B testing: - -1. Choose _Export session as JSON_ -2. Change some settings and test them -3. Restore the old settings by pressing _Import from clipboard_. -==== -+ -[TIP] -==== -For the programmers and script junkies out there: It's perfectly possible to program ReaLearn from outside by passing it a snippet of JSON via https://www.reaper.fm/sdk/reascript/reascripthelp.html#TrackFX_SetNamedConfigParm[`TrackFX_SetNamedConfigParm()`]. Parameter name is `set-state`. This mechanism is implemented on ReaLearn side using https://www.reaper.fm/sdk/vst/vst_ext.php[REAPER's named parameter mechanism] (search for `named_parameter_name`). - -Example that assumes that the first FX of the first track is a ReaLearn instance: - -[source,lua] ----- -local track = reaper.GetTrack(0, 0) -local state = [[ -{ - "controlDeviceId": "62", - "feedbackDeviceId": "fx-output", - "mappings": [ - { - "name": "1", - "source": { - "type": 1, - "channel": 0, - "number": 64 - }, - "mode": {}, - "target": { - "type": 2 - } - } - ] -} -]] -reaper.TrackFX_SetNamedConfigParm(track, 0, "set-state", state) ----- -==== -* *Export main/controller compartment as JSON:* Copies a dump of the currently visible compartment to the clipboard. It contains about the same data that a compartment preset would contain. -* *Export main/controller compartment as Lua:* Copies a dump of the currently visible compartment to the clipboard as Lua code (ReaLearn Script). This form of Lua export skips properties that correspond to ReaLearn's default values, resulting in a minimal result. Perfect for pasting in a forum or programming ReaLearn with focus on only those properties that matter to you. -* *Export main/controller compartment as Lua (include default values):* This Lua export includes even those properties that correspond to ReaLearn's default values, resulting in more text. This gives you the perfect starting point if you want to extensively modify the current compartment (using the Lua programming language) or build a compartment from scratch, using even properties that you haven't touched yet in the user interface! - -===== Import from clipboard - -Pressing the import button does the opposite: It restores whatever ReaLearn dump is currently in the clipboard. - -====== "?" (Help) - -Provides helpful links to the user guide and other stuff. - - -[#projection] -===== Projection - -This is a quite unique feature that allows you to project a schematic representation - of your currently active controller to a mobile device (e.g. a tablet computer). You can put this device close - to your controller in order to see immediately which control element is mapped to which parameter. - This is an attempt to solve an inherent problem with generic controllers: That it's easy to forget which control - element is mapped to which target parameter. If you want to use this feature, just click this button - and you will see detailed instructions on how to set this up. In order to use this feature, you need the - _ReaLearn Companion_ app, which has a <> in this user guide. - -===== Let through - -ReaLearn by default "eats" incoming MIDI events for which there's at least one active mapping with that source. In other words, it doesn't forward MIDI events which are used to control a target parameter. However, unmatched MIDI events are forwarded! - -You can change this using these checkboxes. E.g. you can tick *Matched events* if you want to forward even matched events. The exact behavior differs depending on what you choose as <>: - -* If input is set to -** MIDI events arrive from ReaLearn's FX input. If they get forwarded, they get forwarded to the FX output, usually to the plug-in which is located right below ReaLearn FX. The default setting often makes much sense here, especially if you put ReaLearn right above another instrument plug-in. -* If input is set to a MIDI hardware device -** MIDI events arrive directly from the MIDI hardware device. If they get forwarded, they get forwarded to REAPER's tracks as they would usually do without ReaLearn. If they don't get forwarded, it means they get filtered and will never make it to the tracks. ReaLearn completely eats them, globally! That means, ReaLearn can act as global MIDI filter. -** Please note, with input set to a real MIDI device, MIDI events coming from _FX input_ are _always_ forwarded to the FX output. -** Also, MIDI events captured from a real MIDI device input are *never* forwarded to ReaLearn's FX output. -+ -TIP: This global MIDI filter feature is only available in REAPER v6.36+. -* If input is set to a OSC device -** You won't see the checkboxes because they don't make sense for OSC. -* If input is set to computer keyboard -** You can control whether key presses are forwarded to REAPER or not. -** For example, unticking both checkboxes makes sure that only keyboard hotkeys defined in ReaLearn have an effect. This can be interesting for live scenarios in which you temporarily want to lower the risk of pressing the wrong key and messing up the performance. Just unlock the keys you absolutely need. - -===== Show - -This lets you choose which mapping compartment is displayed. A compartment is basically a list of mappings - that can be saved as independent preset. Initially, ReaLearn shows the so-called "Main compartment" which contains - the so-called "Main mappings" - the bread and butter of ReaLearn. However, there's another interesting compartment, - the "Controller compartment". In a nutshell, this compartment lets you define which hardware controllers you have at - your disposal and which control elements they have. Learn more about that feature in section - <>. - -===== Controller preset / Main preset - -This is the list of available presets for that compartment. By default, it's set - to "<None>", which means that no particular preset is active. If you select a preset in this list, its - corresponding mappings will be loaded and immediately get active. In the _controller_ compartment, this list - will essentially represent the list of available hardware controller presets. A few are shipped with ReaLearn itself - (separately downloadable via ReaPack) but you can also define your own ones and add them to this list! - -* *Save:* If you made changes to a preset, you can save them by pressing this button. This works for built-in presets - as well but I would strongly recommend against changing them directly. Better use _Save as…_ and choose a custom - name. - -===== Save as… - -This allows you to save all currently visible mappings as a new preset. Please choose a descriptive - name. - -** Saving your mappings as a preset is optional. All controller mappings are saved together - with your current ReaLearn instance anyway, no worries. But as soon as you want to reuse these - mappings in other ReaLearn instances, it makes of course sense to save them as a preset! -** All of your presets end up in the REAPER resource directory - (REAPER → Actions → Show action list… → Show REAPER resource path in explorer/finder) at - `Data/helgoboss/realearn/presets`. They are JSON files and very similar to what you get when you press - _Export to clipboard_. -** Those files are usually in the root of that `presets` directory but can also reside in sub directories (one level only, sub/sub directories are not supported). Please note that the sub directory name becomes a part of the preset ID, so better don't move existing presets around if you want preset references of existing ReaLearn instances to stay intact. -** JSON files can also contain custom data sections. For example, the ReaLearn - Companion app adds a custom data section to controller presets in order to memorize the positions and shapes of all control elements. -** When pressing this button, ReaLearn might detect that your current mappings are referring to specific tracks and - FX instances _within the current project_. This would somehow defeat the purpose of presets because what good - are presets that are usable only within one project? That's why ReaLearn also offers you to automatically - convert such mappings to project-independent mappings by applying the following transformations: -*** FX targets are changed to refer to _current instance FX_* instead of a particular one. Their track is set to - *<This>* because it doesn't matter anyway. -*** Track targets are changed to refer to a track via its position instead of its ID. -** If this is not what you want, you can choose to say no and make modifications yourself. - -===== Delete - -This permanently deletes the currently chosen preset. You can also delete built-in presets. - However, if you use ReaPack for installation, it should restore them on next sync. - -===== Notes - -Allows you to save custom notes/comments for the current compartment. These notes are also included in compartment presets. - -===== Add one - -Adds a new mapping at the end of the current mapping list. - -===== Learn many - -Allows you to add and learn many new mappings in a convenient batch mode. Click this button and follow - the on-screen instructions. Click _Stop_ when you are finished with your bulk learning strike. -[#search] - -===== Search - -Enter some text here in order to display just mappings whose name matches the text. The search expression - also supports wildcards `*` and `?` for doing blurry searches. `*` stands for zero or more arbitrary characters and `?` stands for one arbitrary character. - -===== Filter source - -If you work with many mappings and you have problems memorizing them, you - will love this feature. When you press this button, ReaLearn will start listening to incoming MIDI/OSC - events and temporarily disable all target control. You can play around freely on your controller - without having to worry about messing up target parameters. Whenever ReaLearn detects a valid - source, it will filter the mapping list by showing only mappings which have that source. This is a - great way to find out what a specific knob/fader/button etc. is mapped to. Please note that the - list can end up empty (if no mapping has that source). As soon as you press _Stop_, the current - filter setting will get locked. This in turn is useful for temporarily focusing on mappings with a - particular source. When you are done and you want to see all mappings again, press the *X* - button to the right. _Tip:_ Before you freak out thinking that ReaLearn doesn't work anymore - because it won't let you control targets, have a quick look at this button. ReaLearn might still - be in "filter source" mode. Then just calm down and press _Stop_. It's easy to forget. - -===== Filter target - -If you want to find out what mappings exist for a particular target, - press this button and touch something in REAPER. As soon as you have touched a valid target, the - list will show all mappings with that target. Unlike _Filter source_, ReaLearn will - automatically stop learning as soon as a target was touched. Press the *X* button to remove the - filter and show all mappings again. - -[#bottom-panel] -==== Bottom panel - -At the bottom you can see: - -- The current scroll position. -- The session ID of this this ReaLearn instance. -- Tags assigned to this ReaLearn instance. -- Information about the current instance track and instance FX. -- Information whether control and/or feedback is currently inactive instance-wide. -- Information about what version of ReaLearn you have. - -===== Instance data... - -Press this button to change various key-value data of this ReaLearn instance as a whole. - -* *Session ID…:* This lets you customize the ID used to address this particular ReaLearn -instance when using the <> feature. -** By default, the session ID is a random cryptic string -which ensures that every instance is uniquely addressable. The result is that scanning the QR code -of this ReaLearn instance will let your mobile device connect for sure with this unique -instance, not with another one - remember, you can use many instances of ReaLearn in parallel. This -is usually what you want. -** But a side effect is that with every new ReaLearn instance that you create, -you first have to point your mobile device to it in order to see its -<> (by scanning the QR code). Let's assume you have in many of your projects exactly one ReaLearn instance -that lets your favorite MIDI controller control track volumes. By customizing the session ID, you basically can tell -your mobile device that it should always show the <> of this very ReaLearn instance - -no matter in which REAPER project you are and even if they control the volumes of totally -different tracks. -** You can achieve this by setting the session ID of each volume-controlling ReaLearn instance -to exactly the same value, in each project. Ideally it's a descriptive name without spaces, such as "track-volumes". -You have to do the pairing only once et voilà, you have a dedicated device for monitoring your volume control -ReaLearn instances in each project. -+ -[CAUTION] -==== -Make sure to not have more than one ReaLearn instance with the same session -ID active at the same time because then it's not clear to which your mobile device will connect! -==== -** *At the moment, the session ID is part of the ReaLearn preset!* That means, opening a preset, copying/cutting -a ReaLearn FX, importing from clipboard - all of that will overwrite the session ID. This might change in -future in favor of a more nuanced approach! -* *Tags:* Lets you assign tags to this instance (a comma-separated list). They are important if you want to dynamically enable or disable instances using the <> target. - -[#instance-track] -===== Instance track - -The second line of the bottom panel shows the current track chosen as **Instance track** for this instance of ReaLearn. This can be something like "Track 3" or "The currently selected track". Mappings in this ReaLearn instance can refer to this track by choosing the track selector <>. - -The instance track can be changed via target <>. - -[#instance-fx] -===== Instance FX - -The second line of the bottom panel also shows the current FX chosen as **Instance FX** for this instance of ReaLearn. This can be something like "FX 5 on track 3" or "The currently focused track". Mappings in this ReaLearn instance can refer to this FX by choosing the FX selector <>. - -The instance FX can be changed via target <>. - -==== Common compartment settings - -The header panel shows the following user interface elements, no matter if you are in the controller or main -compartment: - -* *Mapping group:* Mapping groups are part of the currently shown compartment and enable you to divide the list of -mappings into multiple groups. -** Groups can be useful … -*** To apply an activation condition to multiple mappings at once. -*** To enable/disable control/feedback for multiple mappings at once. -*** To keep track of mappings if there are many of them. -** This dropdown contains the following options: -*** *<All>:* Displays all mappings in the compartment, no matter to which group they belong. In this view, - you will see the name of the group on the right side of a mapping row. -*** *<Default>:* Displays mappings that belong to the _default_ group. This is where mappings - end up if you don't care about grouping. This is a special group that can't be removed. -*** *_Custom group_:* Displays all mappings in your custom group. -** You can move existing mappings between groups by opening the context menu (accessible via right-click on Windows - and Linux, control-click on macOS) of the corresponding mapping row and choosing "Move to group". -** Groups are saved as part of the project, VST plug-in preset and compartment preset. -* *Add:* Allows you to add a group and give it a specific name. -* *Remove:* Removes the currently displayed group. It will ask you if you want to remove all the mappings in that - group as well. Alternatively they will automatically be moved to the default group. -* *Edit:* Opens the group panel. This allows you to change the group name and change things that affect all mappings in this groups: Assigning tags, enabling/disabling control and/or feedback, setting an activation condition. The activation condition that you provide here is combined with the one that you provide in the mapping. Only if both, the group activation conditions and - the mapping activation condition are satisfied, the corresponding mapping will be active. Read more about - <> below in the section about the <>. - -image:images/screenshot-group-panel.png[Group panel] - -Since ReaLearn 2.10.0, mappings are processed from top to button, exactly in the order in which they are defined -within the corresponding compartment. This matters if you want to map multiple targets to one button and -the order of execution matters. - -*Important:* There's an exception. ReaLearn's processing of its own VST parameters is always deferred. - -- That means changing a ReaLearn parameter in one mapping and relying on it in the next - one (in terms of conditional activation or in a `<Dynamic>` expression), will not work! -- You can work around that by delaying execution of the next mapping via <> but - that's a dirty hack. ReaLearn's parameters are not supposed to be used that way! -- Imagine a railway: ReaLearn's targets can be considered as trains. Triggering a target means moving the train forward. - ReaLearn's parameters can be considered as railway switches. Changing a parameter means setting a course. - The course needs to be set in advance, at least one step before! Not at the same time as moving the train over the - switch. - -[#controller-compartment] -==== Controller compartment - -By default, ReaLearn shows the list of main mappings. If you switch to the _controller_ compartment, you will see the -list of controller mappings instead. Each controller mapping represents a control -element on your hardware controller, e.g. a button or fader. This view lets you describe your controller by - well - -by adding mappings. Almost everything in ReaLearn is a mapping :) - -Defining your own controllers can have a bunch of benefits: - -* You can use the awesome <> feature - to project your controller mapping to a smartphone or tablet (link:https://www.youtube.com/watch?v=omuYBznEShk&feature=youtu.be[watch video]). -* You can use controller presets made by other users and thereby save precious setup time. Or you can contribute them - yourself! -* You can make your main mappings independent of the actual controller that you use. This is done using so-called - _virtual_ sources and targets. -* It allows you to give your knobs, buttons etc. descriptive and friendly names instead of just e.g. "CC 15". -* You don't need to learn your control elements again and again. Although the process of learning an element is easy - in ReaLearn, it can take some time in case the source character is not guessed correctly - (e.g. absolute range element or relative encoder). Just do it once and be done with it! - -If you want to make ReaLearn "learn" about your nice controller device, all you need to do is to create a suitable -controller mapping for each of its control elements. - -Let's first look at the "slow" way to do this - adding and editing each controller mapping one by one: - -. Press the "Add one" button. -. Learn the source by pressing the "Learn source" button and touching the control element. -. Press the "Edit" button. -. Enter a descriptive name for the control element. -** _Hint:_ This name will appear in many places so you want it to be short, clear and unique! -. Assign a unique virtual target. -** At this point we don't want to assign a _concrete_ target yet. The point of controller presets is - to make them as reusable as possible, that's why we choose a so-called _virtual_ target. -** In the _Category_ dropdown, choose _Virtual_. -** As _Type_, choose _Button_ if your control element is a sort of button (something which you can press) - and _Multi_ in all other cases. -** Use for each control element a unique combination of _Type_ and _ID_, starting with number _1_ and counting. -*** Example: It's okay and desired to have one control element mapped to "Multi 1" and one to "Button 1". -** Just imagine the "8 generic knobs + 8 generic buttons" layout which is typical for lots of popular controllers. - You can easily model that by assigning 8 multis and 8 buttons. -** Maybe you have realized that the _Glue_ section is available for controller mappings as well! That opens up all - kinds of possibilities. You could for example restrict the target range for a certain control element. Or make - an encoder generally slower or faster. Or you could simulate a rotary encoder by making two buttons on your - controller act as -/+ buttons emitting relative values. This is possible by mapping them to the same "Multi" in - "Incremental button" mode. - -Before you go ahead and do that for each control element, you might want to check out what this is good for: Navigate -back to the _main_ compartment, learn the source of some main mapping and touch the control element that you -have just mapped: Take note how ReaLearn will assign a _virtual_ source this time, not a MIDI source! It will also -display the name of the control element as source label. Now, let's say at some point you swap your controller device -with another one that has a similar layout, all you need to do is switch the controller preset and you are golden! You -have decoupled your main mappings from the actual controller. Plus, as soon as you have saved your controller mappings -as a preset, you can take full advantage of the <> feature. - -All of this might be a bit of an effort but it's well worth it! Plus, there's a way to do this _a lot_ faster by -using _batch learning_: - -. Press the "Learn many" button. -. Choose whether you want to learn all the "Multis" on your controller or all the "Buttons". -. Simply touch all control elements in the desired order. -** ReaLearn will take care of automatically incrementing the virtual control element numbers. -. Press "Stop". -. Done! -** At this point it's recommended to recheck the learned mappings. -** ReaLearn's source character detection for MIDI CCs is naturally just a guess, so it can be wrong. If so, - just adjust the character in the corresponding mapping panel. - -You can share your preset with other users by sending them to link:mailto:info@helgoboss.org[info@helgoboss.org]. I will add it to https://github.com/helgoboss/realearn/tree/master/resources/controller-presets[this -list]. - -==== Main compartment - -The header panel for main mappings consists of a few more user interface elements: - -[#auto-load] -===== Auto-load - -If you switch this to _Based on instance FX_, ReaLearn will start to observe the <> of this ReaLearn instance. By default, the instance FX is set to ``, which means, it will reflect whatever FX is currently focused. Whenever the instance FX changes, it will check if you have linked a compartment preset - to it and will automatically load it. Whenever the instance FX switches to an unlinked FX or the FX loses focus, ReaLearn falls back to the mapping list defined before activating auto-load. Of course this makes sense only if you actually have linked some - presets. Read on! -The header context menu (accessible via right-click on Windows and Linux, control-click on macOS) for the main -compartment contains the missing piece of the puzzle: - -[#global-fx-to-preset-links] -====== Global FX-to-preset links - -Manage a global list of links from plug-ins to ReaLearn main compartment presets. - -* *Add link from last focused FX to preset:* This lets you link whatever FX window was focused before focusing - ReaLearn, to an arbitrary main compartment preset. Needless to say, this only works if an FX has been focused - before. -** All links will be saved _globally_, not just within this project! -** Location: REAPER resource directory (REAPER → Actions → Show action list… → Show REAPER resource path in - explorer/finder) at `Data/helgoboss/realearn/auto-load-configs/fx.json`. -* *_Arbitrary FX ID:_* If you have added a link already, you will see them here in a list. What you see, is the - so-called _FX ID_, which by default simply corresponds to the plug-in's original name (e.g. `VSTi: ReaSynth (Cockos)`). -** *<Edit FX ID…>:* With this, you can edit the FX ID manually. -*** _All fields_: All the fields below support wildcards. E.g. instead on relying on the original plug-in name you could match plug-ins with similar file names (e.g. VST2 -and VST3 at once): You can use `\*` for matching zero or arbitrary many characters and `?` for matching -exactly one arbitrary character. E.g. `Pianoteq 7 STAGE.*` would match both `Pianoteq 7 STAGE.dll` (VST2) -and `Pianoteq 7 STAGE.vst3` (VST3). -*** *FX name:* Allows you to adjust the (original) plug-in name that triggers the preset change. -*** *FX file name:* Allows you to adjust the plug-in file name that triggers the preset change. -*** *FX preset name:* Maybe the FX name or file name is not enough for you to decide which preset you want to load. - Good news: You can add a preset name as additional criteria! E.g. if you have use a sampler, you can load - different ReaLearn presets depending on which sample library is loaded into your sampler. Just add two - links with the same FX file name (e.g. `Kontakt 5.dll`) but different preset names. You can also use - wildcards here! -** *<Remove link>:* (Globally) this FX-to-preset link. -** *_Arbitrary main preset:_* The checkbox tells you to which main preset the FX ID is linked. You can change - the linked preset by clicking another one. - -====== Instance-wide FX-to-preset links - -This is like <> but saves the links as part of this ReaLearn instance. This is useful if you have 2 controllers (= and therefore 2 ReaLearn instances) and want them to auto-load different presets although the instance FX points to the same plug-in. - - -==== Mapping row - -The mapping, source and target labels of a mapping row should be greyed out whenever the mapping is _off_. A mapping is considered as _on_ only if the following is true: - -. The mapping is complete, that is, both source and target are completely specified. -. The mapping is enabled as a whole. -. The mapping has control and/or feedback enabled. -. The mapping is active (see _conditional activation_). - -If a mapping is _off_, it doesn't have any effect. - -* *✓:* This checkbox at the top left of the mapping row enables or disables the mapping as a whole. -* *●:* This indicator at the very left of the mapping row lights on incoming control messages whenever they match the mapping source. Attention: This doesn't necessarily mean that the message will reach the target (although it often does). There are certain settings in the <> section which allow you to filter messages even they matched the source (e.g. the _Source Min/Max_). -* *Up / Down:* Use these buttons to move this mapping up or down the list. -* *→ / ←:* Use these checkboxes to enable/disable control and/or feedback for this mapping. Disabling both has the same effect as disabling the mapping as a whole. -* *Edit:* Opens the mapping panel for this mapping. -* *Duplicate:* Creates a new mapping just like this one right below. -* *Remove:* Removes this mapping from the list. -* *Learn source:* Starts or stops learning the source of this mapping. -* *Learn target:* Starts or stops learning the target of this mapping. -** _Tip:_ Learning a target that is currently being automated is not possible at the moment because - ReaLearn can't know if the value change notification is coming from the automation or your touch - interaction. - -Each mapping row provides a context menu (accessible via right-click on Windows and Linux, control-click on macOS), -which allows you access to the following functionality: - -* *Copy:* Copies this mapping to the clipboard. -* *Paste (replace):* Replaces this mapping with the mapping in the clipboard. If the clipboard contains just - a part of a mapping (source, mode or target), then just this part gets replaced. -* *Paste (insert below):* Creates a new mapping that's like the mapping in the clipboard and places it below - this mapping. -* *Copy part:* Copies just a part of the mapping (activation condition, source, mode or target). -* *Move to group:* Lets you move this mapping to another mapping group. -* *Advanced:* Provides access to expert features. -** *Copy as Lua:* Copies this mapping as Lua code. This is an indispensable tool if you want to build your mappings in Lua because it gives you a readily executable code snippet that you can adjust as desired. -** *Copy as Lua (include default values):* Includes even default values. -** *Paste from Lua (replaces):* Like _Paste (replace)_ but treats the clipboard content as Lua code. -** *Paste from Lua (insert below):* Like _Paste (insert below)_ but treats the clipboard content as Lua code. -** *Log debug info (now):* Logs debug information about this particular mapping. - -[#mapping-panel] -=== Mapping panel - -At this point it's important to understand some basics about how ReaLearn processes incoming control -events. When there's an incoming control event that matches a particular source, one of the first -things ReaLearn does is to normalize it to a so-called _control value_. - -A control value can be either absolute or relative, depending on the source character: - -* *Source emits absolute values (e.g. faders)*: The control value will be absolute, which means - it's a 64-bit decimal number between 0.0 and 1.0. You can also think of it in terms of - percentages: Something between 0% and 100%. 0% means the minimum possible value of the source has - been emitted whereas 100% means the maximum. -* *Source emits relative values (e.g. rotary encoders)*: The control value will be relative, which - means it's a positive or negative integer that reflects the amount of the increment or decrement. - E.g. -2 means a decrement of 2. - -After having translated the incoming event to a control value, ReaLearn feeds it to the mapping's -glue section. The glue section is responsible for transforming control values before they reach the _target_. -This transformation can change the type of the control value, e.g. from relative to absolute - it depends -on the mapping's target character. The glue section can even "eat" control values so that they don't arrive -at the target at all. - -Finally, ReaLearn converts the transformed control value into some target instruction (e.g. "set -volume to -6.0 dB") and executes it. - -Feedback (from REAPER to controller) works in a similar fashion but is restricted to absolute -control values. Even if the source is relative (e.g. an encoder), ReaLearn will always emit absolute -feedback, because relative feedback doesn't make sense. - -[#mapping] -==== General mapping properties - -This section provides the following mapping-related settings and functions: - -* *Name:* Here you can enter a descriptive name for the mapping. This is especially useful in - combination with the search function if there are many mappings to keep track of. If you clear - the name, ReaLearn will name the mapping automatically based on its target. -* *Tags:* Use this to assign arbitrary tags to this mapping (comma-separated). These tags can be used to organize mappings in a way that is much more flexible than groups. -** Mapping tags are also displayed in mapping rows, including the ones inherited by groups. -** In the header panel <>, you can search for mappings that have a certain tag by entering the tag name prefixed with the hash character `#`. For example, you can search for all mappings tagged with the tag `mixing` by entering `#mixing`. -** Tags are not just something for people that love to keep things tidy! They also get meaning in combination with certain ReaLearn targets such as <>. -* *Control enabled / Feedback enabled:* Use these checkboxes to enable/disable control and/or - feedback for this mapping. -* *Active:* This dropdown controls so-called conditional activation of mappings. See the - <> section below. -* *Feedback:* -** *Normal:* Makes ReaLearn send feedback whenever the target value changes. This is the recommended - option in most cases. -** *Prevent echo feedback:* This option mainly exists for motorized faders that don't like - getting feedback while being moved. If checked, ReaLearn won't send feedback if the target value - change was caused by incoming source events of this mapping. However, it will still send feedback - if the target value change was caused by something else, e.g. a mouse action within REAPER itself. -** *Send feedback after control:* This checkbox mainly exists for "fixing" controllers which allow - their LEDs to be controlled via incoming MIDI/OSC _but at the same time_ insist on controlling these - LEDs themselves. For example, some Behringer X-Touch Compact buttons exhibit this behavior in MIDI mode. - This can lead to wrong LED states which don't reflect the actual state in REAPER. - If this option is not selected (the normal case and recommended for most controllers), ReaLearn - will send feedback to the controller _only_ if the target value has changed. For example, if you - use a button to toggle a target value on and off, the target value will change only when pressing - the button, not when releasing it. As a consequence, feedback will be sent only when pressing the - button, not when releasing it. However, if this option is selected, ReaLearn will send feedback - even after releasing the button - although the target value has not been changed by it. Another - case where this option comes in handy is if you use a target which doesn't support proper feedback - because REAPER doesn't notify ReaLearn about value changes (e.g. "Track FX all enable") and you have - "Poll for feedback" disabled. By choosing this option, ReaLearn will send feedback whenever the target value - change was caused by ReaLearn itself, which improves the situation at least a bit. -* *Show in projection:* When unticked, this mapping will not show up in the <>. - Useful e.g. for feedback-only mappings or buttons with multiple assignments. -* *Advanced settings:* This button is for experts. There are some advanced mapping-related settings in - ReaLearn that are not adjustable via its graphical user interface but only by writing text-based configuration. - Pressing this button should open a small window in which you can write the configuration for this mapping. - If the button label ends with a number, that number denotes the - number of top-level configuration properties set for that mapping. That way you can immediately see if a mapping - has advanced settings or not. You can learn more about the available properties in the section - <>. -** *Open in text editor (Windows and Linux only):* Opens the settings in the system text editor or whatever program is associated with - YAML files. It depends on your system setup if this works or not. If it does and if your text editor is good, - this can make editing larger YAML snippets more convenient (e.g. by providing syntax highlighting). As soon - as you save the file and close the editor, the text will automatically appear in the "Advanced settings" - text area. -** *Help:* Will open an online version of the user guide section that describes the available configuration - properties. -* *Find in mapping list:* Scrolls the mapping rows panel so that the corresponding mapping row for - this mapping gets visible. -* *Beep on success (checkbox on the bottom-left):* Makes the mapping play a sound whenever the target has been invoked successfully. Nice for trigger-like targets such as <> for which there's no other good way to know if it worked. -* *Previous/next buttons:* Allows you to jump to the previous or next mapping. Considers only mappings that are currently visible in the mapping rows panel. -* *Enabled (checkbox on the bottom-right):* Enables or disables the mapping as a whole. - -[#conditional-activation] -==== Conditional activation - -Conditional activation allows you to dynamically enable or disable this mapping based on the state of -ReaLearn's own plug-in parameters and since recently even on the state of arbitrary targets. This is a powerful feature. It is especially practical if your -controller has a limited amount of control elements and you want to give control elements several -responsibilities. It let's you easily implement use cases such as: - -* "This knob should control the track pan, but only when my sustain pedal is pressed, otherwise it - should control track volume!" (modifier use cases) -* "I want to have two buttons for switching between different banks where each bank represents - a group of mappings." (bank use cases) -* "I want to control the volume of this track only if it's not muted." (target-state based use cases) - -TIP: Since ReaLearn 2.11.0, <> provides a slightly less powerful but more straightforward way to implement use cases that were before only achievable with parameter-based conditional activation. - -There are 6 different activation modes: - -* *Always:* Mapping is always active (the default) -* *When modifiers on/off:* Mapping becomes active only if something is pressed / not pressed -* *When bank selected:* Allows you to step through different groups of mappings (sometimes also called "pages") -* *When EEL met* Let an EEL formula decide (total freedom) -* *When expression met:* Let an expression decide (total freedom) -* *When target value met:* Let the current value of the target of another mapping decide - -[NOTE] -==== -At this occasion, some words about ReaLearn's own freely assignable FX parameters. - -ReaLearn itself isn't just able to -control parameters of other FX, it also offers FX parameters itself. At the moment it offers 200 FX parameters, 100 for the main compartment and 100 for the controller compartment. You can control them just like parameters in other FX: - -- Via automation envelopes, -- via track controls, -- via REAPER's own MIDI/OSC learn -- … and of course via ReaLearn itself. - -Initially, they don't do anything at all. First, you need to give meaning to them by referring to them in activation conditions or `<Dynamic>` selector expressions. -==== - -[discrete] -===== When modifiers on/off - -This mode is comparable to modifier keys on a computer keyboard. For example, when you press `Ctrl+V` -for pasting text, `Ctrl` is a modifier because it modifies the meaning of the `V` key. When this -modifier is "on" (= pressed), it activates the "paste text" and deactivates the "write the letter V" -functionality of the `V` key. - -In ReaLearn, the modifier is one of the FX parameters. It's considered to be "on" if the parameter -has a value greater than 0 and "off" if the value is 0. - -You can choose up to 2 modifier parameters, "Modifier A" and "Modifier B". If you select "<None>", -the modifier gets disabled (it won't have any effect on activation). The checkbox to the right of -the dropdown lets you decide if the modifier must be "on" for the mapping to become active or "off". - -Example: The following setting means that this mapping becomes active _only_ if both "Parameter 1" -and "Parameter 2" are "on". - -* *Modifier A:* "Parameter 1" -* *Checkbox A:* Checked -* *Modifier B:* "Parameter 2" -* *Checkbox B:* Checked - -Now you just have to map 2 controller buttons to "Parameter 1" and "Parameter 2" via ReaLearn (by -creating 2 additional mappings - in the same ReaLearn instance or another one, up to you) et voilà, -it works. The beauty of this solution lies in how you can compose different ReaLearn features to -obtain exactly the result you want. For example, the _absolute mode_ of the mapping that controls the modifier -parameter decides if the modifier button is momentary (has to be pressed all the time) -or toggled (switches between on and off everytime you press it). You can also be more adventurous -and let the modifier on/off state change over time, using REAPER's automation envelopes. - -[discrete] -===== When bank selected - -This is the correct activation mode if you want control surface "bank-style" mapping. An in-depth tutorial how -to implement this can be found in the <> section, tutorial number 1. - -TIP: For this kind of use cases you should consider the new <> target, which is available since ReaLearn 2.11.0 as an alternative. It's slightly less powerful than conditional activation but probably easier to use, partly because you can dictate which mappings should be active "from outside", not from the perspective of the mapping itself. - -You can tell ReaLearn to only activate your mapping if a certain parameter has a particular value. -The particular value is called "Bank". Why? Let's -assume you mapped 2 buttons "Previous" and "Next" to increase/decrease the value of the parameter -(by using "Incremental button" mode, you will learn how to do that further below). And you have multiple -mappings where each one uses "When bank selected" with the same parameter but a different "Bank". -Then the result is that you can press "Previous" and "Next" and it will switch between different -mappings within that parameter. If you assign the same "Bank" to multiple mappings, it's like putting -those mappings into one group which can be activated/deactivated as a whole. - -Switching between different programs via "Previous" and "Next" buttons is just one possibility. -Here are some other ones: - -* *Browse banks using a rotary encoder:* Just map the rotary encoder - to the "Bank" parameter and restrict the target range as desired. -* *Activate each bank with a separate button:* Map each button to the "Bank" - parameter (with absolute mode "Normal") and set "Target Min/Max" to a distinct value. E.g. set button - 1 min/max both to 0% and button 2 min/max both to 1%. Then pressing button 1 - will activate bank 0 and pressing button 2 will activate bank 1. - -In previous versions of ReaLearn you could use other methods to achieve a similar behavior, but it always -involved using multiple ReaLearn instances: - -* *By enabling/disabling other ReaLearn instances:* You can use one main ReaLearn instance containing - a bunch of mappings with <> target in order to enable/disable other ReaLearn FX - instances. Then each of the other ReaLearn instances acts as one mapping bank/group. -* *By switching between presets of another ReaLearn instance:* You can use one main ReaLearn instance containing a - mapping with <> target in order to browse presets of - another ReaLearn FX instance. Then each preset in the other ReaLearn instance acts as one mapping bank/group. However, - that method is pretty limited and hard to maintain because presets are something global - (not saved together with your REAPER project). - -With _Conditional activation_ you can do the same (and more) within just one ReaLearn instance. - -TIP: If you want to adjust the number of banks and improve bank handling in general, set a discrete value count for the corresponding bank parameter (see <>). - -[discrete] -===== When EEL met - -This is for experts. It allows you to write a formula in https://www.cockos.com/EEL2/[EEL2] language -that determines if the mapping becomes active or not, based on potentially all parameter values. -This is the most flexible of all parameter-based activation modes. The other modes can be easily simulated. The example -modifier condition scenario mentioned above written as formula would be: - ----- -y = p1 > 0 && p2 > 0 ----- - -`y` represents the result. If `y` is greater than zero, the mapping will become active, otherwise -it will become inactive. `p1` to `p100` contain the current parameter values. Each of them has a -value between 0.0 (= 0%) and 1.0 (= 100%). - -This activation mode accounts for ReaLearn's philosophy to allow for great flexibility instead of just implementing -one particular use case. If you feel limited by the other activation modes, just use EEL. - -TIP: For most activation conditions which need this amount of freedom, the newer activation mode <> is a slightly better choice because it's easier to use and generally performs a bit better. - -[#expression-based-activation-condition] -[discrete] -===== When expression met - -This is very similar to the previous EEL activation mode. But instead of EEL, it lets you use the same expression language as used in <> to express the activation condition. - -The equivalent expression to above EEL example is: - -`p[0] > 0 && p[1] > 0` - -[#target-based-activation-condition] -[discrete] -===== When target value met - -This is different from all the other activation condition types in that it doesn't look at ReaLearn's internal parameter values. Instead, it looks at the target of another mapping (the so-called "lead mapping") and switches our mapping (the so-called "follow mapping") on or off depending on the target value of the lead mapping. - -It works like this: - -. Create the lead mapping and give it a target, e.g. <>. -* This lead mapping doesn't need to have a source. It can even be completely disabled! -. In the **Mapping** dropdown, pick this newly created mapping. -. In the **Expression** text field to the right, enter `y > 0`. -* This means you want the follow mapping to be active whenever the target value of the lead mapping is greater than 0.0. Or in other words, when it's "switched on". - -You can detect an inactive target by using `y == none`. - -[discrete] -===== Custom parameter names - -Because ReaLearn's parameters are freely assignable, they have very generic names by default. However, as soon as you -give them meaning by using them in a specific way, it can be helpful to give them a meaningful name. You can do that: - -. Switch to the compartment whose parameter names you want to change. -. Open the header panel context menu (accessible via right-click on Windows and Linux, control-click on macOS) - and open the _Compartment parameters_ submenu. -. Here you will find each of the 100 compartment parameters with their current names. Simply click the name to change - it. - -Parameter names are not global, they are always saved together with the REAPER project / FX preset / track template etc. -They will also be saved/restored as part of the compartment preset. - -[discrete] -===== Use case: Control A when a button is not pressed, control B when it is - -Here's how you would implement a typical use case. You want your rotary encoder to control target A when the button is -not pressed and control target B when it's pressed. - -. Create a mapping for the button -** As "Target", you need to choose ReaLearn itself (Type: <>, Track: `<This>`, FX: "… VSTi: ReaLearn (Helgoboss)"). As "Parameter", choose an arbitrary ReaLearn parameter, e.g. "Parameter 1". -** As "Mode", choose either "Absolute" (if you want to switch the encoder function just momentarily) or "Toggle" (if you want the button to toggle between the two encoder functions). -. Create a mapping with target A -** Set "Active" to "When modifiers on/off", "Modifier A" to "Parameter 1" and disable the checkbox beside it. Set "Modifier B" to `<None>`. -** This basically means "Hey, ReaLearn! Please activate this mapping only if ReaLearn Parameter 1 is *off*!" (remember, we control ReaLearn Parameter 1 using the button). -** At this point, turning your encoder should control target A, but only if you don't press the button! -. Create a mapping with target B -** Just as in step 2, set "Active" to "When modifiers on/off" and "Modifier A" to "Parameter 1". *But*: Now *enable* the checkbox beside it. Set "Modifier B" to `<None>`. -** This basically means "Hey, ReaLearn! Please activate this mapping only if ReaLearn Parameter 1 is *on*!" -** At this point, turning your encoder should control target A if you don't press the button and control target B if you press the button. - -==== Source - -As mentioned before, a source usually represents a single control element on your controller. -Sources share the following common settings and functions: - -* *Learn:* Starts or stops learning the source of this mapping. -* *Category:* Lets you choose the source category. -** *None:* A special kind of source that will never emit any events. It's intended to be used on mappings which are - not supposed to be controlled directly but only via <>. -** *MIDI:* Incoming MIDI events. -** *OSC:* Incoming OSC events. -** *REAPER:* Events that can occur within REAPER. -** *Virtual:* Invocations of virtual control elements (coming from virtual controller mappings). This source - category is available for main mappings only. -* *Type:* Let's you choose the source type. Available types depend on the selected category. - -All other UI elements in this section depend on the chosen category. - -===== Category "MIDI" - -All types in the MIDI category have the following UI elements in common: - -* *Channel:* Optionally restricts this source to messages from a certain MIDI channel. Only - available for sources that emit MIDI channel messages. - -The remaining UI elements in this section depend on the chosen source type. - -[#cc-value-source] -====== CC value - -This source reacts to incoming MIDI control-change messages. - -* *CC:* Optionally restricts this source to messages with a certain MIDI control-change controller - number. -* *Character:* MIDI control-change messages serve a very wide spectrum of MIDI - control use cases. Even though some control-change controller numbers have a special purpose - according to the MIDI specification (e.g. CC 7 = channel volume), nothing prevents one from using - them for totally different purposes. In practice that happens quite often, especially when using - general-purpose controllers. Also, there's no strict standard whatsoever that specifies how - relative values (increments/decrements) shall be emitted and which controller numbers emit them. - Therefore you explicitly need to tell ReaLearn about it by setting the _source character_. The - good news is: If you use "Learn source", ReaLearn will try to guess the source character for you - by looking at the emitted values. Naturally, the result is not always correct. The best guessing - result can be achieved by turning the knob or encoder quickly and "passionately" into clockwise - direction. Please note that guessing doesn't support encoder type 3. The possible values are: -* *Range element (knob, fader, etc.):* A control element that emits continuous absolute values. Examples: Fader, -knob, modulation wheel, pitch bend, ribbon controller. Would also include a endless rotary encoder -which is (maybe unknowingly) configured to transmit absolute values. -* *Button (momentary):* A control element that can be pressed and emits absolute values. It emits a > 0% -value when pressing it and optionally a 0% value when releasing it. Examples: Damper pedal. -* *Encoder (relative type _x_):* A control element that emits relative values, usually an endless rotary -encoder. The _x_ specifies _how_ the relative values are sent. This 1:1 corresponds to the -relative modes in REAPER's built-in MIDI learn: -** *Type 1*: -** 127 = decrement; 0 = none; 1 = increment -** 127 > value > 63 results in higher decrements (64 possible decrement amounts) -** 1 < value <= 63 results in higher increments (63 possible increment amounts) -** *Type 2*: -** 63 = decrement; 64 = none; 65 = increment -** 63 > value >= 0 results in higher decrements (64 possible decrement amounts) -** 65 < value <= 127 results in higher increments (63 possible increment amounts) -** *Type 3*: -** 65 = decrement; 0 = none; 1 = increment -** 65 < value <= 127 results in higher decrements (63 possible decrement amounts) -** 1 < value <= 64 results in higher increments (64 possible increment amounts) -* *Toggle-only button (avoid!):* A control element that can be pressed and emits absolute values. It emits a > 0% -value when pressing it, no value when releasing it and a 0% value when pressing it again. -** Hint: This is a workaround for controllers that don't have momentary buttons! You should only use this character -if there's absolutely no way to configure this control element as a momentary button. -** Background: ReaLearn can make a momentary hardware button work like a full-blown toggle button (ReaLearn's -toggle mode is inherently more powerful than your controller's built-in toggle mode!). However, the opposite is -not true. It can't make a toggle hardware button act like a momentary button. -** The way this character works: ReaLearn will simply emit 100%, no matter what the hardware sends. -** *Attention:* If you use the toggle-only source character in combination with mode "Incremental button", you -must leave source max at the (default) theoretical maximum value for that source (e.g. 127 for MIDI CC). Even if -your controller device only sends 0 and 1 and in all other mappings you would enter the controller's concrete -(instead of theoretically possible) maximum value. Otherwise, for this special case, a fixed -out-of-range-behavior will set in that will just ignore all button presses. -* *14-bit values:* If unchecked, this source reacts to MIDI control-change messages with 7-bit - resolution (usually the case). If checked, it reacts to MIDI control-change messages with 14-bit - resolution. This is not so common but sometimes used by controllers with high-precision faders. - -====== Note velocity - -This source reacts to incoming MIDI note-on and note-off messages. The higher the velocity of the -incoming note-on message, the higher the absolute control value. Note-off messages are always -translated to 0%, even if there's a note-off velocity. - -* *Note:* Optionally restricts this source to messages with a certain note number (note numbers - represent keys on the MIDI keyboard, e.g. 60 corresponds to C4). - -====== Note number - -This source reacts to incoming MIDI note-on messages. The higher the note number (= key on a MIDI -keyboard), the higher the absolute control value. - -This essentially turns your MIDI keyboard into a "huge fader" with the advantage that you can jump -to any value at any time. - -====== Pitch wheel - -This source reacts to incoming MIDI pitch-bend change messages. The higher the pitch-wheel position, -the higher the absolute control value. The center position corresponds to an absolute control value -of 50%. - -====== Channel after touch - -This source reacts to incoming MIDI channel-pressure messages. The higher the pressure, the higher -the absolute control value. - -====== Program change - -This source reacts to a range of incoming MIDI program-change messages. The higher the program number, the -higher the absolute control value. - -====== (N)RPN value - -This source reacts to incoming non-registered (NRPN) or registered (RPN) MIDI parameter-number -messages. The higher the emitted value, the higher the absolute control value. - -(N)RPN messages are not widely used. If they are, then mostly to take advantage of their ability to -transmit 14-bit values (up to 16384 different values instead of only 128), resulting in a higher -resolution. - -* *Number:* The number of the registered or unregistered parameter-number message. This is a value - between 0 and 16383. -* *RPN:* If unchecked, this source reacts to unregistered parameter-number messages (NRPN). If - checked, it reacts to registered ones (RPN). -* *14-bit values:* If unchecked, this source reacts to (N)RPN messages with 7-bit resolution, including increment/decrement messages. If - checked, it reacts to those with 14-bit resolution. In practice, this if often checked. -* *Character:* See <>. - -====== Polyphonic after touch - -This source reacts to incoming MIDI polyphonic-key-pressure messages. The higher the pressure, the -higher the absolute control value. - -* *Note:* Optionally restricts this source to messages with a certain note number. - -====== MIDI clock tempo - -This source reacts to incoming MIDI clock (MTC) tempo messages. These are metronome-beat-like -messages which can be regularly transmitted by some DAWs and MIDI devices. The frequency with which -this message is sent dictates the tempo. - -The higher the calculated tempo, the higher the absolute control value. A tempo of 1 bpm will be -translated to a control value of 0%, a tempo of 960 bpm to 100% (this corresponds to REAPER's -supported tempo range). - -This source can be used in combination with the <> target to obtain a "poor man's" tempo -synchronization. Be aware: MIDI clock naturally suffers from certain inaccuracies and latencies - -that's an issue inherent to the nature of the MIDI clock protocol itself. E.g. it's not really -suitable if you need super accurate and instant tempo synchronization. Additionally, ReaLearn's -algorithm for calculating the tempo could probably be improved (that's why this source is marked as -experimental). - -====== MIDI clock transport - -This source reacts to incoming MIDI clock (MTC) transport messages. These are simple start, continue -and stop messages which can be sent by some DAWs and MIDI devices. - -* *Message:* The specific transport message to which this source should react. - -[#raw-midi-source] -====== Raw MIDI / SysEx - -This source primarily deals with system-exclusive MIDI messages. Since ReaLearn v2.11.0, it supports both control and feedback direction! - -* *Pattern:* Pattern describing the raw MIDI message. - -*Pattern basics* - -In its most basic form, the pattern is a sequence of bytes notated as hexadecimal numbers. This is typical notation, -especially for system-exclusive MIDI messages. - -Example: - ----- -F0 00 20 6B 7F 42 02 00 10 77 00 F7 ----- - -If you enter this and ReaLearn receives this system-exclusive message from the input, it will fire a 100% value. If feedback is set up correctly, this message will be sent to the device whenever the target value changes. - -Remarks: - -- You can check if the correct feedback messages are sent to the device by <>. -- Each byte is written using 2 hexadecimal digits. -- Spaces between the bytes can be omitted. -- You can express all types of MIDI messages using this raw notation (e.g. pitch wheel), not just system-exclusive ones. If you do this, it will work as expected for the _feedback_ direction. Please note that it will not work for the _control_ direction at the moment (I don't think this is needed). -- If you want a system-exclusive MIDI message, you _must_ include its start (`F0`) and end status byte (`F7`)! - -*Binary notation* - -ReaLearn also supports binary notation of a byte. You need to enclose the binary digits of one byte in brackets. - -Example: - ----- -F0 00 20 [0110 1011] 7F 42 02 00 10 77 00 F7 ----- - -This is equivalent to the first example (`6B` in hexadecimal notation is the same as `0110 1011` in binary -notation). - -Remarks: - -- Between the brackets, each digit represents one bit. The left bit is the most significant one. -- Spaces between the two nibbles (4 bits) can be omitted. - -*Extracting and encoding a value* - -For the _feedback_ direction, the examples I've shown you so far aren't real-world examples, because there's no point in sending the same MIDI message to the device over and over again! If you really would want to send a constant MIDI message to the device, you would be -much better off using a <>, which allow you to send raw MIDI -messages once when a mapping is initialized, not on every target value change. - -But even for the _control_ direction, you might want to react to a whole _range_ of system-exclusive messages, not just a fixed one. One part of your message might represent a variable value. You might want to extract it and control the target with it. - -Fortunately, ReaLearn offers a uniform way to extract a variable value from the raw MIDI message (control) or encode the current target value as part of it (feedback). Bytes which contain a variable value (or a part of it) _must_ be expressed using binary notation. - -Example: - ----- -F0 00 20 6B 7F 42 02 00 10 77 [0000 dcba] F7 ----- - -The second nibble of the second last byte contains the lowercase letters `dcba`. This is the portion of the byte that -denotes the variable value. - -Each letter represents one bit of the variable value: - -* `a` - Bit 1 (least significant bit of the variable value) -* `b` - Bit 2 -* `c` - Bit 3 -* `d` - Bit 4 -* … -* `m` - Bit 13 -* `n` - Bit 14 -* `o` - Bit 15 -* `p` - Bit 16 (most significant bit of the variable value) - -The resolution of the variable value always corresponds to the letter in the whole pattern which represents the -highest bit number. In the example above, the resolution is 4 bit because there's no letter greater than `d` -in the pattern. - -In the following example, the resolution is 7 bit because `n` is the greatest letter in the whole pattern. - ----- -F0 00 20 6B 7F 42 02 00 10 [00nm lkji] [hgfe dcba] F7 ----- - -Remarks: - -- The highest resolution currently supported is 16 bit (= 65536 different values). -- You can put these letter bits anywhere in the pattern (but only within bytes that use binary notation). - -*Byte order* - -This form of notation is slightly unconventional but I think it's very flexible because it gives you much control over -the resulting MIDI message. This amount of control seems appropriate considering the many different ways -hardware manufacturers used and still use to encode their MIDI data. When a number is expressed within more than -one byte, manufacturers sometimes put the most significant byte first and sometimes the least significant one, -there's no rule. This notation supports both because you decide where the bits end up: - -Example for "most significant byte first": - ----- -F0 00 20 6B 7F 42 02 00 10 [ponm lkji] [hgfe dcba] F7 ----- - -Example for "least significant byte first": - ----- -F0 00 20 6B 7F 42 02 00 10 [hgfe dcba] [ponm lkji] F7 ----- - -*More examples* - -"Romeo and Juliet" bits (separated by 2 bytes): - ----- -F0 [1111 000b] [a101 0100] F7 ----- - -Simple on/off value (1 bit only): - ----- -F0 A0 [1111 010a] F7 ----- - -This behaves like pitch wheel (because the pattern describes exactly the way how pitch wheel messages are encoded): - ----- -E0 [0gfe dcba] [0nml kjih] ----- - -[#script-source] -====== MIDI Script - -This source is feedback-only and exists for enabling more complex feedback use cases such as controlling LCDs that are not yet supported by the <> source. It lets you write an EEL or Lua script that will be executed whenever ReaLearn "feels" like it needs to send some feedback to the MIDI device. - -* *Kind:* Whether to use the EEL or Lua language. -* *Script:* The script. Is disabled if the script contains more than one line. -* *…:* Opens the script in a separate window (for multi-line scripts). - -TIP: Prefer the <> source over this one whenever possible. It's easier to use. - -*General mechanics* - -* The script receives an input and must produce an output. -* *Script input* -** The main input is the current feedback value, which the script can access as a variable. -* *Script output* -** The main output that the script is supposed to return is the MIDI message to be sent to the MIDI device. -** Additionally, the script can provide a so-called _feedback address_, which is supposed to uniquely identify the LED, motor fader or display. It's important to provide an address if you want ReaLearn to handle feedback relay correctly, e.g. that it switches off the LED when not in use anymore and doesn't switch it off if another mapping "takes over" the same LED. By convention, the constant (non-variable) bytes of the MIDI message should be used as address. The examples below might help to understand. - -*EEL script specifics* - -* *Script input* -** EEL scripts can access numeric feedback values only. The current numeric feedback value is available as variable `y`, a floating point number between 0.0 and 1.0. This is essentially the current normalized target value after being processed by the "Glue" section of the mapping. -* *Script output* -** In order to provide the output MIDI message, you must assign the raw bytes of that message to subsequent slots of the EEL script's virtual local address space (by indexing via brackets) *and* -set the variable `msg_size` to the number of bytes to be sent. If you forget the latter step, nothing will be sent because that variable defaults to zero! -** In order to provide the address, simply assign an appropriate number to the `address` variable. -* *Examples* -** The following example creates a 3-byte MIDI message. -+ -[source,eel] ----- -address = 0x4bb0; -msg_size = 3; -0[] = 0xb0; -1[] = 0x4b; -2[] = y * 64; ----- - -*Lua script specifics* - -* *Script input* -** Lua scripts can access numeric, text and dynamic feedback values. -** Here's the list of input variables: -*** `y`: The feedback value, either numeric (`type(y) == "number"`) or text (`type(y) == "string")`. -*** `context.feedback_event.color`: The color as set in the <> section. Either the default color (`== nil`) or an RGB color (table with properties `r`, `g` and `b`). -*** `context.feedback_event.background_color`: The background color. -* *Script output* -** A Lua script can even generate multiple output messages. -** You need to return a table which contains the following keys: -*** `address`: The feedback address. -*** `messages`: An array containing all the messages, where each message itself is an array contaning the message bytes. -* *Examples* -** Creates a 3-byte MIDI message, assuming that `y` is a numeric normalized value. -+ -[source,lua] ----- -return { - address = 0x4bb0, - messages = { - { 0xb0, 0x4b, math.floor(y * 10) } - } -} ----- -+ -** Creates a MIDI sys-ex message that changes the color of some fictional device LED/display: -+ -[source,lua] ----- -local color = context.feedback_event.color -if color == nil then - -- This means no specific color is set. Choose whatever you need. - color = { r = 0, g = 0, b = 0 } -end -return { - address = 0x4b, - -- Whatever messages your device needs to set that color. - messages = { - { 0xf0, 0x02, 0x4b, color.r, color.g, color.b, 0xf7 } - } -} ----- -** Creates a 3-byte MIDI message, assuming that `y` is a text value. -+ -[source,lua] ----- -local lookup_table = { - playing = 5, - stopped = 6, - paused = 7, -} -return { - messages = { - { 0xb0, 0x4b, lookup_table[y] or 0 } - } -} ----- -+ -[TIP] -==== -Please note that this kind of simple mapping from text values to integer numbers doesn't need a script. You can use the `feedback_value_table` <> property instead, which can only be set via API though. Do a full-text search for `feedback_value_table` in directory `resources/controller-presets` of the link:https://github.com/helgoboss/realearn[ReaLearn source code] to find usage examples. -==== - -[#display-source] -====== Display - -This is a feedback-only source used to display text on MIDI-controllable hardware displays (LCDs, OLED displays, 7-segment displays, etc.). - -* *Protocol:* Lets you choose the display protocol, which tells ReaLearn how it should communicate with the hardware display and which options it supports. -** *Mackie LCD:* Use this for MCU-compatible LCDs. Depending on your particular control surface, there can be up to 8 LCDs, each of which has up to 2 lines. -** *Mackie XT LCD:* Use this to control the displays of MCU XT devices (= control surface extenders, which provide additional faders and displays). -** *X-Touch Mackie LCD:* Like _Mackie LCD_ but also supports colors on certain X-Touch devices. -** *X-Touch Mackie XT LCD:* Like _Mackie LCD XT_ but also supports colors on certain X-Touch devices. -** *Mackie 7-segment display:* Use this for MCU-compatible 7-segment displays (you know, the ones which only show digits). There's usually one small assignment display and a larger one for showing the time code. -** *SiniCon E24:* Use this with the https://www.sinicon.io/[SiniCon E24 controller]. -** *Launchpad Pro - Scrolling text:* Displays looped scrolling text on a Novation Launchpad Pro. Only seems to work if you set _Output_ to `MIDIOUT2 (Launchpad Pro)`. -** *Studiologic SL Keyboard display:* Displays text on the display of Studiologic SL keyboards (tested with SL88). -* *Display:* Choose the particular display or display portion to which you want to send text. -* *Line:* Choose the line number. - -CAUTION: For controllers with multiple displays and lines, ReaLearn allows you to spread your text over all available displays and lines. This is great if you need to display a lot of text but one display doesn't provide enough space. But be aware: Replacing feedback with other feedback ("feedback relay") doesn't work so nicely anymore if you make use of this feature. - -If you want to know how to define which text shall be sent to the displays, please see <> in the *Glue* section. - - -====== Specific program change - -This source reacts to MIDI program-change messages with a specific program. This is a trigger-only source, that means it always fires 100% (whenever the program number corresponds to the configured one). - -[#category-osc] -===== Category "OSC" - -OSC sources allow configuration of the following aspects: - -====== Address - -This needs to correspond exactly to the address of the corresponding control element on your OSC device. - Example: `/1/fader1`. You don't need to figure that out yourself, just use the _Learn_ function. - -====== Argument - -Each OSC message consists of an arbitrary number of arguments. In most cases, e.g. with faders, knobs or - buttons, it's just one argument. X/Y controls often send 2 arguments, one for each axis. There are rare cases in which messages have even more arguments. - -The first dropdown menu allows you to choose the number of the argument that ReaLearn should look at and process. `1` denotes the first argument, `2` the second one, and so on. - -The second dropdown menu lets you choose the argument type which ReaLearn should use to construct a proper feedback message. - -* This is usually the same type as the one used for control direction. For control direction, choosing an explicit type is irrelevant because ReaLearn handles whatever type arrives automatically in the best possible way. -* If you use _Learn_, the type is filled automatically. -* The value to be sent will be derived from the type (see <>): -+ -[cols="m,m"] -|=== -| Type | Property - -| Float | value.float -| Double | value.double -| Int | value.int -| Long | value.long -| Bool | value.bool -| Nil | nil -| Inf | inf -| String | value.string -| Color | style.color -|=== -* If you want more control over what feedback values are sent, use the <> field. - -====== Range - -Values of argument types _Float_ and _Double_ are by default interpreted as decimal values between 0.0 and 1.0. You can change that by entering a different value range here. Even negative numbers are allowed. - -Customizing the value range is especially important for argument types _Int_ and _Long_ because they don't have a standard value range. - -====== Is relative - -Some messages transmitted by OSC devices are meant to be interpreted as relative - increments/decrements instead of absolute values, e.g. jog wheels. When you enable this checkbox, ReaLearn will - treat each received _1_ value as an increment and _0_ value a decrement. - - -[#feedback-arguments] -====== Feedback arguments - -Allows you to define exactly which feedback value is sent at which argument position. If this field is non-empty, the _Type_ dropdown will be ignored. - -The format of this field is very simple: You enter feedback value property keys separated by spaces. Each entered property key corresponds to one argument position. E.g. if you want ReaLearn to send the current feedback value in text form at argument 1 and the color (see <>) as RRGGBB string at argument 2, you would enter: - ----- -value.string style.color.rrggbb ----- - -The following properties are available: - -[cols="m,m,1"] -|=== -| Property | Type | Description - -| -value.float -| -Float -| -Numeric feedback value interpreted as float - -| -value.double -| -Double -| -Numeric feedback value interpreted as double - -| -value.bool -| -Bool -| -Numeric feedback interpreted as bool (on/off only) - -| -value.string -| -String -| -Numeric or textual feedback value formatted as string - - -| -style.color.rrggbb -| -String -| -Feedback value color formatted as RRGGBB string - - -| -style.background_color.rrggbb -| -String -| -Feedback value background color formatted as RRGGBB string - - - -| -style.color -| -Color -| -Feedback value color as native OSC color - - -| -style.background_color -| -Color -| -Feedback value background color as native OSC color - - -| -nil -| -Nil -| -Nil value - -| -inf -| -Infinity -| -Infinity value -|=== - -===== Category "Keyboard" - -This source reacts to pressing or releasing a key on your computer keyboard. It emits a value of 100% when the key is pressed and 0% when released. - -Usage: - -* In order to set the key, simply click the *Learn* button and press the key of your choice. -* In addition to the key label, ReaLearn might show some warnings regarding the portability of your keystroke. -** This helps you to avoid keyboard shortcuts that don't reliably work cross-platform (in other operating systems) or on other keyboard layouts. -** You can ignore portability warnings if you use just this operating system and don't plan to share your keyboard presets with other users. - -Tips: - -* This only works if <> is set to *Computer keyboard*. -* If you hold a key, it will not keep firing. This is by design! Use <> instead. -* Key combinations are not supported. This is by design! Use <> instead. - -====== MIDI device changes - -===== Category "REAPER" - -====== MIDI device changes - -This source emits a value of 100% whenever any MIDI device is connected and 0% whenever any MIDI device is -disconnected. You can map this to the REAPER action "Reset all MIDI devices" to achieve true plug and play -of MIDI devices (provided the corresponding device has been enabled at least once in REAPER's MIDI device -preferences). - -====== ReaLearn instance start - -This source fires (emits a value of 100%) when ReaLearn starts. It can be used to execute an actions or restore certain states on REAPER startup or project load. - -====== Timer - -This source fires (emits a value of 100%) repeatedly every _n_ milliseconds. - -====== ReaLearn parameter - -This source fires whenever one of ReaLearn's <> is changed. - -One of many ways to use this is to create macro parameters which control multiple parameters of multiple other plug-ins. - -WARNING: At the moment, mappings with this source can't participate in rendering. So it's important to write down automation *before* rendering. This applies *in addition* the things pointed out in <>. - -====== Speech - -This source works for feedback only. It uses the native Windows or macOS text-to-speech engine to speak out any feedback value. - -[#virtual-source] -===== Category "Virtual" - -As pointed out before, _virtual_ sources exist in order to decouple your mappings from the actual -MIDI/OSC source. - -If you want to define a virtual source, you first need to choose among two types of virtual control elements: -"Multi" (control elements that support more than 2 values) and "Button" (simple on/off controls). It's sort of the -lowest common denominator among all possible control element types. This distinction is used by ReaLearn -to optimize its user interface. In future, it might be used for additional improvements. - -Both types are explained in detail below. They support the following settings: - -* *ID:* A number or name for uniquely identifying the control element. -** Numbers are especially suited for the 8-knobs/8-buttons layouts. In a row of 8 knobs one would typically assign - number 1 to the leftmost and number 8 to the rightmost one. It's your choice. -** For more advanced virtual control scenarios it can be useful to think in names instead of numbers. That's why - the IDs of virtual control elements are not limited to numbers only. You can use up to 32 alphanumeric and - punctuation characters (no exotic characters, e.g. no umlauts). -* *Pick:* Lets you conveniently pick out of predefined numbers and names. If you want your main preset to be - compatible with as many controller presets as possible, try to use predefined names instead of inventing your own - naming scheme. -** *DAW control:* The names you see here are heavily inspired by the wording used with Mackie Control devices. -** *Numbered:* Simply lets you pick among any number between 1 and 100. Wow, you can save up to 3 key presses!!! - -====== Multi - -Represents a control element that you can "move", that is, something that allows you to choose between more than 2 -values. Usually everything which is _not_ a simple on/off button :) Here's a list of typical _multis_: - -* Fader -* Knob -* Pitch wheel -* Mod wheel -* Endless encoder -* XY pad (1 axis) -* Touch strip -* (Endless) rotary encoder -* Velocity-sensitive pads or keys - -====== Button - -Represents a control element that distinguishes between two possible states only (e.g. on/off), or even just one -("trigger"). Usually it has the form factor of a button that you can "press". Here's a list of typical _buttons_: - -* Play button -* Switch -* Sustain pedal - -Please note that velocity-sensitive keys should be exposed as "Multi", not as "Button" - unless you know for sure that -you are not interested in the velocity sensitivity. - -[#target] -==== Target - -A target is a thing that is supposed to be controlled. - -===== Common target elements - -====== Learn - -Starts or stops learning the target of this mapping. - -====== Menu - -Opens a small menu related to the target section: - -- *Pick recently touched target (by type):* Gives you a list of recently touched parameters or executed actions in REAPER. When you click one of it, the target will be populated accordingly. It's an alternative to "Learn". Please note that not all targets can be picked that way, some have to be configured manually. -- *Go there (if supported):* If applicable, this makes the target of this mapping visible in REAPER. E.g. if the target is a track FX parameter, the corresponding track FX window will be displayed. - - - -====== Type - -* *Left dropdown:* Lets you choose the target category. -** *Real:* Targets that are about actually changing something "real", e.g. in REAPER or ReaLearn itself. -** *Virtual:* Targets that invoke virtual control elements. This source - category is available for controller mappings only. -* *Right dropdown:* Lets you choose a target type within that category. - -====== Value -Reflects the current value of this mapping target and lets you change it (either via slider and text field or via buttons, depending on the target character). - -* If the target can't be resolved at the moment, it will show "Target currently inactive!". - -====== Unit button - -On the right side of the current value you will see a button with a label such as `1. dB (%)`. - This button displays the currently selected unit which is used for displaying and entering target values. The - number in the parentheses denotes the unit which is used for displaying and entering target step sizes. Clicking - the button switches between the units. Currently there are two options: - -* *1. Use native target units*: Uses the target-specific unit, e.g. dB for volume targets. If the target - doesn't have any specific units, it will displayed as `1. - (-)`. -* *2. Use percentages*: Uses percentages for everything, which can be nice to get a uniform way of - displaying/entering values instead of having to deal with the sometimes clunky target-specific units. - -===== Common selectors - -Targets that need a track, FX, FX parameter or send/receive have dropdowns that let you choose how you want to _address_ these objects. Let's call them _object selectors_. Here's an explanation of commonly available object selectors. - -NOTE: The descriptions below are sometimes a bit tailored to _track_ objects but the same applies to all other objects that support it. - -[#instance-selector] -====== Selector "Instance" - -This selector makes the target work on the current <> or current <> of this particular ReaLearn instance. - -[#by-id] -====== Selector "Particular" - -Lets you pick a specific object (e.g. track) and refer to it by its unique ID. This is the default and - in most cases what you want. Choose this if you want ReaLearn to always control that very particular track even - in case you move it somewhere else or rename it. - -Please note that it's - _not possible_ with this setting to create a ReaLearn preset that is reusable among different projects. Because a - track ID is globally unique, even across projects. That also means it doesn't make sense to use this setting in a ReaLearn monitoring FX instance. - -[#by-position] -====== Selector "At position" - -This is the most straightforward selector. It lets you refer to a track by its position in the track list. This is great if you want to build a preset that you are going to reuse among multiple projects. - -However, this selector has the disadvantage that things fall apart if you reorder, insert or delete tracks. This is why it's not the default. - -Next to the dropdown you will find a text field. Here you should enter the position as number, starting with number `1`. - -[#by-name] -====== Selector "Named" - -Allows you to choose a track depending on its name. In case there are multiple tracks with the same - name, it will always prefer the first one. This will allow you to use one ReaLearn preset across multiple projects that have similar naming schemes, e.g. as monitoring FX. - -In the name field next to the dropdown, you can enter a name. If you don't want exact matching, you can use wildcards: - -* `*` for matching zero or arbitrary many characters -* `?` for matching exactly one arbitrary character. -* Example: `Violin *` would match `Violin 1` or `Violin 12` but not `12th Violin`. - -[#dynamic-selector] -====== Selector "Dynamic" - -This selector allows you to _calculate_ which object (e.g. track) you want to use. - -When you choose this option, a text field will appear next to it. This lets you enter a - mathematical expression whose result should be the object's _index_. E.g. the first track in the project has index 0, the master track -1. For your convenience, you will find a small text label next to the expression text field that always shows the current result of your formula (clamped to the target value range). - - -NOTE: Please note -that the expression language is _not EEL_ - this is a notable difference to ReaLearn's control/feedback -transformation text fields! The expression language used here just -provides very basic mathematical operations like addition (`+/-`), multiplication (`*`) etc. and it also -doesn't allow or need any assignment to an output variable. - -The dynamic selector is a very powerful tool because you can use some special variables: - - -[cols="m,1,1,3"] -|=== -| Variable | Type | Applicable to | Description - -| none | - | All selectors | -Special value that represents a "not set" value. See below for examples. - -| p | Array of floating-point numbers | All selectors | -Allows you to access the values of ReaLearn's internal parameters. E.g. you can get the number of the first parameter by writing `p[0]`. - -By default, parameter values are normalized floating point values, that means they are decimal numbers between 0.0 and 1.0. This can be changed by customizing the parameter with a specific integer value count (see <>). - -| p1 ... p100 | Floating-point numbers | All selectors | -Values of ReaLearn's internal parameters as single variables. - -_Deprecated_: Recent ReaLearn versions offer the `p` array instead. Better use that one. - - -| selected_track_index | Integer >= -1 | Track selectors | -Resolves to the zero-based index of the first currently selected track within the containing project. -If no track is selected, this resolves to `none`. If the master track is selected, `-1`. - -| selected_track_tcp_index | Integer >= -1 | Track selectors | -Like `selected_track_index` but counts only tracks that are visible in the track control panel. - -| selected_track_mcp_index | Integer >= -1 | Track selectors | -Like `selected_track_index` but counts only tracks that are visible in the mixer control panel. - -| selected_track_indexes | Array of integers >= -1 | Track selectors | -Lets you access the indexes of multiple selected tracks. - -E.g. if 2 tracks are selected, `selected_track_indexes[0]` gives you the index of the first selected track whereas `selected_track_indexes[1]` gives you the index of the second selected track. `selected_track_indexes[2]` would resolve to `none`. - -| this_track_index | Integer >= -1 | Track selectors | - -Resolves to the zero-based index of the track on which this ReaLearn instance is located. - -| instance_track_index | Integer >= -1 | Track selectors | - -Resolves to the zero-based index of the instance track of this ReaLearn instance. - -| instance_track_tcp_index | Integer >= -1 | Track selectors | - -Like `instance_track_index` but counts only tracks that are visible in the track control panel. - -| instance_track_mcp_index | Integer >= -1 | Track selectors | - -Like `instance_track_index` but counts only tracks that are visible in the mixer control panel. - -| this_fx_index | Integer >= 0 | FX selectors | - -Resolves to the zero-based index of this ReaLearn FX instance. - -| instance_fx_index | Integer >= 0 | FX selectors | - -Resolves to the zero-based index of the instance FX of this ReaLearn instance. - -| tcp_fx_indexes | Array of integers >= 0 | FX selectors | - -Lets you access the indexes of FXs in a track control panel. - -E.g. `tcp_fx_indexes[2]` will resolve to the index of the third FX displayed in the track control panel. - -| tcp_fx_parameter_indexes | Array of integers >= 0 | FX parameter selectors | - -Lets you access the indexes of FX parameters in a track control panel. - -E.g. `selected_fx_parameter_indexes[2]` will resolve to the index of the third FX parameter displayed in the track control panel. - -This only makes sense if used in conjunction with `tcp_fx_indexes`. - -|=== - -Examples of dynamic track expressions: - -* `p1 * 99` -** Will point to track with index 0 (first track) if "Parameter 1" is set to the minimum and to - track with index 99 (= track number 100) if it's set to the maximum. -** If you use a formula like that, - you should make sure that "Parameter 1" is controlled with a step size that allows for exactly 100 different - values. This conforms to ReaLearn's default step size 0.01 = 1%. -** Since ReaLearn 2.13, this is easier because it adds support for integer parameters: -*** Set the value count of "Parameter 1" to 100 (see <>) -*** You can now treat the parameter in the formula as an integer (just `p1` instead of `p1 * 99`). -*** Most importantly, ReaLearn will take care of using the correct step size automatically when setting up a mapping for -controlling that parameter. -* `p1 * 3 * 100 + p2 * 99` -** This will treat "Parameter 1" as a kind of bank selector that allows you - to choose between exactly 4 banks (0, 1, 2, 3) of 100 tracks each. "Parameter 2" will select the track - number within the bank. You see, this is very flexible. - -===== Common elements and selectors for track targets - -When choosing a track, the following additional elements and selectors are available: - - -====== Track must be selected - -If checked, this mapping will be active only if the track set in _Track_ is currently selected. - -====== Selection ganging - -If checked and if the track in question is selected, all other selected tracks will be adjusted as well. This uses REAPER's built-in selection-ganging feature and therefore should behave exactly like it. - -====== Respect grouping - -If checked, track grouping will be taken into account when adjusting the value. This uses REAPER's built-in track grouping feature and therefore should behave exactly like it. - -NOTE: In older REAPER versions (< 6.69+dev1102), this can only be enabled together with selection ganging when using it on volume, pan or width targets. - -====== Selector "" - -Track which hosts this ReaLearn instance. If ReaLearn is on the monitoring FX -chain, this resolves to the master track of the current project. - -[#selected-selector] -====== Selector "" - -Currently selected track. If multiple tracks are selected, refers only to the first one. - -====== Selector "" - -All currently selected tracks. This makes track targets (not FX target and not send -targets) do their job on _all_ selected tracks. The feedback value always corresponds to the highest value among all selected tracks. - -CAUTION: If you select many tracks, things can become quite slow! - -====== Selector "" - -Master track of the project which hosts this ReaLearn instance. - -* If ReaLearn is on the monitoring FX chain, this resolves to the master track of the current project. -* If you don't have ReaLearn on the monitoring FX chain but you want to control an FX on the monitoring FX -chain, this option is the right choice as well. Make sure to enable the "Monitoring FX" checkbox. - -====== Selector "All named" - -Allows you to use wildcards (see <>) to make track targets do their thing on -all matching tracks instead of only the first one. - -====== Selector "At TCP position" - -Like <> but just considers tracks that are visible in the track control panel. - -====== Selector "At MCP position" - -Like <> but just considers tracks that are visible in the mixer control panel. - -====== Selector "Dynamic (TCP)" - -Like <> but the result should be an index counting only tracks visible in the track control panel. - -====== Selector "Dynamic (MCP)" - -Like <> but the result should be an index counting only tracks visible in the mixer control panel. - -====== Selector "By ID or name (legacy)" - -This lets you refer to a track by its unique ID and name as fallback. This was the default -behavior for ReaLearn versions up to 1.11.0 and is just kept for compatibility reasons. - -_Deprecated_: You shouldn't use this selector anymore. - -===== Common elements for on/off targets - -Targets which control an on/off-style property of tracks (e.g. <>) additionally provide the following elements. - -====== Exclusive - -By default, this option is set to "No". - -* *No:* Makes the track target affect just this track. -* *Within project:* Switches the property on (off) for this track and off (on) for all other tracks in the project. -* *Within folder:* Switches the property on (off) for this track and off (on) for all other tracks in the same folder and same level. -* *Within project (on only):* Variation of _Within project_ that applies exclusivity only when switching the property on for this track. In other words, it never switches the property on for other tracks. -* *Within folder (on only):* Variation of _Within folder_ that applies exclusivity only when switching the property on for this track. In other words, it never switches the property on for other tracks. - - -===== Common elements for send targets - -Only available for targets that work on a send/receive: - -====== Kind - -The kind of send/receive that you want to control. - -* *Send:* Send from the track above to another track of your choice. If you choose <>, - ReaLearn will memorize the ID of the destination track. That way you will still control the correct send even - if you delete another send in that track. -* *Receive:* Receive from another track of your choice to the track above (opposite direction of send). If you - choose the <> selector, ReaLearn will memorize the ID of the source track. -* *Output:* Send from the track above to a hardware output. Please note that with hardware outputs, <> is the - same as <> because hardware outputs don't have unique IDs. - -====== Send/Receive/Output - -This lets you choose the actual send/receive/output. - -===== Common elements and selectors for FX targets - -The following elements and selectors are available for targets associated with a particular FX instance. - -====== FX - -The FX instance associated with this target. ReaLearn will search for the FX in the output or input FX chain of the above selected track. - -====== Selector "" - -Always points to the own ReaLearn instance. Perfect for changing own parameters, e.g. for - usage of the conditional activation or `` features (especially important if you want to create reusable - presets that make use of these features). - -====== Selector "" - -Currently focused FX. _Track_ and _Input FX_ settings are ignored. - -[#fx-by-id] -====== Selector "Particular" - -Lets you pick a specific FX in the FX chain. Renaming the FX or moving it within the FX chain is fine - ReaLearn will still keep controlling exactly this FX. Please note that this only makes sense if you address the containing track using <> as well. - -[#fx-by-name] -====== Selector "Named" - -Lets you address the FX by its name in the FX chain. Just as with tracks, you can use wildcards to have a blurry search. - -====== Selector "All named" - -Allows you to use wildcard3s (see <>) to make FX targets do their thing on all matching FX instances instead of only the first one. - -====== Selector "By ID or position (legacy)" - -This refers to the FX by its unique ID with its position as fallback. This was the default - behavior for ReaLearn versions up to 1.11.0 and is just kept for compatibility reasons. - -_Deprecated_: Don't use this selector anymore. - -====== Input FX - -If unchecked, the _FX_ dropdown will show FX instances in the track's normal FX - chain. If checked, it will show FX instances in the track's input FX chain. - -====== Monitoring FX - -This appears instead of the input FX checkbox if you select track `<Master>`. If you check this, - you can target FX instances on REAPER's global monitoring FX chain. - -WARNING: Because of a limitation in the REAPER API, learning and feedback for monitoring FX doesn't work - at the moment! - -====== FX must have focus - -If checked, this mapping will be active only if the FX instance set in - _FX_ is currently focused. - -If the FX instance is displayed in a floating window, _focused_ means - that the floating window is active. If it's displayed within the FX chain window, _focused_ means - that the FX chain window is currently open and the FX instance is the currently selected FX in - that FX chain. - -Of course, this flag doesn't have any effect if you chose _<Focused>_ FX. - -===== Common elements for pollable targets - -The following elements are available only for the few targets that might need polling (= regular value querying) in order to support automatic feedback in all cases. - -====== Poll for feedback - -This makes ReaLearn query the current target value every few milliseconds in order to send - up-to-date feedback to your controller at all times. - -This is not necessary for most targets because usually ReaLearn - takes advantage of REAPER's internal notification system to get notified about target value changes (which is great - for performance). For the few targets for which it is, this option is enabled by default in order to give - you the best feedback experience out-of-the-box. - -In the probably rare case that the polling causes performance issues, you can untick this checkbox. - -* For most targets, if you untick this checkbox, automatic feedback for that target will simply stop working. This - means you will not receive up-to-date feedback anymore whenever you change the target value within REAPER itself - (not using ReaLearn). -* The <> target is an exception. Automatic feedback will still work, even without _Poll for - feedback_ enabled. But in the following corner cases it might not: -** If the FX is on the monitoring FX chain. -** If you change a preset from within the FX GUI. - -===== Category "Real" - -[#global-last-touched] -====== Global: Last touched - -This will control whatever target has been last touched in REAPER. It's similar to the built-in REAPER action -"Adjust last touched FX parameter" but provides the following benefits: - -. It's applicable to all ReaLearn targets that are learnable, not just FX parameters. -. It offers feedback. -. It can distinguish between parameter modifications caused by ReaLearn (i.e. hardware control) and those caused in other ways (e.g. via mouse). - -- *Targets → Pick!:* This opens a window that lets you pick all considered target types and types of invocations (only macOS and Windows so far). Last-touched targets not checked in this window will be ignored. - -====== Global: Mouse - -This will control the mouse. - -* *Action* -** *Move cursor to:* Moves the mouse cursor on the given axis in an absolute manner. This is a good choice for absolute mouse movement, that is, if you want to position the mouse cursor to a specific screen position. Although it's also possible to move the mouse cursor relatively with this action by controlling the target with relative messages, it's usually better to use _Move cursor by_ instead. -** *Move cursor by:* Moves the mouse cursor on the given axis in a relative manner. This is a good choice if you want to move the cursor e.g. up a bit, starting from its current position. This only works with relative control elements such as encoders or features such as <>. -** *Press or release button:* Presses or releases a certain mouse button, depending on the incoming control value (0% = release, anything else = press). -** *Turn scroll wheel:* Simulates the scroll wheel. -* *Axis:* Determines the direction of movement or scrolling. -* *Button:* Determines which mouse button to use. - -TIP: One popular use of this target is to adjust the FX parameter under the mouse cursor. For this, it's usually best to set _Action_ to "Turn scroll wheel" and _Axis_ to "Y (vertical)". - -TIP: You can unfold the magic of this target by combining multiple mappings. E.g. one can simulate mouse dragging by using one mapping to press/release the left button and another mapping to move the cursor. link:https://raw.githubusercontent.com/helgoboss/realearn/master/resources/test-projects/issue-686-mouse-target.RPP[This example project] contains multiple examples (one per group). - -WARNING: Feedback for this target is not fully implemented. - -====== Global: Set automation mode override - -Sets the global automation mode override to the desired value if the incoming control value is greater than 0%, -otherwise removes the override. - -* *Behavior:* Lets you decide between not overriding anything, bypassing all envelopes or overriding with a specific - automation mode. -* *Mode:* Here you can pick the desired automation mode if _Behavior_ is _Override_. - -====== Project: Any on (solo/mute/...) - -This target is most useful in feedback direction. Map it to some LED on your controller and the LED will light up if at least one of the tracks in your project is e.g. mute (depending on the track parameter in question). - -If the control element is also a button, pressing the button will e.g. unmute all tracks in your project. - -* *Parameter:* The track parameter in question. - -[#project-invoke-reaper-action] -====== Project: Invoke REAPER action - -Triggers or sets the value of a particular REAPER action in the main section. - -* *Invoke:* Specifies _how_ the picked action is going to be controlled. -** *Trigger:* Invokes the action with the incoming absolute control value, but only if it's -greater than 0%. Most suitable for simple trigger-like actions that neither have an on/off state -nor are annotated with "(MIDI CC/OSC only)" or similar. -** *Absolute 14-bit:* Invokes the action with the incoming absolute control value, even if it's 0%. Most -suitable for actions which either have an on/off state or are annotated with "(MIDI CC/OSC -only)" or similar. The resolution of the invocation is 14-bit, no matter what's the resolution of your control element). -** *Absolute 7-bit:* Just like the previous invocation mode but uses 7-bit resolution. Might be necessary for actions provided by 3rd-party extensions which don't interpret 14-bit control values correctly. In all other circumstances, 14-bit is probably the better default choice. -** *Relative:* Invokes the action with the incoming relative control value (absolute ones are -ignored). Only works for actions that are annotated with ("MIDI CC relative only") or similar. -* *Pick!:* Opens REAPER's action dialog so you can select the desired action. -* *With track*: Allows you to choose a track which ReaLearn will select before executing the action. This makes it possible to combine ReaLearn's flexible track selection capabilities with the plethora of REAPER actions that work on the currently selected track. - -The particular action decides if toggling/feedback works completely, has limitations or is not possible at all. There -are multiple types of actions so it's not possible to settle with one invocation type and be done with it. The types -of actions can roughly be divided into: - -. Actions that take care of toggling themselves _and_ report on/off state. -** Example: "25. Track: Toggle record arm for track 01" -** If you want toggle behavior, you have 2 options: -*** a) Set Invoke to "Absolute" and Mode to "Toggle button" (preferred). -*** b) Set Invoke to "Trigger" and Mode to "Normal". -** Feedback is completely supported. -. Actions that take care of toggling themselves but _don't_ report on/off state. -** Example: "40175. Item properties: Toggle mute" -** Toggle behavior is achieved as described in (1) but support for toggling and feedback has limitations (explained - in (4)). -. Actions that don't take care of toggling themselves ("trigger only"). -** Example: "1007. Transport: Play" -** There's no way to make such an action toggle because the action is not designed to do so. -** If the action reports an on/off state, feedback is completely supported though, otherwise not at all! -. Actions that have a complete range of values as state. -** Example: "994. View: Adjust vertical zoom (MIDI CC/OSC only)" -** Since ReaLearn 2 and REAPER 6.20, there's special support for this type of actions. Starting from the first - time this action is triggered, ReaLearn will track its current value. -** That's why toggling is supported. Because ReaLearn itself takes care of toggling, you need to set _Invoke_ to - "Absolute" and Mode to "Toggle button". -** Feedback is also supported. -** Toggling/feedback for this type of actions comes with some inherent limitations that are related to the fact that - a) REAPER itself doesn't necessarily use actions to invoke its own functions and b) MIDI CC/OSC actions don't - have the concept of a "current value" (unlike e.g. toggle actions or FX parameters). -** The bottom line of these limitations is that toggling/feedback will only work if the action itself is used to - trigger the change and if the action is an absolute action (not relative). -** Limitations in detail: -... In most cases, feedback will not work when changing the value in REAPER directly (e.g. when adjusting - vertical zoom directly via the REAPER user interface). -... It will only work for actions that support some kind of absolute value range (usually the case for all - non-relative MIDI CC/OSC actions). -... When the action is invoked via ReaLearn, the feedback will only work if "Invoke" is "Trigger" or "Absolute". - It won't work with "Relative". -... When the action is invoked from ReaScript or other extensions, it will only work if the invocation was done - via `KBD_OnMainActionEx()` and an absolute value change. -... When the action is invoked via a native REAPER action mapping, it will only work if the invocation is done - using absolute MIDI CC/OSC (not relative). - -====== Project: Invoke transport action - -Invokes a transport-related action. - -* *Action:* Specifies which transport action should be invoked. -** *Play/stop:* Starts playing the containing project if the incoming absolute control value is greater than 0%, - otherwise invokes stop. -** *Play/pause:* Starts playing the containing project if the incoming absolute control value is greater than 0%, - otherwise invokes pause. -** *Stop:* Stops the containing project if the incoming absolute control value is greater than 0%. Useful for - distinguishing feedback between _paused_ and _stopped_ state. -** *Pause:* Pauses the containing project if the incoming absolute control value is greater than 0%. Useful for - distinguishing feedback between _paused_ and _stopped_ state. -** *Record:* Starts/enables recording for the current project if the incoming absolute control value is greater than - 0%, otherwise disables recording. -** *Repeat:* Enables repeat for the containing project if the incoming absolute control value is greater than 0%, - otherwise disables it. - -[#browse_tracks_target] -====== Project: Browse tracks - -Steps through tracks. To be used with endless rotary encoders or previous/next-style "Incremental buttons". - -* *Scroll TCP* and *Scroll mixer*: See <> target. -* *Scope:* Decides which tracks are considered and how. -** *All tracks:* Considers all tracks even those which are hidden. -** *Only tracks visible in TCP:* Considers only those tracks which are visible in the track control panel. -** *Only tracks visible in TCP (allow 2 selections):* Like "Only tracks visible in TCP" but makes it possible to have 2 selections. One for the MCP and one for the TCP. These selections can be moved independently. This can make sense if you have a bunch of tracks that you only show in the TCP and another separate bunch of tracks that you only show in the MCP. -** *Only tracks visible in MCP:* Considers only those tracks which are visible in the mixer control panel. -** *Only tracks visible in MCP (allow 2 selections):* See above. - -[#seek-target] -====== Project: Seek - -Allows you to use faders, knobs, encoders or incremental buttons to seek within portions of your project … -with feedback that indicates the current position! - -* *Feedback:* Determines how frequently ReaLearn captures feedback and sends it to your feedback output. -** *Beat:* Every beat. -** *Fast:* As fast as possible, thereby giving the satisfying feeling of continuity. This obviously uses some more - resources. No idea how far you can go with that. Try yourself. -* *Behavior:* Determines whether to use immediate or smooth seeking. -* *Seek play:* Doesn't just change the edit cursor but also changes the play position when the project is currently - being played. -* *Move view:* Allow to scroll / change viewport when seeking. - -The following options determine which time ranges will be taken into consideration as reference for seeking (control) -and feedback. - -. *Use time selection:* Can use the currently set time selection as reference. -. *Use loop points:* Can use the currently set loop points as reference. -. *Use regions:* Can use the current region as reference. -. *Use project:* Can use the complete project as reference, from start to end. - -If you don't tick any "Use" checkbox, ReaLearn will seek within the currently visible viewport. - -If you tick multiple options, this is the order of fallbacks: - -* If there's no time selection, the loop points will be used. -* If there are no loop points, the current region is used. -* If there's no current region, the project will be used. -* If the project is empty, the viewport will be used. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -|target.position.project_default | Position in the current transport time unit -|target.position.time | _minute:second.milli_ -|target.position.measures_beats_time | _measure.beat.milli_ -|target.position.measures_beats | _measure.beat.milli_ -|target.position.seconds | _second.milli_ -|target.position.samples | _sample_ -|target.position.hmsf | _hour:minute:second:milli_ -|target.position.absolute_frames | _frames_ -|target.position.project_default.mcu | Like `target.position.project_default` but tailored to Mackie Control timecode displays -|target.position.time.mcu | Like `target.position.time` but tailored to Mackie Control timecode displays -|target.position.measures_beats_time.mcu | Like `target.position.measures_beats_time` but tailored to Mackie Control timecode displays -|target.position.measures_beats.mcu | Like `target.position.measures_beats` but tailored to Mackie Control timecode displays -|target.position.seconds.mcu | Like `target.position.seconds` but tailored to Mackie Control timecode displays -|target.position.samples.mcu | Like `target.position.samples` but tailored to Mackie Control timecode displays -|target.position.hmsf.mcu | Like `target.position.hmsf` but tailored to Mackie Control timecode displays -|target.position.absolute_frames.mcu | Like `target.position.absolute_frames` but tailored to Mackie Control timecode displays -|=== - - -====== Project: Set playrate - -Sets REAPER's master playrate. - -*Attention:* This target doesn't currently work if the project containing ReaLearn is not the active project tab. - -[#project-set-tempo] -====== Project: Set tempo - -Sets REAPER's master tempo. - -This target is not learnable anymore via the "Learn target" button and also not eligible for -the <> target because it caused too many "false positives". - -[#marker-region-go-to] -====== Marker/region: Go to - -Navigates to a specific marker or region. Here's the behavior in detail: - -* Regions -** If the project is stopped, the editor cursor immediately jumps to the start position of the given region. -** If the project is playing, playback will continue with the given region as soon as the currently playing region - (or measure if not within a region) has finished playing. This is called "smooth seek". -** *Attention:* This currently doesn't work if the project containing ReaLearn is not the active project tab. -* Markers -** If the project is stopped, the editor cursor immediately jumps to the given marker. -** If the project is playing, playback will immediately be continued at the given marker. - -The cool thing about this target compared to REAPER's built-in actions is that it allows to target arbitrarily many -markers/regions (either by position or by ID) … and that it supports visual feedback! If you assign this target to a -button which has an LED, you will see which marker/region is currently playing just by looking at -your controller. - -Please note that this doesn't work when recording! - -User interface elements specific to this target: - -* *Marker/region:* -** *Left dropdown:* This dropdown lets you choose if you want to refer to a marker/region by its - user-assigned ID or by its position on the timeline. -** *Right dropdown:* This dropdown displays the markers or regions (depending on the _Regions_ checkbox state). -* *Now!:* This sets the target to the currently playing (or currently focused, if stopped) marker/region. -* *Behavior:* Determines whether to use immediate or smooth seeking. -* *Regions:* Switches between markers and regions. -* *Set loop points:* For regions, this will additionally set the loop points to the region start and end position. -* *Set time selection:* For regions, this will additionally set the time selection to the region start and end - position. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -|target.bookmark.id | (Numeric) ID of the bookmark -|target.bookmark.index | Index of the bookmark (counting both markers and regions) -|target.bookmark.index_within_type | Index of the bookmark (counting only markers or regions, respectively) -|target.bookmark.name | Name of the bookmark -|=== - -[#track-target] -====== Track - -A target that allows you to define a track. - -The setting **Act/Tags** stands for "Action / Instance tags" and decides what happens when a control messages arrives, e.g. a button press: - -- **None (feedback only):** With this setting, nothing will happen. It's suited very well as neutral target for textual feedback with an expression that contains a track property, e.g. `{{ target.track.name }}`. -- **Set (as instance track):** The button press will set the track defined in this target as <> _without resolving it before_. For example, if this target defines to use the currently selected track (<>), pressing the button will make the instance track dynamically reflect whatever track is selected. -- **Pin (as instance track):** The button press will resolve the track defined in this target and set the result as <>. For example, if this target defines to use the currently selected track, pressing the button will check which track is currently selected and set the instance track to exactly this track. It will stay that way even if the user selects another track. - -The text field to the right defines contains **Instance tags** of the ReaLearn instances whose instance track should be changed. If it's empty, the current instance will be affected. - -====== Track: Arm/disarm - -Arms the track for recording if the incoming absolute control value is greater than 0%, otherwise -disarms the track. This disables "Automatic record-arm when track selected". If you don't want that, -use the _Track: Select/unselect_ target instead. - -====== Track: Enable/disable all FX - -Enables all the track's FX instances if the incoming absolute control value is greater than -0%, otherwise disables them. - -====== Track: Enable/disable parent send - -Enables the parent send routing of the track if the incoming absolute control value is greater than 0%, otherwise disables it. - -====== Track: Mute/unmute - -Mutes the track if the incoming absolute control value is greater than 0%, otherwise unmutes the -track. - -====== Track: Peak - -This is a feedback-only target! It turns your feedback-capable controller into a VU meter by constantly reporting the -current volume of the configured track to it. - -In addition to connecting it with a LED ring or motor fader source (which should be obvious), it can also be used with -a single LED to build a clipping indicator: - -. Set _Target Min_ to the minimum dB value that should make your clipping LED turn on. Leave _Target Max_ at 12.00 dB. -. Make sure the _Out-of-range_ behavior is set to "Min or max". -. If you have an LED that supports multiple colors, you will probably see a rainbow of colors flashing up which can be - quite confusing. Use the feedback transformation formula `x = ceil(y)` to restrict the feedback to just two values: - Min (0%) or Max (100%). You can then use _Source Min_ and _Max_ to adjust the off/on LED colors. - -At the moment this target only reports peak volume, not RMS. - -====== Track: Phase invert/normal - -Inverts the track phase if the incoming absolute control value is greater than 0%, otherwise switches the track phase back to normal. - - -[#track-selectunselect] -====== Track: Select/unselect - -Selects the track if the incoming absolute control value is greater than 0%, otherwise unselects the -track. - -This target stops being learnable if you activate the REAPER preference -"Mouse click on volume/pan faders and track buttons changes track selection" (because this preference would generate -too many false positives). If you change the preference, ReaLearn will take it into consideration the next time -you restart REAPER. - -* *Scroll TCP:* Also scrolls the track control panel to the desired track. -* *Scroll mixer:* Also scrolls the mixer control panel to the desired track. - -====== Track: Set automation mode - -Sets the track to a specific automation mode if the incoming control value is greater than 0%, otherwise -sets it back to REAPER's default track automation mode "Trim/Read". - -* *Mode:* Here you can pick the desired automation mode. - -====== Track: Set monitoring mode - -Sets the track to a specific input monitoring mode if the incoming control value is greater than 0%, otherwise sets it back to "Off". - -* *Mode:* Here you can pick the desired monitoring mode. - -[#track-set-automation-touch-state] -====== Track: Set automation touch state - -When you use REAPER's "Touch" automation mode, REAPER needs a way to know if you are currently touching the control -element which is bound to the automation envelope or not. As long as you keep touching it, it will overwrite -existing automation. As soon as you release it, REAPER will leave the envelope untouched. - -Classical control surfaces implement this very intuitively by providing touch-sensitive faders. With this target, you -can easily reproduce exactly this behavior via ReaLearn. You do this by mapping the touch event (which is usually -nothing else than a MIDI note on/off message) to this target. The touch state is scoped to a particular track and -parameter type which you can choose in the **Type* dropdown. - -However, ReaLearn wouldn't be ReaLearn if it wouldn't allow you to let totally different sources take control of the -touch state. For example, if you have a push encoder, you could map the "push" event to the touch state, allowing you -to write automation only while you are touching the encoder. Or if you don't have a push encoder, you could just use -some spare button. - -====== Track: Set pan - -Sets the track's pan value. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -|target.pan.mcu | Pan value tailored to one line on a Mackie Control LCD -|=== - -====== Track: Set stereo pan width - -Sets the track's width value (applicable if the track is in stereo pan mode). - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -|target.width.mcu | Width value tailored to one line on a Mackie Control LCD -|=== - -====== Track: Set volume - -Sets the track's volume. - -====== Track: Show/hide - -Shows the track if the incoming absolute control value is greater than 0%, otherwise hides it. - -* *Area:* Lets you decide if you want it to show/hide in the track control panel or the mixer. - -[#track-solounsolo] -====== Track: Solo/unsolo - -Soloes the track if the incoming absolute control value is greater than 0%, otherwise unsoloes the -track. - -Provides the following additional settings: - -* *Behavior:* See the REAPER user guide for details. -** *Solo in place:* Soloes the track while respecting REAPER's routing. This is REAPER's default and since - ReaLearn v2.4.0 also ReaLearn's default. -** *Solo (ignore routing):* Soloes the track muting everything else, no matter the routing. -** *Use REAPER preference:* Follows whatever is set in the REAPER preferences. - -Learning this target by pressing the "Solo" button of the _master_ track is currently not possible but -of course you can just select it manually in the dropdown menu. - -====== FX chain: Browse FXs - -Steps through the FX instances in the FX chain by always having exactly one FX instance visible. -To be used with endless rotary encoders or previous/next-style "Incremental buttons". - -* *Display:* Here you can decide if you want to display the FX as part of the FX chain or in a dedicated floating - window. - - -[#fx-target] -====== FX - -A target that allows you to define an FX, in its basic variant perfect for acquiring feedback for a specific FX. - -The setting **Act/Tags** allows you to optionally set/pin the declared FX as <>. This works pretty much the same as described in target <>. - -[#fx-enabledisable] -====== FX: Enable/disable - -Enables the FX instance if the incoming absolute control value is greater than 0%, otherwise -disables it. - -====== FX: Set online/offline - -Sets the FX instance online if the incoming absolute control value is greater than 0%, otherwise -sets it offline. - -[#fx-load-snapshot] -====== FX: Load snapshot - -Restores a certain state of a particular FX. Before using this target, you need to take a snapshot of the desired FX -state using the _Take!_ button. This snapshot will be saved as part of ReaLearn's state itself and as a direct -consequence as a part of your project. This makes your project nicely self-contained. It's perfect for activating -particular FX presets because it will always restore the desired state, even if the preset list has changed. - -This target supports feedback, but only if the snapshot is loaded via ReaLearn itself. - -Please note that some plug-ins have _very large_ states. Therefore you should keep an eye on the snapshot size, which -will be displayed once you take the snapshot. ReaLearn's own state will grow with every new snapshot mapping, so this -can quickly add up and make REAPER/ReaLearn slow! - -[#fx-browse-presets] -====== FX: Browse presets - -Steps through FX presets. - -This target is suited for use with knobs, encoders and incremental buttons (previous/next) because it allows -you to step through the complete preset list. The minimum value always represents _No preset_ whereas the -maximum value always represents the last available preset. - -It's _not_ suited for activating a particular preset (e.g. by setting _Target Min_ and _Max_ to the same value), -because the preset list of an FX is usually not constant. As soon as you modify the preset list, this value will might -suddenly point to a completely different preset. Even worse, the actual preset might have been deleted. - -If you want to activate a particular preset, please use the <> target -instead. - -====== FX: Open/close - -Makes the FX instance visible if the incoming control value is greater than 0%, otherwise hides it. - -* *Display:* Here you can decide if you want to display the FX as part of the FX chain or in a dedicated floating - window. - -====== FX parameter: Set automation touch state - -This does the same as <> but for FX parameter value changes. - - -[#fx-set-parameter-value] -====== FX parameter: Set value - -Sets the value of a particular track FX parameter. - -* *Parameter:* The parameter to be controlled. Please note that both <> and <> address the FX by its position in the FX chain. The difference between the two is that <> shows a dropdown containing the available parameters and <> lets you enter the position as a number in a text field. Latter is useful if at the time of choosing the position, the FX is not available. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -| -target.fx_parameter.index -| -Zero-based index of the resolved FX parameter. - -| -target.fx_parameter.name -| -Name of the resolved FX parameter. - -| -target.fx_parameter.macro.name -| -Name of the corresponding Pot macro parameter. Only works if this parameter is part of a preset loaded via Pot. - -| -target.fx_parameter.macro.section.name -| -Name of the corresponding Pot macro parameter section. Only works if this parameter is part of a preset loaded via Pot. - -| -target.fx_parameter.macro.section.index -| -Zero-based index of the corresponding Pot macro parameter section (within the current bank). Only works if this parameter is part of a preset loaded via Pot. - -| -target.fx_parameter.macro.new_section.name -| -Name of the corresponding Pot macro parameter section, but only if this parameter marks the start of a new section. Only works if this parameter is part of a preset loaded via Pot. - -| -target.fx_parameter.macro.bank.name -| -Name of the corresponding Pot macro parameter bank. Only works if this parameter is part of a preset loaded via Pot. -|=== - -[#pot-browse-filter-items] -====== Pot: Browse filter items - -This target can be used to filter the potentially very large collection of presets in <>. The idea is to map this target to an endless rotary encoder or previous/next buttons (using <> mode) and then navigate within the available filter items, e.g. instruments or banks. - - -* *Kind:* Choose the kind of filter items that you want to browse. They correspond to the filters available in <>. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -| -target.item.name -| -Name of the filter item. - -| -target.item.parent.name -| -Name of the parent filter item if there's any. E.g. the instrument to which a bank belongs or the type to which a sub type belongs. -|=== - -[#pot-browse-presets] -====== Pot: Browse presets - -Use this target to browse a collection of presets. By default, this is the complete collection of presets available in all supported databases, so potentially thousands of presets. If you want to browse just a subset, see <>. - -The idea is to map this target to an endless rotary encoder or previous/next buttons (using <> mode) and then navigate within the available presets. Once you have selected a preset, you can audition it via <> (if it's a sound preset) and load it via <>. - -This target supports the following additional placeholders in textual feedback expressions: - -[cols="m,1"] -|=== -| -target.preset.name -| -Name of the preset. - -| -target.preset.product.name -| -Name of the product to which this preset belongs, if available. - -| -target.preset.file_ext -| -File extension of the preset, in case it's a file-based preset. - -| -target.preset.author -| -Name of the preset author, if available. - -| -target.preset.vendor -| -Name of the preset vendor, if available. - -| -target.preset.comment -| -Preset comment, if available. - -|=== - -[#pot-preview-preset] -====== Pot: Preview preset - -Auditions a preset selected via <>. Only works if it's a sound preset and a sound preview file is available. - -[#pot-load-preset] -====== Pot: Load preset - -Loads a preset selected via <>. - -NOTE: This needs at least REAPER version 6.69+dev1030! Also, it only works if you have the VST2/VST2i version of the corresponding plug-in installed. -- *NKS audio file presets:* Loading supported via ReaSamplOmatic5000 - -Settings: - -* *Track/FX:* You must tell the target at which FX slot to load the corresponding plug-in. The best idea is to use FX selector <>. Selectors such as <> or <> are not suited because the target might replace the plug-in with another one, in which the unique FX ID and the FX name can change. Then the target would turn inactive and stop working. - -This target supports the same additional placeholders for textual feedback expressions as <>. The only difference is that the ones in <> relate to the currently loaded preset, not the one that's selected in the preset browser. - -====== Send: Automation mode - -Sets the track send to a specific automation mode if the incoming control value is greater than 0%, otherwise sets it back to REAPER's default automation mode "Trim/Read". - -====== Send: Mono/stereo - -Sets the track send to mono or back to stereo. - -====== Send: Mute/unmute - -Mutes/unmutes the track send. - -====== Send: Phase invert/normal - -Inverts the track send phase or switches it back to normal. - -====== Send: Set automation touch state - -This does the same as <> but for send volume or pan adjustments. - - -====== Send: Set pan - -Sets the track send's pan value. - -====== Send: Set volume - -Sets the track send's volume. - -====== Clip: Invoke transport action - -CAUTION: Clips are a highly experimental feature of ReaLearn and still subject to many changes! Better don't rely on it at the moment! - -_Under construction_ - -====== Clip: Seek - -_Under construction_ - -====== Clip: Volume - -_Under construction_ - -[#midi-send-message] -====== MIDI: Send message - -Sends arbitrary MIDI messages (also sys-ex!) in response to incoming messages. This target turns ReaLearn into -a capable and convenient MIDI → MIDI and OSC → MIDI converter. - -* *Output:* Where to send the MIDI message. -** *FX output:* Sends the MIDI message to the output of this ReaLearn instance - which usually means it flows - into the FX below ReaLearn, e.g. a VST instrument. -+ -** *Feedback output:* Sends the MIDI message to the device which is set as _output_. -* *Pattern:* Defines the MIDI message to be sent as a sequence of bytes in hexadecimal notation. It also allows you - to encode the incoming _absolute_ control value as part of the message (after it has been processed by the glue - section). The syntax for doing this takes some getting used to but it's very flexible. It's exactly the same syntax as - used in the <>. - Please read about it there! -* *...* Primarily provides predefined patterns. Just pick one here, set the destination to "Feedback output" and - add a "ReaControlMIDI" FX below to see which messages ReaLearn sends. - -[NOTE] -==== -This target is a bit special in that it carries out its processing logic exclusively in the audio thread if it's controlled by a MIDI source. This has the big advantage that receiving and producing MIDI messages happens in one go (without inter-thread-communication latency), which is often important when using MIDI message conversion. - -However, this also means that the following things won't work when controlling this target using MIDI: - -* It can't take the lead in <>. -* If _output_ is set to ``, additional limitations apply: -** It can't act as a follower in <>, either. -** It can't participate in <>. -==== - -[#osc-send-message] -====== OSC: Send message - -Sends OSC messages with up to one argument in response to incoming messages. This target turns ReaLearn into -a capable and convenient MIDI → OSC and OSC → OSC converter. If an argument number is entered (e.g. `1`), -it will encode the incoming absolute control value as that argument (after it has been processed by the glue section). - -* *Output:* Where to send the OSC message. -** *<Feedback output>:* Sends the OSC message to the device which is set as _Output_. Of course this only - works if it's an OSC device. -** *_Specific device:_* Sends the OSC message to a specific device. -* *Address*, *Argument* and *Range*: These correspond to the identically named settings of <>. - Check that section for details. - -[#realearn-enable-disable-instances] -====== ReaLearn: Enable/disable instances - -This target allows you to flexibly enable or disable other ReaLearn instances based on their tags: - -* *Exclusivity* -** *Non-exclusive:* If the incoming control value is greater than 0%, all matching ReaLearn instances will be enabled (on top of the already enabled instances). If the value is 0%, all matching ReaLearn instances will be disabled. -** *Exclusive:* If the incoming control value is greater than 0%, all matching ReaLearn instances will be enabled and all non-matching ones will be disabled. If the value is 0%, it's exactly the opposite (react to button <> if you don't want this to happen). -** *Exclusive (on only):* Variation of _Exclusive_ that applies exclusivity only if the incoming control value is greater than 0%. -* *Tags:* A ReaLearn instance matches when it is tagged with any of the tags entered in this field (comma-separated). - -TIP: Use the main panel to assign tags to a ReaLearn instance. - -Please note: - -* This really affects other ReaLearn instances only, not _this_ instance. -* ReaLearn instances without tags won't be affected at all. -* Only affects instances in the same project. If _this_ ReaLearn instance is on the monitoring FX chain, it only affects other instances in the monitoring FX chain. - -TIP: This target is great for switching between completely different controller setups! - -[#realearn-dummy-target] -====== ReaLearn: Dummy target - -This target simply does nothing when invoked and also doesn't provide any meaningful feedback on its own. - -It's sometimes useful to have such a dummy target, e.g. combined with <>. Or if you want to use ReaLearn as a MIDI filter which just "eats" an incoming MIDI message. Or if you want to send some text feedback to a hardware display, if the text is just a constant string or uses a placeholder that doesn't need target context. - -[#realearn-enable-disable-mappings] -====== ReaLearn: Enable/disable mappings - -This target allows you to flexibly enable or disable other mappings in this instance based on their tags: - -* *Exclusivity* -** *Non-exclusive:* If the incoming control value is greater than 0%, all matching mappings will be enabled (on top of the already enabled mappings). If the value is 0%, all matching mappings will be disabled. -** *Exclusive:* If the incoming control value is greater than 0%, all matching mappings will be enabled and all non-matching ones will be disabled. If the value is 0%, it's exactly the opposite (react to button <> if you don't want this to happen). -** *Exclusive (on only):* Variation of _Exclusive_ that applies exclusivity only if the incoming control value is greater than 0%. -* *Tags:* A mapping matches when it is tagged with any of the tags entered in this field (comma-separated). - -TIP: Use the text field on the top of the mapping panel to assign tags to a mapping. - -Please note: - -* This really affects other mappings only, not _this_ mapping. -* Mappings without tags won't be affected at all. - -TIP: This target is a straightforward alternative to <>, especially when it comes to bank switching! - -[#realearn-load-mapping-snapshot] -====== ReaLearn: Load mapping snapshot - -Restores target values for all or certain mappings in this ReaLearn instances. - -* *Snapshot:* Choose the snapshot that you want to load. -** *:* Restores the initial target values for the mappings. -** *By ID:* Restores target values contained in a snapshot that was taken via <>. Simply enter the corresponding ID here. -* *Default:* Allows you to define a default target value to restore for each participating mapping whenever the snapshot either doesn't exist or doesn't contain a value for that mapping. If that participating mapping has <> checked, the inverse of the default value will be loaded. -* *Tags:* Allows you to restrict the set of mappings whose target values will be restored. -** If this field is empty, target values of all mappings will be restored. -** If this field contains tags (comma-separated), target values will be restored only for mappings that are tagged with any of these. -* *Active mappings only:* By default, even target values for inactive (but control-enabled) mappings are restored! If you don't like that, tick this checkbox. - -Please note: - -* Mappings for which control is not enabled, never participate in snapshotting. -* Some targets don't report values and therefore don't participate in snapshotting. -* Feedback of this target indicates whether the desired snapshot is the one which has last been loaded (for the given tags). - -====== ReaLearn: Modify mapping - -Triggers a modification of another ReaLearn mapping. - -* *Kind:* The kind of modification. -** *Learn target:* Switches "Learn target" on or off for the destination mapping. Use button kbd:[...] to pick the considered target types and invocations to be included in the learning process. -** *Set target to last touched:* Sets the target of the destination mapping to the last-touched target. Use button kbd:[...] to pick the considered target types and invocations. -* *Instance:* Allows you to pick another ReaLearn instance. -* *Mapping:* Allows you to pick the destination mapping. - -TIP: This target is great to "pin" targets to certain control elements on demand. - -[#realearn-take-mapping-snapshot] -====== ReaLearn: Take mapping snapshot - -Memorizes target values for all or certain mappings in this ReaLearn instances and saves them in a snapshot of your choice. - -* *Snapshot*: Choose the snapshot to which you want to save the mapping values. -** *:* Always chooses the snapshot which is currently active (was last loaded) for the given tags. -+ -TIP: Only works if tags are not empty and if all tags have the same last-loaded snapshot. So the best is if you always enter exactly one tag. -** *By ID:* Enter the unique ID of the snapshot, e.g. `scene_1`. -* *Tags:* Allows you to restrict the set of mappings whose target values will be memorized. -** If this field is empty, target values of all mappings will be memorized. -** If this field contains tags (comma-separated), target values will be memorized only for mappings that are tagged with any of these. -* *Active mappings only:* By default, even target values of inactive (but control-enabled) mappings end up in the snapshot! If you don't like that, tick this checkbox. - -[#realearn-browse-group-mappings] -====== ReaLearn: Browse group mappings - -This target lets you choose an arbitrary mapping group in this compartment and cycle through it with an encoder/fader/knob or incremental (previous/next) buttons. - -"Cycling through" means that you move from one mapping in the group to the next one by hitting the next mapping's target with the _Target Max_ value in its glue section (by default 100%). - -* *Group:* The group that you want to browse. -* *Exclusivity* -** *Non-exclusive:* Really just hits the target of the mapping which is next in the line and doesn't do anything with the other mappings. In many cases this is enough, e.g. if the targets of the mappings in the cycled group are the same and just "Target Max" is different. Or if the target itself already takes care of exclusivity. -** *Exclusive:* Doesn't just hit the target of the mapping which is next in the line but also hits the targets of all other mappings in the cycled group with their respective _Target Min_ value (by default 0%). Be careful with this, you often won't need it. - -Please note: - -- Inactive mappings are skipped. - -[TIP] -==== -Mapping lend themselves perfectly for defining things that should happen _in sequence_. This target allows you to take advantage of that! - -- Combine it with <> to browse different banks. -- Combine it with <> to browse completely different controller setups (or banks). -- Combine it with targets that don't provide a "Browse ..." variant themselves. -- Use it as an alternative to <> that allows you to have completely different targets within one sequence. -==== - -[#virtual-target] -===== Category "Virtual" - -This is exactly the counterpart of the possible <>. Choosing a virtual target here is like -placing cables between a control element and all corresponding main mappings that use this -virtual control element as source. - -[#glue] -==== Glue - -As mentioned before, the glue section defines the glue between a source and a target. It's divided into -several sub sections some of which make sense for all kinds of sources and others only for some. - -*At first something important to understand:* Since ReaLearn 2, a mapping can deal with both _absolute_ -and _relative_ values, no matter what's set as _Mode_! ReaLearn checks the type of each emitted source value -and interprets it correctly. The _Mode_ dropdown has been sort of "degraded" because now it only applies to -incoming _absolute_ values and determines how to handle them (see further below). This change has been made -to support virtual sources - because virtual sources can be either absolute or relative depending on the current -controller mappings. ReaLearn allows you to prepare your mapping for both cases by showing all possible settings. - -_Relative_ means that the current target value is relevant and the change of the target value is calculated in -terms of increments or decrements. Control elements that can emit relative values are rotary encoders and -virtual multis. - -Having so many settings available at the same time can be a bit daunting. ReaLearn helps you by hiding settings -that don't make sense in the current context. It shows or hides them based on the following criteria: - -* Is control and/or feedback enabled for the mapping? -* What are the characteristics of the source and target? -* What's the current setting of _Absolute mode_ and _Make absolute_? - -===== Reset to defaults - -Resets the settings to some sensible defaults. - -===== Reverse - -If checked, this inverses the direction of the change. E.g. the target value will - decrease when moving the fader upward and increase when moving it downward. - -[#target-min-max] -===== Target Min/Max - -The controlled range of absolute target values. This enables you to "squeeze" -target values into a specific value range. - -Example: If you set this to "-6 dB to 0 dB" for a _Track -volume_ target, the volume will always stay within that dB range if controlled via this mapping. -It wouldn't prevent the volume from exceeding that range if changed e.g. in REAPER itself. - -This -setting applies to targets which are controlled via absolute control values (= all targets with -the exception of the <> if invocation type is _Relative_). - -These are relevant for the control direction only: - -[#target-value-sequence] -===== Value sequence - -Allows you to define a specific sequence of target values. This is a very powerful feature because - you not only can enter single values but also ranges with customizable step sizes! Plus, it has support for true - relative control. All values are entered comma-separated and using the unit chosen in the <>. - Just click the unit button to switch between the native target unit and percentages. - -Examples: - -* `-20, -14, -12, -6, -3.5, 0`: Enter this sequence for a volume target with target unit switched to dB. When you - move your knob or rotary encoder or press a button using _Incremental button_ mode, ReaLearn will step through - the entered dB values for you. -* `10 - 30, 50 - 70 (5), 80 - 90 (2)`: Enter this sequence for a target with a continuous value range and target - unit switched to %. It will first step in 1% steps from 10% to 30%, then in 2% steps from 50% to 70% and finally - from 80% to 90% in 2% steps. At the moment it's important that the numbers and the range dash are separated by - spaces! -* `20, 10, 10, -5, 8`: When using absolute control, even duplicate values and direction changes are respected, as - seen in this value sequence. However, true relative control naturally supports continuous sequences only. So if - you have a rotary encoder that sends relative messages (hopefully!) or use incremental buttons, the sequence will - be stepped through in a continuous manner (-5, 8, 10, 20). The benefit as always: No parameter jumps! If you want - to use non-continuous sequences with encoders or incremental buttons, you can always use _Make absolute_! - -[#group-interaction] -===== Group interaction - -Lets you control not just _this_ mapping but also _all other mappings in the same mapping - group_. Very powerful feature! - -TIP: If you want to control _other_ mappings only and not _this_ mapping, just pick - a target that doesn't have any effect, for example the <> or an unused internal ReaLearn compartment parameter - (target <> with FX set to ``). - -* *None:* Switches group interaction off. This is the default. Incoming control events will just affect _this_ - mapping, not others. -* *Same control:* This will broadcast any incoming control value to all other mappings in the same group. The - glue section of this mapping will be ignored when controlling the other mappings. The glue sections of the - other mappings will be respected, including the source min/max setting. -* *Same target value:* This will set the target value of each other mapping in the same group to the target value - of this mapping. Nice: It will respect the target min/max setting of both this mapping and the other mappings. - All other settings of the glue section will not be processed. Needless to say, this kind of control is always - absolute, which means it can lead to parameter jumps. Therefore, it's most suited for on/off targets. If you don't - like this, choose _Same control_ instead. -* *Inverse control:* This is like _Same control_ but broadcasts the _inverse_ of the incoming control value. -* *Inverse target value:* This is like _Same target value_ but sets the target values of the other mappings to the - _inverse_ value. This is very useful in practice with buttons because it essentially gives you exclusivity within - one group. It's a great alternative to the _Exclusive_ setting which is available for some targets. Unlike the - latter, _Inverse target value_ allows for exclusivity between completely different target types and completely - custom groupings - independent of e.g. organization of tracks into folders. -* *Inverse target value (on only):* Variation of _Inverse target value_ that applies the inverse only when the target value is > 0%. -* *Inverse target value (off only):* Variation of _Inverse target value_ that applies the inverse only when the target value is 0%. - -===== Feedback type - -Determines whether to send numeric, textual or dynamic feedback to the source. - -====== Numeric feedback: EEL transformation - -Sends numeric feedback to the source. This is the default. - -The text field below allows you to specify an optional feedback transformation formula, which is much like <> but used for translating a target value back to a source value for feedback purposes. Be aware: Here `x` is the desired source value (= output value) and `y` is the current target value (= input value), so you must assign the desired source value to `x`. - - - -Example: `x = y * 2`. - -ReaLearn's feedback processing order is like this since version 2: -.. Apply target interval. -.. Apply reverse. -.. Apply transformation. -.. Apply source interval. - -[#textual-feedback] -====== Textual feedback: Text expression - -With this option, ReaLearn will send textual feedback values to the source. This only works with sources that are capable of displaying text: That is any <> with argument type _String_, <> and <>. - -The field below contains the _textual feedback expression_. Here you define which text is going to be sent to the source _whenever the target value changes_ and also - for your convenience - immediately at the moment of entering the text. Whatever text you enter here, will be sent verbatim to the source. - -Of course, entering a fixed text here is not very exciting. Most likely you want to display dynamic text such as the name of the currently selected track or the current target value, nicely formatted! You can do that by using placeholders, delimited by double braces. Example: `{{target.text_value}}`. - -Which placeholders are available, depends very much on the type of the mapping target. However, there are some which are available for (almost) any target: - -[cols="m,1"] -|=== -| -global.realearn.time -| -Time in milliseconds since ReaLearn has been loaded (the first instance). - -| -mapping.name -| -Name of the mapping. Contains the explicitly assigned mapping name, never an automatically generated one. - -| -target.text_value -| -Short text representing the current target value, including a possible unit. - -If the target value can be represented using some kind of name, this name is preferred over a possibly alternative numeric representation. Example: Let's assume the 4th track in our project is called "Guitar" and the mapping target is <>. Then `target.text_value` contains the text _Guitar_, not the text _4_. - -This is the default value shown if textual feedback is enabled and the textual feedback -expression is empty. - -| -target.available -| -A boolean value indicating whether the target is currently available or not. - -Most targets that are _active_ are also _available_. But some targets can be _active_ and _unavailable_. Example: <> is not _available_ if no preview is available for the preset currently selected in Pot browser. But the target is still considered _active_ in this case! - -Usually used together with <>, for example in order to display different things on displays depending on the target's availability. - -| -target.discrete_value -| -The current target value as zero-based integer. This only works for discrete targets. - -| -target.discrete_value_count -| -The number of possible values in the current target. This only works for discrete targets. - -| -target.numeric_value -| -The current target value as a "human-friendly" number without its unit. - -The purpose of this placeholder is to allow for more freedom in formatting numerical target values than -when using `target.text_value`. Future versions of ReaLearn might extend textual feedback -expressions in a way so the user can define how exactly the numerical value is formatted -(e.g. the number of digits after the decimal point). - -| -target.numeric_value.unit -| -Contains the unit of `target.numeric_value` (e.g. _dB_). - -| -target.normalized_value -| -The current target value as normalized number, that is, a value between 0.0 and 1.0 (the so-called unit interval). You can think of this number as a percentage, and indeed, it's currently always formatted as percentage. - -This value is available for most targets and good if you need a totally uniform and predictable representation of the target value that doesn't differ between target types. - -By default, this number is formatted as percentage _without_ the percent sign. Future versions of ReaLearn might offer user-defined -formatting. This will also be the preferred form to format on/off states in a -custom way (where 0% represents _off_). - -| -target.type.name -| -Short name representing the type of the mapping target. - -| -target.type.long_name -| -Long name representing the type of the mapping target. - -| -target.track.index -| -Zero-based index of the first resolved target track (if supported). - -| -target.track.name -| -Name of the first resolved target track (if supported). - -| -target.fx.index -| -Zero-based index of the first resolved target FX (if supported). - -| -target.fx.name -| -Name of the first resolved target FX (if supported). - -| -target.route.index -| -Zero-based index of the first resolved target send/receive/output (if supported). - -| -target.route.name -| -Name of the first resolved target send/receive/output (if supported). -|=== - -For target-specific placeholders, please look up the corresponding <> section. - -[#dynamic-feedback] -====== Dynamic feedback: Lua script - -This feedback type puts you fully in charge about which feedback to send to the source. It does so by letting you define a Lua script that builds numeric, textual or even arbitrarily structured feedback. - -*General mechanics* - -* ReaLearn executes your script whenever one of the ReaLearn-provided properties used in your script might have changed its value. -* The script receives an input and must produce an output. -* *Script input* -** The input is a function `context.prop` which you can use to query arbitrary properties, e.g. target or mapping properties. Those properties are the very same properties that you can use in <>. -** Here's an example how to use this function: -+ -[source,lua] ----- -local preset_name = context.prop("target.preset.name") -local param_name = context.prop("target.fx_parameter.name") ----- -+ -** Values returned by this function can be `nil`! E.g. target-related properties return a `nil` value whenever the mapping or target turns inactive, which is a very common situation. So it's important to prepare your Lua code for that, otherwise script execution fails and no feedback will be sent. One way to deal with a `nil` value returned by `context.prop` is to also return `nil` as `value` (see below). -* *Script output* -** The output that the script is supposed to return is a table which looks as in the following example: -+ -[source,lua] ----- -return { - feedback_event = { - -- The feedback value <1> - value = "Arbitrary text", - -- An optional color <2> - color = { r = 0, g = 255, b = 0 }, - -- An optional background color <3> - background_color = nil, - } -} ----- -<1> In this example it's a text value, but it can be anything! -<2> Has the same effect as color in <> -<3> Has the same effect as background color in <> -+ -** The most important thing here is `value`. It can either be ... -*** ... a string (ideal for display sources) -*** ... a number (ideal for LEDs and motor faders) -*** ... `nil` (which means "turn the source off", e.g. turn off the LED, turn down the motorfader, clear the display text) -*** ... or anything else (`true`, `false` or an arbitrary table ... at the moment, this is only useful for the <> source because other sources don't know how to deal with it) -* *Examples* -** Displays the number of milliseconds passed since ReaLearn was loaded: -+ -[source,lua] ----- -local millis = context.prop("global.realearn.time") -return { - feedback_event = { - value = "" .. millis .. "ms" - }, -} ----- -+ -** Creates an animation to make a long FX name visible on a tiny screen. -+ -[source,lua] ----- -function create_left_right_animation(global_millis, max_char_count, frame_length, text) - if text == nil then - return nil - end - if #text > max_char_count then - local frame_count = #text - max_char_count - local frame_index = math.floor(global_millis / frame_length) % (frame_count * 2) - local text_offset - if frame_index < frame_count then - text_offset = frame_index - else - local distance = frame_index - frame_count - text_offset = frame_count - distance - end - return text:sub(text_offset + 1, text_offset + max_char_count) - else - return text - end -end - --- The maximum number of characters we want to display at once -local max_char_count = 10 --- How many milliseconds to remain in one position -local frame_length = 150 -local millis = context.prop("global.realearn.time") -local fx_name = context.prop("target.fx.name") -local animation = create_left_right_animation(millis, 10, frame_length, fx_name) -return { - feedback_event = { - value = animation - }, -} ----- -+ -** Returns a structured feedback value ... -+ -[source,lua] ----- -return { - feedback_event = { - value = { - available = context.prop("target.available"), - more_info = { - index = context.prop("target.discrete_value"), - count = context.prop("target.discrete_value_count"), - }, - } - }, -} ----- -** ... which can then be processed by a <> source (this example is not realistic, it just shows how you can access the value table returned by the glue section feedback script): -+ -[source,lua] ----- -return { - address = 0x4bb0, - messages = { - { 0xb0, 0x4b, y.more_info.index, y.more_info.count } - } -} ----- - - - -[#feedback-style] -===== Feedback style - -The ... button provides options to change the _feedback style_. At the moment, it's all about setting colors. - -TIP: If you use <>, changes made here don't have any effect because you are supposed to provide style properties as part of the Lua script result (which is much more flexible). - -* *Color / Background color:* With this you can define the color and background color of the displayed text. Of course this will only work if the display source supports it! -** *:* Chooses the default color, that is the one which is preferred for the corresponding controller and display type. -** *:* Opens a color picker so you can choose the color of your choice. -** *_Property name_:* Maybe you don't want a fixed color but a dynamic one that changes whenever your target changes. Choose one of the following properties to make that happen: -+ -[cols="m,1"] -|=== -| -target.track.color -| -Custom color of the first resolved target track (if supported). - -| -target.bookmark.color -| -Custom color of the resolved marker or region. - -Only works with the <> target. -|=== - - -===== Source Min/Max - -The observed range of absolute source control values. By restricting that - range, you basically tell ReaLearn to react only to a sub range of a control element, e.g. only - the upper half of a fader or only the lower velocity layer of a key press. In relative mode, this - only has an effect on absolute source control values, not on relative ones. This range also - determines the minimum and maximum feedback value. - - -===== Out-of-range behavior - -This determines ReaLearn's behavior if the source value is not within - "Source Min/Max" or the target value not within "Target Min/Max". There are these variants: - -|=== -| | *Control direction (absolute mode only)* | *Feedback direction* -| *Min or max* | If the source value is < _Source Min_, ReaLearn will behave as if _Source Min_ was received (or 0% if _Source Min_ = _Source Max_). - -If the source value is > _Source Max_, ReaLearn will behave as if _Source Max_ was received (or 100% if _Source Min_ = _Source Max_). | If the target value is < _Target Min_, ReaLearn will behave as if _Target Min_ was detected (or 0% if _Target Min_ = _Target Max_). - -If the target value is > _Target Max_, ReaLearn will behave as if _Target Max_ was detected (or 100% if _Target Min_ = _Target Max_). - - | *Min* | ReaLearn will behave as if _Source Min_ was received (or 0% if _Source Min_ = _Source Max_). | ReaLearn will behave as if _Target Min_ was detected (or 0% if _Target Min_ = _Target Max_). Useful for getting radio-button-like feedback. - - | *Ignore* | Target value won't be touched. | No feedback will be sent. -|=== - - -===== Mode ("Absolute mode") - -Let's you choose an _absolute mode_, that is, the way incoming absolute source values are handled. - -TIP: Not all modes make sense at all times! It mostly depends on the character of the source. If a mode doesn't make sense given the current source, it will be marked as `NOT APPLICABLE`. In this case, you should choose another mode or change the source. - -====== Normal - -Takes and optionally transforms absolute source control values _the normal way_. _Normal_ means that -the current target value is irrelevant and the target will just be set to whatever absolute control value is -coming in (potentially transformed). - -[#incremental-button] -====== Incremental button - -With this you can "go relative" without having encoders, provided your control elements -are buttons. Let's assume you use the _MIDI Note velocity_ and select _Incremental button_ mode. -Then it works like this: Each time you press the key, the target value will increase, according to the mode's -settings. You can even make the amount of change velocity-sensitive! If you want the target value to decrease, -just check the _Reverse_ checkbox. - -====== Toggle button - -Toggle button mode is used to toggle a target between on and off states. It only makes sense for momentary buttons (which fire a value > 0 on each press). - -Here's how it works in detail: - -* If the current target value is within the first half of the target min/max range, it's considered as _off_ and will therefore be switched _on_ (set to _target max_). If it's within the second half, it's considered as _on_ and will therefore be switched _off_ (set to _target min_). -* It works a bit differently if _target min_ and _target max_ have the same value (which is a common technique to set the target to a specific value on the press of a button). Instead of toggling between _target min_ and _target max_, this mode now toggles between this specific value (= _target min_ = _target max_) and 0%. This is useful whenever you have a set of buttons each of which sets the same target to a different value, and you want them to toggle between the specified value and an initial value (0%). - -This mode is not supported for controller mappings that have a virtual target. - -[TIP] -==== -Sometimes the controller itself provides a toggle mode for buttons. *Don't use it!* - -Always set up your controller buttons to work in momentary mode! It's impossible for the controller -to know which state (on/off) a target currently has. Therefore, if you use the controller's built-in -toggle function, it's quite likely that it gets out of sync with the actual target state at some point. - -ReaLearn's own toggle mode has a clear advantage here. -==== - -[#make-relative] -====== Make relative - -This converts incoming absolute fader/knob movements into relative adjustments of the target value. It somewhat resembles takeover mode <> but has important differences: - -- It's guaranteed that a full fader/knob swipe from 0% to 100% always results in a swipe over the full target range (assuming the target was at 0% initially). -- It doesn't need to know the current target value. Which means it also works for mappings with <>. - -[#performance-control] -====== Performance control - -This mode emulates the behavior of a typical soft synth modulation matrix mapping: It uses the target value that has been set in REAPER (not via this ReaLearn mapping) as an offset and starts changing it from there. - -[#takeover-mode] -===== Takeover mode - -If you are not using motorized faders, absolute mode is inherently prone to -parameter jumps. A parameter jump occurs if you touch a control element (e.g. fader) whose -position in no way reflects the current target value. This can result in audible jumps because the -value is changed abruptly instead of continuously. You can deal with this by setting the right takeover mode. - -ReaLearn provides multiple takeover modes that decide how to deal with situations when a target parameter -jump would occur. - -====== Off - -The default settings: Jumps allowed. - -[#pick-up] -====== Pick up - -This is the same as "Soft takeover" in REAPER's built-in MIDI learn. It prevents jumps by not changing the target value until your control element reaches it. - -In certain cases, this mode can cause the target value to get stuck. This happens with faders/knobs that cause jumps themselves when moved very rapidly. If you don't like that, you might want to try <>. - -[#pick-up-tolerant] -====== Pick up (tolerant) - -This is like <> but makes extra sure that the target value doesn't get stuck. - -However, unlike <>, this mode will jump if you cause a jump on your controller! Imagine using a touch strip. This kind of control element allows you to jump to arbitrary values at any time. Tolerant mode will not prevent this kind of jumps! - - -====== Long time no see - -This is similar to <> with the difference that the current target value - will gradually "come your way". This results in seamless and fast reunification of control and target - value but it can feel weird because the target value can temporarily move in the opposite - direction of the fader movement. In older ReaLearn versions this was called "Slowly approach if jump too big". - -[#takeover-mode-parallel] -====== Parallel - -With this mode, the target will simply follow your fader moves, in exactly the same tempo - - without any scaling. Reunification only happens when both control and target value meet at the "borders". - - -====== Catch up - -This mode is sometimes called "Proportional" or "Value scaling" mode. It's like "Parallel" mode - but the target value is allowed to move slower than the control value - hence the control can catch up (converge) faster. - -[#control-transformation] -===== Control transformation (EEL) - -This feature allows you to write a formula that transforms incoming control values. - -While very powerful because it allows for arbitrary transformations (velocity curves, - random values - you name it), it's not everybody's cup of tea to write something like that. The formula must be written in the language https://www.cockos.com/EEL2/[EEL2]. Some REAPER power users might be familiar with it because REAPER's JSFX uses the same language. - -Luckily, ReaLearn has a fancy editor which visualizes the formula and has some predefined templates built-in (available on Windows and macOS only at the moment). Press the "*...*" button to open the editor. Code changes are applied immediately. - -The most simple - formula is `y = x`, which means there will be no transformation at all. `y = x / 2` means that - incoming control values will be halved. You get the idea: `y` represents the desired target - control value (= output value) and `x` the incoming source control value (= input value). Both are - 64-bit floating point numbers between 0.0 (0%) and 1.0 (100%). - -The script can be much more - complicated than the mentioned examples and make use of all built-in EEL2 language features. The - important thing is to assign the desired value to `y` at some point. - -The following variables/functions are available in the formula: - -[cols="m,1"] -|=== -| Variable | Description - -| -y -| -`y` initially contains the _current_ target value. You can use that value in order to calculate the new value. With this, you can essentially craft your own relative mode! - -| -y_last -| -This contains the last value of the target before it was affected by this particular mapping. - -Allows you to come up with a performance control mode typical for synth parameter mappings, just like the built-in <> mode but more customizable. Try this -for example: `y = y_last + x * (1 - y_last)` - -| -rel_time -a| -This contains the number of milliseconds since this mapping has last been triggered with a control message coming from the source. - -As soon as you use this and a control message comes in, ReaLearn will start invoking your formula _repeatedly_! That means, this variable is your entrance ticket to smooth transitions and continuous parameter modulation. - -A few examples: - -* Smooth transition from current value to control value: `rel_time; y = abs(x - y) < 0.05 ? stop : y + 0.1 * (x - y)` -* Sinus LFO: `y = (sin(rel_time / 500) + 1) / 2` -* Linear transition to control value (1 second): `y = abs(x - y) < 0.05 ? stop : x * min(rel_time / 500, 1)` -* 2 seconds chaos: `y = rel_time < 2000 ? rand(1) : stop` -* Setting a value with delay: `y = rel_time < 2000 ? none : stop(0.5)` - -| -stop and stop(...) -| - -In combination with `rel_time`, this stops repeated invocation of the formula until the mapping is triggered again. - -Good for building transitions with a defined end. - -Stopping the invocation at some point is also important if the same parameter should be controlled by other mappings as well. Otherwise, if multiple mappings continuously change the target parameter, only the last one wins. - -This also exists as a function, which lets you do both, returning a target value *and* stopping the transition. Pass the desired value in the parentheses, e.g. `stop(0.5)`. - -| -none -| -Usually, each repeated (see `rel_time`) invocation always results in a target invocation (unless the target is not retriggerable and already has the desired value). Sometimes this is not desired. In this case, one can return `none`, in which case the target will not be touched. - -Good for transitions that are not continuous, especially if other mappings want to control the parameter as well from time to time. -|=== - - - -ReaLearn's control processing order is like this: - -. Apply source interval -. Apply transformation -. Apply reverse -. Apply target interval -. Apply rounding - - -===== Step size Min/Max - -When you deal with relative adjustments of target values in terms of - increments/decrements, then you have great flexibility because you can influence the _amount_ of - those increments/decrements. This is done via the _Step size_ setting, which is available for all - _continuous_ targets. - -* _Step size Min_ specifies how much to increase/decrease the target value when an -increment/decrement is received. -* _Step size Max_ is used to limit the effect of acceleration (for rotary encoders -which support acceleration and virtual control elements that are mapped as "Incremental button" and have -a "Speed" > 1x) and changes in velocity (for velocity-sensitive buttons/keys that are used -as "Incremental button"). If -you set this to the same value as _Step size Min_, encoder acceleration or changes in velocity -will have absolutely no effect on the incrementation/decrementation amount. If you set it to -100%, the effect is maximized. - -===== Speed Min/Max - -When you choose a discrete target, the _Step size_ label will change into - _Speed_. _Discrete_ means there's a concrete number of possible values - it's the opposite of - _continuous_. If a target is discrete, it cannot have arbitrarily small step sizes. It rather has - one predefined atomic step size which never should be deceeded. Allowing arbitrary step size - adjustment wouldn't make sense. That's why _Speed_ allows you to _multiply_ (positive - numbers) or _"divide"_ (negative numbers) value increments with a factor instead. Negative numbers are - most useful for rotary encoders because they will essentially lower their sensitivity. Virtual targets - are always discrete. - -Example: - -* Let's assume you selected the discrete target <>, which is - considered discrete because an FX with for example 5 presets has 6 well-defined possible values (including the - <no preset> option), there's nothing inbetween. And let's also assume that you have a controller like Midi - Fighter Twister whose rotary encoders don't support built-in acceleration. Now you slightly move an encoder - clock-wise and your controller sends an increment +1. If the _Speed Min_ slider was at 1 (default), this will just - navigate to the next preset (+1). If the _Speed Min_ slider was at 2, this will jump to the 2nd-next preset (+2). - And so on. -* There are FX plug-ins out there which report their parameter as discrete with an insanely small step size (e.g. - some Native Instrument plug-ins). This kind of defeats the purpose of discrete parameters and one can argue that - those parameters should actually be continuous. In such a case, moving your rotary encoder might need _a lot_ of - turning even if you set _Speed_ to the apparent maximum of 100! In this case you will be happy to know that the - text field next to the slider allows you to enter values higher than 100. -* You can set the "Speed" slider to a negative value, e.g. -2. This is the opposite. It means you need to make your - encoder send 2 increments in order to move to the next preset. Or -5: You need to make your encoder send 5 - increments to move to the next preset. This is like slowing down the encoder movement. - -===== Encoder filter (dropdown) - -Allows you to react to clockwise or counter-clockwise encoder movements only, e.g. if - you want to invoke one action on clockwise movement and another one on counter-clockwise movement. Or if you want - to use different step sizes for different movements. - -* *Increment & decrement:* ReaLearn will process both increments and decrements. -* *Increment only:* ReaLearn will ignore decrements. -* *Decrement only:* ReaLearn will ignore increments. - - -===== Wrap - -If unchecked, the target value will not change anymore if there's an incoming - decrement but the target already reached its minimum value. If checked, the target value will jump - to its maximum value instead. It works analogously if there's an incoming increment and the target - already reached its maximum value. - -If this flag is enabled for controller mappings which have a virtual target, every main mapping controlled by - that virtual control element will _rotate_ - even if the main mapping itself doesn't have _rotate_ enabled. - - -===== Make absolute - -Check this box if you want to emulate an absolute control element with a relative encoder - or with -/+ (incremental) buttons. - -This is useful if you have configured your controller to be relative all the way (which is good!) but you want - to use a control transformation EEL formula - which is not possible if you change the target with relative - increments. It works by keeping an internal absolute value, incrementing or decrementing it accordingly and - then processing it just like normal absolute control values. - -By checking this box: - -* You lose the possibility to be perfectly free of parameter jumps (but you can try to mitigate that loss by - using the jump settings). -* You gain support for control-direction EEL transformation, non-continuous target value sequences and source range. -* You can still use some of the relative-only features: Step size and rotate! - -[#fire-mode] -===== Fire mode - -Normally, when a button gets pressed, it controls the target immediately. However, by using this dropdown -and by changing the values below it, you can change this behavior. This dropdown provides different fire modes that -decide how exactly ReaLearn should cope with button presses. - -====== Fire on press (or release if > 0 ms) - -This mode is essential in order to be able to distinguish between - different press durations. - -* *Min* and *Max* decide how long a button needs to be pressed to have an effect. -* By default, both min and max will be at 0 ms, which means that the duration doesn't matter and - both press (> 0%) and release (0%) will be instantly forwarded. If you change _Min_ to - e.g. 1000 ms and _Max_ to 5000 ms, it will behave as follows: -* If you press the control element and instantly release it, nothing will happen. -* If you press the control element, wait for a maximum of 5 seconds and then release it, the - control value of the press (> 0%) will be forwarded. -* It will never forward the control value of a release (0%), so this is probably only useful for - targets with trigger character. -* The main use case of this setting is to assign multiple functions to one control element, - depending on how long it has been pressed. For this, use settings like the following: -* Short press: 0 ms - 250 ms -* Long press: 250 ms - 5000 ms - -====== Fire after timeout - -This mode is more "satisfying" because it will let ReaLearn "fire" immediately once a certain - time has passed since the press of the button. However, obviously it doesn't have the concept of a "Maximum" - press duration, so it can't be used to execute different things depending on different press durations (or only as - the last part in the press duration chain, so to say). - -* *Timeout:* Sets the timeout in milliseconds. If this is zero, everything will behave as usual. - -[#fire-after-timeout-keep-firing] -====== Fire after timeout, keep firing (turbo) - -Welcome to turbo mode. It will keep hitting your target (always with - with the initial button press velocity) at a specific rate. Optionally with an initial delay. Epic! - -* *Timeout:* This is the initial delay before anything happens. Can be zero, then turbo stage is entered - instantly on press. -* *Rate:* This is how frequently the target will be hit once the timeout has passed. In practice it won't - happen more frequently than about 30 ms (REAPER's main thread loop frequency). - -====== Fire on double press - -This reacts to double presses of a button (analog to double clicks with the mouse). - -====== Fire after single press (if hold < Max ms) - -If you want to do something in response to a double press, chances are that - you want to do something _else_ in response to just a single press. The _Normal_ fire mode will fire no matter - what! That's why there's an additional _Single press_ mode that will not respond to double clicks. Needless to - say, the response happens _slightly_ delayed - because ReaLearn needs to wait a bit to see if it's going to be a - double press or not. - -* *Max:* It's even possible to distinguish between single, double _and_ long press. In order to do that, you - must set the _Max_ value of the _Single press_ mapping to a value that is lower than the _Timeout_ value of - your _After timeout_ mapping. That way you can use one button for 3 different actions! Example: -** Mapping 1 "Single press" with Max = 499ms -** Mapping 2 "Double press" -** Mapping 3 "After timeout" with Timeout = 500ms - - -===== Button filter (right dropdown) - -This allows you to easily ignore button presses or releases. - -* *Press & release:* ReaLearn will process both button presses (control value = 0%) and button releases (control - value > 0%). This is the default. -* [[press-only,press-only]] *Press only:* Makes ReaLearn ignore the release of the button. The same thing can be achieved by setting - _Source Min_ to 1. However, doing so would also affect the feedback direction, which is often undesirable - because it will mess with the button LED color or on/off state. -* *Release only:* Makes ReaLearn ignore the press of the button (just processing its release). Rare, but possible. - -==== Bottom section - -This section has two functions: - -- Providing context-sensitive help for the glue section -- Providing target control information and error reporting - -===== Target control information - -This shows information about how an incoming control value was handled and possible target control errors. - -NOTE: If the target supports MIDI real-time control and the source is a MIDI source, this currently only works if "Log target control" is enabled (see <>). - -===== Help - -Context-sensitive help for the glue section. Whenever you touch a setting in -the glue section, some text will appear which explains what this element does, both for the _control_ and for the -_feedback_ direction (if applicable). - -* *If source is a:* It often depends on the kind of source what effect a setting has. Therefore, this dropdown - always contains a list of sources. It only displays relevant kinds of sources. If a source kind is impossible - according to the current source settings or if it's not supported by the setting, it won't appear in the list. - -=== Provided REAPER actions - -ReaLearn provides some REAPER actions which become available as soon as at least one instance of ReaLearn -is loaded. It can be useful to put a ReaLearn instance on REAPER's monitoring FX chain in order to have access -to those actions at all times. - -In order to find these actions, open REAPER's _Actions_ menu, choose _Show action list…_ and simply search for -`realearn`. The most important actions: - -* *ReaLearn: Find first mapping by source:* This action will ask you to touch some control element. As soon as you - touch a control element which is mapped, it will open the mapping panel for the corresponding mapping. It will search - within all ReaLearn instances loaded in your current project as well as the ones on the monitoring FX chain. -* *ReaLearn: Find first mapping by target:* This action is similar to _Find first mapping by source_. It asks you - to touch some (learnable) REAPER parameter. As soon as you touch one that is mapped, it will open its mapping panel. -* *ReaLearn: Learn single mapping (reassigning source):* Asks you to touch a control element and target and adds a new - mapping in the first ReaLearn instance that it encounters. It prefers instances in the current project over - monitoring FX. It automatically chooses the instance with the correct MIDI/OSC input. If there's an instance which - already has that source assigned, it will be reassigned to the new target that you touched. -* *ReaLearn: Learn single mapping (reassigning source) and open it:* Like _Learn single mapping_ but additionally - opens the mapping panel after having learned the mapping. This is great for subsequent fine tuning. -* *ReaLearn: Learn source for last touched target (reassigning target):* This behaves similar to REAPER's built-in - MIDI learn in that it always relates to the target that has been touched last. -* *ReaLearn: Send feedback for all instances:* Makes each ReaLearn instance in all project tabs send feedback for all - mappings. That shouldn't be necessary most of the time because ReaLearn usually sends feedback automatically, but - there are situations when it might come in handy. - -[#advanced-settings] -=== Advanced settings - -This section describes the _Advanced settings_ feature of the mapping panel (see section <>) in more -detail. - -==== The YAML language - -This feature allows you enter text that -conforms to the so-called https://en.wikipedia.org/wiki/YAML[YAML] format. This is not a programming language, so you -can't write loops, conditions or anything like that. Instead, think of it as a language for writing configuration. Do -you know INI files? REAPER uses INI files to save configuration. YAML is a bit like that, just much more expressive -because it allows you to not only express flat key-value pairs (e.g. `edit_fontsize=29`) but also deeply nested -configuration data and lists. - -*Important thing 1:* YAML is indentation-sensitive, so indentation matters! The bright side of this is that it always -looks clean and nice. The dark side is that ReaLearn will refuse to save your settings if you messed up the indentation. -Therefore: Be consistent with your indentation (e.g. use always an indentation of 2 spaces for nesting) and have an utmost -attention to detail when doing copy and paste from the examples in this section! - -*Important thing 2:* When you close the text editor and ReaLearn saves your advanced settings as part of the mapping, -it will not save the text that you have entered _verbatim_. It will save a structural representation of what you -entered (and it will strip comments!). That means if you open the advanced settings again, your could text could look a -bit different, in particular it can have a different formatting. But don't worry, it _means_ exactly the same to -ReaLearn. - -[discrete] -===== Why the hell did you come up with something like that? - -Deciding for textual configuration and YAML in particular was a conscious decision with the goal to provide a -developer-friendly framework for rapidly extending ReaLearn with advanced features that don't urgently need a graphical -user interface. - -* *Why ask the user to enter text instead of providing a convenient graphical user interface?* -** That's mostly a tradeoff due to the fact that my time available for developing ReaLearn is limited. -** It's much work to develop a graphical user interface for every feature. In fact, programming the user interface - often takes most of the time whereas implementing the actual logic is not that much effort. -** It's true that some sorts of functionality really benefit from having a fancy graphical user interface. But - there's also functionality for which having it is not too important, e.g. functionality that is of configurational - nature and not used that often. -** Also, one of ReaLearn's goals is to give power users and programmers extra powers. Textual configuration can be - more powerful in many situations once the user knows how to go about it. -* *Why YAML?* -** YAML has the advantage of being popular among programmers, widely supported, highly structured and relatively - well readable/writable by both humans and machines. -** Many text editors offer built-in support for editing YAML. -** Makes it easy to provide data for even very complex features. -* *Why not a scripting language?* -** Using a scripting language would spoil any future possibility to add a graphical user interface on top of some of - the functionality. -** It wouldn't allow ReaLearn to apply future optimizations and improvements. ReaLearn is rather declarative - in nature and a scripting language would destroy this quality. -** It's hard to come up with a stable and good API. -** It's harder to use than a configuration language. -* *Why don't you save the text, just the structure?* -** Mostly because saving just the structure makes the entered data become a natural part of ReaLearn's main preset - format (JSON). -** However, this is something that might change in future, depending on how it proves itself in practice. -** Once we would start saving the actual text, it would be hard to go back. - -==== Supported configuration properties - -In this section you will find examples that cover all currently supported configuration properties. You can copy and -paste the stuff you need to the text editor, remove the parts that you don't need and adjust the rest. Comments (lines -starting with `#`) will be removed automatically. - -[#mapping-lifecycle-actions] -===== Mapping lifecycle actions - -ReaLearn allows you to define MIDI messages to be sent to the output whenever a mapping turns active or -inactive. - -Example use cases: - -* Accessing very device-specific features via system-exclusive MIDI messages. -* Choosing a different LED color/style depending on the active mapping. -* Initializing a sys-ex-controllable display with some mapping-specific text (more difficult). - -A mapping can change its active/inactive state based on the following factors: - -* *Preset loading/unloading:* A mapping can turn active when a ReaLearn instance or preset is loaded and turn - inactive when it's changed or unloaded. -* *<>:* A mapping can turn inactive when an activation condition - is not satisfied anymore and can change back to active as soon as it's satisfied again. -* *Target condition:* Targets can also have conditions (e.g. "Track must be selected"). They affect activation - state changes in the same way. -* *Target validity:* A mapping can turn inactive when the target is not valid anymore, e.g. when it's a target that's - based on the currently selected track but no track is currently selected. Analogously, it can turn active again once - a valid target can be resolved. -* *Feedback enabled checkbox (or mapping-enabled checkbox):* A mapping can turn inactive as soon as this checkbox is unticked and turn active - again when ticking it. This is also the best way to test your configuration. - -(Controller) mappings with virtual targets are always considered active as long as the feedback checkbox is ticked. -That's why they are perfectly suited for holding a bunch of controller initialization messages! This feature is -for example used in the "PreSonus FaderPort Classic" controller preset, which needs to be put in a specific mode -before being usable. ReaLearn does this automatically simply by sending some mapping on-activate MIDI messages. - -These are the available configuration properties: - -[source,yaml] ----- -# Contains stuff to be done whenever this mapping becomes active. -on_activate: - # A list of MIDI messages to be sent to the output when this mapping becomes active. - # - # At the moment, only messages of type "raw" are supported. Although this covers all possible types - # of MIDI messages, it's a bit hard to express e.g. simple NOTE ON or CC messages with this notation. - # In particular, you would need to know how MIDI messages are presented as byte sequences. Future ReaLearn - # versions will provide more convenient ways to describe simple MIDI messages. - send_midi_feedback: - # This is an example of a system-exclusive message ("SysEx"). It's usually expressed in hexadecimal string - # notation. Make sure to include the leading F0 and trailing F7, which is the begin and end marker of all - # system-exclusive messages! - - raw: F0 00 20 6B 7F 42 02 00 10 77 01 F7 - # Instead of above hexadecimal string notation, you could also use an array of decimal numbers to describe a raw - # message. The following is a NOTE ON of note 74 on channel 1 with velocity 100. - - raw: - # NOTE ON on channel 1 - - 144 - # Note number 74 - - 74 - # Note velocity 100 - - 100 - -# Contains stuff to be done whenever this mapping becomes inactive. -on_deactivate: - # A list of MIDI messages to be sent to the output when this mapping becomes inactive. - send_midi_feedback: - # Supports exactly the same kinds of messages as described above in "on_activate". - - raw: F0 00 20 6B 7F 42 02 00 10 77 14 F7 ----- - -Please remember that YAML comments (e.g. `# The following line does this and that`) _will not be saved_! In case you -want to explain something, you need to write it as YAML property, such as in the following example: - -[source,yaml] ----- -comment: "The following configuration makes the rightmost pad of the MiniLab mkII light up in red color." -on_activate: - send_midi_feedback: - - raw: F0 00 20 6B 7F 42 02 00 10 77 01 F7 ----- - -ReaLearn will ignore any unknown properties. - -TIP: If you use input `` and find that MIDI lifecycle messages aren't sent, no matter what, make sure "Send feedback only if track armed" is disabled (see <>)! - -WARNING: Disabling the complete ReaLearn instance will cause all mappings to deactivate. However, sending MIDI messages on deactivation in this case will only work if the output is a device! If it is ``, it will not send anything because REAPER will not give that ReaLearn instance any chance to output MIDI messages once it's disabled. Instead, the MIDI message will queue up and be sent once you enable that instance again ... which is probably not what you want. - -=== Configuration files - -ReaLearn creates and/or reads a few files in REAPER's resource directory. - - -[cols="m,1"] -|=== -| File | Description - -| Data/helgoboss | Directory which contains data such as presets or resources that need to be distributed via ReaPack - -| Data/helgoboss/auto-load-configs/fx.json | Contains global FX-to-preset links, see <> - -| Data/helgoboss/archives | Directory which contains archives e.g. the compressed app, distributed via ReaPack - -| Data/helgoboss/doc | Contains offline documentation, e.g. this guide as PDF - -| Data/helgoboss/presets/controller | Contains preset for the controller compartment - -| Data/helgoboss/presets/main | Contains preset for the main compartment - -| Helgoboss | Directory which contains rather personal or device-specific data, not touched via ReaPack - -| licensing.json | Contains license keys - -| Helgoboss/App | Contains the uncompressed App, if installed - -| Helgoboss/Pot/previews | Directory which contains previews recorded by <> - -| Helgoboss/ReaLearn/osc.json | Global OSC device configurations, see <> - -| Helgoboss/ReaLearn/realearn.ini | Very basic global configuration, currently mainly regarding ReaLearn's built-in server. - -Currently supported properties (subject to change): `server_enabled`, `server_http_port`, `server_https_port`, `server_grpc_port`, `companion_web_app_url` - -| Helgoboss/Server/certificates | Contains a list of certificates and corresponding private keys in order to allow encrypted communication with ReaLearn Companion and App. -|=== \ No newline at end of file +The ReaLearn user guide is now available link:realearn-user-guide.adoc[here]. \ No newline at end of file diff --git a/doc/user-guide.md b/doc/user-guide.md index f24a43ed2..3587f4d75 100644 --- a/doc/user-guide.md +++ b/doc/user-guide.md @@ -1 +1 @@ -The ReaLearn user guide is now available [here](user-guide.adoc). \ No newline at end of file +The ReaLearn user guide is now available [here](realearn-user-guide.adoc). \ No newline at end of file diff --git a/extension/src/lib.rs b/extension/src/lib.rs index e8b92e3fe..c0d4790bb 100644 --- a/extension/src/lib.rs +++ b/extension/src/lib.rs @@ -67,7 +67,7 @@ fn eagerly_load_plugin_lib(context: &PluginContext) -> Result { } else { ".so" }; - let matches = file_name.starts_with("realearn") && file_name.ends_with(extension); + let matches = file_name.starts_with("helgobox") && file_name.ends_with(extension); if !matches { return None; } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index ad6061c2a..c9a53b1d7 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "realearn-macros" +name = "helgobox-macros" version = "0.1.0" edition = "2021" publish = false \ No newline at end of file diff --git a/main/Cargo.toml b/main/Cargo.toml index 4c301c4f7..042ca770b 100644 --- a/main/Cargo.toml +++ b/main/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "realearn" +name = "helgobox" version = "2.16.0-pre.18" authors = ["Benjamin Klum "] edition = "2021" @@ -36,7 +36,7 @@ reaper-macros.workspace = true swell-ui.workspace = true rx-util.workspace = true pot.workspace = true -realearn-api.workspace = true +helgobox-api.workspace = true # In future (when helgoboss-learn has matured), this will become a crates.io dependency helgoboss-learn.workspace = true helgoboss-midi.workspace = true @@ -151,7 +151,7 @@ ascii.workspace = true # For using bit flags in the reaper-rs API. enumflags2.workspace = true # For detecting undesired (de)allocation in real-time threads. -helgoboss-allocator.workspace = true +helgobox-allocator.workspace = true # For being able to cleanly implement targets in separate files without needing to resort to dynamic dispatch. enum_dispatch.workspace = true # For processing mappings in user-defined order @@ -169,7 +169,7 @@ metrics-exporter-prometheus = { version = "0.13.0", default-features = false } # For parsing OSC argument value ranges nom.workspace = true # For obtaining dialog constants from dialog crate -realearn-dialogs = { path = "../dialogs" } +helgobox-dialogs = { path = "../dialogs" } # For being able to return iterators of different types either.workspace = true # For reading preset directories recursively @@ -229,12 +229,12 @@ cc = { git = "https://github.com/petrochenkov/cc-rs.git", rev = "4d52bd211aeb2b4 # For embedding dialog resource files on Windows embed-resource = "2.4.1" # For RC dialog file generation -realearn-dialogs = { path = "../dialogs" } +helgobox-dialogs = { path = "../dialogs" } [dev-dependencies] # For the EEL memory consumption test sysinfo = "0.30.5" [lib] -name = "realearn" +name = "helgobox" crate-type = ["cdylib"] diff --git a/main/build.rs b/main/build.rs index e26d7e966..781e73309 100644 --- a/main/build.rs +++ b/main/build.rs @@ -34,7 +34,7 @@ fn generate_gui_dialogs( ) -> Result<(), Box> { let bindings_file = "src/infrastructure/ui/bindings.rs"; fs::create_dir_all(generated_dir.as_ref())?; - realearn_dialogs::generate_dialog_files(generated_dir.as_ref(), bindings_file); + helgobox_dialogs::generate_dialog_files(generated_dir.as_ref(), bindings_file); // On macOS and Linux, try to generate SWELL dialogs (needs PHP) #[cfg(target_family = "unix")] if let Err(e) = generate_dialogs(dialog_rc_file.as_ref()) { diff --git a/main/src/application/mapping_model.rs b/main/src/application/mapping_model.rs index 743adaa06..8f6835b9b 100644 --- a/main/src/application/mapping_model.rs +++ b/main/src/application/mapping_model.rs @@ -17,7 +17,7 @@ use helgoboss_learn::{ ModeApplicabilityCheckInput, ModeParameter, SourceCharacter, Target, UnitValue, }; -use realearn_api::persistence::TrackScope; +use helgobox_api::persistence::TrackScope; use std::cell::RefCell; use std::error::Error; use std::rc::Rc; diff --git a/main/src/application/mode_model.rs b/main/src/application/mode_model.rs index 6d6127dfe..74077074a 100644 --- a/main/src/application/mode_model.rs +++ b/main/src/application/mode_model.rs @@ -11,7 +11,7 @@ use helgoboss_learn::{ use crate::application::{Affected, Change, GetProcessingRelevance, ProcessingRelevance}; use crate::base::CloneAsDefault; use base::hash_util::clone_to_other_hash_map; -use realearn_api::persistence::FeedbackValueTable; +use helgobox_api::persistence::FeedbackValueTable; use std::time::Duration; pub enum ModeCommand { diff --git a/main/src/application/source_model.rs b/main/src/application/source_model.rs index 8ffd075a7..889233d5a 100644 --- a/main/src/application/source_model.rs +++ b/main/src/application/source_model.rs @@ -16,8 +16,8 @@ use helgoboss_learn::{ DEFAULT_OSC_ARG_VALUE_RANGE, }; use helgoboss_midi::{Channel, U14, U7}; +use helgobox_api::persistence::{MidiScriptKind, VirtualControlElementCharacter}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use realearn_api::persistence::{MidiScriptKind, VirtualControlElementCharacter}; use serde::{Deserialize, Serialize}; use serde_repr::*; use std::borrow::Cow; diff --git a/main/src/application/target_model.rs b/main/src/application/target_model.rs index 9786ed6d7..d1b20cc24 100644 --- a/main/src/application/target_model.rs +++ b/main/src/application/target_model.rs @@ -58,8 +58,7 @@ use std::error::Error; use crate::domain::ui_util::format_tags_as_csv; use base::hash_util::NonCryptoHashSet; -use playtime_api::persistence::ColumnAddress; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ Axis, BrowseTracksMode, ClipColumnTrackContext, FxChainDescriptor, FxDescriptorCommons, FxToolAction, LearnTargetMappingModification, LearnableTargetKind, MappingModification, MappingSnapshotDescForLoad, MappingSnapshotDescForTake, MonitoringMode, MouseAction, @@ -69,6 +68,7 @@ use realearn_api::persistence::{ SetTargetToLastTouchedMappingModification, TargetTouchCause, TrackDescriptorCommons, TrackFxChain, TrackScope, TrackToolAction, VirtualControlElementCharacter, }; +use playtime_api::persistence::ColumnAddress; use reaper_medium::{ AutomationMode, BookmarkId, GlobalAutomationModeOverride, InputMonitoringMode, TrackArea, TrackLocation, TrackSendDirection, @@ -2016,8 +2016,8 @@ impl TargetModel { Ok(desc) } - pub fn api_track_descriptor(&self) -> realearn_api::persistence::TrackDescriptor { - use realearn_api::persistence::TrackDescriptor; + pub fn api_track_descriptor(&self) -> helgobox_api::persistence::TrackDescriptor { + use helgobox_api::persistence::TrackDescriptor; use VirtualTrackType::*; let commons = TrackDescriptorCommons { track_must_be_selected: Some(self.enable_only_if_track_selected), @@ -2067,8 +2067,8 @@ impl TargetModel { } } - pub fn api_fx_descriptor(&self) -> realearn_api::persistence::FxDescriptor { - use realearn_api::persistence::FxDescriptor; + pub fn api_fx_descriptor(&self) -> helgobox_api::persistence::FxDescriptor { + use helgobox_api::persistence::FxDescriptor; use VirtualFxType::*; let commons = FxDescriptorCommons { fx_must_have_focus: Some(self.enable_only_if_fx_has_focus), @@ -2653,8 +2653,8 @@ impl TargetModel { } pub fn simple_target(&self) -> Option { + use helgobox_api::persistence; use playtime_api::runtime::SimpleMappingTarget; - use realearn_api::persistence; use ReaperTargetType as T; if self.category != TargetCategory::Reaper { return None; diff --git a/main/src/application/unit_model.rs b/main/src/application/unit_model.rs index 8ca95ca03..38def3b9d 100644 --- a/main/src/application/unit_model.rs +++ b/main/src/application/unit_model.rs @@ -36,12 +36,12 @@ use crate::domain; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; use core::iter; use helgoboss_learn::{AbsoluteMode, ControlResult, ControlValue, UnitValue}; -use itertools::Itertools; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ FxDescriptor, MappingModification, TargetTouchCause, TrackDescriptor, VirtualControlElementCharacter, }; -use realearn_api::runtime::InstanceInfoEvent; +use helgobox_api::runtime::InstanceInfoEvent; +use itertools::Itertools; use reaper_medium::{InputMonitoringMode, RecordingInput}; use std::error::Error; use std::fmt; @@ -202,7 +202,7 @@ impl LearnManyState { pub mod session_defaults { use crate::application::AutoLoadMode; use crate::domain::StayActiveWhenProjectInBackground; - use realearn_api::persistence::FxDescriptor; + use helgobox_api::persistence::FxDescriptor; pub const LET_MATCHED_EVENTS_THROUGH: bool = false; pub const LET_UNMATCHED_EVENTS_THROUGH: bool = true; diff --git a/main/src/base/allocator.rs b/main/src/base/allocator.rs index a33a14633..80a10321c 100644 --- a/main/src/base/allocator.rs +++ b/main/src/base/allocator.rs @@ -1,5 +1,5 @@ use base::metrics_util::record_occurrence; -use helgoboss_allocator::{AsyncDeallocationIntegration, Deallocate, HelgobossAllocator}; +use helgobox_allocator::{AsyncDeallocationIntegration, Deallocate, HelgobossAllocator}; use std::alloc::{GlobalAlloc, Layout, System}; #[global_allocator] diff --git a/main/src/domain/audio_hook.rs b/main/src/domain/audio_hook.rs index 597287e87..e89c27c90 100644 --- a/main/src/domain/audio_hook.rs +++ b/main/src/domain/audio_hook.rs @@ -7,9 +7,9 @@ use crate::domain::{ use base::byte_pattern::{BytePattern, PatternByte}; use base::metrics_util::{measure_time, record_duration}; use base::non_blocking_lock; -use helgoboss_allocator::*; use helgoboss_learn::{AbstractTimestamp, MidiSourceValue, RawMidiEvent, RawMidiEvents}; use helgoboss_midi::{DataEntryByteOrder, RawShortMessage, ShortMessage, ShortMessageType}; +use helgobox_allocator::*; use reaper_high::{MidiInputDevice, MidiOutputDevice, Reaper}; use reaper_medium::{ MidiInputDeviceId, MidiOutputDeviceId, OnAudioBuffer, OnAudioBufferArgs, SendMidiTime, diff --git a/main/src/domain/backbone.rs b/main/src/domain/backbone.rs index db94df30d..b335d23a2 100644 --- a/main/src/domain/backbone.rs +++ b/main/src/domain/backbone.rs @@ -13,8 +13,8 @@ use pot::{PotFavorites, PotFilterExcludes}; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; use fragile::Fragile; +use helgobox_api::persistence::TargetTouchCause; use once_cell::sync::Lazy; -use realearn_api::persistence::TargetTouchCause; use reaper_high::Fx; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::hash::Hash; diff --git a/main/src/domain/control_surface.rs b/main/src/domain/control_surface.rs index cd3fc99c1..afd81fb9d 100644 --- a/main/src/domain/control_surface.rs +++ b/main/src/domain/control_surface.rs @@ -260,7 +260,7 @@ impl RealearnControlSurfaceMiddleware { // should use the global control surface event handler for this! Or ShutdownDetectionPanel? After all, // this is not needed in the domain! let current_undesired_allocation_count = - helgoboss_allocator::undesired_allocation_count(); + helgobox_allocator::undesired_allocation_count(); if current_undesired_allocation_count != self.last_undesired_allocation_count { self.last_undesired_allocation_count = current_undesired_allocation_count; let event = &crate::domain::InternalInfoEvent::UndesiredAllocationCountChanged; diff --git a/main/src/domain/eventing.rs b/main/src/domain/eventing.rs index 379a3114d..c4da0a4a8 100644 --- a/main/src/domain/eventing.rs +++ b/main/src/domain/eventing.rs @@ -5,7 +5,7 @@ use crate::domain::{ }; use base::hash_util::NonCryptoHashSet; use helgoboss_learn::{AbsoluteValue, ControlValue}; -use realearn_api::persistence::MappingModification; +use helgobox_api::persistence::MappingModification; use std::error::Error; use std::fmt::Debug; diff --git a/main/src/domain/instance.rs b/main/src/domain/instance.rs index 3b2790fde..25309b7c7 100644 --- a/main/src/domain/instance.rs +++ b/main/src/domain/instance.rs @@ -3,11 +3,11 @@ use crate::domain::{AnyThreadBackboneState, Backbone, ProcessorContext, RealTime use anyhow::Context; use base::hash_util::NonCryptoHashMap; use base::{NamedChannelSender, SenderToNormalThread, SenderToRealTimeThread}; +use helgobox_api::persistence::PotFilterKind; use pot::{ CurrentPreset, OptFilter, PotFavorites, PotFilterExcludes, PotIntegration, PotUnit, PresetId, SharedRuntimePotUnit, }; -use realearn_api::persistence::PotFilterKind; use reaper_high::{ChangeEvent, Fx}; use std::cell::{Ref, RefCell, RefMut}; use std::fmt; diff --git a/main/src/domain/playtime_util.rs b/main/src/domain/playtime_util.rs index bceacdfa7..4daa86f3c 100644 --- a/main/src/domain/playtime_util.rs +++ b/main/src/domain/playtime_util.rs @@ -3,7 +3,7 @@ use crate::domain::{ TrackResolveError, VirtualPlaytimeColumn, }; use helgoboss_learn::UnitValue; -use realearn_api::persistence::ClipColumnTrackContext; +use helgobox_api::persistence::ClipColumnTrackContext; use reaper_high::Track; /// In clip slot targets, the resolve phase makes sure that the targeted slot actually exists. @@ -46,11 +46,11 @@ pub fn resolve_virtual_track_by_playtime_column( // Panics if called with repeat or record. #[cfg(feature = "playtime")] pub(crate) fn clip_play_state_unit_value( - action: realearn_api::persistence::PlaytimeSlotTransportAction, + action: helgobox_api::persistence::PlaytimeSlotTransportAction, play_state: playtime_clip_engine::rt::ClipPlayState, ) -> UnitValue { + use helgobox_api::persistence::PlaytimeSlotTransportAction::*; use playtime_clip_engine::rt::ClipPlayState; - use realearn_api::persistence::PlaytimeSlotTransportAction::*; match action { Trigger | PlayStop | PlayPause | RecordPlayStop => play_state.feedback_value(), Stop => transport_is_enabled_unit_value(matches!( diff --git a/main/src/domain/props.rs b/main/src/domain/props.rs index 3a8d36014..1a57223a0 100644 --- a/main/src/domain/props.rs +++ b/main/src/domain/props.rs @@ -5,7 +5,7 @@ use crate::domain::{ }; use enum_dispatch::enum_dispatch; use helgoboss_learn::{AbsoluteValue, NumericValue, PropProvider, PropValue, Target}; -use realearn_api::persistence::TrackScope; +use helgobox_api::persistence::TrackScope; use reaper_high::ChangeEvent; use std::str::FromStr; diff --git a/main/src/domain/real_time_processor.rs b/main/src/domain/real_time_processor.rs index 066d6c63f..203c2f165 100644 --- a/main/src/domain/real_time_processor.rs +++ b/main/src/domain/real_time_processor.rs @@ -22,7 +22,7 @@ use reaper_medium::{ use base::{NamedChannelSender, SenderToNormalThread, SenderToRealTimeThread}; use enum_map::{enum_map, EnumMap}; -use helgoboss_allocator::permit_alloc; +use helgobox_allocator::permit_alloc; use std::convert::TryInto; use std::ptr::null_mut; use std::time::Duration; diff --git a/main/src/domain/realearn_target.rs b/main/src/domain/realearn_target.rs index a7a2b0d26..e1ec8eb7b 100644 --- a/main/src/domain/realearn_target.rs +++ b/main/src/domain/realearn_target.rs @@ -34,8 +34,8 @@ use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, NumericValue, PropValue, RawMidiEvent, RgbColor, TransformationInputProvider, UnitValue, }; +use helgobox_api::persistence::{LearnableTargetKind, TrackScope}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use realearn_api::persistence::{LearnableTargetKind, TrackScope}; use reaper_high::{ChangeEvent, Fx, Guid, Project, Reaper, Track, TrackRoute}; use reaper_medium::CommandId; use serde_repr::*; diff --git a/main/src/domain/reaper_target.rs b/main/src/domain/reaper_target.rs index 51c0c4214..70e91642e 100644 --- a/main/src/domain/reaper_target.rs +++ b/main/src/domain/reaper_target.rs @@ -21,7 +21,7 @@ use strum::EnumIter; use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, NumericValue, PropValue, Target, UnitValue, }; -use realearn_api::persistence::{SeekBehavior, TrackScope}; +use helgobox_api::persistence::{SeekBehavior, TrackScope}; use crate::domain::ui_util::convert_bool_to_unit_value; use crate::domain::{ diff --git a/main/src/domain/targets/browse_pot_filter_items_target.rs b/main/src/domain/targets/browse_pot_filter_items_target.rs index 36ad787ea..e28d4f639 100644 --- a/main/src/domain/targets/browse_pot_filter_items_target.rs +++ b/main/src/domain/targets/browse_pot_filter_items_target.rs @@ -9,9 +9,9 @@ use base::blocking_lock_arc; use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, Fraction, NumericValue, PropValue, Target, UnitValue, }; +use helgobox_api::persistence::PotFilterKind; use pot::{Debounce, FilterItemId}; use pot::{FilterItem, RuntimePotUnit}; -use realearn_api::persistence::PotFilterKind; use std::borrow::Cow; #[derive(Debug)] diff --git a/main/src/domain/targets/browse_tracks_target.rs b/main/src/domain/targets/browse_tracks_target.rs index 68aa407bf..689e6be15 100644 --- a/main/src/domain/targets/browse_tracks_target.rs +++ b/main/src/domain/targets/browse_tracks_target.rs @@ -9,7 +9,7 @@ use crate::domain::{ use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, Fraction, NumericValue, Target, UnitValue, }; -use realearn_api::persistence::{BrowseTracksMode, TrackScope}; +use helgobox_api::persistence::{BrowseTracksMode, TrackScope}; use reaper_high::{ChangeEvent, Project, Reaper, Track}; use reaper_medium::{CommandId, MasterTrackBehavior}; use std::borrow::Cow; diff --git a/main/src/domain/targets/fx_tool_target.rs b/main/src/domain/targets/fx_tool_target.rs index a5142264d..5c1e74cfe 100644 --- a/main/src/domain/targets/fx_tool_target.rs +++ b/main/src/domain/targets/fx_tool_target.rs @@ -5,7 +5,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Fraction, NumericValue, Target}; -use realearn_api::persistence::FxToolAction; +use helgobox_api::persistence::FxToolAction; use reaper_high::{Fx, FxChain, Project, Track}; use std::borrow::Cow; diff --git a/main/src/domain/targets/go_to_bookmark_target.rs b/main/src/domain/targets/go_to_bookmark_target.rs index b16b0a767..63571c604 100644 --- a/main/src/domain/targets/go_to_bookmark_target.rs +++ b/main/src/domain/targets/go_to_bookmark_target.rs @@ -9,7 +9,7 @@ use crate::domain::{ use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, NumericValue, PropValue, RgbColor, Target, UnitValue, }; -use realearn_api::persistence::SeekBehavior; +use helgobox_api::persistence::SeekBehavior; use reaper_high::{BookmarkType, ChangeEvent, FindBookmarkResult, Project, Reaper}; use reaper_medium::{AutoSeekBehavior, BookmarkRef, SetEditCurPosOptions}; use std::borrow::Cow; diff --git a/main/src/domain/targets/last_touched_target.rs b/main/src/domain/targets/last_touched_target.rs index 4578f7985..91b3abc0b 100644 --- a/main/src/domain/targets/last_touched_target.rs +++ b/main/src/domain/targets/last_touched_target.rs @@ -4,7 +4,7 @@ use crate::domain::{ ReaperTargetType, TargetSection, TargetTypeDef, UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use base::hash_util::NonCryptoHashSet; -use realearn_api::persistence::TargetTouchCause; +use helgobox_api::persistence::TargetTouchCause; #[derive(Debug)] pub struct UnresolvedLastTouchedTarget { diff --git a/main/src/domain/targets/load_mapping_snapshot_target.rs b/main/src/domain/targets/load_mapping_snapshot_target.rs index f0d877f40..bc094c645 100644 --- a/main/src/domain/targets/load_mapping_snapshot_target.rs +++ b/main/src/domain/targets/load_mapping_snapshot_target.rs @@ -6,7 +6,7 @@ use crate::domain::{ TargetTypeDef, Unit, UnitEvent, UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Target}; -use realearn_api::persistence::MappingSnapshotDescForLoad; +use helgobox_api::persistence::MappingSnapshotDescForLoad; #[derive(Debug)] pub struct UnresolvedLoadMappingSnapshotTarget { diff --git a/main/src/domain/targets/modify_mapping_target.rs b/main/src/domain/targets/modify_mapping_target.rs index 7fbafa994..490da1ec1 100644 --- a/main/src/domain/targets/modify_mapping_target.rs +++ b/main/src/domain/targets/modify_mapping_target.rs @@ -8,7 +8,7 @@ use crate::domain::{ UnitId, UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Target}; -use realearn_api::persistence::MappingModification; +use helgobox_api::persistence::MappingModification; use std::borrow::Cow; use std::rc::Rc; diff --git a/main/src/domain/targets/mouse_target.rs b/main/src/domain/targets/mouse_target.rs index 94982bb86..c68b9b19e 100644 --- a/main/src/domain/targets/mouse_target.rs +++ b/main/src/domain/targets/mouse_target.rs @@ -7,7 +7,7 @@ use crate::domain::{ use base::enigo::EnigoMouse; use base::{Mouse, MouseCursorPosition}; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Fraction, Target}; -use realearn_api::persistence::{Axis, MouseButton}; +use helgobox_api::persistence::{Axis, MouseButton}; use std::fmt::Debug; use strum::EnumIter; diff --git a/main/src/domain/targets/playtime_browse_cells_target.rs b/main/src/domain/targets/playtime_browse_cells_target.rs index 2ac1eea66..2e0f28d27 100644 --- a/main/src/domain/targets/playtime_browse_cells_target.rs +++ b/main/src/domain/targets/playtime_browse_cells_target.rs @@ -3,7 +3,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, DEFAULT_TARGET, }; -use realearn_api::persistence::Axis; +use helgobox_api::persistence::Axis; #[derive(Debug)] pub struct UnresolvedPlaytimeBrowseCellsTarget { @@ -53,10 +53,10 @@ mod playtime_impl { RealearnTarget, ReaperTargetType, TargetCharacter, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Fraction, Target, UnitValue}; + use helgobox_api::persistence::Axis; use playtime_api::runtime::CellAddress; #[cfg(feature = "playtime")] use playtime_clip_engine::base::ClipMatrixEvent; - use realearn_api::persistence::Axis; impl PlaytimeBrowseCellsTarget { fn column_or_row_count(&self, context: ControlContext) -> u32 { diff --git a/main/src/domain/targets/playtime_column_action_target.rs b/main/src/domain/targets/playtime_column_action_target.rs index 5bdb15314..256a820e1 100644 --- a/main/src/domain/targets/playtime_column_action_target.rs +++ b/main/src/domain/targets/playtime_column_action_target.rs @@ -3,7 +3,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, VirtualPlaytimeColumn, DEFAULT_TARGET, }; -use realearn_api::persistence::PlaytimeColumnAction; +use helgobox_api::persistence::PlaytimeColumnAction; pub const PLAYTIME_COLUMN_TARGET: TargetTypeDef = TargetTypeDef { section: TargetSection::Playtime, @@ -84,11 +84,11 @@ mod playtime_impl { TargetCharacter, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Target, UnitValue}; + use helgobox_api::persistence::PlaytimeColumnAction; use playtime_api::persistence::ColumnAddress; use playtime_api::runtime::CellAddress; use playtime_clip_engine::base::ClipMatrixEvent; use playtime_clip_engine::rt::{QualifiedSlotChangeEvent, SlotChangeEvent}; - use realearn_api::persistence::PlaytimeColumnAction; use std::borrow::Cow; impl<'a> Target<'a> for PlaytimeColumnActionTarget { diff --git a/main/src/domain/targets/playtime_control_unit_scroll_target.rs b/main/src/domain/targets/playtime_control_unit_scroll_target.rs index 7aa7cd9dc..95847ebc1 100644 --- a/main/src/domain/targets/playtime_control_unit_scroll_target.rs +++ b/main/src/domain/targets/playtime_control_unit_scroll_target.rs @@ -3,7 +3,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, DEFAULT_TARGET, }; -use realearn_api::persistence::Axis; +use helgobox_api::persistence::Axis; #[derive(Debug)] pub struct UnresolvedPlaytimeControlUnitScrollTarget { @@ -53,10 +53,10 @@ mod playtime_impl { RealearnTarget, ReaperTargetType, TargetCharacter, UnitEvent, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Fraction, Target}; + use helgobox_api::persistence::Axis; use playtime_api::persistence::SlotAddress; #[cfg(feature = "playtime")] use playtime_clip_engine::base::ClipMatrixEvent; - use realearn_api::persistence::Axis; impl PlaytimeControlUnitScrollTarget { fn value_count(&self, context: ControlContext) -> u32 { diff --git a/main/src/domain/targets/playtime_matrix_action_target.rs b/main/src/domain/targets/playtime_matrix_action_target.rs index ffd8a6b0b..01a576291 100644 --- a/main/src/domain/targets/playtime_matrix_action_target.rs +++ b/main/src/domain/targets/playtime_matrix_action_target.rs @@ -2,7 +2,7 @@ use crate::domain::{ CompartmentKind, ExtendedProcessorContext, ReaperTarget, TargetSection, TargetTypeDef, UnresolvedReaperTargetDef, DEFAULT_TARGET, }; -use realearn_api::persistence::PlaytimeMatrixAction; +use helgobox_api::persistence::PlaytimeMatrixAction; #[derive(Debug)] pub struct UnresolvedPlaytimeMatrixActionTarget { @@ -76,10 +76,10 @@ mod playtime_impl { }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Fraction, Target, UnitValue}; + use helgobox_api::persistence::PlaytimeMatrixAction; use playtime_api::persistence::{EvenQuantization, RecordLengthMode}; use playtime_clip_engine::base::{ClipMatrixEvent, Matrix, SequencerStatus}; use playtime_clip_engine::rt::{QualifiedSlotChangeEvent, SlotChangeEvent}; - use realearn_api::persistence::PlaytimeMatrixAction; use std::borrow::Cow; diff --git a/main/src/domain/targets/playtime_row_action_target.rs b/main/src/domain/targets/playtime_row_action_target.rs index f45e782ed..b417656bc 100644 --- a/main/src/domain/targets/playtime_row_action_target.rs +++ b/main/src/domain/targets/playtime_row_action_target.rs @@ -3,7 +3,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, VirtualPlaytimeRow, DEFAULT_TARGET, }; -use realearn_api::persistence::PlaytimeRowAction; +use helgobox_api::persistence::PlaytimeRowAction; #[derive(Debug)] pub struct UnresolvedPlaytimeRowActionTarget { @@ -90,10 +90,10 @@ mod playtime_impl { RealTimeReaperTarget, RealearnTarget, ReaperTargetType, TargetCharacter, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Target}; + use helgobox_api::persistence::PlaytimeRowAction; use playtime_api::persistence::RowAddress; use playtime_api::runtime::CellAddress; use playtime_clip_engine::base::ClipMatrixEvent; - use realearn_api::persistence::PlaytimeRowAction; impl PlaytimeRowActionTarget { fn hit_internal( diff --git a/main/src/domain/targets/playtime_slot_management_action_target.rs b/main/src/domain/targets/playtime_slot_management_action_target.rs index 740485988..019dabc2e 100644 --- a/main/src/domain/targets/playtime_slot_management_action_target.rs +++ b/main/src/domain/targets/playtime_slot_management_action_target.rs @@ -3,8 +3,8 @@ use crate::domain::{ UnresolvedReaperTargetDef, VirtualPlaytimeSlot, DEFAULT_TARGET, }; +use helgobox_api::persistence::PlaytimeSlotManagementAction; use playtime_api::persistence::SlotAddress; -use realearn_api::persistence::PlaytimeSlotManagementAction; #[derive(Debug)] pub struct UnresolvedPlaytimeSlotManagementActionTarget { @@ -63,10 +63,10 @@ mod playtime_impl { PlaytimeSlotManagementActionTarget, RealearnTarget, ReaperTargetType, TargetCharacter, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, PropValue, Target}; + use helgobox_api::persistence::PlaytimeSlotManagementAction; use playtime_api::persistence::SlotAddress; use playtime_clip_engine::base::{ClipAddress, ClipMatrixEvent}; use playtime_clip_engine::rt::{ClipChangeEvent, QualifiedClipChangeEvent}; - use realearn_api::persistence::PlaytimeSlotManagementAction; impl PlaytimeSlotManagementActionTarget { fn hit_internal( diff --git a/main/src/domain/targets/playtime_slot_transport_target.rs b/main/src/domain/targets/playtime_slot_transport_target.rs index 03def3abd..f1726914c 100644 --- a/main/src/domain/targets/playtime_slot_transport_target.rs +++ b/main/src/domain/targets/playtime_slot_transport_target.rs @@ -2,8 +2,8 @@ use crate::domain::{ CompartmentKind, ExtendedProcessorContext, ReaperTarget, TargetSection, TargetTypeDef, UnresolvedReaperTargetDef, VirtualPlaytimeSlot, DEFAULT_TARGET, }; +use helgobox_api::persistence::PlaytimeSlotTransportAction; use playtime_api::persistence::SlotAddress; -use realearn_api::persistence::PlaytimeSlotTransportAction; use reaper_high::Project; #[derive(Debug)] @@ -114,6 +114,7 @@ mod playtime_impl { use crate::domain::playtime_util::{ clip_play_state_unit_value, interpret_current_clip_slot_value, }; + use helgobox_api::persistence::PlaytimeSlotTransportAction; use playtime_clip_engine::base::ClipAddress; use playtime_clip_engine::rt::{ClipPlayState, TriggerSlotMainOptions}; #[cfg(feature = "playtime")] @@ -124,7 +125,6 @@ mod playtime_impl { QualifiedSlotChangeEvent, SlotChangeEvent, }, }; - use realearn_api::persistence::PlaytimeSlotTransportAction; use reaper_high::{ChangeEvent, Project}; use std::borrow::Cow; diff --git a/main/src/domain/targets/seek_target.rs b/main/src/domain/targets/seek_target.rs index bf8ad63ae..256349603 100644 --- a/main/src/domain/targets/seek_target.rs +++ b/main/src/domain/targets/seek_target.rs @@ -7,7 +7,7 @@ use crate::domain::{ use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, NumericValue, PropValue, Target, UnitValue, }; -use realearn_api::persistence::SeekBehavior; +use helgobox_api::persistence::SeekBehavior; use reaper_high::{Project, Reaper}; use reaper_medium::{ GetLoopTimeRange2Result, PositionInSeconds, SetEditCurPosOptions, TimeMode, TimeModeOverride, diff --git a/main/src/domain/targets/take_mapping_snapshot_target.rs b/main/src/domain/targets/take_mapping_snapshot_target.rs index 79685d332..6e66943a9 100644 --- a/main/src/domain/targets/take_mapping_snapshot_target.rs +++ b/main/src/domain/targets/take_mapping_snapshot_target.rs @@ -5,7 +5,7 @@ use crate::domain::{ TargetCharacter, TargetSection, TargetTypeDef, UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, Target}; -use realearn_api::persistence::MappingSnapshotDescForTake; +use helgobox_api::persistence::MappingSnapshotDescForTake; #[derive(Debug)] pub struct UnresolvedTakeMappingSnapshotTarget { diff --git a/main/src/domain/targets/track_tool_target.rs b/main/src/domain/targets/track_tool_target.rs index df97a7c12..e432ba692 100644 --- a/main/src/domain/targets/track_tool_target.rs +++ b/main/src/domain/targets/track_tool_target.rs @@ -6,7 +6,7 @@ use crate::domain::{ UnresolvedReaperTargetDef, DEFAULT_TARGET, }; use helgoboss_learn::{AbsoluteValue, ControlType, ControlValue, NumericValue, Target}; -use realearn_api::persistence::{TrackScope, TrackToolAction}; +use helgobox_api::persistence::{TrackScope, TrackToolAction}; use reaper_high::{Project, Track}; use std::borrow::Cow; diff --git a/main/src/domain/unresolved_reaper_target.rs b/main/src/domain/unresolved_reaper_target.rs index 010d6bdcb..b0125c72d 100644 --- a/main/src/domain/unresolved_reaper_target.rs +++ b/main/src/domain/unresolved_reaper_target.rs @@ -29,10 +29,10 @@ use enum_dispatch::enum_dispatch; use fasteval::{Compiler, Evaler, Instruction, Slab}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use playtime_api::persistence::SlotAddress; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ FxChainDescriptor, FxDescriptorCommons, TrackDescriptorCommons, TrackScope, }; +use playtime_api::persistence::SlotAddress; use reaper_high::{ BookmarkType, FindBookmarkResult, Fx, FxChain, FxParameter, Guid, Project, Reaper, SendPartnerType, Track, TrackRoute, @@ -281,9 +281,9 @@ pub struct TrackDescriptor { impl TrackDescriptor { pub fn from_api( - api_desc: realearn_api::persistence::TrackDescriptor, + api_desc: helgobox_api::persistence::TrackDescriptor, ) -> Result> { - use realearn_api::persistence::TrackDescriptor::*; + use helgobox_api::persistence::TrackDescriptor::*; let (track, commons) = match api_desc { This { commons } => (VirtualTrack::This, commons), Master { commons } => (VirtualTrack::Master, commons), @@ -365,9 +365,9 @@ pub struct FxDescriptor { impl FxDescriptor { pub fn from_api( - api_desc: realearn_api::persistence::FxDescriptor, + api_desc: helgobox_api::persistence::FxDescriptor, ) -> Result> { - use realearn_api::persistence::FxDescriptor; + use helgobox_api::persistence::FxDescriptor; let (track_descriptor, fx, commons): (TrackDescriptor, VirtualFx, FxDescriptorCommons) = match api_desc { FxDescriptor::This { commons } => (Default::default(), VirtualFx::This, commons), @@ -827,9 +827,9 @@ impl Default for VirtualPlaytimeColumn { impl VirtualPlaytimeColumn { pub fn from_descriptor( - descriptor: &realearn_api::persistence::PlaytimeColumnDescriptor, + descriptor: &helgobox_api::persistence::PlaytimeColumnDescriptor, ) -> Result { - use realearn_api::persistence::PlaytimeColumnDescriptor::*; + use helgobox_api::persistence::PlaytimeColumnDescriptor::*; let column = match descriptor { Active => VirtualPlaytimeColumn::Active, ByIndex(address) => VirtualPlaytimeColumn::ByIndex(address.index), @@ -980,7 +980,7 @@ pub enum VirtualTrack { /// Uses the track from the given clip column. FromClipColumn { column: VirtualPlaytimeColumn, - context: realearn_api::persistence::ClipColumnTrackContext, + context: helgobox_api::persistence::ClipColumnTrackContext, }, /// Unit track Unit, @@ -1624,7 +1624,7 @@ impl VirtualTrack { pub fn clip_column_track_context( &self, - ) -> Option { + ) -> Option { if let VirtualTrack::FromClipColumn { context, .. } = self { Some(*context) } else { diff --git a/main/src/domain/virtual.rs b/main/src/domain/virtual.rs index c42afc63c..230b4906b 100644 --- a/main/src/domain/virtual.rs +++ b/main/src/domain/virtual.rs @@ -6,7 +6,7 @@ use derivative::Derivative; use helgoboss_learn::{ AbsoluteValue, ControlType, ControlValue, FeedbackValue, SourceCharacter, Target, UnitValue, }; -use realearn_api::persistence::VirtualControlElementCharacter; +use helgobox_api::persistence::VirtualControlElementCharacter; use std::fmt; use std::fmt::{Display, Formatter}; use std::str::FromStr; diff --git a/main/src/infrastructure/api/convert/defaults.rs b/main/src/infrastructure/api/convert/defaults.rs index 8077456f3..60df2d6c3 100644 --- a/main/src/infrastructure/api/convert/defaults.rs +++ b/main/src/infrastructure/api/convert/defaults.rs @@ -1,4 +1,4 @@ -use realearn_api::persistence::Interval; +use helgobox_api::persistence::Interval; pub const MAPPING_CONTROL_ENABLED: bool = true; pub const MAPPING_FEEDBACK_ENABLED: bool = true; diff --git a/main/src/infrastructure/api/convert/from_data/compartment.rs b/main/src/infrastructure/api/convert/from_data/compartment.rs index 0cefbd509..1eff7429e 100644 --- a/main/src/infrastructure/api/convert/from_data/compartment.rs +++ b/main/src/infrastructure/api/convert/from_data/compartment.rs @@ -3,7 +3,7 @@ use crate::infrastructure::api::convert::from_data::{ }; use crate::infrastructure::api::convert::{convert_multiple, ConversionResult}; use crate::infrastructure::data::CompartmentModelData; -use realearn_api::persistence; +use helgobox_api::persistence; pub fn convert_compartment( data: CompartmentModelData, diff --git a/main/src/infrastructure/api/convert/from_data/glue.rs b/main/src/infrastructure/api/convert/from_data/glue.rs index 05b850140..6c681b86a 100644 --- a/main/src/infrastructure/api/convert/from_data/glue.rs +++ b/main/src/infrastructure/api/convert/from_data/glue.rs @@ -5,8 +5,8 @@ use helgoboss_learn::{ AbsoluteMode, ButtonUsage, DiscreteIncrement, EncoderUsage, FeedbackType, FireMode, GroupInteraction, OutOfRangeBehavior, TakeoverMode, UnitValue, VirtualColor, }; -use realearn_api::persistence; -use realearn_api::persistence::{DynamicFeedback, NumericFeedback, PropColor, TextFeedback}; +use helgobox_api::persistence; +use helgobox_api::persistence::{DynamicFeedback, NumericFeedback, PropColor, TextFeedback}; pub fn convert_glue( data: ModeModelData, diff --git a/main/src/infrastructure/api/convert/from_data/group.rs b/main/src/infrastructure/api/convert/from_data/group.rs index 8bdce5564..ee099ecad 100644 --- a/main/src/infrastructure/api/convert/from_data/group.rs +++ b/main/src/infrastructure/api/convert/from_data/group.rs @@ -3,7 +3,7 @@ use crate::infrastructure::api::convert::from_data::{ }; use crate::infrastructure::api::convert::{defaults, ConversionResult}; use crate::infrastructure::data::GroupModelData; -use realearn_api::persistence; +use helgobox_api::persistence; pub fn convert_group( data: GroupModelData, diff --git a/main/src/infrastructure/api/convert/from_data/mapping.rs b/main/src/infrastructure/api/convert/from_data/mapping.rs index ad61bf303..6f29b4cf4 100644 --- a/main/src/infrastructure/api/convert/from_data/mapping.rs +++ b/main/src/infrastructure/api/convert/from_data/mapping.rs @@ -7,8 +7,8 @@ use crate::infrastructure::api::convert::from_data::{ }; use crate::infrastructure::api::convert::{defaults, ConversionResult}; use crate::infrastructure::data::MappingModelData; -use realearn_api::persistence; -use realearn_api::persistence::LifecycleHook; +use helgobox_api::persistence; +use helgobox_api::persistence::LifecycleHook; pub fn convert_mapping( data: MappingModelData, diff --git a/main/src/infrastructure/api/convert/from_data/mod.rs b/main/src/infrastructure/api/convert/from_data/mod.rs index 70495e434..03df524ec 100644 --- a/main/src/infrastructure/api/convert/from_data/mod.rs +++ b/main/src/infrastructure/api/convert/from_data/mod.rs @@ -18,8 +18,8 @@ use crate::infrastructure::data::{ ActivationConditionData, OscValueRange, VirtualControlElementIdData, }; use helgoboss_learn::OscTypeTag; -use realearn_api::persistence; -use realearn_api::persistence::ParamRef; +use helgobox_api::persistence; +use helgobox_api::persistence::ParamRef; pub use target::*; fn convert_control_element_id( diff --git a/main/src/infrastructure/api/convert/from_data/parameter.rs b/main/src/infrastructure/api/convert/from_data/parameter.rs index 94144fdd3..ddb3e336d 100644 --- a/main/src/infrastructure/api/convert/from_data/parameter.rs +++ b/main/src/infrastructure/api/convert/from_data/parameter.rs @@ -1,7 +1,7 @@ use crate::domain::ParamSetting; use crate::infrastructure::api::convert::from_data::ConversionStyle; use crate::infrastructure::api::convert::ConversionResult; -use realearn_api::persistence; +use helgobox_api::persistence; pub fn convert_parameter( index: String, diff --git a/main/src/infrastructure/api/convert/from_data/source.rs b/main/src/infrastructure/api/convert/from_data/source.rs index 1b25c446c..bccd4539e 100644 --- a/main/src/infrastructure/api/convert/from_data/source.rs +++ b/main/src/infrastructure/api/convert/from_data/source.rs @@ -8,7 +8,7 @@ use helgoboss_learn::{ DisplayType, MackieSevenSegmentDisplayScope, MidiClockTransportMessage, SourceCharacter, }; use helgoboss_midi::{Channel, U14}; -use realearn_api::persistence; +use helgobox_api::persistence; use std::convert::TryInto; pub struct NewSourceProps { diff --git a/main/src/infrastructure/api/convert/from_data/target.rs b/main/src/infrastructure/api/convert/from_data/target.rs index d57db20f3..bc0696526 100644 --- a/main/src/infrastructure/api/convert/from_data/target.rs +++ b/main/src/infrastructure/api/convert/from_data/target.rs @@ -17,8 +17,8 @@ use crate::infrastructure::data::{ MigrationDescriptor, TargetModelData, TrackData, TrackDeserializationInput, }; use base::hash_util::convert_into_other_hash_set; -use realearn_api::persistence; -use realearn_api::persistence::{ +use helgobox_api::persistence; +use helgobox_api::persistence::{ AllTrackFxOnOffStateTarget, AnyOnTarget, AutomationModeOverrideTarget, BackwardCompatibleMappingSnapshotDescForTake, BookmarkDescriptor, BookmarkRef, BrowseFxChainTarget, BrowseFxPresetsTarget, BrowseGroupMappingsTarget, @@ -289,7 +289,7 @@ fn convert_real_target( route: convert_route_descriptor(data, style), }), PlaytimeSlotTransportAction => T::PlaytimeSlotTransportAction( - realearn_api::persistence::PlaytimeSlotTransportActionTarget { + helgobox_api::persistence::PlaytimeSlotTransportActionTarget { commons, slot: data.clip_slot.unwrap_or_default(), action: data.clip_transport_action.unwrap_or_default(), @@ -300,39 +300,39 @@ fn convert_real_target( }, ), PlaytimeColumnAction => { - T::PlaytimeColumnAction(realearn_api::persistence::PlaytimeColumnActionTarget { + T::PlaytimeColumnAction(helgobox_api::persistence::PlaytimeColumnActionTarget { commons, column: data.clip_column, action: data.clip_column_action, }) } PlaytimeRowAction => { - T::PlaytimeRowAction(realearn_api::persistence::PlaytimeRowActionTarget { + T::PlaytimeRowAction(helgobox_api::persistence::PlaytimeRowActionTarget { commons, row: data.clip_row, action: data.clip_row_action, }) } PlaytimeMatrixAction => { - T::PlaytimeMatrixAction(realearn_api::persistence::PlaytimeMatrixActionTarget { + T::PlaytimeMatrixAction(helgobox_api::persistence::PlaytimeMatrixActionTarget { commons, action: data.clip_matrix_action, }) } PlaytimeControlUnitScroll => T::PlaytimeControlUnitScroll( - realearn_api::persistence::PlaytimeControlUnitScrollTarget { + helgobox_api::persistence::PlaytimeControlUnitScrollTarget { commons, axis: data.axis, }, ), PlaytimeBrowseCells => { - T::PlaytimeBrowseCells(realearn_api::persistence::PlaytimeBrowseCellsTarget { + T::PlaytimeBrowseCells(helgobox_api::persistence::PlaytimeBrowseCellsTarget { commons, axis: data.axis, }) } PlaytimeSlotSeek => { - T::PlaytimeSlotSeek(realearn_api::persistence::PlaytimeSlotSeekTarget { + T::PlaytimeSlotSeek(helgobox_api::persistence::PlaytimeSlotSeekTarget { commons, slot: data.clip_slot.unwrap_or_default(), feedback_resolution: convert_feedback_resolution( @@ -342,13 +342,13 @@ fn convert_real_target( }) } PlaytimeSlotVolume => { - T::PlaytimeSlotVolume(realearn_api::persistence::PlaytimeSlotVolumeTarget { + T::PlaytimeSlotVolume(helgobox_api::persistence::PlaytimeSlotVolumeTarget { commons, slot: data.clip_slot.unwrap_or_default(), }) } PlaytimeSlotManagementAction => T::PlaytimeSlotManagementAction( - realearn_api::persistence::PlaytimeSlotManagementActionTarget { + helgobox_api::persistence::PlaytimeSlotManagementActionTarget { commons, slot: data.clip_slot.unwrap_or_default(), action: data.clip_management_action, @@ -867,7 +867,7 @@ fn convert_virtual_target(data: TargetModelData, style: ConversionStyle) -> pers fn convert_track_descriptor( data: TrackData, only_if_track_selected: bool, - clip_column: &realearn_api::persistence::PlaytimeColumnDescriptor, + clip_column: &helgobox_api::persistence::PlaytimeColumnDescriptor, style: ConversionStyle, ) -> Option { let input = TrackDeserializationInput { diff --git a/main/src/infrastructure/api/convert/to_data/compartment.rs b/main/src/infrastructure/api/convert/to_data/compartment.rs index f7d77d1a6..f9f5ca9b2 100644 --- a/main/src/infrastructure/api/convert/to_data/compartment.rs +++ b/main/src/infrastructure/api/convert/to_data/compartment.rs @@ -6,7 +6,7 @@ use crate::infrastructure::api::convert::to_data::parameter::convert_parameter; use crate::infrastructure::api::convert::to_data::{convert_mapping, ApiToDataConversionContext}; use crate::infrastructure::api::convert::{convert_multiple, ConversionResult}; use crate::infrastructure::data::{CompartmentModelData, GroupModelData}; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; pub fn convert_compartment( compartment: CompartmentKind, diff --git a/main/src/infrastructure/api/convert/to_data/glue.rs b/main/src/infrastructure/api/convert/to_data/glue.rs index a859c646e..333ad8a08 100644 --- a/main/src/infrastructure/api/convert/to_data/glue.rs +++ b/main/src/infrastructure/api/convert/to_data/glue.rs @@ -2,7 +2,7 @@ use crate::infrastructure::api::convert::defaults; use crate::infrastructure::api::convert::ConversionResult; use crate::infrastructure::data::ModeModelData; use helgoboss_learn::{DiscreteIncrement, SoftSymmetricUnitValue, UnitValue}; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; use std::convert::TryInto; pub fn convert_glue(g: Glue) -> ConversionResult { diff --git a/main/src/infrastructure/api/convert/to_data/group.rs b/main/src/infrastructure/api/convert/to_data/group.rs index 606c86a3a..2c22d3346 100644 --- a/main/src/infrastructure/api/convert/to_data/group.rs +++ b/main/src/infrastructure/api/convert/to_data/group.rs @@ -2,7 +2,7 @@ use crate::domain::{CompartmentParamIndex, GroupKey}; use crate::infrastructure::api::convert::to_data::{convert_activation, convert_tags}; use crate::infrastructure::api::convert::{defaults, ConversionResult}; use crate::infrastructure::data::{EnabledData, GroupModelData}; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; pub fn convert_group( g: Group, diff --git a/main/src/infrastructure/api/convert/to_data/mapping.rs b/main/src/infrastructure/api/convert/to_data/mapping.rs index 0cff22e62..f676e9b3f 100644 --- a/main/src/infrastructure/api/convert/to_data/mapping.rs +++ b/main/src/infrastructure/api/convert/to_data/mapping.rs @@ -11,7 +11,7 @@ use crate::infrastructure::api::convert::to_data::{ }; use crate::infrastructure::api::convert::{defaults, ConversionResult}; use crate::infrastructure::data::{EnabledData, MappingModelData}; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; use std::convert::TryInto; use std::str::FromStr; diff --git a/main/src/infrastructure/api/convert/to_data/mod.rs b/main/src/infrastructure/api/convert/to_data/mod.rs index a3d8f6fbc..0beb57ad6 100644 --- a/main/src/infrastructure/api/convert/to_data/mod.rs +++ b/main/src/infrastructure/api/convert/to_data/mod.rs @@ -7,11 +7,11 @@ use crate::{application, domain}; use anyhow::anyhow; pub use compartment::*; use enumflags2::BitFlags; -pub use mapping::*; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ ActivationCondition, Interval, Keystroke, ModifierState, OscArgKind, ParamRef, VirtualControlElementId, }; +pub use mapping::*; use reaper_medium::AcceleratorKeyCode; use source::*; diff --git a/main/src/infrastructure/api/convert/to_data/parameter.rs b/main/src/infrastructure/api/convert/to_data/parameter.rs index 361e16958..018d1d8b4 100644 --- a/main/src/infrastructure/api/convert/to_data/parameter.rs +++ b/main/src/infrastructure/api/convert/to_data/parameter.rs @@ -1,6 +1,6 @@ use crate::domain::ParamSetting; use crate::infrastructure::api::convert::ConversionResult; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; pub fn convert_parameter(p: Parameter) -> ConversionResult { let data = ParamSetting { diff --git a/main/src/infrastructure/api/convert/to_data/source.rs b/main/src/infrastructure/api/convert/to_data/source.rs index c5acb533b..1bfa24f74 100644 --- a/main/src/infrastructure/api/convert/to_data/source.rs +++ b/main/src/infrastructure/api/convert/to_data/source.rs @@ -7,7 +7,7 @@ use crate::infrastructure::data::SourceModelData; use anyhow::bail; use helgoboss_learn::DisplayType; use helgoboss_midi::{Channel, U14}; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; use std::convert::TryInto; pub fn convert_source(s: Source) -> ConversionResult { diff --git a/main/src/infrastructure/api/convert/to_data/target.rs b/main/src/infrastructure/api/convert/to_data/target.rs index f1fdb0ae0..3b4b77c35 100644 --- a/main/src/infrastructure/api/convert/to_data/target.rs +++ b/main/src/infrastructure/api/convert/to_data/target.rs @@ -18,7 +18,7 @@ use crate::infrastructure::data::{ }; use crate::{application, domain}; use base::hash_util::convert_into_other_hash_set; -use realearn_api::persistence::*; +use helgobox_api::persistence::*; use reaper_high::Guid; use std::rc::Rc; diff --git a/main/src/infrastructure/data/controller_manager.rs b/main/src/infrastructure/data/controller_manager.rs index 18294349b..d0e9d9ac8 100644 --- a/main/src/infrastructure/data/controller_manager.rs +++ b/main/src/infrastructure/data/controller_manager.rs @@ -1,10 +1,10 @@ use crate::domain::{format_as_pretty_hex, RequestMidiDeviceIdentityReply}; use anyhow::Context; use camino::Utf8PathBuf; -use nanoid::nanoid; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ Controller, ControllerConfig, ControllerConnection, MidiControllerConnection, MidiInputPort, }; +use nanoid::nanoid; use reaper_medium::{MidiInputDeviceId, MidiOutputDeviceId}; use std::cell::RefCell; use std::fmt::Debug; diff --git a/main/src/infrastructure/data/instance_data.rs b/main/src/infrastructure/data/instance_data.rs index ac93cc5b0..dba50933b 100644 --- a/main/src/infrastructure/data/instance_data.rs +++ b/main/src/infrastructure/data/instance_data.rs @@ -1,7 +1,7 @@ use crate::infrastructure::data::{ClipMatrixRefData, UnitData}; use base::default_util::{deserialize_null_default, is_default}; use base::hash_util::NonCryptoHashMap; -use realearn_api::persistence::InstanceSettings; +use helgobox_api::persistence::InstanceSettings; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/main/src/infrastructure/data/mapping_model_data.rs b/main/src/infrastructure/data/mapping_model_data.rs index 7bc787ec0..218e97c74 100644 --- a/main/src/infrastructure/data/mapping_model_data.rs +++ b/main/src/infrastructure/data/mapping_model_data.rs @@ -8,7 +8,7 @@ use crate::infrastructure::data::{ ModeModelData, ModelToDataConversionContext, SourceModelData, TargetModelData, }; use base::default_util::{bool_true, deserialize_null_default, is_bool_true, is_default}; -use realearn_api::persistence::SuccessAudioFeedback; +use helgobox_api::persistence::SuccessAudioFeedback; use semver::Version; use serde::{Deserialize, Serialize}; diff --git a/main/src/infrastructure/data/mode_model_data.rs b/main/src/infrastructure/data/mode_model_data.rs index fbe834497..8246d95ac 100644 --- a/main/src/infrastructure/data/mode_model_data.rs +++ b/main/src/infrastructure/data/mode_model_data.rs @@ -6,7 +6,7 @@ use helgoboss_learn::{ GroupInteraction, Interval, OutOfRangeBehavior, SoftSymmetricUnitValue, TakeoverMode, UnitValue, ValueSequence, VirtualColor, }; -use realearn_api::persistence::FeedbackValueTable; +use helgobox_api::persistence::FeedbackValueTable; use serde::{Deserialize, Serialize}; use std::time::Duration; use tracing::debug; diff --git a/main/src/infrastructure/data/preset.rs b/main/src/infrastructure/data/preset.rs index 28d162898..eb70c15b4 100644 --- a/main/src/infrastructure/data/preset.rs +++ b/main/src/infrastructure/data/preset.rs @@ -13,12 +13,12 @@ use anyhow::{anyhow, bail, Context}; use base::byte_pattern::BytePattern; use base::file_util; use base::file_util::is_hidden; +use helgobox_api::persistence::{ + CommonPresetMetaData, ControllerPresetMetaData, MainPresetMetaData, VirtualControlSchemeId, +}; use include_dir::{include_dir, Dir}; use itertools::Itertools; use mlua::LuaSerdeExt; -use realearn_api::persistence::{ - CommonPresetMetaData, ControllerPresetMetaData, MainPresetMetaData, VirtualControlSchemeId, -}; use reaper_high::Reaper; use rxrust::prelude::*; use serde::Deserialize; @@ -514,7 +514,7 @@ impl FileBasedCompartmentPresetManager { script_name, &file_content, )?; - let compartment_content: realearn_api::persistence::Compartment = + let compartment_content: helgobox_api::persistence::Compartment = lua.as_ref().from_value(value)?; let compartment_data = convert_compartment(self.compartment, compartment_content)?; let compartment_model = compartment_data.to_model( diff --git a/main/src/infrastructure/data/source_model_data.rs b/main/src/infrastructure/data/source_model_data.rs index cc8f67cca..52ce29628 100644 --- a/main/src/infrastructure/data/source_model_data.rs +++ b/main/src/infrastructure/data/source_model_data.rs @@ -9,7 +9,7 @@ use crate::infrastructure::data::VirtualControlElementIdData; use base::default_util::{deserialize_null_default, is_default}; use helgoboss_learn::{DisplayType, MidiClockTransportMessage, OscTypeTag, SourceCharacter}; use helgoboss_midi::{Channel, U14, U7}; -use realearn_api::persistence::{MidiScriptKind, VirtualControlElementCharacter}; +use helgobox_api::persistence::{MidiScriptKind, VirtualControlElementCharacter}; use semver::Version; use serde::{Deserialize, Serialize}; use std::convert::TryInto; diff --git a/main/src/infrastructure/data/target_model_data.rs b/main/src/infrastructure/data/target_model_data.rs index 65f2f12ed..cc5e0e6c7 100644 --- a/main/src/infrastructure/data/target_model_data.rs +++ b/main/src/infrastructure/data/target_model_data.rs @@ -28,14 +28,14 @@ use base::default_util::{ bool_true, deserialize_null_default, is_bool_true, is_default, is_none_or_some_default, }; use helgoboss_learn::{AbsoluteValue, Fraction, OscTypeTag, UnitValue}; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ Axis, BrowseTracksMode, FxToolAction, LearnableTargetKind, MappingSnapshotDescForLoad, MappingSnapshotDescForTake, MonitoringMode, MouseAction, PotFilterKind, SeekBehavior, TargetTouchCause, TargetValue, TrackScope, TrackToolAction, VirtualControlElementCharacter, }; use base::hash_util::NonCryptoHashSet; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ ClipColumnTrackContext, PlaytimeColumnAction, PlaytimeColumnDescriptor, PlaytimeMatrixAction, PlaytimeRowAction, PlaytimeRowDescriptor, PlaytimeSlotDescriptor, PlaytimeSlotManagementAction, PlaytimeSlotTransportAction, @@ -1011,7 +1011,7 @@ impl TargetModelData { pub struct TrackSerializationOutput { pub track_data: TrackData, - pub clip_column: Option, + pub clip_column: Option, } /// This function is so annoying because of backward compatibility. Once made the bad decision diff --git a/main/src/infrastructure/data/unit_data.rs b/main/src/infrastructure/data/unit_data.rs index 9d3bbefeb..a4ab12cc8 100644 --- a/main/src/infrastructure/data/unit_data.rs +++ b/main/src/infrastructure/data/unit_data.rs @@ -20,7 +20,7 @@ use base::default_util::{bool_true, deserialize_null_default, is_bool_true, is_d use crate::base::notification; use crate::infrastructure::api::convert::to_data::ApiToDataConversionContext; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ FxDescriptor, MappingInSnapshot, MappingSnapshot, TrackDescriptor, }; use reaper_medium::{MidiInputDeviceId, MidiOutputDeviceId}; diff --git a/main/src/infrastructure/plugin/api_impl.rs b/main/src/infrastructure/plugin/api_impl.rs index 5751d0514..0d387b6d9 100644 --- a/main/src/infrastructure/plugin/api_impl.rs +++ b/main/src/infrastructure/plugin/api_impl.rs @@ -1,6 +1,6 @@ use crate::infrastructure::plugin::BackboneShell; use anyhow::Context; -use realearn_api::runtime::{register_helgobox_api, HelgoboxApi}; +use helgobox_api::runtime::{register_helgobox_api, HelgoboxApi}; use reaper_high::{OrCurrentProject, Project, Reaper}; use reaper_low::raw::ReaProject; use reaper_medium::{ReaperStr, RegistrationObject}; diff --git a/main/src/infrastructure/plugin/auto_units.rs b/main/src/infrastructure/plugin/auto_units.rs index db89b6945..441c6a812 100644 --- a/main/src/infrastructure/plugin/auto_units.rs +++ b/main/src/infrastructure/plugin/auto_units.rs @@ -9,7 +9,7 @@ use anyhow::Context; use base::byte_pattern::BytePattern; use base::hash_util::NonCryptoHashMap; use base::Global; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ Controller, ControllerConnection, ControllerPresetMetaData, MainPresetMetaData, MidiControllerConnection, }; diff --git a/main/src/infrastructure/plugin/backbone_shell.rs b/main/src/infrastructure/plugin/backbone_shell.rs index df7bc52ae..59db6de2c 100644 --- a/main/src/infrastructure/plugin/backbone_shell.rs +++ b/main/src/infrastructure/plugin/backbone_shell.rs @@ -58,15 +58,15 @@ use anyhow::{bail, Context}; use base::hash_util::NonCryptoHashSet; use base::metrics_util::MetricsHook; use camino::{Utf8Path, Utf8PathBuf}; -use helgoboss_allocator::{start_async_deallocation_thread, AsyncDeallocatorCommandReceiver}; -use itertools::Itertools; -use once_cell::sync::Lazy; -use realearn_api::persistence::{ +use helgobox_allocator::{start_async_deallocation_thread, AsyncDeallocatorCommandReceiver}; +use helgobox_api::persistence::{ CompartmentPresetId, Controller, ControllerConnection, Envelope, FxChainDescriptor, FxDescriptor, MidiControllerConnection, MidiInputPort, MidiOutputPort, TargetTouchCause, TrackDescriptor, TrackFxChain, VirtualControlElementCharacter, }; -use realearn_api::runtime::{AutoAddedControllerEvent, GlobalInfoEvent}; +use helgobox_api::runtime::{AutoAddedControllerEvent, GlobalInfoEvent}; +use itertools::Itertools; +use once_cell::sync::Lazy; use reaper_high::{ ChangeEvent, CrashInfo, Fx, Guid, MiddlewareControlSurface, Project, Reaper, Track, }; @@ -318,7 +318,7 @@ impl BackboneShell { ); // We initialize tracing here already instead of activating/deactivating it when waking up // or go to sleep. Reason: It doesn't matter that the async logger thread is lurking around - // even when no ReaLearn instance exists anymore, because the REALEARN_LOG env variable is + // even when no Helgobox instance exists anymore, because the HELGOBOX_LOG env variable is // opt-in and should only be used for debugging purposes anyway. Also, // activating/deactivating would be more difficult because the global tracing subscriber can // be set only once. There's no way to unset it. @@ -2920,7 +2920,7 @@ mod playtime_impl { // example so far where we actually use our exposed API! If we don't "eat our own dogfood", we would have // to add integration tests in order to quickly realize if this works or not. // TODO-low Add integration tests instead of using API here. - let helgobox_api = realearn_api::runtime::HelgoboxApiSession::load(plugin_context) + let helgobox_api = helgobox_api::runtime::HelgoboxApiSession::load(plugin_context) .context("Couldn't load Helgobox API even after adding Helgobox. Old version?")?; let playtime_api = playtime_api::runtime::PlaytimeApiSession::load(plugin_context) .context("Couldn't load Playtime API even after adding Helgobox. Old version? Or Helgobox built without Playtime?")?; diff --git a/main/src/infrastructure/plugin/clip_matrix_handler.rs b/main/src/infrastructure/plugin/clip_matrix_handler.rs index 6f269602a..abe83930d 100644 --- a/main/src/infrastructure/plugin/clip_matrix_handler.rs +++ b/main/src/infrastructure/plugin/clip_matrix_handler.rs @@ -6,12 +6,12 @@ use crate::domain::{ }; use crate::infrastructure::plugin::WeakInstanceShell; use base::{Global, NamedChannelSender}; +use helgobox_api::persistence::{ + PlaytimeColumnAction, PlaytimeMatrixAction, PlaytimeRowAction, PlaytimeSlotTransportAction, +}; use playtime_api::runtime::{ ControlUnit, ControlUnitId, SimpleMappingContainer, SimpleMappingTarget, }; -use realearn_api::persistence::{ - PlaytimeColumnAction, PlaytimeMatrixAction, PlaytimeRowAction, PlaytimeSlotTransportAction, -}; use reaper_high::Reaper; #[derive(Debug)] diff --git a/main/src/infrastructure/plugin/helgobox_plugin.rs b/main/src/infrastructure/plugin/helgobox_plugin.rs index 4c54e6898..9fe2ccd06 100644 --- a/main/src/infrastructure/plugin/helgobox_plugin.rs +++ b/main/src/infrastructure/plugin/helgobox_plugin.rs @@ -12,7 +12,7 @@ use crate::domain::{ use crate::infrastructure::plugin::instance_parameter_container::InstanceParameterContainer; use crate::infrastructure::plugin::{init_backbone_shell, SET_STATE_PARAM_NAME}; use base::Global; -use helgoboss_allocator::*; +use helgobox_allocator::*; use reaper_high::{Reaper, ReaperGuard}; use reaper_low::{static_plugin_context, PluginContext}; use reaper_medium::{Hz, ReaperStr}; diff --git a/main/src/infrastructure/plugin/instance_shell.rs b/main/src/infrastructure/plugin/instance_shell.rs index 6494e81cf..98b05ab27 100644 --- a/main/src/infrastructure/plugin/instance_shell.rs +++ b/main/src/infrastructure/plugin/instance_shell.rs @@ -12,8 +12,8 @@ use anyhow::{bail, Context}; use base::hash_util::NonCryptoHashMap; use base::{blocking_read_lock, blocking_write_lock, non_blocking_try_read_lock}; use fragile::Fragile; +use helgobox_api::persistence::{instance_features, InstanceSettings}; use playtime_api::persistence::FlexibleMatrix; -use realearn_api::persistence::{instance_features, InstanceSettings}; use reaper_high::Project; use std::cell::RefCell; use std::iter::once; diff --git a/main/src/infrastructure/plugin/tracing_util.rs b/main/src/infrastructure/plugin/tracing_util.rs index be60e43e2..4237c3af4 100644 --- a/main/src/infrastructure/plugin/tracing_util.rs +++ b/main/src/infrastructure/plugin/tracing_util.rs @@ -16,7 +16,7 @@ pub struct TracingHook { } impl TracingHook { - /// Initializes tracing/logging if the env variable `REALEARN_LOG` is set. + /// Initializes tracing/logging if the env variable `HELGOBOX_LOG` is set. /// /// This doesn't just set the global tracing subscriber, it also starts an async logger /// thread, which is responsible for logging messages that come from real-time threads (reducing @@ -30,13 +30,13 @@ impl TracingHook { /// The returned tracing hook must be dropped before the library is unloaded, otherwise the /// async logger thread sticks around and that can't be good. pub fn init() -> Option { - let env_var = std::env::var("REALEARN_LOG").ok()?; + let env_var = std::env::var("HELGOBOX_LOG").ok()?; let env_filter = EnvFilter::new(env_var); let (sender, receiver) = std::sync::mpsc::channel(); thread::Builder::new() - .name(String::from("ReaLearn async logger")) + .name(String::from("Helgobox async logger")) .spawn(move || keep_logging(receiver, std::io::stdout())) - .expect("ReaLearn async logger thread couldn't be created"); + .expect("Helgobox async logger thread couldn't be created"); // In the beginning, I wrapped the subscriber in one that calls permit_alloc() in on_event() // in order to prevent assert_no_alloc() from aborting because of logging. However, this was // not enough. Some tracing-core stuff also did allocations and it was not possible to wrap it. diff --git a/main/src/infrastructure/proto/ext.rs b/main/src/infrastructure/proto/ext.rs index fcda400c6..166dcf3c5 100644 --- a/main/src/infrastructure/proto/ext.rs +++ b/main/src/infrastructure/proto/ext.rs @@ -2,7 +2,7 @@ use enumflags2::BitFlags; use reaper_high::Reaper; use reaper_medium::{EnumPitchShiftModesResult, PlayState, ReaperStr, ReaperString}; -use realearn_api::runtime::{ControllerPreset, LicenseInfo, MainPreset, ValidatedLicense}; +use helgobox_api::runtime::{ControllerPreset, LicenseInfo, MainPreset, ValidatedLicense}; use crate::application::UnitModel; use crate::domain::CompartmentKind; @@ -31,7 +31,7 @@ use crate::infrastructure::proto::{ use crate::infrastructure::server::data::get_controller_routing; impl occasional_instance_update::Update { - pub fn info_event(event: realearn_api::runtime::InstanceInfoEvent) -> Self { + pub fn info_event(event: helgobox_api::runtime::InstanceInfoEvent) -> Self { let json = serde_json::to_string(&event).expect("couldn't represent instance info event as JSON"); Self::InfoEvent(json) @@ -117,7 +117,7 @@ impl occasional_global_update::Update { Self::ArrangementPlayState(ArrangementPlayState::from_engine(play_state).into()) } - pub fn info_event(event: realearn_api::runtime::GlobalInfoEvent) -> Self { + pub fn info_event(event: helgobox_api::runtime::GlobalInfoEvent) -> Self { let json = serde_json::to_string(&event).expect("couldn't represent global info event as JSON"); Self::InfoEvent(json) diff --git a/main/src/infrastructure/proto/generated.rs b/main/src/infrastructure/proto/generated.rs index 8f7307824..0baf6f895 100644 --- a/main/src/infrastructure/proto/generated.rs +++ b/main/src/infrastructure/proto/generated.rs @@ -112,9 +112,7 @@ pub mod command_request { #[prost(message, tag = "37")] GetOccasionalGlobalUpdates(super::GetOccasionalGlobalUpdatesRequest), #[prost(message, tag = "53")] - GetOccasionalPlaytimeEngineUpdates( - super::GetOccasionalPlaytimeEngineUpdatesRequest, - ), + GetOccasionalPlaytimeEngineUpdates(super::GetOccasionalPlaytimeEngineUpdatesRequest), #[prost(message, tag = "40")] GetOccasionalInstanceUpdates(super::GetOccasionalInstanceUpdatesRequest), #[prost(message, tag = "25")] @@ -278,9 +276,7 @@ pub mod event_reply { #[prost(message, tag = "1")] OccasionalGlobalUpdatesReply(super::GetOccasionalGlobalUpdatesReply), #[prost(message, tag = "15")] - OccasionalPlaytimeEngineUpdatesReply( - super::GetOccasionalPlaytimeEngineUpdatesReply, - ), + OccasionalPlaytimeEngineUpdatesReply(super::GetOccasionalPlaytimeEngineUpdatesReply), #[prost(message, tag = "13")] OccasionalInstanceUpdatesReply(super::GetOccasionalInstanceUpdatesReply), #[prost(message, tag = "14")] @@ -1771,15 +1767,9 @@ impl TriggerInstanceAction { TriggerInstanceAction::ArrangementTogglePlayStop => { "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" } - TriggerInstanceAction::ArrangementPlay => { - "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_PLAY" - } - TriggerInstanceAction::ArrangementStop => { - "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_STOP" - } - TriggerInstanceAction::ArrangementPause => { - "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_PAUSE" - } + TriggerInstanceAction::ArrangementPlay => "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_PLAY", + TriggerInstanceAction::ArrangementStop => "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_STOP", + TriggerInstanceAction::ArrangementPause => "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_PAUSE", TriggerInstanceAction::ArrangementStartRecording => { "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_START_RECORDING" } @@ -1794,9 +1784,7 @@ impl TriggerInstanceAction { /// Creates an enum from field names used in the ProtoBuf definition. pub fn from_str_name(value: &str) -> ::core::option::Option { match value { - "TRIGGER_INSTANCE_ACTION_SHOW_HELGOBOX_PLUGIN" => { - Some(Self::ShowHelgoboxPlugin) - } + "TRIGGER_INSTANCE_ACTION_SHOW_HELGOBOX_PLUGIN" => Some(Self::ShowHelgoboxPlugin), "TRIGGER_INSTANCE_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" => { Some(Self::ArrangementTogglePlayStop) } @@ -2119,9 +2107,7 @@ impl TriggerTrackAction { TriggerTrackAction::ToggleArm => "TRIGGER_TRACK_ACTION_TOGGLE_ARM", TriggerTrackAction::ShowFx => "TRIGGER_TRACK_ACTION_SHOW_FX", TriggerTrackAction::ShowRouting => "TRIGGER_TRACK_ACTION_SHOW_ROUTING", - TriggerTrackAction::ToggleLearnInput => { - "TRIGGER_TRACK_ACTION_TOGGLE_LEARN_INPUT" - } + TriggerTrackAction::ToggleLearnInput => "TRIGGER_TRACK_ACTION_TOGGLE_LEARN_INPUT", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2170,9 +2156,7 @@ impl TriggerColumnAction { "TRIGGER_COLUMN_ACTION_REMOVE_SIMPLE_MAPPING" } TriggerColumnAction::Activate => "TRIGGER_COLUMN_ACTION_ACTIVATE", - TriggerColumnAction::ExportToClipboard => { - "TRIGGER_COLUMN_ACTION_EXPORT_TO_CLIPBOARD" - } + TriggerColumnAction::ExportToClipboard => "TRIGGER_COLUMN_ACTION_EXPORT_TO_CLIPBOARD", TriggerColumnAction::ExportToArrangement => { "TRIGGER_COLUMN_ACTION_EXPORT_TO_ARRANGEMENT" } @@ -2189,14 +2173,10 @@ impl TriggerColumnAction { "TRIGGER_COLUMN_ACTION_TOGGLE_LEARN_SIMPLE_MAPPING" => { Some(Self::ToggleLearnSimpleMapping) } - "TRIGGER_COLUMN_ACTION_REMOVE_SIMPLE_MAPPING" => { - Some(Self::RemoveSimpleMapping) - } + "TRIGGER_COLUMN_ACTION_REMOVE_SIMPLE_MAPPING" => Some(Self::RemoveSimpleMapping), "TRIGGER_COLUMN_ACTION_ACTIVATE" => Some(Self::Activate), "TRIGGER_COLUMN_ACTION_EXPORT_TO_CLIPBOARD" => Some(Self::ExportToClipboard), - "TRIGGER_COLUMN_ACTION_EXPORT_TO_ARRANGEMENT" => { - Some(Self::ExportToArrangement) - } + "TRIGGER_COLUMN_ACTION_EXPORT_TO_ARRANGEMENT" => Some(Self::ExportToArrangement), _ => None, } } @@ -2238,16 +2218,12 @@ impl TriggerRowAction { TriggerRowAction::ToggleLearnSimpleMapping => { "TRIGGER_ROW_ACTION_TOGGLE_LEARN_SIMPLE_MAPPING" } - TriggerRowAction::RemoveSimpleMapping => { - "TRIGGER_ROW_ACTION_REMOVE_SIMPLE_MAPPING" - } + TriggerRowAction::RemoveSimpleMapping => "TRIGGER_ROW_ACTION_REMOVE_SIMPLE_MAPPING", TriggerRowAction::BuildSceneFromPlayingSlots => { "TRIGGER_ROW_ACTION_BUILD_SCENE_FROM_PLAYING_SLOTS" } TriggerRowAction::Activate => "TRIGGER_ROW_ACTION_ACTIVATE", - TriggerRowAction::ExportToArrangement => { - "TRIGGER_ROW_ACTION_EXPORT_TO_ARRANGEMENT" - } + TriggerRowAction::ExportToArrangement => "TRIGGER_ROW_ACTION_EXPORT_TO_ARRANGEMENT", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2362,28 +2338,18 @@ impl TriggerSlotAction { TriggerSlotAction::Copy => "TRIGGER_SLOT_ACTION_COPY", TriggerSlotAction::Cut => "TRIGGER_SLOT_ACTION_CUT", TriggerSlotAction::Paste => "TRIGGER_SLOT_ACTION_PASTE", - TriggerSlotAction::ImportSelectedItems => { - "TRIGGER_SLOT_ACTION_IMPORT_SELECTED_ITEMS" - } + TriggerSlotAction::ImportSelectedItems => "TRIGGER_SLOT_ACTION_IMPORT_SELECTED_ITEMS", TriggerSlotAction::Panic => "TRIGGER_SLOT_ACTION_PANIC", - TriggerSlotAction::CreateEmptyMidiClip => { - "TRIGGER_SLOT_ACTION_CREATE_EMPTY_MIDI_CLIP" - } + TriggerSlotAction::CreateEmptyMidiClip => "TRIGGER_SLOT_ACTION_CREATE_EMPTY_MIDI_CLIP", TriggerSlotAction::ToggleLearnSimpleMapping => { "TRIGGER_SLOT_ACTION_TOGGLE_LEARN_SIMPLE_MAPPING" } - TriggerSlotAction::RemoveSimpleMapping => { - "TRIGGER_SLOT_ACTION_REMOVE_SIMPLE_MAPPING" - } + TriggerSlotAction::RemoveSimpleMapping => "TRIGGER_SLOT_ACTION_REMOVE_SIMPLE_MAPPING", TriggerSlotAction::TriggerOn => "TRIGGER_SLOT_ACTION_TRIGGER_ON", TriggerSlotAction::TriggerOff => "TRIGGER_SLOT_ACTION_TRIGGER_OFF", TriggerSlotAction::Activate => "TRIGGER_SLOT_ACTION_ACTIVATE", - TriggerSlotAction::ExportToClipboard => { - "TRIGGER_SLOT_ACTION_EXPORT_TO_CLIPBOARD" - } - TriggerSlotAction::ExportToArrangement => { - "TRIGGER_SLOT_ACTION_EXPORT_TO_ARRANGEMENT" - } + TriggerSlotAction::ExportToClipboard => "TRIGGER_SLOT_ACTION_EXPORT_TO_CLIPBOARD", + TriggerSlotAction::ExportToArrangement => "TRIGGER_SLOT_ACTION_EXPORT_TO_ARRANGEMENT", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2396,26 +2362,18 @@ impl TriggerSlotAction { "TRIGGER_SLOT_ACTION_COPY" => Some(Self::Copy), "TRIGGER_SLOT_ACTION_CUT" => Some(Self::Cut), "TRIGGER_SLOT_ACTION_PASTE" => Some(Self::Paste), - "TRIGGER_SLOT_ACTION_IMPORT_SELECTED_ITEMS" => { - Some(Self::ImportSelectedItems) - } + "TRIGGER_SLOT_ACTION_IMPORT_SELECTED_ITEMS" => Some(Self::ImportSelectedItems), "TRIGGER_SLOT_ACTION_PANIC" => Some(Self::Panic), - "TRIGGER_SLOT_ACTION_CREATE_EMPTY_MIDI_CLIP" => { - Some(Self::CreateEmptyMidiClip) - } + "TRIGGER_SLOT_ACTION_CREATE_EMPTY_MIDI_CLIP" => Some(Self::CreateEmptyMidiClip), "TRIGGER_SLOT_ACTION_TOGGLE_LEARN_SIMPLE_MAPPING" => { Some(Self::ToggleLearnSimpleMapping) } - "TRIGGER_SLOT_ACTION_REMOVE_SIMPLE_MAPPING" => { - Some(Self::RemoveSimpleMapping) - } + "TRIGGER_SLOT_ACTION_REMOVE_SIMPLE_MAPPING" => Some(Self::RemoveSimpleMapping), "TRIGGER_SLOT_ACTION_TRIGGER_ON" => Some(Self::TriggerOn), "TRIGGER_SLOT_ACTION_TRIGGER_OFF" => Some(Self::TriggerOff), "TRIGGER_SLOT_ACTION_ACTIVATE" => Some(Self::Activate), "TRIGGER_SLOT_ACTION_EXPORT_TO_CLIPBOARD" => Some(Self::ExportToClipboard), - "TRIGGER_SLOT_ACTION_EXPORT_TO_ARRANGEMENT" => { - Some(Self::ExportToArrangement) - } + "TRIGGER_SLOT_ACTION_EXPORT_TO_ARRANGEMENT" => Some(Self::ExportToArrangement), _ => None, } } @@ -2445,20 +2403,12 @@ impl TriggerClipAction { TriggerClipAction::Edit => "TRIGGER_CLIP_ACTION_EDIT", TriggerClipAction::Remove => "TRIGGER_CLIP_ACTION_REMOVE", TriggerClipAction::Promote => "TRIGGER_CLIP_ACTION_PROMOTE", - TriggerClipAction::OpenInMediaExplorer => { - "TRIGGER_CLIP_ACTION_OPEN_IN_MEDIA_EXPLORER" - } + TriggerClipAction::OpenInMediaExplorer => "TRIGGER_CLIP_ACTION_OPEN_IN_MEDIA_EXPLORER", TriggerClipAction::Quantize => "TRIGGER_CLIP_ACTION_QUANTIZE", TriggerClipAction::Unquantize => "TRIGGER_CLIP_ACTION_UNQUANTIZE", - TriggerClipAction::ExportToClipboard => { - "TRIGGER_CLIP_ACTION_EXPORT_TO_CLIPBOARD" - } - TriggerClipAction::ExportToArrangement => { - "TRIGGER_CLIP_ACTION_EXPORT_TO_ARRANGEMENT" - } - TriggerClipAction::ToggleMidiOverdub => { - "TRIGGER_CLIP_ACTION_TOGGLE_MIDI_OVERDUB" - } + TriggerClipAction::ExportToClipboard => "TRIGGER_CLIP_ACTION_EXPORT_TO_CLIPBOARD", + TriggerClipAction::ExportToArrangement => "TRIGGER_CLIP_ACTION_EXPORT_TO_ARRANGEMENT", + TriggerClipAction::ToggleMidiOverdub => "TRIGGER_CLIP_ACTION_TOGGLE_MIDI_OVERDUB", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2468,15 +2418,11 @@ impl TriggerClipAction { "TRIGGER_CLIP_ACTION_EDIT" => Some(Self::Edit), "TRIGGER_CLIP_ACTION_REMOVE" => Some(Self::Remove), "TRIGGER_CLIP_ACTION_PROMOTE" => Some(Self::Promote), - "TRIGGER_CLIP_ACTION_OPEN_IN_MEDIA_EXPLORER" => { - Some(Self::OpenInMediaExplorer) - } + "TRIGGER_CLIP_ACTION_OPEN_IN_MEDIA_EXPLORER" => Some(Self::OpenInMediaExplorer), "TRIGGER_CLIP_ACTION_QUANTIZE" => Some(Self::Quantize), "TRIGGER_CLIP_ACTION_UNQUANTIZE" => Some(Self::Unquantize), "TRIGGER_CLIP_ACTION_EXPORT_TO_CLIPBOARD" => Some(Self::ExportToClipboard), - "TRIGGER_CLIP_ACTION_EXPORT_TO_ARRANGEMENT" => { - Some(Self::ExportToArrangement) - } + "TRIGGER_CLIP_ACTION_EXPORT_TO_ARRANGEMENT" => Some(Self::ExportToArrangement), "TRIGGER_CLIP_ACTION_TOGGLE_MIDI_OVERDUB" => Some(Self::ToggleMidiOverdub), _ => None, } @@ -2669,17 +2615,11 @@ impl SlotPlayState { SlotPlayState::Unknown => "SLOT_PLAY_STATE_UNKNOWN", SlotPlayState::Stopped => "SLOT_PLAY_STATE_STOPPED", SlotPlayState::Ignited => "SLOT_PLAY_STATE_IGNITED", - SlotPlayState::ScheduledForPlayStart => { - "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_START" - } + SlotPlayState::ScheduledForPlayStart => "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_START", SlotPlayState::Playing => "SLOT_PLAY_STATE_PLAYING", SlotPlayState::Paused => "SLOT_PLAY_STATE_PAUSED", - SlotPlayState::ScheduledForPlayRestart => { - "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_RESTART" - } - SlotPlayState::ScheduledForPlayStop => { - "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_STOP" - } + SlotPlayState::ScheduledForPlayRestart => "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_RESTART", + SlotPlayState::ScheduledForPlayStop => "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_STOP", SlotPlayState::ScheduledForRecordingStart => { "SLOT_PLAY_STATE_SCHEDULED_FOR_RECORDING_START" } @@ -2695,22 +2635,16 @@ impl SlotPlayState { "SLOT_PLAY_STATE_UNKNOWN" => Some(Self::Unknown), "SLOT_PLAY_STATE_STOPPED" => Some(Self::Stopped), "SLOT_PLAY_STATE_IGNITED" => Some(Self::Ignited), - "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_START" => { - Some(Self::ScheduledForPlayStart) - } + "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_START" => Some(Self::ScheduledForPlayStart), "SLOT_PLAY_STATE_PLAYING" => Some(Self::Playing), "SLOT_PLAY_STATE_PAUSED" => Some(Self::Paused), - "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_RESTART" => { - Some(Self::ScheduledForPlayRestart) - } + "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_RESTART" => Some(Self::ScheduledForPlayRestart), "SLOT_PLAY_STATE_SCHEDULED_FOR_PLAY_STOP" => Some(Self::ScheduledForPlayStop), "SLOT_PLAY_STATE_SCHEDULED_FOR_RECORDING_START" => { Some(Self::ScheduledForRecordingStart) } "SLOT_PLAY_STATE_RECORDING" => Some(Self::Recording), - "SLOT_PLAY_STATE_SCHEDULED_FOR_RECORDING_STOP" => { - Some(Self::ScheduledForRecordingStop) - } + "SLOT_PLAY_STATE_SCHEDULED_FOR_RECORDING_STOP" => Some(Self::ScheduledForRecordingStop), _ => None, } } @@ -2730,9 +2664,7 @@ impl MidiDeviceStatus { pub fn as_str_name(&self) -> &'static str { match self { MidiDeviceStatus::Disconnected => "MIDI_DEVICE_STATUS_DISCONNECTED", - MidiDeviceStatus::ConnectedButDisabled => { - "MIDI_DEVICE_STATUS_CONNECTED_BUT_DISABLED" - } + MidiDeviceStatus::ConnectedButDisabled => "MIDI_DEVICE_STATUS_CONNECTED_BUT_DISABLED", MidiDeviceStatus::Connected => "MIDI_DEVICE_STATUS_CONNECTED", } } @@ -2740,9 +2672,7 @@ impl MidiDeviceStatus { pub fn from_str_name(value: &str) -> ::core::option::Option { match value { "MIDI_DEVICE_STATUS_DISCONNECTED" => Some(Self::Disconnected), - "MIDI_DEVICE_STATUS_CONNECTED_BUT_DISABLED" => { - Some(Self::ConnectedButDisabled) - } + "MIDI_DEVICE_STATUS_CONNECTED_BUT_DISABLED" => Some(Self::ConnectedButDisabled), "MIDI_DEVICE_STATUS_CONNECTED" => Some(Self::Connected), _ => None, } @@ -2768,13 +2698,9 @@ impl ArrangementPlayState { ArrangementPlayState::Unknown => "ARRANGEMENT_PLAY_STATE_UNKNOWN", ArrangementPlayState::Stopped => "ARRANGEMENT_PLAY_STATE_STOPPED", ArrangementPlayState::Playing => "ARRANGEMENT_PLAY_STATE_PLAYING", - ArrangementPlayState::PlayingPaused => { - "ARRANGEMENT_PLAY_STATE_PLAYING_PAUSED" - } + ArrangementPlayState::PlayingPaused => "ARRANGEMENT_PLAY_STATE_PLAYING_PAUSED", ArrangementPlayState::Recording => "ARRANGEMENT_PLAY_STATE_RECORDING", - ArrangementPlayState::RecordingPaused => { - "ARRANGEMENT_PLAY_STATE_RECORDING_PAUSED" - } + ArrangementPlayState::RecordingPaused => "ARRANGEMENT_PLAY_STATE_RECORDING_PAUSED", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2833,63 +2759,39 @@ pub mod helgobox_service_server { async fn get_host_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn prove_authenticity( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_app_settings( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// General instance queries async fn get_custom_instance_data( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Playtime matrix queries async fn get_project_dir( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_arrangement_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Playtime clip queries async fn get_clip_detail( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// ReaLearn compartment queries async fn get_compartment_data( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// General global commands async fn trigger_global( &self, @@ -3071,12 +2973,8 @@ pub mod helgobox_service_server { ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the GetOccasionalGlobalUpdates method. type GetOccasionalGlobalUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalGlobalUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// General global events async fn get_occasional_global_updates( @@ -3092,8 +2990,7 @@ pub mod helgobox_service_server { super::GetOccasionalPlaytimeEngineUpdatesReply, tonic::Status, >, - > - + Send + > + Send + 'static; /// Playtime global events async fn get_occasional_playtime_engine_updates( @@ -3105,12 +3002,8 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetOccasionalInstanceUpdates method. type GetOccasionalInstanceUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalInstanceUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// General instance events async fn get_occasional_instance_updates( @@ -3122,28 +3015,17 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetOccasionalUnitUpdates method. type GetOccasionalUnitUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalUnitUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; async fn get_occasional_unit_updates( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the GetOccasionalMatrixUpdates method. type GetOccasionalMatrixUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalMatrixUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime matrix events async fn get_occasional_matrix_updates( @@ -3155,12 +3037,8 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetContinuousMatrixUpdates method. type GetContinuousMatrixUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetContinuousMatrixUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; async fn get_continuous_matrix_updates( &self, @@ -3171,12 +3049,8 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetOccasionalColumnUpdates method. type GetOccasionalColumnUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalColumnUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime column events async fn get_occasional_column_updates( @@ -3188,12 +3062,8 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetContinuousColumnUpdates method. type GetContinuousColumnUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetContinuousColumnUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; async fn get_continuous_column_updates( &self, @@ -3204,12 +3074,8 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetOccasionalTrackUpdates method. type GetOccasionalTrackUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalTrackUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime track events async fn get_occasional_track_updates( @@ -3221,71 +3087,43 @@ pub mod helgobox_service_server { >; /// Server streaming response type for the GetOccasionalRowUpdates method. type GetOccasionalRowUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalRowUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime row events async fn get_occasional_row_updates( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the GetOccasionalSlotUpdates method. type GetOccasionalSlotUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalSlotUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime slot events async fn get_occasional_slot_updates( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the GetContinuousSlotUpdates method. type GetContinuousSlotUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetContinuousSlotUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; async fn get_continuous_slot_updates( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the GetOccasionalClipUpdates method. type GetOccasionalClipUpdatesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOccasionalClipUpdatesReply, - tonic::Status, - >, - > - + Send + Item = std::result::Result, + > + Send + 'static; /// Playtime clip events async fn get_occasional_clip_updates( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct HelgoboxServiceServer { @@ -3310,10 +3148,7 @@ pub mod helgobox_service_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -3369,15 +3204,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetHostInfo" => { #[allow(non_camel_case_types)] struct GetHostInfoSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetHostInfoSvc { + impl tonic::server::UnaryService + for GetHostInfoSvc + { type Response = super::GetHostInfoReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -3415,23 +3246,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/ProveAuthenticity" => { #[allow(non_camel_case_types)] struct ProveAuthenticitySvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for ProveAuthenticitySvc { + impl + tonic::server::UnaryService + for ProveAuthenticitySvc + { type Response = super::ProveAuthenticityReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::prove_authenticity(&inner, request) - .await + ::prove_authenticity(&inner, request).await }; Box::pin(fut) } @@ -3462,23 +3289,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetAppSettings" => { #[allow(non_camel_case_types)] struct GetAppSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetAppSettingsSvc { + impl + tonic::server::UnaryService + for GetAppSettingsSvc + { type Response = super::GetAppSettingsReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_app_settings(&inner, request) - .await + ::get_app_settings(&inner, request).await }; Box::pin(fut) } @@ -3509,25 +3332,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetCustomInstanceData" => { #[allow(non_camel_case_types)] struct GetCustomInstanceDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetCustomInstanceDataSvc { + impl + tonic::server::UnaryService + for GetCustomInstanceDataSvc + { type Response = super::GetCustomInstanceDataReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_custom_instance_data( - &inner, - request, - ) + ::get_custom_instance_data(&inner, request) .await }; Box::pin(fut) @@ -3559,23 +3376,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetProjectDir" => { #[allow(non_camel_case_types)] struct GetProjectDirSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetProjectDirSvc { + impl + tonic::server::UnaryService + for GetProjectDirSvc + { type Response = super::GetProjectDirReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_project_dir(&inner, request) - .await + ::get_project_dir(&inner, request).await }; Box::pin(fut) } @@ -3606,26 +3419,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetArrangementInfo" => { #[allow(non_camel_case_types)] struct GetArrangementInfoSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetArrangementInfoSvc { + impl + tonic::server::UnaryService + for GetArrangementInfoSvc + { type Response = super::GetArrangementInfoReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_arrangement_info( - &inner, - request, - ) - .await + ::get_arrangement_info(&inner, request).await }; Box::pin(fut) } @@ -3656,23 +3462,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetClipDetail" => { #[allow(non_camel_case_types)] struct GetClipDetailSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetClipDetailSvc { + impl + tonic::server::UnaryService + for GetClipDetailSvc + { type Response = super::GetClipDetailReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_clip_detail(&inner, request) - .await + ::get_clip_detail(&inner, request).await }; Box::pin(fut) } @@ -3703,26 +3505,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetCompartmentData" => { #[allow(non_camel_case_types)] struct GetCompartmentDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for GetCompartmentDataSvc { + impl + tonic::server::UnaryService + for GetCompartmentDataSvc + { type Response = super::GetCompartmentDataReply; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_compartment_data( - &inner, - request, - ) - .await + ::get_compartment_data(&inner, request).await }; Box::pin(fut) } @@ -3753,23 +3548,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerGlobal" => { #[allow(non_camel_case_types)] struct TriggerGlobalSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerGlobalSvc { + impl + tonic::server::UnaryService + for TriggerGlobalSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::trigger_global(&inner, request) - .await + ::trigger_global(&inner, request).await }; Box::pin(fut) } @@ -3800,23 +3591,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetAppSettings" => { #[allow(non_camel_case_types)] struct SetAppSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetAppSettingsSvc { + impl + tonic::server::UnaryService + for SetAppSettingsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_app_settings(&inner, request) - .await + ::set_app_settings(&inner, request).await }; Box::pin(fut) } @@ -3847,15 +3634,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/AddLicense" => { #[allow(non_camel_case_types)] struct AddLicenseSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for AddLicenseSvc { + impl tonic::server::UnaryService + for AddLicenseSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -3893,23 +3676,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SaveController" => { #[allow(non_camel_case_types)] struct SaveControllerSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SaveControllerSvc { + impl + tonic::server::UnaryService + for SaveControllerSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::save_controller(&inner, request) - .await + ::save_controller(&inner, request).await }; Box::pin(fut) } @@ -3940,23 +3719,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/DeleteController" => { #[allow(non_camel_case_types)] struct DeleteControllerSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for DeleteControllerSvc { + impl + tonic::server::UnaryService + for DeleteControllerSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::delete_controller(&inner, request) - .await + ::delete_controller(&inner, request).await }; Box::pin(fut) } @@ -3987,29 +3762,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetPlaytimeEngineSettings" => { #[allow(non_camel_case_types)] struct SetPlaytimeEngineSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService< - super::SetPlaytimeEngineSettingsRequest, - > for SetPlaytimeEngineSettingsSvc { + impl + tonic::server::UnaryService + for SetPlaytimeEngineSettingsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::SetPlaytimeEngineSettingsRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::set_playtime_engine_settings( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -4040,23 +3808,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerInstance" => { #[allow(non_camel_case_types)] struct TriggerInstanceSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerInstanceSvc { + impl + tonic::server::UnaryService + for TriggerInstanceSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::trigger_instance(&inner, request) - .await + ::trigger_instance(&inner, request).await }; Box::pin(fut) } @@ -4087,26 +3851,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetInstanceSettings" => { #[allow(non_camel_case_types)] struct SetInstanceSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetInstanceSettingsSvc { + impl + tonic::server::UnaryService + for SetInstanceSettingsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_instance_settings( - &inner, - request, - ) - .await + ::set_instance_settings(&inner, request).await }; Box::pin(fut) } @@ -4137,25 +3894,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetCustomInstanceData" => { #[allow(non_camel_case_types)] struct SetCustomInstanceDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetCustomInstanceDataSvc { + impl + tonic::server::UnaryService + for SetCustomInstanceDataSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_custom_instance_data( - &inner, - request, - ) + ::set_custom_instance_data(&inner, request) .await }; Box::pin(fut) @@ -4187,23 +3938,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerMatrix" => { #[allow(non_camel_case_types)] struct TriggerMatrixSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerMatrixSvc { + impl + tonic::server::UnaryService + for TriggerMatrixSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::trigger_matrix(&inner, request) - .await + ::trigger_matrix(&inner, request).await }; Box::pin(fut) } @@ -4234,23 +3981,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixSettings" => { #[allow(non_camel_case_types)] struct SetMatrixSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixSettingsSvc { + impl + tonic::server::UnaryService + for SetMatrixSettingsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_settings(&inner, request) - .await + ::set_matrix_settings(&inner, request).await }; Box::pin(fut) } @@ -4281,23 +4024,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixTempo" => { #[allow(non_camel_case_types)] struct SetMatrixTempoSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixTempoSvc { + impl + tonic::server::UnaryService + for SetMatrixTempoSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_tempo(&inner, request) - .await + ::set_matrix_tempo(&inner, request).await }; Box::pin(fut) } @@ -4328,25 +4067,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixTimeSignature" => { #[allow(non_camel_case_types)] struct SetMatrixTimeSignatureSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixTimeSignatureSvc { + impl + tonic::server::UnaryService + for SetMatrixTimeSignatureSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_time_signature( - &inner, - request, - ) + ::set_matrix_time_signature(&inner, request) .await }; Box::pin(fut) @@ -4378,26 +4111,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixPlayRate" => { #[allow(non_camel_case_types)] struct SetMatrixPlayRateSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixPlayRateSvc { + impl + tonic::server::UnaryService + for SetMatrixPlayRateSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_play_rate( - &inner, - request, - ) - .await + ::set_matrix_play_rate(&inner, request).await }; Box::pin(fut) } @@ -4428,23 +4154,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixVolume" => { #[allow(non_camel_case_types)] struct SetMatrixVolumeSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixVolumeSvc { + impl + tonic::server::UnaryService + for SetMatrixVolumeSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_volume(&inner, request) - .await + ::set_matrix_volume(&inner, request).await }; Box::pin(fut) } @@ -4475,23 +4197,18 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetMatrixPan" => { #[allow(non_camel_case_types)] struct SetMatrixPanSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetMatrixPanSvc { + impl tonic::server::UnaryService + for SetMatrixPanSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_matrix_pan(&inner, request) - .await + ::set_matrix_pan(&inner, request).await }; Box::pin(fut) } @@ -4522,23 +4239,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerColumn" => { #[allow(non_camel_case_types)] struct TriggerColumnSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerColumnSvc { + impl + tonic::server::UnaryService + for TriggerColumnSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::trigger_column(&inner, request) - .await + ::trigger_column(&inner, request).await }; Box::pin(fut) } @@ -4569,23 +4282,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/InsertColumns" => { #[allow(non_camel_case_types)] struct InsertColumnsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for InsertColumnsSvc { + impl + tonic::server::UnaryService + for InsertColumnsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::insert_columns(&inner, request) - .await + ::insert_columns(&inner, request).await }; Box::pin(fut) } @@ -4616,23 +4325,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetColumnSettings" => { #[allow(non_camel_case_types)] struct SetColumnSettingsSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetColumnSettingsSvc { + impl + tonic::server::UnaryService + for SetColumnSettingsSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_column_settings(&inner, request) - .await + ::set_column_settings(&inner, request).await }; Box::pin(fut) } @@ -4663,23 +4368,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetColumnTrack" => { #[allow(non_camel_case_types)] struct SetColumnTrackSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetColumnTrackSvc { + impl + tonic::server::UnaryService + for SetColumnTrackSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_column_track(&inner, request) - .await + ::set_column_track(&inner, request).await }; Box::pin(fut) } @@ -4710,15 +4411,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/DragColumn" => { #[allow(non_camel_case_types)] struct DragColumnSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for DragColumnSvc { + impl tonic::server::UnaryService + for DragColumnSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -4756,15 +4453,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerTrack" => { #[allow(non_camel_case_types)] struct TriggerTrackSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerTrackSvc { + impl tonic::server::UnaryService + for TriggerTrackSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -4802,23 +4495,18 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackName" => { #[allow(non_camel_case_types)] struct SetTrackNameSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackNameSvc { + impl tonic::server::UnaryService + for SetTrackNameSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_track_name(&inner, request) - .await + ::set_track_name(&inner, request).await }; Box::pin(fut) } @@ -4849,23 +4537,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackColor" => { #[allow(non_camel_case_types)] struct SetTrackColorSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackColorSvc { + impl + tonic::server::UnaryService + for SetTrackColorSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_track_color(&inner, request) - .await + ::set_track_color(&inner, request).await }; Box::pin(fut) } @@ -4896,23 +4580,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackInput" => { #[allow(non_camel_case_types)] struct SetTrackInputSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackInputSvc { + impl + tonic::server::UnaryService + for SetTrackInputSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_track_input(&inner, request) - .await + ::set_track_input(&inner, request).await }; Box::pin(fut) } @@ -4943,27 +4623,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackInputMonitoring" => { #[allow(non_camel_case_types)] struct SetTrackInputMonitoringSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackInputMonitoringSvc { + impl + tonic::server::UnaryService + for SetTrackInputMonitoringSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::SetTrackInputMonitoringRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_track_input_monitoring( - &inner, - request, - ) + ::set_track_input_monitoring(&inner, request) .await }; Box::pin(fut) @@ -4995,23 +4667,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackVolume" => { #[allow(non_camel_case_types)] struct SetTrackVolumeSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackVolumeSvc { + impl + tonic::server::UnaryService + for SetTrackVolumeSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_track_volume(&inner, request) - .await + ::set_track_volume(&inner, request).await }; Box::pin(fut) } @@ -5042,15 +4710,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetTrackPan" => { #[allow(non_camel_case_types)] struct SetTrackPanSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetTrackPanSvc { + impl tonic::server::UnaryService + for SetTrackPanSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5088,15 +4752,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/OpenTrackFx" => { #[allow(non_camel_case_types)] struct OpenTrackFxSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for OpenTrackFxSvc { + impl tonic::server::UnaryService + for OpenTrackFxSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5134,15 +4794,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerRow" => { #[allow(non_camel_case_types)] struct TriggerRowSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerRowSvc { + impl tonic::server::UnaryService + for TriggerRowSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5180,15 +4836,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetRowData" => { #[allow(non_camel_case_types)] struct SetRowDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetRowDataSvc { + impl tonic::server::UnaryService + for SetRowDataSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5226,15 +4878,9 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/DragRow" => { #[allow(non_camel_case_types)] struct DragRowSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for DragRowSvc { + impl tonic::server::UnaryService for DragRowSvc { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5272,15 +4918,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerSlot" => { #[allow(non_camel_case_types)] struct TriggerSlotSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerSlotSvc { + impl tonic::server::UnaryService + for TriggerSlotSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5318,15 +4960,9 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/DragSlot" => { #[allow(non_camel_case_types)] struct DragSlotSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for DragSlotSvc { + impl tonic::server::UnaryService for DragSlotSvc { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5364,15 +5000,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/ImportFiles" => { #[allow(non_camel_case_types)] struct ImportFilesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for ImportFilesSvc { + impl tonic::server::UnaryService + for ImportFilesSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5410,15 +5042,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerClip" => { #[allow(non_camel_case_types)] struct TriggerClipSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerClipSvc { + impl tonic::server::UnaryService + for TriggerClipSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5456,15 +5084,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetClipName" => { #[allow(non_camel_case_types)] struct SetClipNameSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetClipNameSvc { + impl tonic::server::UnaryService + for SetClipNameSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5502,15 +5126,11 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetClipData" => { #[allow(non_camel_case_types)] struct SetClipDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetClipDataSvc { + impl tonic::server::UnaryService + for SetClipDataSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5548,15 +5168,9 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/DragClip" => { #[allow(non_camel_case_types)] struct DragClipSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for DragClipSvc { + impl tonic::server::UnaryService for DragClipSvc { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -5594,23 +5208,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/TriggerSequence" => { #[allow(non_camel_case_types)] struct TriggerSequenceSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for TriggerSequenceSvc { + impl + tonic::server::UnaryService + for TriggerSequenceSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::trigger_sequence(&inner, request) - .await + ::trigger_sequence(&inner, request).await }; Box::pin(fut) } @@ -5641,23 +5251,19 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SetSequenceInfo" => { #[allow(non_camel_case_types)] struct SetSequenceInfoSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService - for SetSequenceInfoSvc { + impl + tonic::server::UnaryService + for SetSequenceInfoSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::set_sequence_info(&inner, request) - .await + ::set_sequence_info(&inner, request).await }; Box::pin(fut) } @@ -5688,29 +5294,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/SaveCustomCompartmentData" => { #[allow(non_camel_case_types)] struct SaveCustomCompartmentDataSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::UnaryService< - super::SaveCustomCompartmentDataRequest, - > for SaveCustomCompartmentDataSvc { + impl + tonic::server::UnaryService + for SaveCustomCompartmentDataSvc + { type Response = super::Empty; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::SaveCustomCompartmentDataRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::save_custom_compartment_data( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -5741,30 +5340,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalGlobalUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalGlobalUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalGlobalUpdatesRequest, - > for GetOccasionalGlobalUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalGlobalUpdatesRequest, + > for GetOccasionalGlobalUpdatesSvc + { type Response = super::GetOccasionalGlobalUpdatesReply; type ResponseStream = T::GetOccasionalGlobalUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalGlobalUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_global_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -5794,20 +5388,16 @@ pub mod helgobox_service_server { } "/generated.HelgoboxService/GetOccasionalPlaytimeEngineUpdates" => { #[allow(non_camel_case_types)] - struct GetOccasionalPlaytimeEngineUpdatesSvc( - pub Arc, - ); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalPlaytimeEngineUpdatesRequest, - > for GetOccasionalPlaytimeEngineUpdatesSvc { + struct GetOccasionalPlaytimeEngineUpdatesSvc(pub Arc); + impl + tonic::server::ServerStreamingService< + super::GetOccasionalPlaytimeEngineUpdatesRequest, + > for GetOccasionalPlaytimeEngineUpdatesSvc + { type Response = super::GetOccasionalPlaytimeEngineUpdatesReply; type ResponseStream = T::GetOccasionalPlaytimeEngineUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request< @@ -5817,10 +5407,9 @@ pub mod helgobox_service_server { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_playtime_engine_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -5850,33 +5439,26 @@ pub mod helgobox_service_server { } "/generated.HelgoboxService/GetOccasionalInstanceUpdates" => { #[allow(non_camel_case_types)] - struct GetOccasionalInstanceUpdatesSvc( - pub Arc, - ); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalInstanceUpdatesRequest, - > for GetOccasionalInstanceUpdatesSvc { + struct GetOccasionalInstanceUpdatesSvc(pub Arc); + impl + tonic::server::ServerStreamingService< + super::GetOccasionalInstanceUpdatesRequest, + > for GetOccasionalInstanceUpdatesSvc + { type Response = super::GetOccasionalInstanceUpdatesReply; type ResponseStream = T::GetOccasionalInstanceUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalInstanceUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_instance_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -5907,29 +5489,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalUnitUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalUnitUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalUnitUpdatesRequest, - > for GetOccasionalUnitUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalUnitUpdatesRequest, + > for GetOccasionalUnitUpdatesSvc + { type Response = super::GetOccasionalUnitUpdatesReply; type ResponseStream = T::GetOccasionalUnitUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalUnitUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_occasional_unit_updates( - &inner, - request, - ) + ::get_occasional_unit_updates(&inner, request) .await }; Box::pin(fut) @@ -5961,30 +5536,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalMatrixUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalMatrixUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalMatrixUpdatesRequest, - > for GetOccasionalMatrixUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalMatrixUpdatesRequest, + > for GetOccasionalMatrixUpdatesSvc + { type Response = super::GetOccasionalMatrixUpdatesReply; type ResponseStream = T::GetOccasionalMatrixUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalMatrixUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_matrix_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -6015,30 +5585,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetContinuousMatrixUpdates" => { #[allow(non_camel_case_types)] struct GetContinuousMatrixUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetContinuousMatrixUpdatesRequest, - > for GetContinuousMatrixUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetContinuousMatrixUpdatesRequest, + > for GetContinuousMatrixUpdatesSvc + { type Response = super::GetContinuousMatrixUpdatesReply; type ResponseStream = T::GetContinuousMatrixUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetContinuousMatrixUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_continuous_matrix_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -6069,30 +5634,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalColumnUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalColumnUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalColumnUpdatesRequest, - > for GetOccasionalColumnUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalColumnUpdatesRequest, + > for GetOccasionalColumnUpdatesSvc + { type Response = super::GetOccasionalColumnUpdatesReply; type ResponseStream = T::GetOccasionalColumnUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalColumnUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_column_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -6123,30 +5683,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetContinuousColumnUpdates" => { #[allow(non_camel_case_types)] struct GetContinuousColumnUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetContinuousColumnUpdatesRequest, - > for GetContinuousColumnUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetContinuousColumnUpdatesRequest, + > for GetContinuousColumnUpdatesSvc + { type Response = super::GetContinuousColumnUpdatesReply; type ResponseStream = T::GetContinuousColumnUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetContinuousColumnUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_continuous_column_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -6177,30 +5732,25 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalTrackUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalTrackUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalTrackUpdatesRequest, - > for GetOccasionalTrackUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalTrackUpdatesRequest, + > for GetOccasionalTrackUpdatesSvc + { type Response = super::GetOccasionalTrackUpdatesReply; type ResponseStream = T::GetOccasionalTrackUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalTrackUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { ::get_occasional_track_updates( - &inner, - request, - ) - .await + &inner, request, + ) + .await }; Box::pin(fut) } @@ -6231,29 +5781,21 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalRowUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalRowUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalRowUpdatesRequest, - > for GetOccasionalRowUpdatesSvc { + impl + tonic::server::ServerStreamingService + for GetOccasionalRowUpdatesSvc + { type Response = super::GetOccasionalRowUpdatesReply; type ResponseStream = T::GetOccasionalRowUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalRowUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_occasional_row_updates( - &inner, - request, - ) + ::get_occasional_row_updates(&inner, request) .await }; Box::pin(fut) @@ -6285,29 +5827,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalSlotUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalSlotUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalSlotUpdatesRequest, - > for GetOccasionalSlotUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalSlotUpdatesRequest, + > for GetOccasionalSlotUpdatesSvc + { type Response = super::GetOccasionalSlotUpdatesReply; type ResponseStream = T::GetOccasionalSlotUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalSlotUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_occasional_slot_updates( - &inner, - request, - ) + ::get_occasional_slot_updates(&inner, request) .await }; Box::pin(fut) @@ -6339,29 +5874,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetContinuousSlotUpdates" => { #[allow(non_camel_case_types)] struct GetContinuousSlotUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetContinuousSlotUpdatesRequest, - > for GetContinuousSlotUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetContinuousSlotUpdatesRequest, + > for GetContinuousSlotUpdatesSvc + { type Response = super::GetContinuousSlotUpdatesReply; type ResponseStream = T::GetContinuousSlotUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetContinuousSlotUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_continuous_slot_updates( - &inner, - request, - ) + ::get_continuous_slot_updates(&inner, request) .await }; Box::pin(fut) @@ -6393,29 +5921,22 @@ pub mod helgobox_service_server { "/generated.HelgoboxService/GetOccasionalClipUpdates" => { #[allow(non_camel_case_types)] struct GetOccasionalClipUpdatesSvc(pub Arc); - impl< - T: HelgoboxService, - > tonic::server::ServerStreamingService< - super::GetOccasionalClipUpdatesRequest, - > for GetOccasionalClipUpdatesSvc { + impl + tonic::server::ServerStreamingService< + super::GetOccasionalClipUpdatesRequest, + > for GetOccasionalClipUpdatesSvc + { type Response = super::GetOccasionalClipUpdatesReply; type ResponseStream = T::GetOccasionalClipUpdatesStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = + BoxFuture, tonic::Status>; fn call( &mut self, - request: tonic::Request< - super::GetOccasionalClipUpdatesRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_occasional_clip_updates( - &inner, - request, - ) + ::get_occasional_clip_updates(&inner, request) .await }; Box::pin(fut) @@ -6444,18 +5965,14 @@ pub mod helgobox_service_server { }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } diff --git a/main/src/infrastructure/proto/hub.rs b/main/src/infrastructure/proto/hub.rs index b075c644b..627f2764d 100644 --- a/main/src/infrastructure/proto/hub.rs +++ b/main/src/infrastructure/proto/hub.rs @@ -1,6 +1,6 @@ use reaper_high::ChangeEvent; -use realearn_api::runtime::{GlobalInfoEvent, InstanceInfoEvent}; +use helgobox_api::runtime::{GlobalInfoEvent, InstanceInfoEvent}; use crate::application::UnitModel; use crate::domain::{InstanceId, UnitId}; diff --git a/main/src/infrastructure/proto/request_handler.rs b/main/src/infrastructure/proto/request_handler.rs index 2a9772013..c0b1afa33 100644 --- a/main/src/infrastructure/proto/request_handler.rs +++ b/main/src/infrastructure/proto/request_handler.rs @@ -5,7 +5,7 @@ use reaper_medium::CommandId; use tonic::{Response, Status}; use base::spawn_in_main_thread; -use realearn_api::runtime::InstanceInfoEvent; +use helgobox_api::runtime::InstanceInfoEvent; use swell_ui::Window; use crate::domain::{CompartmentKind, UnitId}; diff --git a/main/src/infrastructure/ui/egui_views/target_filter_panel.rs b/main/src/infrastructure/ui/egui_views/target_filter_panel.rs index f2b6d57cf..e9a5bd472 100644 --- a/main/src/infrastructure/ui/egui_views/target_filter_panel.rs +++ b/main/src/infrastructure/ui/egui_views/target_filter_panel.rs @@ -4,7 +4,7 @@ use base::{NamedChannelSender, SenderToNormalThread}; use derivative::Derivative; use egui::CentralPanel; use egui::Context; -use realearn_api::persistence::{LearnableTargetKind, TargetTouchCause}; +use helgobox_api::persistence::{LearnableTargetKind, TargetTouchCause}; use strum::IntoEnumIterator; pub fn run_ui(ctx: &Context, state: &mut State) { diff --git a/main/src/infrastructure/ui/header_panel.rs b/main/src/infrastructure/ui/header_panel.rs index db8555ed7..ef423302c 100644 --- a/main/src/infrastructure/ui/header_panel.rs +++ b/main/src/infrastructure/ui/header_panel.rs @@ -60,8 +60,8 @@ use crate::infrastructure::ui::{ }; use crate::infrastructure::ui::{dialog_util, CompanionAppPresenter}; use anyhow::{bail, Context}; +use helgobox_api::persistence::{Envelope, VirtualControlElementCharacter}; use itertools::Itertools; -use realearn_api::persistence::{Envelope, VirtualControlElementCharacter}; use reaper_medium::Hbrush; use semver::Version; use std::cell::{Cell, RefCell}; @@ -260,10 +260,7 @@ impl HeaderPanel { use swell_ui::menu_tree::*; anonymous_menu(vec![ item("Open in browser (old)", false), - item( - "Open in app (new, but temporarily only works for Playtime testers)", - true, - ), + item("Open in app (new)", true), ]) }; self.view @@ -652,9 +649,9 @@ impl HeaderPanel { )], ), item("Open Pot Browser", MainMenuAction::OpenPotBrowser), - item("Show App (not usable yet)", MainMenuAction::ShowApp), + item("Show App", MainMenuAction::ShowApp), item_with_opts( - "Close App (not usable yet)", + "Close App", ItemOpts { enabled: app_is_open, checked: false, @@ -847,18 +844,26 @@ impl HeaderPanel { let pure_menu = { use swell_ui::menu_tree::*; let entries = vec![ - item( - "User guide for this version (PDF, offline)", - HelpMenuAction::OpenOfflineUserGuide, - ), - item( - "User guide for latest version (HTML, online)", - HelpMenuAction::OpenOnlineUserGuide, + menu( + "ReaLearn", + vec![ + item( + "User guide for this version (PDF, offline)", + HelpMenuAction::OpenRealearnOfflineUserGuide, + ), + item( + "User guide for latest version (HTML, online)", + HelpMenuAction::OpenRealearnOnlineUserGuide, + ), + item( + "List of controllers", + HelpMenuAction::OpenRealearnControllerList, + ), + item("Forum", HelpMenuAction::OpenRealearnForum), + item("Website", HelpMenuAction::OpenRealearnWebsite), + ], ), - item("List of controllers", HelpMenuAction::OpenControllerList), - item("Forum", HelpMenuAction::OpenForum), item("Contact developer", HelpMenuAction::ContactDeveloper), - item("Website", HelpMenuAction::OpenWebsite), item("Donate", HelpMenuAction::Donate), ]; anonymous_menu(entries) @@ -869,12 +874,12 @@ impl HeaderPanel { .open_popup_menu(pure_menu, location) .ok_or("no entry selected")?; match result { - HelpMenuAction::OpenOfflineUserGuide => self.open_user_guide_offline(), - HelpMenuAction::OpenOnlineUserGuide => self.open_user_guide_online(), - HelpMenuAction::OpenControllerList => self.open_controller_list(), - HelpMenuAction::OpenForum => self.open_forum(), + HelpMenuAction::OpenRealearnOfflineUserGuide => self.open_realearn_user_guide_offline(), + HelpMenuAction::OpenRealearnOnlineUserGuide => self.open_realearn_user_guide_online(), + HelpMenuAction::OpenRealearnControllerList => self.open_realearn_controller_list(), + HelpMenuAction::OpenRealearnForum => self.open_realearn_forum(), HelpMenuAction::ContactDeveloper => self.contact_developer(), - HelpMenuAction::OpenWebsite => self.open_website(), + HelpMenuAction::OpenRealearnWebsite => self.open_realearn_website(), HelpMenuAction::Donate => self.donate(), }; Ok(()) @@ -2413,7 +2418,7 @@ impl HeaderPanel { BackboneShell::get().log_debug_info(session.unit_key()); } - fn open_user_guide_offline(&self) { + fn open_realearn_user_guide_offline(&self) { let user_guide_pdf = BackboneShell::realearn_data_dir_path().join("doc/realearn-user-guide.pdf"); if open::that(user_guide_pdf).is_err() { @@ -2424,11 +2429,13 @@ impl HeaderPanel { } } - fn open_user_guide_online(&self) { - open_in_browser("https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc"); + fn open_realearn_user_guide_online(&self) { + open_in_browser( + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc", + ); } - fn open_controller_list(&self) { + fn open_realearn_controller_list(&self) { open_in_browser("https://github.com/helgoboss/realearn/blob/master/doc/controllers.adoc"); } @@ -2436,7 +2443,7 @@ impl HeaderPanel { open_in_browser("https://paypal.me/helgoboss"); } - fn open_forum(&self) { + fn open_realearn_forum(&self) { open_in_browser("https://forum.cockos.com/showthread.php?t=178015"); } @@ -2444,7 +2451,7 @@ impl HeaderPanel { open_in_browser("mailto:info@helgoboss.org"); } - fn open_website(&self) { + fn open_realearn_website(&self) { open_in_browser("https://www.helgoboss.org/projects/realearn/"); } @@ -3007,12 +3014,12 @@ enum MainMenuAction { } enum HelpMenuAction { - OpenOfflineUserGuide, - OpenOnlineUserGuide, - OpenControllerList, - OpenForum, + OpenRealearnOfflineUserGuide, + OpenRealearnOnlineUserGuide, + OpenRealearnControllerList, + OpenRealearnForum, + OpenRealearnWebsite, ContactDeveloper, - OpenWebsite, Donate, } diff --git a/main/src/infrastructure/ui/import.rs b/main/src/infrastructure/ui/import.rs index 8f3fff46f..ebcff9608 100644 --- a/main/src/infrastructure/ui/import.rs +++ b/main/src/infrastructure/ui/import.rs @@ -19,11 +19,11 @@ use crate::infrastructure::plugin::BackboneShell; use crate::infrastructure::ui::lua_serializer; use crate::infrastructure::ui::util::open_in_browser; use base::hash_util::NonCryptoHashSet; +use helgobox_api::persistence; +use helgobox_api::persistence::{ApiObject, CommonPresetMetaData, Envelope}; use mlua::prelude::LuaError; use mlua::Value; use playtime_api::persistence::FlexibleMatrix; -use realearn_api::persistence; -use realearn_api::persistence::{ApiObject, CommonPresetMetaData, Envelope}; use reaper_high::Reaper; use semver::Version; diff --git a/main/src/infrastructure/ui/mapping_panel.rs b/main/src/infrastructure/ui/mapping_panel.rs index b7b661fb7..4243845f5 100644 --- a/main/src/infrastructure/ui/mapping_panel.rs +++ b/main/src/infrastructure/ui/mapping_panel.rs @@ -27,7 +27,7 @@ use helgoboss_learn::{ ModeParameter, OscTypeTag, OutOfRangeBehavior, PercentIo, RgbColor, SourceCharacter, TakeoverMode, Target, UnitValue, ValueSequence, VirtualColor, DEFAULT_OSC_ARG_VALUE_RANGE, }; -use realearn_api::persistence::{ +use helgobox_api::persistence::{ Axis, BrowseTracksMode, FxDescriptor, FxToolAction, LearnableTargetKind, MidiScriptKind, MonitoringMode, MouseButton, PlaytimeColumnAction, PlaytimeColumnDescriptor, PlaytimeColumnDescriptorKind, PlaytimeMatrixAction, PlaytimeRowAction, PlaytimeRowDescriptor, @@ -819,7 +819,7 @@ impl MappingPanel { let session = self.session.clone(); let engine = Box::new(RawMidiScriptEngine); let help_url = - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#midi-send-message"; + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#midi-send-message"; self.edit_script_in_simple_editor( engine, help_url, @@ -1089,7 +1089,7 @@ impl MappingPanel { let session = self.session.clone(); let engine = Box::new(RawMidiScriptEngine); let help_url = - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#raw-midi-source"; + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#raw-midi-source"; self.edit_script_in_simple_editor( engine, help_url, @@ -1145,7 +1145,7 @@ impl MappingPanel { fn edit_control_transformation(&self) { let session = self.session.clone(); let engine = Box::new(EelControlTransformationEngine); - let help_url = "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#control-transformation"; + let help_url = "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#control-transformation"; let get_value = |m: &MappingModel| m.mode_model.eel_control_transformation().to_owned(); let set_value = move |m: &mut MappingModel, eel: String| { UnitModel::change_mapping_from_ui_simple( @@ -1175,7 +1175,7 @@ impl MappingPanel { let session = self.session.clone(); self.edit_script_in_simple_editor( Box::new(EelFeedbackTransformationEngine), - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#feedback-type", + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#feedback-type", |m| m.mode_model.eel_feedback_transformation().to_owned(), move |m, eel| { UnitModel::change_mapping_from_ui_simple( @@ -1192,7 +1192,7 @@ impl MappingPanel { let session = self.session.clone(); self.edit_script_in_simple_editor( Box::new(TextualFeedbackExpressionEngine), - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#feedback-type", + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#feedback-type", |m| m.mode_model.textual_feedback_expression().to_owned(), move |m, eel| { UnitModel::change_mapping_from_ui_simple( @@ -1209,7 +1209,7 @@ impl MappingPanel { let session = self.session.clone(); self.edit_script_in_simple_editor( Box::new(LuaFeedbackScriptEngine::new()), - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#feedback-type", + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#feedback-type", |m| m.mode_model.textual_feedback_expression().to_owned(), move |m, eel| { UnitModel::change_mapping_from_ui_simple( @@ -1229,7 +1229,7 @@ impl MappingPanel { ) { let engine = Box::new(OscFeedbackArgumentsEngine); let help_url = - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#feedback-arguments"; + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#feedback-arguments"; self.edit_script_in_simple_editor(engine, help_url, get_initial_value, apply); } @@ -1244,7 +1244,7 @@ impl MappingPanel { MidiScriptKind::Lua => Box::new(LuaMidiScriptEngine::new()), }; let help_url = - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#script-source"; + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#script-source"; self.edit_script_in_simple_editor(engine, help_url, get_initial_value, apply); } @@ -3428,7 +3428,7 @@ impl<'a> MutableMappingPanel<'a> { self.panel.set_simple_help_text( Side::Right, "Target warning", - r#"ATTENTION: You just picked a parameter for the last focused FX. This is okay but you should know that as soon as you focus another type of FX, the parameter list will change and your mapping will control a completely different parameter! You probably want to use this in combination with the "Auto-load" feature, which lets you link FX types to mapping presets. See https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#using-auto-load-to-control-whatever-plug-in-is-currently-in-focus."# + r#"ATTENTION: You just picked a parameter for the last focused FX. This is okay but you should know that as soon as you focus another type of FX, the parameter list will change and your mapping will control a completely different parameter! You probably want to use this in combination with the "Auto-load" feature, which lets you link FX types to mapping presets. See https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#using-auto-load-to-control-whatever-plug-in-is-currently-in-focus."# ); } } diff --git a/main/src/infrastructure/ui/mapping_row_panel.rs b/main/src/infrastructure/ui/mapping_row_panel.rs index f3b35f3fd..840bc99c5 100644 --- a/main/src/infrastructure/ui/mapping_row_panel.rs +++ b/main/src/infrastructure/ui/mapping_row_panel.rs @@ -28,7 +28,7 @@ use crate::infrastructure::ui::{ SerializationFormat, SharedMainState, }; use core::iter; -use realearn_api::persistence::{ApiObject, Envelope}; +use helgobox_api::persistence::{ApiObject, Envelope}; use reaper_medium::Hbrush; use rxrust::prelude::*; use std::cell::{Ref, RefCell}; diff --git a/main/src/infrastructure/ui/mapping_rows_panel.rs b/main/src/infrastructure/ui/mapping_rows_panel.rs index db05b981a..d848358cf 100644 --- a/main/src/infrastructure/ui/mapping_rows_panel.rs +++ b/main/src/infrastructure/ui/mapping_rows_panel.rs @@ -7,7 +7,7 @@ use crate::infrastructure::ui::{ DataObject, IndependentPanelManager, MainState, MappingRowPanel, ScrollStatus, SharedIndependentPanelManager, SharedMainState, }; -use realearn_api::persistence::Envelope; +use helgobox_api::persistence::Envelope; use reaper_high::Reaper; use reaper_low::raw; use rxrust::prelude::*; @@ -39,7 +39,7 @@ impl MappingRowsPanel { main_state: SharedMainState, position: Point, ) -> MappingRowsPanel { - let row_count = realearn_dialogs::constants::MAPPING_ROW_COUNT; + let row_count = helgobox_dialogs::constants::MAPPING_ROW_COUNT; MappingRowsPanel { view: Default::default(), rows: (0..row_count) diff --git a/main/src/infrastructure/ui/menus.rs b/main/src/infrastructure/ui/menus.rs index a723bee10..36e17b9eb 100644 --- a/main/src/infrastructure/ui/menus.rs +++ b/main/src/infrastructure/ui/menus.rs @@ -13,7 +13,7 @@ use reaper_high::{FxChainContext, MidiInputDevice, MidiOutputDevice, Reaper}; use base::hash_util::NonCryptoIndexMap; use derive_more::Display; -use realearn_api::persistence::VirtualControlElementCharacter; +use helgobox_api::persistence::VirtualControlElementCharacter; use reaper_medium::ReaperString; use std::iter; use strum::IntoEnumIterator; diff --git a/main/src/infrastructure/ui/session_message_panel.rs b/main/src/infrastructure/ui/session_message_panel.rs index ef9a26e8b..96cebbbb1 100644 --- a/main/src/infrastructure/ui/session_message_panel.rs +++ b/main/src/infrastructure/ui/session_message_panel.rs @@ -2,7 +2,7 @@ use crate::application::{LearnManySubState, SharedUnitModel, WeakUnitModel}; use crate::base::when; use crate::domain::CompartmentKind; use crate::infrastructure::ui::bindings::root; -use realearn_api::persistence::VirtualControlElementCharacter; +use helgobox_api::persistence::VirtualControlElementCharacter; use reaper_low::raw; use rxrust::prelude::*; use std::rc::Rc; diff --git a/main/src/infrastructure/ui/unit_panel.rs b/main/src/infrastructure/ui/unit_panel.rs index f17ee8c93..2cbcfdbd6 100644 --- a/main/src/infrastructure/ui/unit_panel.rs +++ b/main/src/infrastructure/ui/unit_panel.rs @@ -27,8 +27,8 @@ use crate::infrastructure::ui::instance_panel::InstancePanel; use crate::infrastructure::ui::util::{header_panel_height, parse_tags_from_csv}; use anyhow::Context; use base::{Global, SoundPlayer}; -use helgoboss_allocator::undesired_allocation_count; -use realearn_api::runtime::InstanceInfoEvent; +use helgobox_allocator::undesired_allocation_count; +use helgobox_api::runtime::InstanceInfoEvent; use rxrust::prelude::*; use std::rc::{Rc, Weak}; use swell_ui::{DialogUnits, Point, SharedView, View, ViewContext, WeakView, Window}; diff --git a/main/src/infrastructure/ui/util.rs b/main/src/infrastructure/ui/util.rs index 83f411c78..73b1982c3 100644 --- a/main/src/infrastructure/ui/util.rs +++ b/main/src/infrastructure/ui/util.rs @@ -1,7 +1,7 @@ use crate::application::UnitModel; use crate::domain::{compartment_param_index_iter, CompartmentKind, Tag}; use crate::infrastructure::ui::bindings::root; -use realearn_dialogs::constants; +use helgobox_dialogs::constants; use reaper_high::Reaper; use std::cell::RefCell; use std::path::Path; diff --git a/main/src/infrastructure/ui/yaml_editor_panel.rs b/main/src/infrastructure/ui/yaml_editor_panel.rs index eb4badbd4..2c8ed6777 100644 --- a/main/src/infrastructure/ui/yaml_editor_panel.rs +++ b/main/src/infrastructure/ui/yaml_editor_panel.rs @@ -150,6 +150,6 @@ impl View for YamlEditorPanel { fn help() { open_in_browser( - "https://github.com/helgoboss/realearn/blob/master/doc/user-guide.adoc#advanced-settings", + "https://github.com/helgoboss/realearn/blob/master/doc/realearn-user-guide.adoc#advanced-settings", ); } diff --git a/playtime-api/Cargo.toml b/playtime-api/Cargo.toml index 9f55291bb..c6febcbdf 100644 --- a/playtime-api/Cargo.toml +++ b/playtime-api/Cargo.toml @@ -12,7 +12,7 @@ reaper-common-types.workspace = true # For exposing a runtime API within REAPER reaper-low.workspace = true # For being able to use the API macro -realearn-macros.workspace = true +helgobox-macros.workspace = true # For generating random IDs nanoid.workspace = true # For easier Display impl diff --git a/playtime-api/src/runtime/reaper.rs b/playtime-api/src/runtime/reaper.rs index 46b6a6da8..7cd3238a5 100644 --- a/playtime-api/src/runtime/reaper.rs +++ b/playtime-api/src/runtime/reaper.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case)] -use realearn_macros::reaper_api; +use helgobox_macros::reaper_api; reaper_api![ PlaytimeApi, PlaytimeApiPointers, PlaytimeApiSession, register_playtime_api diff --git a/playtime-clip-engine b/playtime-clip-engine index 007c20ad7..459dfedce 160000 --- a/playtime-clip-engine +++ b/playtime-clip-engine @@ -1 +1 @@ -Subproject commit 007c20ad7032962f43fd8671ded7c2f680c9e2c5 +Subproject commit 459dfedced6af509a2008b7f6f30784cd328d695 diff --git a/pot-browser/Cargo.toml b/pot-browser/Cargo.toml index 9a77a0871..a3eb20455 100644 --- a/pot-browser/Cargo.toml +++ b/pot-browser/Cargo.toml @@ -11,7 +11,7 @@ pot.workspace = true reaper-high.workspace = true reaper-medium.workspace = true base.workspace = true -realearn-api.workspace = true +helgobox-api.workspace = true # 3rd-party egui.workspace = true diff --git a/pot-browser/src/pot_browser_panel.rs b/pot-browser/src/pot_browser_panel.rs index 2428f6da2..e5d036ffb 100644 --- a/pot-browser/src/pot_browser_panel.rs +++ b/pot-browser/src/pot_browser_panel.rs @@ -16,6 +16,7 @@ use egui::{ use egui::{Context, SidePanel}; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use egui_toast::Toasts; +use helgobox_api::persistence::PotFilterKind; use lru::LruCache; use pot::preset_crawler::{ crawl_presets, import_crawled_presets, CrawlPresetArgs, PresetCrawlerStopReason, @@ -36,7 +37,6 @@ use pot::{ WorkerDispatcher, }; use pot::{FilterItemId, PresetId}; -use realearn_api::persistence::PotFilterKind; use reaper_high::{Fx, FxParameter, Reaper, SliderVolume, Track}; use reaper_medium::{ReaperNormalizedFxParamValue, ReaperVolumeValue}; use std::borrow::Cow; diff --git a/pot/Cargo.toml b/pot/Cargo.toml index a94fbe93e..d876cdb70 100644 --- a/pot/Cargo.toml +++ b/pot/Cargo.toml @@ -10,7 +10,7 @@ publish = false base.workspace = true reaper-high.workspace = true reaper-medium.workspace = true -realearn-api.workspace = true +helgobox-api.workspace = true rppxml-parser.workspace = true # 3rd-party @@ -41,7 +41,7 @@ tempfile.workspace = true sanitize-filename = "0.5.0" tracing.workspace = true regex.workspace = true -helgoboss-allocator.workspace = true +helgobox-allocator.workspace = true nanoid.workspace = true chrono.workspace = true anyhow.workspace = true diff --git a/pot/src/api.rs b/pot/src/api.rs index 4504e8b59..cbe7c40f7 100644 --- a/pot/src/api.rs +++ b/pot/src/api.rs @@ -6,8 +6,8 @@ use crate::provider_database::{ use crate::{FilterItem, PotPreset}; use enum_map::EnumMap; use enumset::EnumSet; +use helgobox_api::persistence::PotFilterKind; use once_cell::sync::Lazy; -use realearn_api::persistence::PotFilterKind; use std::collections::HashSet; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; diff --git a/pot/src/lib.rs b/pot/src/lib.rs index a1c430c19..62f91c8ac 100644 --- a/pot/src/lib.rs +++ b/pot/src/lib.rs @@ -9,7 +9,7 @@ use base::{blocking_lock, blocking_lock_arc, blocking_write_lock, hash_util, SoundPlayer}; use enumset::EnumSet; -use realearn_api::persistence::PotFilterKind; +use helgobox_api::persistence::PotFilterKind; use reaper_high::{Chunk, Fx, FxChain, GroupingBehavior, Project, Reaper, Track}; use reaper_medium::{ reaper_str, FxPresetRef, GangBehavior, InputMonitoringMode, InsertMediaMode, diff --git a/pot/src/pot_database.rs b/pot/src/pot_database.rs index b769ca65f..f2f1f8ac4 100644 --- a/pot/src/pot_database.rs +++ b/pot/src/pot_database.rs @@ -19,7 +19,7 @@ use crate::providers::defaults::DefaultsDatabase; use crate::providers::ini::IniDatabase; use enumset::{enum_set, EnumSet}; -use realearn_api::persistence::PotFilterKind; +use helgobox_api::persistence::PotFilterKind; use reaper_high::Reaper; use std::collections::{BTreeMap, HashSet}; use std::error::Error; diff --git a/pot/src/preset_crawler.rs b/pot/src/preset_crawler.rs index 71a554985..3d139fdba 100644 --- a/pot/src/preset_crawler.rs +++ b/pot/src/preset_crawler.rs @@ -7,7 +7,7 @@ use base::hash_util::NonCryptoIndexMap; use base::{blocking_lock_arc, file_util, hash_util}; use base::{Mouse, MouseCursorPosition}; use camino::{Utf8Path, Utf8PathBuf}; -use realearn_api::persistence::MouseButton; +use helgobox_api::persistence::MouseButton; use reaper_high::{Fx, FxInfo, Reaper}; use std::error::Error; use std::fs; diff --git a/pot/src/preview_recorder.rs b/pot/src/preview_recorder.rs index 269419708..b6fb81d58 100644 --- a/pot/src/preview_recorder.rs +++ b/pot/src/preview_recorder.rs @@ -10,7 +10,7 @@ use base::future_util::millis; use base::hash_util::PersistentHash; use base::{blocking_lock_arc, blocking_write_lock, file_util}; use camino::{Utf8Path, Utf8PathBuf}; -use realearn_api::persistence::PotFilterKind; +use helgobox_api::persistence::PotFilterKind; use reaper_high::{Project, Reaper}; use reaper_medium::{CommandId, OpenProjectBehavior, ProjectContext, ProjectInfoAttributeKey}; use std::error::Error; diff --git a/pot/src/provider_database.rs b/pot/src/provider_database.rs index 270146915..84b1e105f 100644 --- a/pot/src/provider_database.rs +++ b/pot/src/provider_database.rs @@ -4,7 +4,7 @@ use crate::{ InnerPresetId, PersistentDatabaseId, PotPreset, ProductId, }; use enumset::{enum_set, EnumSet}; -use realearn_api::persistence::PotFilterKind; +use helgobox_api::persistence::PotFilterKind; use std::borrow::Cow; use std::error::Error; diff --git a/pot/src/providers/defaults.rs b/pot/src/providers/defaults.rs index 729f57174..d9bed8971 100644 --- a/pot/src/providers/defaults.rs +++ b/pot/src/providers/defaults.rs @@ -11,8 +11,8 @@ use std::borrow::Cow; use crate::plugins::PluginCommon; use either::Either; use enumset::{enum_set, EnumSet}; +use helgobox_api::persistence::PotFilterKind; use itertools::Itertools; -use realearn_api::persistence::PotFilterKind; use std::error::Error; use std::iter; diff --git a/pot/src/providers/directory.rs b/pot/src/providers/directory.rs index 65aab22f7..5a64e76c1 100644 --- a/pot/src/providers/directory.rs +++ b/pot/src/providers/directory.rs @@ -13,8 +13,8 @@ use base::hash_util::{NonCryptoHashSet, NonCryptoIndexMap, PersistentHash, Persi use camino::Utf8PathBuf; use either::Either; use enumset::{enum_set, EnumSet}; +use helgobox_api::persistence::PotFilterKind; use itertools::Itertools; -use realearn_api::persistence::PotFilterKind; use std::error::Error; use std::ffi::OsStr; use std::fs::File; diff --git a/pot/src/providers/ini.rs b/pot/src/providers/ini.rs index dec489f35..4bf5d59e5 100644 --- a/pot/src/providers/ini.rs +++ b/pot/src/providers/ini.rs @@ -13,9 +13,9 @@ use base::hash_util::{PersistentHash, PersistentHasher}; use camino::Utf8PathBuf; use either::Either; use enumset::{enum_set, EnumSet}; +use helgobox_api::persistence::PotFilterKind; use ini::Ini; use itertools::Itertools; -use realearn_api::persistence::PotFilterKind; use std::error::Error; use std::hash::Hasher; use std::iter; diff --git a/pot/src/providers/komplete.rs b/pot/src/providers/komplete.rs index ea1f39adc..9c640d010 100644 --- a/pot/src/providers/komplete.rs +++ b/pot/src/providers/komplete.rs @@ -13,7 +13,7 @@ use crate::{ use crate::{FilterItem, FilterItemId, Filters, MacroParam, ParamAssignment, PluginId}; use base::blocking_lock; use enumset::{enum_set, EnumSet}; -use realearn_api::persistence::PotFilterKind; +use helgobox_api::persistence::PotFilterKind; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; use camino::{Utf8Path, Utf8PathBuf}; diff --git a/pot/src/providers/projects.rs b/pot/src/providers/projects.rs index 18e488f59..6c14a8629 100644 --- a/pot/src/providers/projects.rs +++ b/pot/src/providers/projects.rs @@ -14,8 +14,8 @@ use base::hash_util::{ }; use either::Either; use enumset::{enum_set, EnumSet}; +use helgobox_api::persistence::PotFilterKind; use itertools::Itertools; -use realearn_api::persistence::PotFilterKind; use std::error::Error; use std::ffi::OsStr;