Out-of-line definition error?

Hello, everyone. I’m very new to audio programming. I’m getting the following errors when following “The Audio Programmer’s Tutorials” https://www.youtube.com/c/TheAudioProgrammer/videos

“Out-of-line definition of ‘VibeSynthAudioProcessorEditor’ does not match any declaration in ‘VibeSynthAudioProcessorEditor’”

“Member initializer ‘audioProcessor’ does not name a non-static data member or base class”

these errors are on my plugineditor.cpp file. For context, here’s the code where the error shows up:
VibeSynthAudioProcessorEditor::VibeSynthAudioProcessorEditor (VibeSynthAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor §
{
setSize (400, 300);
}
Does anyone have any idea what could be the issue? Let me know if there’s anything I can do to provide more context. Thanks!

Try to write juce:: in front of AudioProcessorEditor. You have to do this to all juce symbols.

I did that and it threw the exact same error

What is that code? That seems to be the problem to me.

it’s supposed to be “( p )” without spaces. idk why it showed up as that

First of all, you should put a line of ``` above and below your code snippets, that will display them in code formatting and avoid such unwanted formatting errors.

Then to the error you are getting:

The error message is quite clear here. Generally, if you write audioProcessor (p) in your constructors member initializer list like you did, it means that you want to assign p to your member variable with the name audioProcessor or that your class is inheriting a base class and you call that base class constructor with this argument (like you do with AudioProcessorEditor). In this case, I highly assume that you want to initialize a (private) member variable of the type VibeSynthAudioProcessor& named audioProcessor. Now the compiler fails to find this variable. So, where did you declare that variable that you are trying to assign?

I declare audioProcessor in my PluginProcessor.h file. VibeSynthAudioProcessor inherits from juce::AudioProcessor . I made sure to use #include the PluginProcessor.h file.

Declaring it there won’t help you. You have to make it a member of your VibeSynthAudioProcessorEditor class. The member initializer list of a class constructor is meant to initialize member variables of the class you are about to construct. So the compiler is absolutely right when it complains that

while it is trying to compile your editor class.

Explicitly, in your editor class, you have to add something like

class VibeSynthAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
    //...

private:
    VibeSynthAudioProcessor& audioProcessor; // <- this is the variable that the compiler fails to find
}

That’s exactly what I have in my .h file. So, you’re saying I should put that in my .cpp file?

That should typically go in your PluginEditor.h file (or however you name the header file where you put your class declaration). However, you wrote

Which would not be the right place to put the declaration of your editor class. If you want to share your processor and editor .h and .cpp files here, it will be probably a lot easier to help you

ok, it says new users can’t upload attachments. So, I’ll just paste it here.
PluginEditor.cpp

  ==============================================================================

    This file contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"
#include <JuceHeader.h>


//==============================================================================
VibeSynthAudioProcessorEditor::VibeSynthAudioProcessorEditor (VibeSynthAudioProcessor& p) //VibeSynthAudioProcessor& p
    : AudioProcessorEditor (&p)
    , audioProcessor (p)
{
    
    //juce::AudioProcessorEditor::AudioProcessorEditor(getAudioProcessor()& )
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (400, 300);
    //setVisible(true);
}
    //mastergain slider stuff
//    masterGainSlider.setSliderStyle(juce::Slider::LinearBarVertical);
//    masterGainSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 10, 50);
//    masterGainSlider.setPopupDisplayEnabled (true, false, this);
//    masterGainSlider.setRange(0.0, 1, 0.1);
//    masterGainSlider.setValue(1.0);
//    addAndMakeVisible(&masterGainSlider);
//}

VibeSynthAudioProcessorEditor::~VibeSynthAudioProcessorEditor()
{
}
//==============================================================================
//void VibeSynthAudioProcessorEditor::paint (juce::Graphics& g)
//{
//    // (Our component is opaque, so we must completely fill the background with a solid colour)
//    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
//
//    g.setColour (juce::Colours::white);
//    g.setFont (15.0f);
//    g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
//}
void VibeSynthAudioProcessorEditor::paint (juce::Graphics& g)
{
    // fill the whole window white
    g.fillAll (juce::Colours::white);
 
    // set the current drawing colour to black
    g.setColour (juce::Colours::black);
 
    // set the font size and draw text to the screen
    g.setFont (15.0f);
 
    g.drawFittedText ("Midi Volume", 0, 0, getWidth(), 30, juce::Justification::centred, 1);
}
void VibeSynthAudioProcessorEditor::resized()
{
    // This is generally where you'll want to lay out the positions of any
    // subcomponents in your editor..
    //gets the bounds wen users resize window, bounds updates
//    auto bounds = getLocalBounds();
//    const int componentSize{100};
    masterGainSlider.setBounds(40, 30, 20, getHeight() - 60); //(x,y, width, height)
}
/*
void VibeSynthAudioProcessorEditor::closedButtonPressed()
{
    juce::JUCEApplicationBase::getInstance()->systemRequestedQuit();
}

void VibeSynthAudioProcessorEditor::shutdown()
{
    mainWindow = nullptr;
}
*/

PluginEditor.h

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#pragma once

#include <JuceHeader.h>
#include "PluginProcessor.h"

//==============================================================================
/**
*/

class VibeSynthAudioProcessorEditor  : public juce::AudioProcessorEditor //(name, Colours::white,
                                                                        //VibeSynthAudioProcessorEditor::allButtons)
{
public:
    VibeSynthAudioProcessorEditor (VibeSynthAudioProcessorEditor&);//VibeSynthAudioProcessor&
    ~VibeSynthAudioProcessorEditor() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;
    /*
    void closedButtonPressed();
    void shutdown();
    */

private:
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    VibeSynthAudioProcessorEditor& audioProcessor;
    std::unique_ptr<VibeSynthAudioProcessorEditor> mainWindow;
    juce::Slider masterGainSlider;
    
    

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VibeSynthAudioProcessorEditor)
};

PluginProcessor.cpp

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"

//==============================================================================
VibeSynthAudioProcessor::VibeSynthAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  juce::AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", juce::AudioChannelSet::stereo(), true)
                     #endif
                       )//, apvts(*this, nullptr, "Parameters", createParameters())
#endif
{
    synth.addSound(new SynthSound());
    synth.addVoice(new SynthVoice());
}

VibeSynthAudioProcessor::~VibeSynthAudioProcessor()
{
}

//==============================================================================
const juce::String VibeSynthAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

bool VibeSynthAudioProcessor::acceptsMidi() const
{
   #if JucePlugin_WantsMidiInput
    return true;
   #else
    return false;
   #endif
}

bool VibeSynthAudioProcessor::producesMidi() const
{
   #if JucePlugin_ProducesMidiOutput
    return true;
   #else
    return false;
   #endif
}

bool VibeSynthAudioProcessor::isMidiEffect() const
{
   #if JucePlugin_IsMidiEffect
    return true;
   #else
    return false;
   #endif
}

double VibeSynthAudioProcessor::getTailLengthSeconds() const
{
    return 0.0;
}

int VibeSynthAudioProcessor::getNumPrograms()
{
    return 1;   // NB: some hosts don't cope very well if you tell them there are 0 programs,
                // so this should be at least 1, even if you're not really implementing programs.
}

int VibeSynthAudioProcessor::getCurrentProgram()
{
    return 0;
}

void VibeSynthAudioProcessor::setCurrentProgram (int index)
{
}

const juce::String VibeSynthAudioProcessor::getProgramName (int index)
{
    return {};
}

void VibeSynthAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
}

//==============================================================================
void VibeSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    synth.setCurrentPlaybackSampleRate(sampleRate); //sets the samplerate
    //grabs the total num voices in the synth
    for (int i = 0; i < synth.getNumVoices(); i++) {
        //dynamic cast converts types, pointers, references to classes up down sideways in their hierarchy
        //grabs the synth voices, makes them pointers* and puts them into prep to play function
        if (auto voice = dynamic_cast<SynthVoice*>(synth.getVoice(i))) {
            voice->prepareToPlay(sampleRate, samplesPerBlock, synth.getNumVoices());
        }
    }
}

void VibeSynthAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}

#ifndef JucePlugin_PreferredChannelConfigurations
bool VibeSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
  #if JucePlugin_IsMidiEffect
    juce::ignoreUnused (layouts);
    return true;
  #else
    // This is the place where you check if the layout is supported.
    // In this template code we only support mono or stereo.
    // Some plugin hosts, such as certain GarageBand versions, will only
    // load plugins that support stereo bus layouts.
    if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
     && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
        return false;

    // This checks if the input layout matches the output layout
   #if ! JucePlugin_IsSynth
    if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
        return false;
   #endif

    return true;
  #endif
}
#endif

void VibeSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());
    
    for (int i = 0; i < synth.getNumVoices(); i++) {
        if (auto voice = dynamic_cast<juce::SynthesiserVoice*>(synth.getVoice(i))) {
            //do stuff with osc, ADSR, LFO, update parameters from value tree
        }
    }
    //basically loads the next block of audio input from midi i guess
    //rendernext block is what actually ouputs the sound from the synth
    synth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    // Make sure to reset the state if your inner loop is processing
    // the samples and the outer loop is handling the channels.
    // Alternatively, you can process the samples with the channels
    // interleaved by keeping the same state.
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        auto* channelData = buffer.getWritePointer (channel);

        // ..do something to the data...
    }
}

//==============================================================================
bool VibeSynthAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

juce::AudioProcessorEditor* VibeSynthAudioProcessor::createEditor()
{
    return new VibeSynthAudioProcessorEditor (*this);
}

//==============================================================================
void VibeSynthAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
}

void VibeSynthAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
}

//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new VibeSynthAudioProcessor();
}

//Value Tree. this is where you define your osc types, ADSR, etc.

PluginProcessor.h

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#pragma once

#include <JuceHeader.h>
#include "SynthVoice.h"
#include "SynthSound.h"
#include "PluginEditor.h"

//==============================================================================
/**
*/
class VibeSynthAudioProcessor  : public juce::AudioProcessor
{
public:
    //==============================================================================
    VibeSynthAudioProcessor();
    ~VibeSynthAudioProcessor() override;

    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;

   #ifndef JucePlugin_PreferredChannelConfigurations
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
   #endif

    void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;

    //==============================================================================
    juce::AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;

    //==============================================================================
    const juce::String getName() const override;

    bool acceptsMidi() const override;
    bool producesMidi() const override;
    bool isMidiEffect() const override;
    double getTailLengthSeconds() const override;

    //==============================================================================
    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram (int index) override;
    const juce::String getProgramName (int index) override;
    void changeProgramName (int index, const juce::String& newName) override;

    //==============================================================================
    void getStateInformation (juce::MemoryBlock& destData) override;
    void setStateInformation (const void* data, int sizeInBytes) override;

private:
    juce::Synthesiser synth;
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VibeSynthAudioProcessor)
};

This should of course be a reference to a VibeSynthAudioProcessor not a VibeSynthAudioProcessorEditor :grinning:

However, this does not really match the compiler error message you posted. Did you change that line after your initial post?

Likewise, the parameter for the constructor should be a reference to the processor, not to the editor, because this is the editor class. It gets passed a reference to the processor. This is why you get that error: because you’re trying to use the parameter ‘p’ as if it referenced a processor instance, but you’ve declared it to reference an editor instance.

I tried that and it says
Unknown type name 'VibeSynthAudioProcessor'; did you mean 'VibeSynthAudioProcessorEditor'? Replace 'VibeSynthAudioProcessor' with 'VibeSynthAudioProcessorEditor'

When I change (VibeSynthAudioProcessorEditor&) to (VibeSynthAudioProcessor&) it throws the error Unknown type name 'VibeSynthAudioProcessor'; did you mean 'VibeSynthAudioProcessorEditor'? Replace 'VibeSynthAudioProcessor' with 'VibeSynthAudioProcessorEditor'

This is not really JUCE-related, but basic C/C++ requirements. Any symbol you use has to be defined someplace where your code will know what it represents. That means including the header file where it is defined in the file where it will be used.

Since you don’t want your editor’s header to include your processor’s header (and vice-versa), you can use the “class” forward declaration in your editor’s header:

class VibeSynthAudioProcessor;

Put this just before the definition of your VibeSynthAudioProcessorEditor class, in PluginEditor.h. (You already include PluginProcessor.h in your PluginEditor.cpp file, so you don’t need to add that.)