Midi generator creation


#1

Hello all,

I am new in Juce and my english is not really good. I'll do my best to be clear.

I try to write a Midi generator using juce.

In my AudioProcessor constructor, I create a thread and I open a midioutput:

midioutput = MidiOutput::openDevice(MidiOutput::getDefaultDeviceIndex());

    timerThread = new TimerThread("Midi Player", this);

    timerThread->startThread();

 

In this thread, for test, I make a loop:

    MidiMessage instance;

    MidiMessage myNote = instance.noteOn((int)1, (int)36, (float)127);

    MidiMessage myNoteOff = instance.noteOff((int)1, (int)36, (float)127);    

    while (1)

{

   out->sendMessageNow(myNote);

    out->sendMessageNow(myNoteOff);

    usleep(500);

}

So I open ableton live, create an instance of my plugin, and I hear a every half second (usleep(500)) a piano note.

I do not know where it comes from, but not from ableton. Its like its was going out of a defaulft midi synth on my OS (windows7)

I did some test writing midimessages in ProcessBlock's MidiBuffer, and then the midi come out of my plugin and goes to ableton, so its ok.

But when I write midimessages on the midioutput, I dont know how to stream the  output in ableton live.

 

So my question are:
-Am I on the good way to write a midigenerator (thread, ...) ?

-How can I have my midioutput like if I was writting on the ProcessBlock's midiBuffer ?

 

I hope my questions means something, and my english can be understood.

 

Thank you very much,

 

Mat

 

 


#2

Howdy

I was dealing with this a little bit. Basically, it is a bit more complicated to send midi OUTPUT from a PLUGIN, and usually depends on the host how this is handled.

In Ableton, you need to route the midi output of the plugin back into another track.

Load a VST plug-in into a MIDI track.
Create another MIDI track.
In the Input Type chooser (“MIDI From”) of the created track, select the track containing the VST plug-in.
In the (lower) Input Channel chooser, select the VST plug-in.

(from https://www.ableton.com/en/articles/vst-plugin-midi-output/)

I think you are on the right path - good work so far.


#3

Hi, thanks for your answer.

That is what I am used to do but it does not work anymore when I play my note on the MidiOutput .
To test I wrote this code in the ProcessBlock function:

    MidiMessage myNote = myNote.noteOn((int)1, (int)36, (float)64);
    MidiMessage myNoteOff = myNoteOff.noteOff((int)1, (int)36, (float)64);
So my thread is playing on the MidiOutput I opened with : midioutput = MidiOutput::openDevice(MidiOutput::getDefaultDeviceIndex()); , and I push events on midibuffer of processBlock.

Only the notes pushed in processblock come out from the plugin (I think it is normal), and I can stream them into instruments or whatever.

But the notes played by the thread does not appear in any output of the plugin. And I can hear them. its a piano but the sound does not come from ableton. So I think the plugin play on a OS midichannel or something like that and the default instrument is a piano.

So maybe I do not open the good Device for my MidiOutput.

Thank you


#4

Try it without opening the midioutput - ableton should already have it open for you.

 

But yes...it looks like you are opening the default midi port of your computer and sending it data, when you should just be adding messages to the buffer in processBlock.


#5

How to initialize my output if I dont get the pointer from openDevice function ? I dont see anything in the class functions

Thanks


#6

Do not use openDevice - Ableton and the plugin have already done that for you and are just waiting for messages to show up in midiMessages in processBlock.

 

Just do...

midiMessages.addEvent(MyNoteOn, myTime1);
midiMessages.addEvent(MyNoteOff, myTime2);

...in processBlock

 

edit: you will need to calculate the time and insert appropriately, but the point is, you do not need to open the mididevice.


#7

Using : createNewDevice ( getDevices ()[0] ) ?
Its written "not available on windows".

thanks


#8

Do not do anything with the midi device - read my replies, slow down, you got this ;]


#9

Ok I think I misunderstood somethings, maybe its super easy but I dont get it, sry. I did a thread, because I wanted to run a timer and send the note at the right time.

And I understood that processBlock is called every idk milliseconds by the host (maybe i am totaly wrong ?), so

How am I suposed to do a timer in ProcessBlock ? (I think I cant)

Or how am I supposed to communicate from my thread to processBlock to tell it its the good time to addEvent() ?

Thanks


#10

The processBlock is sort of a timer callback in itself, so you shouldn't really need an additional timer.


static double playTime;

void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
   int numSamples = buffer.getNumSamples();
   double numSecondsInThisBlock = numSamples / yourSampleFrequence;

/*
you might have a MidiMessageSequence here or a MidiBuffer holding the sequence of midimessages you want to play, each with a proper timeStamp. This sequence might have been read from a midi file (then you'd probably use MidiMessageSequence) or created on the fly (using MidiBuffer to add those events you want to play)

You iterate through this midisequence (or midibuffer) till you find the next midievent with a timeStamp within this processBlock i.e.

timeStampOfMidiEvent >= playTime && timeStamp < playTime + numSecondsInThisBlock

then you add this/those midievent to the MidiBuffer of the processBlock
*/

midiMessages.addEvent(midiMessage, timeStampOfMidiEvent - playTime);

/*
You repeat this until you don't have any more events for this processBlock. Then you wait for the next invocation (where playTime have incremented by the amount below) and continues the iteration of your Midibuffer or MidimessageSequence from the previous point
*/

   playTime += numSecondsInThisBlock;
}

Hope this will get you going!


#11

Yes the processblock is itself a timer.

 

I find it helps to keep another buffer of midi for your own processes, then just add those into midiMessages in the processblock.

I think this way you could still run your own timer, but I'm not sure if that is the best way.

 

But yes, it is overcomplicating to try to open midi ports - remember, the host (ableton) has already done that for you :].

 

Keep us posted.


#12

Thanks you for your replies guys, Ill try to understand how to do all thoses things in processBlock and use time well.


#13

Hi all,

So here is my code in the processblock function:

 

    int numSamples = buffer.getNumSamples();

    double numSecondsInThisBlock = numSamples / 44100;

    if (UserParams[generate] == 1.0)

    {

        playTime = hostInfo.timeInSeconds;

        play = true;

        UserParams[generate] = 0;

    }

    if (play)

    {

        int i;

        MidiMessage *tempMessage;

        const MidiMessageSequence *seq;

 

        seq = myMidiFile->getTrack(0);

        for(i = 0; i < seq->getNumEvents(); i++)

        {

            tempMessage = &(seq->getEventPointer(i)->message);

            if (tempMessage->getTimeStamp() >= playTime && tempMessage->getTimeStamp() < playTime + numSecondsInThisBlock)

                midiMessages.addEvent(*tempMessage, tempMessage->getTimeStamp() - playTime);

        }

    }

    playTime += numSecondsInThisBlock;

 

 

So its pretty working, but I dont know how to set the playTime variable. 

I think Its close to work, but I need to know how to set this playTime.

 

 

Does anyone know How I have to set it ?

 

Thanks

Mat


#14

For this condition :

if (tempMessage->getTimeStamp() >= playTime && tempMessage->getTimeStamp() < playTime + numSecondsInThisBlock), 

I used oxxyyd post,

timeStampOfMidiEvent >= playTime && timeStamp < playTime + numSecondsInThisBlock
 

 but I think I dont use it correclty.
Anyone could help me ?

Thanks


#15

The midiMessageSequence is sorted acc to the timestamps of its midi events so it's unneccassary to iterate through the complete sequence for every processblock call.

You could save the current midiSequence position in a member variable like nextEventID that you initalize to 0 or to
nextEvent ID = midiMessageSequence.getNextIndexAtTime (timeInMidiYouWantToStartAt), prior to any processBlock call.

 

In the processblock the code then could look something like

processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
   int numSamples = buffer.getNumSamples();   
   double numSecondsInThisBlock = numSamples / yourSampleFrequence;
   double endTime = playTime + numSecondsInThisBlock;

   MidiMessageSequence::MidiEventHolder *m;

   for (; m = midiSequence.getEventPointer (nextEventID); nextEventID++)
   {
      double timeStamp = m->getTimeStamp();
      if (timeStamp => endTime)
        break;   //we're done for now. leave remaining midi events to process calls to come!

      if (timeStamp => playTime)
         midiMessages.addEvent(midiMessage, timeStamp - playTime); //this event is within our time slot. 
   }


   playTime = endTime;
}

#16

Hello again, thanks for your help. 
So here is my code now:

static double playTime;

void StereoWidthCtrlAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)

{

    static bool play = false;

    int numSamples = buffer.getNumSamples();

    double numSecondsInThisBlock = numSamples / 44100;

    double endTime = playTime + numSecondsInThisBlock;

    static int nextEventID = 0;


    if (UserParams[generate] == 1.0)

    {

        play = true;

        UserParams[generate] = 0;

    }

    if (play)

    {

        const MidiMessageSequence *midiSequence;

        MidiMessageSequence::MidiEventHolder *m;

        midiSequence = myMidiFile->getTrack(0);

        for (; m = midiSequence->getEventPointer(nextEventID); nextEventID++)

        {

            double timeStamp = m->message.getTimeStamp();

            if (timeStamp >= endTime)

                break;

            if (timeStamp >= playTime)

                midiMessages.addEvent(m->message, timeStamp - playTime);

        }

         playTime = endTime;

    }

}

I understant why I do not have to iterate on all midievents, but,  I still do not know how to set the playTime variable.

Is it the time when I clic my button to lauch the midifile ? If it is that, I tried to playTime = hostinfo.timeInSeconds, and it does not work (And I think its normal).

 

Thank you very much


#17

Maybe you can have a look at a plugin i wrote some time ago for a friend. It hosts other plugins and changes their state/morphs the programs fluently. I did some time calculations there to get the right timitng for those events. Might be helpful.

The project is located at: http://sourceforge.net/projects/morf-wrapper/?source=directory

I also deal with MIDI a lot in Ctrlr http://ctrlr.org some of the code there might be helpful too.


#18

Hello Atom,

Thanks very much for sharing your code, but it is too complicated for me, I can not understand what you wrote :s

I will try again after my class, but I really think I am too new in this to understand.

I think I can do it with someting more simple. I just want to know how to drop a midi file play at a special moment (When I clic a button by exemple)

 

Thanks


#19

This should be simple.

In your editor class, when a button is clicked, lock the audio thread, set a boolean value in the audio thread to "TRUE" and in the processBlock() check if that value is TRUE, if it is start playing (adding) MIDI events from the MIDI file to the MidiBuffer provided in the processBlock parameter.

I think it might be possible to do that without locking using the Atomic class and just set that value 0/2 and check it in the audio thread.

Anyway, most of people here will tell you that locking the audio thread in the GUI thread is very bad and might cause clicks and dropouts and all sort of weird behavior, so make sure your MIDI file is prepared to play so you do minimal operations during the audio thread lock.


#20

Hello Atom,

Thanks very much for sharing your code, but it is too complicated for me, I can not understand what you wrote :s

I will try again after my class, but I really think I am too new in this to understand.

I think I can do it with someting more simple. I just want to know how to drop a midi file play at a special moment (When I clic a button by exemple)

 

Thanks