MidiOutput + TiMidity = Silence?

Does anyone know how to get Juce’s MidiOutput class to work with TiMidity? I am writing a cross-platform “virtual piano” program, but it’s not going to be much use to Linux users unless it will work with a software synth!

Currently I have a ComboBox populated with the devices obtained from MidiOutput::getDevices() which is fine, I can select from “Midi Through”, “OSS Sequencer” and the 4 “TiMidity (x)” virtual devices.

The ComboBoxListener is set up to close any current device and open the new device when it is selected:

class MidiDevicesListener : public ComboBoxListener
{
public:
	MidiDevicesListener()
	{

	}

	~MidiDevicesListener()
	{
	}

	void comboBoxChanged(ComboBox* comboBox)
	{
		delete outputDevice;
		outputDevice = MidiOutput::openDevice(comboBox->getSelectedItemIndex());		
		outputDevice->reset();		
		outputDevice->setVolume(1.0, 1.0);
	}
};

This IS opening the device because I used an AlertWindow to see if it was returning a NULL pointer, which it is not.

I have a MidiKeyboardStateListener set up like this:

class KeyStateListener : public MidiKeyboardStateListener
{
public:

	KeyStateListener()
	{

	}
	
	~KeyStateListener()
	{

	}	

	void handleNoteOn(MidiKeyboardState* keyState, int midiChannel, int midiNoteNumber, float velocity)
	{
		if (outputDevice) {		
			outputDevice->sendMessageNow(MidiMessage::noteOn(midiChannel, midiNoteNumber, velocity));
		}
	}

	void handleNoteOff(MidiKeyboardState* keyState, int midiChannel, int midiNoteNumber)	
	{
		if (outputDevice) {
			outputDevice->sendMessageNow(MidiMessage::noteOff(midiChannel, midiNoteNumber));
		}
	}
};

Again, I tested this with AlertWindows inside the if statements, and they appeared so the TiMidity device is definately getting opened.

But it makes about as much noise as a… thing that doesn’t make noise!

And the thing is, a program I have called “NoteEdit” shows a virtual MIDI device, and my program can connect to that and it successfully records the notes, and even loops them through to the TiMidity device so I can hear them!

So I am obviously missing some kind of code to do with volume or whatever. Does anyone know the solution to this strange problem?

I am even ensuring the MidiKeyboardComponent is sending “audible” notes by maxing out it’s velocity:

addAndMakeVisible(keyComponent = new PCAnoKeyboard(keyState));
keyComponent->setVelocity(1.0);

(PCAnoKeyboard is just a subclass of MidiKeyboardComponent that “skins” itself in it’s constructor, nothing wrong with that :stuck_out_tongue: )

As a VB6 programmer moving to C++ after about a year of translating C++ code from MSDN into VB6 code, I’m loving JUCE so far, I doubt I’ll ever be going Back to (Visual) Basic(s) again!

EDIT: I just tried this with my physical keyboard using a USB-MIDI cable, and the “output” light on that is not even blinking as I click notes. So that’s just making things even more confusing - How come the only thing that “sees” what the program is sending is NoteEdit’s virtual device?

Also further confirmation that it is definately opening the device - If it really, really can’t open it (for example beacuse I try to select the USB Midi after unplugging it), as expected from that code it crashes with a null pointer exception - Devices that exist and aren’t busy don’t crash it so they’re being opened just fine.

Have you tried directly firing MidiMessages at the device, like a set volume, change program, etc?

Hey Jules!

No I haven’t tried sending these messages, but even if I did my USB MIDI cable tells me they probably wouldn’t work because the green light flashes whenever it receives data from the PC, so even “silent” messages would cause that to flash.

And again it’s not a problem with the MIDI cable because NoteEdit quite happily sends messages for the metronome and I hear them on my keyboard, complete with flashing green light.

So basically, JUCE is opening and closing devices properly, but is not actually sending the messages out to the opened device.

Also, even a Windows program using WINE can produce sound on these devices! So it’s definitely something to do with JUCE or my code.

Am I doing something ridiculously-obviously wrong in my code??

But I thought you said it was correctly sending data to the NoteEdit app? I can’t think of any reason it’d work for one midi device but fail with another - as far as the code is concerned, they’re all the same…

I guess you could try debugging it as it actually does the sendMessageNow call to make sure the messages are getting sent?

Well it WAS sending data, now it suddenly doesn’t work. Perhaps the NoteEdit app was responding to key presses itself.

I have absolutely no idea how to debug - It’s not like I can set breakpoints or anything in a text editor (gedit). I DO have gdb, and I think I know how to compile a debug version, but I haven’t the faintest idea what to do with it…

Has anyone else managed to use MidiOutput on Linux since Thursday? That’s when I checked out the latest revision from SVN. I had a quick look in the source and nothing there is obviously wrong, but like I said I don’t know much about the Linux API for MIDI.

EDIT:

I’ve tried using GDB and I managed to step through the code to some extent… Now unless some symbols are missing (which I doubt because it would have shown an error), it seems not to be calling the internal function at all, rather skipping straight to the ending “}”. I’ve no idea why this is and now I have a headache! Someone please help ASAP…

OK I really really don’t get this.

I’ve traced the code with GDB all the way down to the line that actually outputs the message to the sequencer (snd_seq_event_output_direct), and I read the documentation for ALSA to find what the function should be returning, then I recompiled using an AlertWindow to display the return value of this function call.

The documentation states a negative number is an error, a positive number is the number of bytes successfully sent to the sequencer. The function is returning 28, which means 28 bytes were successfully sent to the sequencer.

So it seems JUCE is getting the messages out to the sequencer, but they are not proceeding any further than this.

Is there any other function I have to call, e.g. to flush buffers or something along those lines?

Here is some output from GDB that might be helpful:

Breakpoint 1, juce::MidiOutputDevice::sendMessageNow (this=0x8846090, 
    message=@0xbfe34efc) at ../../../juce_amalgamated.cpp:262029
262029		snd_seq_event_output_direct (seqHandle, &event);
(gdb) print seqHandle
$1 = (snd_seq_t * const) 0x884a2c8
(gdb) print event
$2 = {type = 6 '\006', flags = 0 '\0', tag = 0 '\0', queue = 253 '�', time = {
    tick = 0, time = {tv_sec = 0, tv_nsec = 0}}, source = {client = 0 '\0', 
    port = 0 '\0'}, dest = {client = 254 '�', port = 253 '�'}, data = {note = {
      channel = 0 '\0', note = 77 'M', velocity = 127 '\177', 
      off_velocity = 0 '\0', duration = 0}, control = {channel = 0 '\0', 
      unused = "M\177", param = 0, value = 0}, raw8 = {
      d = "\000M\177\000\000\000\000\000\000\000\000"}, raw32 = {d = {8342784, 
        0, 0}}, ext = {len = 8342784, ptr = 0x0}, queue = {queue = 0 '\0', 
      unused = "M\177", param = {value = 0, time = {tick = 0, time = {
            tv_sec = 0, tv_nsec = 0}}, position = 0, skew = {value = 0, 
          base = 0}, d32 = {0, 0}, d8 = "\000\000\000\000\000\000\000"}}, 
    time = {tick = 8342784, time = {tv_sec = 8342784, tv_nsec = 0}}, addr = {
      client = 0 '\0', port = 77 'M'}, connect = {sender = {client = 0 '\0', 
        port = 77 'M'}, dest = {client = 127 '\177', port = 0 '\0'}}, 
    result = {event = 8342784, result = 0}}}

EDIT: Oh! This piece of code partially solves it - It is sending to client “127:0” which is NOT the device I selected, it should be sending to “128:0”

There isn’t even any device at “127:0” so how on earth is this happening?
Have I found a bug in JUCE?

The client number could just be a difference in numbering - i.e. the device’s description might number it from 1, but the debug info might just dump it out counting from 0.

Looking at the code, I can’t see any obvious way it could open the wrong device, but you could make certain by adding some printfs at line 109 to print the name of the device it actually opens.

If it’s opening it and sending data, but the data’s not going anywhere, then I’m afraid I’ve no idea what to suggest!

I’m using the amalgamated file, so what line number would I need to add the printf()s to?

I’ve got a feeling it’s that client number thing because that’s the only thing that looks dodgy, everything else seems file!

Well, just find the section in iterateDevices() where it checks the deviceIndex - it should be pretty obvious what’s going on. I just can’t see anywhere that the client number could go wrong though…