Great video!! I only wish I’d had this resource a few years back when I started out with preset management, it would’ve saved me many sleepless nights… I’m very reassured to see that my approach is similar to yours, though :) You’ve filled a huge documentation void with this content, which I’m sure new JUCE adopters will be extremely thankful for :)
i've written some preset browsers now and i can say: it's really cool to see that this one is actually quite similiar in terms of code architecture but a bit more straight-forward due to the use of FileChooser and ComboBox
That's great to know! Yes, it's a definitely a simple one and can be embellished with a lot more functionality, but the key aspect, as you already know, is to keep the preset management service loosely coupled from the rest of the app.
@@akashmurthy can we take a moment and talk about more complicated states though? cause it looks like the implementation in this video works perfectly fine for states that just consist of parameters, as that replaceState call probably also writes the new values to all the parameters automatically. but let's say i want to have more complicated state changes in there, like whole buffers that need to resize on a preset change, which would either require an elaborated thread-safety mechanism somewhere or temporarily stopping the audio processing. this could make sense in situations where you have.. maybe an oversampler that always has an extra buffer of the size it needs depending on oversampling order. does apvts' replaceState have something in it that would allow such things to happen or what kinda extra steps would you likely add to make that possible?
@@Beatsbasteln Hello. So the APVTS class is really just a decorated ValueTree class, with some extra functionality geared towards storing parameter values. It has other functionalities to offer like detecting when changes happen, or when new parameter values have been introduced or when the tree has been replaced. It can also store non-parameter values. But basically, you can think about it as a key-value store. The replaceState method is basically just replacing the values of this key-value store with new values that it was able to parse from an xml file. So AVPTS as a whole is not responsible for anything else apart from this. In your case, you wanted to resize some buffers for example. That's related to the functionality of your application and is not the responsibility of APVTS. Here's a simple example of a use case similar to yours: You are storing the samples of a wav file in a buffer within your app. And this wav file is associated with a preset. Changing the preset would point to a new wav file, and the app would need to resize it's buffers and fill it with the samples of the new wav file. Does this sound correct? Here's a solution that could be used. Here, the wav file associated with the preset would probably just be stored as a File path within the state. It's still a key-value pair. When the state changes, the app would get notified that the state changed, and a new wav file would need to be loaded. The buffer is stored as a ReferenceCountedArray of buffers. When a new wav file needs to be loaded, you could spin off a separate thread to create a new buffer, fill it and add it to the ReferenceCountedArray and change the currentBuffer that needs to be referenced. In the audio thread, at the very start of the callback, a reference to the currentBuffer is held by the callback. When the currentBuffer is pointed to the new buffer, the old buffer's reference count would drop to zero. The ReferenceCountedArray would be cleaned up from time to time and the buffers whose reference count is zero would be deleted. This way, the transition between the 2 buffers would be seamless. The audio processing wouldn't have to stop to wait for the new buffer to be allocated and loaded. There is a Juce tutorial describing exactly what I mentioned above, if you are interested. I think it's called Using the AudioSampleBuffer class (advanced)
@@akashmurthy that's correct. that is a comparable usecase. i understand your explanation. you'd consider thread-safety not to be the responsibility of a triggered patch change. in my last preset browser i made it so that every patch change suspends processing and calls prepareToPlay so that there is a general solution to patch changes regardless of what kinda data needs to be processed. while that is kind of a dirty solution, especially when people want to swiftly move through patches, it kinda gives me a peaceful feeling, because it's really easy to handle and works stable. i neither want to be convinced of your method nor being praised for mine by the way. i just wanted to discuss this so i can sort my own thoughts about this topic some more. it's kind of an abstract part of plugin development imo
Hi Akash, Great tutorial. I am having a problem while running the app in iOS with and error "Could not create preset directory: Operation not permitted JUCE Assertion failure in PresetManager.cpp:14"
Thank you for this tutorial! Can you talk a bit more about the juce audioProcessor's setCurrentProgram(), getCurrentProgram(), getNumPrograms() methods ? how are they related to this and do we actually need them? Thank you!
Hey, thanks a lot! So a simple way of handling categories of presets is to separate them out into their own directories. In the preset directory, you'd have a factory and a user directory. The presets in these directories can be listed as a nested structure in the drop down of the Combobox. This requires a bit of work, the idea would be, instead of adding the preset names directly on to the Popup menu of the Combobox, you add submenus of directory names instead, and populate the submenus with the presets from each directory. You can add extra validation to factory presets if you want. You can check if the preset being overwritten or being deleted is within the Factory directory, and disallow it.
@@jacopolovatello Another option to reinforce the “read only” aspect is to separate your factory presets and include them as a binary resource rather than storing in a local directory. If users want to modify a factory preset, they can just recall it and save separately as a user preset on their local machine, for example. And of course, JUCE makes it super easy to parse binary resource XMLs :)
Amazing tutorial, and yes please create the sequel :D
Great video!! I only wish I’d had this resource a few years back when I started out with preset management, it would’ve saved me many sleepless nights… I’m very reassured to see that my approach is similar to yours, though :)
You’ve filled a huge documentation void with this content, which I’m sure new JUCE adopters will be extremely thankful for :)
Thank you Akash! Akash's channel is sick. I highly recommend you all check it out. Tons of high quality video's.
Thanks very much Ethan! :)
I had to look twice at the channel name. Thank you Josh and Akash for the great content! 👍
Wow! Awesome tutorial. Thank you very much!
Amazing tutorial, really great help! Hope this gets its sequel with the new features
Thank you so much! Learned alot from this tutorial, hope to see more of this content!
Godlike vid as always Akash!
Great work! Would LOVE to see the follow-up video!
i've written some preset browsers now and i can say: it's really cool to see that this one is actually quite similiar in terms of code architecture but a bit more straight-forward due to the use of FileChooser and ComboBox
That's great to know! Yes, it's a definitely a simple one and can be embellished with a lot more functionality, but the key aspect, as you already know, is to keep the preset management service loosely coupled from the rest of the app.
@@akashmurthy can we take a moment and talk about more complicated states though? cause it looks like the implementation in this video works perfectly fine for states that just consist of parameters, as that replaceState call probably also writes the new values to all the parameters automatically. but let's say i want to have more complicated state changes in there, like whole buffers that need to resize on a preset change, which would either require an elaborated thread-safety mechanism somewhere or temporarily stopping the audio processing. this could make sense in situations where you have.. maybe an oversampler that always has an extra buffer of the size it needs depending on oversampling order. does apvts' replaceState have something in it that would allow such things to happen or what kinda extra steps would you likely add to make that possible?
@@Beatsbasteln Hello. So the APVTS class is really just a decorated ValueTree class, with some extra functionality geared towards storing parameter values. It has other functionalities to offer like detecting when changes happen, or when new parameter values have been introduced or when the tree has been replaced. It can also store non-parameter values. But basically, you can think about it as a key-value store. The replaceState method is basically just replacing the values of this key-value store with new values that it was able to parse from an xml file.
So AVPTS as a whole is not responsible for anything else apart from this. In your case, you wanted to resize some buffers for example. That's related to the functionality of your application and is not the responsibility of APVTS.
Here's a simple example of a use case similar to yours: You are storing the samples of a wav file in a buffer within your app. And this wav file is associated with a preset. Changing the preset would point to a new wav file, and the app would need to resize it's buffers and fill it with the samples of the new wav file. Does this sound correct?
Here's a solution that could be used. Here, the wav file associated with the preset would probably just be stored as a File path within the state. It's still a key-value pair. When the state changes, the app would get notified that the state changed, and a new wav file would need to be loaded. The buffer is stored as a ReferenceCountedArray of buffers. When a new wav file needs to be loaded, you could spin off a separate thread to create a new buffer, fill it and add it to the ReferenceCountedArray and change the currentBuffer that needs to be referenced. In the audio thread, at the very start of the callback, a reference to the currentBuffer is held by the callback. When the currentBuffer is pointed to the new buffer, the old buffer's reference count would drop to zero. The ReferenceCountedArray would be cleaned up from time to time and the buffers whose reference count is zero would be deleted. This way, the transition between the 2 buffers would be seamless. The audio processing wouldn't have to stop to wait for the new buffer to be allocated and loaded.
There is a Juce tutorial describing exactly what I mentioned above, if you are interested. I think it's called Using the AudioSampleBuffer class (advanced)
@@akashmurthy that's correct. that is a comparable usecase. i understand your explanation. you'd consider thread-safety not to be the responsibility of a triggered patch change. in my last preset browser i made it so that every patch change suspends processing and calls prepareToPlay so that there is a general solution to patch changes regardless of what kinda data needs to be processed. while that is kind of a dirty solution, especially when people want to swiftly move through patches, it kinda gives me a peaceful feeling, because it's really easy to handle and works stable. i neither want to be convinced of your method nor being praised for mine by the way. i just wanted to discuss this so i can sort my own thoughts about this topic some more. it's kind of an abstract part of plugin development imo
The video that I was waiting for. I enjoyed and applied very well your meters video. (And English is not my native language)
Thank you very much! I'm glad you liked the meters tutorial :)
Hi Akash, Great tutorial. I am having a problem while running the app in iOS with and error "Could not create preset directory: Operation not permitted
JUCE Assertion failure in PresetManager.cpp:14"
Thank you for this tutorial! Can you talk a bit more about the juce audioProcessor's setCurrentProgram(), getCurrentProgram(), getNumPrograms() methods ? how are they related to this and do we actually need them? Thank you!
What about importing midi files because most midis freezes my program. What can I do about it? I'm using soft soft Bundle Pack by
Take ti, enjoy it most of all and results will follow
Great tutorial, thanks!
How would you go about handling factory (read only) vs. user presets?
Hey, thanks a lot!
So a simple way of handling categories of presets is to separate them out into their own directories. In the preset directory, you'd have a factory and a user directory.
The presets in these directories can be listed as a nested structure in the drop down of the Combobox. This requires a bit of work, the idea would be, instead of adding the preset names directly on to the Popup menu of the Combobox, you add submenus of directory names instead, and populate the submenus with the presets from each directory.
You can add extra validation to factory presets if you want. You can check if the preset being overwritten or being deleted is within the Factory directory, and disallow it.
@@akashmurthy Thanks for the suggestions! I'll give it a try!
@@jacopolovatello Another option to reinforce the “read only” aspect is to separate your factory presets and include them as a binary resource rather than storing in a local directory. If users want to modify a factory preset, they can just recall it and save separately as a user preset on their local machine, for example. And of course, JUCE makes it super easy to parse binary resource XMLs :)
@@mattphillipsdev that's a great hint. I read some posts on the forum about it if I remember correctly. I'll give it a try. Thanks!
Woow woow
Ikr!
I am just wondering why you are being so generous with your knowledge?
Because we only live once, my friend.
Nnice
PAUSE THE VIDEO!!! TNice tutorialS IS A GREAT SKILL TO HAVE!!!
Jesus is the Way