Cabbage Logo
Back to Cabbage Site

Several cabbageSet on image-widgets causes blinking

Hello. I have a small issue with a custom scrubber I’m making. If you hold several keys simultaneously, the scrubbers will start blinking.

Wondering if anyone has any tips on how to solve this.

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

instr 2
    iWidget = 0
    giCount = 0
    
    // Create scrubbers
    while iWidget < 20 do
        SScrubber sprintf "bounds(-10, 0, 2, 100), channel(\"Scrubber%d\"), colour(255, 255, 255, 255)", iWidget+1
        cabbageCreate "image", SScrubber
        iWidget += 1
    od
endin

instr 1
    SCurrentScrubber sprintfk "Scrubber%d", giCount
    giCount += 1
    
    if (giCount == 20) then
        giCount = 0
    endif
    
    kEnv madsr .5, .2, 1, .4

    kPositionX = kEnv*200
    kSizeY = 100
    
    kMetro = metro(60)

    cabbageSet kMetro, SCurrentScrubber, "bounds", kPositionX, 10, 1, kSizeY
endin

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

Looks like a painting issue. I’m not sure how best to address it. You can try calling toFront() before moving a scrubber, but if you are pressing several keys at once I’m not sure this will help. You could try using an image with a svgElement() and draw the scrubbers to a single image. It will be a bit of work, but that should fix the problem.

:grimacing:

You mean similarly to the svgElements.csd example that uses path-elements?

I’m afraid so. In this instance Cabbage is only painting a single image to screen rather than 20…

I’ll give it a try :+1:

Must it be a path-element? Or would this also be usable?

<svg width="266" height="100" viewBox="0 0 266 100" fill="none" xmlns="http://www.w3.org/2000/svg">
	<rect width="266" height="100" fill="white"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
</svg>

This will work. Any valid xml code will be drawn. Now comes the tricky part, moving each of those rectangles :slight_smile:

But you don’t need to pass the <svg width="266" height="100" viewBox="0 0 266 100" fill="none" xmlns="http://www.w3.org/2000/svg"> stuff, just the svg elements, i.e,

    cabbageSet "image1", "svgElement", {{
    <rect width="266" height="100" fill="white"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
	<rect width="1" height="100" fill="#D9D9D9"/>
    }}

And to move it along the x-axis, something like this? Couldn’t get that to work.

kEnv line 0, 1, 100

cabbageSet "image1", "svgElement", {{
    <rect width="266" height="100" fill="white" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
    <rect width="1" height="100" fill="#D9D9D9"/>
}}, kEnv

Some issues here:

1: You’re using an i-rate version of Cabbage set
2: You’re also using i-rate string code, you need to generate your string using sprintfk.

Here’s an example:

<Cabbage>
form caption("Star") size(200, 200), guiMode("queue") pluginId("def1")
image bounds(0, 0, 300, 300), channel("image1") 
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d 
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

instr 1
    kLine line 0, 10, 100
    cabbageSet, metro(10), "image1", "svgElement", sprintfk({{
    <rect width="266" height="100" fill="red"/>
	<rect width="1" height="100" fill="#D9D9D9" x="%d"/>
    }}, kLine)    
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
;starts instrument 1 and runs it for a week
i1 0 [60*60*24*7] 
</CsScore>
</CsoundSynthesizer>

Thanks, but how would I keep track of each individual rectangle without overwriting the previous one?

I thought I could get lucky and set the id, but that doesn’t work…

<Cabbage>
form caption("Untitled") size(400, 300), guiMode("queue"), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
image bounds(26, 22, 160, 120) channel("image1")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1
giCount init 0

instr 1
    giCount += 1
    
    if (giCount == 20) then
        giCount = 0
    endif
    
    kEnv madsr .5, .2, 1, .4
    kEnvMulti = kEnv*100

    cabbageSet metro(60), "image1", "svgElement", sprintfk({{
        <rect width="1" height="100" fill="#D9D9D9" x="%d" id="%d"/>
    }}, kEnvMulti, giCount)   

endin

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

You’ll have to create as many rectangles as scrubbers. I only reduced this to one to simplify the code I was posting. So it would look more like this:

cabbageSet metro(10), "image1", "svgElement", sprintfk({{
    <rect width="266" height="100" fill="white" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    <rect width="1" height="100" fill="#D9D9D9" x="%d" />
}}, k1, k2, k3, k4, k5, k6, k7, k8, k9...) 

Of course wen I see that code I think immediately that there must be a way of writing it using a loop so we don’t have to keep repeating ourselves, but for now it’s good to get a proof of concept…

Yes, that’s what I thought, but how would I set each individual k-variable without having to overwrite the last one from the previous instance of the instrument? Because this way, setting all the k-variables in every instrument instance is required correct?

I’m not sure I follow, you will have to draw everything from scratch on each frame…

Say you hold a chord. You want to draw a scrubber for each note held simultaneously. Doing it this way would overwrite every previous note no?

<Cabbage>
form caption("Untitled") size(400, 300), guiMode("queue"), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
image bounds(26, 22, 160, 120) channel("image1")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

instr 1
    kEnv madsr .5, .2, 1, .4
    kEnvMulti = kEnv*100
    
    k1 = kEnvMulti
    k2 = kEnvMulti
    k3 = kEnvMulti

    cabbageSet metro(10), "image1", "svgElement", sprintfk({{
        <rect width="266" height="100" fill="white" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    }}, k1, k2, k3) 
endin

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

I think it will need to run the image drawing in an ‘always on’ instrument and feed it values from your midi triggered instrument. But you will have to overwrite the previous image all the time, that’s the nature of the svgElement(), you need to draw each frame over and over again.

That was a great suggestion :clap:

<Cabbage>
form caption("Untitled") size(400, 300), guiMode("queue"), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
image bounds(26, 22, 160, 120) channel("image1")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

giCount init 0

instr 1
    giCount += 1
    
    if (giCount == 3) then
        giCount = 0
    endif
    
    kEnv madsr .5, .2, 1, .4
    
    if (giCount == 0) then
        gkEnvMulti1 = kEnv*100
    elseif (giCount == 1) then
        gkEnvMulti2 = kEnv*100
    elseif (giCount == 2) then
        gkEnvMulti3 = kEnv*100
    endif
endin

instr 2
    k1, k2, k3 init 0

    k1 = gkEnvMulti1
    k2 = gkEnvMulti2
    k3 = gkEnvMulti3   
    
    cabbageSet metro(10), "image1", "svgElement", sprintfk({{
        <rect width="266" height="100" fill="white" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
        <rect width="1" height="100" fill="#D9D9D9" x="%d" />
    }}, k1, k2, k3) 
endin

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

i2 0 z
</CsScore>
</CsoundSynthesizer>

That works quite well. The nice thing is you can style the hell out of this. You’re no longer restricted to Cabbage images.

You’re right :slight_smile: . Maybe consider adding this to the examples? Could be useful for others :+1: