midi tune select problem

mark63

Active member
i use one of the midi samples as a basis for a program.
it creates virtual cables and routes it to serial ports.
so all received midi via usb is fetched like this :
if (usbMIDI.read()) {
byte type = usbMIDI.getType();
byte channel = usbMIDI.getChannel();
byte data1 = usbMIDI.getData1();
byte data2 = usbMIDI.getData2();
byte cable = usbMIDI.getCable();
and then there is the check for system exclusive and the data is routed to a serial port.

this works correct for normal midi and sysex data.
but when i send a tune select (0xf6) i do get odd data. depending which tool i used for sending this 1 byte midi command.
a tool like sendmidi from https://github.com/gbevin/SendMIDI will give odd data which change too.

so the question is, is the sample midi4x4 supposed to support these 1 byte midi commands too?
 
i use the teensy4.1
this is the example that came with it

/* Create a "class compliant " USB to 6 MIDI IN and 6 MIDI OUT interface,
plus 10 more USB connected devices. Admittedly, you could just plug
those 10 devices directly into your computer, but this example is meant
to show how to forward any MIDI message between the 3 different MIDI
libraries. A "real" application might do something more interesting,
like translate or modify the MIDI messages....

MIDI receive (6N138 optocoupler) input circuit and series resistor
outputs need to be connected to Serial1-Serial6. A USB host cable
is needed on Teensy 3.6's second USB port, and obviously USB hubs
are needed to connect up to 10 USB MIDI devices. That's a *LOT* of
extra hardware to connect to a Teensy!

You must select MIDIx16 from the "Tools > USB Type" menu

This example code is in the public domain.
*/

#include <MIDI.h> // access to serial (5 pin DIN) MIDI
#include <USBHost_t36.h> // access to USB MIDI devices (plugged into 2nd USB port)

// Create the Serial MIDI ports
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI2);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, MIDI3);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial4, MIDI4);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial5, MIDI5);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial6, MIDI6);
//midi::MidiInterface &SerialMidiList[6] = {MIDI1, MIDI2, MIDI3, MIDI4, MIDI5, MIDI6};

// Create the ports for USB devices plugged into Teensy's 2nd USB port (via hubs)
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);
MIDIDevice midi01(myusb);
MIDIDevice midi02(myusb);
MIDIDevice midi03(myusb);
MIDIDevice midi04(myusb);
MIDIDevice midi05(myusb);
MIDIDevice midi06(myusb);
MIDIDevice midi07(myusb);
MIDIDevice midi08(myusb);
MIDIDevice midi09(myusb);
MIDIDevice midi10(myusb);
MIDIDevice * midilist[10] = {
&midi01, &midi02, &midi03, &midi04, &midi05, &midi06, &midi07, &midi08, &midi09, &midi10
};

// A variable to know how long the LED has been turned on
elapsedMillis ledOnMillis;

USBDriver *drivers[] = {&hub1, &hub2, &hub3, &hub4, &midi01, &midi02, &midi03, &midi04, &midi05, &midi06, &midi07, &midi08, &midi09, &midi10};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "Hub3" , "Hub4", "MIDI1", "MIDI2", "MIDI3" , "MIDI4", "MIDI5", "MIDI6", "MIDI7", "MIDI8", "MIDI9", "MIDI10"};
bool driver_active[CNT_DEVICES] = {false, false, false, false,false,false,false,false,false,false , false,false,false,false};


void setup() {
Serial.begin(115200);
pinMode(13, OUTPUT); // LED pin
digitalWrite(13, LOW);
MIDI1.begin(MIDI_CHANNEL_OMNI);
MIDI2.begin(MIDI_CHANNEL_OMNI);
MIDI3.begin(MIDI_CHANNEL_OMNI);
MIDI4.begin(MIDI_CHANNEL_OMNI);
MIDI5.begin(MIDI_CHANNEL_OMNI);
MIDI6.begin(MIDI_CHANNEL_OMNI);
// Wait 1.5 seconds before turning on USB Host. If connected USB devices
// use too much power, Teensy at least completes USB enumeration, which
// makes isolating the power issue easier.
delay(1500);
Serial.println("Interface_16x16 Example");
delay(10);
myusb.begin();
}


void loop() {
bool activity = false;

// First read messages from the 6 Serial MIDI IN ports
if (MIDI1.read()) {
sendToComputer(MIDI1.getType(), MIDI1.getData1(), MIDI1.getData2(), MIDI1.getChannel(), MIDI1.getSysExArray(), 0);
activity = true;
}
if (MIDI2.read()) {
sendToComputer(MIDI2.getType(), MIDI2.getData1(), MIDI2.getData2(), MIDI2.getChannel(), MIDI2.getSysExArray(), 1);
activity = true;
}
if (MIDI3.read()) {
sendToComputer(MIDI3.getType(), MIDI3.getData1(), MIDI3.getData2(), MIDI3.getChannel(), MIDI3.getSysExArray(), 2);
activity = true;
}
if (MIDI4.read()) {
sendToComputer(MIDI4.getType(), MIDI4.getData1(), MIDI4.getData2(), MIDI4.getChannel(), MIDI4.getSysExArray(), 3);
activity = true;
}
if (MIDI5.read()) {
sendToComputer(MIDI5.getType(), MIDI5.getData1(), MIDI5.getData2(), MIDI5.getChannel(), MIDI5.getSysExArray(), 4);
activity = true;
}
if (MIDI6.read()) {
sendToComputer(MIDI6.getType(), MIDI6.getData1(), MIDI6.getData2(), MIDI6.getChannel(), MIDI6.getSysExArray(), 5);
activity = true;
}

// Next read messages arriving from the (up to) 10 USB devices plugged into the USB Host port

for (int port=0; port < 10; port++) {
if (midilist[port]->read()) {
uint8_t type = midilist[port]->getType();
uint8_t data1 = midilist[port]->getData1();
uint8_t data2 = midilist[port]->getData2();
uint8_t channel = midilist[port]->getChannel();
const uint8_t *sys = midilist[port]->getSysExArray();
sendToComputer(type, data1, data2, channel, sys, 6 + port);
activity = true;
}
}

// Finally, read any messages the PC sends to Teensy, and forward them
// to either Serial MIDI or to USB devices on the USB host port.
if (usbMIDI.read()) {
// get the USB MIDI message, defined by these 5 numbers (except SysEX)
byte type = usbMIDI.getType();
byte channel = usbMIDI.getChannel();
byte data1 = usbMIDI.getData1();
byte data2 = usbMIDI.getData2();
byte cable = usbMIDI.getCable();

Serial.printf("cable % d, chan % d , % d , % d \r\n" ,cable,channel,data1,data2);

// midilist[0]->send(type, data1, data2, channel); //to edirol?

// forward this message to 1 of the 3 Serial MIDI OUT ports
if (type != usbMIDI.SystemExclusive) {
// Normal messages, first we must convert usbMIDI's type (an ordinary
// byte) to the MIDI library's special MidiType.
midi::MidiType mtype = (midi::MidiType)type;

// Then simply give the data to the MIDI library send()
switch (cable) {
case 0: MIDI1.send(mtype, data1, data2, channel); break;
case 1: MIDI2.send(mtype, data1, data2, channel); break;
case 2: MIDI3.send(mtype, data1, data2, channel); break;
case 3: MIDI4.send(mtype, data1, data2, channel); break;
case 4: MIDI5.send(mtype, data1, data2, channel); break;
case 5: MIDI6.send(mtype, data1, data2, channel); break;
default: // cases 6-15
midilist[cable - 6]->send(type, data1, data2, channel);
}

} else {
// SysEx messages are special. The message length is given in data1 & data2
unsigned int SysExLength = data1 + data2 * 256;
switch (cable) {
case 0: MIDI1.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
case 1: MIDI2.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
case 2: MIDI3.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
case 3: MIDI4.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
case 4: MIDI5.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
case 5: MIDI6.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
default: // cases 6-15
midilist[cable - 6]->sendSysEx(SysExLength, usbMIDI.getSysExArray(), true);
}
}
activity = true;
}

// blink the LED when any activity has happened
if (activity) {
digitalWriteFast(13, HIGH); // LED on
ledOnMillis = 0;
}
if (ledOnMillis > 15) {
digitalWriteFast(13, LOW); // LED off
}

myusb.Task();

for (uint8_t i = 0; i < CNT_DEVICES; i++) {
if (*drivers != driver_active) {
if (driver_active) {
Serial.printf("*** Device % s - disconnected ***\n", driver_names);
driver_active = false;
} else {
Serial.printf("*** Device % s % x: % x - connected ***\n", driver_names, drivers->idVendor(), drivers->idProduct());
driver_active = true;

const uint8_t *psz = drivers->manufacturer();
if (psz && *psz) Serial.printf(" manufacturer: % s\n", psz);
psz = drivers->product();
if (psz && *psz) Serial.printf(" product: % s\n", psz);
psz = drivers->serialNumber();
if (psz && *psz) Serial.printf(" Serial: % s\n", psz);
}
}
}

}


void sendToComputer(byte type, byte data1, byte data2, byte channel, const uint8_t *sysexarray, byte cable)
{
Serial.println("data");
if (type != midi::SystemExclusive) {
usbMIDI.send(type, data1, data2, channel, cable);
} else {
unsigned int SysExLength = data1 + data2 * 256;
usbMIDI.sendSysEx(SysExLength, sysexarray, true, cable);
}
}
 
Code:
#include <MIDI.h> // access to serial (5 pin DIN) MIDI
#include <USBHost_t36.h> // access to USB MIDI devices (plugged into 2nd USB port)

// Create the Serial MIDI ports
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI2);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, MIDI3);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial4, MIDI4);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial5, MIDI5);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial6, MIDI6);
//midi::MidiInterface &SerialMidiList[6] = {MIDI1, MIDI2, MIDI3, MIDI4, MIDI5, MIDI6};

// Create the ports for USB devices plugged into Teensy's 2nd USB port (via hubs)
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);
MIDIDevice midi01(myusb);
MIDIDevice midi02(myusb);
MIDIDevice midi03(myusb);
MIDIDevice midi04(myusb);
MIDIDevice midi05(myusb);
MIDIDevice midi06(myusb);
MIDIDevice midi07(myusb);
MIDIDevice midi08(myusb);
MIDIDevice midi09(myusb);
MIDIDevice midi10(myusb);
MIDIDevice* midilist[10] = {
&midi01, &midi02, &midi03, &midi04, &midi05, &midi06, &midi07, &midi08, &midi09, &midi10
};

// A variable to know how long the LED has been turned on
elapsedMillis ledOnMillis;

USBDriver* drivers[] = { &hub1, &hub2, &hub3, &hub4, &midi01, &midi02, &midi03, &midi04, &midi05, &midi06, &midi07, &midi08, &midi09, &midi10 };
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char* driver_names[CNT_DEVICES] = { "Hub1", "Hub2", "Hub3" , "Hub4", "MIDI1", "MIDI2", "MIDI3" , "MIDI4", "MIDI5", "MIDI6", "MIDI7", "MIDI8", "MIDI9", "MIDI10" };
bool driver_active[CNT_DEVICES] = { false, false, false, false,false,false,false,false,false,false , false,false,false,false };


void setup() {
	Serial.begin(115200);
	pinMode(13, OUTPUT); // LED pin
	digitalWrite(13, LOW);
	MIDI1.begin(MIDI_CHANNEL_OMNI);
	MIDI2.begin(MIDI_CHANNEL_OMNI);
	MIDI3.begin(MIDI_CHANNEL_OMNI);
	MIDI4.begin(MIDI_CHANNEL_OMNI);
	MIDI5.begin(MIDI_CHANNEL_OMNI);
	MIDI6.begin(MIDI_CHANNEL_OMNI);
	// Wait 1.5 seconds before turning on USB Host. If connected USB devices
	// use too much power, Teensy at least completes USB enumeration, which
	// makes isolating the power issue easier.
	delay(1500);
	Serial.println("Interface_16x16 Example");
	delay(10);
	myusb.begin();
}


void loop() {
	bool activity = false;

	// First read messages from the 6 Serial MIDI IN ports
	if (MIDI1.read()) {
		sendToComputer(MIDI1.getType(), MIDI1.getData1(), MIDI1.getData2(), MIDI1.getChannel(), MIDI1.getSysExArray(), 0);
		activity = true;
	}
	if (MIDI2.read()) {
		sendToComputer(MIDI2.getType(), MIDI2.getData1(), MIDI2.getData2(), MIDI2.getChannel(), MIDI2.getSysExArray(), 1);
		activity = true;
	}
	if (MIDI3.read()) {
		sendToComputer(MIDI3.getType(), MIDI3.getData1(), MIDI3.getData2(), MIDI3.getChannel(), MIDI3.getSysExArray(), 2);
		activity = true;
	}
	if (MIDI4.read()) {
		sendToComputer(MIDI4.getType(), MIDI4.getData1(), MIDI4.getData2(), MIDI4.getChannel(), MIDI4.getSysExArray(), 3);
		activity = true;
	}
	if (MIDI5.read()) {
		sendToComputer(MIDI5.getType(), MIDI5.getData1(), MIDI5.getData2(), MIDI5.getChannel(), MIDI5.getSysExArray(), 4);
		activity = true;
	}
	if (MIDI6.read()) {
		sendToComputer(MIDI6.getType(), MIDI6.getData1(), MIDI6.getData2(), MIDI6.getChannel(), MIDI6.getSysExArray(), 5);
		activity = true;
	}

	// Next read messages arriving from the (up to) 10 USB devices plugged into the USB Host port

	for (int port = 0; port < 10; port++) {
		if (midilist[port]->read()) {
			uint8_t type = midilist[port]->getType();
			uint8_t data1 = midilist[port]->getData1();
			uint8_t data2 = midilist[port]->getData2();
			uint8_t channel = midilist[port]->getChannel();
			const uint8_t* sys = midilist[port]->getSysExArray();
			sendToComputer(type, data1, data2, channel, sys, 6 + port);
			activity = true;
		}
	}

	// Finally, read any messages the PC sends to Teensy, and forward them
	// to either Serial MIDI or to USB devices on the USB host port.
	if (usbMIDI.read()) {
		// get the USB MIDI message, defined by these 5 numbers (except SysEX)
		byte type = usbMIDI.getType();
		byte channel = usbMIDI.getChannel();
		byte data1 = usbMIDI.getData1();
		byte data2 = usbMIDI.getData2();
		byte cable = usbMIDI.getCable();

		Serial.printf("cable % d, chan % d , % d , % d \r\n", cable, channel, data1, data2);

		// midilist[0]->send(type, data1, data2, channel); //to edirol?

		// forward this message to 1 of the 3 Serial MIDI OUT ports
		if (type != usbMIDI.SystemExclusive) {
			// Normal messages, first we must convert usbMIDI's type (an ordinary
			// byte) to the MIDI library's special MidiType.
			midi::MidiType mtype = (midi::MidiType)type;

			// Then simply give the data to the MIDI library send()
			switch (cable) {
			case 0: MIDI1.send(mtype, data1, data2, channel); break;
			case 1: MIDI2.send(mtype, data1, data2, channel); break;
			case 2: MIDI3.send(mtype, data1, data2, channel); break;
			case 3: MIDI4.send(mtype, data1, data2, channel); break;
			case 4: MIDI5.send(mtype, data1, data2, channel); break;
			case 5: MIDI6.send(mtype, data1, data2, channel); break;
			default: // cases 6-15
				midilist[cable - 6]->send(type, data1, data2, channel);
			}

		}
		else {
			// SysEx messages are special. The message length is given in data1 & data2
			unsigned int SysExLength = data1 + data2 * 256;
			switch (cable) {
			case 0: MIDI1.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			case 1: MIDI2.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			case 2: MIDI3.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			case 3: MIDI4.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			case 4: MIDI5.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			case 5: MIDI6.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true); break;
			default: // cases 6-15
				midilist[cable - 6]->sendSysEx(SysExLength, usbMIDI.getSysExArray(), true);
			}
		}
		activity = true;
	}

	// blink the LED when any activity has happened
	if (activity) {
		digitalWriteFast(13, HIGH); // LED on
		ledOnMillis = 0;
	}
	if (ledOnMillis > 15) {
		digitalWriteFast(13, LOW); // LED off
	}

	myusb.Task();

	for (uint8_t i = 0; i < CNT_DEVICES; i++) {
		if (*drivers[i] != driver_active[i]) {
			if (driver_active[i]) {
				Serial.printf("*** Device % s - disconnected ***\n", driver_names[i]);
				driver_active[i] = false;
			}
			else {
				Serial.printf("*** Device % s % x: % x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
				driver_active[i] = true;

				const uint8_t* psz = drivers[i]->manufacturer();
				if (psz && *psz) Serial.printf(" manufacturer: % s\n", psz);
				psz = drivers[i]->product();
				if (psz && *psz) Serial.printf(" product: % s\n", psz);
				psz = drivers[i]->serialNumber();
				if (psz && *psz) Serial.printf(" Serial: % s\n", psz);
			}
		}
	}

}


void sendToComputer(byte type, byte data1, byte data2, byte channel, const uint8_t* sysexarray, byte cable)
{
	Serial.println("data");
	if (type != midi::SystemExclusive) {
		usbMIDI.send(type, data1, data2, channel, cable);
	}
	else {
		unsigned int SysExLength = data1 + data2 * 256;
		usbMIDI.sendSysEx(SysExLength, sysexarray, true, cable);
	}
}

In future could you post your code between CODE tags using the # button on the form, it makes the code so much easier to read and understand.

Unfortunately I have no experience with MIDI, but hopefully someone else can help you.
 
thanks for explaining the code tags. i was looking for that but could not find it. next time i will check again and look for the # button.

ok i tried another simpler example. i think the problem is that the demo code is not handling it correct.
it only checks for systemexclusive and all other is considered as normal.
i then get output like :
USB Host InputFunctions example
cable 0, chan 7 , 116 , 0
cable 0, chan 7 , 124 , 23
cable 0, chan 7 , 101 , 0

it could also be a bug in the midi lib. imo the getdata1 and getdata2 functions should return a zero. I even wonder if it is allowed to use getdata1 and getdata2 in case of a tune song command(single byte command?)
The help does not say anything about it. in fact there is not much help.
So i hope some expert can give his opinion : should getdata1/2 be used or not? and/or should they give a zero?

I hope this helps some other users. i could not find anyone else with this problem so they probably know this already?
anyway to fix the problem the sample must be extended to test for all single byte commands. then do not use getdata but when you transfer, send 0 instead. that will hopefully solve the problem
 
I'm probably too late, but I've finally been able to have a look at the midi code.
As far as I can tell, the code in usb_midi.h explicitly tests for and sends the Tune Select message.

However, the code in MIDI.hpp (in directory ...\hardware\teensy\avr\libraries\MIDI\src) which handles serial midi, does not send a tune select message, neither in regular midi transport nor in thru MIDI.
The send method (starting at line 160 of MIDI.hpp) for single byte messages does this:
Code:
    else if (inType >= Clock && inType <= SystemReset)
    {
        sendRealTime(inType); // System Real-time and 1 byte.
    }
This is part of what is causing the observed problem because it only checks for System Real-time messages. The tune request is a system common message and is the only defined one of those which is a single byte. Note that the comment "and 1 byte" implies that it also handles the tune request when it obviously doesn't.
That statement in MIDI.hpp should be modified to this:
Code:
    else if ((inType >= Clock && inType <= SystemReset) || (inType == TuneRequest))

Also, in the thruFilter method (starts at line 1400) there is a switch statement which handles one-byte messages:
Code:
        // Send the message to the output
        switch (mMessage.type)
        {
                // Real Time and 1 byte
            case Clock:
            case Start:
            case Stop:
            case Continue:
            case ActiveSensing:
            case SystemReset:
            case TuneRequest:
                sendRealTime(mMessage.type);
                break;
This clearly detects the TuneRequest message and passes it to the sendRealTime function. But there is a similar switch statement in sendRealTime except that it does not have a case for TuneRequest and therefore does not send the one-byte message.
So, in sendRealTime (which starts at line 492) change the switch statement to this:
Code:
    switch (inType)
    {
        case Clock:
        case Start:
        case Stop:
        case Continue:
        case ActiveSensing:
        case SystemReset:
        case TuneRequest:
            if (mTransport.beginTransmission(inType))

One caveat: It's not clear to me whether the tune request message was deliberately omitted for some reason related to the MIDI protocol or whether it was a bug.
I can't test this because I only have have one synth and it does not respond to, or generate, a tune request message.

Pete
 
thank you for having a look at it. I will make the suggested changes.
if it works it makes the main code simpler. i did not test the other single byte commands but i will do that too.
still it could be just a bug in the sample code as it was just a sample and maybe the sample is not suppose to support it.
 
Back
Top