1347 lines
31 KiB
C
1347 lines
31 KiB
C
#if 0
|
|
SRC=$0
|
|
NAME=midi2rtttl
|
|
[ ! $1 ] && set -- -s -O2
|
|
CMDLINE="${CC:-gcc} -Wall $@ -o $NAME $SRC -DMIDI2RTTTL"
|
|
echo $CMDLINE; exec $CMDLINE; exit -1
|
|
#endif
|
|
/*
|
|
* $Id: midi2rtttl.c $
|
|
*
|
|
* Last modified: Sun Aug 29 18:06:52 1999 too
|
|
*
|
|
* HISTORY
|
|
* $Log: $
|
|
*/
|
|
|
|
/* embedding modified midifile.h and midifile.c into this file */
|
|
|
|
/***** midifile.h ******/
|
|
|
|
struct MF {
|
|
/* definitions for MIDI file parsing code */
|
|
int (*Mf_getc)(struct MF *);
|
|
void (*Mf_header)(struct MF *, int, int, int);
|
|
void (*Mf_trackstart)(struct MF *);
|
|
void (*Mf_trackend)(struct MF *);
|
|
void (*Mf_noteon)(struct MF *, int, int, int);
|
|
void (*Mf_noteoff)(struct MF *, int, int, int);
|
|
void (*Mf_pressure)(struct MF *, int, int, int);
|
|
void (*Mf_parameter)(struct MF *, int, int, int);
|
|
void (*Mf_pitchbend)(struct MF *, int, int, int);
|
|
void (*Mf_program)(struct MF *, int, int);
|
|
void (*Mf_chanpressure)(struct MF *, int, int);
|
|
void (*Mf_sysex)(struct MF *, int, char *);
|
|
void (*Mf_metamisc)(struct MF *, int, int, char * );
|
|
void (*Mf_seqspecific)(struct MF *, int, int, char *);
|
|
void (*Mf_seqnum)(struct MF *, int);
|
|
void (*Mf_text)(struct MF *, int, int, char *);
|
|
void (*Mf_eot)(struct MF *);
|
|
void (*Mf_timesig)(struct MF *, int, int, int, int);
|
|
void (*Mf_smpte)(struct MF *, int, int, int, int, int);
|
|
void (*Mf_tempo)(struct MF *, long);
|
|
void (*Mf_keysig)(struct MF *, int, int);
|
|
void (*Mf_arbitrary)(struct MF *, int, char *);
|
|
void (*Mf_error)(struct MF *, char * );
|
|
#if 0
|
|
/* definitions for MIDI file writing code */
|
|
void (*Mf_putc)(struct MF *);
|
|
void (*Mf_writetrack)(struct MF *);
|
|
void (*Mf_writetempotrack)(struct MF *);
|
|
#endif
|
|
/* variables */
|
|
int Mf_nomerge; /* 1 => continue'ed system exclusives are */
|
|
/* not collapsed. */
|
|
long Mf_currtime; /* current time in delta-time units */
|
|
|
|
/* private stuff */
|
|
long Mf_toberead;
|
|
long Mf_numbyteswritten;
|
|
|
|
char *Msgbuff; /* message buffer */
|
|
int Msgsize; /* Size of currently allocated Msg */
|
|
int Msgindex; /* index of next available location in Msg */
|
|
|
|
};
|
|
|
|
float mf_ticks2sec(unsigned long ticks,int division,unsigned int tempo);
|
|
unsigned long mf_sec2ticks(float secs,int division, unsigned int tempo);
|
|
|
|
void mferror(struct MF * mf, char * s);
|
|
|
|
/*void mfwrite(); */
|
|
|
|
|
|
/* MIDI status commands most significant bit is 1 */
|
|
#define note_off 0x80
|
|
#define note_on 0x90
|
|
#define poly_aftertouch 0xa0
|
|
#define control_change 0xb0
|
|
#define program_chng 0xc0
|
|
#define channel_aftertouch 0xd0
|
|
#define pitch_wheel 0xe0
|
|
#define system_exclusive 0xf0
|
|
#define delay_packet (1111)
|
|
|
|
/* 7 bit controllers */
|
|
#define damper_pedal 0x40
|
|
#define portamento 0x41
|
|
#define sostenuto 0x42
|
|
#define soft_pedal 0x43
|
|
#define general_4 0x44
|
|
#define hold_2 0x45
|
|
#define general_5 0x50
|
|
#define general_6 0x51
|
|
#define general_7 0x52
|
|
#define general_8 0x53
|
|
#define tremolo_depth 0x5c
|
|
#define chorus_depth 0x5d
|
|
#define detune 0x5e
|
|
#define phaser_depth 0x5f
|
|
|
|
/* parameter values */
|
|
#define data_inc 0x60
|
|
#define data_dec 0x61
|
|
|
|
/* parameter selection */
|
|
#define non_reg_lsb 0x62
|
|
#define non_reg_msb 0x63
|
|
#define reg_lsb 0x64
|
|
#define reg_msb 0x65
|
|
|
|
/* Standard MIDI Files meta event definitions */
|
|
#define meta_event 0xFF
|
|
#define sequence_number 0x00
|
|
#define text_event 0x01
|
|
#define copyright_notice 0x02
|
|
#define sequence_name 0x03
|
|
#define instrument_name 0x04
|
|
#define lyric 0x05
|
|
#define marker 0x06
|
|
#define cue_point 0x07
|
|
#define channel_prefix 0x20
|
|
#define end_of_track 0x2f
|
|
#define set_tempo 0x51
|
|
#define smpte_offset 0x54
|
|
#define time_signature 0x58
|
|
#define key_signature 0x59
|
|
#define sequencer_specific 0x74
|
|
|
|
/* Manufacturer's ID number */
|
|
#define Seq_Circuits (0x01) /* Sequential Circuits Inc. */
|
|
#define Big_Briar (0x02) /* Big Briar Inc. */
|
|
#define Octave (0x03) /* Octave/Plateau */
|
|
#define Moog (0x04) /* Moog Music */
|
|
#define Passport (0x05) /* Passport Designs */
|
|
#define Lexicon (0x06) /* Lexicon */
|
|
#define Tempi (0x20) /* Bon Tempi */
|
|
#define Siel (0x21) /* S.I.E.L. */
|
|
#define Kawai (0x41)
|
|
#define Roland (0x42)
|
|
#define Korg (0x42)
|
|
#define Yamaha (0x43)
|
|
|
|
/* miscellaneous definitions */
|
|
#define MThd 0x4d546864
|
|
#define MTrk 0x4d54726b
|
|
#define lowerbyte(x) ((unsigned char)(x & 0xff))
|
|
#define upperbyte(x) ((unsigned char)((x & 0xff00)>>8))
|
|
|
|
/* the midifile interface */
|
|
void midifile(struct MF * mf);
|
|
|
|
/***** midifile.c ******/
|
|
|
|
/*
|
|
* midifile 1.11
|
|
*
|
|
* Read and write a MIDI file. Externally-assigned function pointers are
|
|
* called upon recognizing things in the file.
|
|
*
|
|
* Original release ?
|
|
* June 1989 - Added writing capability, M. Czeiszperger.
|
|
*
|
|
* The file format implemented here is called
|
|
* Standard MIDI Files, and is part of the Musical
|
|
* instrument Digital Interface specification.
|
|
* The spec is avaiable from:
|
|
*
|
|
* International MIDI Association
|
|
* 5316 West 57th Street
|
|
* Los Angeles, CA 90056
|
|
*
|
|
* An in-depth description of the spec can also be found
|
|
* in the article "Introducing Standard MIDI Files", published
|
|
* in Electronic Musician magazine, April, 1989.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
//#include <unistd.h>
|
|
|
|
#define NULLFUNC NULL
|
|
|
|
static void readheader(struct MF * mf);
|
|
static int readtrack(struct MF * mf);
|
|
static void chanmessage(struct MF * mf,int status,int c1,int c2);
|
|
static void msginit(struct MF * mf);
|
|
static void msgadd(struct MF * mf,int c);
|
|
static void metaevent(struct MF * mf, int type);
|
|
static void sysex(struct MF * mf);
|
|
static int msgleng(struct MF * mf);
|
|
static void badbyte(struct MF * mf,int c);
|
|
static void biggermsg(struct MF * mf);
|
|
|
|
|
|
static long readvarinum(struct MF * mf);
|
|
static long read32bit(struct MF * mf);
|
|
static long to32bit(int, int, int, int);
|
|
static int read16bit(struct MF * mf);
|
|
static int to16bit(int, int);
|
|
static char * msg(struct MF * mf);
|
|
|
|
/* The only non-static function in this file. */
|
|
void mfread(struct MF * mf)
|
|
{
|
|
if ( mf->Mf_getc == NULLFUNC )
|
|
mferror(mf, "mfread() called without setting Mf_getc");
|
|
|
|
readheader(mf);
|
|
while ( readtrack(mf) )
|
|
;
|
|
}
|
|
|
|
/* for backward compatibility with the original lib */
|
|
void midifile(struct MF * mf)
|
|
{
|
|
mfread(mf);
|
|
}
|
|
|
|
/* read through the "MThd" or "MTrk" header string */
|
|
static int readmt(struct MF * mf, char * s)
|
|
{
|
|
int n = 0;
|
|
char *p = s;
|
|
int c;
|
|
|
|
while ( n++<4 && (c=mf->Mf_getc(mf)) != EOF ) {
|
|
if ( c != *p++ ) {
|
|
char buff[32];
|
|
(void) strcpy(buff, "expecting ");
|
|
(void) strcat(buff, s);
|
|
mferror(mf, buff);
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/* read a single character and abort on EOF */
|
|
static int egetc(struct MF * mf)
|
|
{
|
|
int c = mf->Mf_getc(mf);
|
|
|
|
if ( c == EOF )
|
|
mferror(mf, "premature EOF");
|
|
mf->Mf_toberead--;
|
|
return c;
|
|
}
|
|
|
|
/* read a header chunk */
|
|
static void readheader(struct MF * mf)
|
|
{
|
|
int format, ntrks, division;
|
|
|
|
if ( readmt(mf, "MThd") == EOF )
|
|
return;
|
|
|
|
mf->Mf_toberead = read32bit(mf);
|
|
format = read16bit(mf);
|
|
ntrks = read16bit(mf);
|
|
division = read16bit(mf);
|
|
|
|
if ( mf->Mf_header )
|
|
(*mf->Mf_header)(mf, format,ntrks,division);
|
|
|
|
/* flush any extra stuff, in case the length of header is not 6 */
|
|
while ( mf->Mf_toberead > 0 )
|
|
(void) egetc(mf);
|
|
}
|
|
|
|
static int readtrack(struct MF * mf) /* read a track chunk */
|
|
{
|
|
/* This array is indexed by the high half of a status byte. It's */
|
|
/* value is either the number of bytes needed (1 or 2) for a channel */
|
|
/* message, or 0 (meaning it's not a channel message). */
|
|
static int chantype[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
|
|
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
|
|
};
|
|
long lookfor;
|
|
int c, c1, type;
|
|
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
|
|
int running = 0; /* 1 when running status used */
|
|
int status = 0; /* status value (e.g. 0x90==note-on) */
|
|
int needed;
|
|
|
|
if ( readmt(mf, "MTrk") == EOF )
|
|
return(0);
|
|
|
|
mf->Mf_toberead = read32bit(mf);
|
|
mf->Mf_currtime = 0;
|
|
|
|
if ( mf->Mf_trackstart )
|
|
(*mf->Mf_trackstart)(mf);
|
|
|
|
while ( mf->Mf_toberead > 0 ) {
|
|
|
|
mf->Mf_currtime += readvarinum(mf); /* delta time */
|
|
|
|
c = egetc(mf);
|
|
|
|
if ( sysexcontinue && c != 0xf7 )
|
|
mferror(mf, "didn't find expected continuation of a sysex");
|
|
|
|
if ( (c & 0x80) == 0 ) { /* running status? */
|
|
if ( status == 0 )
|
|
mferror(mf, "unexpected running status");
|
|
running = 1;
|
|
}
|
|
else {
|
|
status = c;
|
|
running = 0;
|
|
}
|
|
|
|
needed = chantype[ (status>>4) & 0xf ];
|
|
|
|
if ( needed ) { /* ie. is it a channel message? */
|
|
|
|
if ( running )
|
|
c1 = c;
|
|
else
|
|
c1 = egetc(mf);
|
|
chanmessage(mf, status, c1, (needed>1)? egetc(mf): 0);
|
|
continue;;
|
|
}
|
|
|
|
switch ( c ) {
|
|
|
|
case 0xff: /* meta event */
|
|
|
|
type = egetc(mf);
|
|
lookfor = mf->Mf_toberead - readvarinum(mf);
|
|
msginit(mf);
|
|
|
|
while ( mf->Mf_toberead > lookfor )
|
|
msgadd(mf, egetc(mf));
|
|
|
|
metaevent(mf, type);
|
|
break;
|
|
|
|
case 0xf0: /* start of system exclusive */
|
|
|
|
lookfor = mf->Mf_toberead - readvarinum(mf);
|
|
msginit(mf);
|
|
msgadd(mf, 0xf0);
|
|
|
|
while ( mf->Mf_toberead > lookfor )
|
|
msgadd(mf, c=egetc(mf));
|
|
|
|
if ( c==0xf7 || mf->Mf_nomerge==0 )
|
|
sysex(mf);
|
|
else
|
|
sysexcontinue = 1; /* merge into next msg */
|
|
break;
|
|
|
|
case 0xf7: /* sysex continuation or arbitrary stuff */
|
|
|
|
lookfor = mf->Mf_toberead - readvarinum(mf);
|
|
|
|
if ( ! sysexcontinue )
|
|
msginit(mf);
|
|
|
|
while ( mf->Mf_toberead > lookfor )
|
|
msgadd(mf, c=egetc(mf));
|
|
|
|
if ( ! sysexcontinue ) {
|
|
if ( mf->Mf_arbitrary )
|
|
(*mf->Mf_arbitrary)(mf, msgleng(mf),msg(mf));
|
|
}
|
|
else if ( c == 0xf7 ) {
|
|
sysex(mf);
|
|
sysexcontinue = 0;
|
|
}
|
|
break;
|
|
default:
|
|
badbyte(mf, c);
|
|
break;
|
|
}
|
|
}
|
|
if ( mf->Mf_trackend )
|
|
(*mf->Mf_trackend)(mf);
|
|
return(1);
|
|
}
|
|
|
|
static void badbyte(struct MF * mf,int c)
|
|
{
|
|
char buff[32];
|
|
|
|
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
|
mferror(mf, buff);
|
|
}
|
|
|
|
static void metaevent(struct MF * mf, int type)
|
|
{
|
|
int leng = msgleng(mf);
|
|
char *m = msg(mf);
|
|
|
|
switch ( type ) {
|
|
case 0x00:
|
|
if ( mf->Mf_seqnum )
|
|
(*mf->Mf_seqnum)(mf, 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:
|
|
/* These are all text events */
|
|
if ( mf->Mf_text )
|
|
(*mf->Mf_text)(mf, type,leng,m);
|
|
break;
|
|
case 0x2f: /* End of Track */
|
|
if ( mf->Mf_eot )
|
|
(*mf->Mf_eot)(mf);
|
|
break;
|
|
case 0x51: /* Set tempo */
|
|
if ( mf->Mf_tempo )
|
|
(*mf->Mf_tempo)(mf, to32bit(0,m[0],m[1],m[2]));
|
|
break;
|
|
case 0x54:
|
|
if ( mf->Mf_smpte )
|
|
(*mf->Mf_smpte)(mf, m[0],m[1],m[2],m[3],m[4]);
|
|
break;
|
|
case 0x58:
|
|
if ( mf->Mf_timesig )
|
|
(*mf->Mf_timesig)(mf, m[0],m[1],m[2],m[3]);
|
|
break;
|
|
case 0x59:
|
|
if ( mf->Mf_keysig )
|
|
(*mf->Mf_keysig)(mf, m[0],m[1]);
|
|
break;
|
|
case 0x7f:
|
|
if ( mf->Mf_seqspecific )
|
|
(*mf->Mf_seqspecific)(mf, type, leng, m);
|
|
break;
|
|
default:
|
|
if ( mf->Mf_metamisc )
|
|
(*mf->Mf_metamisc)(mf, type,leng,m);
|
|
}
|
|
}
|
|
|
|
static void sysex(struct MF * mf)
|
|
{
|
|
if ( mf->Mf_sysex )
|
|
(*mf->Mf_sysex)(mf, msgleng(mf),msg(mf));
|
|
}
|
|
|
|
static void chanmessage(struct MF * mf,int status,int c1,int c2)
|
|
{
|
|
int chan = status & 0xf;
|
|
|
|
switch ( status & 0xf0 ) {
|
|
case 0x80:
|
|
if ( mf->Mf_noteoff )
|
|
(*mf->Mf_noteoff)(mf, chan,c1,c2);
|
|
break;
|
|
case 0x90:
|
|
if ( mf->Mf_noteon )
|
|
(*mf->Mf_noteon)(mf, chan,c1,c2);
|
|
break;
|
|
case 0xa0:
|
|
if ( mf->Mf_pressure )
|
|
(*mf->Mf_pressure)(mf, chan,c1,c2);
|
|
break;
|
|
case 0xb0:
|
|
if ( mf->Mf_parameter )
|
|
(*mf->Mf_parameter)(mf, chan,c1,c2);
|
|
break;
|
|
case 0xe0:
|
|
if ( mf->Mf_pitchbend )
|
|
(*mf->Mf_pitchbend)(mf, chan,c1,c2);
|
|
break;
|
|
case 0xc0:
|
|
if ( mf->Mf_program )
|
|
(*mf->Mf_program)(mf, chan,c1);
|
|
break;
|
|
case 0xd0:
|
|
if ( mf->Mf_chanpressure )
|
|
(*mf->Mf_chanpressure)(mf, chan,c1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* readvarinum - read a varying-length number, and return the */
|
|
/* number of characters it took. */
|
|
|
|
static long readvarinum(struct MF * mf)
|
|
{
|
|
long value;
|
|
int c;
|
|
|
|
c = egetc(mf);
|
|
value = c;
|
|
if ( c & 0x80 ) {
|
|
value &= 0x7f;
|
|
do {
|
|
c = egetc(mf);
|
|
value = (value << 7) + (c & 0x7f);
|
|
} while (c & 0x80);
|
|
}
|
|
return (value);
|
|
}
|
|
|
|
static long to32bit(int c1,int c2,int c3,int 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 int to16bit(int c1,int c2)
|
|
{
|
|
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
|
}
|
|
|
|
static long read32bit(struct MF * mf)
|
|
{
|
|
int c1, c2, c3, c4;
|
|
|
|
c1 = egetc(mf);
|
|
c2 = egetc(mf);
|
|
c3 = egetc(mf);
|
|
c4 = egetc(mf);
|
|
return to32bit(c1,c2,c3,c4);
|
|
}
|
|
|
|
static int read16bit(struct MF * mf)
|
|
{
|
|
int c1, c2;
|
|
c1 = egetc(mf);
|
|
c2 = egetc(mf);
|
|
return to16bit(c1,c2);
|
|
}
|
|
|
|
/* static */
|
|
void mferror(struct MF * mf, char * s)
|
|
{
|
|
if ( mf->Mf_error )
|
|
(*mf->Mf_error)(mf, s);
|
|
exit(1);
|
|
}
|
|
|
|
/* The code below allows collection of a system exclusive message of */
|
|
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
|
|
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
|
|
|
|
#define MSGINCREMENT 128
|
|
|
|
static void msginit(struct MF * mf)
|
|
{
|
|
mf->Msgindex = 0;
|
|
}
|
|
|
|
static char * msg(struct MF * mf)
|
|
{
|
|
return(mf->Msgbuff);
|
|
}
|
|
|
|
static int msgleng(struct MF * mf)
|
|
{
|
|
return(mf->Msgindex);
|
|
}
|
|
|
|
static void msgadd(struct MF * mf,int c)
|
|
{
|
|
/* If necessary, allocate larger message buffer. */
|
|
if ( mf->Msgindex >= mf->Msgsize )
|
|
biggermsg(mf);
|
|
mf->Msgbuff[mf->Msgindex++] = c;
|
|
}
|
|
|
|
static void biggermsg(struct MF * mf)
|
|
{
|
|
char *newmess;
|
|
char *oldmess = mf->Msgbuff;
|
|
int oldleng = mf->Msgsize;
|
|
|
|
mf->Msgsize += MSGINCREMENT;
|
|
newmess = (char *) malloc( (unsigned)(sizeof(char) * mf->Msgsize) );
|
|
|
|
if(newmess == NULL)
|
|
mferror(mf, "malloc error!");
|
|
|
|
/* copy old message into larger new one */
|
|
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);
|
|
}
|
|
mf->Msgbuff = newmess;
|
|
}
|
|
|
|
#if 0 /* saving time not converting write function at this time
|
|
*/
|
|
/*
|
|
* mfwrite() - The only fuction you'll need to call to write out
|
|
* a midi file.
|
|
*
|
|
* format 0 - Single multi-channel track
|
|
* 1 - Multiple simultaneous tracks
|
|
* 2 - One or more sequentially independent
|
|
* single track patterns
|
|
* ntracks The number of tracks in the file.
|
|
* division This is kind of tricky, it can represent two
|
|
* things, depending on whether it is positive or negative
|
|
* (bit 15 set or not). If bit 15 of division is zero,
|
|
* bits 14 through 0 represent the number of delta-time
|
|
* "ticks" which make up a quarter note. If bit 15 of
|
|
* division is a one, delta-times in a file correspond to
|
|
* subdivisions of a second similiar to SMPTE and MIDI
|
|
* time code. In this format bits 14 through 8 contain
|
|
* one of four values - 24, -25, -29, or -30,
|
|
* corresponding to the four standard SMPTE and MIDI
|
|
* time code frame per second formats, where -29
|
|
* represents 30 drop frame. The second byte
|
|
* consisting of bits 7 through 0 corresponds the the
|
|
* resolution within a frame. Refer the Standard MIDI
|
|
* Files 1.0 spec for more details.
|
|
* fp This should be the open file pointer to the file you
|
|
* want to write. It will have be a global in order
|
|
* to work with Mf_putc.
|
|
*/
|
|
void
|
|
mfwrite(format,ntracks,division,fp)
|
|
int format,ntracks,division;
|
|
FILE *fp;
|
|
{
|
|
int i; void mf_write_track_chunk(), mf_write_header_chunk();
|
|
|
|
if ( mf->Mf_putc == NULLFUNC )
|
|
mferror(mf, "mfmf_write() called without setting Mf_putc");
|
|
|
|
if ( mf->Mf_writetrack == NULLFUNC )
|
|
mferror(mf, "mfmf_write() called without setting Mf_mf_writetrack");
|
|
|
|
/* every MIDI file starts with a header */
|
|
mf_write_header_chunk(format,ntracks,division);
|
|
|
|
/* In format 1 files, the first track is a tempo map */
|
|
if(format == 1 && ( mf->Mf_writetempotrack ))
|
|
{
|
|
(*mf->Mf_writetempotrack)();
|
|
}
|
|
|
|
/* The rest of the file is a series of tracks */
|
|
for(i = 0; i < ntracks; i++)
|
|
mf_write_track_chunk(i,fp);
|
|
}
|
|
|
|
void
|
|
mf_write_track_chunk(which_track,fp)
|
|
int which_track;
|
|
FILE *fp;
|
|
{
|
|
unsigned long trkhdr,trklength;
|
|
long offset, place_marker;
|
|
void write16bit(),write32bit();
|
|
|
|
|
|
trkhdr = MTrk;
|
|
trklength = 0;
|
|
|
|
/* Remember where the length was written, because we don't
|
|
know how long it will be until we've finished writing */
|
|
offset = ftell(fp);
|
|
|
|
#ifdef DEBUG
|
|
printf("offset = %d\n",(int) offset);
|
|
#endif
|
|
|
|
/* Write the track chunk header */
|
|
write32bit(trkhdr);
|
|
write32bit(trklength);
|
|
|
|
mf->Mf_numbyteswritten = 0L; /* the header's length doesn't count */
|
|
|
|
if( mf->Mf_writetrack )
|
|
{
|
|
(*mf->Mf_writetrack)(which_track);
|
|
}
|
|
|
|
/* mf_write End of track meta event */
|
|
eputc(mf, 0);
|
|
eputc(mf, meta_event);
|
|
eputc(mf, end_of_track);
|
|
|
|
eputc(mf, 0);
|
|
|
|
/* It's impossible to know how long the track chunk will be beforehand,
|
|
so the position of the track length data is kept so that it can
|
|
be written after the chunk has been generated */
|
|
place_marker = ftell(fp);
|
|
|
|
/* This method turned out not to be portable because the
|
|
parameter returned from ftell is not guaranteed to be
|
|
in bytes on every machine */
|
|
/* track.length = place_marker - offset - (long) sizeof(track); */
|
|
|
|
#ifdef DEBUG
|
|
printf("length = %d\n",(int) trklength);
|
|
#endif
|
|
|
|
if(fseek(fp,offset,0) < 0)
|
|
mferror(mf, "error seeking during final stage of write");
|
|
|
|
trklength = mf->Mf_numbyteswritten;
|
|
|
|
/* Re-mf_write the track chunk header with right length */
|
|
write32bit(trkhdr);
|
|
write32bit(trklength);
|
|
|
|
fseek(fp,place_marker,0);
|
|
} /* End gen_track_chunk() */
|
|
|
|
|
|
void
|
|
mf_write_header_chunk(format,ntracks,division)
|
|
int format,ntracks,division;
|
|
{
|
|
unsigned long ident,length;
|
|
void write16bit(),write32bit();
|
|
|
|
ident = MThd; /* Head chunk identifier */
|
|
length = 6; /* Chunk length */
|
|
|
|
/* individual bytes of the header must be written separately
|
|
to preserve byte order across cpu types :-( */
|
|
write32bit(ident);
|
|
write32bit(length);
|
|
write16bit(format);
|
|
write16bit(ntracks);
|
|
write16bit(division);
|
|
} /* end gen_header_chunk() */
|
|
|
|
|
|
#ifdef WHENISTHISNEEDED
|
|
/*
|
|
* mf_write_midi_event()
|
|
*
|
|
* Library routine to mf_write a single MIDI track event in the standard MIDI
|
|
* file format. The format is:
|
|
*
|
|
* <delta-time><event>
|
|
*
|
|
* In this case, event can be any multi-byte midi message, such as
|
|
* "note on", "note off", etc.
|
|
*
|
|
* delta_time - the time in ticks since the last event.
|
|
* type - the type of meta event.
|
|
* chan - The midi channel.
|
|
* data - A pointer to a block of chars containing the META EVENT,
|
|
* data.
|
|
* size - The length of the meta-event data.
|
|
*/
|
|
int
|
|
mf_write_midi_event(delta_time, type, chan, data, size)
|
|
unsigned long delta_time;
|
|
unsigned int chan,type;
|
|
unsigned long size;
|
|
unsigned char *data;
|
|
{
|
|
int i;
|
|
void WriteVarLen();
|
|
unsigned char c;
|
|
|
|
WriteVarLen(delta_time);
|
|
|
|
/* all MIDI events start with the type in the first four bits,
|
|
and the channel in the lower four bits */
|
|
c = type | chan;
|
|
|
|
if(chan > 15)
|
|
perror("error: MIDI channel greater than 16\n");
|
|
|
|
eputc(mf, c);
|
|
|
|
/* write out the data bytes */
|
|
for(i = 0; i < size; i++)
|
|
eputc(mf, data[i]);
|
|
|
|
return(size);
|
|
} /* end mf_write MIDI event */
|
|
|
|
/*
|
|
* mf_write_meta_event()
|
|
*
|
|
* Library routine to mf_write a single meta event in the standard MIDI
|
|
* file format. The format of a meta event is:
|
|
*
|
|
* <delta-time><FF><type><length><bytes>
|
|
*
|
|
* delta_time - the time in ticks since the last event.
|
|
* type - the type of meta event.
|
|
* data - A pointer to a block of chars containing the META EVENT,
|
|
* data.
|
|
* size - The length of the meta-event data.
|
|
*/
|
|
int
|
|
mf_write_meta_event(delta_time, type, data, size)
|
|
unsigned long delta_time;
|
|
unsigned char *data,type;
|
|
unsigned long size;
|
|
{
|
|
int i;
|
|
|
|
WriteVarLen(delta_time);
|
|
|
|
/* This marks the fact we're writing a meta-event */
|
|
eputc(mf, meta_event);
|
|
|
|
/* The type of meta event */
|
|
eputc(mf, type);
|
|
|
|
/* The length of the data bytes to follow */
|
|
WriteVarLen(size);
|
|
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
if(eputc(mf, data[i]) != data[i])
|
|
return(-1);
|
|
}
|
|
return(size);
|
|
} /* end mf_write_meta_event */
|
|
|
|
void
|
|
mf_write_tempo(tempo)
|
|
unsigned long tempo;
|
|
{
|
|
/* Write tempo */
|
|
/* all tempos are written as 120 beats/minute, */
|
|
/* expressed in microseconds/quarter note */
|
|
eputc(mf, 0);
|
|
eputc(mf, meta_event);
|
|
eputc(mf, set_tempo);
|
|
|
|
eputc(mf, 3);
|
|
eputc(mf, (unsigned)(0xff & (tempo >> 16)));
|
|
eputc(mf, (unsigned)(0xff & (tempo >> 8)));
|
|
eputc(mf, (unsigned)(0xff & tempo));
|
|
}
|
|
|
|
#endif
|
|
/*
|
|
* Write multi-length bytes to MIDI format files
|
|
*/
|
|
void
|
|
WriteVarLen(value)
|
|
unsigned long value;
|
|
{
|
|
unsigned long buffer;
|
|
|
|
buffer = value & 0x7f;
|
|
while((value >>= 7) > 0)
|
|
{
|
|
buffer <<= 8;
|
|
buffer |= 0x80;
|
|
buffer += (value & 0x7f);
|
|
}
|
|
while(1){
|
|
eputc(mf, (unsigned)(buffer & 0xff));
|
|
|
|
if(buffer & 0x80)
|
|
buffer >>= 8;
|
|
else
|
|
return;
|
|
}
|
|
}/* end of WriteVarLen */
|
|
|
|
|
|
/*
|
|
* write32bit()
|
|
* write16bit()
|
|
*
|
|
* These routines are used to make sure that the byte order of
|
|
* the various data types remains constant between machines. This
|
|
* helps make sure that the code will be portable from one system
|
|
* to the next. It is slightly dangerous that it assumes that longs
|
|
* have at least 32 bits and ints have at least 16 bits, but this
|
|
* has been true at least on PCs, UNIX machines, and Macintosh's.
|
|
*
|
|
*/
|
|
void
|
|
write32bit(data)
|
|
unsigned long data;
|
|
{
|
|
eputc(mf, x(unsigned)((data >> 24) & 0xff));
|
|
eputc(mf, (unsigned)((data >> 16) & 0xff));
|
|
eputc(mf, (unsigned)((data >> 8 ) & 0xff));
|
|
eputc(mf, (unsigned)(data & 0xff));
|
|
}
|
|
|
|
void
|
|
write16bit(data)
|
|
int data;
|
|
{
|
|
eputc(mf, (unsigned)((data & 0xff00) >> 8));
|
|
eputc(mf, (unsigned)(data & 0xff));
|
|
}
|
|
|
|
/* write a single character and abort on error */
|
|
eputc(mf, c)
|
|
unsigned char c;
|
|
{
|
|
int return_val;
|
|
|
|
if((mf->Mf_putc) == NULLFUNC)
|
|
{
|
|
mferror(mf, "Mf_putc undefined");
|
|
return(-1);
|
|
}
|
|
|
|
return_val = (mf->Mf_putc)(mf, c);
|
|
|
|
if ( return_val == EOF )
|
|
mferror(mf, "error writing");
|
|
|
|
mf->Mf_numbyteswritten++;
|
|
return(return_val);
|
|
}
|
|
|
|
#endif
|
|
|
|
unsigned long mf_sec2ticks(float secs,int division, unsigned int tempo)
|
|
{
|
|
return (long)(((secs * 1000.0) / 4.0 * division) / tempo);
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine converts delta times in ticks into seconds. The
|
|
* else statement is needed because the formula is different for tracks
|
|
* based on notes and tracks based on SMPTE times.
|
|
*
|
|
*/
|
|
float mf_ticks2sec(unsigned long ticks,int division,unsigned int 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));
|
|
}
|
|
} /* end of ticks2sec() */
|
|
|
|
/* code to utilize the interface */
|
|
|
|
#define TRACE(x, y) do { if (x) printf y; } while (0)
|
|
|
|
|
|
typedef unsigned long ulg;
|
|
typedef unsigned char uch;
|
|
typedef unsigned int ui;
|
|
|
|
#ifndef MIDI2RTTTL
|
|
|
|
#include "readmidi.h"
|
|
|
|
#else /* MIDI2RTTTL */
|
|
|
|
struct NoteInfo
|
|
{
|
|
int beats;
|
|
int nrnotes;
|
|
struct Notes {
|
|
ui note : 4;
|
|
ui scale : 4;
|
|
ui length : 4;
|
|
ui lextra : 4;
|
|
} note[1];
|
|
};
|
|
|
|
#endif /* MIDI2RTTTL */
|
|
|
|
#define IBUFSIZE 1024
|
|
struct MFX
|
|
{
|
|
struct MF mfi;
|
|
struct NoteInfo * ni;
|
|
int allocated;
|
|
|
|
int division;
|
|
int trackstate;
|
|
|
|
int prevnoteonpitch; /* -1, nothing, 0 pause, 1-x note. */
|
|
ulg prevnoteontime;
|
|
|
|
struct {
|
|
int fd;
|
|
uch buf[IBUFSIZE];
|
|
int len;
|
|
int p;
|
|
} istrm;
|
|
};
|
|
|
|
enum { TRK_NONE, TRK_READING, TRK_FINISHED };
|
|
|
|
#define ALLOCSIZE 256
|
|
|
|
#define NIALLOC(size) (struct NoteInfo *)malloc(sizeof (struct NoteInfo) + ((size) - 1) * sizeof (struct Notes))
|
|
|
|
#define NIREALLOC(ni, size) (struct NoteInfo *)realloc((ni), sizeof (struct NoteInfo) + ((size) - 1) * sizeof (struct Notes))
|
|
|
|
|
|
static void lm_error(struct MF * mf, char * s);
|
|
|
|
static int lm_getc(struct MF * mf);
|
|
static void lm_header(struct MF * mf, int, int, int);
|
|
static void lm_trackstart(struct MF * mf);
|
|
static void lm_trackend(struct MF * mf);
|
|
static void lm_tempo(struct MF *, long);
|
|
static void lm_noteon(struct MF *, int, int, int);
|
|
static void lm_noteoff(struct MF *, int, int, int);
|
|
|
|
|
|
struct NoteInfo * readmidi(int fd)
|
|
{
|
|
struct MFX mfxi = { { 0 } };
|
|
struct MF * mf = (struct MF *)&mfxi;
|
|
|
|
mfxi.ni = NIALLOC(ALLOCSIZE);
|
|
mfxi.allocated = ALLOCSIZE;
|
|
|
|
/* set variables to their initial values */
|
|
mfxi.division = 0;
|
|
mfxi.trackstate = TRK_NONE;
|
|
mfxi.prevnoteonpitch = -1;
|
|
mfxi.ni->nrnotes = 0;
|
|
mfxi.ni->beats = 120;
|
|
|
|
mfxi.istrm.fd = fd;
|
|
mfxi.istrm.p = mfxi.istrm.len = 0;
|
|
mf->Mf_getc = lm_getc;
|
|
|
|
mf->Mf_header = lm_header;
|
|
mf->Mf_tempo = lm_tempo;
|
|
mf->Mf_trackstart = lm_trackstart;
|
|
mf->Mf_trackend = lm_trackend;
|
|
mf->Mf_noteon = lm_noteon;
|
|
mf->Mf_noteoff = lm_noteoff;
|
|
|
|
mf->Mf_error = lm_error;
|
|
|
|
midifile(mf);
|
|
|
|
return mfxi.ni;
|
|
}
|
|
|
|
static void lm_error(struct MF * mf, char * s)
|
|
{
|
|
fprintf(stderr, "%s\n", s);
|
|
}
|
|
|
|
static int lm_getc(struct MF * mf)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
|
|
/* printf("p %d, len %d\n", mfx->istrm.p, mfx->istrm.len); */
|
|
if (mfx->istrm.p == mfx->istrm.len)
|
|
{
|
|
mfx->istrm.len = read(mfx->istrm.fd, mfx->istrm.buf, IBUFSIZE);
|
|
/* printf("readlen %d\n", mfx->istrm.len); */
|
|
if (mfx->istrm.len <= 0)
|
|
return -1;
|
|
|
|
mfx->istrm.p = 1;
|
|
return mfx->istrm.buf[0];
|
|
}
|
|
/* else */
|
|
return mfx->istrm.buf[mfx->istrm.p++];
|
|
}
|
|
|
|
static void lm_header(struct MF * mf, int format, int ntrks, int division)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
|
|
TRACE(0, ("lm_header(%p, %d, %d, %d)\n", mf, format, ntrks, division));
|
|
|
|
mfx->division = division;
|
|
}
|
|
|
|
/* this is just a quess */
|
|
static void lm_tempo(struct MF * mf, long tempo)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
|
|
TRACE(0, ("lm_tempo(%p, %ld)\n", mf, tempo));
|
|
|
|
if (mfx->trackstate != TRK_FINISHED)
|
|
mfx->ni->beats = 60000000 / tempo;
|
|
}
|
|
|
|
|
|
static void addnote(struct MFX * mfx, int pitch, int duration, int special)
|
|
{
|
|
int nr, p, s;
|
|
struct NoteInfo * ni;
|
|
|
|
if (mfx->ni->nrnotes == mfx->allocated)
|
|
{
|
|
mfx->allocated += ALLOCSIZE;
|
|
mfx->ni = NIREALLOC(mfx->ni, mfx->allocated);
|
|
if (mfx->ni == NULL)
|
|
exit(1);
|
|
}
|
|
ni = mfx->ni; /* mfx->ni pointer value may have changed above */
|
|
nr = ni->nrnotes++;
|
|
|
|
|
|
if (pitch == 0) { p = 0; s = 0; }
|
|
else { pitch--; p = (pitch % 12) + 1; s = pitch / 12; }
|
|
|
|
ni->note[nr].note = p;
|
|
ni->note[nr].scale = s;
|
|
|
|
ni->note[nr].length = duration;
|
|
ni->note[nr].lextra = special;
|
|
}
|
|
|
|
/* currently supported */
|
|
static /* N 32 32. 16 16. 8 8. 4 4. 2 2. 1 1. */
|
|
int vals[] = { 15, 38, 54, 78, 109, 156, 218, 312, 437, 625, 875, 1250 };
|
|
|
|
static void writenote(struct MFX * mfx, int delta)
|
|
{
|
|
ulg millinotetime = delta * 250 / mfx->division;
|
|
int i;
|
|
int duration;
|
|
int special;
|
|
|
|
for(i = 0; i < sizeof vals / sizeof vals[0]; i++)
|
|
{
|
|
if (millinotetime < vals[i])
|
|
break;
|
|
}
|
|
|
|
if (i == 0)
|
|
return;
|
|
|
|
i--;
|
|
duration = i / 2;
|
|
special = i & 1;
|
|
|
|
addnote(mfx, mfx->prevnoteonpitch, duration, special); /* XXX think this */
|
|
}
|
|
|
|
|
|
static void lm_trackstart(struct MF * mf)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
|
|
TRACE(0, ("lm_trackstart(%p)\n", mf));
|
|
|
|
if (mfx->trackstate == TRK_NONE)
|
|
mfx->trackstate = TRK_READING;
|
|
|
|
mfx->prevnoteonpitch = -1;
|
|
}
|
|
|
|
static void lm_trackend(struct MF * mf)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
long time = mf->Mf_currtime;
|
|
|
|
TRACE(0, ("lm_trackend(%p)\n", mf));
|
|
|
|
if (mfx->trackstate == TRK_READING && mfx->ni->nrnotes > 0)
|
|
mfx->trackstate = TRK_FINISHED;
|
|
|
|
if (mfx->prevnoteonpitch >= 0)
|
|
writenote(mfx, time - mfx->prevnoteontime);
|
|
|
|
mfx->prevnoteonpitch = -1;
|
|
}
|
|
|
|
static void lm_noteon(struct MF * mf, int chan, int pitch, int vol)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
long time = mf->Mf_currtime;
|
|
|
|
TRACE(0, ("lm_noteon(%p, %d, %d, %d)\n", mf, chan, pitch, vol));
|
|
|
|
if (vol == 0) /* kludge? to handle some (format 1? midi files) */
|
|
return;
|
|
|
|
if (mfx->trackstate != TRK_READING)
|
|
return;
|
|
|
|
if (mfx->prevnoteonpitch >= 0)
|
|
writenote(mfx, time - mfx->prevnoteontime);
|
|
|
|
if (vol == 0)
|
|
mfx->prevnoteonpitch = 0;
|
|
else
|
|
mfx->prevnoteonpitch = pitch + 1;
|
|
|
|
mfx->prevnoteontime = time;
|
|
}
|
|
|
|
static void lm_noteoff(struct MF * mf, int chan, int pitch, int vol)
|
|
{
|
|
struct MFX * mfx = (struct MFX *)mf;
|
|
long time = mf->Mf_currtime;
|
|
|
|
TRACE(0, ("lm_noteoff(%p, %d, %d, %d)\n", mf, chan, pitch, vol));
|
|
|
|
if (mfx->prevnoteonpitch >= 0)
|
|
{
|
|
writenote(mfx, time - mfx->prevnoteontime);
|
|
mfx->prevnoteonpitch = -1;
|
|
}
|
|
mfx->prevnoteonpitch = 0;
|
|
mfx->prevnoteontime = time;
|
|
}
|
|
|
|
|
|
#ifdef MIDI2RTTTL
|
|
|
|
char * notes[] =
|
|
{ "p", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "h" };
|
|
|
|
char * lengths[] = { "32", "16", "8", "4", "2", "1" };
|
|
|
|
static void countdefaults(struct NoteInfo * ni, int * length_p, int * scale_p)
|
|
{
|
|
int lengths[15] = { 0 };
|
|
int scales[15] = { 0 };
|
|
int maxlenval = 0; /* (*) */
|
|
int maxscaleval = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < ni->nrnotes; i++)
|
|
{
|
|
struct Notes * note = &ni->note[i];
|
|
|
|
lengths[note->length]++;
|
|
scales[note->scale]++;
|
|
}
|
|
|
|
maxlenval = lengths[0]; /* (*) smart compiler eliminates dead code */
|
|
*length_p = 0;
|
|
|
|
for (i = 1; i < 15; i++) /* `p' incremented scales[0], therefore ignored */
|
|
{
|
|
TRACE(0, ("%d - len: %d, scale: %d\n", i, lengths[i], scales[i]));
|
|
|
|
if (lengths[i] > maxlenval) {
|
|
*length_p = i;
|
|
maxlenval = lengths[i];
|
|
}
|
|
if (scales[i] > maxscaleval) {
|
|
*scale_p = i;
|
|
maxscaleval = scales[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
int fd;
|
|
struct NoteInfo * ni;
|
|
int i;
|
|
int deflen, defscale;
|
|
char buf[1024];
|
|
|
|
if (argv[1] == NULL)
|
|
return 0;
|
|
|
|
if ((fd = open(argv[1], O_RDONLY)) < 0)
|
|
{
|
|
perror("open");
|
|
return 0;
|
|
}
|
|
|
|
|
|
ni = readmidi(fd);
|
|
|
|
if (ni == NULL)
|
|
return 0;
|
|
|
|
countdefaults(ni, &deflen, &defscale);
|
|
|
|
/* patch argv[1]; */
|
|
#if 1
|
|
{
|
|
char * p = strrchr(argv[1], '/');
|
|
if (p) argv[1] = p + 1;
|
|
p = strchr(argv[1], '.');
|
|
if (p) *p = '\0';
|
|
}
|
|
#endif
|
|
sprintf(buf, "%.10s:d=%s,o=%d,b=%d:",
|
|
argv[1], lengths[deflen], defscale, ni->beats);
|
|
write(1, buf, strlen(buf));
|
|
|
|
for (i = 0; i < ni->nrnotes; i++)
|
|
{
|
|
char * p = buf;
|
|
struct Notes * note = &ni->note[i];
|
|
|
|
if (i)
|
|
*p++ = ',';
|
|
|
|
if (note->length != deflen) {
|
|
strcpy(p, lengths[note->length]);
|
|
p+= strlen(p);
|
|
}
|
|
strcpy(p , notes[note->note]);
|
|
p+= strlen(p);
|
|
|
|
if (note->note && note->scale != defscale)
|
|
*p++ = note->scale + '0'; /* supports scales up to 9 */
|
|
if (note->lextra)
|
|
*p++ = '.';
|
|
|
|
write(1, buf, p - buf);
|
|
}
|
|
write(1, "\n", 1);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* MIDI2RTTTL */
|
|
|