Hi,
Was wondering if there’s any example of how we could work with presets using a custom UI?
Hi,
Was wondering if there’s any example of how we could work with presets using a custom UI?
The following opcodes will save the Cabbage widgets JSON and recall it later when triggered. Even if you’re working with custom UI, you’ll still need to declare some parameters, so these opcodes should still work for recalling values.
Here is an example of how these opcodes can be used.
GridSequencer.zip (31.8 KB)
Working with this now. I was wondering if you were to seperate the presets in, say a Factory folder and a User folder:
presetDirectory@global:S = strcat(chnget:S("CSD_PATH"), "/Presets")
presetDirectoryFactory@global:S = strcat(chnget:S("CSD_PATH"), "/Presets/Factory")
presetDirectoryUser@global:S = strcat(chnget:S("CSD_PATH"), "/Presets/User")
Would there be a way in this LoadPreset-instrument to find the correct full preset-path between the factory- and user-folders?
// Load a preset from a selected file in the combo box
instr LoadPreset
fileName:S = cabbageGetValue("presetCombo")
extension:S = cabbageGet("presetCombo", "populate.fileType")
fullPresetPath:S = cabbageJoinPath(presetDirectoryUser, fileName, extension)
cabbageLoadState(fullPresetPath)
endin
Hmm, the problem is the combobox can only load the contents of a single folder. I could extend it to mutliple folder, but then we woudln’t know the path - unless you choose to show the full path, but thats ugly. Let me think about it.
[edit] As it stands, if populate.fullFileAndPath is set to false we only see the the file name. I strip the rest in the back end, but I should probably do it on the front end, and therefore keep a full list of paths in items[]. That way, so long as your presets have unique names, one could search through the items array for a file with a name matching the current combobox item. Then populate.directory could be an array too:
“populate”: {
“directory”: [“/user/home/great”, "user/home/better],
“fileType”: “.wav”,
“fullFileAndPath”: false,
“order”: “date”,
“labelWhenEmpty”: “No Files”,
“defaultLabel”: “”
}
And then in Csound (imaginary code.. for now):
instr 1
items:S[] = cabbageGet("combo1", "populate.directories")
currentItems:S = cabbageGetValue("combo1")
index:i = 0
while (i < lenarray(items)) do
res:i = strindex(items[index], currentItems
if(res>0) then
cabbageLoadState(items[index])
endif
index++
od
endin
That would be nice, I think this is also useful when you want to group your presets, like in a synth per instrument-type too, so I think it is a common case?
Yeah, it actually make no sense to strip those paths away. And this means we don’t have to concat full paths ourselves. Much better. But how are you getting this info to your custom widgets?
I haven’t gotten there yet, but my plan was to list out the items from the combobox-channel. Think that might work?
Yeah, so add a dummy combobox box, could work, we need to consider the best way to do this. I’ll push this update a bit, just working on it now..
Nice, how does it work when you make a update upstream to Cabbage 3? Do we need a new version of vscabbage then?
It’s best to wait for one because it will be synced. I’m still working on this actually, Changing directory to directories[] is causing my a bit of a headache. But I’ll try to get it sorted in the next hour or so.
I think I have to call it a night. I didn’t get this finished. Pretty close, but there are still a few teething issues. So the first thing is we can’t try to retrieve items straight after calling populate. When we populate, we need to scan the file system, which needs to happen on a separate thread so as not to block the audio thread. So we need to give it a few moments to complete. But I think this is reasonable. So if I do this, calling instrument 2 a few milliseconds after instrument one, it works fine:
instr 1
dirs:S[] = ["/Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets", "/Users/rwalsh/sourcecode/cabbage3-recipes/samples"]
cabbageSet "presetCombo", "populate.directories", dirs
endin
instr 2
items:S[] = cabbageGet("presetCombo", "items")
prints("Number of presets found: %d", lenarray(items))
index:i = 0
while (index < lenarray(items)) do
printf_i("Found preset: %s at index %d", 1, items[index], index)
index += 1
od
endin
This prints out:
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/SilentCabbage.json at index 0
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/SilentCabbage.jso at index 1
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/ShinyCelery.json at index 2
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/ShinyCelery.json at index 3
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/rory.json at index 4
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/rory.json at index 5
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/HappyTurnip.json at index 6
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/HappyTurnip.json at index 7
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/FierceTurnip.json at index 8
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/FierceTurnip.json at index 9
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/CalmLettuce.json at index 10
Found preset: /Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets/CalmLettuce.json at index 11
As you can see it’s adding duplicates. Need to sort that out. And I also need to fix the wildcard * pattern for file types. Maybe I’ll give it another few minutes. I’m close..
Ok, new build underway. You can test with the CSD below. A few things to watch out for that I will fix later. For now, you have to pass a valid directory in the Cabbage JSON code. I will remove this requirement, as it’s stupid. Also, the wildcard pattern doesn’t work yet, so you have to explicitly tell it what kind of file you want. I will fix this tomorrow.
<Cabbage>
[
{ "type": "form", "caption": "Untitled", "size": {"height": 300, "width": 600}, "pluginId": "def1" },
{
"//" : "----------- A combo box to load presets ----------",
"type" : "comboBox",
"bounds" : {"left": 10, "top": 10, "width": 76},
"channels" : [
{ "id": "presetCombo", "range": {"increment": 0.001, "max": 1}, "type": "string" }
],
"style" : {"backgroundColor": "#0295cf28", "borderColor": "#131b1e28", "borderRadius": 4, "borderWidth": 0},
"presetIgnore": true,
"populate" : {
"fileType" : ".json",
"directories" : ["/Users/rwalsh/sourcecode/cabbage3-recipes/samples"],
"labelWhenEmpty": "No Presets",
"defaultLabel" : "Load Preset"
},
"automatable" : false
}
]
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-d
</CsOptions>
<CsInstruments>
nchnls = 2
0dbfs = 1
instr 1
dirs:S[] = ["/Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets", "/Users/rwalsh/sourcecode/cabbage3-recipes/samples"]
cabbageSet "presetCombo", "populate.directories", dirs
endin
instr 2
items:S[] = cabbageGet("presetCombo", "items")
prints("Number of presets found: %d", lenarray(items))
index:i = 0
while (index < lenarray(items)) do
printf_i("Found preset: %s at index %d", 1, items[index], index)
index += 1
od
endin
instr 3
items:S[] = cabbageGet("presetCombo", "items")
currentItem:S, comboTrig:k = cabbageGetValue("presetCombo")
printf("Current selected preset: %s", comboTrig, currentItem)
index:k = 0
while (index < lenarray(items)) do
res:i = strindex(items[index], currentItem)
if(res != -1) then
printf_i("Current preset is: %s at index %d", 1, items[index], index)
endif
index += 1
od
endin
</CsInstruments>
<CsScore>
i1 0 z
i2 1 z
i3 1 z
</CsScore>
Btw, there’s no reason your custom UI can’t access all of these items directly. If you create a combobox as shown in the previous example, Cabbage will send the JSON object to the front end when the instrument first compiles. Just listen for a command called "widgetUpdate". It will contain the JSON object for each widget. That means you should be able to read the widget’s items array in your frontend and use it to populate your combobox.
I’m done. It’s beer o’clock!
Nice one, this solution is very ideal! I’m testing now with an adjusted version of the GridSequencer and experiencing some crashes:
Saving the preset worked fine here, and the initialization of the files in presetCombo with cabbageSet also worked.
Thanks, I am aware of some of those issues. I probably shouldn’t have pushed a new build with them in place, but I thought it would be good to get some early feedback on it. I’ll try to sort these things out now. ![]()
Ok, these issues have been fixed now and a new build underway. The grid sequencer instrument needed updating due to the fact that items now contains full paths. I’ve attached the update version here.
GridSequencer.csd (17.2 KB)
Nice, so I made a few changes to the .csd to adjust for an array of directories (line 266-268, line 276 and 436). I think it struggles to populate the array of directories into the combobox?
GridSequencer.csd (17.4 KB)
Try with this simpler csd file, it’s working for me:
<Cabbage>
[
{ "type": "form", "caption": "Untitled", "size": {"height": 300, "width": 600}, "pluginId": "def1" },
{
"//" : "----------- A combo box to load presets ----------",
"type" : "comboBox",
"bounds" : {"left": 10, "top": 10, "width": 76},
"channels" : [
{ "id": "presetCombo", "range": {"increment": 0.001, "max": 1}, "type": "string" }
],
"style" : {"backgroundColor": "#0295cf28", "borderColor": "#131b1e28", "borderRadius": 4, "borderWidth": 0},
"presetIgnore": true,
"populate" : {
"fileType" : "*",
"labelWhenEmpty": "No Presets",
"defaultLabel" : "Load Preset"
},
"automatable" : false
}
]
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-d
</CsOptions>
<CsInstruments>
nchnls = 2
0dbfs = 1
instr 1
dirs:S[] = ["/Users/rwalsh/sourcecode/cabbage3-recipes/samples/GridSequencerPresets", "/Users/rwalsh/sourcecode/cabbage3-recipes/samples"]
cabbageSet "presetCombo", "populate.directories", dirs
endin
instr 2
items:S[] = cabbageGet("presetCombo", "items")
prints("Number of presets found: %d", lenarray(items))
index:i = 0
while (index < lenarray(items)) do
printf_i("Found preset: %s at index %d", 1, items[index], index)
index += 1
od
endin
instr 3
items:S[] = cabbageGet("presetCombo", "items")
currentItem:S, comboTrig:k = cabbageGetValue("presetCombo")
printf("Current selected preset: %s", comboTrig, currentItem)
index:k = 0
while (index < lenarray(items)) do
res:i = strindex(items[index], currentItem)
if(res != -1) then
printf_i("Current preset is: %s at index %d", 1, items[index], index)
endif
index += 1
od
endin
</CsInstruments>
<CsScore>
i1 0 z
i2 1 z
i3 1 z
</CsScore>
Hmm, I can do this, and it will populate:
cabbageSet "presetCombo", "populate.directories", "C:/Users/Dale/Desktop/GridSequencer/GridSequencerPresets/User"
but when I wrap it in an array, it does not populate ![]()
cabbageSet "presetCombo", "populate.directories", ["C:/Users/Dale/Desktop/GridSequencer/GridSequencerPresets/User"]
I’m on version 1.0.131-alpha