Reorganize top-level directories with clearer naming convention

app -> app-desktop-macos, presets -> app-presets, server -> remote-server,
daw-config-reaper -> osc-config-daw, plugin-reaper-realearn -> plugin-reaper-relearn.
Updated run.py and presets.py path references accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Paul Lipscomb
2026-07-15 17:51:05 -04:00
parent e58f06d9fa
commit 7ecc718f5d
2256 changed files with 11 additions and 5 deletions
@@ -0,0 +1,484 @@
= Compartment concepts
[[compartment-preset]]
== Compartment preset
=== Main presets vs. controller presets
[[main-preset]]
==== Main preset
The term _main preset_ is just a shortcut for saying "compartment preset in the main compartment".
The number of factory _main_ presets is relatively small because ReaLearn is all about enabling you to quickly come up with your own customized main mappings. However, a few interesting main presets are available link:https://github.com/helgoboss/helgobox/wiki/ReaLearn-Interesting-Presets[in the Wiki].
[[controller-preset]]
==== Controller preset
The term _controller preset_ is just a shortcut for saying "compartment preset in the controller compartment".
.Mix and match!
TIP: The big advantage of separating controller presets from main presets is that you can freely combine them on a mix-and-match basis.
This enables you to get out-of-the-box access to all kinds of interesting control scenarios with potentially very different controllers.
=== Factory preset vs. User preset
[[factory-compartment-preset]]
==== Factory preset
Factory presets are built-in compartment presets.
Characteristics:
* You can't change them.
* If you want to change a factory preset, you can make a copy of it, either by pressing btn:[Save as...] or by <<writing-presets-with-luau, creating your personal Lua workspace>>.
* It's always possible that factory presets are changed in future ReaLearn versions.
+
.No worries
NOTE: This is not a problem for existing compartments. A changed preset will *only* be applied to the compartment if you select the preset again!
+
.Preset stability
TIP: Whenever you like a certain factory preset and want it to remain the same, even if you select it again, it's best if you make your own copy of it. The simplest way to do this is to press btn:[Save as...].
[[user-compartment-preset]]
==== User preset
User presets are made by users, for example by you.
* Saving your mappings as a preset is optional.
All controller mappings are saved together with your current ReaLearn unit anyway, no worries.
But as soon as you want to reuse these mappings in other ReaLearn unit or for xref:further-concepts/unit.adoc#auto-load[], it makes of course sense to save them as a preset!
* All of your presets end up in the REAPER resource directory (REAPER → Options → Show REAPER resource path in explorer/finder) at
`Data/helgoboss/realearn/presets` followed by `main` (for main compartment presets) or `controller` (for controller compartment presets).
They are JSON files and very similar to what you get when you press
_Export to clipboard_.
* They can even be in a subdirectory.
Please note that the subdirectory name becomes a part of the preset ID, so better don't move existing presets around if you want preset references of existing ReaLearn units to stay intact.
* JSON files can also contain <<custom-data>>.
[#writing-presets-with-luau]
=== Writing presets with Luau
It is possible to write compartment presets with the link:https://luau.org/[Luau language] instead of building them via the user interface.
Many of the more complex ReaLearn factory presets are written in Lua, e.g. the "DAW control" preset.
A good way to get started writing Luau presets is to create your personal compartment preset user workspace.
A preset workspace is a subdirectory within the compartment preset parent directory that may contain a bunch of presets and other files.
Important facts about preset workspaces/namespaces:
* It may contain both Luau presets (`.preset.luau`) and conventional JSON presets (`.json`)!
* The name of the workspace (subdirectory) is at the same time the first part of the preset ID.
For example, if the subdirectory name is `helgoboss` and it contains a preset file `my-preset.json`, the final ID of that preset will be `helgoboss/my-preset`.
* That also means that presets from different workspaces never conflict with each other.
* Therefore, a preset "workspace" is at the same time a preset "namespace".
Those terms are sometimes used interchangeably.
* It's important that the ID of a preset doesn't change, especially if you want to use that preset with xref:further-concepts/unit.adoc#auto-load[].
If you change the ID, it's another preset from ReaLearn's perspective!
* Conversely, the name of the workspace directory and the name/path of the preset file within the workspace directory should not change!
* The only thing that is allowed to change is the file extension.
This makes it possible to convert a preset from JSON to Luau and vice versa.
* Preset workspaces are self-contained.
What does that mean?
Luau presets can use the `require` statement to share common Luau code.
However, this is only possible within one preset workspace.
* As a result, it is safe to have multiple completely different preset workspace, and it's guaranteed that they don't conflict with each other.
This makes preset sharing easy (it's just a matter of copying the preset workspace directory).
* There's one special preset workspace: The _user workspace_.
It's the workspace whose directory has the same name as your macOS/Windows/Linux user.
Special features:
** The user workspace is where ReaLearn puts your presets when you save them via the user interface (as `.json` files).
** All `require` statements in Luau code imported via *Import from clipboard* are resolved against this user workspace.
You can create a preset workspace by pressing *Menu → Compartment presets → Create compartment preset workspace (including factory presets)* (done for each compartment type separately).
This will create a randomly-named preset workspace directory within the compartment preset parent directory.
If this is your first preset workspace, it is best practice to turn it into your personal _user workspace_ by renaming the generated directory to your macOS/Windows/Linux username (name must match exactly!).
Maybe the user workspace directory exists already.
Most likely because you have saved presets from the user interface, in which case it should contain only JSON files.
In that case you can safely move all files and directories from the generated preset workspace directory into that existing directory.
The generated workspace contains:
* A README file with some general information and tips.
* A copy of all ReaLearn factory presets for that compartment.
** Mainly Luau presets (ending with `.preset.luau`).
** You can use them as inspiration for your own ones.
** Most of the factory presets in the main compartment are quite advanced.
One of the easier ones is
`generic/numbered/fx-paraameters.preset.luau`.
* A bunch of Luau SDK files in the first directory level of the workspace.
** They contain Luau types and utility functions.
** You can require them within your own Luau files in that workspace and use them to build presets.
** However, the usage of the SDK files is completely optional!
The only important thing about building ReaLearn presets is that the returned table conforms to the ReaLearn compartment API (= has the structure that you get when you do *Export from clipboard → Export ... compartment as Lua*).
It doesn't matter if you use Luau's type system to build that table or the provided utility functions or your own or none.
** The SDK files can change in incompatible ways in newer ReaLearn versions.
Only ReaLearn's built-in compartment API is guaranteed to stay backward-compatible!
Luau presets have a YAML frontmatter comment section right at the top of the file that contain meta information about the preset.
The following properties are possible:
[cols="m,1,3"]
|===
|
name
|
required
|
Preset display name
|
realearn_version
|
required
|
The ReaLearn version for which this preset was built.
This can effect the way the preset is loaded, e.g. it can lead to different interpretation
or migration of properties. So care should be taken to set this correctly!
|
author
|
|
Preset author
|
description
|
|
Preset description.
Preferably in link:https://en.wikipedia.org/wiki/Markdown[Markdown] format, but can also be plain text.
|
setup_instructions
|
|
Setup instructions.
Preferably in link:https://en.wikipedia.org/wiki/Markdown[Markdown] format, but can also be plain text.
|
device_manufacturer
|
controller compartment only
|
Manufacturer of the device represented by the controller preset.
|
device_name
|
controller compartment only
|
Name of the device represented by the controller preset.
|
midi_identity_pattern
|
controller compartment only
|
MIDI identity compatibility pattern.
Will be used for auto-adding controllers and for finding the correct controller preset when calculating auto-units.
|
midi_output_port_patterns
|
controller compartment only
|
Possible MIDI identity compatibility patterns.
Will be used for auto-adding controllers and for finding the correct controller preset when calculating auto-units.
It should only be provided if the device in question doesn't reply to device queries or if it exposes
multiple ports which all respond with the same device identity and only one of the ports is the correct one.
Example: APC Key 25 mk2, which exposes a "Control" and a "Keys" port.
ReaLearn will match any in the list. OS-prefixes are alowed, e.g. `macos:` will only match on macOS.
|
provided_schemes
|
controller compartment only
|
Provided virtual control schemes.
Will be used for finding the correct controller preset when calculating auto units.
The order matters! It directly influences the choice of the best-suited main presets. In particular, schemes that are more specific to this particular controller (e.g. "novation/launchpad-mk3") should come first. Generic schemes (e.g. "grid") should come last. When auto-picking a main preset, matches of more specific schemes will be favored over less specific ones.
|
used_schemes
|
main compartment only
|
Used virtual control schemes.
Will be used for finding the correct controller preset when calculating auto units.
|
required_features
|
main compartment only
|
A set of features that a Helgobox instance needs to provide for the preset to make sense.
Will be used for determining whether an auto unit should be created for a specific instance or not. Example: If the required feature is "playtime" and a controller is configured with
this main preset but the instance doesn't contain a Playtime Matrix, this instance will not load the main preset.
Currently, only feature `playtime` is supported, which matches if the Helgobox instance contains a Playtime Matrix.
|===
[#compartment-parameter]
== Compartment parameter
Each ReaLearn compartment contains 100 freely assignable parameters.
Compartment parameters can be used in the following ways:
* For xref:further-concepts/mapping.adoc#conditional-activation[] (lets the parameter value influence which mappings are active)
* For xref:further-concepts/target.adoc#dynamic-selector[] (lets the parameter value influence which object, such as track or FX, is targeted for a specific mapping)
* As xref:sources/reaper/realearn-parameter.adoc[] (lets the parameter value control any ReaLearn target)
They can be customized as described in xref:user-interface/main-panel/menu-bar.adoc#compartment-parameters[].
Parameter customizations are saved together with the compartment preset.
Parameter values will be reset whenever you load a preset (just the ones in that compartment).
[[continuous-vs-discrete-compartment-parameters]]
=== Continuous vs. discrete compartment parameters
By default, <<compartment-parameter,compartment parameters>> have a xref:further-concepts/target.adoc#continuous-value-range[].
Although that makes them very versatile, it's often easier to work with a xref:further-concepts/target.adoc#discrete-value-range[].
Entering a value count (see xref:user-interface/main-panel/menu-bar.adoc#set-compartment-parameter-value-count[]) turns this into a parameter with a xref:further-concepts/target.adoc#discrete-value-range[], 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!*
You probably want to refer to values of this parameter in certain parts of ReaLearn, e.g. in xref:user-interface/mapping-panel/glue-section.adoc#target-min-max[].
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!
====
[[compartment-wide-lua-code]]
== Compartment-wide Lua code
Each compartment may contain arbitrary Luau code to be reused by multiple mapping MIDI source and feedback scripts.
This avoids code duplication and decreases memory usage.
It even allows the usage of shared state.
You can set the compartment-wide Lua code using xref:user-interface/main-panel/menu-bar.adoc#edit-compartment-wide-lua-code[].
The code that you provide here is treated as a module that xref:further-concepts/source.adoc#midi-source-script[MIDI source scripts] and xref:further-concepts/glue.adoc#dynamic-feedback[Feedback scripts] can import using `require("compartment")`.
That means you need to export everything that you want the MIDI source and feedback scripts to see, simply by returning it.
.Compartment-wide Lua code
====
The following compartment-wide Lua code exports 2 functions named `get_text` and `get_number`:
[source,lua]
----
local module = {}
local function private_function()
return "i'm private"
end
function module.get_text()
return "hello world"
end
function module.get_number()
return 5
end
return module
----
These functions can then be reused in MIDI source and feedback scripts:
[source,lua]
----
local compartment = require("compartment")
local text = compartment.get_text()
local number = compartment.get_number()
----
====
Compartment-wide Lua code is part of the compartment, that means it's also saved as part of a compartment preset!
[[virtual-control]]
== Virtual control
_Virtual control_ makes it possible to create <<main-preset,main presets>>
that can be reused with many different xref:key-concepts.adoc#controller[controllers].
The idea is simple:
1. You define a <<controller-preset>> for a DAW controller, mapping each <<real-control-element>> (e.g. its first fader, which emits MIDI CC7 messages) to a corresponding <<virtual-control-element>> (e.g. named `ch1/fader`) by using a xref:further-concepts/target.adoc#virtual-target[].
2. You define a <<main-preset>>, mapping each <<virtual-control-element>> to some xref:further-concepts/target.adoc#real-target[] by using a xref:further-concepts/source.adoc#virtual-source[].
For example, you map `ch1/fader` to the xref:targets/track/set-volume.adoc[].
3. ReaLearn creates a sort of wire between the xref:key-concepts.adoc#controller-compartment[] and the xref:key-concepts.adoc#main-compartment[].
So you can now control the track volume by moving the first fader.
4. Most importantly, the main preset is now generic because it's not built for a specific controller anymore!
See xref:best-practices.adoc#using-the-controller-compartment[] for more information how to do this in detail!
[[virtual-feedback]]
== Virtual feedback
_Virtual feedback_ is just like <<virtual-control>>, but in the opposite direction (from REAPER to your xref:key-concepts.adoc#controller[]).
== Real vs. virtual control elements
Wait ... control elements that are not real!?
Yes!
In ReaLearn, they exist.
We can distinguish between <<real-control-element>> and <<virtual-control-element>>.
[[real-control-element]]
=== Real control element
A _real_ control element is an element that really exists on a xref:key-concepts.adoc#controller[], e.g. a fader that you can touch.
[[virtual-control-element]]
=== Virtual control element
A _virtual_ control element is an abstraction of a <<real-control-element>>.
It enables <<virtual-control>> and <<virtual-feedback>>.
Each virtual control element has a _type_ and an _ID_.
[[virtual-control-element-id]]
==== Virtual control element ID
A number or name that uniquely identifies the control element on the device.
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.
You can use up to 32 alphanumeric and punctuation characters (no exotic characters, e.g. no umlauts).
[[virtual-control-element-type]]
==== Virtual control element type
If you want to define a virtual control element, you should first decide which type is should have: _Multi_ or _Button_.
This distinction is used by ReaLearn to optimize its user interface.
IMPORTANT: For numbered control elements, the type is even part of the <<virtual-control-element-id>>.
For example, "Multi 1" is considered a different virtual control element than "Button 1".
For named control elements, this is not the case. `col1/row1/pad` defined as Multi is considered the same as `col1/row1/pad` defined as Button.
[[virtual-control-element-type-multi]] 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
* Rotary (endless) encoder
* Velocity-sensitive pads or keys
[[virtual-control-element-type-button]] 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 (a simple on/off one, not a half-pedaling one!)
Please note that velocity-sensitive keys should be exposed as <<virtual-control-element-type-button>> - unless you know for sure that you are not interested in the velocity sensitivity.
[[virtual-control-scheme]]
==== Virtual control scheme
===== Introduction
Mixing and matching controller and main presets only works as long as controller and main presets share the same philosophy and virtual control element naming/numbering.
IMPORTANT: A main preset is not automagically compatible with any controller preset!
As you can imagine, there are far too many types of controllers and control scenarios out there to make this work in all cases.
However, ReaLearn provides many means to get close to this ideal.
To make it easier for users to know which main presets are compatible with which controller presets, ReaLearn has the concept of _virtual control schemes_. At the moment, ReaLearn uses the following schemes:
DAW control (`daw`)::
Control elements that are typically used for controlling a DAW.
The control elements names available in this scheme are heavily inspired by the wording used on Mackie Control devices, e.g. `ch1/fader`.
Grid (`grid`)::
Control elements that are laid out in columns and rows, as for example found on the Novation Launchpad. Typical control element names are `col1/row2/pad`.
Numbered (`numbered`)::
Control elements that are laid out as a simple row or column. The names available in this scheme are simply numbers, e.g. from 1 to 8.
The general idea is:
* A **controller preset** _provides_ an arbitrary number of schemes.
** E.g. the Akai APC Key 25 provides control elements of all the mentioned schemes.
** A controller should not double-expose one single control element under different virtual control element names.
* A **main preset** _uses_ an arbitrary number of schemes.
** E.g. they could assign a track volume change to both the numbered multi control element 5 (_Numbered_ scheme) and to the named multi control element `ch5/fader` (_DAW_ scheme) by using 2 mappings with the same target.
Then we say this preset uses both schemes.
===== How main presets can leverage virtual control schemes
There are roughly 3 kinds of main presets:
. **Controller-specific main presets without virtual sources:** They are built for one specific controller and use MIDI/OSC sources directly.
. **Controller-specific main presets with virtual sources:** They are built for one specific controller but primarily use virtual sources (e.g. in order to take advantage of ReaLearn's projection feature).
. **Reusable main presets:** Thy are built for all controllers that provide a specific control scheme.
===== Usage
If you want your main preset to be compatible with as many controller presets as possible, try to use predefined control element names instead of coming up with your own way of naming things!
When you define a virtual source or virtual target, there's a convenient picker that provides names for the following standardized virtual control schemes.
[[custom-data]]
== Custom compartment data
Each xref:key-concepts.adoc#compartment[] in ReaLearn can carry _custom data_. That is arbitrary structured (JSON-like) data which can be used in certain ways.
I know that's a quite generic description. It's best to give some examples:
* ReaLearn's xref:further-concepts/unit.adoc#projection[projection feature] uses custom compartment data within the xref:key-concepts.adoc#controller-compartment[] in order to memorize the positions and shapes of all control elements.
* Playtime's xref:playtime::further-concepts/matrix.adoc#control-unit[] feature reads custom compartment data within the xref:key-concepts.adoc#main-compartment[] in order to know the size of the connected xref:playtime::key-concepts.adoc#grid-controller[].
Such custom data can't directly be edited in ReaLearn's xref:user-interface.adoc[]. But you can read it when dealing with JSON or Lua chunks, e.g. when <<writing-presets-with-luau>>.
== Order in which mappings are processed
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 xref:user-interface/mapping-panel/glue-section.adoc#fire-mode[fire mode] 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.
@@ -0,0 +1,55 @@
= General concepts
[[import-export]]
== Import/export
ReaLearn can import and export data in 2 formats:
link: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 link:https://en.wikipedia.org/wiki/Text_editor[text editor], this is one way to do batch editing.
link:https://luau.org/[Luau]::
A full-blown programming language derived from the famous Lua language that is also used in REAPER itself.
[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)
----
====
[#feedback-relay]
== Feedback relay
Feedback _relay_ happens when a feedback-enabled mapping becomes inactive and another feedback-enabled mapping with the same xref:key-concepts.adoc#source[] becomes active. In that case, ReaLearn has to swap the displayed value of the previous mapping target with the displayed value of the new mapping target.
@@ -0,0 +1,257 @@
= Glue concepts
[[target-value-sequence]]
== Target value sequence
A target value sequence represents a list of possible target values.
It can be entered using into the xref:user-interface/mapping-panel/glue-section.adoc#value-sequence[].
The mapping will set the target only to values contained in that sequence.
Such a sequence doesn't just support single values but also ranges with customizable step sizes.
All values are entered comma-separated using the target unit specified with the xref:user-interface/mapping-panel/target-section.adoc#display-unit[].
.Single values
====
Enter this sequence for a volume target with target unit switched to *dB*:
`-20, -14, -12, -6, -3.5, 0`
When you move your knob or rotary encoder or press a button using xref:user-interface/mapping-panel/glue-section.adoc#incremental-button[], ReaLearn will step through the entered dB values for you.
====
.Value ranges
====
Enter this sequence for a target with a continuous value range and target unit switched to *%*:
`10 - 30, 50 - 70 (5), 80 - 90 (2)`
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.
It's important that the numbers and the range dash are separated by spaces!
====
.Non-monotonic or non-strict-monotonic sequences
====
Let's look at this sequence:
`20, 10, 10, -5, 8`
It's non-monotonic: It decreases, and then increases again.
Even if it would just decrease, it would be non-strict monotonic because it contains duplicates (value 10).
When using xref:further-concepts/mapping.adoc#absolute-control[], it's no problem stepping through such sequences.
However, xref:further-concepts/mapping.adoc#relative-control[] only supports strictly increasing or strictly decreasing sequences.
So if you control this sequence e.g. via xref:further-concepts/mapping.adoc#rotary-endless-encoder[] or via xref:user-interface/mapping-panel/glue-section.adoc#incremental-button[], the sequence will be stepped through like this: -5, 8, 10, 20.
Alternative: Use xref:user-interface/mapping-panel/glue-section.adoc#make-absolute[]!
====
[#feedback-type]
== Feedback type
The _feedback type_ determines whether to send numeric, text or dynamic feedback to the source.
It can be set using the xref:user-interface/mapping-panel/glue-section.adoc#feedback-type-controls[].
[[numeric-feedback-type]]
=== Numeric feedback: EEL transformation
Sends numeric feedback to the source.
This is the default.
Numeric feedback can be combined with an EEL feedback transformation formula.
This is similar to xref:user-interface/mapping-panel/glue-section.adoc#control-transformation[] 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`.
.Simple feedback transformation formula
====
`x = y * 2`
====
ReaLearn's feedback processing order is:
. Apply target interval.
. Apply reverse.
. Apply transformation.
. Apply source interval.
[[text-feedback]]
=== Text feedback: Text expression
With this option, ReaLearn will send text feedback values to the source.
This only works with sources that are capable of displaying text: That is any xref:sources/osc.adoc[] with argument type _String_, xref:sources/midi/display.adoc[] and xref:sources/midi/midi-script.adoc[].
Text feedback can be combined with a _text expression_, which lets you define which text is going to be sent to the source _whenever the target value changes_ and immediately when 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.
.Simple text expression
====
`{{target.text_value}}`
====
See xref:further-concepts/target.adoc#target-property[] for a list of properties that you can use in placeholders.
[#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 Luau 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 <<text-feedback,textual feedback>>.
+
.How to use `context.prop()`
====
[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 Luau 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.
+
.Result table structure
====
[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 xref:user-interface/mapping-panel/glue-section.adoc#feedback-style[]
<3> Has the same effect as background color in xref:user-interface/mapping-panel/glue-section.adoc#feedback-style[]
====
+
* 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 xref:sources/midi/midi-script.adoc[] because other sources don't know how to deal with it)
.`global.realearn.time`
====
Displays the number of milliseconds passed since ReaLearn was loaded:
[source,lua]
----
local millis = context.prop("global.realearn.time") or 0
return {
feedback_event = {
value = "" .. millis .. "ms"
},
}
----
====
.Animation
====
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
},
}
----
====
.Structured feedback values
====
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 xref:sources/midi/midi-script.adoc[]:
[source,lua]
----
return {
address = 0x4bb0,
messages = {
{ 0xb0, 0x4b, y.more_info.index, y.more_info.count }
}
}
----
This example is not realistic, it just shows how you can access the value table returned by the glue section feedback script.
====
You can share code between multiple feedback scripts by using xref:further-concepts/compartment.adoc#compartment-wide-lua-code[], with the following limitations (which hopefully will be lifted over time):
* The shared code is not yet available to the Lua code editor window.
That means writing `require("compartment")` will evaluate to `nil` in the editor.
You might see a corresponding error message when the editor tries to compile your code.
* When ReaLearn queries the script in advance to know which target properties it needs, the shared code is also not available yet.
Currently, you need to make sure that the target properties are queried even if `require("compartment")` evaluates to `nil`.
@@ -0,0 +1,38 @@
= Instance concepts
[[instance-tag]]
== Instance tag
Each instance can have arbitrarily many tags.
Tags are important if you want to dynamically enable or disable instances using the xref:targets/realearn/enable-disable-instances.adoc[].
[[auto-units]]
== Auto units
Each xref:key-concepts.adoc#instance[] optionally supports the automatic addition and configuration of a xref:key-concepts.adoc#unit[] with a user-defined xref:further-concepts/compartment.adoc#main-preset[] if a certain type of controller is connected, and automatic removal if it is disconnected.
The general procedure is:
. Globally define once what controllers you have at your disposal and choose which main preset you want to use for which controller (in the xref:user-interface/settings-dialog.adoc[] of the xref:helgobox::app.adoc[])
. Enable global control for one xref:key-concepts.adoc#instance[] using xref:user-interface/main-panel/menu-bar.adoc#enable-global-control[].
=== Global control
If global control is enabled for an instance, all connected controllers that are configured in the xref:realearn::user-interface/settings-dialog.adoc[ReaLearn settings dialog] will load their corresponding main preset.
You can configure controllers using the xref:helgobox::app/user-interface/title-bar.adoc#title-bar-settings[] button in the window title bar of the xref:helgobox::app.adoc[].
If global control is disabled for an instance, global controllers will be ignored in this instance.
This gives you the freedom to create a custom control experience using ReaLearn.
TIP: It's a good idea to enable global control for a Helgobox instance on the monitoring FX chain.
Such an instance will be around permanently as long as REAPER is running, even if you open and close different projects.
Perfect for project-spanning control scenarios!
=== Overrides
If you decide to use a specific device for something else in a certain project, all you need to do is to set use the device as input and/or output in a project-specific ReaLearn unit!
If you do that, the project-specific instance "wins" over the monitoring FX instance.
You got a project-specific override.
If you close the project, the monitoring FX instance takes over again.
@@ -0,0 +1,523 @@
= Mapping concepts
== Absolute vs. relative control
We can distinguish between two fundamentally different ways of xref:key-concepts.adoc#control[]: _Absolute_ and _relative_ control.
[[absolute-control]]
=== Absolute control
Imagine someone tells you: "Change the volume to -6 dB!" You go ahead and move the fader to -6 dB.
That's absolute control in a nutshell!
Absolute control uses <<absolute-control-value, absolute control values>>.
[TIP]
====
Often, absolute control involves an <<absolute-control-element>>, but it doesn't have to!
ReaLearn can simulate absolute control even with a <<relative-control-element>> (see xref:user-interface/mapping-panel/glue-section.adoc#make-absolute[]).
====
[[relative-control]]
=== Relative control
Imagine someone tells you: "Raise the volume by 2 dB!" You go ahead and move the fader up by 2 dB.
Before it was -6 dB, that means it's -4 dB now.
That's relative control!
Relative control uses <<relative-control-value, relative control values>>.
[TIP]
====
Often, relative control involves a <<relative-control-element>>, but it doesn't have to!
ReaLearn masters multiple ways of turning absolute control into relative one:
* Buttons or keys naturally emit absolute control values.
But using xref:user-interface/mapping-panel/glue-section.adoc#incremental-button[], they can be used for relative control (previous/next style buttons).
* Knobs naturally emit absolute control values.
But using xref:user-interface/mapping-panel/glue-section.adoc#make-relative[] or xref:user-interface/mapping-panel/glue-section.adoc#performance-control[], they can be used for relative control.
====
== Absolute vs. relative control elements
We can distinguish between control elements with regard to what sort of <<control-value>> they emit: In this dimension, we have <<absolute-control-element>> and <<relative-control-element>>.
[[absolute-control-element]]
=== Absolute control element
A xref:key-concepts.adoc#control-element[] is _absolute_ if it emits absolute values.
You can think of an absolute value as a percentage: The value is something between 0% and 100%, where 0% represents the minimum possible value and 100% the maximum.
.Typical absolute control elements
[[fader]] Fader::
A fader is a vertical or horizontal element with a thumb that you can move.
When moving a fader up from bottom to top, it will continuously emit values from 0% to 100%.
[[knob]] Knob::
A knob is a circular element with boundaries on the left and right.
When moving a knob clockwise, it will continuously emit values from 0% (at the left boundary) to 100% (at the right boundary).
[[momentary-button]] Momentary button::
A momentary button is a button that emits 100% when pressed and 0% when released.
ReaLearn can easily use momentary buttons to toggle a target.
See xref:user-interface/mapping-panel/glue-section.adoc#toggle-button-mode[].
[[toggle-button]] Toggle button::
A toggle button is a button that emits 100% when pressed and 0% when pressed again.
+
IMPORTANT: You don't want toggle buttons on the hardware side!
They are much less flexible than momentary buttons!
Momentary buttons can be turned into toggle buttons by ReaLearn, but not vice versa!
If you have to put up with a hardware toggle button, use xref:further-concepts/source.adoc#midi-source-character[] xref:further-concepts/source.adoc#toggle-only-button[].
[[velocity-sensitive-key]] Velocity-sensitive key:: A key emits a value between 0% and 100% when pressed, depending on the velocity with which the key was hit.
When released, it will emit 0%.
[[aftertouch]] Aftertouch:: When pressing the key a bit more into the keybed after it has already been pressed, it will continuously emit increasing values starting at 0%.
When releasing pressure, it will continuously emit decreasing values until reaching 0% again.
[[pitch-wheel]] Pitch wheel:: When moving a pitch wheel up, it will continuously emit increasing values starting at 50%.
When letting it snap back, it will continuously emit decreasing value until reaching 50% again.
When moving it down, it will continuously emit decreasing values, and so on.
[[touch-strip]] Touch strips:: When touching the strip somewhere in the middle, it will emit a value around 50%.
When dragging upward, it will continuously emit increasing values starting from where you touched it first.
[[relative-control-element]]
=== Relative control element
A xref:key-concepts.adoc#control-element[] is _relative_ if it emits relative values.
You can think of a relative value as an _instruction_.
It can be one of the following two instructions:
Please decrease!:: We call such a value a *decrement*.
Please increase!:: We call such a value an *increment*.
.Typical relative control elements
[[rotary-endless-encoder]] Rotary endless encoder::
A rotary endless encoder (or just _encoder_) is a circular, like a knob.
But unlike a knob, it doesn't have boundaries.
When moving a rotary endless encoder clockwise, it will continuously emit increments.
When moving it counter-clockwise, it will continuously emit decrements.
+
IMPORTANT: It happens very often that controllers have rotary endless encoders, but they will act like knobs by default, sending absolute messages.
That is a great waste, and you should change that setting as soon as possible on the hardware side.
[[control-value]]
== Control value
A _control value_ is the signal that travels through a ReaLearn xref:key-concepts.adoc#mapping[] from xref:key-concepts.adoc#source[] to xref:key-concepts.adoc#target[] when it receives an event from an xref:key-concepts.adoc#input-port[].
A control value can be absolute or relative.
[[feedback-value]]
== Feedback value
A _feedback value_ is the signal that travels through a ReaLearn xref:key-concepts.adoc#mapping[] back from xref:key-concepts.adoc#target[] to xref:key-concepts.adoc#source[] when the target changes its value.
A feedback value is always absolute.
== Absolute vs. relative control values
[[absolute-control-value]]
=== Absolute control value
An _absolute_ control value is conceptually a percentage between 0.0% and 100.0%.
Internally, it is represented by a high-precision floating point number between 0.0 and 1.0. E.g. 0.25 is 25%.
[[relative-control-value]]
=== Relative control value
A _relative_ control value is a number of increments or decrements.
Internally, it is represented as a positive or negative integer.
E.g. control value -2 means a decrement of 2.
[[mapping-tag]]
== Mapping tag
Each mapping can have arbitrarily many tags.
Such tags can be used to organize mappings in a way that is much more flexible than groups.
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 xref:targets/realearn/enable-disable-mappings.adoc[].
[[group]]
== 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.
You can decide which group is displays using xref:user-interface/main-panel/mapping-group-section.adoc#mapping-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.
[[mapping-activation-state]]
== Mapping activation state
A mapping is considered as *on* or *active* (terms are used interchangeably) only if all following criteria are fulfilled:
. 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-activation-condition>> is fulfilled
. The xref:further-concepts/target.adoc#target-activation-condition[] is fulfilled
. The target is valid
+
====
Example: A track target can be invalid when it's using xref:further-concepts/target.adoc#selected-selector[] but no track is currently selected).
====
In all other cases, mapping is *off* or *inactive*.
In that case, it doesn't have any effect!
(Controller) mappings with xref:further-concepts/target.adoc#virtual-target[] are always considered active as long as the feedback checkbox is ticked.
[[mapping-signal-flow]]
== Mapping signal flow
Here's how ReaLearn processes an incoming control event that matches a mapping source.
1. ReaLearn converts the event coming from the xref:key-concepts.adoc#input-port[] to a <<control-value>>.
2. ReaLearn feeds the <<control-value>> to the mapping's xref:key-concepts.adoc#glue[].
The glue section is responsible for transforming control values before they reach the xref:key-concepts.adoc#target[].
This transformation can change the type of the control value, e.g. from relative to absolute - it depends on the settings in the glue section and the mapping's target.
The glue section can even "eat" control values so that they don't arrive at the target at all.
3. Finally, ReaLearn converts the transformed <<control-value>> into some target instruction (e.g. "set volume to -6.0 dB") and executes it.
Feedback (from target to source) 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.
[[conditional-activation]]
== Conditional activation
Conditional activation is a powerful feature that allows you to dynamically activate or deactivate a mapping depending on its <<mapping-activation-condition>>.
.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: xref:targets/fx-parameter/set-value.adoc[], 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.
====
[[mapping-activation-condition]]
== Mapping activation condition
The activation condition of a mapping determines under which circumstances a mapping is active or inactive, based on the value of a xref:further-concepts/compartment.adoc#compartment-parameter[] or based on the state of arbitrary xref:key-concepts.adoc#target[targets].
It is especially practical if your controller has a limited amount of control elements and you want to give control elements several responsibilities.
It lets 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, xref:targets/realearn/enable-disable-mappings.adoc[] 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.
====
=== 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.
=== When bank selected
This is the correct activation mode if you want control surface "bank-style" mapping.
TIP: For this kind of use cases you should consider the new xref:targets/realearn/enable-disable-mappings.adoc[], 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 xref:targets/fx/enable-disable.adoc[] 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 xref:targets/fx/browse-presets.adoc[] 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 unit.
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 xref:further-concepts/compartment.adoc#compartment-parameter[]).
=== 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 <<expression-based-activation-condition>> is a slightly better choice because it's easier to use and generally performs a bit better.
[#expression-based-activation-condition]
=== 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 xref:further-concepts/target.adoc#dynamic-selector[dynamic selectors] to express the activation condition.
The equivalent expression to above EEL example is:
`p[0] > 0 && p[1] > 0`
[#target-based-activation-condition]
=== 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. xref:targets/track/select-unselect.adoc[].
* 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`.
[[raw-midi-pattern]]
== Raw MIDI pattern
xref:sources/midi/raw-midi-sysex.adoc[] and xref:targets/midi/send-message.adoc[] allow to enter so-called MIDI patterns, an advanced ReaLearn concept.
=== 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.
.A SysEx pattern
====
----
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 enabling xref:further-concepts/unit.adoc#logging-of-real-feedback-messages[].
* 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.
.Binary notation
====
----
F0 00 20 [0110 1011] 7F 42 02 00 10 77 00 F7
----
This is equivalent to the previous 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.
=== Variable patterns (extracting and encoding a value)
For the xref:key-concepts.adoc#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 xref:user-interface/mapping-panel/advanced-settings-dialog.adoc#mapping-lifecycle-actions[Mapping lifecycle action], which allow you to send raw MIDI messages once when a mapping is initialized, not on every target value change.
But even for the xref:key-concepts.adoc#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 into the raw MIDI message (feedback).
Bytes which contain a variable value (or a part of it) _must_ be expressed using binary notation.
.Variable pattern
====
----
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:
[horizontal]
`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)
=== Resolution of variable patterns
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.
.Another variable pattern
====
In this 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:
.Most significant byte first
====
----
F0 00 20 6B 7F 42 02 00 10 [ponm lkji] [hgfe dcba] F7
----
====
.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
----
====
.Pitch wheel simulation
====
This behaves like pitch wheel because the pattern describes exactly the way how pitch wheel messages are encoded.
----
E0 [0gfe dcba] [0nml kjih]
----
====
@@ -0,0 +1,318 @@
= Source concepts
== Real vs. virtual sources
We distinguish between _virtual_ and _real_ sources.
[[virtual-source]]
=== Virtual source
A _virtual_ source refers to a xref:further-concepts/compartment.adoc#virtual-control-element[] and can only be used in the xref:key-concepts.adoc#main-compartment[].
Examples: `ch1/fader`
[[real-source]]
=== Real source
A _real_ source refers to a xref:further-concepts/compartment.adoc#real-control-element[].
[[midi-source-character]]
== MIDI source 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 source characters 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,Toggle-only button]] Toggle-only button (avoid!):: A control element that can be pressed and emits absolute values.
ReaLearn will simply emit 100%, no matter what the hardware sends.
+
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.
+
[NOTE]
.Background
====
ReaLearn can make a momentary hardware button work like a full-blown toggle button.
Its 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.
====
+
[CAUTION]
.Combination with xref:user-interface/mapping-panel/glue-section.adoc#incremental-button[]
====
If you use the toggle-only source character in combination with mode xref:user-interface/mapping-panel/glue-section.adoc#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.
====
[[midi-source-script]]
== MIDI source script
MIDI source scripts are EEL or Luau scripts to configure the xref:sources/midi/midi-script.adoc[].
=== General mechanics
Each 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
Scripts written in EEL work as follows.
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.
.Creating a 3-byte MIDI message
====
[source,eel]
----
address = 0x4bb0;
msg_size = 3;
0[] = 0xb0;
1[] = 0x4b;
2[] = y * 64;
----
====
=== Luau script specifics
Scripts written in Luau work as follows.
Script input::
* Luau 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 xref:user-interface/mapping-panel/glue-section.adoc[] 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 Luau 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.
.Creating 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) }
}
}
----
====
.Creating 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 }
}
}
----
====
.Creating 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` xref:user-interface/mapping-panel/glue-section.adoc[] 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.
====
You can share code between multiple MIDI scripts by using xref:further-concepts/compartment.adoc#compartment-wide-lua-code[], with the following limitations (which hopefully will be lifted over time):
* The shared code is not yet available to the Lua code editor window.
That means writing `require("compartment")` will evaluate to `nil` in the editor.
You might see a corresponding error message when the editor tries to compile your code.
[[osc-feedback-arguments-expression]]
== OSC feedback arguments expression
This expression is used to enable for more flexible feedback for the xref:sources/osc.adoc[].
It allows you to define exactly which feedback value is sent at which argument position.
If this field is non-empty, the argument type will be ignored for the xref:key-concepts.adoc#feedback[] direction.
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.
.Custom feedback with 2 arguments
====
If you want ReaLearn to send the current feedback value in text form at argument 1 and the color (see xref:user-interface/mapping-panel/glue-section.adoc#feedback-style[]) 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
|===
@@ -0,0 +1,539 @@
= Target concepts
== Real vs. virtual targets
We distinguish between _virtual_ and _real_ targets.
[[virtual-target]]
=== Virtual target
A _virtual_ target controls a xref:further-concepts/compartment.adoc#virtual-control-element[] and can only be used in the xref:key-concepts.adoc#controller-compartment[].
Example: `ch1/fader`
It's then picked up by a xref:further-concepts/source.adoc#virtual-source[]] in the xref:key-concepts.adoc#main-compartment[].
[[real-target]]
=== Real target
All others targets are real.
Examples: xref:targets/track/set-volume.adoc[]
[[target-object-selectors]]
== Target object selectors
Many ReaLearn xref:key-concepts.adoc#target[targets] work on some kind of _object_:
* A track
* An FX
* An FX parameter
* A send or receive
All of those objects need to be _addressed_ somehow.
For this purpose, ReaLearn uses so-called _object selectors_.
=== Common object selectors
This section describes commonly available object selectors.
NOTE: The descriptions below are somewhat tailored to track objects.
However, the same concepts can easily be applied to other objects that support these selectors.
[#unit-selector]
==== Unit selector
This selector makes the target work on the current xref:further-concepts/unit.adoc#unit-track[] or current xref:further-concepts/unit.adoc#unit-fx[] of this particular ReaLearn xref:key-concepts.adoc#unit[].
[#particular-selector]
==== Particular selector
Lets you pick a specific object (e.g. track) and refer to it by its unique ID.
This is the default.
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 this is an extremely <<sticky-selectors,sticky>> selector.
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 xref:key-concepts.adoc#unit[] on the monitoring FX chain.
[#at-position-selector]
==== At position selector
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`.
[#named-selector]
==== Named selector
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:
[horizontal]
`*`:: for matching zero or arbitrary many characters
`?`:: for matching exactly one arbitrary character.
.Wildcards in named selectors
====
`Violin *` would match `Violin 1` or `Violin 12` but not `12th Violin`.
====
[#dynamic-selector]
==== Dynamic selector
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 xref:further-concepts/compartment.adoc#compartment-parameter[]).
| 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`.
|===
.Simple example
====
`p1 * 99`
* Will point to track with index 0 (first track) if xref:further-concepts/compartment.adoc#compartment-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 xref:further-concepts/compartment.adoc#compartment-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 xref:user-interface/main-panel/menu-bar.adoc#set-compartment-parameter-value-count[] of the parameter to 100
** 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.
====
.More complex example
====
`p1 * 3 * 100 + p2 * 99`
This will treat xref:further-concepts/compartment.adoc#compartment-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.
xref:further-concepts/compartment.adoc#compartment-parameter[] 2 will select the track number within the bank.
You see, this is very flexible.
====
=== Additional object selectors for tracks
==== <This> 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]
==== <Selected> selector
Currently selected track.
If multiple tracks are selected, refers only to the first one.
==== <All selected> 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!
==== <Master> 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.
==== All named selector
Allows you to use wildcards (see <<named-selector>>) to make track targets do their thing on all matching tracks instead of only the first one.
==== At TCP position selector
Like <<at-position-selector>> but just considers tracks that are visible in the track control panel.
==== At MCP position selector
Like <<at-position-selector>> but just considers tracks that are visible in the mixer control panel.
==== Dynamic (TCP) selector
Like <<dynamic-selector>> but the result should be an index counting only tracks visible in the track control panel.
==== Dynamic (MCP) selector
Like <<dynamic-selector>> but the result should be an index counting only tracks visible in the mixer control panel.
==== By ID or name (legacy) selector
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.
IMPORTANT: **This selector is deprecated!** You shouldn't use it anymore.
=== Additional target selectors for FXs
==== <This> selector
Always points to the own ReaLearn FX xref:helgobox:ROOT:key-concepts.adoc#instance[].
Perfect for changing own parameters, e.g. for usage of the conditional activation or <<dynamic-selector>> features (especially important if you want to create reusable presets that make use of these features).
[[fx-focused-selector]]
==== Focused selector
Currently or last focused FX. _Track_ and _Input FX_ settings are ignored.
[#particular-fx-selector]
==== Particular selector
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 <<particular-selector>> as well.
[#named-fx-selector]
==== Named selector
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.
[#all-named-fx-selector]
==== All named selector
Allows you to use wildcards (see <<named-fx-selector>>) to make FX targets do their thing on all matching FX instances instead of only the first one.
==== By ID or position (legacy) selector
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.
IMPORTANT: **This selector is deprecated!** You shouldn't use it anymore.
=== Sticky vs. non-sticky selectors
We call object selectors _sticky_ if they refer to a particular object (e.g. a track).
[[sticky-selectors]] Sticky selectors:: `<Master>`, `<This>`, `Particular`
[[non-sticky-selectors]] Non-sticky selectors:: `<Dynamic>`, `<Focused>`, `<Selected>`, `<Unit>`, `<All selected>`, `Named`, `All named`, `At position`, `From Playtime column`
[[target-property]]
== Target property
Targets can expose properties, which you can use for xref:further-concepts/glue.adoc#text-feedback[] or xref:further-concepts/glue.adoc#dynamic-feedback[].
Which properties are available, depends very much on the type of the target type.
There are some properties which are available for (almost) any target (for very target-specific properties, please look up the corresponding target in xref:targets.adoc[]):
.Common target properties
[cols="m,1,3"]
|===
|Name | Type | Description
|
global.realearn.time
|
Decimal
|
Time in milliseconds since ReaLearn has been loaded (the first instance).
|
mapping.name
|
String
|
Name of the mapping. Contains the explicitly assigned mapping name, never an automatically generated one.
|
target.text_value
|
String
|
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 xref:targets/project/browse-tracks.adoc[]. 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
|
Boolean
|
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: xref:targets/pot/preview-preset.adoc[] 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 xref:further-concepts/glue.adoc#dynamic-feedback[], for example in order to display different things on displays depending on the target's availability.
|
target.discrete_value
|
Integer
|
The current target value as zero-based integer. This only works for discrete targets.
|
target.discrete_value_count
|
Integer
|
The number of possible values in the current target. This only works for discrete targets.
|
target.numeric_value
|
Decimal
|
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`. This can be done using xref:further-concepts/glue.adoc#dynamic-feedback[].
|
target.numeric_value.unit
|
String
|
Contains the unit of `target.numeric_value` (e.g. _dB_).
|
target.normalized_value
|
Decimal
|
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
|
String
|
Short name representing the type of the mapping target.
|
target.type.long_name
|
String
|
Long name representing the type of the mapping target.
|
target.track.index
|
Integer
|
Zero-based index of the first resolved target track (if supported).
|
target.track.name
|
String
|
Name of the first resolved target track (if supported).
|
target.track.color
|
Color
|
Custom color of the first resolved target track (if supported).
|
target.fx.index
|
Integer
|
Zero-based index of the first resolved target FX (if supported).
|
target.fx.name
|
String
|
Name of the first resolved target FX (if supported).
|
target.route.index
|
Integer
|
Zero-based index of the first resolved target send/receive/output (if supported).
|
target.route.name
|
String
|
Name of the first resolved target send/receive/output (if supported).
|===
[[target-value-polling]]
== Target value polling
Target value polling makes ReaLearn query the current value of a target every few milliseconds as part of the main application loop in order to send up-to-date feedback to your controller at all times.
IMPORTANT: Target value polling 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 good 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.
Remarks:
* For most targets that support polling, if you disable polling, 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 xref:targets/fx-parameter/set-value.adoc[] 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.
[[target-activation-condition]]
== Target activation condition
xref:key-concepts.adoc#target[Targets] can have activation conditions as well.
They are very specific to the type of the target.
.Typical target conditions
====
* xref:user-interface/mapping-panel/target-section.adoc#track-must-be-selected[]
* xref:user-interface/mapping-panel/target-section.adoc#fx-must-have-focus[]
====
[[continuous-vs-discrete-value-range]]
== Continuous vs. discrete value range
ReaLearn xref:key-concepts.adoc#target[targets] can have a _continuous_ or _discrete_ value range.
[[continuous-value-range]]
=== Continuous value range
A _continuous_ value range is a range of arbitrary floating point numbers between 0.0 and 1.0. You can also think of them as _percentages_ between 0.0% and 100.0%.
Continuous value ranges don't have steps.
.Some targets with a continuous value range.
====
* xref:targets/track/set-volume.adoc[]
* xref:targets/project/set-tempo.adoc[]
====
[[discrete-value-range]]
=== Discrete value range
A _discrete_ value range is a range of integers, e.g. 0 to 9. That would be 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. That's it!
No other values are possible.
Discrete value ranges have steps.
.Some targets with a discrete value range.
====
* xref:targets/fx/browse-presets.adoc[]
* xref:targets/project/browse-tracks.adoc[]
====
[[group-properties]]
== Group properties
A group doesn't just have a name.
It has a few properties that are also available for mappings and thus affect all mappings in that group:
Tags::
Tags defined in the group will be merged with the tags in each mapping.
Control/feedback-enabled::
Lets you switch off control and/or feedback off for all mappings in that group.
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
xref:further-concepts/mapping.adoc#conditional-activation[conditional activation] in the section about the xref:user-interface/mapping-panel.adoc[].
@@ -0,0 +1,187 @@
= Unit concepts
[[letting-through-events]]
== Letting through MIDI events
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 xref:user-interface/main-panel/let-through-section.adoc[].
The exact behavior differs depending on what you choose as xref:user-interface/main-panel/input-output-section.adoc#input[]:
* If input is set to xref:user-interface/main-panel/input-output-section.adoc#fx-input[]
** 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+.
* The checkboxes don't have any effect on computer keyboard input or OSC.
Keys are always passed through when doing text entry and never passed through if a mapping matches.
[[auto-load]]
== Auto-load
If you activate xref:user-interface/main-panel/preset-section.adoc#auto-load-based-on-unit-fx[], ReaLearn will start to observe the <<unit-fx>> of this ReaLearn unit and keep loading xref:further-concepts/compartment.adoc#main-preset[main presets] according to which <<fx-to-preset-link, FX-to-preset links>> you have defined.
By default, the unit FX is set to `<Focused>`, which means, it will reflect whatever FX is currently focused.
Whenever the unit FX changes, it will check if you have linked a compartment preset to it and will automatically load it.
Whenever the unit FX switches to an unlinked FX or the FX loses focus, ReaLearn falls back to the mapping list or preset that was active before activating auto-load.
Of course this makes sense only if you actually have linked some presets.
Section <<unit-fx-to-preset-link>> describes how to do that.
[[fx-to-preset-link]]
== FX-to-preset link
A link between a FX and a xref:further-concepts/compartment.adoc#main-preset[].
Used in <<auto-load>>.
[[unit-fx-to-preset-link]]
=== Unit FX-to-preset link
A link saved as part of a xref:key-concepts.adoc#unit[].
[[global-fx-to-preset-link]]
=== Global FX-to-preset link
This is like a <<unit-fx-to-preset-link>> but the link is saved globally.
This is useful if you have only one controller or if you have x controllers (= and therefore x ReaLearn units) and want both of them to always auto-load the same preset if the unit FX points to the same plug-in.
* All links will be saved _globally_, not just within this project!
* Location: REAPER resource directory (menu:Options[Show REAPER resource path in explorer/finder]) at `Data/helgoboss/realearn/auto-load-configs/fx.json`.
[[unit-key]]
== Unit key
Each ReaLearn unit has a key that's used to address this particular ReaLearn unit when using the <<projection>> feature.
By default, the unit key is a random cryptic string which ensures that every unit is uniquely addressable.
The result is that scanning the QR code of this ReaLearn unit will let your mobile device connect for sure with this unique unit, not with another one - remember, you can use many units of ReaLearn in parallel.
This is usually what you want.
But a side effect is that with every new ReaLearn unit that you create, you first have to point your mobile device to it in order to see its
<<projection>> (by scanning the QR code).
Let's assume you have in many of your projects exactly one ReaLearn unit that lets your favorite MIDI controller control track volumes.
By customizing the unit key, you can tell your mobile device that it should always show the <<projection>> of this very ReaLearn unit - 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 unit key of each volume-controlling ReaLearn unit to exactly the same value, in each project, using xref:user-interface/main-panel/bottom-section.adoc#set-unit-data[].
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 units in each project.
[CAUTION]
====
Make sure to not have more than one ReaLearn unit with the same unit key active at the same time because then it's not clear to which your mobile device will connect!
====
*At the moment, the unit key 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 unit key.
This might change in future in favor of a more nuanced approach!
[#unit-track]
== Unit track
The second line of the bottom panel shows the current track chosen as **Unit track** for this unit of ReaLearn.
This can be something like "Track 3" or "The currently selected track".
Mappings in this ReaLearn unit can refer to this track by choosing the track selector xref:further-concepts/target.adoc#unit-selector[].
The unit track can be changed via xref:targets/track/track.adoc[].
[#unit-fx]
== Unit FX
The second line of the bottom panel also shows the current FX chosen as **Unit FX** for this unit of ReaLearn.
This can be something like "FX 5 on track 3" or "The currently focused track".
Mappings in this ReaLearn unit can refer to this FX by choosing the FX selector xref:further-concepts/target.adoc#unit-selector[].
The unit FX can be changed via xref:targets/fx/fx.adoc[].
[[unit-tag]]
== Unit tag
Each unit can have arbitrarily many tags.
Tags are important if you want to dynamically enable or disable units using the xref:targets/realearn/enable-disable-units.adoc[].
[[projection]]
== Projection
Projection 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.
See xref:user-interface/projection.adoc[].
== Logging
Logging can be enabled or disabled via xref:user-interface/main-panel/menu-bar.adoc#logging[].
[[logging-of-real-control-messages]]
=== Logging of real control messages
Each log entry contains the following information:
* Timestamp in seconds
* Helgobox xref:helgobox:ROOT:key-concepts.adoc#instance-id[]
* 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.
[[logging-of-real-feedback-messages]]
=== Logging of real feedback messages
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 xref:user-interface/mapping-panel/advanced-settings-dialog.adoc#mapping-lifecycle-actions[Mapping lifecycle actions]).
** *Target output:* A message sent because of either the xref:targets/midi/send-message.adoc[] or xref:targets/osc/send-message.adoc[].
[[superior-units]]
== Superior units
When a unit is made superior via menu entry xref:user-interface/main-panel/menu-bar.adoc#make-unit-superior[], this unit is allowed to suspend other units which share the same input and/or output device (hardware devices only, not FX input or output!).
[TIP]
====
Making units superior is **rarely needed**!
This option was initially introduced in order to add more flexibility to the <<auto-load>> feature.
The idea was to let a controller _fall back_ to some default behavior if the currently focused FX is closed.
Multiple instances were necessary to make this work with one of them (the auto-load instance) being superior.
However, since ReaLearn 2.14.0, falling back to initial mappings when the FX loses focus in auto-load mode became much easier and doesn't require multiple units anymore!
Your initial mappings or initial preset will be memorized and reloaded once the FX loses focus.
See <<auto-load>> for more information.
====
Behavior:
* By default, ReaLearn units are not superior, just normal.
This is most of the time okay, even if you have multiple units that share the same input and output ... as long as you don't have any conflicting mappings active at the same time.
* For example, if 2 units 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 units 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 unit.
This can't work.
* Sometimes you want one unit to suspend/cover/cancel/mute another one!
You can do this by making this unit _superior_.
Then, whenever this unit has at least one active mapping, all non-superior units with the same control and/or feedback device will be disabled for control and/or feedback.
* You can have multiple superior units.
Make sure they get along with each other :)