1351 lines
33 KiB
C
1351 lines
33 KiB
C
/*
|
|
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
|
|
*/
|
|
|
|
char *usage = "\n\
|
|
midicomp v0.0.1 20031129 markc@renta.net \n\
|
|
\n\
|
|
http://alsa.opensrc.org/midicomp/ \n\
|
|
\n\
|
|
Command line argument usage: \n\
|
|
\n\
|
|
-d --debug send any debug output to stderr \n\
|
|
-v --verbose output in columns with notes on \n\
|
|
-c --compile compile ascii input into SMF \n\
|
|
-n --note note on/off value as note|octave \n\
|
|
-t --time use absolute time instead of ticks \n\
|
|
-fN --fold=N fold sysex data at N columns \n\
|
|
\n\
|
|
To translate a SMF file to plain ascii format: \n\
|
|
\n\
|
|
midicomp some.mid # to view as plain text \n\
|
|
midicomp some.mid > some.asc # to create a text version \n\
|
|
\n\
|
|
To translate a plain ascii formatted file to SMF: \n\
|
|
\n\
|
|
midicomp -c some.asc some.mid # input and output filenames \n\
|
|
midicomp -c some.mid < some.asc # input from stdin with one arg \n\
|
|
\n\
|
|
midicomp some.mid | somefilter | midicomp -c some2.mid \n";
|
|
|
|
#include <malloc.h>
|
|
#include <setjmp.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <getopt.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "midicomp.h"
|
|
|
|
int main(int argc,char **argv) {
|
|
|
|
FILE *efopen();
|
|
Mf_nomerge = 1;
|
|
opterr = 0;
|
|
int compile = 0;
|
|
int c;
|
|
|
|
struct option long_options[] = {
|
|
{"debug", no_argument, 0, 'd'},
|
|
{"verbose", no_argument, 0, 'v'},
|
|
{"compile", no_argument, 0, 'c'},
|
|
{"note", no_argument, 0, 'n'},
|
|
{"time", no_argument, 0, 't'},
|
|
{"fold", required_argument, 0, 'f'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
int option_index = 0;
|
|
|
|
while ((c = getopt_long(argc,argv,"dvcntf:",long_options,&option_index)) != -1) {
|
|
switch (c) {
|
|
case 0:
|
|
if (long_options[option_index].flag != 0)
|
|
break;
|
|
case 'd':
|
|
dbg++;
|
|
break;
|
|
case 'f':
|
|
fold = atoi(optarg);
|
|
fprintf(stderr,"fold=%d\n",fold);
|
|
break;
|
|
case 'm':
|
|
Mf_nomerge = 0;
|
|
break;
|
|
case 'n':
|
|
notes++;
|
|
break;
|
|
case 't':
|
|
times++;
|
|
break;
|
|
case 'c':
|
|
compile++;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
notes++;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
fprintf(stderr,"%s\n",usage);
|
|
return 1;
|
|
}
|
|
}
|
|
if (dbg) fprintf(stderr,"main()\n");
|
|
|
|
TrkNr = 0;
|
|
Measure = 4;
|
|
Beat = 96;
|
|
Clicks = 96;
|
|
M0 = 0;
|
|
T0 = 0;
|
|
|
|
if (compile) {
|
|
char *infile;
|
|
char *outfile;
|
|
|
|
if (optind < argc) {
|
|
yyin = efopen (argv[optind], "r");
|
|
infile = argv[optind];
|
|
} else {
|
|
yyin = stdin;
|
|
infile = "stdin";
|
|
}
|
|
if (optind+1 < argc ) {
|
|
F = efopen(argv[optind+1],"wb");
|
|
outfile = argv[optind+1];
|
|
} else {
|
|
#ifdef SETMODE
|
|
setmode (fileno(stdout), O_BINARY);
|
|
F = stdout;
|
|
#else
|
|
F = fdopen (fileno(stdout), "wb");
|
|
#endif
|
|
outfile = "stdout";
|
|
}
|
|
if (dbg) fprintf(stderr,"Compiling %s to %s\n",infile,outfile);
|
|
|
|
Mf_putc = fileputc;
|
|
Mf_wtrack = mywritetrack;
|
|
translate();
|
|
fclose(F);
|
|
fclose(yyin);
|
|
} else {
|
|
if (verbose) {
|
|
Onmsg = "On ch=%-2d note=%-3s vol=%-3d\n";
|
|
Offmsg = "Off ch=%-2d note=%-3s vol=%-3d\n";
|
|
PoPrmsg = "PolyPr ch=%-2d note=%-3s val=%-3d\n";
|
|
Parmsg = "Param ch=%-2d con=%-3d val=%-3d\n";
|
|
Pbmsg = "Pb ch=%-2d val=%-3d\n";
|
|
PrChmsg = "ProgCh ch=%-2d prog=%-3d\n";
|
|
ChPrmsg = "ChanPr ch=%-2d val=%-3d\n";
|
|
}
|
|
if (optind < argc)
|
|
F = efopen(argv[optind],"rb");
|
|
else
|
|
F = fdopen(fileno(stdin),"rb");
|
|
|
|
initfuncs();
|
|
Mf_getc = filegetc;
|
|
mfread();
|
|
if (ferror(F)) error ("Output file error");
|
|
fclose(F);
|
|
}
|
|
}
|
|
|
|
mfread() {
|
|
|
|
if (Mf_getc == NULLFUNC)
|
|
mferror("mfread() called without setting Mf_getc");
|
|
|
|
readheader();
|
|
while(readtrack()) ;
|
|
}
|
|
|
|
static readmt(char *s) {
|
|
|
|
int n = 0;
|
|
char *p = s;
|
|
int c;
|
|
|
|
while ( n++<4 && (c=(*Mf_getc)()) != EOF ) {
|
|
if ( c != *p++ ) {
|
|
char buff[32];
|
|
(void) strcpy(buff,"expecting ");
|
|
(void) strcat(buff,s);
|
|
mferror(buff);
|
|
}
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
static egetc() {
|
|
|
|
int c = (*Mf_getc)();
|
|
|
|
if ( c == EOF )
|
|
mferror("premature EOF");
|
|
Mf_toberead--;
|
|
return(c);
|
|
}
|
|
|
|
static void readheader() {
|
|
|
|
int format, ntrks, division;
|
|
|
|
if (readmt("MThd") == EOF) return;
|
|
Mf_toberead = read32bit();
|
|
format = read16bit();
|
|
ntrks = read16bit();
|
|
division = read16bit();
|
|
if (Mf_header) (*Mf_header)(format,ntrks,division);
|
|
while(Mf_toberead > 0) (void) egetc();
|
|
}
|
|
|
|
static readtrack() {
|
|
|
|
long lookfor;
|
|
int c, c1, type;
|
|
int sysexcontinue = 0;
|
|
int running = 0;
|
|
int status = 0;
|
|
int needed;
|
|
static int chantype[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
2, 2, 2, 2, 1, 1, 2, 0
|
|
};
|
|
|
|
if (readmt("MTrk") == EOF) return(0);
|
|
Mf_toberead = read32bit();
|
|
Mf_currtime = 0;
|
|
if (Mf_starttrack) (*Mf_starttrack)();
|
|
|
|
while (Mf_toberead > 0) {
|
|
Mf_currtime += readvarinum();
|
|
c = egetc();
|
|
if (sysexcontinue && c != 0xf7)
|
|
mferror("didn't find expected continuation of a sysex");
|
|
if ((c & 0x80) == 0) {
|
|
if (status == 0) mferror("unexpected running status");
|
|
running = 1;
|
|
c1 = c;
|
|
c = status;
|
|
} else if (c < 0xf0) {
|
|
status = c;
|
|
running = 0;
|
|
}
|
|
needed = chantype[ (c>>4) & 0xf ];
|
|
if (needed) {
|
|
if (!running) c1 = egetc();
|
|
chanmessage(status,c1,(needed>1) ? egetc() : 0 );
|
|
continue;
|
|
}
|
|
|
|
switch(c) {
|
|
case 0xff:
|
|
type = egetc();
|
|
lookfor = Mf_toberead - readvarinum();
|
|
msginit();
|
|
while(Mf_toberead >= lookfor) msgadd(egetc());
|
|
metaevent(type);
|
|
break;
|
|
case 0xf0:
|
|
lookfor = Mf_toberead - readvarinum();
|
|
msginit();
|
|
msgadd(0xf0);
|
|
while(Mf_toberead > lookfor) msgadd(c=egetc());
|
|
if (c == 0xf7 || Mf_nomerge == 0)
|
|
sysex();
|
|
else
|
|
sysexcontinue = 1;
|
|
break;
|
|
case 0xf7:
|
|
lookfor = Mf_toberead - readvarinum();
|
|
if (! sysexcontinue) msginit();
|
|
while (Mf_toberead > lookfor) msgadd(c=egetc());
|
|
if (! sysexcontinue) {
|
|
if (Mf_arbitrary) (*Mf_arbitrary)(msgleng(),msg());
|
|
} else if (c == 0xf7) {
|
|
sysex();
|
|
sysexcontinue = 0;
|
|
}
|
|
break;
|
|
default:
|
|
badbyte(c);
|
|
break;
|
|
}
|
|
}
|
|
if ( Mf_endtrack ) (*Mf_endtrack)();
|
|
return(1);
|
|
}
|
|
|
|
static badbyte(int c) {
|
|
|
|
char buff[32];
|
|
|
|
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
|
mferror(buff);
|
|
}
|
|
|
|
static metaevent(type) {
|
|
|
|
int leng = msgleng();
|
|
char *m = msg();
|
|
|
|
switch (type) {
|
|
case 0x00:
|
|
if (Mf_seqnum) (*Mf_seqnum)(to16bit(m[0],m[1]));
|
|
break;
|
|
case 0x01: /* Text event */
|
|
case 0x02: /* Copyright notice */
|
|
case 0x03: /* Sequence/Track name */
|
|
case 0x04: /* Instrument name */
|
|
case 0x05: /* Lyric */
|
|
case 0x06: /* Marker */
|
|
case 0x07: /* Cue point */
|
|
case 0x08:
|
|
case 0x09:
|
|
case 0x0a:
|
|
case 0x0b:
|
|
case 0x0c:
|
|
case 0x0d:
|
|
case 0x0e:
|
|
case 0x0f:
|
|
if (Mf_text) (*Mf_text)(type,leng,m);
|
|
break;
|
|
case 0x2f:
|
|
if (Mf_eot) (*Mf_eot)();
|
|
break;
|
|
case 0x51:
|
|
if (Mf_tempo) (*Mf_tempo)(to32bit(0,m[0],m[1],m[2]));
|
|
break;
|
|
case 0x54:
|
|
if (Mf_smpte)
|
|
(*Mf_smpte)(m[0],m[1],m[2],m[3],m[4]);
|
|
break;
|
|
case 0x58:
|
|
if (Mf_timesig)
|
|
(*Mf_timesig)(m[0],m[1],m[2],m[3]);
|
|
break;
|
|
case 0x59:
|
|
if (Mf_keysig) (*Mf_keysig)(m[0],m[1]);
|
|
break;
|
|
case 0x7f:
|
|
if (Mf_sqspecific) (*Mf_sqspecific)(leng,m);
|
|
break;
|
|
default:
|
|
if (Mf_metamisc) (*Mf_metamisc)(type,leng,m);
|
|
}
|
|
}
|
|
|
|
static sysex() {
|
|
|
|
if (Mf_sysex) (*Mf_sysex)(msgleng(),msg());
|
|
}
|
|
|
|
static chanmessage(int status,int c1,int c2) {
|
|
|
|
int chan = status & 0xf;
|
|
|
|
switch(status & 0xf0) {
|
|
case 0x80: if (Mf_off) (*Mf_off)(chan,c1,c2); break;
|
|
case 0x90: if (Mf_on) (*Mf_on)(chan,c1,c2); break;
|
|
case 0xa0: if (Mf_pressure) (*Mf_pressure)(chan,c1,c2); break;
|
|
case 0xb0: if (Mf_parameter) (*Mf_parameter)(chan,c1,c2); break;
|
|
case 0xe0: if (Mf_pitchbend) (*Mf_pitchbend)(chan,c1,c2); break;
|
|
case 0xc0: if (Mf_program) (*Mf_program)(chan,c1); break;
|
|
case 0xd0: if (Mf_chanpressure) (*Mf_chanpressure)(chan,c1); break;
|
|
}
|
|
}
|
|
|
|
static long readvarinum() {
|
|
|
|
long value;
|
|
int c;
|
|
|
|
c = egetc();
|
|
value = c;
|
|
if (c & 0x80) {
|
|
value &= 0x7f;
|
|
do {
|
|
c = egetc();
|
|
value = (value << 7) + (c & 0x7f);
|
|
} while (c & 0x80);
|
|
}
|
|
return (value);
|
|
}
|
|
|
|
static long to32bit(c1,c2,c3,c4) {
|
|
|
|
long value = 0L;
|
|
|
|
value = (c1 & 0xff);
|
|
value = (value<<8) + (c2 & 0xff);
|
|
value = (value<<8) + (c3 & 0xff);
|
|
value = (value<<8) + (c4 & 0xff);
|
|
return (value);
|
|
}
|
|
|
|
static to16bit(int c1,int c2) {
|
|
|
|
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
|
}
|
|
|
|
static long read32bit() {
|
|
|
|
int c1, c2, c3, c4;
|
|
|
|
c1 = egetc();
|
|
c2 = egetc();
|
|
c3 = egetc();
|
|
c4 = egetc();
|
|
return to32bit(c1,c2,c3,c4);
|
|
}
|
|
|
|
static read16bit() {
|
|
|
|
int c1, c2;
|
|
c1 = egetc();
|
|
c2 = egetc();
|
|
return to16bit(c1,c2);
|
|
}
|
|
|
|
mferror(char *s) {
|
|
|
|
if (Mf_error) (*Mf_error)(s);
|
|
exit(1);
|
|
}
|
|
|
|
static msginit() {
|
|
|
|
Msgindex = 0;
|
|
}
|
|
|
|
static char * msg() {
|
|
|
|
return(Msgbuff);
|
|
}
|
|
|
|
static msgleng() {
|
|
|
|
return(Msgindex);
|
|
}
|
|
|
|
static msgadd(int c) {
|
|
|
|
if (Msgindex >= Msgsize) biggermsg();
|
|
Msgbuff[Msgindex++] = c;
|
|
}
|
|
|
|
static biggermsg() {
|
|
|
|
char *newmess;
|
|
char *oldmess = Msgbuff;
|
|
int oldleng = Msgsize;
|
|
|
|
Msgsize += MSGINCREMENT;
|
|
newmess = (char *) malloc((unsigned)(sizeof(char)*Msgsize));
|
|
if (newmess == NULL) mferror("malloc error!");
|
|
if (oldmess != NULL) {
|
|
register char *p = newmess;
|
|
register char *q = oldmess;
|
|
register char *endq = &oldmess[oldleng];
|
|
for( ; q!=endq ; p++,q++ ) *p = *q;
|
|
free(oldmess);
|
|
}
|
|
Msgbuff = newmess;
|
|
}
|
|
|
|
void mfwrite(int format,int ntracks,int division,FILE *fp) {
|
|
|
|
int i;
|
|
void mf_w_track_chunk(),mf_w_header_chunk();
|
|
|
|
if (Mf_putc == NULLFUNC)
|
|
mferror("mfmf_write() called without setting Mf_putc");
|
|
if (Mf_wtrack == NULLFUNC)
|
|
mferror("mfmf_write() called without setting Mf_mf_writetrack");
|
|
mf_w_header_chunk(format,ntracks,division);
|
|
if (format == 1 && ( Mf_wtempotrack )) {
|
|
mf_w_track_chunk(-1,fp,Mf_wtempotrack);
|
|
ntracks--;
|
|
}
|
|
for(i = 0; i < ntracks; i++)
|
|
mf_w_track_chunk(i,fp,Mf_wtrack);
|
|
}
|
|
|
|
void mf_w_track_chunk(which_track,fp,wtrack)
|
|
int which_track;
|
|
FILE *fp;
|
|
int (*wtrack)();
|
|
{
|
|
unsigned long trkhdr,trklength;
|
|
long offset, place_marker;
|
|
void write16bit(),write32bit();
|
|
|
|
trkhdr = MTrk;
|
|
trklength = 0;
|
|
|
|
offset = ftell(fp);
|
|
write32bit(trkhdr);
|
|
write32bit(trklength);
|
|
|
|
Mf_numbyteswritten = 0L;
|
|
laststat = 0;
|
|
(*wtrack)(which_track);
|
|
|
|
if (laststat != meta_event || lastmeta != end_of_track) {
|
|
eputc(0);
|
|
eputc(meta_event);
|
|
eputc(end_of_track);
|
|
eputc(0);
|
|
}
|
|
|
|
laststat = 0;
|
|
place_marker = ftell(fp);
|
|
if (fseek(fp,offset,0) < 0)
|
|
mferror("error seeking during final stage of write");
|
|
trklength = Mf_numbyteswritten;
|
|
write32bit(trkhdr);
|
|
write32bit(trklength);
|
|
fseek(fp,place_marker,0);
|
|
}
|
|
|
|
void mf_w_header_chunk(int format,int ntracks,int division) {
|
|
|
|
unsigned long ident,length;
|
|
void write16bit(),write32bit();
|
|
|
|
ident = MThd;
|
|
length = 6;
|
|
write32bit(ident);
|
|
write32bit(length);
|
|
write16bit(format);
|
|
write16bit(ntracks);
|
|
write16bit(division);
|
|
}
|
|
|
|
int mf_w_midi_event(unsigned long delta_time,unsigned int type,
|
|
unsigned int chan,unsigned char *data,unsigned long size) {
|
|
|
|
int i;
|
|
unsigned char c;
|
|
|
|
WriteVarLen(delta_time);
|
|
c = type | chan;
|
|
|
|
if(chan > 15)
|
|
perror("error: MIDI channel greater than 16\n");
|
|
if (!Mf_RunStat || laststat != c)
|
|
eputc(c);
|
|
laststat = c;
|
|
for(i = 0; i < size; i++)
|
|
eputc(data[i]);
|
|
|
|
return(size);
|
|
}
|
|
|
|
int mf_w_meta_event(unsigned long delta_time,
|
|
unsigned int type,unsigned char *data,unsigned long size) {
|
|
|
|
int i;
|
|
|
|
WriteVarLen(delta_time);
|
|
eputc(meta_event);
|
|
laststat = meta_event;
|
|
eputc(type);
|
|
lastmeta = type;
|
|
WriteVarLen(size);
|
|
for(i = 0; i < size; i++) {
|
|
if (eputc(data[i]) != data[i]) return(-1);
|
|
}
|
|
return(size);
|
|
}
|
|
|
|
int mf_w_sysex_event(unsigned long delta_time,
|
|
unsigned char *data,unsigned long size) {
|
|
|
|
int i;
|
|
|
|
WriteVarLen(delta_time);
|
|
eputc(*data);
|
|
laststat = 0;
|
|
WriteVarLen(size-1);
|
|
for(i = 1; i < size; i++) {
|
|
if(eputc(data[i]) != data[i]) return(-1);
|
|
}
|
|
return(size);
|
|
}
|
|
|
|
void mf_w_tempo(unsigned long delta_time,unsigned long tempo) {
|
|
|
|
WriteVarLen(delta_time);
|
|
eputc(meta_event);
|
|
laststat = meta_event;
|
|
eputc(set_tempo);
|
|
eputc(3);
|
|
eputc((unsigned)(0xff & (tempo >> 16)));
|
|
eputc((unsigned)(0xff & (tempo >> 8)));
|
|
eputc((unsigned)(0xff & tempo));
|
|
}
|
|
|
|
unsigned long mf_sec2ticks(float secs,int division,unsigned int tempo) {
|
|
|
|
return (long)(((secs * 1000.0) / 4.0 * division) / tempo);
|
|
}
|
|
|
|
void WriteVarLen(unsigned long value) {
|
|
|
|
unsigned long buffer;
|
|
|
|
buffer = value & 0x7f;
|
|
while((value >>= 7) > 0) {
|
|
buffer <<= 8;
|
|
buffer |= 0x80;
|
|
buffer += (value & 0x7f);
|
|
}
|
|
while(1) {
|
|
eputc((unsigned)(buffer & 0xff));
|
|
if (buffer & 0x80)
|
|
buffer >>= 8;
|
|
else
|
|
return;
|
|
}
|
|
}
|
|
|
|
float mf_ticks2sec(int ticks,unsigned int division,unsigned long tempo) {
|
|
|
|
float smpte_format,smpte_resolution;
|
|
|
|
if (division > 0) {
|
|
return((float)(((float) (ticks)*(float)(tempo))/
|
|
((float)(division)*1000000.0)));
|
|
} else {
|
|
smpte_format = upperbyte(division);
|
|
smpte_resolution = lowerbyte(division);
|
|
return(float)((float) ticks/(smpte_format*smpte_resolution*1000000.0));
|
|
}
|
|
}
|
|
|
|
void write32bit(unsigned long data) {
|
|
|
|
eputc((unsigned)((data >> 24) & 0xff));
|
|
eputc((unsigned)((data >> 16) & 0xff));
|
|
eputc((unsigned)((data >> 8 ) & 0xff));
|
|
eputc((unsigned)(data & 0xff));
|
|
}
|
|
|
|
void write16bit(int data) {
|
|
|
|
eputc((unsigned)((data & 0xff00) >> 8));
|
|
eputc((unsigned)(data & 0xff));
|
|
}
|
|
|
|
int eputc(unsigned char c) {
|
|
|
|
int return_val;
|
|
|
|
if ((Mf_putc) == NULLFUNC) {
|
|
mferror("Mf_putc undefined");
|
|
return(-1);
|
|
}
|
|
|
|
return_val = (*Mf_putc)(c);
|
|
|
|
if (return_val == EOF) mferror("error writing");
|
|
Mf_numbyteswritten++;
|
|
return(return_val);
|
|
}
|
|
|
|
char *mknote(int pitch) {
|
|
|
|
static char * Notes [] =
|
|
{"c","c#","d","d#","e","f","f#","g","g#","a","a#","b"};
|
|
static char buf[5];
|
|
if ( notes )
|
|
sprintf (buf,"%s%d",Notes[pitch % 12], pitch/12);
|
|
else
|
|
sprintf (buf,"%d",pitch);
|
|
return buf;
|
|
}
|
|
|
|
myheader(int format,int ntrks,int division) {
|
|
|
|
if (division & 0x8000) {
|
|
times = 0;
|
|
printf("MFile %d %d %d %d\n",
|
|
format,ntrks,-((-(division>>8))&0xff),division&0xff);
|
|
} else {
|
|
printf("MFile %d %d %d\n",format,ntrks,division);
|
|
}
|
|
if (format > 2) {
|
|
fprintf(stderr, "Can't deal with format %d files\n",format);
|
|
exit (1);
|
|
}
|
|
Beat = Clicks = division;
|
|
TrksToDo = ntrks;
|
|
}
|
|
|
|
mytrstart() {
|
|
|
|
printf("MTrk\n");
|
|
TrkNr ++;
|
|
}
|
|
|
|
mytrend() {
|
|
|
|
printf("TrkEnd\n");
|
|
--TrksToDo;
|
|
}
|
|
|
|
mynon(int chan,int pitch,int vol) {
|
|
|
|
prtime();
|
|
printf(Onmsg,chan+1,mknote(pitch),vol);
|
|
}
|
|
|
|
mynoff(int chan,int pitch,int vol) {
|
|
|
|
prtime();
|
|
printf(Offmsg,chan+1,mknote(pitch),vol);
|
|
}
|
|
|
|
mypressure(int chan,int pitch,int press) {
|
|
|
|
prtime();
|
|
printf(PoPrmsg,chan+1,mknote(pitch),press);
|
|
}
|
|
|
|
myparameter(int chan,int control,int value) {
|
|
|
|
prtime();
|
|
printf(Parmsg,chan+1,control,value);
|
|
}
|
|
|
|
mypitchbend(int chan,int lsb,int msb) {
|
|
|
|
prtime();
|
|
printf(Pbmsg,chan+1,128*msb+lsb);
|
|
}
|
|
|
|
myprogram(int chan,int program) {
|
|
|
|
prtime();
|
|
printf(PrChmsg,chan+1,program);
|
|
}
|
|
|
|
mychanpressure(int chan,int press) {
|
|
|
|
prtime();
|
|
printf(ChPrmsg,chan+1,press);
|
|
}
|
|
|
|
mysysex(int leng,char *mess) {
|
|
|
|
prtime();
|
|
printf("SysEx");
|
|
prhex (mess, leng);
|
|
}
|
|
|
|
mymmisc(int type,int leng,char *mess) {
|
|
|
|
prtime();
|
|
printf("Meta 0x%02x",type);
|
|
prhex(mess, leng);
|
|
}
|
|
|
|
mymspecial(int leng,char *mess) {
|
|
|
|
prtime();
|
|
printf("SeqSpec");
|
|
prhex(mess, leng);
|
|
}
|
|
|
|
mymtext(int type,int leng,char *mess) {
|
|
|
|
static char *ttype[] = {
|
|
NULL,
|
|
"Text","Copyright","TrkName","InstrName","Lyric","Marker","Cue","Unrec"
|
|
};
|
|
int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
|
|
prtime();
|
|
if (type < 1 || type > unrecognized)
|
|
printf("Meta 0x%02x ",type);
|
|
else if (type == 3 && TrkNr == 1)
|
|
printf("Meta SeqName ");
|
|
else
|
|
printf("Meta %s ",ttype[type]);
|
|
prtext (mess, leng);
|
|
}
|
|
|
|
mymseq(int num) {
|
|
|
|
prtime();
|
|
printf("SeqNr %d\n",num);
|
|
}
|
|
|
|
mymeot() {
|
|
|
|
prtime();
|
|
printf("Meta TrkEnd\n");
|
|
}
|
|
|
|
mykeysig(int sf,int mi) {
|
|
|
|
prtime();
|
|
printf("KeySig %d %s\n",(sf>127?sf-256:sf),(mi?"minor":"major"));
|
|
}
|
|
|
|
mytempo(long tempo) {
|
|
|
|
prtime();
|
|
printf("Tempo %ld\n",tempo);
|
|
}
|
|
|
|
mytimesig(int nn,int dd,int cc,int bb) {
|
|
|
|
int denom = 1;
|
|
|
|
while (dd-- > 0) denom *= 2;
|
|
prtime();
|
|
printf("TimeSig %d/%d %d %d\n",nn,denom,cc,bb);
|
|
M0 += (Mf_currtime-T0)/(Beat*Measure);
|
|
T0 = Mf_currtime;
|
|
Measure = nn;
|
|
Beat = 4 * Clicks / denom;
|
|
}
|
|
|
|
mysmpte(int hr,int mn,int se,int fr,int ff) {
|
|
|
|
prtime();
|
|
printf("SMPTE %d %d %d %d %d\n",hr,mn,se,fr,ff);
|
|
}
|
|
|
|
myarbitrary(int leng,char *mess) {
|
|
|
|
prtime();
|
|
printf("Arb",leng);
|
|
prhex(mess, leng);
|
|
}
|
|
|
|
prtime() {
|
|
|
|
if (times) {
|
|
long m = (Mf_currtime-T0)/Beat;
|
|
if (verbose)
|
|
printf("%03ld:%02ld:%03ld ",
|
|
m/Measure+M0,m%Measure,(Mf_currtime-T0)%Beat);
|
|
else
|
|
printf("%ld:%ld:%ld ",
|
|
m/Measure+M0,m%Measure,(Mf_currtime-T0)%Beat);
|
|
} else {
|
|
if (verbose)
|
|
printf("%-10ld ",Mf_currtime);
|
|
else
|
|
printf("%ld ",Mf_currtime);
|
|
}
|
|
}
|
|
|
|
prtext(unsigned char *p,int leng) {
|
|
|
|
int n, c;
|
|
int pos = 25;
|
|
|
|
printf("\"");
|
|
for ( n=0; n<leng; n++ ) {
|
|
c = *p++;
|
|
if (fold && pos >= fold) {
|
|
printf ("\\\n\t");
|
|
pos = 13; /* tab + \xab + \ */
|
|
if (c == ' ' || c == '\t') {
|
|
putchar ('\\');
|
|
++pos;
|
|
}
|
|
}
|
|
switch (c) {
|
|
case '\\':
|
|
case '"':
|
|
printf ("\\%c", c);
|
|
pos += 2;
|
|
break;
|
|
case '\r':
|
|
printf ("\\r");
|
|
pos += 2;
|
|
break;
|
|
case '\n':
|
|
printf ("\\n");
|
|
pos += 2;
|
|
break;
|
|
case '\0':
|
|
printf ("\\0");
|
|
pos += 2;
|
|
break;
|
|
default:
|
|
if (isprint(c)) {
|
|
putchar(c);
|
|
++pos;
|
|
} else {
|
|
printf("\\x%02x" , c);
|
|
pos += 4;
|
|
}
|
|
}
|
|
}
|
|
printf("\"\n");
|
|
}
|
|
|
|
prhex(unsigned char *p,int leng) {
|
|
|
|
int n;
|
|
int pos = 25;
|
|
|
|
for(n=0; n<leng; n++,p++) {
|
|
if (fold && pos >= fold) {
|
|
printf ("\\\n\t%02x",*p);
|
|
pos = 14;
|
|
} else {
|
|
printf(" %02x",*p);
|
|
pos += 3;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
myerror(char *s) {
|
|
|
|
if (TrksToDo <= 0)
|
|
fprintf(stderr,"Error: Garbage at end\n",s);
|
|
else
|
|
fprintf(stderr,"Error: %s\n",s);
|
|
}
|
|
|
|
initfuncs() {
|
|
|
|
Mf_error = myerror;
|
|
Mf_header = myheader;
|
|
Mf_starttrack = mytrstart;
|
|
Mf_endtrack = mytrend;
|
|
Mf_on = mynon;
|
|
Mf_off = mynoff;
|
|
Mf_pressure = mypressure;
|
|
Mf_parameter = myparameter;
|
|
Mf_pitchbend = mypitchbend;
|
|
Mf_program = myprogram;
|
|
Mf_chanpressure = mychanpressure;
|
|
Mf_sysex = mysysex;
|
|
Mf_metamisc = mymmisc;
|
|
Mf_seqnum = mymseq;
|
|
Mf_eot = mymeot;
|
|
Mf_timesig = mytimesig;
|
|
Mf_smpte = mysmpte;
|
|
Mf_tempo = mytempo;
|
|
Mf_keysig = mykeysig;
|
|
Mf_sqspecific = mymspecial;
|
|
Mf_text = mymtext;
|
|
Mf_arbitrary = myarbitrary;
|
|
}
|
|
|
|
prs_error(char *s) {
|
|
|
|
int c;
|
|
int count;
|
|
int ln = (eol_seen? lineno-1 : lineno);
|
|
fprintf (stderr, "%d: %s\n", ln, s);
|
|
if (yyleng > 0 && *yytext != '\n')
|
|
fprintf (stderr, "*** %*s ***\n", yyleng, yytext);
|
|
count = 0;
|
|
while (count < 100 &&
|
|
(c=yylex()) != EOL && c != EOF) count++/* skip rest of line */;
|
|
if (c == EOF) exit(1);
|
|
if (err_cont)
|
|
longjmp (erjump,1);
|
|
}
|
|
|
|
syntax() {
|
|
|
|
prs_error("Syntax error");
|
|
}
|
|
|
|
translate() {
|
|
|
|
if (yylex() == MTHD) {
|
|
Format = getint("MFile format");
|
|
Ntrks = getint("MFile #tracks");
|
|
Clicks = getint("MFile Clicks");
|
|
if (Clicks < 0)
|
|
Clicks = (Clicks&0xff)<<8|getint("MFile SMPTE division");
|
|
checkeol();
|
|
mfwrite(Format, Ntrks, Clicks, F);
|
|
} else {
|
|
fprintf (stderr, "Missing MFile - can't continue\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int mywritetrack() {
|
|
|
|
int opcode, c;
|
|
long currtime = 0;
|
|
long newtime, delta;
|
|
int i, k;
|
|
|
|
while ((opcode = yylex()) == EOL) ;
|
|
if (opcode != MTRK) prs_error("Missing MTrk");
|
|
checkeol();
|
|
while(1) {
|
|
err_cont = 1;
|
|
setjmp (erjump);
|
|
switch(yylex()) {
|
|
case MTRK:
|
|
prs_error("Unexpected MTrk");
|
|
case EOF:
|
|
err_cont = 0;
|
|
error("Unexpected EOF");
|
|
return -1;
|
|
case TRKEND:
|
|
err_cont = 0;
|
|
checkeol();
|
|
return 1;
|
|
case INT:
|
|
newtime = yyval;
|
|
if ((opcode=yylex())=='/') {
|
|
if (yylex()!=INT) prs_error("Illegal time value");
|
|
newtime = (newtime-M0)*Measure+yyval;
|
|
if (yylex()!='/'||yylex()!=INT) prs_error("Illegal time value");
|
|
newtime = T0 + newtime*Beat + yyval;
|
|
opcode = yylex();
|
|
}
|
|
delta = newtime - currtime;
|
|
switch(opcode) {
|
|
case ON:
|
|
case OFF:
|
|
case POPR:
|
|
checkchan();
|
|
checknote();
|
|
checkval();
|
|
mf_w_midi_event(delta,opcode,chan,data,2L);
|
|
break;
|
|
case PAR:
|
|
checkchan();
|
|
checkcon();
|
|
checkval();
|
|
mf_w_midi_event(delta,opcode,chan,data,2L);
|
|
break;
|
|
case PB:
|
|
checkchan();
|
|
splitval();
|
|
mf_w_midi_event(delta,opcode,chan,data,2L);
|
|
break;
|
|
case PRCH:
|
|
checkchan();
|
|
checkprog();
|
|
mf_w_midi_event(delta,opcode,chan,data,1L);
|
|
break;
|
|
case CHPR:
|
|
checkchan();
|
|
checkval();
|
|
data[0] = data[1];
|
|
mf_w_midi_event(delta,opcode,chan,data,1L);
|
|
break;
|
|
case SYSEX:
|
|
case ARB:
|
|
gethex();
|
|
mf_w_sysex_event(delta,buffer,(long)buflen);
|
|
break;
|
|
case TEMPO:
|
|
if (yylex() != INT) syntax();
|
|
mf_w_tempo (delta,yyval);
|
|
break;
|
|
case TIMESIG: {
|
|
int nn,denom,cc,bb;
|
|
if (yylex() != INT || yylex() != '/') syntax();
|
|
nn = yyval;
|
|
denom = getbyte("Denom");
|
|
cc = getbyte("clocks per click");
|
|
bb = getbyte("32nd notes per 24 clocks");
|
|
for(i=0, k=1 ; k<denom; i++, k<<=1);
|
|
if (k!=denom) error("Illegal TimeSig");
|
|
data[0] = nn;
|
|
data[1] = i;
|
|
data[2] = cc;
|
|
data[3] = bb;
|
|
M0 += (newtime-T0)/(Beat*Measure);
|
|
T0 = newtime;
|
|
Measure = nn;
|
|
Beat = 4 * Clicks / denom;
|
|
mf_w_meta_event(delta,time_signature,data,4L);
|
|
}
|
|
break;
|
|
case SMPTE:
|
|
for(i = 0; i < 5; i++) data[i] = getbyte("SMPTE");
|
|
mf_w_meta_event(delta,smpte_offset,data,5L);
|
|
break;
|
|
case KEYSIG:
|
|
data[0] = i = getint("Keysig");
|
|
if (i < -7 || i > 7)
|
|
error("Key Sig must be between -7 and 7");
|
|
if ((c=yylex()) != MINOR && c != MAJOR)
|
|
syntax();
|
|
data[1] = (c == MINOR);
|
|
mf_w_meta_event(delta,key_signature,data,2L);
|
|
break;
|
|
case SEQNR:
|
|
get16val("SeqNr");
|
|
mf_w_meta_event(delta,sequence_number,data,2L);
|
|
break;
|
|
case META: {
|
|
int type = yylex();
|
|
switch(type) {
|
|
case TRKEND: type = end_of_track; break;
|
|
case TEXT:
|
|
case COPYRIGHT:
|
|
case SEQNAME:
|
|
case INSTRNAME:
|
|
case LYRIC:
|
|
case MARKER:
|
|
case CUE: type -= (META+1); break;
|
|
case INT: type = yyval; break;
|
|
default: prs_error("Illegal Meta type");
|
|
}
|
|
if (type == end_of_track)
|
|
buflen = 0;
|
|
else
|
|
gethex();
|
|
mf_w_meta_event(delta,type,buffer,(long)buflen);
|
|
break;
|
|
}
|
|
case SEQSPEC:
|
|
gethex();
|
|
mf_w_meta_event(delta,sequencer_specific,buffer,(long)buflen);
|
|
break;
|
|
default:
|
|
prs_error("Unknown input");
|
|
break;
|
|
}
|
|
currtime = newtime;
|
|
case EOL:
|
|
break;
|
|
default:
|
|
prs_error("Unknown input");
|
|
break;
|
|
}
|
|
checkeol();
|
|
}
|
|
}
|
|
|
|
getbyte(char *mess) {
|
|
|
|
char ermesg[100];
|
|
|
|
getint(mess);
|
|
if (yyval < 0 || yyval > 127) {
|
|
sprintf(ermesg,"Wrong value (%ld) for %s",yyval,mess);
|
|
error(ermesg);
|
|
yyval = 0;
|
|
}
|
|
return yyval;
|
|
}
|
|
|
|
getint(char *mess) {
|
|
|
|
char ermesg[100];
|
|
if (yylex() != INT) {
|
|
sprintf(ermesg,"Integer expected for %s",mess);
|
|
error(ermesg);
|
|
yyval = 0;
|
|
}
|
|
return yyval;
|
|
}
|
|
|
|
static void checkchan() {
|
|
|
|
if (yylex() != CH || yylex() != INT) syntax();
|
|
if (yyval < 1 || yyval > 16) error("Chan must be between 1 and 16");
|
|
chan = yyval-1;
|
|
}
|
|
|
|
static void checknote() {
|
|
|
|
int c;
|
|
|
|
if (yylex() != NOTE || ((c=yylex()) != INT && c != NOTEVAL))
|
|
syntax();
|
|
if (c == NOTEVAL) {
|
|
static int notes[] = {9,11,0,2,4,5,7};
|
|
char *p = yytext;
|
|
c = *p++;
|
|
if (isupper(c)) c = tolower(c);
|
|
yyval = notes[c-'a'];
|
|
switch(*p) {
|
|
case '#':
|
|
case '+': yyval++; p++; break;
|
|
case 'b':
|
|
case 'B':
|
|
case '-': yyval--; p++; break;
|
|
}
|
|
yyval += 12 * atoi(p);
|
|
}
|
|
if (yyval < 0 || yyval > 127)
|
|
error("Note must be between 0 and 127");
|
|
data[0] = yyval;
|
|
}
|
|
|
|
static void checkval() {
|
|
|
|
if (yylex() != VAL || yylex() != INT) syntax();
|
|
if (yyval < 0 || yyval > 127)
|
|
error("Value must be between 0 and 127");
|
|
data[1] = yyval;
|
|
}
|
|
|
|
static void splitval() {
|
|
|
|
if (yylex() != VAL || yylex() != INT) syntax();
|
|
if (yyval < 0 || yyval > 16383)
|
|
error("Value must be between 0 and 16383");
|
|
data[0] = yyval%128;
|
|
data[1] = yyval/128;
|
|
}
|
|
|
|
static void get16val() {
|
|
|
|
if (yylex() != VAL || yylex() != INT) syntax();
|
|
if (yyval < 0 || yyval > 65535)
|
|
error("Value must be between 0 and 65535");
|
|
data[0] = (yyval>>8)&0xff;
|
|
data[1] = yyval&0xff;
|
|
}
|
|
|
|
static void checkcon() {
|
|
|
|
if (yylex() != CON || yylex() != INT)
|
|
syntax();
|
|
if (yyval < 0 || yyval > 127)
|
|
error("Controller must be between 0 and 127");
|
|
data[0] = yyval;
|
|
}
|
|
|
|
static void checkprog() {
|
|
|
|
if (yylex() != PROG || yylex() != INT) syntax();
|
|
if (yyval < 0 || yyval > 127)
|
|
error("Program number must be between 0 and 127");
|
|
data[0] = yyval;
|
|
}
|
|
|
|
static void checkeol() {
|
|
|
|
if (eol_seen) return;
|
|
if (yylex() != EOL) {
|
|
prs_error ("Garbage deleted");
|
|
while (!eol_seen) yylex();
|
|
}
|
|
}
|
|
|
|
static void gethex() {
|
|
|
|
int c;
|
|
|
|
buflen = 0;
|
|
do_hex = 1;
|
|
c = yylex();
|
|
if (c == STRING) {
|
|
int i = 0;
|
|
if (yyleng-1 > bufsiz) {
|
|
bufsiz = yyleng-1;
|
|
if (buffer)
|
|
buffer = realloc(buffer, bufsiz);
|
|
else
|
|
buffer = malloc (bufsiz);
|
|
if (! buffer) error("Out of memory");
|
|
}
|
|
while(i<yyleng-1) {
|
|
c = yytext[i++];
|
|
rescan:
|
|
if (c == '\\') {
|
|
switch (c = yytext[i++]) {
|
|
case '0': c = '\0'; break;
|
|
case 'n': c = '\n'; break;
|
|
case 'r': c = '\r'; break;
|
|
case 't': c = '\t'; break;
|
|
case 'x':
|
|
if (sscanf (yytext+i, "%2x", &c) != 1)
|
|
prs_error ("Illegal \\x in string");
|
|
i += 2;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
while ((c=yytext[i++])==' '||c=='\t'||c=='\r'||c=='\n')
|
|
goto rescan;
|
|
}
|
|
}
|
|
buffer[buflen++] = c;
|
|
}
|
|
} else if (c == INT) {
|
|
do {
|
|
if (buflen >= bufsiz) {
|
|
bufsiz += 128;
|
|
if (buffer)
|
|
buffer = realloc(buffer,bufsiz);
|
|
else
|
|
buffer = malloc(bufsiz);
|
|
if (! buffer) error ("Out of memory");
|
|
}
|
|
buffer[buflen++] = yyval;
|
|
c = yylex();
|
|
} while (c == INT);
|
|
if (c != EOL) prs_error("Unknown hex input");
|
|
} else {
|
|
prs_error("String or hex input expected");
|
|
}
|
|
}
|
|
|
|
long bankno (char *s,int n) {
|
|
|
|
long res = 0;
|
|
int c;
|
|
while(n-- > 0) {
|
|
c = (*s++);
|
|
if (islower(c))
|
|
c -= 'a';
|
|
else if (isupper(c))
|
|
c -= 'A';
|
|
else
|
|
c -= '1';
|
|
res = res * 8 + c;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
FILE *efopen(char *name,char *mode) {
|
|
if (dbg) fprintf(stderr,"efopen(%s,%s)\n",name,mode);
|
|
|
|
FILE *f;
|
|
if ((f = fopen(name,mode)) == NULL) {
|
|
(void) fprintf(stderr,"Cannot open '%s', %s!\n",name,strerror(errno));
|
|
exit(1);
|
|
}
|
|
return(f);
|
|
}
|
|
|
|
fileputc(int c) {
|
|
|
|
return putc(c,F);
|
|
}
|
|
|
|
int filegetc() {
|
|
|
|
return(getc(F));
|
|
}
|