Subsections

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:

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>>trlLists, 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:

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: