Cabbage Logo
Back to Cabbage Site

cabbageGetValue init behaviour changed

Btw, it wasn’t always as you have experienced it. The first iteration of these opcodes didn’t return 1 on the first perf pass. Then they did. Now they no longer do, but can be instructed to do so. I hope this is the end of their evolution :rofl:

In my mind channels do change on the first pass, they are “born”, they acquire values from widgets. Again I’d refer to changed.

I understand your points, but I’d still revert the default and the optional behaviour for consistency reason (with Csound and Cabbage). Please let me know if you plan to change also the cabbageChanged.

When I wrote these opcodes I never intended for the changed trigger to fire on startup, because I wanted it to be different to changed, which always bugged me in the context of Cabbage widgets. A typical use of this opcode would be to test for a button press, and if so, trigger another instrument, something like this:

kEffectButton, kTrigEffectButton cabageGetValue "enableEffectButton"
if kTrigEffectButton == 1 then
    //start effect instrument
endif

This is very readable. If someone pushes the button they will start the effect instrument. Under your proposal this code would now trigger the effect instrument when it first runs. I can see my students scratching their head wonder why the heck the effects instrument started even though they never pushed the effect button.

For me this seems like it should be the default behaviour. I think we might have to call in the big guns @iainmccurdy and @Oeyvind

I always found the first-k-rate-pass trigger from changed to be inconvenient and would occasionally have to construct some contortion - normally with if (timeinstk:k() != 1) ... - to suppress it. With changed2 I think there was a belief that this behaviour was a mistake as its manual entry states: “Unlike the opcode changed this opcode will never report the first cycle as a change.” My own belief is that it was a mistake in changed.

Perhaps a more logical way of getting Csound to do something on the first k-rate pass is something like:

if (timeinstk:k() == 1) then...

I don’t consider a widget as having changed if it has only been created as I normally use these kinds of change triggers as sensors for user interaction with the GUI, as I suspect most others do too. I can fully appreciate that others may have found a use for these triggers on the first k-rate pass, but that would strike me more as making good use of unexpected behaviour. We should try and design these things to be as intuitive and logical as possible for the benefit of newcomers.

Prompted by reading through this thread though, I did test a few things for myself and have a few comments:

  • The trigger behaviour of cabbageGetValue seems mostly fine to me now and the fact that it has a proper value at i-time is good and keeps it in line with CsoundQt widgets and invalue.

  • One curious outcome is that cabbageGetValue's trigger is 1 at i-time.

    k1,kT cabbageGetValue “1”
    print i(kT)

This doesn’t seem right, but I can’t imagine anyone would ever encounter it as a problem.

@Samo is right in that the behaviour of cabbageChanged is inconsistent with the new behaviour of cabbageGetValue in that it outputs 1 on the first k pass, like changed.

SChans[] cabbageGetWidgetChannels
S1, kT cabbageChanged SChans
printk2 kT

outputs:

i1     1.00000
i1     0.00000

(It also outputs a 1 at i-time.)

At the risk of becoming very unpopular, I would vote for changing the behaviour of cabbageChanged to move in line with that of cabbageGetValue.

I wouldn’t be a fan of cabbageGetValue2, again for the reason that it would create confusion for newcomers.

I too have felt the pain of having to respond to Rory’s capricious changes of Cabbage syntax. But that is his right and the protection of backwards compatibility with Csound can seem like a ball and chain a times when an opcode design was clearly a blunder in the first place. In his defence, he does always do these things to improve efficiency and logic and he does consult the community. I do say a little prayer every night that Rory won’t decide to change everything again though.

This is logical but I wanted to avoid another checking for performance reason.

That’s what I saw as consistent with Csound but not consistent with Cabbage.

I appreciate this chat and I’ll be fine with the verdict of the “big” guns but I love the new option for cabbageGetValue and I hope the option will remain! :grimacing:
And I feel the same as this if the option is included (that’s indeed what I had in mind when asking about cabbageChanged):

Thanks guys. I hope I didn’t cause any offence to Iain and @Oeyvind in referring to them as the big guns, it’s just they have been with Cabbage from the very start. I find their input invaluable, as I do yours @Samo. It’s a right mess I’ve got us into here :rofl:

  • I have no intention of removing the new on-first-cycle option to cabbageGetValue as I think we can can all see use cases for this.
  • I can change the output of cabbageChanged to match that of cabbageGetValue. But I’m afraid that adding a new on-first-cycle option might be a bit clunky as there are already two optional parameters. However, I could add a new mode which would cause a trigger on first-cycle if that would be helpful?
  • I will also update that anomaly regarding the k-rate version of cabbageGetValue outputting a trigger at i-time.

I think after all that is done we can call this opcode finally finished, and move swiftly on to the next one :face_with_hand_over_mouth:

1 Like

The optional argument seems like the best solution.

WRT cabbageChanged, I agree that growing lists of optional arguments can be a pain, particularly when you have to input values for arguments you don’t care about to get to the one you do care about. A bank of switches based on the sum of powers of two in a single argument can be quite neat, like is used for imode in grain3. Would this work?
https://www.csounds.com/manual/html/grain3.html

Yes, that might help if we need to add any more, and could become a pattern for future opcodes. It seems that it’s the string channels that are causing cabbageChanged to trigger on startup. I’m just taking a look now.

Ok, I’ve fixed cabbageChanged. I haven’t pushed yet, because I’m not sure how the trigger on first k-cycle should work. It’s easy to return a trigger, but what should the index / channel be? The first or the last widget from the array?

Ok, to get cabbageChanged to trigger on the first perf pass you can set its mode to 3:

kIndex, kTrig cabbageChanged SChannels, 0, 3

In this case it will return 0 as the index, or if using the channel output version, the first channel. I’ve pushed this to GIT now, Thanks again for all the input. Iain, fwiw, I really don’t want to revisit these opcodes again either :slight_smile:

Thanks Rory for the options! :yum: I’ll let you know how it goes when I get the time to test it and start updating my old instruments. :fearful: I also hope there will be no need for further revisions. :crossed_fingers: :pray:

1 Like

So far so good with the cabbageGetValue and also with the recent cabbageSet fix :slight_smile:
I’m fine with the old cabbageChanged but I don’t see the point of the last update to it.

The way I’m using it is like this:

;button bounds(200, 10, 32, 18) channel("B1")  text("1", "1") latched(0) colour:0(100, 80, 0, 255) colour:1(255, 255, 0, 255) fontColour:1(0, 0, 0, 255) _buttons(1)
;button bounds(200, 30, 32, 18) channel("B2")  text("2", "2") latched(0) colour:0(100, 80, 0, 255) colour:1(255, 255, 0, 255) fontColour:1(0, 0, 0, 255) _buttons(1)
;button bounds(200, 50, 32, 18) channel("B3")  text("3", "3") latched(0) colour:0(100, 80, 0, 255) colour:1(255, 255, 0, 255) fontColour:1(0, 0, 0, 255) _buttons(1)

and then:

Sbuttons[] cabbageGetWidgetChannels "_buttons(1)"
kIndexB, kTrigB cabbageChanged Sbuttons, .5, 1

if kTrigB == 1 then 
    printk 0, kIndexB
endif

This works just fine! But using it with the option 3
kIndexB, kTrigB cabbageChanged Sbuttons, .5, 3
only triggers on the first pass an no more.

Wasn’t this option “supposed” to be like 0 but with added first pass trigger (0 + 3 = 3) and then we have the other cases 1 + 3 = 4, 2 + 3 = 5?

Thanks again for your wonderful work and patience!

You’re right, it should trigger on the first pass and all subsequent cycle if need be. I’ll fix that tomorrow :+1:

Will the other combinations be included as well?

Actually in the end I just added an extra mode, 3. From the updated docs:

kMode = 3 : Special mode that will cause a trigger on the first k-cycle of performance.

OK. I guess the other options will not be necessary since the widget starts at zero - as far as the default value is not set to negative, in which case one might need an UP->DOWN trigger?

This should be fixed now. When you add mode 3, the threshold parameter is not taken into account.