/* * seqmread.c * * Convert a MIDI file to a seq. */ /* Copyright 1989 Carnegie Mellon University */ /***************************************************************************** * Change Log * Date | who : Change *-----------+----------------------------------------------------------------- * 17-Feb-92 | GWL : only one stdio.h * : fix to satisfy compiler: void returns, time_type giotime(), int filegetc() *****************************************************************************/ #include "switches.h" #include "stdio.h" #include "cext.h" #include "cmdline.h" #include "midifns.h" /* to get time_type */ #include "timebase.h" #include "moxc.h" /* to get debug declared */ #include "seq.h" #include "seqread.h" /* to get scale */ #include "seqmread.h" #include "userio.h" #include "ctype.h" #include "midifile.h" #include "tempomap.h" int filegetc(); void initfuncs(); void prtime(); void snding_free(); typedef struct snding_struct { struct snding_struct *next; event_type event_ptr; int pitch; int channel; } snding_node, *snding_type; #define snding_alloc() (snding_type) memget(sizeof(snding_node)) #define snding_free(s) memfree(s, sizeof(snding_node)) snding_type snding_list = NULL; tempomap_type the_tempomap; event_type initial_clock; /* remember the first clock event */ long prev_ticksize; /* remember the previous ticksize */ int sysex_id = 0; void smf_noteoff(); void smf_error(); void smf_header(); void smf_trackstart(); void smf_trackend(); void smf_noteon(); void smf_pressure(); void smf_parameter(); void smf_pitchbend(); void smf_program(); void smf_chanpressure(); void smf_sysex(); void smf_metamisc(); void smf_metaseq(); void smf_metaeot(); void smf_timesig(); void smf_smpte(); void smf_tempo(); void smf_keysig(); void smf_metaspecial(); void smf_metatext(); void smf_arbitrary(); private seq_type the_score; static FILE *F; int filegetc() { /* int temp = getc(F); printf(" %x ", temp);*/ return(int)(getc(F)); } void seq_read_smf(seq, fp) seq_type seq; FILE *fp; { F = fp; initfuncs(); sysex_id = 0; /* sysex in seq has to correspond to a symbol */ the_score = seq; /* current sequence is a global within this module */ if (!seq) return; the_tempomap = tempomap_create(); /* insert an initial clock to correspond to the default midifile tempo (tempomap_create creates a corresponding initial entry in the tempomap) (see smf_tempo for explanation of the scale() call) */ initial_clock = insert_clock(the_score, 0L, 0, 500L << 16); /* scale(24 * 500000, 1 << 16, 24000) */ if (!the_tempomap) return; Mf_getc = filegetc; midifile(); /* fmac_close(F); -- do not close the file because the caller might try to * close it (in fact XLISP insists on closing it as a side effect of * garbage collection. */ gprintf(TRANS, "\nLoaded Midi file with %ld note(s), %ld ctrl(s).\n\n", seq_notecount(seq), seq_ctrlcount(seq)); seq_reset(seq); while (snding_list) { snding_type snding = snding_list; snding_list = snding_list->next; gprintf(TRANS, "Note-on (key %d, chan %d) has no matching noteoff\n", snding->pitch, snding->channel + 1); snding_free(snding); } tempomap_free(the_tempomap); } /* gio_time -- get the time in millisec for Adagio */ /* * Since Adagio times are (in their precise form) 1/256 ms, we want * a similar time for midifiles, whose natural unit would be microseconds. * We'll shift the microsecond time by 2 to get 1/250 ms = 4 us units * and convert using the scale function when necessary. * Real time is the time of the last tempo change (last_tempo_time) * which is in 4us units + elapsed time. * Elapsed time is the elapsed beats times the beat duration. * Elapsed beats is Mf_currtime - last_tempo_beat. * Beat duration is the specified tempo / division, where specified tempo * is in microseconds, and division is parts per quarternote. */ unsigned long divisions = 24L; time_type gio_time() { return (tempomap_lookup(the_tempomap, Mf_currtime) + 125L) / 250L; } void smf_header(format,ntrks,division) { /* gprintf(TRANS, "Header format=%d ntrks=%d division=%d\n", format,ntrks,division); */ if (format > 1) gprintf(TRANS, "Warning: format %d midi file may not work.\n", format); divisions = division; /* adjust the initial tempochange */ the_tempomap->entries->tempo = 500000L / division; } void smf_trackstart() { /* gprintf(TRANS, "Track start\n"); */ } void smf_trackend() { /* gprintf(TRANS, "Track end\n"); */ } void smf_noteon(chan,pitch,vol) { snding_type snding; if (vol == 0) { /* convert to a noteoff */ smf_noteoff(chan, pitch, 0); return; } /* prtime(); gprintf(TRANS, "Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); */ /* get ready to remember the sounding note */ snding = snding_alloc(); snding->next = snding_list; snding_list = snding; /* enter an event into score and remember it */ snding->event_ptr = insert_note(the_score, gio_time(), 0, chan + 1, pitch, 0L, vol); snding->pitch = pitch; snding->channel = chan; } void smf_noteoff(chan,pitch,vol) { snding_type *snding_ptr; register snding_type snding; /* prtime(); gprintf(TRANS, "Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); */ /* search for the snding record */ for (snding_ptr = &snding_list; (snding = *snding_ptr) && ((snding->pitch != pitch) || (snding->channel != chan)); snding_ptr = &(snding->next)) /* printf("* search *\n") */; if (!snding) { gprintf(TRANS, "Note off %d, channel %d ignored: no note on\n", pitch, chan + 1); } else { event_type event = snding->event_ptr; event->u.note.ndur += (gio_time() - event->ntime) << 8; /* free the snding record */ *snding_ptr = snding->next; snding_free(snding); } } void smf_pressure(chan,pitch,press) { prtime(); gprintf(TRANS, "Pressure, chan=%d pitch=%d press=%d (IGNORED)\n", chan + 1, pitch, press); } void smf_parameter(chan,control,value) { int ctrl = 0; /* prtime(); gprintf(TRANS, "Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value); */ /* see if the control is one of the standard Adagio controls that can be encoded in a special way. If not, ctrl remains at zero. */ switch (control) { case PORTASWITCH: ctrl = PSWITCH_CTRL; break; case MODWHEEL: ctrl = MODWHEEL_CTRL; break; case VOLUME: ctrl = VOLUME_CTRL; break; } if (ctrl) /* then do special ctrl insert and save storage */ insert_ctrl(the_score, gio_time(), 0, ctrl, chan + 1, value); else insert_macctrl(the_score, gio_time(), 0, control, chan + 1, value); } /* smf_pitchbend -- handle a pitch bend event */ /* * NOTE: the midifile code from Tim Thompson has the msb and lsb bytes swapped. * Thus the parameter msb is really the low order byte and lsb is high order. */ void smf_pitchbend(chan,msb,lsb) { /* prtime(); gprintf(TRANS, "Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb); */ insert_ctrl(the_score, gio_time(), 0, BEND_CTRL, chan + 1, ((lsb << 7) + msb) >> 6); } void smf_program(chan,program) { /* prtime(); gprintf(TRANS, "Program, chan=%d program=%d\n",chan+1,program); */ insert_ctrl(the_score, gio_time(), 0, PROGRAM_CTRL, chan + 1, program); } void smf_chanpressure(chan,press) { /* prtime(); gprintf(TRANS, "Channel pressure, chan=%d pressure=%d\n",chan+1,press); */ insert_ctrl(the_score, gio_time(), 0, TOUCH_CTRL, chan + 1, press); } void smf_sysex(leng,mess) int leng; char *mess; { char symb[10]; def_type defn; int i; sprintf(symb, "X%d", sysex_id++); if (leng > 255) { gprintf(TRANS, "sysex too long (%d bytes), ignored\n", leng - 2); return; } /* need to end up with a prefix of [0][length], so add 2 to length; note that this will copy past the end of the message -- this is slightly dangerous and definitely crufty: */ defn = insert_def(the_score, symb, (unsigned char *) mess, leng + 2); /* now fix up the definition by inserting the prefix bytes: */ for (i = leng + 1; i > 1; i--) defn->definition[i] = defn->definition[i - 2]; defn->definition[0] = 0; defn->definition[1] = leng; insert_macro(the_score, gio_time(), 0, defn, 1, 0, NULL); /* prtime(); gprintf(TRANS, "Sysex, leng=%d (IGNORED)\n",leng); */ } void smf_metamisc(type,leng,mess) char *mess; { prtime(); gprintf(TRANS, "Meta event, unrecognized, type=0x%02x leng=%d (IGNORED)\n", type, leng); } void smf_metaspecial(type,leng,mess) char *mess; { prtime(); gprintf(TRANS, "Meta event, sequencer-specific, type=0x%02x leng=%d (IGNORED)\n", type, leng); } void smf_metatext(type,leng,mess) char *mess; { static char *ttype[] = { NULL, "Text Event", /* type=0x01 */ "Copyright Notice", /* type=0x02 */ "Sequence/Track Name", "Instrument Name", /* ... */ "Lyric", "Marker", "Cue Point", /* type=0x07 */ "Unrecognized" }; int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; if ( type < 1 || type > unrecognized ) type = unrecognized; } void smf_metaseq(num) { prtime(); gprintf(TRANS, "Meta event, sequence number = %d (IGNORED)\n",num); } void smf_metaeot() { /* prtime(); gprintf(TRANS, "Meta event, end of track\n"); */ } void smf_keysig(sf,mi) { /* prtime(); gprintf(TRANS, "Key signature, sharp/flats=%d minor=%d\n",sf,mi); */ } /* smf_tempo -- handle a midifile tempo change */ /* * NOTE: if divisions is positive, it gives time units per quarter, and * tempo is microsec per division. The product is microsec per quarter. * To convert to ticksize (parameter to insert_clock), we divide by 24*1000 * to get units of millisec and 24ths of quarter notes. insert_clock * expects this to have a 16 bit fractional part. */ void smf_tempo(tempo) long tempo; { time_type ctime = gio_time(); long ticksize = scale(tempo, 1024L, 375L); /* (tempo / 24000) << 16; microsec/clock converted to ms/quarter, shifted 16*/ /* prtime(); gprintf(TRANS, "Tempo, microseconds-per-MIDI-quarter-note = %ld\n",tempo); */ tempomap_insert(the_tempomap, Mf_currtime, tempo / divisions); if (ctime == 0) { /* we already have a clock event at t=0 -> fix it */ initial_clock->u.clock.ticksize = ticksize; } else { /* we need a new one */ /* NOTE: after the first clock, insert clock events 1/2 tick early to make sure ticksize is set before clock_tick() wakes up and reads it. */ insert_clock(the_score, ctime - (prev_ticksize >> 17), 0, ticksize); prev_ticksize = ticksize; } } void smf_timesig(nn,dd,cc,bb) { /* int denom = 1; while ( dd-- > 0 ) denom *= 2; prtime(); gprintf(TRANS, "Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n", nn,denom,cc,bb); */ } void smf_smpte(hr,mn,se,fr,ff) { prtime(); gprintf(TRANS, "SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d (IGNORED)\n", hr, mn, se, fr, ff); } void smf_arbitrary(leng,mess) char *mess; { prtime(); gprintf(TRANS, "Arbitrary bytes, leng=%d (IGNORED)\n",leng); } void smf_error(msg) char *msg; { gprintf(ERROR, msg); } void prtime() { gprintf(TRANS, "Time=%ld/%ld ",Mf_currtime, gio_time()); } void initfuncs() { Mf_error = smf_error; Mf_header = smf_header; Mf_starttrack = smf_trackstart; Mf_endtrack = smf_trackend; Mf_on = smf_noteon; Mf_off = smf_noteoff; Mf_pressure = smf_pressure; Mf_controller = smf_parameter; Mf_pitchbend = smf_pitchbend; Mf_program = smf_program; Mf_chanpressure = smf_chanpressure; Mf_sysex = smf_sysex; Mf_metamisc = smf_metamisc; Mf_seqnum = smf_metaseq; Mf_eot = smf_metaeot; Mf_timesig = smf_timesig; Mf_smpte = smf_smpte; Mf_tempo = smf_tempo; Mf_keysig = smf_keysig; Mf_sqspecific = smf_metaspecial; Mf_text = smf_metatext; Mf_arbitrary = smf_arbitrary; }