| /* | |
| * 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 available 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. | |
| * | |
| * February 1993 - Minor adjustments, Greg Lee: | |
| * (1) can now set the global variable Mf_interactive to 1 to prevent the | |
| * reading functions from looking for file and track headers | |
| * (2) can now write system exclusive data with | |
| * mf_write_midi_event(delta_time, system_exclusive, 0, data, size) | |
| * (3) changed definition of 'sequencer_specific' in midifile.h to 0x7f | |
| * (4) changed mf_write_tempo to take additional delta_time as first argument | |
| * (since delta need not be zero) | |
| * (5) added function mf_write_seqnum(unsigned long delta_time, unsigned seqnum) | |
| * (6) changed mf_write_midi_event to use running status | |
| * (7) removed the code to write an end of track meta event automatically | |
| * -- this must now be done by the user of the library (I changed | |
| * it because I need to be able to control the time delta of this | |
| * meta event) | |
| * (8) added global variables Mf_division, Mf_currtempo, Mf_realtime, which | |
| * are updated by the reading functions. Mf_realtime is useful, | |
| * because Mf_currtime does not really measure time at all, since | |
| * its units change value at every tempo change. Mf_realtime is | |
| * the midi-time elapsed in units of 1/16 of a centisecond (but it | |
| * does not handle SMPTE times) | |
| * (9) maintains a history of tempo settings to update Mf_currtempo, | |
| * to handle tempo tracks. | |
| * (10) if there is an Mf_error function, the error routine no longer | |
| * exits, leaving it to the application to do this. | |
| * (11) chanmessage skips over invalid c1 command bytes > 127 and | |
| * adjusts invalid c2 argument byte > 127 to 127. | |
| * (12) readmt returns EOF when it encounters a 0 or 0x1a byte instead of an expected | |
| * header string (some midi files have padding at end). | |
| */ | |
| #define NO_LC_DEFINES | |
| #include "midifile.h" | |
| #ifdef NO_LC_DEFINES | |
| #define system_exclusive 0xf0 | |
| #define meta_event 0xFF | |
| #define set_tempo 0x51 | |
| #define lowerbyte(x) ((unsigned char)(x & 0xff)) | |
| #define upperbyte(x) ((unsigned char)((x & 0xff00)>>8)) | |
| #endif | |
| #define NULLFUNC 0 | |
| #if 0 | |
| #define NULL 0 | |
| #endif | |
| #define THINK | |
| #ifdef THINK | |
| #include <stdlib.h> | |
| #endif | |
| #include <stdio.h> | |
| #include <limits.h> | |
| #include <string.h> | |
| /*void exit(), free();*/ | |
| /* public stuff */ | |
| /* Functions to be called while processing the MIDI file. */ | |
| int (*Mf_getc) () = NULLFUNC; | |
| void (*Mf_error) () = NULLFUNC; | |
| void (*Mf_header) () = NULLFUNC; | |
| void (*Mf_trackstart) () = NULLFUNC; | |
| void (*Mf_trackend) () = NULLFUNC; | |
| void (*Mf_noteon) () = NULLFUNC; | |
| void (*Mf_noteoff) () = NULLFUNC; | |
| void (*Mf_pressure) () = NULLFUNC; | |
| void (*Mf_parameter) () = NULLFUNC; | |
| void (*Mf_pitchbend) () = NULLFUNC; | |
| void (*Mf_program) () = NULLFUNC; | |
| void (*Mf_chanpressure) () = NULLFUNC; | |
| void (*Mf_sysex) () = NULLFUNC; | |
| void (*Mf_arbitrary) () = NULLFUNC; | |
| void (*Mf_metamisc) () = NULLFUNC; | |
| void (*Mf_seqnum) () = NULLFUNC; | |
| void (*Mf_eot) () = NULLFUNC; | |
| void (*Mf_smpte) () = NULLFUNC; | |
| void (*Mf_tempo) () = NULLFUNC; | |
| void (*Mf_timesig) () = NULLFUNC; | |
| void (*Mf_keysig) () = NULLFUNC; | |
| void (*Mf_seqspecific) () = NULLFUNC; | |
| void (*Mf_text) () = NULLFUNC; | |
| /* Functions to implement in order to write a MIDI file */ | |
| int (*Mf_putc) () = NULLFUNC; | |
| int (*Mf_writetrack) () = NULLFUNC; | |
| int (*Mf_writetempotrack) () = NULLFUNC; | |
| int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */ | |
| /* not collapsed. */ | |
| int Mf_interactive = 0; /* 1 => file and track headers are not required */ | |
| unsigned long Mf_currtime = 0L; /* current time in delta-time units */ | |
| unsigned long Mf_realtime = 0L; /* current time in 1/16 centisecond-time units */ | |
| static double Mf_f_realtime = 0;/* as above, floating */ | |
| static double old_f_realtime = 0; | |
| int Mf_division = 96; | |
| unsigned long Mf_currtempo = 500000; | |
| static unsigned long old_currtempo = 500000; | |
| static unsigned long old_realtime = 0; | |
| static unsigned long old_currtime = 0; | |
| static unsigned long revised_time = 0; | |
| static unsigned long tempo_change_time = 0; | |
| #define MAX_HISTORY 512 | |
| static unsigned long tempo_history[MAX_HISTORY]; | |
| static unsigned long tempo_history_time[MAX_HISTORY]; | |
| static int tempo_history_count = 0; | |
| /* private stuff */ | |
| static long Mf_toberead = 0L; | |
| static long Mf_numbyteswritten = 0L; | |
| static long readvarinum (); | |
| static long read32bit (); | |
| static long to32bit (); | |
| static int read16bit (); | |
| static int to16bit (); | |
| static char *msg (); | |
| static void readheader (); | |
| static int readtrack (); | |
| static void badbyte (); | |
| static void metaevent (); | |
| static void sysex (); | |
| static void chanmessage (); | |
| static void msginit (); | |
| static int msgleng (); | |
| static void msgadd (); | |
| static void biggermsg (); | |
| static int eputc (); | |
| double mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo); | |
| int mf_write_meta_event (); | |
| void mf_write_tempo (); | |
| void mf_write_seqnum (); | |
| void WriteVarLen (); | |
| #ifdef READ_MODS | |
| #include "mp_mod.c" | |
| static int mod_file_flag = 0; | |
| #endif /* READ_MODS */ | |
| static int force_exit; | |
| void | |
| mfread () | |
| { | |
| force_exit = 0; | |
| if (Mf_getc == NULLFUNC) | |
| mferror ("mfread() called without setting Mf_getc"); | |
| readheader (); | |
| #ifdef READ_MODS | |
| if (mod_file_flag) | |
| do_module(); | |
| else | |
| #endif | |
| while (readtrack () && !force_exit) | |
| ; | |
| } | |
| /* for backward compatibility with the original lib */ | |
| void | |
| midifile () | |
| { | |
| mfread (); | |
| } | |
| static | |
| int | |
| readmt (s) /* read through the "MThd" or "MTrk" header string */ | |
| char *s; | |
| { | |
| int n = 0; | |
| char *p = s; | |
| int c; | |
| while (n++ < 4 && (c = (*Mf_getc) ()) != EOF) | |
| { | |
| if (c != *p++) | |
| { | |
| char buff[32]; | |
| if (!c) return(EOF); | |
| if (c == 0x1a) return(EOF); | |
| (void) strcpy (buff, "expecting "); | |
| (void) strcat (buff, s); | |
| mferror (buff); | |
| break; | |
| } | |
| } | |
| return (c); | |
| } | |
| static | |
| int | |
| egetc () /* read a single character and abort on EOF */ | |
| { | |
| int c = (*Mf_getc) (); | |
| if (c == EOF) { | |
| mferror ("premature EOF"); | |
| force_exit = 1; | |
| } | |
| Mf_toberead--; | |
| return (c); | |
| } | |
| static | |
| void | |
| readheader () /* read a header chunk */ | |
| { | |
| int format, ntrks, division; | |
| Mf_division = 96; | |
| Mf_currtempo = 500000; | |
| old_currtempo = 500000; | |
| tempo_history_count = 0; | |
| tempo_history[tempo_history_count] = Mf_currtempo; | |
| tempo_history_time[tempo_history_count] = 0; | |
| if (Mf_interactive) | |
| { | |
| Mf_toberead = 0; | |
| format = 0; | |
| ntrks = 1; | |
| division = 96; | |
| } | |
| else | |
| #ifdef READ_MODS | |
| if (!strncmp(Mf_file_contents, "MThd", 4)) | |
| #endif | |
| { | |
| if (readmt ("MThd") == EOF) | |
| return; | |
| Mf_toberead = read32bit (); | |
| format = read16bit (); | |
| ntrks = read16bit (); | |
| Mf_division = division = read16bit (); | |
| } | |
| #ifdef READ_MODS | |
| else | |
| { | |
| format = 0; | |
| ntrks = 1; | |
| division = Mf_division; | |
| Mf_toberead = 0; | |
| mod_file_flag = 1; | |
| } | |
| #endif | |
| if (Mf_header) | |
| (*Mf_header) (format, ntrks, division); | |
| /* flush any extra stuff, in case the length of header is not 6 */ | |
| while (Mf_toberead > 0 && !force_exit) | |
| (void) egetc (); | |
| } | |
| /*#define DEBUG_TIMES*/ | |
| static | |
| unsigned long | |
| find_tempo() | |
| { | |
| int i; | |
| unsigned long old_tempo = Mf_currtempo; | |
| unsigned long new_tempo = Mf_currtempo; | |
| for (i = 0; i <= tempo_history_count; i++) { | |
| if (tempo_history_time[i] <= Mf_currtime) old_tempo = tempo_history[i]; | |
| new_tempo = tempo_history[i]; | |
| if (tempo_history_time[i] > revised_time) break; | |
| } | |
| if (i > tempo_history_count || tempo_history_time[i] > Mf_currtime) { | |
| #ifdef DEBUG_TIMES | |
| printf("[past %lu, old_tempo %lu]\n", tempo_history_time[i], old_tempo); | |
| #endif | |
| revised_time = Mf_currtime; | |
| return(old_tempo); | |
| } | |
| tempo_change_time = revised_time = tempo_history_time[i]; | |
| #ifdef DEBUG_TIMES | |
| printf("[revised_time %lu, new_tempo %lu]\n", revised_time, new_tempo); | |
| #endif | |
| return(new_tempo); | |
| } | |
| static | |
| int | |
| readtrack () /* 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 (Mf_interactive) | |
| { | |
| Mf_toberead = INT_MAX; | |
| } | |
| else | |
| { | |
| if (readmt ("MTrk") == EOF) | |
| return (0); | |
| Mf_toberead = read32bit (); | |
| } | |
| Mf_currtime = Mf_realtime = 0; | |
| Mf_f_realtime = old_f_realtime = 0; | |
| old_currtime = old_realtime = 0; | |
| Mf_currtempo = find_tempo(); | |
| if (Mf_trackstart) | |
| (*Mf_trackstart) (); | |
| while (!force_exit && (Mf_interactive || Mf_toberead > 0)) | |
| { | |
| if (Mf_interactive) | |
| Mf_currtime += 1; | |
| else | |
| { | |
| double delta_secs; | |
| unsigned long delta_ticks = readvarinum (); | |
| revised_time = Mf_currtime; | |
| Mf_currtime += delta_ticks; /* delta time */ | |
| /* | |
| * Step through each tempo change from old_currtime up to now, | |
| * revising Mf_realtime after each change. | |
| */ | |
| while (revised_time < Mf_currtime) { | |
| unsigned long save_time = revised_time; | |
| unsigned long save_tempo = Mf_currtempo; | |
| Mf_currtempo = find_tempo(); | |
| if (Mf_currtempo != old_currtempo) { | |
| old_currtempo = Mf_currtempo; | |
| old_realtime = Mf_realtime; | |
| if (revised_time != tempo_change_time) { | |
| old_f_realtime = Mf_f_realtime; | |
| old_currtime = save_time; | |
| } | |
| delta_secs = mf_ticks2sec (revised_time-old_currtime, Mf_division, save_tempo); | |
| #ifdef DEBUG_TIMES | |
| printf("d(rev %lu - old %lu, div %d, tempo %lu) = %.3f\n", | |
| revised_time, old_currtime, Mf_division, save_tempo, delta_secs * 1600.0); | |
| #endif | |
| Mf_f_realtime = old_f_realtime + delta_secs * 1600.0; | |
| Mf_realtime = (unsigned long)(0.5 + Mf_f_realtime); | |
| #ifdef DEBUG_TIMES | |
| printf("\tt=%lu ticks ( = %lu csec/16 < old %.2f + %.2f)\n", Mf_currtime, Mf_realtime, | |
| old_f_realtime, delta_secs * 1600.0); | |
| #endif | |
| if (revised_time == tempo_change_time) { | |
| old_currtime = revised_time; | |
| old_f_realtime = Mf_f_realtime; | |
| } | |
| } | |
| else { | |
| delta_secs = mf_ticks2sec (revised_time-old_currtime, Mf_division, Mf_currtempo); | |
| #ifdef DEBUG_TIMES | |
| printf("d(rev %lu - old %lu, div %d, tempo %lu) = %.3f\n", | |
| revised_time, old_currtime, Mf_division, Mf_currtempo, delta_secs * 1600.0); | |
| #endif | |
| Mf_f_realtime = old_f_realtime + delta_secs * 1600.0; | |
| Mf_realtime = (unsigned long)(0.5 + Mf_f_realtime); | |
| #ifdef DEBUG_TIMES | |
| printf("\tt=%lu ticks ( = %lu csec/16 < old %.2f + %.2f)\n", Mf_currtime, Mf_realtime, | |
| old_f_realtime, delta_secs * 1600.0); | |
| #endif | |
| } | |
| } | |
| } | |
| c = egetc (); | |
| if (sysexcontinue && c != 0xf7) | |
| mferror ("didn't find expected continuation of a sysex"); | |
| if ((c & 0x80) == 0) | |
| { /* running status? */ | |
| if (status == 0) | |
| mferror ("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 (); | |
| chanmessage (status, c1, (needed > 1) ? egetc () : 0); | |
| continue;; | |
| } | |
| switch (c) | |
| { | |
| case 0xff: /* meta event */ | |
| type = egetc (); | |
| lookfor = Mf_toberead - readvarinum (); | |
| msginit (); | |
| while (Mf_toberead > lookfor) | |
| msgadd (egetc ()); | |
| metaevent (type); | |
| break; | |
| case 0xf0: /* start of system exclusive */ | |
| lookfor = Mf_toberead - readvarinum (); | |
| msginit (); | |
| msgadd (0xf0); | |
| while (Mf_toberead > lookfor) | |
| msgadd (c = egetc ()); | |
| if (c == 0xf7 || Mf_nomerge == 0) | |
| sysex (); | |
| else | |
| sysexcontinue = 1; /* merge into next msg */ | |
| break; | |
| case 0xf7: /* sysex continuation or arbitrary stuff */ | |
| 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_trackend) | |
| (*Mf_trackend) (); | |
| return (1); | |
| } | |
| static | |
| void | |
| badbyte (c) | |
| int c; | |
| { | |
| char buff[32]; | |
| (void) sprintf (buff, "unexpected byte: 0x%02x", c); | |
| mferror (buff); | |
| } | |
| static | |
| void | |
| metaevent (int 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: | |
| /* These are all text events */ | |
| if (Mf_text) | |
| (*Mf_text) (type, leng, m); | |
| break; | |
| case 0x2f: /* End of Track */ | |
| if (Mf_eot) | |
| (*Mf_eot) (); | |
| break; | |
| case 0x51: /* Set tempo */ | |
| if (Mf_tempo) | |
| (*Mf_tempo) (Mf_currtempo = to32bit (0, m[0], m[1], m[2])); | |
| if (tempo_history[tempo_history_count] == Mf_currtempo) break; | |
| if (tempo_history_time[tempo_history_count] > Mf_currtime) break; | |
| if (tempo_history_count < MAX_HISTORY - 1) tempo_history_count++; | |
| tempo_history[tempo_history_count] = Mf_currtempo; | |
| tempo_history_time[tempo_history_count] = Mf_currtime; | |
| 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_seqspecific) | |
| (*Mf_seqspecific) (leng, m); | |
| break; | |
| default: | |
| if (Mf_metamisc) | |
| (*Mf_metamisc) (type, leng, m); | |
| } | |
| } | |
| static | |
| void | |
| sysex () | |
| { | |
| if (Mf_sysex) | |
| (*Mf_sysex) (msgleng (), msg ()); | |
| } | |
| static | |
| void | |
| chanmessage (status, c1, c2) | |
| int status; | |
| int c1, c2; | |
| { | |
| int chan = status & 0xf; | |
| /* I found a midi file with Mod Wheel values 128. --gl */ | |
| if (c1 > 127) /*mferror("chanmessage: bad c1") ??*/ return; | |
| if (c2 > 127) c2 = 127; | |
| switch (status & 0xf0) | |
| { | |
| case 0x80: | |
| if (Mf_noteoff) | |
| (*Mf_noteoff) (chan, c1, c2); | |
| break; | |
| case 0x90: | |
| if (Mf_noteon) | |
| (*Mf_noteon) (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; | |
| } | |
| } | |
| /* readvarinum - read a varying-length number, and return the */ | |
| /* number of characters it took. */ | |
| 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 (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 (c1, c2) | |
| int c1, 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 int | |
| read16bit () | |
| { | |
| int c1, c2; | |
| c1 = egetc (); | |
| c2 = egetc (); | |
| return to16bit (c1, c2); | |
| } | |
| /* static */ | |
| void | |
| mferror (s) | |
| char *s; | |
| { | |
| if (Mf_error) | |
| (*Mf_error) (s); | |
| else 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 char *Msgbuff = NULL; /* message buffer */ | |
| static int Msgsize = 0; /* Size of currently allocated Msg */ | |
| static int Msgindex = 0; /* index of next available location in Msg */ | |
| static | |
| void | |
| msginit () | |
| { | |
| Msgindex = 0; | |
| } | |
| static char * | |
| msg () | |
| { | |
| return (Msgbuff); | |
| } | |
| static | |
| int | |
| msgleng () | |
| { | |
| return (Msgindex); | |
| } | |
| static | |
| void | |
| msgadd (c) | |
| int c; | |
| { | |
| /* If necessary, allocate larger message buffer. */ | |
| if (Msgindex >= Msgsize) | |
| biggermsg (); | |
| Msgbuff[Msgindex++] = c; | |
| } | |
| static | |
| void | |
| biggermsg () | |
| { | |
| /* char *malloc(); */ | |
| char *newmess; | |
| char *oldmess = Msgbuff; | |
| int oldleng = Msgsize; | |
| Msgsize += MSGINCREMENT; | |
| newmess = (char *) malloc ((unsigned) (sizeof (char) * Msgsize)); | |
| if (newmess == NULL) | |
| mferror ("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); | |
| } | |
| Msgbuff = newmess; | |
| } | |
| static int laststatus = 0; | |
| /* | |
| * mfwrite() - The only function 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 similar 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_putc == NULLFUNC) | |
| mferror ("mfmf_write() called without setting Mf_putc"); | |
| if (Mf_writetrack == NULLFUNC) | |
| mferror ("mfmf_write() called without setting Mf_mf_writetrack"); | |
| laststatus = 0; | |
| /* every MIDI file starts with a header */ | |
| mf_write_header_chunk (format, ntracks, division); | |
| laststatus = 0; | |
| /* In format 1 files, the first track is a tempo map */ | |
| if (format == 1 && (Mf_writetempotrack)) | |
| { | |
| (*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 (); | |
| laststatus = 0; | |
| 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_numbyteswritten = 0L; /* the header's length doesn't count */ | |
| if (Mf_writetrack) | |
| { | |
| (*Mf_writetrack) (which_track); | |
| } | |
| /* mf_write End of track meta event */ | |
| /* but this does not necessarily have a delta of 0, so | |
| * I don't want to do it -- leave it up to the user of the | |
| * library functions to do | |
| * --gl | |
| eputc(0); | |
| eputc(laststatus = meta_event); | |
| eputc(end_of_track); | |
| eputc(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 ("error seeking during final stage of write"); | |
| trklength = 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() */ | |
| /* | |
| * 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; | |
| int chan, type; | |
| unsigned long size; | |
| char *data; | |
| { | |
| int i; | |
| 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 */ | |
| if (type == system_exclusive || type == 0xf7) | |
| { | |
| c = type; | |
| laststatus = 0; | |
| } | |
| else | |
| c = type | chan; | |
| if (chan > 15) | |
| perror ("error: MIDI channel greater than 16\n"); | |
| if (laststatus != c) | |
| eputc (laststatus = c); | |
| if (type == system_exclusive || type == 0xf7) | |
| WriteVarLen (size); | |
| /* write out the data bytes */ | |
| for (i = 0; i < (int)size; i++) | |
| eputc (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 (laststatus = meta_event); | |
| /* The type of meta event */ | |
| eputc (type); | |
| /* The length of the data bytes to follow */ | |
| WriteVarLen (size); | |
| for (i = 0; i < (int)size; i++) | |
| { | |
| if (eputc (data[i]) != data[i]) | |
| return (-1); | |
| } | |
| return (size); | |
| } /* end mf_write_meta_event */ | |
| void | |
| mf_write_tempo (delta_time, tempo) | |
| unsigned long delta_time; | |
| unsigned long tempo; | |
| { | |
| /* Write tempo */ | |
| /* all tempos are written as 120 beats/minute, */ | |
| /* expressed in microseconds/quarter note */ | |
| WriteVarLen (delta_time); | |
| eputc (laststatus = meta_event); | |
| eputc (set_tempo); | |
| eputc (3); | |
| eputc ((unsigned) (0xff & (tempo >> 16))); | |
| eputc ((unsigned) (0xff & (tempo >> 8))); | |
| eputc ((unsigned) (0xff & tempo)); | |
| } | |
| void | |
| mf_write_seqnum (delta_time, seqnum) | |
| unsigned long delta_time; | |
| unsigned seqnum; | |
| { | |
| WriteVarLen (delta_time); | |
| eputc (laststatus = meta_event); | |
| eputc (0); | |
| eputc ((unsigned) (0xff & (seqnum >> 8))); | |
| eputc ((unsigned) (0xff & seqnum)); | |
| } | |
| unsigned long | |
| mf_sec2ticks (secs, division, tempo) | |
| int division; | |
| unsigned long tempo; | |
| double secs; | |
| { | |
| return (unsigned long) (((secs * 1000.0) / 4.0 * division) / tempo); | |
| } | |
| /* | |
| * 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 ((unsigned) (buffer & 0xff)); | |
| if (buffer & 0x80) | |
| buffer >>= 8; | |
| else | |
| return; | |
| } | |
| } /* end of WriteVarLen */ | |
| /* | |
| * 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. | |
| * | |
| */ | |
| double | |
| mf_ticks2sec (ticks, division, tempo) | |
| int division; | |
| unsigned long tempo; | |
| unsigned long ticks; | |
| { | |
| double smpte_format, smpte_resolution; | |
| if (division > 0) | |
| return ((double) (((double) (ticks) * (double) (tempo)) / ((double) (division) * 1000000.0))); | |
| else | |
| { | |
| smpte_format = upperbyte (division); | |
| smpte_resolution = lowerbyte (division); | |
| return (double) ((double) ticks / (smpte_format * smpte_resolution * 1000000.0)); | |
| } | |
| } /* end of ticks2sec() */ | |
| /* | |
| * 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 ((unsigned) ((data >> 24) & 0xff)); | |
| eputc ((unsigned) ((data >> 16) & 0xff)); | |
| eputc ((unsigned) ((data >> 8) & 0xff)); | |
| eputc ((unsigned) (data & 0xff)); | |
| } | |
| void | |
| write16bit (data) | |
| int data; | |
| { | |
| eputc ((unsigned) ((data & 0xff00) >> 8)); | |
| eputc ((unsigned) (data & 0xff)); | |
| } | |
| /* write a single character and abort on error */ | |
| static int | |
| eputc (c) | |
| 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); | |
| } |