Search code examples
c++multithreadingcpu-usagemidijuce

Lowering CPU on MIDI playback thread


The function below is run on an std::thread that is detached. The code itself is written using the JUCE API (Hence Array<> object & MidiMessage).

 void triggerMidiMessages(Array<Array<MidiMessage>> messageBundle) 
{
    //for each group of messages (bar) within the bundle
    for(int bar = 0; bar < messageBundle.size(); bar ++)
    {   
        //store our message from the bundle for playback
        Array<MidiMessage> messages;
        messages.clear();
        messages = messageBundle[bar];

        //intialise start time
        double timestart = Time::getMillisecondCounterHiRes();

        //for each midi inside a single "gene"
        for(int i = 0; i <= messages.size();)
        {
            double elapsedTime = Time::getMillisecondCounterHiRes() - timestart;

            //output message whens appropriate
            if(elapsedTime > messages[i].getTimeStamp())
            {
                //output this message
                masterMidiOutput->sendMessageNow(messages[i]);

                //increment through the array
                i++;
            }
        }
    }
}

I need the midi messages to be output in real time but without having to run through the loop condition so much that the CPU runs super hot.

Any ideas? I'm stuck for how to playback the messages in such an order that doesn't require constant checking with a timer.

Thanks in advance.

//===================================================================== update trying to sleep the thread...

void triggerMidiMessages(Array<Array<MidiMessage>> messageBundle)
{
    //for each group of messages (bar) within a bundle
    for(int bar = 0; bar < messageBundle.size(); bar ++)
    {   
        //store our message from the bundle for playback
        Array<MidiMessage> messages;
        messages.clear();
        messages = messageBundle[bar];
        }

        //intialise start time
        double previousTimeStamp = 0;

        //for each midi inside a single "gene"
        for(int i = 0; i <= messages.size();)
        {
            //fire off all note on messages
            while(messages[i].isNoteOn())
            {
                masterMidiOutput->sendMessageNow(messages[i]);
                i++; //increment to the next
            }

            //fire off all note off messages
            while(!messages[i].isNoteOn())
            {
                masterMidiOutput->sendMessageNow(messages[i]);
                 i++; // do the next one

                //if the next message is back to a note on command
                if(messages[i+1].isNoteOn() == true)
                {
                    //sleep for x amount of time
                    int sleepTime = messages[i].getTimeStamp() - previousTimeStamp;
                    std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
                    previousTimeStamp = messages[i].getTimeStamp();
                }
            }

        }
    }
}

Solution

  • To stop the build up within the thread it is better to turn on and off a timer object and trigger each message one by one.

    Broadcast messages (Action Broadcaster )can then be used to keep track of the index.

    Here is some example code to give the general idea :

    MIDIThing::MIDIThing ()
    {
        startTimer(1); //start a timer
    }
    
    void MIDIThing::start ()
    {
        playstate = 1;
        startTime = Time::getCurrentTime().toMilliseconds();
    }
    
    void MIDIThing::timerCallback()
    {
        if (playstate == 1) {
            Time::getMillisecondCounterHiRes();
            int64 target;
            if (Time::getCurrentTime().toMilliseconds() > target) {
                //fire off the message
            }
    
            //ended (possibly on a condition)
            ActionBroadcaster::sendActionMessage(FINISHED_PLAYBACK);
        }
    }