Refactoring past JUCE coursework. Any criticisms on how I am structuring my code?

Hey, I am looking to polish some course work toward building sample projects for potential job opportunities. I want this to be a very rudimentary “modern metal” guitar effect that will also later apply lowpassing around 100Hz and hard compression to get that aggressive “djent” sound. I’m by no means an expert c++ programmer. I have the intuition that functions should be clean, so creating lots of subroutines is useful. I have never had a job or internship in c++ programming before so I don’t know anything about what is expected of me as an entry level applicant. I am open to you tearing my code apart as much as you think I need to hear.

GuitarGUI.h

class GuitarGUI  : public AudioProcessorEditor, public Slider::Listener

{

public:
    GuitarGUI(GuitarProcessor&p);
    ~GuitarGUI();
    void paint (Graphics&) override;
    void sliderValueChanged(Slider* slider) override; 
    void resized() override;
    void clippingTypeChanged();
    void modTypeChanged();
    enum distorttype
    {   
        softclipping = 1,
        hardclipping = 2 
    };
    enum modtype
    {
        FM = 1,
        AM = 2 
    };
    ComboBox changeDistorttype; 
    ComboBox changeModtype; 
    Slider   changeoverdrive;
    Slider   changemoddelta;
    Slider   changepulsedelta;

private:
    void setAngleDelta(Slider* slider, float delta); 
    void setOverdrive();
    void setGui(ComboBox* box , distorttype typeOne, distorttype typetwo);

    void setGui(Slider* slider, String suffix, float low, float high, float inc);
    void setGui(Slider* slider, String suffix, Slider::SliderStyle style, float low, float high, float inc);
    

    GuitarProcessor& processor;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GuitarGUI)
}; ```

#include "GuitarProcessor.h"
#include "GuitarGUI.h"

GuitarGUI::GuitarGUI(GuitarProcessor& p)
    : AudioProcessorEditor(&p), processor(p)
{   setSize(500, 500);
    
  setGui(&changeoverdrive, "Overdrive", 0.0f, 3.0f, 0.01f); 

}

void GuitarGUI::clippingTypeChanged()
{    
    
}

 void GuitarGUI::modTypeChanged()
{
    

}

void GuitarGUI::paint (Graphics& g)
{
   

}

void GuitarGUI::setAngleDelta(Slider* slider, float delta) 
{
    auto cyclesPerSecond = slider->getValue() / processor.mSampleRate;
    auto angledelta = cyclesPerSecond * 2.0 * MathConstants<double>::pi;
}
void GuitarGUI::sliderValueChanged(Slider* slider)
{   
    //set the modulator angle delta. 
    setAngleDelta(&changemoddelta, processor.mModAngleDelta);
    //set the pulser angle delta. 
    setAngleDelta(&changepulsedelta, processor.mPulseAngleDelta);

    processor.mOverdrive = changeoverdrive.getValue(); 

}
void GuitarGUI::setGui(Slider* slider, String suffix, float low, float high, float inc)
{   
    addAndMakeVisible(slider);
    slider->setTextValueSuffix(suffix); 
    //Set the low and high values of the slider as well as how much the slider is incremented on slider event. 
    slider->setRange(low, high, inc); 
    slider->setSliderStyle(Slider::LinearBarVertical);
    slider->setTextBoxStyle(Slider::NoTextBox, false, 90, 0);
    slider->setValue(low);
}
void setGui(Slider* slider, String* suffix, Slider::SliderStyle style, float low, float high, float inc)
{
    slider->setTextValueSuffix(*suffix);
    //Set the low and high values of the slider as well as how much the slider is incremented on slider event. 
    slider->setRange(low, high, inc);
    slider->setSliderStyle(style);
    slider->setTextBoxStyle(Slider::NoTextBox, false, 90, 0);
    slider->setValue(low);


}
/*
void GuitarGUI::setGui(ComboBox* box, distorttype typeOne, distorttype typetwo) 
{

}
*/


    
void GuitarGUI::resized()
{   
    
}

GuitarGUI::~GuitarGUI() 
{

        
} 
void GuitarProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();
    auto n = buffer.getNumSamples();
    
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());
   
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        auto* channelData = buffer.getWritePointer(channel);
        
        double currentModAngle = mModCurrentAngle;

        // do modulation on the sample buffer first. 
        for (int i = 0; i < buffer.getNumSamples(); ++i)
        {
            
            if (mModType == 1)
            {
                float ringMod; 
                ringMod = std::sin(currentModAngle);
                currentModAngle += mModAngleDelta;
                channelData[i] *= ringMod;

            }
            
            if(mModType == 2)
            {
                float AM; 
                AM = std::sin(currentModAngle);
                currentModAngle += mModAngleDelta;
                channelData[i] *= AM;
                AM += 1.0;
                AM *= 0.5;
            }


        }
        // Then do distortion. 
        for (int i = 0; i < buffer.getNumSamples(); ++i)
        {
            channelData[i] *= mOverdrive;


            if (mDistortionType == 1)
                channelData[i] = tanh(channelData[i]);
            else if (mDistortionType == 2)
            {
                if (channelData[i] > 1.0f)
                    channelData[i] = 1.0f;

                else if (channelData[i] < -1.0f)
                    channelData[i] = -1.0f;
            }


        }

        //Then do pulse. 
        double currentAngle = mPulseCurrentAngle;
        
        for (int i = 0; i < buffer.getNumSamples(); ++i)
        {
            float lfoSample;

            switch (channel)
            {

            case 0:
                lfoSample = std::sin(currentAngle);
                break;
            case 1:
                lfoSample = std::sin(currentAngle + MathConstants<double>::pi);
                break;
            default:
                lfoSample = 0.0;
                break;
            }

            lfoSample += 1.0;

            lfoSample *= 0.5;


            currentAngle += mPulseAngleDelta;

            //Do modulation if the there is an LFO.  
            if (mPulseAngleDelta > 0.0)
            {
                channelData[i] *= lfoSample;

            }


        }

        

    }
    
    mModCurrentAngle += n * mModAngleDelta;
    mModCurrentAngle = fmod(mModCurrentAngle, 2.0 * MathConstants<double>::pi);
    mPulseCurrentAngle += n * mPulseAngleDelta;
    mPulseCurrentAngle = fmod(mPulseCurrentAngle, 2.0 * MathConstants<double>::pi);
  
} ```

If I were the interviewer I would ask the following questions:

  • why low-passing around 100Hz? quite a lot of notes are above 100Hz, do you mean high-passing?
  • why no std:: for tanh? (rhetorical one)
  • what is the pulse section doing and is there a more elegant way to write this std::sin(currentAngle + MathConstants<double>::pi)? I guess I also would ask if there’s a more elegant way to write std::sin(currentAngle + 0.5 * MathConstants<double>::pi) just to test trigonometry skills :wink:
  • what are your strategies to prevent aliasing due to non-linear distortion?
1 Like

Yes high passing. I simply looked up how the style of music I was interested in reproducing uses filtering. The pulse section is applying an LFO to create pulsing, but can be delegated to a seperate sub-routine for clarity of the code. The same is true for the modulator, that should be moved to a separate subroutine. As for applying the compression, for I would have to place constraints around the length of the attack to make it staccato, while also being sure to balance the makeup gain to get the “punch” that I would like without blowing out the user’s eardrums. Modulation in the context of the modern metal genre would have to be constrained very tightly as well given that the sound should sound, “big” but not wobbly. I also have a schroeder reverb I was assigned to build that I wasn’t able to debug to the full capacity. I will also revisit this.

Given that I my goal for this processor is to sound big I would likely want to have subtle early reflections for the sound to be “big” but not hyperbolic. As for std:: I can see that this is something to improve upon.

I also agree with you that routine args longer than a few characters should be stored as variables.

Oh no that’s just fine! It’s just a little sum, no need for an extra line of code. What I actually meant is: sin(x + pi) == -sin(x), and also sin(x + pi/2) == cos(x)

Oh that’s right. I was getting there. To shift a wave 180 degrees out of phase is to make it negative, cos is sin a half a cycle out of phase. I took a matlab based course on this stuff, but just needed a refresher.