ALSA midi problems

Hi everybody,

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:

http://ccrma-mail.stanford.edu/pipermail/cmdist/2009-August/thread.html (starting from “cm3 woes” thread)

After a few tests, it seems that Juce is the culprit.
I was hoping that someone on this list with an understanding of ALSA could help us troubleshoot…

Thanks

[/url]

sendMessageNow just calls some alsa functions - there’s nothing in there that looks capable of causing a delay…

What makes you think it’s that method that’s at fault, rather than the timing of the thread that’s calling it?

When we changed (as lieven discovered):

snd_seq_event_output_direct(seqHandle, &event);

to

snd_seq_event_output (seqHandle, &event);
snd_seq_drain_output (seqHandle);

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();
}

Ah, well that makes sense! Ok, well if no-one has any objections, I guess I’ll take your advice there - thanks!