How to write code that allows for a user defined amount of something

Hi all,

I should probably start by saying I’m a complete beginner to JUCE and programming in general so I’m not sure how to articulate myself properly.

What I would like to know is if there is a way for you to write your code that allows you to predefine the functionality of a type of object or component without having to explicitly write the code for each instance of it.

For example in a DAW you could have potentially hundreds of tracks. Would you be able to define what a track is and let the user create as many tracks as they’d like without having to duplicate the code needed to make a track for every possible track.

Sorry if this is a silly question or doesn’t make much sense, I’m having a go at making a sequencer and wondering if there’s a way to avoid making my code overly bloated.

Not a silly question from a beginner. And yes, you will write code that is usable as you have indicated. Now go learn c++ before starting with juce. :nerd_face: This is a good course to teach you C++ and JUCE

https://www.programmingformusicians.com/pfmcpp/

3 Likes

yes. that’s what classes are for in c++. go to youtube and watch the cherno’s c++ playlist for all the basic infos

2 Likes

I second the recommendation of learning a bit basic C++ first before starting with JUCE. Your question is however asking something between programing language specific (C++), framework specific (JUCE) and general design pattern specific. So I want to sketch a very broad answer to your DAW example.

From a design pattern point of view, a DAW track will probably be defined by at least two main entities. First, the data structure that is invisible to the user, which will hold all the information that is needed to process the audio. This might be the name of the track, the list of audio files along with their positions on the time line, the plug-ins loaded on that track along with their state and the solo, muted, volume and pan applied to that single track. Second, there will be the data structure that represents the visual part of the GUI presented to the user. For a DAW track, typically a horizontal wave form view of the audio track, along with a area on the left that reads the track name, holds the mute and solo button and maybe a button to open up the plugin window. The interesting part here is, that in a good design, the GUI part doesn’t have to exist to make audio playback work, therefore it’s optional or there might be multiple ways of visualizing a track – think of a DAW where you can switch the view to a mixer mode. All this is a design pattern approach, completely unrelated to C++ or JUCE, but it defines the way you should think about problems as a programmer.

In C++ code we map those entities to classes. You want to write a class for the background data structure of a track and one for the GUI element. While the data structure part could be a simple basic C++ class without further dependencies (let’s call that class TrackData), if you want to build your application in JUCE, the GUI part will be a class inheriting juce::Component and this class should probably keep a reference to the corresponding background data structure (let’s call this class TrackView). Now that you defined those two classes, you can put them into containers. Containers are collections of multiple instances of the same type. The most common container in C++ is std::vector, so you could declare a std::vector<TrackData> tracks and a std::vector<std::unique_ptr<TrackView>> trackViews. You can add elements to the vector at runtime based on user interaction with the push_back member function. Depending on the actual design choices, this might or might not be the right answer, and if e.g. a plain std::vector<> or a std::vector<std::unique_ptr<>> is the right choice or a completely different container is a bit out of scope for this broad answer, but it might help you to find the direction to go when trying to learn things.

Hope that helps

3 Likes

Very interesting cheers guys. I was already aware of what classes were but I think (part of) my problem was I was struggling to visualise how to implement one properly. I hadn’t come across containers yet so that’s definitely something I’ll have to look into more.

My question came after working through The Audio Programmers tutorial on building a plug-in with a gain slider. To create the slider you needed to write several lines of code in the editor’s constructor to call the slider, set its range, make it visible etc. This is what lead me to thinking how if you were to make a DAW how bloated your code might become if you needed say 100 sliders for 100 tracks. I hadn’t connected the dots how you could write everything you needed to create a slider in a class to make your code more concise :man_facepalming::sweat_smile:.

Anyway lots of food for thought, thanks again.

What you’re describing here is abstraction, and the magical thing about programming is that you can abstract anything. Now that you’ve build a single class which can construct any number of sliders, you’ll start to notice other patterns and repetitions in your code (for instance that the code you’re using to add a slider to a track is very similar to the code where you add a button). So you create a new level of abstraction, and then another, and another. This way, you can write code that not only never repeats itself, but is also impossible to understand and to debug :laughing:.

Have fun, make mistakes, feel like a genius, ask stupid questions, and ignore the people who tell you not to do it this way or that way (only to discover 6 months later why they were right).

5 Likes

If you have a “Track” class, the slider and everything will be created within that class, you just have to create a dynamic list and pass the creation parameters when the user adds a new one, although most of them could be by default.

Then you just have to go through the list to process, define its drawing bounds in resize, or whatever you want.

1 Like

one thing to consider when watching audio development-related youtube videos is that the code you see on screen might not be the same that actually ends up in a project. when you try to make a point in a youtube video you must make it simple to look at. for example in a “real” project people often split something between header and cpp file. that makes a lot of sense when you have 2 screens to put your stuff on. but when making a youtube video it might make more sense to put everything into a single header, so the people watching have a good overview over everything. you might also not write a function call for every little thing, but just put everything into one big function call and explain that with words, so that the people see the sequence of things better, while also understanding the details. or in josh’s case: irl you’d make a class where you give things the right properties to start with, so that instantiating the things then looks nice and easy. but if you did that in a tutorial people might just instantiate a slider and wonder why it doesn’t look the same. so take youtube videos with a grain of salt and question their code patterns sometimes. find a code pattern that works for your actual project! :slight_smile:

2 Likes

Really useful insight, thanks for that

1 Like

Absolutely love the supportive comments here :metal:

1 Like

Agreed, it can be quite intimidating reaching out as a beginner, I’ve been pleasantly surprised by the response here, cheers everyone