Sounds great! Looking forward to start implementing the new trigger.
I suppose this will trigger also for sliders when they cross the target value but in both directions (from higher to lower and lower to higher), right?
Sounds great! Looking forward to start implementing the new trigger.
I suppose this will trigger also for sliders when they cross the target value but in both directions (from higher to lower and lower to higher), right?
Yes, correct. Let me think about this. We could add an optional trigger-esque mode parameter?
That’s what I had in mind all along!
I’ve updated this so the additional parameters behave like the trigger opcode. I modified the original post here so that it contains a description of the most current version. Need testing, but seem to mimic the Csound trigger opcode now, albeit with channel array input.
Fantastic! I think this addition will be very useful! And it now beats the Csound (non) alternatives even by a larger margin. Thanks! I’ll get on with it tonight.
[edit] I hope the stew will be ready soon …
MacOS stew is available, just click into the pipeline. Steinberg are doing maintenance on their servers at the moment which means the ASIO SKD is not currently available. This is stopping the Windows builds from completing for now. I hope it will be sorted shortly.
I did some tests and it doesn’t seem to work as I expected. Or maybe I am doing something wrong?
the new trigger-esque options don’t seem to have any effect at all, e.g. sliders are triggering all the time, tried with i-rate and k-rate option values (how are they supposed to be set?)
radioGroup
-ed buttons don’t trigger when going back and forth between any two buttons in a group
Here is my testing playground (please comment/uncomment parts of it).
<Cabbage>
form caption("Cabbage Changed") size(430, 290) pluginId("tl01") guiMode("queue")
rslider bounds(10, 10, 100, 100), channel("slider1") _isSlider(1)
rslider bounds(110, 10, 100, 100), channel("slider2") _isSlider(1)
rslider bounds(210, 10, 100, 100), channel("slider3") _isSlider(1)
rslider bounds(310, 10, 100, 100), channel("slider4") _isSlider(1)
combobox bounds(20, 118, 80, 20) channel("combo1"), channelType("string") _isCombo(1)
combobox bounds(120, 118, 80, 20) channel("combo2"), channelType("string") _isCombo(1)
combobox bounds(220, 118, 80, 20) channel("combo3"), channelType("string") _isCombo(1)
combobox bounds(320, 118, 80, 20) channel("combo4"), channelType("string") value("1") _isCombo(1)
label bounds(8, 158, 412, 21) channel("label1"), align("left"), fontColour(0, 0, 0, 255) text("Most recently changed widget:")
image bounds(6, 234, 412, 41) channel("image15")
button bounds(20, 246, 80, 20) channel("RG1") text("1", "1") radioGroup("RG") colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _isGrouped(1)
button bounds(120, 246, 80, 20) channel("RG2") text("2", "2") radioGroup("RG") colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _isGrouped(1)
button bounds(220, 246, 80, 20) channel("RG3") text("3", "3") radioGroup("RG") colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _isGrouped(1)
button bounds(320, 246, 80, 20) channel("RG4") text("4", "4") radioGroup("RG") colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _isGrouped(1)
button bounds(20, 198, 80, 20) channel("NonLatched1") text("X", "X") latched(0) colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _latched(0)
button bounds(120, 198, 80, 20) channel("NonLatched2") text("Y", "Y") latched(0) colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _latched(0)
button bounds(220, 198, 80, 20) channel("NonLatched3") text("Z", "Z") latched(0) colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _latched(0)
button bounds(320, 198, 80, 20) channel("NonLatched4") text("W", "W") latched(0) colour:1(255, 255, 0, 255) fontColour:1(160, 0, 0, 255) _latched(0)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -m0d -+rtmidi=NULL
</CsOptions>
<CsInstruments>
ksmps = 32
instr 1
;SWidgetChannels[] cabbageGetWidgetChannels "_isSlider(1)"
;SWidgetChannels[] cabbageGetWidgetChannels "_latched(0)"
SWidgetChannels[] cabbageGetWidgetChannels "_isGrouped(1)"
;prints SWidgetChannels[0]
;SUpdatedChannel, kTrig cabbageChanged SWidgetChannels
;cabbageSet kTrig, "label1", sprintfk("text(\"Last updated widget: %s\n\")", SUpdatedChannel)
;kIndex, kTrig cabbageChanged SWidgetChannels
;cabbageSet kTrig, "label1", sprintfk("text(\"Last updated widget: %s\n\")", SWidgetChannels[kIndex])
;printk2 kIndex
kIndex, kTrig cabbageChanged SWidgetChannels, k(0.5), k(0)
if kTrig == 1 then
printk 0, kIndex
endif
endin
</CsInstruments>
<CsScore>
i1 0 z
</CsScore>
</CsoundSynthesizer>
I’d like it to do something like this (comment one of the fillarray
blocks)
kON[] fillarray \
trigger(cabbageGetValue:k("NonLatched1"),0.5,0),\
trigger(cabbageGetValue:k("NonLatched2"),0.5,0),\
trigger(cabbageGetValue:k("NonLatched3"),0.5,0),\
trigger(cabbageGetValue:k("NonLatched4"),0.5,0)
kON[] fillarray \
trigger(cabbageGetValue:k("RG1"),0.5,0),\
trigger(cabbageGetValue:k("RG2"),0.5,0),\
trigger(cabbageGetValue:k("RG3"),0.5,0),\
trigger(cabbageGetValue:k("RG4"),0.5,0)
kind = 0
until kind == 4 do
if kON[kind] == 1 then
printk 0, kind
endif
kind += 1
od
My bad, seems I only added it to the version:
SChannel, kTrig cabbageChanged ...
I’ll update the
kIndex, kTrig cabbageChanged ...
now. Give me a sec…
The slider now works 50%. Could you please check my code for all 3 types of widgets.
the kIndex doesn’t change?
radioGroup doesn’t work - still seems the same as I noted before
Maybe you could consider replicating this behaviour if possible - instead of printing in the until loop I’d of course do more interesting things with the index.
It’s working for me 100% of the time. I tested with the trigger opcode in Csound and they both seem to behave the same?
Sorry, my fault That’s fixed now
The problem is that when clicking on a radio group, two buttons change state at the same time, or thereabouts. This means that we intermittently miss one. Looks like there is little we can do in this case because it’s not a single channel changing but two.
One possible way around this would be to build your own radiogroup using a slider and some images. Each time you update a button(image) in the group it would send an update to the slider. That way you only have to keep track of the slider position? Here is n example:
<Cabbage>
form caption("Cabbage Changed") size(430, 390) pluginId("tl01") guiMode("queue")
image bounds(10, 296, 90, 30) channel("radioImage1"), value(1), colour("black"), _isGrouped(1)
image bounds(110, 296, 90, 30) channel("radioImage2"), value(1), _isGrouped(1)
image bounds(210, 296, 90, 30) channel("radioImage3"), value(1), _isGrouped(1)
image bounds(310, 296, 90, 30) channel("radioImage4"), value(1), _isGrouped(1)
hslider bounds(-1000, 310, 90, 20), alpha(0), popupText(0), channel("radioGroup1"), range(0, 3, 0, 1, 1)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -m0d -+rtmidi=NULL
</CsOptions>
<CsInstruments>
ksmps = 32
opcode Radio, 0,SS[]i
SSliderChannel, SChannels[], iIndex xin
kIndex, kTrig cabbageChanged SChannels
kCnt = 0
printf "Changed:%d", kTrig, kIndex
if kTrig == 1 then
while kCnt < 4 do
cabbageSet kTrig, SChannels[kCnt], (kCnt == kIndex ? "colour(255, 0, 0)" : "colour(255, 255, 255)")
kCnt+=1
od
cabbageSetValue SSliderChannel, kIndex
endif
endop
instr 1
SWidgetChannels[] cabbageGetWidgetChannels "_isGrouped(1)"
Radio "RadioGroup1", SWidgetChannels, 1
kIndex, kTrig cabbageGetValue "RadioGroup1"
printf "RadioGroupIndex:%d", kTrig, kIndex
endin
</CsInstruments>
<CsScore>
i1 0 z
</CsScore>
</CsoundSynthesizer>
You might want to make each one of those image interactive(0)
so they don’t confuse users in a DAW. In fact, I wonder can you just use a slider with your current setup too? Let me take a look…
I guess you could do this:
kIndex, kTrig cabbageChanged SWidgetChannels, k(0.5), k(0)
kCurrentRadio init 0
if kTrig == 1 then
kCnt = 0
while kCnt < 4 do
if cabbageGetValue:k(SWidgetChannels[kCnt]) == 1 then
kCurrentRadio = kCnt
endif
kCnt += 1
od
endif
printk2 kCurrentRadio
Until radioGroups behave like a single object I think we might be stuck with some kind of hybrid solution
Thank you very much for explanations and examples! Very helpful!
Not sure what you mean, but a slider would constrain changes to be monotonic? What about a sequence of jumps like 1 4 2 5 1?
The idea was to make it less taxing on CPU. I’m wandering if this isn’t going to be more expensive?
I was thinking that the index would determine the position of the slider? Even still, you could keep the sequence in an array and use the slider position to index them?
I still think this might be less taxing than the trigger/cabbageGetValue solution? But it would need testing…
Right, because cabbageGetValue
is only invoked if triggered by cabbageChanged
and the latter one is super efficient (?). Taken to extreme, one could wrap all the widgets cabbageGetValues like this, and it would be interesting to see an efficiency curve as a function of number of widgets - but this is my physicist brain waking up now
That’s my reading of it. In your example you are calling lots of opcodes on every k-cycle. In this hybrid solution it only happens when something changes. In fact, you could guard your fillarray
code with a trigger from the cabbageChanged opcode too. That way you don’t have to make whole changes to your code, but will still prevent it from being hit on each k-cycle
Claver idea! Many ways of making an efficient hybrid
I think my confusion with this comes from assuming that cabbageGetValue
would only operate when widget changes value, maybe that is true when it stands outside array, loop, or is it ever true? I guess at least my understanding of cabbageSet
is true, which updates widgets only when a new k-value appears on its doors?
cabbageGetValue
will query a channel on every k-cycle UNLESS you use the optional trigger input, i.e,
k1 cabbageGetValue "slider", metro(1)
Yes, the k-rate version will not do anything unless you trigger it with a k-value of 1. The i-rate version will trigger at init time and never again.