Cabbage Logo
Back to Cabbage Site

cabbageCreate opcode update

The new cabbageCreate opcode has changed so that it accepts two strings as input arguments, the widget type, and the widget code.

cabbageCreate "image", "bounds(0, 0, 100, 100), channel(\"image1\")"

I think this is cleaner and makes things easer to read. I also found myself getting caught out with this opcode because I would somehow frequently forget to add the widget type! This is no longer possible. This opcode is now completely Rory proof :+1:

Iā€™m trying to create some image widgets in my plugin, but nothing seems to be happeningā€¦ I made a short test program to show you what I mean. I also got some questions about ā€œprintskā€, it works in my other programs, but it doesnā€™t seem to print anything here, so I canā€™t really tell what is happening. (Also, ā€˜printskā€™ doesnā€™t show up as an opcode in Csound, but Cabbage has it on itā€™s syntax highlighting list. It works, but Iā€™m confused as to whyā€¦)

<Cabbage>
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

;instrument will be triggered by keyboard widget
instr 1
    SStr init "bounds(0, 0, 10, 10) channel(\"imagex\")"
    printsk "%s\n", SStr
    cabbageCreate "image", SStr
    
    turnoff
endin

instr 2
    kCount init 0
    until kCount >= 20 do
        SStr sprintfk "bounds(20, 20, 10, 10) channel(\"trace%d\")", kCount
        printsk "%s\n", SStr
        cabbageCreate "image", SStr
        kCount = kCount + 1
    od
    turnoff
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z

i 1 0 0
i 2 3 0
</CsScore>
</CsoundSynthesizer>

instr 1 seems to add the widget, but I donā€™t know about instr 2. Is there some sort of debug mode that will display any cabbage calls? I only assume that my loop is executing because in my real program, the printsk calls print out the text being passed to cabbageCreate, and it is exactly the same as the string passed in instr 1.

Screen Shot 2021-05-19 at 1.34.04 PM

This is a screenshot of the window after running. As you can see, the image added by instr 1 is displaying in the upper left corner, but none of the images added by instr 2 are showing just down to the lower right corner of the instr 1 image.

the cabbageCreate opcodes is i-time only. So you should use any k-rate string opcodes with them. Look at the example I posted online, and the ones in the docs, they all use i-rate loop and string opcodes. :+1:

Iā€™m going to make a wild guess and assume that when you say ā€œi-timeā€, you are refering to the orchestra initialization time, right? Before anything else runs? I changed the time for instrument two to 0, and the elements created there now show up (I also changed the opcodes to run at i-rate). So, effectively, the only place you can use ā€˜cabbageCreateā€™ is in instruments that have a start time of 0, or can we also make use of it within the orchestra header?

Either way, I think armed with this new information (to me at least), I can proceedā€¦

You can read all about i-rate and k-rate here. Because the plugins host needs to know about parameters when it loads the plugin, we have to create them straight away. Anyhow, sounds like you have already progressed :+1:

1 Like

So is it not possible to load widgets/controls dynamically when an instr is scheduled within the running instance? For example - scheduling a new instrument on a button press and that then creates a widget?

You can create all the widgets you need at the start of your program, and then use visible(1) to show it, and visible(0) to hide it.

Something I have done is using the active and visible identifier for similar stuff, and combined it with if-statements for them to show and work when theyā€™re supposed to work.

Right. I have something similar set up for another instrument.

My use case is a bit different though in this particular scenario - I wish to load/unload UIs into a designated groupbox depending on what the user has installed in a particular folder. I think I can do a workaround - just not sure what that will be yet!

In the programs that Iā€™ve written, Iā€™ve only been able to use cabbageCreate before any k-rate code gets run. In one of my versions, I was deferring the creation of these widgets until about .5 seconds into the ā€œscoreā€, but as such it never created anything. But once the instrument ran at time 0, it created everything I asked for.

1 Like

The thing is that the host need to register these widgets when it creates its editor. So although it may be possible to offset the creation of them, itā€™s not a good idea in practice. And you most certainly can not delay the creation of any widget that the host has access to, i.e., any parameter widgets.

If each UI is more or less similar in terms of widgets, you could keep using the same ones for each UI. In a way, each UI becomes a preset of sorts?

Yeah, makes sense. And thought about this as an option. In my case I think it comes down to being able to swap multiple interfaces, like a library of synths, on the fly. But perhaps preloading all of them and displaying as they are selected would make sense. Would it be a ton of overhead to have, say 80 widgets loaded at a time?

80 widgets shouldnā€™t be an issue with the new GUI system. But if each of these are automatable by the host it will be hard to navigate. But will you really have 80 widgets for one ā€˜synthā€™? I thought you would maybe have a maximum of 10/15 controls on any synth. My thinking was that you could just reuse those 1015 widgets for each interface?

Hello again! cabbageCreate breaks somehow when activated via UDP. Let me explain.

With the ā€”port option, Csound opens a UDP connection to listen for commands. Using nc or similar UDP client, you can send orc code to be live-compiled, trigger i-events, and send values to channels. It works with Cabbage, and I created a cabbage file that is a blank canvas for livecoding.

I created this instrument:

instr 127
cabbageCreate ā€œrsliderā€, ā€œbounds(10,10,40,40)ā€
ares oscil ampdbfs(-12), 440
outs ares, ares 
endin 

Which as you can see creates a knob widget. I also include a 440Hz tone to indicate when the instrument is playing for debugging purposes.

It works fine when I activate it from a regular score i-statement:
i 127 0 1
Although for some reason it doesnā€™t work if p3 is 0 (initialization pass only). The knob is created and visible. Edit: actually I take it back, it works fine from the score when p3 is 0.

However if I use the same orchestra code and try to activate it from the UDP client with this command
&i127 0 1
I hear the tone but no knob is created.

You can imagine the cool things you could do with livecoding if widgets could be created on the fly.

However, working around this issue by simply generating a large number of invisible widgets at the beginning of performance and then using identifiers to marshal them as needed, seems to be an acceptable solution for my purposes. Still I thought it was odd.

Update: The same problem also appears when activating instr 127 using real-time MIDI input. The tone is heard, meaning instr 127 is being turned on, but the knob doesnā€™t appear. So it must be a problem that doesnā€™t have anything directly to do with UDP, but rather whenever the instrument containing cabbageCreate is activated in real-time.

I did download the latest build off the Azure page.

All widgets have to be created when the plugin window first opens. I should put a note about this in the docs as I think itā€™s causing some confusion. Itā€™s not possible to dynamically create them after this.

1 Like