home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
bmd-1.0beta.tar.Z
/
bmd-1.0beta.tar
/
bmd-1.0beta
/
app
/
omtd
/
midi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-21
|
7KB
|
363 lines
/*
* Copyright (c) 1990 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* The midi hooks for mtd. We handle all communication with the driver.
* We are at a fairly low level here. All time stamps are unwarped, driver
* times, and all events are raw (i.e., note events are separate note on
* note off pairs).
*/
#include <errno.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sundev/midi.h>
#include <signal.h>
#include "setting.h"
#include "event.h"
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
static int midifd = -1;
static u_long midirate;
static void
midi_prune(pp, start, end)
struct event **pp;
u_long start;
u_long end;
{
struct event *next;
/* XXX This can be more efficient. */
while (*pp) {
if ((*pp)->m.mm_time < start || (*pp)->m.mm_time > end) {
next = (*pp)->next;
efree(*pp);
*pp = next;
} else
pp = &(*pp)->next;
}
}
static void
miditotv(mt, p)
u_long mt;
struct timeval *p;
{
u_long usec = midirate * mt;
p->tv_usec = usec % 1000000;
p->tv_sec = usec / 1000000;
}
/*
* Read pending input into the input track.
* Events are unprocessed; when input track is merged into
* a play track, we will unwarp the timestamps and turn
* note_on/note_off pairs into "single note with duration" events.
*/
struct event **
midi_read(pp, icnt, stop)
struct event **pp;
int *icnt;
u_long stop;
{
register int cc;
register struct midi_msg *mp;
struct midi_msg buf[512];
cc = read(midifd, (char *)buf, sizeof buf);
if (cc < 0) {
perror("read");
exit(1);
}
*icnt += cc / sizeof(*mp);
for (mp = buf; cc > 0; cc -= sizeof(*mp)) {
*pp = ealloc();
(*pp)->m = *mp++;
pp = &(*pp)->next;
}
*pp = 0;
return pp;
}
/*
* Called knowing that a write will not block.
*/
struct event *
midi_write(workahead, front, click)
u_long workahead;
struct event *front;
struct event *click;
{
struct event **pp, *tmp;
#define NMSG 64
struct midi_msg msgbuf[NMSG], *mp;
int maxmsg, nmsg, cc;
pp = &front;
if (ioctl(midifd, BMDNWRITE, (char *)&maxmsg) < 0) {
perror("BMDNWRITE");
exit(1);
}
if (maxmsg == 0)
abort();
if (maxmsg > NMSG)
maxmsg = NMSG;
mp = msgbuf;
for (nmsg = 0; nmsg < maxmsg; ++nmsg) {
if (click && click->m.mm_time <= workahead &&
(*pp == 0 || click->m.mm_time <= (*pp)->m.mm_time)) {
*mp++ = click->m;
advance_click(click);
} else if (*pp != 0 && (*pp)->m.mm_time <= workahead) {
*mp++ = (*pp)->m;
pp = &(*pp)->next;
} else
break;
}
if (nmsg > 0) {
cc = write(midifd, (char *)msgbuf,
nmsg * sizeof(struct midi_msg));
if (cc < 0) {
perror("write");
exit(1);
}
if (cc != nmsg * sizeof(struct midi_msg)) {
/*
* This shouldn't happen.
*/
error("midi_write: driver did a partial write");
exit(1);
}
}
/*
* Free the amount we wrote, and return the rest.
*/
tmp = *pp;
*pp = 0;
efreel(front);
return tmp;
#undef NMSG
}
static int interrupted;
static void
midiintr()
{
interrupted = 1;
}
#define WORKAHEAD 3000
/*
* Start up output. Enable record.
*/
struct event *
midi_run(time_start, time_end, output, click)
u_long time_start;
u_long time_end;
struct event *output;
struct event *click;
{
struct event *input, **intail;
void (*oldfn)();
fd_set wset, rset;
int wfd, width;
struct timeval tv;
u_long t;
int icnt = 0;
tv.tv_sec = WORKAHEAD / 1000;
tv.tv_usec = 0;
interrupted = 0;
oldfn = signal(SIGINT, midiintr);
midiflush();
midireset(time_start); /* XXX */
intail = &input;
wfd = dup(midifd);
if (wfd < 0) {
perror("dup");
exit(1);
}
width = MAX(wfd, midifd) + 1;
while (!interrupted && (t = miditime()) <= time_end) {
FD_ZERO(&wset);
if (output && output->m.mm_time < t + WORKAHEAD)
FD_SET(wfd, &wset);
FD_ZERO(&rset);
FD_SET(midifd, &rset);
if (select(width, &rset, &wset, 0, &tv) < 0) {
if (errno == EINTR)
continue;
perror("select");
exit(1);
}
if (FD_ISSET(wfd, &wset))
output = midi_write(t + WORKAHEAD, output, click);
if (FD_ISSET(midifd, &rset))
intail = midi_read(intail, &icnt);
}
intail = midi_read(intail, &icnt);
*intail = 0;
midi_prune(&input, time_start, time_end);
/* Free any remaining output. */
efreel(output);
midishutup();
close(wfd);
printf("Read %d events\n", icnt);
(void)signal(SIGINT, oldfn);
return input;
}
midiflush()
{
if (ioctl(midifd, BMDFLUSH, (char *)0) < 0) {
perror("BMDFLUSH");
exit(1);
}
}
midi_open(flag, port)
int flag;
int port;
{
int fd;
int n = 0;
char device[sizeof "/dev/midi000"];
/*
* Go through all the minors and find one that isn't in use.
*/
do {
(void)sprintf(device, "/dev/midi%d", n++);
fd = open(device, flag);
} while (fd < 0 && errno == EBUSY);
if (fd < 0) {
perror(device);
exit(1);
}
if (ioctl(fd, BMDATTACH, (char *)&port) < 0) {
perror("BMDATTACH");
exit(1);
}
return fd;
}
initmidi()
{
u_long filter;
midifd = midi_open(O_RDWR, 1);
midirate = 0;
if (ioctl(midifd, BMDRATE, &midirate) < 0 || midirate == 0) {
perror("MIDIRATE");
exit(1);
}
s.ticks_per_beat = (60.0 / BASETEMPO) * 1e6 / midirate;
/* XXX */
s.beats_per_bar = 4;
filter = MIDIMASK(0xf0)|MIDIMASK(0xfe);
if (ioctl(midifd, BMDFILTER, &filter) < 0) {
perror("BMDFILTER");
exit(1);
}
if (fcntl(midifd, F_SETFL, FNDELAY) < 0) {
perror("F_SETFL");
exit(1);
}
#ifdef notdef
if (ioctl(midifd, FIONBIO, 0) < 0) {
perror("FNBIO");
exit(1);
}
#endif
}
midishutup()
{
int cc, i;
/* All notes off message. */
struct midi_msg m = { 0, { 0xb0, 123, 0 } };
if (midifd < 0)
return;
if (ioctl(midifd, BMDFLUSH, (char *)0) < 0) {
perror("BMDFLUSH");
exit(1);
}
i = 0;
/*
* Assume all output will make it since we just did a flush.
* (i.e., we don't check for cc == 0)
*/
for (i = 0; i < 16; ++i) {
m.mm_data[0] = 0xb0 | i;
cc = write(midifd, &m, sizeof(m));
if (cc < 0) {
perror("write");
exit(1);
}
}
}
miditime()
{
u_long x;
if (ioctl(midifd, BMDGTIME, &x) < 0) {
perror("BMDGTIME");
exit(1);
}
return x;
}
midireset(time)
u_long time;
{
if (ioctl(midifd, BMDSTIME, (char *)&time) < 0) {
perror("BMDSTIME");
exit(1);
}
}
midiecho(chan)
int chan;
{
u_int uchan = chan;
if (ioctl(midifd, BMDECHO, (char *)&uchan) < 0) {
perror("BMDECHO");
exit(1);
}
}