Let's Build a Synth with Juce Part 6 - Object Oriented Concepts (ADSR)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up everybody i'd like to welcome you to another juice tutorial and in this tutorial we're going to talk a little bit about object oriented concepts so we have this adsr that we created uh over the past couple tutorials in our synth tutorial series and now what we want to do is we want to subclass this out to its own class so it encapsulates its own behavior and this is a common question that we get in the community about how to start creating larger apps and plugins and when to move things out to their own class and things like that and how to pass this data around so i hope that this tutorial is helpful for you this is my perspective so it's not by any means the you know the one perspective of how to do this but this is something that i've learned through my time and i hope that is helpful for you so before we get started be sure to join the audio programmer community great place for audio programmers of all levels to connect we have now over 5 000 members in our discord uh so you can join us on the audio programmer dot com forward slash community and if you find this helpful this helpful for you be sure to like and subscribe so let's go ahead and get started so currently we're in our synth project i'm just going to open this up in xcode and show you what we have so we currently have this uh this adsr that we created uh a couple tutorials ago that is in uh the the behavior is kind of abstracted by the slider so so the user has uh the sliders that they could use to control the adsr and we have these slider attachments and what we want to do is uh as we can see this is starting to get a little bit bulky and what we want to do is maybe we could start moving some of this out to its own class and make this its own uh its own component so i can call adsr and it produces the attack decay sustain and release sliders all at once without having to call all of these each individually at a time so that's what we're going to do and i'm going to go back to the producer and this is where we're going to start so if you remember or if you've seen any of the past tutorials where i talk about plug-in architecture we have this plug-in processor which is kind of the data side of our plug-in where we have all of the hard number crunching that happens then we have the plug-in editor which is what the user sees so all the visual components that we have that the user can use to control those parameters that are happening in the processor and what uh one thing that i've learned recently is has been that we want to kind of carry on that type of thinking as we start to create and subclass things out uh so anything that's visual we want to have separated for anything that's that's uh doing data and number crunching and that sort of thing so because of that what i'm going to do is i'm going to first create a group and i will just do that by just right clicking in here and i'll do add new group i'll just call this ui so this will be where all of my ui classes are going to be so i'll just move this into my source folder and then i will create another group and i will call this data so this will be all of the data processing that happens and i think oh sitting in my ui folder now just needed to sit outside there we go so there we go and now we need to create so let's let's take those sliders and let's move those out to a ui class of their own so i can call an adsr and conceptually that will just be those four sliders and all that behavior encapsulated into one class so the way that we would do this we could add a new component class so component once again is just a juice based class that refers to anything visual that you see on the screen in a plug-in or an audio app so that's what we're going to do we're just going to add this one and i will just call this adsr component and i will create this and what i like to do so one thing that i found confusing when i was first starting is that i see that i created a folder here called ui and another folder called data but i don't see anything like that in in the source folder okay because that's those are two separate things and what i'd like to do is i like to create my folders in my actual folders in my structure to mirror what i see in the juice project that's how i like to do it i'm not sure if it's the the right way or not but i find that it makes things a lot easier for me so i've created this folder called ui and it's called and then the class is called adsr component now created that i'll create another one called data and let's do a new cpp and header file so this will not be a component class it'll just be its own just a blank cpp and header file and once again i will create a new folder in here i will call this data and you don't by any means need to even create folders called data in ui i just find it easier for me so i will call this adsr data and we will do that so now we have a data class that's going to encapsulate what our adsr is doing and a ui class that will encapsulate it so uh what a ui class that'll encapsulate what the adsr looks like visually okay and those two things are separate right so maybe before i might have taken the adsr object that was sitting in my synth voice class and i might have said uh okay so the adsr object as data uh the the juice class of uh the juice adsr class and the plug-in editor the sliders that are what the adsr is visually to the user i would have thought maybe before that those two things were part of the same thing and i would have put those two things in the same class but the uh i don't know if i want to say it's the best practice but i think that i think that a lot of people would say that you would want to keep those two things separate so keep your data separate from what your ui is doing okay you don't want those two things to uh to be together in the same class so okay so now we're going to open up in xcode again and here we are so now we're just going to do a little bit of kind of surgery so to speak and uh and this can be a little bit uh this could be a little bit messy or a little bit confusing so i hope that uh this is helpful for you and that doesn't uh as the one the road isn't too windy uh on this so so first thing that we're gonna do is we're going to try to kind of transplant everything that's uh about our adsr into this adsr component class so i will just take these these sliders and i'm just going to cut those i'll just add a new tab and then this tab will be the adsr component tab and i will just paste those so now my sliders are an adsr component now what i want to do is i want to i want to actually include uh the adsr component in the plugin editor now so i'm going to do a hashtag include and it would be ui slash adsr component.h so now we have the edsr component so now what i want to do is i'll just create one of these objects now so adsr component now this is what i'm going to call adsr okay rather than having separate sliders so now what i want to do is i want to go in here and now we need to figure out what what else we need to transfer over okay so we go back to our editor and i will go to the cpp and we have uh so now i just go through and i say okay everywhere everything that i see that says attack slider i probably need to move it over to my new adsr component class so we have the slider attachments right that we that we created a couple tutorials ago and now i will just take that and i'm going to move that over to my adsr component constructor okay so there we go like that we're going to get a bunch of errors that are going to pop up but don't worry we're going to uh we're going to work through each one of those one at a time and then we have this set slider params and we have our our sliders and now i can just take that i'm going to cut that and i am going to put that in once again in our adsr component and so we have this set slider params method that we created in our editor that that now is saying that it doesn't know what we mean by that and that's because that's still sitting in our editor right so we have this this method that we created in our last tutorial called set slider params i'm just going to take this i'm going to cut this and i'm going to bring it over to my adsr component.cpp now i'm just going to change this to make this a adsr component method now so i it's not sitting in tap synth audio processor editor anymore it's now an adsr component method and now i need to put this in the header file as well so we got set slider params and move back over to the header and this is a private method i'm going to put this as a private method don't need to have it public because it's just something that i'm using internally and so that's fine now let's see what else do we need to bring back over so we have all of this stuff where we were setting the bounds and all of all of that we did that last tutorial so now i'm just going to take that and i'm going to move it into resized in my adsr component class so now that's that has the resized method okay i'm just going to take all this stuff and paint i'm just going to make the i'm just going to make the background black so i'm going to do fill all juice colors black there we go okay so what else do we have here so that looks like we've got everything right and so we're going to need to do so so we have this adsr now that's sitting in our plugin editor okay and so we're going to need to do something like uh make this visible and then we're going to in the resize we're going to need to set the adsr bounds right so because that's what we were doing before now we just have to do that using our new class but you're going to see that we have a problem first which is that we have this error that says use of undeclared identifier attach attack attachment and then use of undeclared identifier audio processor so let's just think about that so we have this attack attachment it doesn't know what attack attachment is so it's probably sitting still in our plugin editor so let's go to our plugin editor header file and yes we have attack attachment all of these attachments we need to grab the using as well bring that along for the ride so now all of that can now go in our adsr component header file like that okay so that's that's uh that's all sitting there now and what else so that's got that should get rid of our one error uh should so we can build here real quick and you'll see that that should get rid of that error and now we have one more error use of undeclared identifier audio processor so what is audio processor so if we go back to the plug-in editor header file we see that audio processor is a reference to our uh to our plug-in processor okay so that's how we get access to the audio processor value tree state okay so um and if you're not sure what i mean by reference check the c plus tutorial i've done with my son on references uh very valuable to know about okay so so now we can see how this we can see this class is cleaning up really nicely right and we don't need this set slider params method anymore that's gone now and so now what we need is we need a way that we can pass this audio processor or the audio processor value tree state into this uh this slider attachment that we created right because it needs to know about the audio processor value tree state that is sitting in our in our plug-in processor header file right needs access to that data how are we going to do that so we can actually pass in the audio processor value tree state as a uh argument in our constructor okay so that's that's a good place to uh to pass that in so what i could do is i can use the constructor and i can say okay i want to pass in this this audio processor value tree state object so i can say juice audio processor value tree state now we want to make this a reference because we don't need to make a copy of the audio processor value tree state so that's why i'm putting that ampersand there apbts and we've got to make sure that we do the same thing in our cpp and now since we're passing since we're passing this in as a constructor argument what we need to do is we need to go back to our plugin editor where we're actually creating the adsr component and we need to pass that in as an argument into our adsr component and we can do that using an initializer list so uh so here we have our audio processor editor audio processor and now i can say adsr and we can pass in that audio processor value tree state object which would be audio processor dot audio processor or ap vts just like that okay so that now we should be good to go okay so we got this field audio processor will be initialized after field adsr okay so we're in a situation where we've we have the adsr initializing before the audio process uh before the audio processor and saying that there's a problem there right because we need audio processor to initial we need the reference to the audio processor before we can get the reference to the audio processor value tree state so if we go back to our header file we'll see that our adsr object is initialized before our audio processor reference so what we need to do to fix that is we need to put that afterwards so we need audio so we need the audio processor to get its reference to uh to our audio processor class and then we can use the apvts that's in the audio processor and that should correct that error right so we still have a build failed so let's see what else we need oh yes so now this is saying audioprocessor.apvts now we can just grab this argument from our constructor and i can just get rid of this audio processor and it just say ap vts okay so if you wanted to pass in the audio processor you could but uh i'm just trying to follow the rules of encapsulation which would say don't give don't give your class access to any more data than what you need to so at the moment it doesn't need access to the whole processor just needs access to uh ap the apvts so that's that's why i've that's why i've given it the argument to ap vts rather than the whole processor okay it looks like i might have moved something over that i didn't want to so this is my combo box for my osc oscillator selector that we're going to do later so i just need to move that back over to here and now what we need to do is we need to go to our cpp and we just need to make the adsr visible right which is what we did before so we do add and make visible adsr and now i can say adsr set bounds and however however i set this is however large the those four sliders will be okay because now those four sliders are sitting in this adsr component and i can put it wherever i want and make it as big as i want as well so let's just do get local bounds first and see how that see how that goes so now we should be fine uh so we're just building okay we got build succeeded and hopefully all of this but you can see now this audio processor editor is now very contained isn't it we see before we had all of this added make visible for each one of the sliders we had all these attachments but now it's nice and contained it's very simple to read right it's it's like very straightforward to to see what's happening now um so now let's just pull this up we don't need to make any sounds with it we can just see what it looks like and so now we can see that we have that slider it looks basically the same way it does before because we set the bounds to uh whatever get local bounds was so now uh let's just go back and let's let's do an example of like what if i just want it to be on the right hand side of the screen so let's have a look at our our uh our resize method where we're where we're actually setting the size of each one of these sliders okay so one of the things that i see right away is that i have slider height equal equaling the width divided by 4 minus padding that's actually wrong i think i want that to be bounds get height like that so i think that's fine and then slider start before we had it halfway down the screen let's let's just make that zero now and notice notice that i just need to change that in one place and now it changes everywhere right so so i don't have to go in and i have and change each individual one that's the beauty of having just one place where it lists these numbers and i can just dynamically do that and it does it to all the sliders at once so let's see now what we are doing and hopefully this will uh actually i'm sorry i needed to do one more thing so let's go back to the editor and let's do set bounds and let's do uh let's start at get with divided by two so let's start in the middle of the screen on the x-axis and then we will do uh zero on the y-axis and then we will make the width of this get with divided by two and then get height and what we should have is we should have that adsr now on the right hand on the right hand side of the screen rather than going all the way across the screen and the sliders themselves should be larger as well this is something you can toy around with over time yeah so so now we have those we see that it's nicely on the right hand side of the screen so that's so that's fine right so that looks good yeah i can show you a little bit later how to correct these numbers as well we see that these numbers are have that little dot dot dot we could correct that a little bit later down the line but that's something we could do a little bit later when we're tweaking uh so let's see one more thing so let's do one more thing right so we did that so so now we've set the adsr for the uh for the visual the visual aspect of the edsr let's do the same thing for data so uh we could do this for data so at the moment we have this uh adsr that in adsr params that are sitting in synth voice okay so if we take a look at the methods that we need for this uh we can just take a look and we see that we have a set sample rate we have an update adsr and then we have where we're doing this apply envelope to buffer so there's really not a whole lot that we need to sub class this out but i'm just going to do it anyway just to show you kind of the thinking around it so let's and this might be helpful for some people so what i'm going to do is i'm going to now go into adsr data so one of the first things i'm going to do is i want to add the juice header so this will just give me access to all the juice all the juice methods and all that good stuff so now i'm going to create a class called adsr data and since this is going to be a type of adsr so i don't really need to i don't really need to recreate all the stuff that the juice adsr object is already doing right because it's we're calling all of these methods like set sample rate so on and so forth so because of that what i'm going to do is i'm actually just going to inherit from juice slider so i could say adsr adsr data inherits from public juice adsr okay so what i'm saying is adsr data is a type of juice adsr so i'm just going to add a little bit of functionality to this and by functionality i mean like one method that uh that this is going to do that the adsr class doesn't already do so now once again we need to make adsr data visible uh and include it in our synth our synth voice so i will do hashtag include and we will do data adsr data you know something like this might be a little bit redundant but let's let's just try it out right so we have this method now called update adsr or actually i'm sorry what we're going to do is first is we're going to take adsr and adsr params and we're going to take this we're going to put this now in adsr data because that's what we want to do we want to encapsulate what everything that adsr is doing and we want to encapsulate that in the adsr data class okay so i'm just going to move back over here to the adsr data class and now i've put adsr and adsr params in there actually i don't want adsr there because adsr data is a juice adsr okay so we just need adsr params now going back to synth voice we have this update adsr where we're doing a float uh where we got the attack decay sustain and release and we have these adsr params that we're updating those now live in our adsr data class so what i could do is i'm going to cut this for a moment and what i'm going to do is i'm going to create a method called i'm actually going to just cut it from here actually or i'll copy it from here and i will i will paste it in my cpp sorry about this so here we go adsr data this is not in the synth voice class this is an adsr data now and now i need to take update adsr i need to make sure that i prototype it in my header so here we go it's a public method and now this adr dot set parameters i can just change this back to set parameters because because this is a native method that's called that's that's called uh within the adsr class right which is adsr data is a type of juice adsr okay i need to put a semicolon down here and now let's see so we have that and now what i'll do is i'll go back to my cpp and we don't have these anymore and now what i could do is i could say adsr data update adsr and now i will pass in attack decay sustain and release okay now why am i doing that the reason that i'm doing that is because sorry this is not adsr data this is adsr oh i need to create it actually sorry about this adsr data adsr and here we go and the question is well why do that why have one method that's saying called update adsr and then within it have another method that's called adsr update adsr well the reason is because we i just want to build this really quick make sure this actually gets rid of all of our errors yeah i think that's fine yep so that's good so the reason that i'm doing that is because our remember our processor is calling this updsr okay so if i go into the plug-in processor we have this voice update adsr where we have all of these parameters right so what i need to do is i i need to make a choice the choice is either the the choices either i need to make the adsr object or the adsr data object visible to the processor and call it from the processor or i need to call it i need to plumb it down through the voice which is the way that i've done it i need to plumb it down through the through the voice into the synth voice and then that plugs into the adsr uh the adsr data object that i've created okay so that's that's the reason i've done it now that that probably sounds a little bit redundant to do it like that but what if i what if i had other parameters that i need to update so like let's say that we have uh a jog wheel right so we have like this pitch wheel moved and things like that now what i could do is i could actually change this where this this actually becomes more of like update all of the all of this stuff update the pitch wheel update the adsr update everything that needs to be updated and so what i could do is i could actually change this to update we don't have anything else that we need to update at the moment but but this is just to show you how extendable this is so now i'll call this update of course in the plug-in processor now this is called update so oops deleting the wrong thing there and so we see how how extendable that is and now now we have all of the behavior of update adsr inside inside the adsr class all the data stuff in the adsr data class all of the ui stuff in the adsr component class so now let's just build this and make sure this all actually uh is set up correctly so now we got build succeeded you know once again this is just abstracting out this is this is abstracting out behavior and then what we're going to do is we're just going to instantiate this and make sure that it still actually plays fingers crossed here we are just turn your headphones down a bit if you have them really loud so you don't hear the synth really loud so so now that's working fine [Music] so that so that's all been set up correctly yeah so this so just to review just to review the reason why we're doing that is because in what we want to do is we want to try to keep the editor and the processor as clean as possible right so time that we have uh anytime that we have a whole bunch of stuff that's happening that is around one one process we want to think about taking that and classing that out moving it out to its own class and now what happens is that this becomes really simple right once we get the os once we get the oscillator all set up and everything then we can take this and we can make an oscillator class that has a selectable selectable oscillator now and then it would be atomic visible oscillator atomic visible adsr and anybody can you know nearly anybody can read that now that becomes that becomes very readable code and then as you get deeper and deeper then it starts then you start to see the complexity right so when you go into adsr component now we see all the individual sliders we see all the individual attachments we see all of the connections that we need to make how they're set out so on and so forth it starts getting more and more complex but we want to try to keep the processor and the editor and the behavior and the code that we see there as clean as possible so so that it becomes very readable and people can just see it like a tree where branches are where the classes are extending out like branches sometimes it's not quite as just cut and dry like that but it's it's great when it is so uh so i hope you found that that helpful for you uh if you did uh yeah please leave a like and subscribe and also leave your comments if you have better ways that you think that this could be abstracted out i'd love to hear them and i will see you next time
Info
Channel: The Audio Programmer
Views: 1,517
Rating: undefined out of 5
Keywords: synth, juce, digital signal processing, dsp, juce framework, vst, c++, cplusplus, realtime audio, creative coding, audio software development, audio dev, audio programming, audio programmer
Id: fbXjSlQYeVw
Channel Id: undefined
Length: 33min 53sec (2033 seconds)
Published: Sun Feb 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.