New simple yet complete JUCE 5.1 synthesizer


#1

Hello everyone. I’ve just published new GPL3 sample code for a simple, yet complete music synthesizer plugin based on Juce 5.1. The code is on GitHub at https://github.com/getdunne/VanillaJuce, and there is some quite detailed documentation at http://getdunne.net/wiki/doku.php?id=vanillajuce.

I welcome all feedback and suggestions to improve this example, to make it the best possible illustration of the “JUCE way”. Comment on this post or message me, whichever you prefer. Thanks in advance.


#2

This is probably a great introduction to those who are just learning JUCE.

Not quite sure what your goal is though, is it to help out newcomers or to make a good synthesizer?


#3

Goal is to help newcomers; I should have clarified in my post, thanks.

Also, the documentation link is fixed now. No need to log in.


#4

Great contribution!

But… if you’re posting your code for beginners to learn from, you really need to get up to speed with modern C++ practices, because all the old-fashioned stuff in here sets a really bad example to any newcomers who don’t know that there are better ways to do things.

I can see that you’re a proficient programmer, and I’m guessing you’ve written a lot of high-quality C in the past, but modern C++ is a very different beast and you need to lose all that old baggage!

So quick code review:

ditch all your new/delete/sprintf/strcmp and other C functions.
Convert all your for loops into range-based fors and you’ll cut out a ton of boilerplate. Use member variables for subcomponents rather than pointers.
Use juce::String or std::string, not raw char arrays (!)
“typedef enum” is C, not C++! An “enum class” has vastly more type-safety, and enforces a syntax that makes the old-fashioned ‘k’ prefix redundant.
NEVER use a #define for a constant!! Use constexpr!
Pass references if possible, not pointers!
Use inline member variable initialisation and you’ll save another hundred lines of code.
If you’re passing an argument that’s a string, you can just use a string literal e.g. foo ("xyz"), you don’t need to wrap it in foo (String ("xyz")) (God knows why we see sooo much code that’s written that way)

Hope that helps! :slight_smile:

(Also, the list above is probably something that could be copy-pasted as a code review for hundreds of other projects that people have shared on here over the years!)


#5

This “old dog” has been working alone for too long :slight_smile: Thanks for a terrific list of “new tricks”.

With your permission, I’d like to use your list as the basis for a new article on my wiki. My idea is to start with exactly what you’ve written, then expand on each point with a bit of research and some examples drawn from the present VanillaJuce code. By the end of the process I will have modernized the entire code base, and produced a practical teaching tool.


#6

Quick question: What do you mean by “Use member variables for subcomponents rather than pointers”?


#7

Just took a peek at your code, it looks like you’re using instances of ScopedPointer to hold all your GUI’s subcomponents. You can just make them member variables and initialize them in your constructor’s initializer list, no need for pointers or new.


#8

I also looked at your constructors and you can clean those up using lambda expressions. Quick example that’s not exactly your code but should give you the right idea.

class EnvelopeComponent : public Component
{
public:
    EnvelopeComponent() /* user the initializer list */
    :    attack (Slider::Rotary, Slider::noTextBox), //very helpful constructor for Sliders
         decay  (Slider::Rotary, Slider::noTextBox),
         sustain (Slider::Rotary, Slider::noTextBox),
         release (Slider::Rotary, Slider::noTextBox)
    {
        auto initSlider = [this] (Slider& s)  // lambda expression declaration
        {
            addAndMakeVisible (s):
            s.setRange (1.0, 500.0);
            s.setValue (25.0);
        };

         initSlider (attack);
         initSlider (decay);
         initSlider (release);
         
         addAndMakeVisible (sustain);
         sustain.setRange (0.0, 1.0);
         sustain.setValue (0.707);
    }
    //.... yada yada yada 
private:
    Slider attack, sustain, decay, release; /* don't need to put them on different lines */
};

Using short little lambdas like that in your constructors makes the code easier to understand, prevents copy-paste errors, and lets you change small things about how you initialize your subcomponents very easily, while at the same time shortening your code so you don’t have a 100+ line constructor.


#9

Thanks for clarifying. @jules was this what you meant?


#10

All the ScopedPointers and lengthy constructors were generated by the Projucer (using its GUI editor features), so I thought that was the recommended style.


#11

I’ll be honest I’ve never used the GUI editor. In this thread @jules mentioned that the GUI editor is kept around for legacy reasons so I’m not sure if it’s the best tool to be using. Plus the GUI editor might have no idea that you’re creating a bunch of sliders with the same initialization code, and it’s never a good idea to repeat yourself.

Maybe the wrong thread for this but I don’t understand why the GUI editor would use scoped pointers and that syntax for constructing the subcomponents. The subclassed Component instance is going to keep ownership of the objects whether or not they’re scoped pointers and added to the component that way, or if they’re just members.


#12

Fair point, that might be a result of the ambiguity towards the GUI editor in Projucer, they can’t decide if to groom or to kill it. The third option, ignoring it, leads to that understanding…

It was (and is still) the accepted style IMHO, using members is a kind of preference, e.g. some components of them might be optional, so you can’t avoid the scopedPointers in that instance…

Most of the time many ways lead to Rome, some take longer and some may have disadvantages. And some like raw pointers are just hazardous… (haven’t looked at your code specifically, just generally speaking)


#13

I’ve completed an initial update of the VanillaJuce code, using this list as a guide. For the benefit of others who may also be unaware of, or uncertain about these newer C++ features and techniques, I have written up the whole process in detail at http://getdunne.net/wiki/doku.php?id=modernizing_the_vanillajuce_code_base.

To all who are interested: please review and comment if you have time, and thank you for your suggestions so far. I can’t imagine a better, quicker learning experience.

I won’t be attending ADC 2017 this year (didn’t feel I was ready, and scared off by those Shoreditch hotel prices :wink: ) but I’m starting to feel a lot more confident for 2018!


#14

You mention at the end of your last post that you are not quite sure about what Jule’s meant when he wrote the above. Perhaps he’s referring to default member initializer which has been around since C++11.
http://en.cppreference.com/w/cpp/language/data_members#Member_initialization