Inserting Midi events into AudioDeviceManager stream?


#1

I'm trying to figure out how to best insert midi messages into the midi Buffer that this class uses when you open a midi device, but i'm not sure how to access it.  

 

The issue i'm having is that my MainContentComponent has a child component, which responds to mouse clicks.  These mouse clicks toggle a midi message, and I need to insert that message into MainContentComponent's handleIncomingMidiMessage(), which distributes the message to the other child components.   So, what's the best approach to talk to the parent component from the child component, and call a method on the parent from within the child? 


#2

is making my MainContentComponent subclass the Message or MessageListener class the right approach, and then make the child subclass Message/MessageListener to make them talk to each other? 


#3

Well, if your approach works, then it should be fine I guess.

Otherwise you can take a look at the MidiKeyboardComponent class. It displays a virtual keyboard, which you can click on with your mouse. You can see it in action in the JUCE Demo "Midi I/O". When you create a MidiKeyboardComponent, you give it a MidiKeyboardState. This keyboard state makes the Midi events available. You can pass a Midi buffer to it and it is going to add the Midi events, which were created by the mouse clicks. To do so, use the function MidiKeyboardState::processNextMidiBuffer(...).

You can create a similar processNextMidiBuffer(...) function for your component.

Now maybe you are wondering how to let the processNextMidiBuffer(...) function interact with the AudioDeviceManager. Well, I assume you have already created a Midi collector using the MidiMessageCollector class. And I assume, you already have added the Midi collector to your audio device manager using code such as

myAudioDeviceManager.addMidiInputCallback (String::empty, &myMidiCollector);

So that means, your audio device manager is happily filling your Midi collector with Midi events, from your external Midi keyboard. Somewhere in your application you have an audio or Midi loop. That is: a function, which gets called over and over again, to process your audio and/or Midi data (e.g. AudioSource::getNextAudioBlock(...) ). Inside of that loop you probably already have code similar to

// fill a midi buffer with incoming messages from the external midi device.
MidiBuffer incomingMidi;
myMidiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);

Now just add the processNextMidiBuffer(...) function, which I talked about above. So altogether the code could look similar to:

// fill a midi buffer with incoming messages from the external midi device.
MidiBuffer incomingMidi;
myMidiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
// Add midi data from my own GUI component, which I click on on with the mouse:
myGUIComponent.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);

 

 

 


#4

that's not what I was asking at all. 


#5

Why not use getParentComponent() casting the result to your MainContentComponent class?


#6

ok, I made this workaround. 

 

#ifndef MESSAGEOBJECT_H_INCLUDED
#define MESSAGEOBJECT_H_INCLUDED
#include "../JuceLibraryCode/JuceHeader.h"

class MessageObject : public Message
{
public:
    MessageObject( String d ) : Message()
    {
        data = d;
    }
    ~MessageObject(){}
    String data;
private:
};

class MessageDistributor
{
public:
    MessageDistributor() {}
    ~MessageDistributor() {}
    void postMessageForDistribution( const Message& message ) {
        for( int i = 0; i < recipients.size(); i++ ) {
            recipients[i]->handleMessage( message );
        }
    }
    void addRecipient( MessageListener* recip ) {
        recipients.push_back(recip );
    }
    void clearRecipients() {
        recipients.clear();
    }
private:
    std::vector<MessageListener*> recipients;
};

#endif  // MESSAGEOBJECT_H_INCLUDED

 

as long as my component classes subclass MessageListener, and they all have the following member variable, it works:

    SharedResourcePointer<MessageDistributor> messageDistributor;

to test it, in each Component I added the following methods (change the class qualifier, of course )

void MessageTester::handleMessage(const Message& message) {
    std::cout << "MessageTester received message: ";
    const MessageObject& mo = dynamic_cast<const MessageObject&>(message);
    std::cout << mo.data << "\n";
}

void MessageTester::mouseDown(const MouseEvent& event ) {
    MessageObject mo ( "MessageTester::mouseDown" );
    messageDistributor->postMessageForDistribution( mo );
}

now, when I click inside my "MainContentComponent", the console output looks like this:

MessageTester received message: MainContentComponent::mouseDown
MainContentComponent received Message: MainContentComponent::mouseDown

good times, good times.