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