Create chords from one midi note

midi

#1

Hi all,
I am trying to figure out how to play chords with a single midi note.
Eventually I want a single midi note to trigger then next chord in a sequence.
I used the audio plugin projucer template .
So many of the tutorials just have a header and main which makes it a little tricky for me to figure out where to put functions etc …
At this point I have declared a std::vector or vectors to store a basic chord sequence.
Its declared globally in PluginProcessor.cpp like this :

// global variables for plugin

int playing[127]; // array to hold notes playing
int numChords; // num of chords in chord sequence
int numNotes ; // number of notes in a chord
int note; // a midi note value
int chordsPosition = 0; // pointer to postion in chord array
vector<int> chord;
// initialize the chord array to 3 chords
vector< vector<int> > chords { { 60, 67 , 72 },
                                { 72 , 79 , 84 },
                                { 77, 84, 89 } };

I tried this in the process block but not much happened !

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)

{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    buffer.clear();
    MidiBuffer processedMidi;
    int time;
    MidiMessage m;
    MidiBuffer midiBuffer;
        // midi 58 resets the iterator
       // midi 59 moves the iterator to the left
       // 60 and 61 repeat the chord without moving the iterator
       // 62 and up move through the sequence and if it gets to the end it resets to start 
    for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
    {
            
            if (m.isNoteOn() && m.getNoteNumber() < 58 ) // reset the array position and play first chord
            {
                chordsPosition = 0;
                // for notes in current chord
                for ( int i = 0; i < (chords[chordsPosition]).size() - 1 ; i++){
                    int NewNote = chords[chordsPosition][i];
                    m = MidiMessage::noteOn(m.getChannel(), NewNote , m.getVelocity());
                    midiBuffer.addEvent (m, time);
                }
            }
            
            else if (m.isNoteOff() && m.getNoteNumber() < 58 )
            
            {
               // send note offs for first chord in array
                // for notes in current  chord
                for ( int i = 0; i < (chords[chordsPosition]).size() - 1 ; i++){
                    int NewNote = chords[chordsPosition][i];
                    m = MidiMessage::noteOff(m.getChannel(), NewNote , m.getVelocity());
                    midiBuffer.addEvent (m, time);
                }

            }
            
            else if (m.isNoteOn() && m.getNoteNumber() == 59 )
                
            {
                // move chord pointer back one space if it is not at zero
                if (chordsPosition > 0)
                { chordsPosition -= 1 ;}
                for ( int i = 0; i < (chords[chordsPosition]).size() - 1 ; i++){
                    int NewNote = chords[chordsPosition][i];
                    m = MidiMessage::noteOn(m.getChannel(), NewNote , m.getVelocity());
                    midiBuffer.addEvent (m, time);
                    }
                
            }
            else if (m.isNoteOff() && m.getNoteNumber() == 59)
                
                {
                    // send note offs for first chord in array
                    // for notes in current  chord
                    for ( int i = 0; i < (chords[chordsPosition]).size() - 1 ; i++){
                        int NewNote = chords[chordsPosition][i];
                        m = MidiMessage::noteOff(m.getChannel(), NewNote , m.getVelocity());
                        midiBuffer.addEvent (m, time);
                    }
            }

How should I do this ?
When I was just dealing with single notes I used this

midiMessages.swapWith (processedMidi);

Can I put the those noteon() routines in a function ?
Where would I declare it ?
Can I just declare a global function(s) with that stuff in it under the variables ?
Sorry 2 questions in one and still a beginner at JUCE !
Sean


#2

I also tried to simplify things and just get each note press to play a chord …
Still can’t get any midi messages out the other side of the plugin :slight_smile:

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)

{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    MidiMessage m;
    using namespace std;
    buffer.clear();
    MidiBuffer processedMidi;
    int time;
    
    vector<int> chord { 60, 67 , 72 };
    vector<MidiMessage> midiChord;
        
    for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {
            if (m.isNoteOn())
            {
                for (int i = 0; i < 3; i++)
                {
                    int NewNote = chord[i];
                    m = MidiMessage::noteOn(m.getChannel(), NewNote , m.getVelocity());
                    midiChord[i] = m;
                }
            }
            if (m.isNoteOff())
            {
                for (int i = 0; i < 3; i++)
                {
                    int NewNote = chord[i];
                    m = MidiMessage::noteOff(m.getChannel(), NewNote , m.getVelocity());
                    midiChord[i] = m;
                }
            }
            
            for ( int i = 0; i < 3; i ++)
            {
                processedMidi.addEvent (midiChord[i], time);
            }
        }

        midiMessages.swapWith (processedMidi);

This is even simpler but still no dice …

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)

{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    MidiMessage m;
    MidiMessage n;
    using namespace std;
    buffer.clear();
    MidiBuffer processedMidi;
    int time;

    for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {
            if (m.isNoteOn())
            {
                    m = MidiMessage::noteOn(m.getChannel(), 60 , m.getVelocity());
                    n = MidiMessage::noteOn(m.getChannel(), 72 , m.getVelocity());
            }
            if (m.isNoteOff())
            {
                for (int i = 0; i < 3; i++)
                {
                    m = MidiMessage::noteOff(m.getChannel(), 60 , m.getVelocity());
                    n = MidiMessage::noteOn(m.getChannel(), 72 , m.getVelocity());
                }
            }
         processedMidi.addEvent (m, time);
         processedMidi.addEvent (n, time);
        }
        midiMessages.swapWith (processedMidi);

I don’t understand how to handle this :

i.getNextEvent (m, time)

processedMidi.addEvent (m, time);

I can see I am adding multiple events to a midi buffer at the same sample position ?

This didn’t help either !

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)

{
    auto startTime (Time::getMillisecondCounterHiRes() * 0.001);
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    MidiMessage m;
    MidiMessage n;
    using namespace std;
    buffer.clear();
    MidiBuffer processedMidi;
    int time;

    for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {
            if (m.isNoteOn())
            {
                    m = MidiMessage::noteOn(m.getChannel(), 60 , m.getVelocity());
                    m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001 - startTime);
                    n = MidiMessage::noteOn(m.getChannel(), 72 , m.getVelocity());
                    n.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001 - startTime);
            }
            if (m.isNoteOff())
            {
                for (int i = 0; i < 3; i++)
                {
                    m = MidiMessage::noteOff(m.getChannel(), 60 , m.getVelocity());
                    m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001 - startTime);
                    n = MidiMessage::noteOn(m.getChannel(), 72 , m.getVelocity());
                    n.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001 - startTime);
                }
            }
        auto timestamp = m.getTimeStamp();
        auto sampleNumber =  (int) (timestamp * 48000);
        processedMidi.addEvent (m, sampleNumber);
        timestamp = n.getTimeStamp();
        sampleNumber =  (int) (timestamp * 48000);
         processedMidi.addEvent (n, sampleNumber);
        }
        midiMessages.swapWith (processedMidi);

Tried this too !

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)

{
    
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
    {buffer.clear (i, 0, buffer.getNumSamples());}
    
    MidiBuffer processedMidi;
    int time;
    MidiMessage m;
    int chordLength = 3;
    for(MidiBuffer::Iterator i (midiMessages); i.getNextEvent(m, time);)
    {
        if(m.isNoteOn()) {
    for(int i = 0;i<chordLength;i++) {

        int interval = 60 + i*12;
        MidiMessage n = MidiMessage::noteOn(m.getChannel(), interval, m.getFloatVelocity());
        processedMidi.addEvent(n, time);
    }
        }
        else if(m.isNoteOff())
        {
            MidiMessage n = MidiMessage::allNotesOff(m.getChannel());
            processedMidi.addEvent(n, time);
        }
    }
    midiMessages.swapWith(processedMidi);
    

}

Soul lang rant #2 , midi VST3 issues
#3

Searching git this looks so similar to my original code …


Can’t see the difference!


#4

Oh it works as an .au component but not as a VST3 plugin …
Looks like the issue is a JUCE one.
I was tearing my hair out staring at my code
Sean