Jump to content

7-bit sysex scrambling from mios


Durisian

Recommended Posts

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

Link to comment
Share on other sites

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...