Cabbage Logo
Back to Cabbage Site

Reinit reinit i-rate variables which are functions of other i-rate variables

I hope to learn something here since I was not able to find info in the books.

I can reinit i-rate variables just fine, e.g. i1, i2. However, when I’m using for example products of those i1*i2 later on, it seems those are only calculated at the init pass and if I’m not using for example i3 = i1*i2 in the reinit block (rather than just i1*i2 ), I’m not getting an updated product. I was surprised by this since I though i-rate variables are still used at k-rate to calculate k-rate variables like k1 = i1*i2*k0. Even more peculiar I find the behaviour of constants (C) in k-rate calculations. The code below has 6 cases:

  1. no calculations with i1 and i2 - all good (updates on i1 & i2)
  2. calculation k(C)*i1*i2 - updates on i1 & i2 (why? not expected!)
  3. calculation C*k(i1)*i2 - updates only on i2 (not on i1?)
  4. calculation C*k(i2)*i1 - updates only on i1 (does conversion prevent updates?)
  5. calculation C*i1*i2 - no update (almost expected)
  6. calculation C*i1*k(i2) - no update (the order seems to matter?)

I’m confused by this. I hope you could help me sort this logic out. Thank you!

Change the horizontal slider to check the different cases and Freq1/Freq1 sliders to set new values to be updated in the reinit block after pushing the Trigger button.

bounds(0, 0, 0, 0)

form caption("Test Reinit") size(365, 125), pluginId("tri1")  

rslider bounds(56, 56, 60, 60) range(1, 2, 1, 1, 0.01) channel("Freq1") text("Freq1")
rslider bounds(246, 56, 60, 60) range(1, 2, 1, 1, 0.01) channel("Freq2") text("Freq2")

hslider bounds(8, 14, 323, 30) range(1, 6, 1, 1, 1) channel("Choose") 
label   bounds(-12, 4, 380, 12) text("i1/i2    k(C)*i1*i2    C*k(i1)*i2    C*k(i2)*i1    C*i1*i2    C*i1*k(i2)") fontColour(255, 255, 150, 255) channel("ChooseLabel")

button bounds(142, 66, 80, 40) channel("reTrigger") latched(0) text("Re-init")


-d -n -+rtmidi=null -M0 -m0d


sr = 44100
ksmps 	=  16
nchnls 	= 	2
0dbfs	=	1

instr 1

if cabbageGetValue:k("reTrigger") == 1 then
    reinit reTRIGGER

iFreq1 = cabbageGetValue:i("Freq1")
iFreq2 = cabbageGetValue:i("Freq2")

iFreqOut1 = 440 * iFreq1 
iFreqOut2 = 440 * iFreq2


if cabbageGetValue:k("Choose") == 1 then
    a1 poscil .01, iFreqOut1 // updates on Freq1 & Freq2
    a2 poscil .01, iFreqOut2
elseif cabbageGetValue:k("Choose") == 2 then    
    a1 poscil .01, k(440) * iFreq1 * iFreq2 // updates on Freq1 & Freq2
    a2 = a1
elseif cabbageGetValue:k("Choose") == 3 then
    a1 poscil .01, 440 * k(iFreq1) * iFreq2 // updates only on Freq2
    a2 = a1
elseif cabbageGetValue:k("Choose") == 4 then
    a1 poscil .01, 440 * k(iFreq2) * iFreq1 // updates only on Freq1
    a2 = a1    
elseif cabbageGetValue:k("Choose") == 5 then   
    a1 poscil .01, 440 * iFreq1 * iFreq2  // no update
    a2 = a1    
    a1 poscil .01, 440 * iFreq1 * k(iFreq2) // no update
    a2 = a1

outs a1, a2


f0 z
i 1 0 [3600*24*7]


Hi @samo, I’ve really used reinit, so this is double dutch to me. But I’m going to cautiously invite @iainmccurdy to the conversation. He has used more reinits than we’ve had hot dinners :rofl: Hopefully he takes the bait :fishing_pole_and_fish:

Hi @Samo,
I’ve taken a close look at this as there are several curious aspects that you are highlighting. I also showed it to Victor Lazzarini.

The initial response might be to simply put the reinit after all of the poscils, and this would certainly be more explicit regarding how you want them to respond, but I appreciate that that is not really the point of this demonstration.

The first thing to note is that k() appears not to be working as intended. In the following simple test:

ktime  =    int(timeinsts())
if changed:k(ktime) == 1 then
 reinit REINIT
irnd   random  0, 10

k1  =       irnd
    printk2 k1
    printk2 k(irnd)

printk2 k1 prints the changing value of irnd, once a second, wheres printk2 k(irnd) doesn’t, indicating that k(irnd) is not truly k-rate. This explains the behaviour of number of the options in your example. In principle as far as I am aware, the two should work identically. I will investigate further and file a bug ticket if it seems appropriate.

Another thing to note is that if poscil is given an expression containing only i-rate arguments for frequency, such as 440 * iFreq1 * iFreq2, then Csound will evaluate it at i-time and therafter not bother checking. This would need to be within the reinitialisation block. This is also why case one does update even though this seems inconsistent - the expression is evaluated elsewhere in a reinitialisation block. If you put the expression in instead of iFreqOut1, it won’t update.

There are matters regarding synthetic variables - variables that are generated in the parsing of your Csound code - that are not immediately apparent and the use of the -v command line flag for a more verbose test output during performance might be revealing.

If I come up with anything else, I will add to the thread.

Thanks @iainmccurdy for taking some time to look at this. Very insightful as always. I was well out of my depth on this one :joy:

I also wonder if this might have something to do with the Cabbage opcodes? Can the same inconsistencies be created with a vanilla Csound CSD?

The behaviour is exactly the same outside of Cabbage. On this occasion, Cabbage is completely innocent :angel:

Thank you very much for the clear feedback @iainmccurdy and for the persistent fishing @rorywalsh :slight_smile:

I’d just like to re-note that (as you know) this is not specific to any opcode, the poscil was just there for an audio test. It makes sense that the i-rate arguments would be evaluated only at i-rate, also in expressions or in conditional blocks for example, which are not in reinit blocks. The inconsistency comes from converting or using i-rate variables in k-rate expressions, which you have clarified. As a workaround I’m using k-rate variables with the init, so they only change in the reinit block. This seems to have no overhead cost.

1 Like