/* * midi.rawmidi.c midi play with rawmidi device * * Copyright (C) 1997-1998 Masaki Chikama (Wren) * 1998- * * Based on midiplay+ by Daisuke NAGANO * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* $Id: midi.rawmidi.c,v 1.7 2003/01/31 12:58:28 chikama Exp $ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_MIDI_SEQMIDI #if defined(__FreeBSD__) #include #elif defined(__OpenBSD__) || defined(__NetBSD__) #include #else #include #endif #include #endif #include "portab.h" #include "system.h" #include "midi.h" #include "music_server.h" #include "midifile.h" #include "counter.h" struct _midiflag { char midi_variable[128]; char midi_flag[128]; }; typedef struct _midiflag midiflag_t; static void signal_pause(int sig_num); static void send_reset(); static unsigned long ticks2usec(unsigned long ticks, int division, unsigned int tempo); static void parse_event(); static int midi_initilize(char *devnm, int subdev); static int midi_exit(); static int midi_start(int no, char *data, int datalen); static int midi_stop(); static int midi_pause(void); static int midi_unpause(void); static int midi_get_playing_info(midiplaystate *st); static int midi_getflag(int mode, int index); static int midi_setflag(int mode, int index, int val); static long(* myflush)(void); static void(* mywrite)(unsigned char *d, int n, int p); #ifdef ENABLE_MIDI_RAWMIDI static long raw_myflush(); static void raw_mywrite(unsigned char *d, int n, int p); #endif #ifdef ENABLE_MIDI_SEQMIDI static long seq_myflush(); static void seq_mywrite(unsigned char *d, int n, int p); #endif mididevice_t midi_rawmidi = { midi_initilize, midi_exit, midi_start, midi_stop, midi_pause, midi_unpause, midi_get_playing_info, midi_getflag, midi_setflag, NULL, NULL }; #ifndef MIDI_DEVICE #define MIDI_DEVICE "/dev/midi" #endif #ifndef SEQ_DEVICE #define SEQ_DEVICE "/dev/sequencer" #endif static boolean enabled = FALSE; static struct midiinfo *midi; static int midifd; static char *mididevname; static int midino; static pid_t midipid; static int counter; static int shmkey; static midiflag_t *flags; static boolean cmd_pause; static boolean cmd_stop; static long extratime; #define ALL_NOTEOFF_SZ 48 static char all_noteoff[] = { 0xb0, 0x7b, 0x00, 0xb1, 0x7b, 0x00, 0xb2, 0x7b, 0x00, 0xb3, 0x7b, 0x00, 0xb4, 0x7b, 0x00, 0xb5, 0x7b, 0x00, 0xb6, 0x7b, 0x00, 0xb7, 0x7b, 0x00, 0xb8, 0x7b, 0x00, 0xb9, 0x7b, 0x00, 0xba, 0x7b, 0x00, 0xbb, 0x7b, 0x00, 0xbc, 0x7b, 0x00, 0xbd, 0x7b, 0x00, 0xbe, 0x7b, 0x00, 0xbf, 0x7b, 0x00 }; #define ALL_RESET_SZ 96 static char all_reset[] = { 0xb0, 0x78, 0x00, 0xb0, 0x79, 0x00, 0xb1, 0x78, 0x00, 0xb1, 0x79, 0x00, 0xb2, 0x78, 0x00, 0xb2, 0x79, 0x00, 0xb3, 0x78, 0x00, 0xb3, 0x79, 0x00, 0xb4, 0x78, 0x00, 0xb4, 0x79, 0x00, 0xb5, 0x78, 0x00, 0xb5, 0x79, 0x00, 0xb6, 0x78, 0x00, 0xb6, 0x79, 0x00, 0xb7, 0x78, 0x00, 0xb7, 0x79, 0x00, 0xb8, 0x78, 0x00, 0xb8, 0x79, 0x00, 0xb9, 0x78, 0x00, 0xb9, 0x79, 0x00, 0xba, 0x78, 0x00, 0xba, 0x79, 0x00, 0xbb, 0x78, 0x00, 0xbb, 0x79, 0x00, 0xbc, 0x78, 0x00, 0xbc, 0x79, 0x00, 0xbd, 0x78, 0x00, 0xbd, 0x79, 0x00, 0xbe, 0x78, 0x00, 0xbe, 0x79, 0x00, 0xbf, 0x78, 0x00, 0xbf, 0x79, 0x00 }; extern void sys_set_signalhandler(int SIG, void (*handler)(int)); static void signal_pause(int sig_num) { switch(sig_num) { case SIGTERM: cmd_stop = TRUE; break; case SIGUSR1: /* pause */ if (!cmd_pause) { cmd_pause = TRUE; } break; case SIGUSR2: /* unpause */ if (cmd_pause) { cmd_pause = FALSE; } break; } } static void send_reset() { mywrite(all_reset, ALL_RESET_SZ, 0); usleep(60000); myflush(); usleep(70000); return; } #ifdef ENABLE_MIDI_RAWMIDI #define WRBFSIZE 16384 static unsigned char wrbuff[WRBFSIZE]; static int nwr = 0; /* wrbuff ¤Î index */ static long raw_myflush() { struct timeval st, et; static long ret; static long t1, t2; int i; gettimeofday(&st, NULL); for (i = 0 ;i < nwr ; i++) { while(write(midifd, wrbuff+i, 1) != 1); } nwr = 0; gettimeofday(&et, NULL); t1 = et.tv_usec - st.tv_usec; t2 = et.tv_sec - st.tv_sec; while (t1 < 0) { t2--; t1 += 1000000; } ret = t2 * 1000000 + t1 + extratime; extratime = 0; return ret; } /* * write the code to the buffer */ static void raw_mywrite(unsigned char *d, int n, int p) { if ((nwr + n + 2) >= WRBFSIZE) { extratime = myflush(); } while (n--) { wrbuff[nwr++] = *(d++); } return; } #endif #ifdef ENABLE_MIDI_SEQMIDI SEQ_DEFINEBUF(2048); SEQ_USE_EXTBUF(); static int midi_subdev; void seqbuf_dump() { if (_seqbufptr) { if (write(midifd, _seqbuf, _seqbufptr) == -1) { perror("write"); exit(-1); } } _seqbufptr = 0; } static long seq_myflush() { struct timeval st, et; static long ret; static long t1, t2; gettimeofday(&st, NULL); seqbuf_dump(); gettimeofday(&et, NULL); t1 = et.tv_usec - st.tv_usec; t2 = et.tv_sec - st.tv_sec; while (t1 < 0) { t2--; t1 += 1000000; } ret = t2 * 1000000 + t1 + extratime; extratime = 0; return ret; } /* * write the code to the buffer */ static void seq_mywrite(unsigned char *d, int n, int p) { while(n--) { SEQ_MIDIOUT(midi_subdev, *(d++)); } return; } #endif /* * tick count to micro second for (usleep) * */ static unsigned long ticks2usec(unsigned long ticks, int division, unsigned int tempo) { return (unsigned long) ((ticks) * (tempo) / (division)); } static void parse_event() { int i = 0; int tick = 0; int ctick = 0; int tempo = 500000; unsigned long ts, tu; struct timeval tp; gettimeofday(&tp, NULL); ts = tp.tv_sec; tu = tp.tv_usec; while(TRUE) { /* pause / stop check */ if (cmd_stop) { break; } if (cmd_pause) { gettimeofday(&tp, NULL); ts = tp.tv_sec; tu = tp.tv_usec; usleep(50*1000); continue; } while(tick == midi->event[i].ctime) { /* delta wait */ if (ctick != midi->event[i].ctime) { int delta, stime; delta = midi->event[i].ctime - ctick; stime = ticks2usec(delta, midi->division, tempo) - myflush(); if (stime > 0) { long j; struct timespec ttmp, *req; req = &ttmp; req->tv_sec = (time_t)stime / 1000000; req->tv_nsec = (long)(stime % 1000000) * 1000; nanosleep(req, NULL); gettimeofday(&tp, NULL); j = (tp.tv_sec * 1000000 + tp.tv_usec) - (ts * 1000000 + tu); if (j != stime) { extratime += (j - stime); } } else { gettimeofday(&tp, NULL); extratime += -stime; } ts = tp.tv_sec; tu = tp.tv_usec; ctick = midi->event[i].ctime; } if (midi->event[i].type == 0) { /* ordinary data */ mywrite(midi->event[i].data, midi->event[i].n, midi->event[i].port); i++; } else if (midi->event[i].type == 2) { /* tempo change */ int *p; p = (int*)midi->event[i].data; tempo = (unsigned int)*p; i++; } else if (midi->event[i].type == 3) { /* system35 maker */ int vn1 = midi->event[i+1].data[2]; int vn2 = midi->event[i+2].data[2]; int vn3 = midi->event[i+3].data[2]; switch(vn1) { case 0: /* set label */ i += 6; break; case 1: /* jump */ mywrite(all_noteoff, ALL_NOTEOFF_SZ, 0); i = midi->sys35_label[vn2]; ctick = tick = midi->event[i].ctime; break; case 2: /* set flag */ flags->midi_flag[vn2] = vn3; i += 6; break; case 3: /* flag jump */ if (flags->midi_flag[vn2] == 1) { i = midi->sys35_label[vn3]; ctick = tick = midi->event[i].ctime; mywrite(all_noteoff, ALL_NOTEOFF_SZ, 0); } else { i += 6; } break; case 4: /* set variable */ flags->midi_variable[vn2] = vn3; i += 6; break; case 5: /* variable jump */ if (--(flags->midi_variable[vn2]) == 0) { i = midi->sys35_label[vn3]; ctick = tick = midi->event[i].ctime; mywrite(all_noteoff, ALL_NOTEOFF_SZ, 0); } else { i += 6; } break; } } else { WARNING("Unknown type of event %x (NEVER!).\n", midi->event[i].type); } if (i >= midi->eventsize) return; } tick++; } } static int midi_initilize(char *devnm, int subdev) { enabled = FALSE; reset_counter_high(SYSTEMCOUNTER_MIDI, 10, 0); if (devnm == NULL) { if (subdev == -1) { mididevname = MIDI_DEVICE; } else { mididevname = SEQ_DEVICE; } } else { mididevname = devnm; } if (0 > (shmkey = shmget(IPC_PRIVATE, sizeof(midiflag_t), IPC_CREAT | 0600))) { perror("shmget"); return NG; } if ((void *)-1 == (flags = (midiflag_t *)shmat(shmkey, 0, 0))) { perror("shmat"); return NG; } memset(flags, 0, sizeof(midiflag_t)); if (subdev == -1) { #ifdef ENABLE_MIDI_RAWMIDI myflush = raw_myflush; mywrite = raw_mywrite; NOTICE("RAWMIDI Initilize OK\n"); #endif } else { #ifdef ENABLE_MIDI_SEQMIDI myflush = seq_myflush; mywrite = seq_mywrite; midi_subdev = subdev; NOTICE("SEQMIDI Initilize OK\n"); #endif } enabled = TRUE; return OK; } static int midi_exit() { if (enabled) { midi_stop(); if (0 > shmdt((char *)flags)) { perror("shmdt"); } if (0 > shmctl(shmkey, IPC_RMID, 0)) { perror("shmctl"); } } return OK; } /* no = 0~ */ static int midi_start(int no, char *data, int datalen) { pid_t pid; pid = fork(); if (pid == 0) { #ifdef QUITE_MIDI close(1); #endif sys_set_signalhandler(SIGTERM, SIG_DFL); if (NULL == (midi = mf_read_midifile(data, datalen))) { _exit(-1); } if (0 > (midifd = open(mididevname, O_RDWR))) { perror("open"); _exit(-1); } #ifdef ENABLE_SEQMIDI { int nrsynths; if (-1 == ioctl(mididev, SNDCTL_SEQ_NRSYNTHS, &nrsynths)) { perror("SNDCTL_SEQ_NRSYNTHS"); _exit(-1); }} #endif send_reset(); sys_set_signalhandler(SIGUSR1, signal_pause); /* pause */ sys_set_signalhandler(SIGUSR2, signal_pause); /* unpause */ sys_set_signalhandler(SIGTERM, signal_pause); /* stop */ /* parse */ parse_event(); send_reset(); mf_remove_midifile(midi); _exit(0); } midino = no; midipid = pid; counter = get_high_counter(SYSTEMCOUNTER_MIDI); return OK; } static int midi_stop() { int status=0; if (!enabled || midipid == 0) { return OK; } kill(midipid, SIGTERM); while(0 >= waitpid(midipid, &status, WNOHANG)); midipid = 0; midino = 0; return OK; } static int midi_pause(void) { if (!enabled) return OK; kill(midipid, SIGUSR1); return OK; } static int midi_unpause(void) { if (!enabled) return OK; kill(midipid, SIGUSR2); return OK; } static int midi_get_playing_info(midiplaystate *st) { int status, cnt, err; if (!enabled || midino == 0) { st->in_play = FALSE; st->play_no = 0; st->loc_ms = 0; return OK; } if (midipid == (err = waitpid(midipid, &status, WNOHANG))) { st->in_play = FALSE; st->play_no = 0; st->loc_ms = 0; midipid = 0; return OK; } cnt = get_high_counter(SYSTEMCOUNTER_MIDI) - counter; st->in_play = TRUE; st->play_no = midino; st->loc_ms = cnt * 10; return OK; } static int midi_getflag(int mode, int index) { if (mode == 0) { /* flag */ return flags->midi_flag[index]; } else { /* variable */ return flags->midi_variable[index]; } } static int midi_setflag(int mode, int index, int val) { if (mode == 0) { /* flag */ flags->midi_flag[index] = val; } else { /* variable */ flags->midi_variable[index] = val; } return OK; }