Custom browser - custom preset file format???
-
Okay... starting to get a feel for this system now....
One thing I ran into today was trying to decide whether my menus should have their saveInPreset bool enabled or not. My menus have a callback associated with them that controls the loading of an effect and the binding of parameter knobs.
But because I am eventually going to be aiming at an effect chain preset format.... I figured saveInPreset should be disabled, and I should track the effect data manually.
I got there in there, but it did require a bit of thought. Because loading the effect networks seems to be asynchronous, so you cannot rely on the parameter knobs to be properly flushed at the right time when calling updateSaveInPresetComponents().
So after a lot of trial and error, I just decided to call it twice!!
inline function onPresetLoad(obj) { local data = obj.presetData; if (!isDefined(data)) return; if (isDefined(data.ui)) { UserPresetHandler.updateSaveInPresetComponents(data.ui); } local engIds = ["Engine1", "Engine2", "Engine3"]; for (i = 0; i < engIds.length; i++) { local engineIdx = i + 1; local smKey = "engine" + engineIdx + "SampleMap"; local wtKey = "engine" + engineIdx + "Wavetable"; if (isDefined(data[smKey])) UISoundSelector.syncSamplerMenu(engineIdx, data[smKey]); if (isDefined(data[wtKey])) UISoundSelector.syncSynthMenu(engineIdx, data[wtKey]); } if (isDefined(data.fxSelections)) { for (k in data.fxSelections) { local fxName = data.fxSelections[k]; UIEffectDropDownMenu.syncEffectMenu(k, fxName); } } if (isDefined(data.ui)) { UserPresetHandler.updateSaveInPresetComponents(data.ui); } } inline function onPresetSave() { return { "version": "1.0.0", "presetAuthor": "", "presetDescription": "User Preset", "presetTags": [], "presetData": { "ui": UserPresetHandler.createObjectForSaveInPresetComponents(), "engine1SampleMap": PluginSharedData.engineSounds["Engine1"].sampler, "engine2SampleMap": PluginSharedData.engineSounds["Engine2"].sampler, "engine3SampleMap": PluginSharedData.engineSounds["Engine3"].sampler, "engine1Wavetable": PluginSharedData.engineSounds["Engine1"].synth, "engine2Wavetable": PluginSharedData.engineSounds["Engine2"].synth, "engine3Wavetable": PluginSharedData.engineSounds["Engine3"].synth, "fxSelections": captureFXSelections() } }; }This works well, if a little wasteful perhaps.
Part of the reason I wanted to do this is because the menu values are obviously floats, not the text value of the menu. And I knew that in the future if I wanted to add an effect type, and change the order of effects in the menu list, that this would break backwards compatibility. So I basically have a shim that will take a string and convert it to the right integer for the menu as it is in that moment, and then set the menu to the appropriate value .... which then triggers the callback to load the right effect network...
and then that last updateSaveInPresetComponents() call makes sure the parameters match what the preset has stored.
-
@Orvillain this all seems massively complicated for preset saving and loading, what does the existing prest management and a custom tag based browser on the front of it not give you?
-
@Lindon said in Custom browser - custom preset file format???:
@Orvillain this all seems massively complicated for preset saving and loading, what does the existing prest management and a custom tag based browser on the front of it not give you?
Ultimately it isn't just about preset saving and loading.
They want full customization of the preset data model. They want each preset to have an associated image. They want to be able to import and export presets along with sample content at the same time. Which means I need full path control over the samples when saving. They want rich meta-data based browsing and on the fly searching. When saving a preset, they want to be able to specify various tags and descriptors, and it makes sense to store that stuff directly into the xml. There's probably more they want, but off the top of my head that's all I remember.
Everything they're asking for seems to involve deeper control over the load and save mechanics. But it's actually not that complicated. I'm essentially overriding the HISE automatic system, with a manual system. I'm capturing nearly everything that HISE captures automatically in a single call to UserPresetHandler.createObjectForSaveInPresetComponents() - everything else is custom.
Unless I'm missing something, there's a lot that the stock preset management system doesn't give you.
After spending a fair amount of time trying things out and reading the API, I'm fairly sure I have a decent grasp of how it all works now. There are also pre and post callbacks for loading and there's a post callback for saving too, which I'm going to need to tell the system to refresh the list of presets; or to update the external database, which is probably going to be required too.
-
woohoo! I've now got a custom FX chain file format too. At least in prototype form.
I think I'll write a tutorial on all of this once I've properly sussed it all out. Because it is quite powerful, and no-one else really seems to have dove into this.
-
@Orvillain said in Custom browser - custom preset file format???:
and no-one else really seems to have dove into this.
I have :)
-
Battling a little with extension issues however.
@Christoph-Hart When saving a user preset am I stuck with the .preset extension??
-
@Christoph-Hart said in Custom browser - custom preset file format???:
@Orvillain said in Custom browser - custom preset file format???:
and no-one else really seems to have dove into this.
I have :)
No one else with the time to do a tutorial example ;-)
-
@Orvillain said in Custom browser - custom preset file format???:
and no-one else really seems to have dove into this.
I have too... :)
-
@Orvillain said in Custom browser - custom preset file format???:
No one else with the time to do a tutorial example ;-)
Seemed pretty trivial really - its just taking the values you have assigned to params of a given FX and encoding those with the FX name in a json object...
-
@Lindon said in Custom browser - custom preset file format???:
@Orvillain said in Custom browser - custom preset file format???:
No one else with the time to do a tutorial example ;-)
Seemed pretty trivial really - its just taking the values you have assigned to params of a given FX and encoding those with the FX name in a json object...
Cool, thanks for the help.
-
Here's a handy line:
FileSystem.getFolder(FileSystem.UserPresets).getChildFile("FXChains").getChildFile(PluginSharedData.lastSavedFXChainFileName).move(FileSystem.getFolder(FileSystem.UserPresets).getChildFile("FXChains").getChildFile(PluginSharedData.lastSavedFXChainFileName.replace(".preset", "")));I've got this in the post save callback. It's job is to take the FXchain file I just saved, and rename it to take off the .preset extension that HISE automatically adds whenever you use Engine.saveUserPreset command.
So now I can save two types of user preset: a global state preset, with a .preset extension... and an fxchain preset that collects module information for each of my FX slots, UI parameters, and bundles it all up into a .fxchain preset.
-
@Orvillain You can simplify your chain by including multiple directory levels in the
.getChildFilecalls. -
@David-Healey said in Custom browser - custom preset file format???:
@Orvillain You can simplify your chain by including multiple directory levels in the
.getChildFilecalls.Ohh, more like this I guess?
const base = FileSystem.getFolder(FileSystem.UserPresets); base.getChildFile("FXChains/" + PluginSharedData.lastSavedFXChainFileName) .move( base.getChildFile( "FXChains/" + PluginSharedData.lastSavedFXChainFileName.replace(".preset", "") ) );??
-
@Orvillain Yeah that's it! I'd break it up a little more to improve the readability and to remove some repetition.
const presetDir = FileSystem.getFolder(FileSystem.UserPresets); const filePath = "FXChains/" + pluginSharedData.lastSavedFXChainFileName; const file = presetDir.getChildFile(filePath); const target = presetDir.getChildFile(filePath.replace(".preset")); file.move(target);You could simplify it further by using
.renameinstead of.moveconst presetDir = FileSystem.getFolder(FileSystem.UserPresets); const file = presetDir.getChildFile("FXChains/" + pluginSharedData.lastSavedFXChainFileName); file.rename(file.toString(file.Filename).replace(".preset")); -
@David-Healey I'll implement that a bit later on. Right now I'm trying to figure out why when I load my fxchain "user preset" it peforms a full state reset!
updateSaveInPresetComponents
I think it is this call.... I'm willing to bet it has a default fallback mechanism where it resets controls to default, if it isn't specified in the data you feed it.
So I change my FXchain load mechanism to iterate over the components manually.