Not sure if I should post this under Noobs or if it’s preferable here, also not sure if this is old news for most people, but I share it here just in case it’s useful
I’m designing my first synth with Cabbage and I wanted it to:
- Have a basic GUI with standard knob controls for starters (ADSR, etc.).
- Also have those same parameters respond to incoming MIDI CC data.
In the process of setting it up, I learned a couple of things that I’d like to share:
CC & WIDGET INTEGRATION
It might be a little tricky to integrate the two (MIDI + widget), as either you get the data from incoming MIDI ––or–– from a widget, but not both in one connection, so you have to create a loop of sorts. First, let’s get the order of instructions:
- Get the MIDI data
- Get the widget data
- Check if the widget data changed
- Check if the MIDI data changed
- If the widget changed, kMIDI = widget state
- If MIDI changed, change widget according to the incoming CC
Parameter input will ultimately come from the widget…
kmatt ctrl7 1, 19, 0, 1 iatt cabbageGetValue "att" ktkatt changed iatt ktmatt changed kmatt if ktkatt == 1 then kmatt = iwatt endif cabbageSetValue "att", kmatt, ktmatt
I found this recipe to work OK so far (still need to test it some more), after struggling with the knob state changing every time I played my MIDI controller (somehow, MIDI controller data is remembered by Csound and it ‘updates’ the widget even though I may have previously set it to some other value manually by dragging with the mouse: I want to be able to change the parameters either directly on the GUI or via MIDI but then for the parameters to stay wherever I leave them, if that makes sense.
Also, I realize most DAWs have a ‘MIDI learn’ feature, but I prefer my instruments to incorporate any MIDI mapping directly –– when I’m making music, I’m either too lazy to set everything up or I find that it messes with the creative flow; or in the case of Ableton Live, for example, the mappings are session-dependent as opposed to instrument-dependent.
NOTE: I also found some weird widget behavior using the code above and not sure if this is something to do with it or the widgets: if I set the widget range to start from absolute zero, and I drag the value on the GUI to minimum –– after I had changed the value via MIDI –– it seems that the ‘widget changed’ instruction doesn’t actually register a change and when I play again some MIDI notes on the keyboard, the patch updates with the last MIDI setting and the knob jumps to that value (when it’s not meant to do so). Perhaps @rorywalsh can take a look at this (whenever)? In the meantime, my solution is to give the widget an approximate range:
rslider bounds (275, 180, 60, 60), channel(“att”), range(0.001, 1, 0, 1, 0.001), text(“A”)
Parameter mapping, in some cases, is not as straight-forward as just connecting a widget (say to a volume control): you’ll get zipper noise ‘out of the box’. But that’s the nature of the signals themselves (widgets sending data at k-rate, therefore changing the state of the audio and ‘skipping’ from one value to the next, as opposed to a smooth change, as it would happen naturally in the analog world with voltage control). This is well documented in the Csound manual, but I’ll post here the link (under “Smoothing 7-bit Quantisation in MIDI Controllers”) and an example code for convenience:
; Smoothing kvpt linseg 0,0.001,0.01 kvol portk kvol, kvpt avol interp kvol * kvol
There are three stages to the smoothing –– to the Csound veterans here, please correct me if I’m wrong! Happy to edit the post for future reference:
- The linseg opcode will create a ramp from one value to the next.
- The portk opcode applies a ‘portamento’ to the signal (a kind of secondary ramp IMO?).
- The interp opcode interpolates the segmented values at audio rate.
Hope that helps!