home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C++ Games Programming
/
CPPGAMES.ISO
/
mt
/
mf_to_mt.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-01-14
|
17KB
|
521 lines
/* mf_to_mt.c converts Standard MIDI Files 1.0 format to MT song files */
/* `MIDI Sequencing In C', Jim Conger, M&T Books, 1989 */
/* #define TURBOC 1 Define if using TURBOC, leave out for Microsoft */
#include "standard.h"
#include <stdio.h>
#include <string.h>
#ifdef TURBOC
#include <alloc.h>
#else
#include <malloc.h>
#endif
#define NTRACK 8
#define TITLE_WIDE 51
#define TRACK_NAME_WIDE 9
#define MT_TICKS 120
#define NBYTES 60000 /* default track data buffer */
#define TIME_OUT 0xF8
#define MEAS_END 0xF9
#define ALL_END 0xFC
#define META 0xFF /* meta event codes */
#define TEXTEVENT 0x01
#define SEQNAME 0x03
#define INSNAME 0x04
#define CHANPREF 0x20
#define ENDTRACK 0x2F
#define SETTEMPO 0x51
#define TIMESIG 0X58
/* function prototypes */
char *copy_event(unsigned char *buf, unsigned char nbyte, unsigned char b1,
unsigned char b2, unsigned char b3, unsigned char b4);
void skip_byte(int n, FILE *infile);
long get_var_len(FILE *infile);
long get_long(FILE *infile);
int get_int(FILE *infile);
int get_tempo(FILE *infile);
void get_string(char *c, FILE *infile, int size);
void write_buf(char *sp, char *ep, FILE *outfile);
int find_str(FILE *infile, char *str);
long ratio_time(long input_time, int division);
FILE *open_file(char *filename, char *status);
void exit_program(void);
void
main(argc, argv)
int argc;
char *argv[];
{
unsigned char *buf, *cp, nbyte, b0, b1, b2, b3, runstatus, chunk[5],
fbyte, sbyte;
unsigned char *title, *metrate, *meter, *pitchbend, *exclusive, *endp;
unsigned char *trackname[NTRACK], *channel[NTRACK], *events[NTRACK],
*status[NTRACK], *volume[NTRACK];
int i, n, len, length, format, trk, tracks, end_track, meas_time,
meas_ticks, add_data, division;
long ln, time, eventcount;
FILE *infile, *outfile;
if (argc < 3){ /* did not specify infile and outfile on command line */
exit_program();
}
infile = open_file(argv[1], "rb");
outfile = open_file(argv[2], "wb");
/* buf is a buffer that holds entire track until written to file */
buf = (char *)malloc(NBYTES);
if (buf == NULL){
fputs("\nCould not allocate memory for track data.", stdout);
exit(0);
}
/* intialization portion of program */
title = buf; /* find pointers to all header items */
metrate = title + TITLE_WIDE;
meter = metrate + sizeof(int);
pitchbend = meter + sizeof(int);
exclusive = pitchbend + sizeof(int);
endp = exclusive + sizeof(int);
for (i = 0; i < NTRACK; i++){
trackname[i] = endp;
channel[i] = trackname[i] + TRACK_NAME_WIDE;
events[i] = channel[i] + sizeof(int);
status[i] = events[i] + sizeof(long);
volume[i] = status[i] + sizeof(int);
endp = volume[i] + sizeof(int); /* endp points to start of */
} /* track data area */
cp = buf; /* clear buffer area in header zone */
while (cp != endp){
*cp++ = '\0';
}
strcpy(title, "Not titled."); /* put in default values */
n = 100;
memcpy(metrate, &n, sizeof(int));
n = 4;
memcpy(meter, &n, sizeof(int));
meas_ticks = n * MT_TICKS;
n = 0;
memcpy(pitchbend, &n, sizeof(int));
memcpy(exclusive, &n, sizeof(int));
for (i = 0; i < NTRACK; i++){
strcpy(trackname[i], "< >");
memcpy(channel[i], &i, sizeof(int));
ln = 1;
memcpy(events[i], &ln, sizeof(long));
n = 0;
memcpy(status[i], &n, sizeof(int));
n = 100;
memcpy(volume[i], &n, sizeof(int));
}
/* end initialization */
/* read by any junk in front of MThd chunk, such as 128 byte */
/* header on files from a Mac. */
if (find_str(infile, "MThd") == 0){
fputs("\nInput file lacks header chunk, can't read it", stdout);
exit(0);
}
length = get_long(infile);
format = get_int(infile);
if (format != 1){
fputs("\nInput file does not use MIDI files format 1, can't read it.",
stdout);
exit(0);
}
tracks = get_int(infile);
division = get_int(infile);
fputs("\nConverting to MT .SNG file format (ESC to exit)...\n", stdout);
/* read each track's data in sequence and write to the buffer */
for (trk = 0; trk < tracks && trk < NTRACK + 1; trk++){
eventcount = runstatus = add_data = 0;
get_string(chunk, infile, 4);
length = get_long(infile);
if (strcmp(chunk, "MTrk")){
fputs("\nIgnored unrecognized chunk type ", stdout);
fputs(chunk, stdout);
fputc('\n', stdout);
skip_byte(length, infile);
}
/* A track chunk, so reading data until end found */
else{
meas_time = end_track = 0;
while (!end_track){
if (kbhit()){ /* allow keypress to stop program */
if (ESC == getch()){
fclose(infile);
fclose(outfile);
fputs("\nInterupted before conversion completed.\n",
stdout);
exit(0);
}
}
time = ratio_time(get_var_len(infile), division);
fbyte = fgetc(infile);
if (fbyte == META){ /* meta event ? */
sbyte = fgetc(infile);
len = (int)get_var_len(infile);
if (len == -1){
end_track = 1;
fputs("\nRan off end of input file.\n", stdout);
}
switch (sbyte){
case (1): /* text meta event */
case (3): /* sequence/track name */
case (4): /* instrument name */
if (trk == 0){
if (len < TITLE_WIDE - 1)
get_string(title, infile, len);
else{
get_string(title, infile, TITLE_WIDE - 1);
skip_byte(len - (TITLE_WIDE - 1), infile);
}
}
else {
if (len < TRACK_NAME_WIDE - 1)
get_string(trackname[trk - 1], infile, len);
else{
get_string(trackname[trk - 1], infile,
TRACK_NAME_WIDE - 1);
skip_byte(len - (TRACK_NAME_WIDE - 1),
infile);
}
}
break;
case (0x20): /* MIDI channel prefix */
*channel[trk - 1] = (unsigned char)fgetc(infile);
break;
case (0x2F): /* end of track */
end_track = 1;
break;
case (0x51): /* set tempo */
n = get_tempo(infile);
memcpy(metrate, &n, sizeof(int));
break;
case (0x58): /* time signature */
n = fgetc(infile);
memcpy(meter, &n, sizeof(int));
meas_ticks = *meter * MT_TICKS;
skip_byte(3, infile);
break;
default: /* ignore all other meta events */
skip_byte(len, infile);
break;
}
}
else { /* if not a META event */
if (!add_data){ /* if first event on track */
add_data = 1;
endp = copy_event(endp, 2, 0, MEAS_END, 0, 0);
eventcount++;
}
/* if a four byte MIDI message */
if (fbyte >= 0x80 && fbyte <= 0xBF ||
fbyte >= 0xE0 && fbyte <= 0xEF){
nbyte = 4;
b1 = runstatus = fbyte;
b2 = (char)fgetc(infile);
b3 = (char)fgetc(infile);
n = b1 & 0x0F;
memcpy(channel[trk - 1], &n, sizeof(int));
}
/* if a three byte MIDI message */
else if (fbyte >= 0xC0 && fbyte <= 0xDF){
nbyte = 3;
b1 = runstatus = fbyte;
b2 = (char)fgetc(infile);
b3 = 0;
}
else{ /* running status, so only two bytes fetched */
b1 = runstatus;
b2 = fbyte;
if (nbyte > 3)
b3 = (char)fgetc(infile);
else
b3 = 0;
}
while (time >= 0){ /* write event */
if (meas_time + time < meas_ticks){
if (time < 240){
b0 = (unsigned char)time;
endp = copy_event(endp, nbyte, b0, b1,
b2, b3);
eventcount++;
meas_time += time;
time = -1; /* wrote event, so quit loop */
}
else{ /* need to add a time out marker */
endp = copy_event(endp, 1, TIME_OUT, 0, 0, 0);
eventcount++;
time -= 240;
meas_time += 240;
}
}
else{ /* need measure end before event */
if (meas_ticks - meas_time < 240){
sbyte = meas_ticks - meas_time;
endp = copy_event(endp, 2, sbyte, MEAS_END,
0, 0);
eventcount++;
time -= sbyte;
meas_time = 0;
}
else{ /* need to add a time out marker */
endp = copy_event(endp, 1, TIME_OUT, 0, 0, 0);
eventcount++;
time -= 240;
meas_time += 240;
}
}
}
}
} /* while (!end_track) */
if (add_data){
endp = copy_event(endp, 2, 0, ALL_END, 0, 0);
eventcount++;
}
if (trk > 0){
memcpy(events[trk - 1], &eventcount, sizeof(long));
}
} /* if track chunk */
} /* for each track */
/* add starting MEAS_END for any empty tracks */
for (trk = tracks; trk < NTRACK + 1; trk++){
endp = copy_event(endp, 2, 0, MEAS_END, 0, 0);
}
write_buf(buf, endp, outfile); /* send all data to outfile */
fclose(infile); /* close all files */
fclose(outfile);
fputs("\nCompleted data conversion.", stdout);
exit(0);
}
/* copies five char values to one memory area in sequence */
char
*copy_event(buf, nbyte, b1, b2, b3, b4)
unsigned char *buf, nbyte, b1, b2, b3, b4;
{
*buf++ = nbyte;
*buf++ = b1;
*buf++ = b2;
*buf++ = b3;
*buf++ = b4;
return (buf);
}
/* skip over n bytes in infile */
void
skip_byte(n, infile)
int n;
FILE *infile;
{
int i;
for (i = 0; i < n; i++)
fgetc(infile);
}
/* Get a variable length number from infile. Variable length numbers are */
/* a part of the MIDI files specification. Bit 7 in each byte is used to */
/* designate continuance of the number into the next byte. The lower 7 */
/* bits hold the numeric information. This allows numbers of different */
/* magnitudes to take up the minimum space on disk. */
long
get_var_len(infile)
FILE *infile;
{
register long ln;
unsigned char c;
int i;
i = ln = 0;
do {
c = fgetc(infile);
ln = (ln << 7) + (c & 0x7F);
i++;
} while (c & 0x80 && i < 5);
return (ln);
}
/* read and return long int value from file infile */
long
get_long(infile)
FILE *infile;
{
int i;
register long ln;
ln = 0;
for (i = 0; i < sizeof(long); i++){
ln = (ln << 8) + fgetc(infile);
}
return (ln);
}
/* read and return int value from file infile */
int
get_int(infile)
FILE *infile;
{
int i, n;
n = 0;
for (i = 0; i < sizeof(int); i++){
n = (n << 8) + fgetc(infile);
}
return (n);
}
/* read string of size into memory pointed to by c */
void
get_string(c, infile, size)
char *c;
FILE *infile;
int size;
{
int i;
for (i = 0; i < size; i++){
*c++ = (char)fgetc(infile);
}
*c = '\0';
}
/* read MIDI files tempo data in milliseconds/beat and return as MT tempo */
/* in beats per minute */
int
get_tempo(infile)
FILE *infile;
{
long msec_beat;
int i;
msec_beat = 0;
for (i = 0; i < 3; i++){
msec_beat = (msec_beat << 8) + fgetc(infile);
}
i = (int)(60000000/msec_beat);
return(i);
}
/* send bytes pointed to from s to e to outfile */
void
write_buf(s, e, outfile)
char *s, *e;
FILE *outfile;
{
while (s != e)
fputc(*s++, outfile);
}
/* find string in input stream, return 1 if found, 0 if not, leaves infile */
/* pointing to end of str, ready for next read */
int
find_str(infile, str)
FILE *infile;
char *str;
{
char *s;
int c;
s = str;
while (*s != '\0'){
c = fgetc(infile);
if (c == EOF)
return(0);
else if (c == *s)
s++;
else
s = str;
}
return(1);
}
/* Round time to nearest MT clock tick. Roundoff errors can accumulate if */
/* time values are truncated to match MT's clock. */
long
ratio_time(input_time, division)
long input_time;
int division;
{
int time;
float f, divf, intime;
static int odd_even = 0;
divf = division;
intime = input_time;
time = f = (intime * MT_TICKS) / divf;
if (f - time == 0.5){ /* alternate distribution of even time */
if (odd_even){ /* splits to avoid one track gaining on */
odd_even = 0; /* another. */
return(time);
}
else{
odd_even = 1;
return(time + 1);
}
}
else if (f - time > 0.5){ /* otherwise, just round times */
return(time + 1);
}
return(time);
}
/* Open a file. status is "rb" for read binary, "wb" for write binary, etc */
FILE
*open_file(filename, status)
char *filename, *status;
{
FILE *file;
file = fopen(filename, status);
if (file == NULL){
fputs("\nCould not open file ", stdout);
fputs(filename, stdout);
fputc('\n', stdout);
exit(0);
}
return(file);
}
void
exit_program()
{
fputs("\nUsage: mf_to_mt infile outfile", stdout);
fputs("\nWhere infile is the Standard MIDI file file name;", stdout);
fputs("\n outfile is the MT .SNG file name for output.\n", stdout);
exit(0);
}