There seems to be a problem with how Juce deals with ALSA midi.
It seems that when midi notes are send with MidiOutput::sendMessageNow,
that the timing of the messages is not right (and in a very audible way).
I experienced this problem when using Common Music (CM), and we are discussing it here:
This drastically improved jitter. Beforehand, the jitter was measured around 7ms average and was very noticeable. After this replacement it was perfectly steady. We used the following test program:
#include <stdio.h>
#include <iostream>
#include "juce_amalgamated.h"
void test1(void)
{
using namespace std;
using namespace juce;
//Select a device.
StringArray devices = MidiOutput::getDevices();
cout << endl;
cout << "===================================================" << endl;
cout << "Current MIDI Devices" << endl;
cout << "--------------------" << endl;
for(int i = 0; i < devices.size(); i++)
cout << i << ") " << devices[i].toUTF8() << endl;
cout << "===================================================" << endl;
cout << endl << "Make a device selection: ";
int deviceIndex = 0;
cin >> deviceIndex;
cout << "Using " << devices[deviceIndex].toUTF8() << "..." << endl;
//Open the device.
MidiOutput* device = MidiOutput::openDevice(deviceIndex);
if(!device)
{
cout << "Failed to open device." << endl;
return;
}
else
cout << "Successfully opened device." << endl << "You can now connect "
<< "the JUCE Midi Output port in ALSA to its destination using JACK.";
//Set process priority.
cout << "Enter process priority" << endl;
cout << " (0 = low, 1 = normal, 2 = high, 3 = realtime): ";
int processPriority = 0;
cin >> processPriority;
if(processPriority == 3) Process::setPriority(Process::RealtimePriority);
else if(processPriority == 2) Process::setPriority(Process::HighPriority);
else if(processPriority == 1) Process::setPriority(Process::NormalPriority);
else Process::setPriority(Process::LowPriority);
//MIDI Message Test
cout << "Ready for MIDI test? [y/n]:";
char ready = 'n';
cin >> ready;
if(ready != 'n')
{
double startTime = Time::getMillisecondCounterHiRes();
double currentTime = Time::getMillisecondCounterHiRes();
double timeOfLastNote = currentTime;
bool waitingForNoteOn = true;
double secondsToStay = 10.0; //This is how long the test runs in seconds.
double msNoteDuration = 250; //Time in between notes (ms)
double msNoteArticulation = 100; //How long the note is held down (ms)
int keyNumber = 30; //Starting key number
while(currentTime - startTime < secondsToStay * 1000.0)
{
if(waitingForNoteOn && currentTime > timeOfLastNote + msNoteDuration)
{
MidiMessage m = MidiMessage::noteOn(1, keyNumber, 1.0f);
device->sendMessageNow(m);
waitingForNoteOn = false;
timeOfLastNote += msNoteDuration;
}
else if(!waitingForNoteOn &&
currentTime > timeOfLastNote + msNoteArticulation)
{
MidiMessage m = MidiMessage::noteOff(1, keyNumber);
device->sendMessageNow(m);
waitingForNoteOn = true;
keyNumber += 2;
}
currentTime = Time::getMillisecondCounterHiRes();
}
}
//Cleanup
cout << "Cleaning up..." << endl;
delete device;
//Exit
cout << "Bye!" << endl;
}
int main(void)
{
initialiseJuce_NonGUI();
test1();
}