Keywords: developer_docs

Subsections

- Handling user input
  - Plugins and synthesizers
    - Overview
    - Processing the input, recording
    - How it's stored
    - Enabling and disabling controllers

# User controls and automation

## Handling user input

### Plugins and synthesizers

#### Overview

When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). The relevant classes are `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginGui`, `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase` (in `fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lugin.h`) and `fterfirstO`O=95 _ `O=58 : `O>64 `O<91 OOO <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>scIF` (in `fterfirsto`o=95 _ `o=58 : `o>64 `o<91 ooo <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>sc.h`).

If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation while the user operates it. Then MusE will update the plugin's parameter value, and also record the new value. When appropriate, the controller is enabled again.

#### Processing the input, recording

Upon operating a slider, `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase::setParam` is called, which usually writes the control change into the ringbuffer `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo`. (`fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::apply()`, `fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::getData()` will read this ringbuffer and do the processing accordingly). Furthermore, `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::recordAutomation` is called, which either directly modifies the controller lists or writes the change into a "to be recorded"-list (`fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents`) (depending on whether the song is stopped or played).

The `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents` list consists of `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal` items (see `fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trl.h`), which hold the following data:

- the frame where the change occurred
- the value
- the type, which can be `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START`, `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL` or `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP`. `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL` are written by every `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>udioTrack::recordAutomation` call, `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START` and `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP` are generated by `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::startAutoRecord` and `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord`, respectively.
- and the id of the controller which is affected

It is processed when the song is stopped. The call path for this is: `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling` calls `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents` calls `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::processAutomationEvents`. This function removes the old events from the track's controller list and replaces them with the new events from `fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>recEvents`. In `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE` mode, just all controller events within the recorded range are erased; in `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH` mode, the `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START` and `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP` types of the `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal` events are used to determine the range(s) which should be wiped.

#### How it's stored

Automation data is kept in `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller`, which is a `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList`, that is, a list of `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList`s, that is, a list of lists of controller-objects which hold the control points of the automation graph. The `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList` also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). Furthermore, it stores a `fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>curVal` (accessed by `fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal()`), which holds the currently active value, which can be different from the actually stored value because of user interaction. This value is also used when there is no stored automation data.

`fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::addController` and `fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>emoveController` are used to add/remove whole controller types; the most important functions which access `fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller` are:

- `fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rocessAutomationEvents`, `fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ecordAutomation`, `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>tartAutoRecord`, `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord`: see above.
- `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekPrevACEvent`, `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekNextACEvent`, `fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseACEvent`, `fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseRangeACEvents`, `fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ddACEvent`, `fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>hangeACEvent`, which do the obvious
- `fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginCtrlVal`, `fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>etPluginCtrlVal`: the first returns the current value according to the `fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller` list, the second only sets the `fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal`, but does not insert any events.

Whenever a `fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList` has been manipulated, `fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChange(Track*)` shall be called, which emits the `fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*)` signal in order to inform any parts of MusE about the change (currently, only the arranger's part canvas utilizes this).

#### Enabling and disabling controllers

Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. In `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE` mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked.

In `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH` (and currently (r1492) `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_READ`, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, however they only affect the recorded automation until the last toggle of the checkbox. (Example: start the song, toggle the checkbox, toggle it again, wait 10 seconds, stop the song. This will NOT overwrite the last 10 seconds of automation data, but everything between the first and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current behaviour for native GUIs is to behave like in `fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE` mode.

The responsible functions are: `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::oscControl` and `fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::oscControl` for handling native GUIs, `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::ctrlPressed` and `fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlReleased` for MusE default GUIs and `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::guiParamPressed`, `fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParamReleased`, `fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderPressed` and `fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderReleased` for MusE GUIs read from a UI file; `fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSlider*` obviously handle sliders, while `fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParam*` handle everything else which is not a slider. They call `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableController` to enable/disable it.

Furthermore, on every song stop or seek, `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers` is called, which re-enables all controllers again. The call paths for this are:

- For stop: `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling` calls `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents` calls `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation` calls `fterfirstT`T=95 _ `T=58 : `T>64 `T<91 TTT <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rack::clearRecAutomation` calls `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers`
- For seek: `fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udio::seek` sends a message ("`fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>`") to `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::seqSignal` which calls `fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation` which calls `fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers`