Hi,
I’m new to designing plugins (using Cabbage/Csound) and I hope you can help me find a solution to my problem.
Perhaps my problem cannot be resolved with Cabbage/Csound due to any inherent limitations or perhaps the limitations may be due to Audio Unit and VST principles.
In any case your feedback or suggestions for alternative approaches would be very precious.
I’d like my plugins to send MIDI feedback (for the mapped messages) to MIDI controllers (with endless encoders) not only when I tweak plugin parameters/variables with a mouse but also when they are changed internally within the plugin. For example, pushing a button may multiply the frequency of an oscillator and update the encoder widget used to control that frequency. Such a change should inform a MIDI controller that the frequency was changed even though it was not changed with a mouse. This would set the controller at the correct new position and prevent jumping from the new (changed only within plugin but not updated in controller) to the old position when the controller is changed.
I’d particularly like to have this functioning so that I could save/load presets on the fly. I’m aware of the “snapshot” implementation to save presets, but I don’t want to load presets with a mouse. Instead, I’d like to load/change them very quickly by pushing buttons on a MIDI controller. I have a fine working preset handling based on saving/loading plugin parameters to/from function tables. I have a range of buttons (say 1-8) to write all parameters to any of the “slots”, i.e. tables 1-8, by a long button press. Then I can load all parameters (different presents) by a short press on any of the buttons. All tables, i.e. presets, can be saved/loaded using ftsave/ftload. Plugin parameters and widget states get updated correctly without a problem. The only thing that I’m missing in this scenario is the ability for plugin to send feedback to MIDI controller when changes to plugin parameters occur within the plugin, e.g. after they have been loaded from function tables and the widget states have been updated. If I change plugin parameters with a mouse, plugin sends feedback to MIDI controller as expected, but this is not the case when plugin parameters and widget states are updated from within the plugin. An exception to this is when plugin parameters are changed by using e.g. VST program or bank change, but this to my knowledge does not provide a solution to what I’m trying to achieve.
I couldn’t find the info I am looking for on the Cabbage forum.
The most related discussion I found was: Bug with presets and plugin parameter state (in VST)
To illustrate the issue, I’m attaching a short video clip (Cabbage_MIDIfeedback.mp4.zip (2.6 MB) ) and posting the code for this simple demo.
In the video, there are 6 buttons/endless encoders of the MIDI controller (Midi Fighter Twister) visible.
Only the bottom row is mapped to the test plugin:
- the endless encoder on the left is mapped to the plugins’ encoder, i.e. oscillator frequency
- the middle button is mapped to the upper (red) button on the plugin, i.e. used to multiply frequency by factor two
- the right button is mapped to the lower (green) button on the plugin, i.e. used to divide frequency by factor two
You can see that changing the plugin controls with the mouse sends feedback to the MIDI controller.
The MIDI controller affects the plugin as expected, but when buttons on the controller are pushed to multiply/divide frequency, the plugin doesn’t send MIDI feedback to the frequency knob on the controller after the plugin frequency and the associated widget have changed. It seems that MIDI feedback is only sent if the plugin controls are changed with a mouse and not when they are changed internally within the plugin.
Cabbage: 2.3.0
Computer: MacBook Pro (Retina, 15-inch, Mid 2015), MacOS Sierra (10.12.6)
Code:
<Cabbage>
form caption("Untitled") size(200, 200), colour(58, 110, 182), pluginid("tmf1")
rslider bounds(2, 22, 120, 120) range(0.5, 10000, 220, 0.25, 0.001) channel("OscFrq") identchannel("OscFrqID") popuptext("0") colour(0, 255, 0, 255) trackercolour(255, 0, 0, 255)
label bounds(6, 154, 114, 22), text("Osc-Frq")
button bounds(126, 20, 65, 65) latched(0) channel("OscFrqMultiplier2") text("","") value(0) corners(3) colour:0(255, 0, 0, 255) colour:1(255, 255, 0, 255) fontcolour:0(0, 0, 0, 255) fontcolour:1(160, 0, 0, 255)
button bounds(126, 88, 65, 65) latched(0) channel("OscFrqMultiplier05") text("","") value(0) corners(3) colour:0(0, 255, 0, 255) colour:1(255, 255, 0, 255) fontcolour:0(0, 0, 0, 255) fontcolour:1(160, 0, 0, 255)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
; just some options I tested
;-n -d -+rtmidi=NULL -M0 -m0d
;-+rtmidi=NULL -Ma -Q1
;-odac -n -d -M0 -Q0 -b 4096
;-dm0 -odac -Q2
;-dm0 -n -d -M0 -Q0
-d -n -m0d -M1 -Q0 -+rtmidi=NULL
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
kOscFrq chnget "OscFrq"
if (trigger(chnget:k("OscFrqMultiplier2"), .5, 0) == 1) then;
kOscFrq *= 2
chnset kOscFrq, "OscFrq"
endif
if (trigger(chnget:k("OscFrqMultiplier05"), .5, 0) == 1) then;
kOscFrq *= 0.5
chnset kOscFrq, "OscFrq"
endif
aSig poscil 0.8, kOscFrq
outs aSig,aSig
endin
</CsInstruments>
<CsScore>
f0 z
i1 0 [60*60*24*7]
</CsScore>
</CsoundSynthesizer>