Design pattern & access to other classes


#1

Hi Juce community !

 

I started to do a thing I've wanted to do for a long time: develop audio application. And I just found this fantastic & amazing library ! 

I read all the tutorials and a lot of topics of this forum but I'm still not able to fix my problem. It's all about design pattern.

I know a lot of programming language (objective-c for ios,  c#,  c, and so on...) but it's my first time with c++, so maybe my post could help some "newbies" in c++/Juce like me.

 

Here is my problem:

I want to play a sound and control some parameter with the help of slider. Like the "Audio level control" tutorial for exemple.

I used to use MVC pattern so I can't help to have different files. From what I understood Juce is not really made for a MVC pattern, but we can make a Model / View pattern. And here comes my issues.



Let's say I have MyAudiProcessor (.cpp/.h) where all the audio things are done especially the famous void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) .

Then I have MainComponent (.cpp/.h) where all the GUI things go (sliders, buttons, label, ...).

And I want to control the audio volume from the components values (it's exactly the same thing that we can see in the tutorial but instead using one file want to use 2 (or 4 with the .h)

 

My issues would be:

1°) What is the best way to interact between the classes ? What I have to do for having the value of the slider in the getNextAudioBlock method?

I thought to declare an instance of each class in each other. But how to handle the changes events?

Or maybe I have to work with listeners/change ?

Or an other thing I saw was Value and ValueTree ? But I'm not sure it's the best thing to use here (and I don't really understand what are these 2 things...)

I'm totally lost...

 

2°) What do you think about this design pattern which separate the GUI and from the rest ? Is it good or do you use a better pattern for Juce applications? 

 

Thanking you in advance for your help.

 


#2

I don't know the details of your program. It may depend to some extent on how big the program is or will become. Anyway, this is my opinion only.

I think it's fine to think of the MainComponent as kind of a controller. So in the MainComponent you create instances of the slider (and other controls) and also MyAudioComponent. Then you can make MainComponent a listener of the slider. That is, MainComponent inherits from Slider::Listener and calls AddListener() to register itself as a listener.

The handler for the slider events is then a member function of MainComponent. When the volume slider is moved and that handler gets called, you then push that value to the MyAudioComponent object. For example, make a "setter" function in MyAudioComponent which just stores the volume value. Then the next time getNextAudioBlock() function gets called, you can use that value that was set earlier. So this way, MyAudioComponent doesn't know anything about the GUI, it just uses the value given to it by MainComponent. Careful though, you have to protect that volume value because it will be accessed by two different threads: the GUI thread and audio thread.

Without knowing more about your app I don't know what would be considered the Model in MVC. Perhaps the files. I usually have some data that gets loaded or saved, such as a document in a word processing application, and that's my model. But if you're creating some program that, for example, just generates audio data and nothing more, then I'm not sure if there's a Model or not.

I've only created one program so far that uses audio and haven't created any plug-in programs yet. I was just now looking at a couple of those tutorial apps for the first time and noticed how some have the main component derived from AudioAppComponent. That's very convenient for small programs but you probably won't be doing that. It's hard to think about MVC in programs so small. In the tutorial they read the slider directly from the getNextAudioBlock function but that's not the best idea in a real program, as they do warn about.

I sometimes do what might be considered mixing up the View and Controller. For example, if there is a TreeView control, I might derive my own class from that called MyTreeView and add some extra intelligence to that. When MainComponent creates a MyTreeView, it gives it a pointer the the Model. So MyTreeView can retrieve the data it needs directly from the Model without having to depend on some controller to provide it. Perhaps that is having to much program-specific code inside a View component, but there are tradeoffs. I'm not saying this is "right" but I suppose I like views that are self-sufficient. For smallish programs I've made I mostly put program logic in MainComponent. So yeah, I also try to keep MVC in mind and gravitate towards it. Especially to keep the model separate.


#3

Thank you so much JimH for you very detailed reply.

I was a bit confused because I read a post of Jules who said that JUCE was not designed for a MVC pattern.

 

The main idea of my program is to mixe two different audio stream and send the result of this mix as output. So I choose a "player" as Model. It's a class who has an AudioFormatReaderSource object to keep a file, and other relevant attributs.

 

Why do you said that "In the tutorial they read the slider directly from the getNextAudioBlock function but that's not the best idea in a real program, as they do warn about." I thought it wasn't a problem as they said "While it is acceptable to call Slider::getValue() in the getNextAudioBlock() function you must not call the Slider::setValue() function within this the getNextAudioBlock() function."

 

 


#4

[quote]I was a bit confused because I read a post of Jules who said that JUCE was not designed for a MVC pattern. [/quote]

That may be true. I'm not sure I understand MVC perfectly clearly myself. I think it used to mean something different back in the days before operating systems had UI controls built in. One comment I was going to add: I've never used Qt before but I was reading about it a little one day. They seemed to make much of the fact that you can swap out different UI controls easily. For example, if you started out using a listbox you could easily switch it to a tree view if you wanted. But it seemed to me the way they achieved that by requiring the app to format its data in the particular way that Qt wants it, so they were effectively just pushing the work off to the app developer, I thought. Anyway, perhaps that's more of an MVC design, I don't know. I'm not sure that it's black and white---I usually just try to gravitate towards separating M, V, and C, even if I don't make it very strict.

[quote]Why do you said that "In the tutorial they read the slider directly from the getNextAudioBlock function but that's not the best idea in a real program, as they do warn about." I thought it wasn't a problem as they said "While it is acceptable to call Slider::getValue() in the getNextAudioBlock() function you must not call the Slider::setValue() function within this the getNextAudioBlock() function."[/quote]

Sorry I didn't say that very clearly. Here they're not talking about accessing Slider in general. Rather they are warning about calling the setValue() function in particular because there are two different threads involved. It's alright to call getValue() because you're not changing anything. It's just a read. But if you try to call setValue() from the audio thread while the user is changing the slider at the same time in the UI thread there is a conflict.

I was talking about accessing the slider, or any other UI object, in general from an audio class. That makes the audio class less reusable and reduces the encapsulation. It then depends on some UI control that you might not have in a different program, and so you can't use that audio class in another program so easily. It's better to keep UI out of such classes and just have them process data. Then use "glue code" in an application to connect the UI controls to the audio class. I would think of the glue code as part of the controller and something you need to rewrite for different applications.


#5

Yes, I understand. I think MVC is rather a "way of think" than an "exact science".

 

Yes you're right for the encapsulation the encapsulation. In order to acces to GUI things in other thread I use MessageManager::callAsync.

But if I had a method to do some DSP stuff, is it good to have such a prototype : computedsp(ObjectA objA, int valueParam); ? And just before I would do something like: valueParam = slider->getValue(). We agree that it's ok, isn't it?

 

I always wonder if my code is good or not, and it's very difficult to answer to it.


#6

Seems fine to me.