prefect
Home Engineering Publications
DACS: MIDI Handler Code, midi.c
 
  next up previous contents
Next: Mixer Mid-Level Driver Header, Up: MIDI Controller Previous: MIDI Handler Header, midi.h   Contents

MIDI Handler Code, midi.c

This code handles low-level MIDI streams. This code supports MIDI-over-serial as well as true MIDI ports in a Linux environment.
/*****************************************************************************
 * DACS : Distributed Audio Control System
 *============================================================================
 *         File: midi.c
 *       Author: Stephen S. Richardson
 * Date Created: 04.21.97
 *  Environment: GNU C Compiler (GCC) v2.7.1, Linux i486 v2.0.28
 *        Build: make
 *============================================================================
 * The code, executables, documentation, firmware images, and all related
 * material of DACS are  
 * Copyright (C) 1997 Stephen S. Richardson - ALL RIGHTS RESERVED
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include "midi.h"

/*****************************************************************************
 * midi_openser
 *
 * opens a serial port as a virtual MIDI device.
 *****************************************************************************/
int midi_openser (char *devnam, int bd)
{
  struct termios t;
  int fd;
  
  /* open device */
  fd=open(devnam, O_RDWR|O_NONBLOCK);
  
  if (fd<1) {
    fprintf (stderr, "Error opening device %s.\n", devnam);
    exit (1);
  }
  
  tcgetattr (fd, &t);
  
  /* set the port discipline */
  t.c_iflag=BRKINT|IGNPAR;
  t.c_oflag=OPOST;
  t.c_cflag=CS8|CREAD|CLOCAL;
  t.c_lflag=0;
  
  cfsetospeed (&t, (speed_t) bd);
  cfsetispeed (&t, (speed_t) bd);
  tcsetattr (fd, TCSANOW, &t);
  
  return (fd);
}

/*****************************************************************************
 * midi_openmidi
 *
 * opens a sound card's midi port as a midi device
 *****************************************************************************/
int midi_openmidi (char *devnam)
{
  int fd;

  fd=open (devnam, O_RDWR|O_NONBLOCK);

  if (fd==-1) {
    fprintf (stderr, "error opening MIDI device %s.\n", devnam);
    exit (1);
  }
  return (fd);
}


/*****************************************************************************
 * midi_readstream
 *
 * handles reading a MIDI stream rather inefficiently.
 *****************************************************************************/
void midi_readstream (struct midi_stream *ms) 
{
  int r;
  unsigned char ch;

  do {
    r=read(ms->fd,&ch,1);
  } while (r<1);

  if (ch&0x80) {
    /* MIDI command */

    /*
     * first we check to see if it's a MIDI realtime message.
     *
     * from the MIDI spec:
     *
     * Each RealTime Category message (ie, Status of 0xF8 to 0xFF) consists
     * of only 1 byte, the Status. These messages are primarily concerned
     * with timing/syncing functions which means that they must be sent and
     * received at specific times without any delays. Because of this, MIDI
     * allows a RealTime message to be sent at any time, even interspersed
     * within some other MIDI message. For example, a RealTime message
     * could be sent inbetween the two data bytes of a Note On message. A
     * device should always be prepared to handle such a situation;
     * processing the 1 byte RealTime message, and then subsequently resume
     * processing the previously interrupted message as if the RealTime
     * message had never occurred.
     */

    if ( (ch >= 0xF8) && (ch <= 0xFF) ) {
      /* MIDI realtime */

      switch (ch) {
	
      case MIDICMD_CLOCK:
	ms->midiclock++;
	ms->validdata=0;
	break;
	
      case MIDICMD_START:
	ms->midiclock=0;
	ms->runstatus=MSTART;
	ms->validdata=0;
	break;

      case MIDICMD_CONT:
	ms->runstatus=MCONT;
	ms->validdata=0;
	break;

      case MIDICMD_STOP:
	ms->runstatus=MSTOP;
	ms->validdata=0;
	break;

      case MIDICMD_ASENSE:
	ms->validdata=0;
	break;

      case MIDICMD_RESET:
	ms->resetflag=1;
	ms->validdata=0;
	break;
      }
    } else if ( (ch==MIDICMD_SYSEXST) || (ch==MIDICMD_SYSEXEN) ) {
      /* system exclusive */

      

    } else {
      /* some other kind of MIDI command */

      ms->cmd = ch&0xF0;       /* upper nybble is command */
      ms->chan = ch&0x0F;      /* lower nybble is channel */

      switch (ms->cmd) {
      case MIDICMD_NOTEOFF:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_NOTEON:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_AFTER:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_CONTROL:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_PROGRAM:
	ms->dptr=ms->data;
	ms->dcount=1;
	ms->validdata=0;
	break;
      case MIDICMD_PRESS:
	ms->dptr=ms->data;
	ms->dcount=1;
	ms->validdata=0;
	break;
      case MIDICMD_BEND:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_MTCQF:
	ms->dptr=ms->data;
	ms->dcount=1;
	ms->validdata=0;
	break;
      case MIDICMD_SPP:
	ms->dptr=ms->data;
	ms->dcount=2;
	ms->validdata=0;
	break;
      case MIDICMD_SONGSEL:
	ms->dptr=ms->data;
	ms->dcount=1;
	ms->validdata=0;
	break;
      case MIDICMD_TUNEREQ:
	ms->dptr=ms->data;
	ms->dcount=0;
	ms->validdata=1;  /* no data, so it's valid right away */
	break;
      default:
	ms->dptr=ms->data;
	ms->dcount=0;
	ms->validdata=0;
	break;
      }

    }
  } else {
    /* MIDI data, not command */

    if (ms->dcount) {
      /* current MIDI command still has pending data, save it */

      *ms->dptr = ch;
      ms->dptr++;
      ms->dcount--;
    }

    /* did we get all of the data for this command? */
    if (!ms->dcount) ms->validdata=1;

  }
}


int midi_datawaiting (struct midi_stream *ms)
{
  int fd;
  fd_set fds;
  struct timeval tv;

  fd=ms->fd;

  bzero (&tv, sizeof (struct timeval));
  tv.tv_usec = 1;

  FD_ZERO (&fds);
  FD_SET (fd, &fds);
  while ((select (fd+1, &fds, NULL, NULL, &tv))==-1); /* make sure select */
                                                       /* works            */

  if (FD_ISSET (fd, &fds)) return (1);
  else return (0);
}


Steve Richardson 2000-07-06
Table of Contents

[PDF] [Whole document in PDF 1.9MB]

[more photos and information]

 
Page last modified:
Copyright © 1993-2000 prefect - All Rights Reserved.