MIDI Sniffer

MIDI Sniffer

  • The purpose of this program is parsing MIDI messages sent from a master keyboard.
  • Messages are then written on a serial terminal ( and by the way passed through )

Architecture

  • MIDI frames are the same as RS232, with a special rate of 31250 bauds.
  • UART6 is used for midi messages, UART2 is used to write on a PC terminal.

architecture_midi_sniffer.svg

Assembly Instructions

  • Plug the midi shield on the nucleo F411.
  • Connect the RX and TX MIDI shield to Nucleo F411 UART6-RX and UART6-TX.

Capturing and Analyzing a midi message

If we press e key on a master keyboard, the following message is sent ( Saleae Capture ) :

saleae.svg

  • NOTE ON : [ 0x90] [ 0x30 ] [ 0x40 ] : [ Type = Note_on + Channel = 1 ] [ note = C5 ] [ Velocity = 0x40 ]

When the key is released :

  • NOTE OFF : [ 0x80] [ 0x30 ] [ 0x61 ] : [ Type = Note_off + Channel = 1 ] [ note = C5 ] [ Velocity = 0x61 ]

Look at midi.org for further specifications.


Algorithm

int main(void)
{
    uint16_t val;
    uint8_t mess_to_send[3];

	HAL_Init();
	SystemClock_Config();

	uart2_Init();			// CABLE VCOM
	uart6_Init();			// CABLE MIDI
    HAL_Delay(500);

	while(1)
	{
		if(MIDI_receive())
		{
			switch (midi_mess.type)
			{
				case NOTE_ON : 	term_printf("NOTE ON, channel : %d, Note : %d, Velocity : %d \n\r",midi_mess.channel, midi_mess.data1, midi_mess.data2);
								mess_to_send[0] = midi_mess.type | ( midi_mess.channel -1 );
								mess_to_send[1] = midi_mess.data1;
								mess_to_send[2] = midi_mess.data2;
								MIDI_send(mess_to_send , 3);		// MIDI THROUGH
								break;

								...

				case PROGRAM_CHANGE:
								term_printf("PRG CHANGE, channel : %d, Value : %d \n\r",midi_mess.channel,midi_mess.data1);
								mess_to_send[0] = midi_mess.type | ( midi_mess.channel -1 );
								mess_to_send[1] = midi_mess.data1;
								MIDI_send(mess_to_send , 3);
								break;

				case RESET : 	term_printf("RESET \n\r");
								mess_to_send[0] = midi_mess.type;
								MIDI_send(mess_to_send , 3);
								break;

				default : 		break ;
			}
		}
	}
	return 0;
}

When a byte is received on UART6, a receive interrupt occurs.
The MIDI_rcv_cb() callback function updates MIDI_buf .

 void MIDI_rcv_cb(uint8_t car)
{
		midi_buf[(p_wr++)%BUF_SIZE] = car;
		size++;
}

receive_int.svg

In the main program, we call continuously MIDI_receive() function. MIDI_receive() evaluates the size of the received message, and then looking at the type, manages (or not) to parse the message.

The parsed message is recorded in the MIDI_mess structure.

mess_structure.svg

Message parsed is then displayed on the terminal.

Some messages can be 1 byte , 2 bytes or 3 bytes long.

In addition we can consider a delay between several messages received and the MIDI_receive() function.
The complete algorithm of MIDI_receive() is :

midi_receive_algo.svg


Project

Source Project

WORKSPACE_F411_MIDI_SNIFFER.zip