Field Type ' ' is an abstract class

#1

Hello, so I am trying to make it so that my synthesizer can handle external midi inputs, and so far i’ve been following the juce website tutorial on “handling midi inputs”, Ive made a new class with a header& cpp for the midi keyboard and input alone and this is what it looks like:

Header

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

//==============================================================================
/*
*/
class Midi    : public Component,
                private MidiInputCallback,
                private MidiKeyboardStateListener,
                private ComboBox::Listener
{
public:
    Midi();
    ~Midi();

    void paint (Graphics&) override;
    void resized() override;
    void setMidiInput(int index);
    void handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message)override;
    void handleNoteOn(MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)override;
    void handleNoteOff(MidiKeyboardState*, int midiChannel, int midiNoteNumber);
    void postMessageToList (const MidiMessage& message, const String& source);
    void addMessageToList (const MidiMessage& message, const String& source);
    void logMessage (const String& m);
    String getMidiMessageDescription (const MidiMessage& m);
    void comboBoxChanged(ComboBox*) override;

    private:
    AudioDeviceManager deviceManager;
    ComboBox midiInputList;
    Label midiInputListLabel;
    int lastInputIndex = 0;
    bool isAddingFromMidiInput = false;
    MidiKeyboardState keyboardState;           
    MidiKeyboardComponent keyboardComponent;
    TextEditor midiMessagesBox;
    double startTime;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Midi)
};

cpp

#include "../JuceLibraryCode/JuceHeader.h"
#include "Midi.h"

//==============================================================================
Midi::Midi() : keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
startTime (Time::getMillisecondCounterHiRes() * 0.001)
{
    addAndMakeVisible (midiInputListLabel);
    midiInputListLabel.setText ("MIDI Input:", dontSendNotification);
    midiInputListLabel.attachToComponent (&midiInputList, true);
    
    addAndMakeVisible (midiInputList);
    midiInputList.setTextWhenNoChoicesAvailable ("No MIDI Inputs Enabled");
    auto midiInputs = MidiInput::getDevices();
    midiInputList.addItemList (midiInputs, 1);
    midiInputList.onChange = [this] { setMidiInput (midiInputList.getSelectedItemIndex()); };
    
    // find the first enabled device and use that by default
    for (auto midiInput : midiInputs)
    {
        if (deviceManager.isMidiInputEnabled (midiInput))
        {
            setMidiInput (midiInputs.indexOf (midiInput));
            break;
        }
    }
    
    // if no enabled devices were found just use the first one in the list
    if (midiInputList.getSelectedId() == 0)
        setMidiInput (0);
    
    addAndMakeVisible (keyboardComponent);
    keyboardState.addListener (this);
    
    addAndMakeVisible (midiMessagesBox);
    midiMessagesBox.setMultiLine (true);
    midiMessagesBox.setReturnKeyStartsNewLine (true);
    midiMessagesBox.setReadOnly (true);
    midiMessagesBox.setScrollbarsShown (true);
    midiMessagesBox.setCaretVisible (false);
    midiMessagesBox.setPopupMenuEnabled (true);
    midiMessagesBox.setColour (TextEditor::backgroundColourId, Colour (0x32ffffff));
    midiMessagesBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
    midiMessagesBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));
}

Midi::~Midi()
{
}

void Midi::paint (Graphics& g)
{

}

void Midi::resized()
{
    auto area = getLocalBounds();
    
    midiInputList    .setBounds (area.removeFromTop (36).removeFromRight (getWidth() - 150).reduced (8));
    keyboardComponent.setBounds (area.removeFromTop (80).reduced(8));
    midiMessagesBox  .setBounds (area.reduced (8));
    
}
void Midi::setMidiInput (int index)
{
    auto list = MidiInput::getDevices();
    deviceManager.removeMidiInputCallback (list[lastInputIndex], this);
    auto newInput = list[index];
    if (! deviceManager.isMidiInputEnabled (newInput))
        deviceManager.setMidiInputEnabled (newInput, true);
    deviceManager.addMidiInputCallback (newInput, this);
    midiInputList.setSelectedId (index + 1, dontSendNotification);
    lastInputIndex = index;
}
void Midi::handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message)
{
    const ScopedValueSetter<bool> scopedInputFlag (isAddingFromMidiInput, true);
    keyboardState.processNextMidiEvent (message);
    postMessageToList (message, source->getName());
}
void Midi::handleNoteOn(MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
{
    if (! isAddingFromMidiInput)
    {
        auto m = MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity);
        m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
        postMessageToList (m, "On-Screen Keyboard");
    }
}
void Midi::handleNoteOff(MidiKeyboardState*, int midiChannel, int midiNoteNumber) 
{
    if (! isAddingFromMidiInput)
    {
        auto m = MidiMessage::noteOff (midiChannel, midiNoteNumber);
        m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
        postMessageToList (m, "On-Screen Keyboard");
    }
}

class IncomingMessageCallback   : public CallbackMessage
{
public:
    IncomingMessageCallback (Midi* o, const MidiMessage& m, const String& s)
    : owner (o), message (m), source (s)
    {}
    
    void messageCallback() override
    {
        if (owner != nullptr)
            owner->addMessageToList (message, source);
    }
    
    Component::SafePointer<Midi> owner;
    MidiMessage message;
    String source;
};

void Midi::postMessageToList (const MidiMessage& message, const String& source)
{
    (new IncomingMessageCallback (this, message, source))->post();
}
void Midi::addMessageToList (const MidiMessage& message, const String& source)
{
    auto time = message.getTimeStamp() - startTime;
    auto hours   = ((int) (time / 3600.0)) % 24;
    auto minutes = ((int) (time / 60.0)) % 60;
    auto seconds = ((int) time) % 60;
    auto millis  = ((int) (time * 1000.0)) % 1000;
    auto timecode = String::formatted ("%02d:%02d:%02d.%03d",
                                       hours,
                                       minutes,
                                       seconds,
                                       millis);
    auto description = getMidiMessageDescription (message);
    String midiMessageString (timecode + "  -  " + description + " (" + source + ")"); // [7]
    logMessage (midiMessageString);
}

 String Midi::getMidiMessageDescription (const MidiMessage& m)
{
    if (m.isNoteOn())           return "Note on "          + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3);
    if (m.isNoteOff())          return "Note off "         + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3);
    if (m.isProgramChange())    return "Program change "   + String (m.getProgramChangeNumber());
    if (m.isPitchWheel())       return "Pitch wheel "      + String (m.getPitchWheelValue());
    if (m.isAftertouch())       return "After touch "      + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) +  ": " + String (m.getAfterTouchValue());
    if (m.isChannelPressure())  return "Channel pressure " + String (m.getChannelPressureValue());
    if (m.isAllNotesOff())      return "All notes off";
    if (m.isAllSoundOff())      return "All sound off";
    if (m.isMetaEvent())        return "Meta event";
    
    if (m.isController())
    {
        String name (MidiMessage::getControllerName (m.getControllerNumber()));
        
        if (name.isEmpty())
            name = "[" + String (m.getControllerNumber()) + "]";
        
        return "Controller " + name + ": " + String (m.getControllerValue());
    }
    
    return String::toHexString (m.getRawData(), m.getRawDataSize());
}

void Midi::logMessage (const String& m)
{
    midiMessagesBox.moveCaretToEnd();
    midiMessagesBox.insertTextAtCaret (m + newLine);
}
void Midi::comboBoxChanged(ComboBox* box)
{
    
}

I wish to pass this class as an object in my pluginEditor.h, however I always recieve an error “Field type ‘Midi’ is an abstract class”, I tried researching the bug, and adding all the functions required from each class, but still do not know why i get this error.

pluginEditor

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"
#include "OscillatorEditor.h"
#include "Midi.h"
//==============================================================================
/**
*/
class SharodVstAudioProcessorEditor:
public AudioProcessorEditor,
public Slider::Listener
{
public:
    SharodVstAudioProcessorEditor (SharodVstAudioProcessor&);
    ~SharodVstAudioProcessorEditor();

    //==============================================================================
    void paint (Graphics&) override;
    void resized() override;
    void sliderValueChanged(Slider* slider) override;
private:
    
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    SharodVstAudioProcessor& processor;
        Midi midiGUI;
public:
(SharodVstAudioProcessorEditor)
};

Thanks for the help in advance.

0 Likes

#2

The synthesizer classes will allow you handle external midi inputs - have you watched the tutorial that I linked you to in the other thread? That should explain everything in detail.

0 Likes

#4

I had watched a few of these videos, and I can input midi notes, i just can’t seem to get inputs from external devices, the tutorial does not seem to cover this sadly.

0 Likes

#5

There are several things wrong here:

If you want it to be a plugin, you will get the midi and audio data in processBlock.
a) You must not create an AudioDeviceManager, it is handled by the host
b) You must not create a MidiInputDevice, it is handled by the host
c) try to avoid inheriting, if you can achieve the same with aggregation (MidiKeyboardComponent)

About your actual compiler error:

“Field type ‘Midi’ is an abstract class”

The class Midi inherits a class with pure virtual functions, that you have to implement.
Component is not pure virtual, but all other three methods have abstract methods, that you have to implement:

private MidiInputCallback,
private MidiKeyboardStateListener,
private ComboBox::Listener

Good luck

0 Likes