I am using the default MidiOutput background thread and I realize it cannot not be sublcassed.
In my previous wxWidgets program all times were stored/saved using a tempo of 60 bpm with a quarter equaling 1000 ms. MIDI was sent using a process similar to the JUCE sendMessageNow function. Essentially the program used timers to fire the next MIDI message at a future time and responded to user interface changes in between.. In that system I had no problem getting updates from the tempo slider and adjusting MIDI time stamps on the fly. When I've tried using the MidiOutput:;sendMessageNow() and th Time::waitForMillisecondCounter() functions the slider is unresponsive. MIDI plays but I get the spinning beach ball while the MIDI plays and I cannot adjust the slider until it stops. This code works with sendBlockOfMessages and the GUI is responsive but the GUI cannot send to the private MidiOutput background thread.
void MidiUtils::TxAll()
{
const bool LINE_BY_LINE = true;
if ( LINE_BY_LINE )
TxLineByLineMs();
else
{
midiout->stopBackgroundThread();
midiout->startBackgroundThread();
MidiBuffer::Iterator MI(mbuf);
MI.setNextSamplePosition(0);
// MidiMessage msg;
double timeNow = (double)Time::getMillisecondCounter();
const double samplesPerSecondForBuffer = 44100.0;
midiout->sendBlockOfMessages( mbuf, timeNow, samplesPerSecondForBuffer );
}
}
This code also works if I set LINE_BY_LINE = false above but I get the spinning beach ball with no GUI interaction. This is the TxLineByLine code using sendMessageNow and waitForMillisecondCounter.
void MidiUtils::TxLineByLineMs()
{
// uses difference Ms time
std::vector<MidiPacket> mpv = createDiffMsVector();
const bool PRINT_DIFF_MS_VECTOR = false;
if (PRINT_DIFF_MS_VECTOR) {
for ( auto iv : mpv)
printMidiPacket( iv );
}
midiout->stopBackgroundThread();
midiout->startBackgroundThread();
int nowTime;
int pktTime;
for ( auto iter : mpv )
{
nowTime = Time::getMillisecondCounter();
pktTime = static_cast<int>( ( (iter.timeStamp * 60.0) / global::tempo ) );
Time::waitForMillisecondCounter( nowTime + pktTime );
midiout->sendMessageNow( MidiPacketToMidiMessage( iter ) );
}
}
The MidiPackts vector items are structured like this:
JUCE v3.2.0
991 c0 32 0
0 90 28 114
9 99 40 90
41 c1 0 0
0 91 28 74
34 c7 105 0
0 97 59 66
25 97 67 74
233 97 67 0
The columns are respectively: timeStamps in ms, status, data1, data2 (data2 is ignored for the 0xCn program change messages). The routine gets the time stamp from the first column and waits that amount of time before sending the next three bytes as a MIDI message. The code works, MIDI plays correctly, but the cursor becomes the spinning beach ball and the slider is unresponsive so the tempo never gets updated.
The JUCE code for Time::waitForMillisecondCounter() does not seem to be yielding to GUI interaction.
void Time::waitForMillisecondCounter (const uint32 targetTime) noexcept
{
for (;;)
{
const uint32 now = getMillisecondCounter();
if (now >= targetTime)
break;
const int toWait = (int) (targetTime - now);
if (toWait > 2)
{
-> Thread::sleep (jmin (20, toWait >> 1));
}
else
{
// xxx should consider using mutex_pause on the mac as it apparently
// makes it seem less like a spinlock and avoids lowering the thread pri.
for (int i = 10; --i >= 0;)
-> Thread::yield();
}
}
}
I've tried experiments changing numbers in both lines with the -> with no luck.
I'm sure this problem has been solved by others using the JUCE framework but I'm not seeing an obvious solution at this point. Any ideas?