|   | 
          
            
  
  
  
   
 
 Next: Mixer Mid-Level Driver Header,
 Up: MIDI Controller
 Previous: MIDI Handler Header, midi.h
     Contents 
 
 
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
 
 
[Whole document in PDF 1.9MB]
 
 
[more photos and information]
 
 |