I think I solved the riddle using a prefix for the json identifier that I concat as a global String var at k rate with the actual identifier name for storing changes. In the example I initialise the prefix as “init_” and then I change it to “perform_” in an instrument that gets called with a bit of a delay. The first k-cycle parses the initialised empty array to the identifier with the “init_” prefix and the stored data under the identifier with the “perform_” prefix stays intact. Did it only for Slider2 while Slider1 has the old solution, that doesn’t work anymore.
gkslider1Array[] init 10
gkslider2Array[] init 10
gSprefix init "init_"
instr 1
kNumber init 0
kNumber = (changed(chnget:k("next")) == 1 && kNumber < 9)? kNumber + 1 : kNumber // change active index
kNumber = (changed(chnget:k("prev")) == 1 && kNumber > 0)? kNumber - 1 : kNumber // with buttons
if( changed(kNumber) == 1 ) then
String sprintfk "text(\"%d\")",kNumber // index
cabbageSet 1, "number", String
cabbageSetValue "slider1",gkslider1Array[kNumber]
cabbageSetValue "slider2",gkslider2Array[kNumber]
endif
strset 1,"dummy"
kslider1 chnget "slider1"
if(changed(kslider1) == 1)then
gkslider1Array[kNumber] = kslider1
SIdent sprintfk "%s", "dataSlider1"
SIdent strget 1
cabbageSetStateValue SIdent, gkslider1Array
endif
kslider2, ktrig2 cabbageGetValue "slider2"
if(changed(kslider2) == 1)then
gkslider2Array[kNumber] = kslider2
SIdent sprintfk "%s_dataSlider2",gSprefix
cabbageSetStateValue SIdent, gkslider2Array
endif
if( changed(chnget:k("print")) == 1) then // call instrument to print the stored data
event "i","printStoredData",0,0
endif
endin
instr printStoredData
prints "\n\nPrinting using readStateData\n"
prints cabbageReadStateData:S()
endin
instr enablePrefix
gSprefix sprintfk "%s","perform_"
endin
and then in the score:
i 1 0 z
i "enablePrefix" 0.1 0.0008
Seems to be working with your current version.
And it’s even shorter than the old solution.
Implemented this method into my plugin and it works perfectly. I think I’m just going to keep it that way. Feels very safe.
So there’s a solution that works, in case you don’t want to break your head over this.
1 Like
Something like this actually works as well:
gkPerform init 0
instr 1
…
SIdent sprintfk "%s", (gkPerform == 0)? "dummy":"dataSlider1"
cabbageSetStateValue SIdent, gkslider1Array
and with a delay:
instr startStoring
gkPerform = 1
endin
Weirdly the init write doesn’t happen on identifier “dummy”, but on “” which means that (gkPerform == 0)? doesn’t get queried in the initialising k-cycle, so SIdent stays NULL. Still the actual identifier doesn’t get overwritten so it works and I only produce one more json array and not one for every array.
Maybe it could be interesting to have a global variable in Cabbage that is set to 1 / true after the init k-cycle. Is that possible?
I guess, but it’s the kind of thing one can easily do in Csound?
Hey Rory,
I was coming across some problems with some serious CPU spikes using the cabbageSetStateValue opcode. I optimised everything to work very efficiently. For example when changing data with a slider I don’t write the state data on every change anymore, but only when I release the mouse button, so I’m not storing state data many many times because I’m moving a slider but only after I made my setting. Or if I’m skipping through different programs (in my case sample slots with individual settings) I set a “skipping” - flag so the sudden changes of all the sliders don’t trigger the storing of state data of every single slider.
I kind of fixed it, but randomly occasionally I still get a CPU spike from the opcode. It’s hard for me to reproduce this in a simple example because it’s likely that it is also connected to the monstrous size of my code. Having had multiple instruments running in parallel contributed, so I had to put some stuff together into one instrument. I can still tell that it is definitely connected to the cabbageSetStateValue opcode, as I don’t get any CPU spikes when I take those out.
Did you come across any issues with CPU load on those opcodes yourself so far? Is there a way you could evaluate / examine this somehow?
The quick answer is no I haven’t, but I haven’t used them much. I’ll go over the code and see if I can optimize it better. I’ll let you know when I have something 
I just had a look through this here. I pushed a small improvement, but I’m not sure there is much else I can do to optimise it. What causes the CPU spike is parsing and updating the JSON data. I would definitely advise against updating on each cycle. That’s why I used changed() in the example code to trigger an update.
When I wrote this opcodes I was thinking of them being used for those rare case where non-widget data would need to be saved. I think you are taking this to the next level?
Thank you for looking into this. I’ll try and see what’s happening. Yes, I guess I’m doing a bit of a stretch here with the individual settings that switch on every sample I play, storing arrays of data with that opcode. I’m using the opcode as sparse as I can now through even only store a change of state data after I lifted the mouse button from the sliders. That seems to be working almost always without getting a CPU spike. At first, when I stored the state data on every slider change, moving the slider was literally holding my CPU meter at 100%. Now when I lift the mouse button after doing a change I get usually just a 1% change. So it works well apart from some very occasional spikes. Also it seems different in different DAWs. Reaper seems to be handling it better that Studio One for example. One interesting thing I noticed that it actually gets better if I set my buffer to 32 samples and below. Decreasing it from higher buffer sizes to 64 samples would have the expected result of it getting worse, but then with 32 and 16 it got better again. I wonder if that could have anything to do with my 32 sample k rate (and maybe having one instrument that runs on ksmps = 1 additionally). Anyway, I’ll manage. Just thought I share my experience with you and I’m curious about your change!
I find the the ksmps is related to the buffer size it usually help performance. I set the default ksmps to 32 because over the years this seemed to give the best overall performance without draining too much CPU.
Just curious what the use case is here in comparison to snapshot saving? As the DAW already recalls all of your cabbage plugin settings.
These are for times you might want to save data that is not associated with a widget. For example, you may have generated a sequence of notes that you want to save when the sessions is saved. Or a particular drum pattern. Anything that isn’t exactly tied to a widget.