Cabbage Logo
Back to Cabbage Site

Toggling buttons not working as expected

Posting this in Csound Noobs instead of Cabbage Slugs as I haven’t determined if this is a bug or just me yet lol

Using Cabbage 2.9.98

I’m building a terrain based footstep designer and i’ve had some trouble updating button UI text and the instrument state depending on button states. Hope somebody can spot the issue:

I have three buttons currently:
Drone: On/Off
Steps: On/Off
Sneak: On/Off

button bounds(10, 50, 80, 30), channel("toggleDrone"), identChannel("toggleDroneID"), text("Drone: OFF"), latched(1), colour(150, 0, 0), fontColour(255, 255, 255)
button bounds(100, 50, 80, 30), channel("toggleSteps"), identChannel("toggleStepsID"), text("Steps: OFF"), latched(1), colour(0, 150, 0), fontColour(255, 255, 255)
button bounds(190, 50, 80, 30), channel("toggleSneak"), identChannel("toggleSneakID"), text("Sneak: OFF"), latched(1), colour(150, 150, 0), fontColour(255, 255, 255)

I have an instrument called DetectTriggers:

instr DetectTriggers

kToggleDrone chnget "toggleDrone"
kTrigDrone changed kToggleDrone

kToggleSteps chnget "toggleSteps"
kTrigSteps changed kToggleSteps

kToggleSneak chnget "toggleSneak"
kTrigSneak changed kToggleSneak

; Drone Button
if kTrigDrone == 1 then
    if kToggleDrone == 1 then
        
        chnset "text(\"Steps: OFF\")", "toggleStepsID"
        chnset "colour(0, 0, 0)", "toggleStepsID" ; Set Steps colour to black when OFF

        chnset "text(\"Drone: ON\")", "toggleDroneID"
        chnset "colour(150, 0, 0)", "toggleDroneID" ; Set Drone colour for ON state 
        
        event "i", "ControlSteps", 0, 0
        
        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
               
    else 
        chnset "text(\"Drone: OFF\")", "toggleDroneID"
        chnset "colour(0, 0, 0)", "toggleDroneID" 
    endif
endif

; Steps Button
if kTrigSteps == 1 then
        if kToggleSteps == 1 then

        chnset 0, "toggleInstrument"
        chnset "text(\"Drone: OFF\")", "toggleDroneID"
        chnset "colour(0, 0, 0)", "toggleDroneID" 

        chnset "text(\"Steps: ON\")", "toggleStepsID"
        chnset "colour(0, 150, 0)", "toggleStepsID" 

        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
        
        event "i", "ControlSteps", 0, 0
        event "i", "ControlSteps", 0, -1
        
    else
        chnset "text(\"Steps: OFF\")", "toggleStepsID"
        chnset "colour(0, 0, 0)", "toggleStepsID"
        event "i", "ControlSteps", 0, 0
    endif
endif

; Sneak Button
if kTrigSneak == 1 then
    if kToggleSneak == 1 then
        chnset "text(\"Sneak: ON\")", "toggleSneakID"
    else
        chnset "text(\"Sneak: OFF\")", "toggleSneakID"
    endif
endif 

endin

DetectTriggers is called continuously like so in the Cscore:

i “UpdateMixer” 0 z ;
i “DetectTriggers” 0 z ;

My aim is to have Steps turn off if Drone is turned on, and vice versa Drone should turn off if Steps is enabled, as they represent two different playback modes.

Drone = ON should disable Steps processes, Steps = ON should disable Drone processes and both cases should change the text/colour appropriately to reflect the state of each button.

For some reason, the text is not updating for the Drone or Steps button when clicked, Sneak however works fine and toggles the text to ON/OFF

I’ve tried simplifying the code through testing Drone button in isolation (removing Steps button) and only leaving the visual changing logic, the button changes colour fine but the text still doesn’t update?:

; Simplified Drone Button

if kTrigDrone == 1 then
    if kToggleDrone == 1 then

        chnset "text(\"Drone: ON\")", "toggleDroneID"
        chnset "colour(150, 0, 0)", "toggleDroneID" ; Set Drone colour for ON state 
    else 
        chnset "text(\"Drone: OFF\")", "toggleDroneID"
        chnset "colour(0, 0, 0)", "toggleDroneID" 
    endif

endif

I feel like there may be an issue in my attempted logic. Can you spot any possibly conflicting conditions in my code that may cancel out my expected changes?

The text() identifier is not usually applied in this way for buttons because buttons take two text elements. Why don’t you just do something liek this when declaring your button:

button bounds(10, 50, 80, 30), channel("toggleDrone"), identChannel("toggleDroneID"), text("Drone: OFF", "Drone: ON")

If you need to dyanmically update the button text, it’s best to do something like this:

chnset "text(\"Drone: 0\", \"Drone: 1\")", "toggleDroneID"

Note that you must use guiMode(“polling”) for this to work. With guiMode(“queue”) you need to make use of the cabbageGet/Set opcodes.

As for thew logic of having one button disable the other, have you looked at making them part of a radio group?

button bounds(10, 50, 80, 30), colour:1("red"), radioGroup(99)
button bounds(100, 50, 80, 30), colour:1("red"), radioGroup(99)
button bounds(190, 50, 80, 30), colour:1("red"), radioGroup(99)

With this setup, only one button in the group can be enabled at a time.

Hey Rory thanks for the response! Implemented that and it indeed works. Though, upon using it I decided against radio groups. If I use radio groups, if button A is set to ON, I can’t set button A to OFF by pressing button A again, I need to press/set button B to ON in order to set button A to OFF. This is indeed the kind of behaviour I wished for in my og post, but I still wanted to be able to toggle buttons on and off individually, I just didn’t know it yet.

Heres my updated solution:

button bounds(10, 50, 80, 30), channel("toggleOneStep"), identChannel("toggleOneStepID"), text("OneStep"), latched(0), colour(150, 0, 0), fontColour(255, 255, 255)
button bounds(90, 50, 80, 30), channel("toggleSteps"), identChannel("toggleStepsID"), text("Steps: OFF", "Steps: ON"), latched(1), colour(0, 150, 0), fontColour(255, 255, 255)
button bounds(170, 50, 80, 30), channel("toggleSneak"), identChannel("toggleSneakID"), text("Sneak: OFF" , "Sneak: ON"), latched(1), colour(150, 150, 0), fontColour(255, 255, 255)
button bounds(250, 50, 80, 30), channel("toggleDrone"), identChannel("toggleDroneID"), text("Drone: OFF" , "Drone: ON"), latched(1), colour(0, 0, 150), fontColour(255, 255, 255)

instr DetectTriggers

kToggleOneStep chnget "toggleOneStep"
kTrigOneStep changed kToggleOneStep

kToggleSteps chnget "toggleSteps"
kTrigSteps changed kToggleSteps

kToggleSneak chnget "toggleSneak"
kTrigSneak changed kToggleSneak

kToggleDrone chnget "toggleDrone"
kTrigDrone changed kToggleDrone

; OneStep Button
if kTrigOneStep == 1 then
    if kToggleOneStep == 1 then
        
        chnset "colour(150, 0, 0)", "toggleOneStepID"          
        
        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
               
    else 
        chnset "colour(0, 0, 0)", "toggleOneStepID" 
    endif
endif

; Drone Button
if kTrigDrone == 1 then
    if kToggleDrone == 1 then
        
        gkDroneActive = 1
        
        chnset "colour(0, 0, 150)", "toggleDroneID"      
        
        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
               
    else 
        gkDroneActive = 0
        
        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
        chnset "colour(0, 0, 0)", "toggleDroneID" 
    endif
endif

; Steps Button
if kTrigSteps == 1 then
    if kToggleSteps == 1 then
        
        chnset 0, "toggleInstrument"
        
        chnset "colour(0, 0, 0)", "toggleDroneID" 
        chnset "text(\"Steps: 0\", \"Steps: 1\")", "toggleStepsID"
        chnset "colour(0, 150, 0)", "toggleStepsID" 

        event "i", "Footstep", 0, 0
        event "i", "Footstep", 0, -1
        
        event "i", "ControlSteps", 0, 0
        event "i", "ControlSteps", 0, -1
        
        else
            chnset "text(\"Steps: 1\", \"Steps: 0\")", "toggleStepsID"
            chnset "colour(0, 0, 0)", "toggleStepsID"
            event "i", "ControlSteps", 0, 0
    endif
endif

; Sneak Button
if kTrigSneak == 1 then
    if kToggleSneak == 1 then
        chnset "text(\"Sneak: ON\")", "toggleSneakID"
    else
        chnset "text(\"Sneak: OFF\")", "toggleSneakID"
    endif
endif 
endin

Thanks for the assist!!

I’m glad you found a solution :slight_smile: It’s easier for people to test your code if you include the full .csd, or simply upload it. Right now I find myself having to add your code to a new .csd, while trying to ensure I have everything in the right place. Btw, are you planning on bringing this into a game engine? If so you’ll need to be careful with the cabbageSet/Get opcodes, as they won’t work outside of Cabbage. But you can overload them by writing the equivalant as a UDO using chnget/chnset. :+1:

1 Like

One other issue I am facing however, is that the ‘Footstep’ instrument is only triggered when i’ve toggled any button that triggers the Footstep sound at least once? Any button that triggers the footstep sound works fine after this. It just doesn’t work on the first press. I’m sure it’s a noob error - It might be a bit overkill to post it all here, so i’ve attached the project incase anyone wishes to investigate:

Footsteps.csd (10.6 KB)

Okay I wasn’t aware of that thank you! I am indeed planning on triggering this instrument from Unity

I think you’re overcomplicating things here with the chnset calls to change colour. Cabbage will do that for you, just use colour:1() for your on colour and colour:0() for your off colour when you declare the button.

As for why it’s not triggering, I’m not quite sure. The footsteps instrument is being triggered, you can see this in the output console. Perhaps a channel is not right in that instrument until after it has played the first time? I’m not sure, I can take a deeper look later, but right now I have several small kids to look after :rofl:

1 Like

None of the buttons appear to work on first press (for example opening the file and pressing the Drone button).

Try changing them all to:

    ; OneStep Button
    if changed(kTrigOneStep) == 1 then
        if kToggleOneStep == 1 then

Seems to work for me.

1 Like

Hey thank you for the contribution:) I actually ended up resolving the issue late last night by changing the Footstep instrument to use i rate variables instead of k, seems to work better this way. I suppose because k is a constant update frequency and I don’t need to update it constantly in the Footstep instrument, as the channels are being already updated constantly, separately in UpdateMixer which is called indefinitely by CsScore with i “UpdateMixer” 0 z - Heres the UpdateMixer which uses the k rate:

instr UpdateMixer

; Calculate the weighted ADSR based on terrain mixtures
kGrassMix chnget "grassMix"
kWoodMix chnget "woodMix"
kStoneMix chnget "stoneMix"
kMudMix chnget "mudMix"
kWater chnget "waterMix"

kHeel chnget "heel"
kHeel = kHeel * 10

kWaterDepth chnget "waterDepth"

kSum max 0.1, (kGrassMix + kWoodMix + kStoneMix + kMudMix + kWater)

kDecay = (kGrassMix * giGrassD + kWoodMix * giWoodD + kStoneMix * giStoneD + kMudMix * giMudD + kWater * giWaterD) / kSum

if (gkDroneActive == 1) then 
kAttack = 1
kSustain = 1 
else
kAttack = (kGrassMix * giGrassA + kWoodMix * giWoodA + kStoneMix * giStoneA + kMudMix * giMudA + kWater * giWaterA) / kSum * kHeel
kSustain = (kGrassMix * giGrassS + kWoodMix * giWoodS + kStoneMix * giStoneS + kMudMix * giMudS + kWater * giWaterS) / kSum
endif

kRelease = (kGrassMix * giGrassR + kWoodMix * giWoodR + kStoneMix * giStoneR + kMudMix * giMudR + kWater * giWaterR) / kSum

; Update the UI sliders with these values
chnset kAttack, "attack"
chnset kDecay, "decay"
chnset kSustain, "sustain"
chnset kRelease, "release"

endin

Here are the variables that changed from k to i in the Footstep instrument:

iType chnget "terrainType"
iTrigger chnget "trigger"

iAttack chnget "attack"
iDecay chnget "decay"
iSustain chnget "sustain"
iRelease chnget "release"

iGrassMix chnget "grassMix"
iWoodMix chnget "woodMix"
iStoneMix chnget "stoneMix"
iMudMix chnget "mudMix"
iWater chnget "waterMix"

iSum max 0.1, (iGrassMix + iWoodMix + iStoneMix + iMudMix + iWater)

Here’s a fresh copy, does this work for you on the first instance with these changes implemented? -
Footsteps.csd (10.5 KB)

Though, your suggestion does strike me as something I should implement anyway? Would there be a specific benefit to use if changed instead of just if? It seems to work after implementing the changes I described so I may leave well alone, perhaps I only need to use if changed if I were to use k rate in Footsteps?

Thanks again for the help!

1 Like

Odd that one would need to use changed twice in this context though? kTrigOneStep is already assigned the value of a previous changed opcode.

That’s a good idea. Especially for instruments that are going to be triggered so often, and last for a short time. I’m glad you resolved it :slight_smile:

1 Like

@rorywalsh Perhaps odd, but it works, the if without changed didn’t, not without requiring the button being instantiated twice. I can’t test it with a console to compare it with the output of the first changed which is not in an if statement as the Android app doesn’t have one. Maybe I’ll test later in Csound as I am a bit curious now. This was actually from my Polyrhythm Metronome, the first newbie Cabbage instr I created last year and you helped me set up the buttons😆 (thanks again!).

@WillH0ward-1 glad you found a solution, if it works now without if changed then I don’t see any benefit to using it, Rory would certainly know better. The instruments I’ve created that used if changed all required k-rate variables to function so not sure if that makes a difference.

I will test your new one later but if it works for you then it’s likely fine.

1 Like

Thank you both! All our discussions help a huge amount.

1 Like

Interesting, works perfectly here, I’ll have to try & remember this for future reference.

The only reason I’m inclined to still use if changed is that it saves a few extra lines of code😄. Yes, I am that lazy.

I left you a comment in the Useful Learning Materials? thread as well.

1 Like