Sending midi (clock) from standalone app?


#1

Hello all,

I have a standalone app which sends midi clock, but I’m not managing at all to get it working.

I was looking at the MidiBuffer, and I’m filling a midi buffer like I’m supposed to, but I have no idea when to send the midi buffer to the midi output. As the sendBlockOfMessages of the MidiOutput creates new objects ( new PendingMessage (data, len, eventTime) ) I suppose you’re not supposed to use it inside the audio thread. However, how do I now work around this while still keeping stable timing?? And, even though I tried about 10 different ways, I can get my message to be sent to the output. I did this:

  void SomeClass::audioDeviceIOCallback(const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples)
  {
     for (...)
     {
        midiBuffer.addEvent(MidiEvent(...), positionInSamples); // can positionInSamples be larger than the size of the current block??
     }
     ...
     midiOutput->sendBlockOfMessages(midiBuffer, (numSamples*1000.0)/m_SampleRate, m_SampleRate);
     // delay one block of samples?? how much should I delay?
     // this allocates memory... ouch

     midiBuffer.clear()
  }

Other than that there seem to be quite a few timing questions in my head, like the millisecondCounterToStartAt in sendBlockOfMessages. As audio buffers aren’t really (wall-clock) timestamped in Juce the only way to figure out anything about the wall clock is to do a getTime( ) at the start of the block, but this seems a bit… weird to me.

In general any more pointers towards getting a (stable) midi output clock running would be greatly appreciated!

cheers,

  • bram

#2

Best way is with a high-priority thread. Have a look at the way the MidiOutput::sendBlockOfMessages method works internally, as that’s a good example to follow.


#3

Hi!

I, more or less, want to do the same thing. (Record 16 channels of audio while sending MTC to an external MIDI device).

Have you made any progress – any insight to share?


#4

Been busy with other things meanwhile, so… nope…

Likewise: if you end up doing something, let me know!

  • Bram

#5

Yeah, I will. However, I am only interested in syncing the transport display of my mixer with the recording, so perfect timing isn’t critical for my purpose. So I guess I might end up with something not good enough for your purpose.


#6

I have been testing the following:

  • Set up a thread which wakes up every time a new MTC quarter frame is due to be sent (every 1000/(4*fps) ms). Send a prepared MIDI message. Then prepare the next MIDI message. (This is done using sleep now, but a timer would be better I guess).
  • The thread above grabs the current time from a global SMPTE counter, which internally has an AbstractFifo. The audioDeviceIOCallback (in my recorder class) writes it’s incoming numSamples argument to this fifo every time it runs. The MIDI-thread reads the fifo and converts the incoming sample counts to number of frames to increment the SMPTE counter with every time a whole set of MTC quarter frames have been sent.

The concept seems to work, and the transport of Reaper follows my app, but the difference between my SMPTE time and wall clock time is too big, so something needs to be fixed. Let me know what you think about this concept!


#7

Is the allocation of new pending messages in Midioutput really an issue?
I mean, adding Midi events to the MidiBuffer inside the processblock could also
cause memory allocation if the buffer size is to small, no?

Joerg


#8

That’s why I use a AbstractFifo with fixed pre-allocated data. In the case of midi clock, you know how many messages to expect until the buffer is cleared, so allocation shouldn’t be a problem.

I use this kind of code now in my midi-clock thread:

void MTCEmitter::run()
{
    while (! threadShouldExit())
    {
        // Interval is a double with time in ms between MTC packages. E.g. 8.333333... ms @ 30 fps
        const int timeToWait = roundToInt(interval + delta);
        // Store the rounding error and take that into account the next time this (a bit like nosie shaping)
        delta = timeToWait - interval;
        wait (timeToWait);
        
        // Send prepared message
        midiOutput->sendMessageNow(nextMidiMsg);

        // Post message to SMPTE-display component
        postDisplayUpdate();

        // Prepare next Midi-clock message
        prepareNextMessage(false);
    }
}

This seems to work fine, and Reaper’s transport follows perfectly.


#9

Could you share the whole class for the midi clock? I’d love to see how you did that.


#10

Sure, here it is. Keep in mind that this is work in progress, and not everything is tested or even remotely finished. I would love to have some feedback on these ideas too!

MTCEmitter.cpp
http://www.box.com/s/9ce309863735240d86d5

MTCEmitter.h
http://www.box.com/s/209708e2c5a49a6929d0

SMPTE.h
http://www.box.com/s/329df6fd082e8e88ace6


How to output MIDI Timecode (MTC)
#11

Hi again.

Some updates. Even though this seemed to work when testing locally on my computer with Reaper, it didn’t work that good when I tried it with an external MIDI device. This is unfortunate, as my main goal is to make a simple multitrack recorder which can work together with my digital mixer (Soundcraft Spirit 328). The SMPTE display on the mixer follows the transmitter MIDI-clock, but it is not smooth and the LEDs indicating play and stop flickers. I think there is too much jitter in the generated clock and that the mixer has minimal buffers and can’t tolerate this.

So how can I improve the accuracy of the timing of the transmitted packages? Any ideas? Is this even possible with threads and wait()?

I have a feeling that I should use some other kind of OS timer, but I don’t know anything about those, so any suggestions are welcome.

Another idea is to try to improve the “noise shaping” algorithm (see comments in my code above) by polling some high-resolution time function to figure out when the message was sent, and subtract the timing error from the next message.