Jump to content

Recommended Posts

Posted

Here we go. Its largely based on the syx_dump example, but does some descrambling. The function you are interested in is SYSEX_Cmd_GetDataBytes. Comments on the efficiency and style of coding as always very welcome, I have very little experience in C and assembler.

// $Id: sysex.c 441 2008-08-13 21:27:08Z tk $
/*
 * SysEx Parser Demo
 * see README.txt for details
 *
 * ==========================================================================
 *
 *  Copyright (C) 2008 Thorsten Klose (tk@midibox.org)
 *  Licensed for personal non-commercial use only.
 *  All other rights reserved.
 * 
 * ==========================================================================
 */

//wackazong: Differences to original
//I use a format that has only one command byte (with the MSB = 0) and then 
//8bit data coded into 7bit values
//The length of the message depends on the command byte

/////////////////////////////////////////////////////////////////////////////
// Include files
/////////////////////////////////////////////////////////////////////////////

#include <cmios.h>

#include <sysex.h>
#include <knoepfli.h>

/////////////////////////////////////////////////////////////////////////////
// Internal Prototypes
/////////////////////////////////////////////////////////////////////////////

void SYSEX_SendAck(unsigned char ack_code, unsigned char ack_arg);
void SYSEX_CmdFinished(void);
void SYSEX_SendFooter(unsigned char force);
void SYSEX_Cmd(unsigned char cmd_state, unsigned char midi_in);
void SYSEX_Cmd_GetDataBytes(unsigned char cmd_state, unsigned char midi_in);
void SYSEX_Cmd_Ping(unsigned char cmd_state, unsigned char midi_in);

/////////////////////////////////////////////////////////////////////////////
// Global Variables
/////////////////////////////////////////////////////////////////////////////

sysex_state_t sysex_state;
unsigned char sysex_cmd;
unsigned char sysex_data_size;  //size of the sysex data part in bytes
unsigned char sysex_checksum;
unsigned char sysex_received_checksum;
unsigned int sysex_receive_ctr;      //counts the number of sysex bytes received
unsigned int sysex_data_receive_ctr; //counts the number of data bytes received (8bit)
unsigned char sysex_bit_pointer;  //for unscrambling, tells how many bits of the received 7 already belong to the next bit
unsigned char sysex_buffer[8]; //8byte buffer containing the sysex message converted to 8bit format

/////////////////////////////////////////////////////////////////////////////
// Static definitions
/////////////////////////////////////////////////////////////////////////////

// should be changed for your own application
// Headers used by MIDIbox applications are documented here:
// http://svnmios.midibox.org/filedetails.php?repname=svn.mios&path=%2Ftrunk%2Fdoc%2FSYSEX_HEADERS
// if you decide to use "F0 00 00 7E" prefix, please ensure that your
// own ID (here: 0x7f) will be entered into this document.
// Otherwise please use a different header
const unsigned char sysex_header[] = { 0xf0, 0x00, 0x00, 0x7e, 0x7f };


/////////////////////////////////////////////////////////////////////////////
// This function initializes the SysEx handler
/////////////////////////////////////////////////////////////////////////////
void SYSEX_Init(void)
{
	sysex_state.ALL = 0;
}

/////////////////////////////////////////////////////////////////////////////
// This function parses an incoming sysex stream for SysEx messages
/////////////////////////////////////////////////////////////////////////////
void SYSEX_Parser(unsigned char midi_in)
{
	// ignore realtime messages (see MIDI spec - realtime messages can 
	// always be injected into events/streams, and don't change the running status)
	if( midi_in >= 0xf8 )
		return;

	// branch depending on state
	if( !sysex_state.MY_SYSEX ) {
		if( midi_in != sysex_header[sysex_state.CTR] ) {
			// incoming byte doesn't match
			SYSEX_CmdFinished();
		} else {
			if( ++sysex_state.CTR == sizeof(sysex_header) ) {
				// complete header received, waiting for data
				sysex_state.MY_SYSEX = 1;
				// disable merger forwarding until end of sysex message
				MIOS_MPROC_MergerDisable();
			}
		}
	} else {
		// check for end of SysEx message or invalid status byte
		if( midi_in >= 0x80 ) {
			if( midi_in == 0xf7 && sysex_state.CMD ) {
				SYSEX_Cmd(SYSEX_CMD_STATE_END, midi_in);
			}
			SYSEX_CmdFinished();
		} else {
			// check if command byte has been received
			if( !sysex_state.CMD ) {
				sysex_state.CMD = 1;
				sysex_cmd = midi_in;
				SYSEX_Cmd(SYSEX_CMD_STATE_BEGIN, midi_in);
			}
			else
				SYSEX_Cmd(SYSEX_CMD_STATE_CONT, midi_in);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// This function sends a SysEx acknowledge to notify the user about the received command
// expects acknowledge code (e.g. 0x0f for good, 0x0e for error) and additional argument
/////////////////////////////////////////////////////////////////////////////
void SYSEX_SendAck(unsigned char ack_code, unsigned char ack_arg)
{
	int i;
	unsigned char checksum;
	unsigned char c;

	// send header
	for(i=0; i<sizeof(sysex_header); ++i)
		MIOS_MIDI_TxBufferPut(sysex_header[i]);

	// send ack code and argument
	MIOS_MIDI_TxBufferPut(ack_code);
	MIOS_MIDI_TxBufferPut(ack_arg);

	// send footer
	MIOS_MIDI_TxBufferPut(0xf7);
}


/////////////////////////////////////////////////////////////////////////////
// This function is called at the end of a sysex command or on 
// an invalid message
/////////////////////////////////////////////////////////////////////////////
void SYSEX_CmdFinished(void)
{
	// clear all status variables
	sysex_state.ALL = 0;
	sysex_cmd = 0;

	// enable MIDI forwarding again
	MIOS_MPROC_MergerEnable();
}

/////////////////////////////////////////////////////////////////////////////
// This function sends the SysEx footer if merger enabled
// if force == 1, send the footer regardless of merger state
/////////////////////////////////////////////////////////////////////////////
void SYSEX_SendFooter(unsigned char force)
{
	if( force || (MIOS_MIDI_MergerGet() & 0x01) )
		MIOS_MIDI_TxBufferPut(0xf7);
}

/////////////////////////////////////////////////////////////////////////////
// This function handles the sysex commands
/////////////////////////////////////////////////////////////////////////////
void SYSEX_Cmd(unsigned char cmd_state, unsigned char midi_in)
{
	// enter the commands here
	switch( sysex_cmd ) {
		case 0x01:  //set color of one LED
			sysex_data_size = 10;
			SYSEX_Cmd_GetDataBytes(cmd_state,midi_in);
			break;
		case 0x02:
			//
			break;
		case 0x0f:
			SYSEX_Cmd_Ping(cmd_state, midi_in);
			break;
		default:
			// unknown command
			SYSEX_SendFooter(0);
			SYSEX_SendAck(SYSEX_DISACK, SYSEX_DISACK_INVALID_COMMAND);
			SYSEX_CmdFinished();      
	}
}

/////////////////////////////////////////////////////////////////////////////
// Receive the data bytes of the command
/////////////////////////////////////////////////////////////////////////////
void SYSEX_Cmd_GetDataBytes(unsigned char cmd_state, unsigned char midi_in)
{
	int i;
	unsigned char tmp;

	switch( cmd_state ) {

		case SYSEX_CMD_STATE_BEGIN:
			sysex_checksum = 0; // clear checksum
			sysex_receive_ctr = 0; // clear byte counter
			sysex_data_receive_ctr = 0;
			sysex_received_checksum = 0;
			sysex_bit_pointer = 0;
			break;

		case SYSEX_CMD_STATE_CONT:

			if( sysex_receive_ctr < sysex_data_size ) {

				// add to checksum
				sysex_checksum += midi_in;

				//do some unscrambling

				//shift the midi_in one to the left, then the LSB is 0 and has no meaning
				midi_in <<= 1;

				//move the right bits over to the last buffer char
				for (i = 0; i < sysex_bit_pointer; ++i) {

					//set the bit to be changed to 0
					//sysex_buffer[sysex_data_receive_ctr-1] & ~(0x01 << i); //it is zero anyway
					//isolate the bit to be set into tmp. Its always the MSB!
					tmp = midi_in & 0x80;
					//shift the bit to be set from midi_in to the right place
					tmp >>= (7-sysex_bit_pointer+i+1);
					//set the new bit value
					sysex_buffer[sysex_data_receive_ctr-1] |= tmp;
					//shift midi_in to the left, now the next bit we are interested in is again the MSB
					midi_in <<= 1;

				};

				//store the received byte in the buffer
				sysex_buffer[sysex_data_receive_ctr] = midi_in;

				//increase the pointer
				if(sysex_bit_pointer==7) {
					sysex_bit_pointer = 0;
				} else {
					++sysex_bit_pointer;
					++sysex_data_receive_ctr;
				}					

#if SYSEX_CHECKSUM_PROTECTION
			} else if( sysex_receive_ctr == sysex_data_size ) {
				// store received checksum
				sysex_received_checksum = midi_in;
#endif
			} else {
				// wait for F7
			}

			// increment counter
			++sysex_receive_ctr;

			break;

		default: // SYSEX_CMD_STATE_END
			SYSEX_SendFooter(0);

#if SYSEX_CHECKSUM_PROTECTION
			//increase the sysex_data_size by the checksum byte
			++sysex_data_size
#endif

			//prepare here for new packet in the same sysex session.

			if( sysex_receive_ctr < sysex_data_size) {
				// not enough bytes received
				SYSEX_SendAck(SYSEX_DISACK, SYSEX_DISACK_LESS_BYTES_THAN_EXP);
			} else if( sysex_receive_ctr > sysex_data_size) {
				// too many bytes received
				SYSEX_SendAck(SYSEX_DISACK, SYSEX_DISACK_MORE_BYTES_THAN_EXP);
#if SYSEX_CHECKSUM_PROTECTION
			} else if( sysex_received_checksum != ((sysex_checksum ^ 0xff) & 0x7f) ) {
				// notify that wrong checksum has been received
				SYSEX_SendAck(SYSEX_DISACK, SYSEX_DISACK_WRONG_CHECKSUM);
#endif
			} else {
				// call the command as a function returning 0 or 1 depending on success
				// put your function where the 0 is
				if( 0 ) {
					// write failed (bankstick not available)
					SYSEX_SendAck(SYSEX_DISACK, SYSEX_DISACK_BS_NOT_AVAILABLE);
				} else {
					// notify that bytes have been received
					SYSEX_SendAck(SYSEX_ACK, 0x00);
				}
			}
			break;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Command 0F: Ping (just send back acknowledge)
/////////////////////////////////////////////////////////////////////////////
void SYSEX_Cmd_Ping(unsigned char cmd_state, unsigned char midi_in)
{
	switch( cmd_state ) {

		case SYSEX_CMD_STATE_BEGIN:
			// nothing to do
			break;

		case SYSEX_CMD_STATE_CONT:
			// nothing to do
			break;

		default: // SYSEX_CMD_STATE_END
			SYSEX_SendFooter(0);

			// send acknowledge
			SYSEX_SendAck(SYSEX_ACK, 0x00);

			break;
	}
}

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...