Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

midi/oss_midi_parser.c

MIDI mesage parser

Description




This file is part of Open Sound System.

Copyright (C) 4Front Technologies 1996-2008.

This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.


#include "oss_config.h"
#include "midiparser.h"

struct _mtc_state
{
  int prev_ix;
  int state;
  int offset;
  oss_mtc_data_t mtc, mtc0;
};

typedef struct midi_input_info
{				/* MIDI input scanner variables */
#define MI_MAX	64
  int m_busy;
  unsigned char m_buf[MI_MAX];
  unsigned char m_prev_status;	/* For running status */
  int m_ptr;
#define MST_INIT			0
#define MST_DATA			1
#define MST_SYSEX			2
  int m_state;
  int m_left;
  int m_f1_flag;
} midi_input_info_t;

struct midiparser_common
{
  midi_input_info_t inc;
  midiparser_callback_t callback;
  midiparser_mtc_callback_t mtc_callback;
  void *client_context;
  struct _mtc_state mtc_state;
};

#define CALLBACK(cat, msg, ch, p1, p2, p3) \
	{ \
		unsigned char arr[3];arr[0]=p1;arr[1]=p2;arr[2]=p3; \
		synth->callback(synth->client_context, cat, msg, ch, arr, 3); \
	}
#define CALLBACK_A(cat, msg, ch, parms, len) \
	synth->callback(synth->client_context, cat, msg, ch, parms, len)

static void
do_system_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
{
  CALLBACK_A (CAT_REALTIME, *msg, 0, msg, mlen);
  return;
}

static void
do_sysex_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
{
  CALLBACK_A (CAT_SYSEX, 0, 0, msg, mlen);
  return;
}

static void
do_realtime_msg (midiparser_common_p synth, unsigned char data)
{
}

static void
do_midi_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
{
  switch (msg[0] & 0xf0)
    {
    case 0x90:
      if (msg[2] != 0)
	{
	  CALLBACK (CAT_VOICE, 0x90, msg[0] & 0x0f, msg[1], msg[2], 0);
	  break;
	}
      msg[2] = 64;

    case 0x80:
      CALLBACK (CAT_VOICE, 0x80, msg[0] & 0x0f, msg[1], msg[2], 0);
      break;

    case 0xA0:
      CALLBACK (CAT_VOICE, 0xA0, msg[0] & 0x0f, msg[1], msg[2], 0);
      break;

    case 0xB0:
      CALLBACK (CAT_CHN, 0xB0, msg[0] & 0x0f, msg[1], msg[2], 0);
      break;

    case 0xC0:
      CALLBACK (CAT_CHN, 0xC0, msg[0] & 0x0f, msg[1], 0, 0);
      break;

    case 0xD0:
      CALLBACK (CAT_CHN, 0xD0, msg[0] & 0x0f, msg[1], 0, 0);
      break;

    case 0xE0:
      CALLBACK (CAT_VOICE, 0xE0, msg[0] & 0x0f, msg[1], msg[2], 0);
      break;

    case 0xf0:			/* System common messages */
      do_system_msg (synth, msg, mlen);
      break;

    default:
      ;
    }
}

static void
send_mtc (midiparser_common_p synth, struct _mtc_state *st)
{
  oss_mtc_data_t mtc;

  memcpy (&mtc, &st->mtc, sizeof (mtc));

  mtc.qframes += st->offset;
  mtc.frames += mtc.qframes / 4;
  mtc.qframes %= 4;

  if (mtc.time_code_type == 0)
    mtc.time_code_type = 30;
  mtc.seconds += mtc.frames / mtc.time_code_type;	/* TODO: Handle drop frames */
  mtc.frames %= mtc.time_code_type;

  mtc.minutes += mtc.seconds / 60;
  mtc.seconds %= 60;

  mtc.hours += mtc.minutes / 60;
  mtc.minutes %= 60;

  synth->mtc_callback (synth->client_context, &mtc);
}

static void
mtc_message (midiparser_common_p synth, struct _mtc_state *st,
	     unsigned char b)
{
  static char frame_types[4] = { 24, 25, 29, 30 };
  int ix, data;
  int previx;

  ix = b >> 4;
  data = b & 0x0f;

  previx = (st->prev_ix + 1) % 8;

  if (ix == previx)
    st->mtc0.direction = st->mtc.direction = MTC_DIR_FORWARD;
  else if (ix == st->prev_ix)
    st->mtc0.direction = st->mtc.direction = MTC_DIR_STOPPED;
  else
    st->mtc0.direction = st->mtc.direction = MTC_DIR_BACKWARD;
  st->prev_ix = ix;

  if (st->state == 0)
    {
      if (ix != 0)		/* Not the beginning of the sequence yet */
	return;
      st->state = 1;
      st->offset = -1;
    }

  switch (ix)
    {
    case 0:			/* Frame count LS nibble */
      st->mtc0.qframes = 0;
      st->mtc0.frames = data;
      break;

    case 1:			/* Frame count MS nibble */
      st->mtc0.frames |= data << 4;
      break;

    case 2:			/* Seconds count LS nibble */
      st->mtc0.seconds = data;
      break;

    case 3:			/* Seconds count MS nibble */
      st->mtc0.seconds |= data << 4;
      break;

    case 4:			/* Minutes count LS nibble */
      st->mtc0.minutes = data;
      break;

    case 5:			/* Minutes count MS nibble */
      st->mtc0.minutes |= data << 4;
      break;

    case 6:			/* Hours count LS nibble */
      st->mtc0.hours = data;
      break;

    case 7:			/* Hours count MS nibble */
      st->mtc0.hours |= data << 4;
      st->mtc0.time_code_type = frame_types[(st->mtc0.hours >> 5) & 0x03];
      st->mtc0.hours &= 0x1f;
      memcpy (&st->mtc, &st->mtc0, sizeof (st->mtc));
      break;
    }

  if (ix == 7)
    st->offset = 7;
  else
    st->offset++;
  send_mtc (synth, st);
}

static void
handle_midi_input (midiparser_common_p synth, midi_input_info_t * inc,
		   unsigned char data)
{

  {
    2,				/* 8x */
    2,				/* 9x */
    2,				/* Ax */
    2,				/* Bx */
    1,				/* Cx */
    1,				/* Dx */
    2,				/* Ex */
    0				/* Fx */
  };

  if (data == 0xfe)		/* Active sensing */
    {
      return;
    }

  if (data >= 0xf8)		/* Real time message */
    {
      do_realtime_msg (synth, data);
      CALLBACK (CAT_REALTIME, data, 0, 0, 0, 0);
      return;
    }

  if (data == 0xf1)		/* MTC quarter frame (1st byte) */
    {
      inc->m_f1_flag = 1;
      return;
    }

  if (inc->m_f1_flag)		/* MTC quarter frame (2nd byte) */
    {
      inc->m_f1_flag = 0;

      if (synth->mtc_callback != NULL)
	{
	  mtc_message (synth, &synth->mtc_state, data);
	  return;
	}
      CALLBACK (CAT_MTC, 0xf1, 0, data, 0, 0);
      return;
    }

  switch (inc->m_state)
    {
    case MST_INIT:
      if (data & 0x80)		/* MIDI status byte */
	{
	  if ((data & 0xf0) == 0xf0)	/* Common message */
	    {
	      switch (data)
		{
		case 0xf0:	/* Sysex */
		  inc->m_state = MST_SYSEX;
		  inc->m_ptr = 1;
		  inc->m_left = MI_MAX;
		  inc->m_buf[0] = data;
		  break;	/* Sysex */

		case 0xf1:	/* MTC quarter frame */
		case 0xf3:	/* Song select */
		  inc->m_state = MST_DATA;
		  inc->m_ptr = 1;
		  inc->m_left = 1;
		  inc->m_buf[0] = data;
		  break;

		case 0xf2:	/* Song position pointer */
		  inc->m_state = MST_DATA;
		  inc->m_ptr = 1;
		  inc->m_left = 2;
		  inc->m_buf[0] = data;
		  break;

		default:	/* Other common messages */
		  inc->m_buf[0] = data;
		  inc->m_ptr = 1;
		  do_midi_msg (synth, inc->m_buf, inc->m_ptr);
		  inc->m_ptr = 0;
		  inc->m_left = 0;
		}
	    }
	  else
	    {
	      /* Channel messages */
	      inc->m_state = MST_DATA;
	      inc->m_ptr = 1;
	      inc->m_left = len_tab[(data >> 4) - 8];
	      inc->m_buf[0] = inc->m_prev_status = data;
	    }
	}
      else /* Running status */ if (inc->m_prev_status & 0x80)	/* Ignore if no previous status (yet) */
	{
	  inc->m_state = MST_DATA;
	  inc->m_ptr = 2;
	  inc->m_left = len_tab[(inc->m_prev_status >> 4) - 8] - 1;
	  inc->m_buf[0] = inc->m_prev_status;
	  inc->m_buf[1] = data;
	}
      break;			/* MST_INIT */

    case MST_DATA:
      inc->m_buf[inc->m_ptr++] = data;
      if (--inc->m_left <= 0)
	{
	  inc->m_state = MST_INIT;
	  do_midi_msg (synth, inc->m_buf, inc->m_ptr);
	  inc->m_ptr = 0;
	}
      break;			/* MST_DATA */

    case MST_SYSEX:
      inc->m_buf[inc->m_ptr++] = data;
      if (data == 0xf7)		/* Sysex end */
	{
	  do_sysex_msg (synth, inc->m_buf, inc->m_ptr);
	  inc->m_state = MST_INIT;
	  inc->m_left = 0;
	  inc->m_ptr = 0;

	}
      else if (inc->m_ptr >= MI_MAX)	/* Overflow protection */
	{
	  do_sysex_msg (synth, inc->m_buf, inc->m_ptr);
	  inc->m_ptr = 0;
	}

      break;			/* MST_SYSEX */

    default:
      inc->m_state = MST_INIT;
    }
}

midiparser_common_p
midiparser_create (midiparser_callback_t callback, void *context)
{
  midiparser_common_p synth;

  if ((synth = KERNEL_MALLOC (sizeof (*synth))) == NULL)
    return NULL;

  memset (synth, 0, sizeof (*synth));

  synth->callback = callback;
  synth->client_context = context;
  synth->mtc_state.prev_ix = -1;

  return synth;
}

void
midiparser_unalloc (midiparser_common_p common)
{
  KERNEL_FREE (common);
}

void
midiparser_mtc_callback (midiparser_common_p common,
			 midiparser_mtc_callback_t callback)
{
  common->mtc_callback = callback;
}

void
midiparser_input (midiparser_common_p synth, unsigned char data)
{
  handle_midi_input (synth, &synth->inc, data);
}

void
midiparser_input_buf (midiparser_common_p synth, unsigned char *data, int len)
{
  int i;

  for (i = 0; i < len; i++)
    handle_midi_input (synth, &synth->inc, data[i]);
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site