|
|
@ -253,8 +253,13 @@ |
|
|
|
* 30 September 2016, Scott Allen, V1.13 |
|
|
|
* 30 September 2016, Scott Allen, V1.13 |
|
|
|
* - Allow -c channel numbers to be specified as hex or octal |
|
|
|
* - Allow -c channel numbers to be specified as hex or octal |
|
|
|
* - Add -r to end the file with "repeat" instead of "score end" |
|
|
|
* - Add -r to end the file with "repeat" instead of "score end" |
|
|
|
|
|
|
|
* 30 September 2016, Len Shustek, V1.14 |
|
|
|
|
|
|
|
* - Prevent unnecessary "note off" commands from being generated by delaying |
|
|
|
|
|
|
|
* them until we see if a "note on" is generated before the next wait. |
|
|
|
|
|
|
|
* Thanks to Scott Allen for inspiring me to do this. In the best case we've |
|
|
|
|
|
|
|
* seen, this makes the bytestream 21% smaller! |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
#define VERSION "1.13" |
|
|
|
#define VERSION "1.14" |
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------------------------
|
|
|
|
/*--------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
@ -381,6 +386,7 @@ unsigned long tempo; /* current tempo in usec/qnote */ |
|
|
|
|
|
|
|
|
|
|
|
struct tonegen_status { /* current status of a tone generator */ |
|
|
|
struct tonegen_status { /* current status of a tone generator */ |
|
|
|
bool playing; /* is it playing? */ |
|
|
|
bool playing; /* is it playing? */ |
|
|
|
|
|
|
|
bool stopnote_pending; /* do we need to stop this generator before the next wait? */ |
|
|
|
int track; /* if so, which track is the note from? */ |
|
|
|
int track; /* if so, which track is the note from? */ |
|
|
|
int note; /* what note is playing? */ |
|
|
|
int note; /* what note is playing? */ |
|
|
|
int instrument; /* what instrument? */ |
|
|
|
int instrument; /* what instrument? */ |
|
|
@ -394,11 +400,11 @@ struct track_status { /* current processing point of a MIDI track */ |
|
|
|
uint8_t *trkend; /* ptr past the end of the track */ |
|
|
|
uint8_t *trkend; /* ptr past the end of the track */ |
|
|
|
unsigned long time; /* what time we're at in the score */ |
|
|
|
unsigned long time; /* what time we're at in the score */ |
|
|
|
unsigned long tempo; /* the tempo last set, in usec per qnote */ |
|
|
|
unsigned long tempo; /* the tempo last set, in usec per qnote */ |
|
|
|
unsigned int preferred_tonegen; /* for strategy2, try to use this generator */ |
|
|
|
unsigned int preferred_tonegen; /* for strategy2, try to use this generator */ |
|
|
|
unsigned char cmd; /* CMD_xxxx next to do */ |
|
|
|
unsigned char cmd; /* CMD_xxxx next to do */ |
|
|
|
unsigned char note; /* for which note */ |
|
|
|
unsigned char note; /* for which note */ |
|
|
|
unsigned char chan; /* from which channel it was */ |
|
|
|
unsigned char chan; /* from which channel it was */ |
|
|
|
unsigned char velocity; |
|
|
|
unsigned char velocity; /* the current volume */ |
|
|
|
unsigned char last_event; /* the last event, for MIDI's "running status" */ |
|
|
|
unsigned char last_event; /* the last event, for MIDI's "running status" */ |
|
|
|
bool tonegens[MAX_TONEGENS]; /* which tone generators our notes are playing on */ |
|
|
|
bool tonegens[MAX_TONEGENS]; /* which tone generators our notes are playing on */ |
|
|
|
} track[MAX_TRACKS] = { |
|
|
|
} track[MAX_TRACKS] = { |
|
|
@ -1003,6 +1009,25 @@ void find_note (int tracknum) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* generate "stop note" commands for any channels that have them pending */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void gen_stopnotes(void) { |
|
|
|
|
|
|
|
struct tonegen_status *tg; |
|
|
|
|
|
|
|
for (int tgnum = 0; tgnum < num_tonegens; ++tgnum) { |
|
|
|
|
|
|
|
tg = &tonegen[tgnum]; |
|
|
|
|
|
|
|
if (tg->stopnote_pending) { |
|
|
|
|
|
|
|
if (binaryoutput) { |
|
|
|
|
|
|
|
putc (CMD_STOPNOTE | tgnum, outfile); |
|
|
|
|
|
|
|
outfile_bytecount += 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
fprintf (outfile, "0x%02X, ", CMD_STOPNOTE | tgnum); |
|
|
|
|
|
|
|
outfile_items (1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
tg->stopnote_pending = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/********************* main ****************************/ |
|
|
|
/********************* main ****************************/ |
|
|
|
|
|
|
|
|
|
|
|
int main (int argc, char *argv[]) { |
|
|
|
int main (int argc, char *argv[]) { |
|
|
@ -1196,6 +1221,7 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's! |
|
|
|
|
|
|
|
|
|
|
|
delta_time = earliest_time - timenow; |
|
|
|
delta_time = earliest_time - timenow; |
|
|
|
if (delta_time) { |
|
|
|
if (delta_time) { |
|
|
|
|
|
|
|
gen_stopnotes(); /* first check if any tone generators have "stop note" commands pending */ |
|
|
|
/* Convert ticks to milliseconds based on the current tempo */ |
|
|
|
/* Convert ticks to milliseconds based on the current tempo */ |
|
|
|
unsigned long long temp; |
|
|
|
unsigned long long temp; |
|
|
|
temp = ((unsigned long long) delta_time * tempo) / ticks_per_beat; |
|
|
|
temp = ((unsigned long long) delta_time * tempo) / ticks_per_beat; |
|
|
@ -1240,13 +1266,7 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's! |
|
|
|
fprintf (logfile, |
|
|
|
fprintf (logfile, |
|
|
|
"->Stop note %d, generator %d, track %d\n", |
|
|
|
"->Stop note %d, generator %d, track %d\n", |
|
|
|
tg->note, tgnum, tracknum); |
|
|
|
tg->note, tgnum, tracknum); |
|
|
|
if (binaryoutput) { |
|
|
|
tg->stopnote_pending = true; /* must stop the current note if another doesn't start first */ |
|
|
|
putc (CMD_STOPNOTE | tgnum, outfile); |
|
|
|
|
|
|
|
outfile_bytecount += 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
fprintf (outfile, "0x%02X, ", CMD_STOPNOTE | tgnum); |
|
|
|
|
|
|
|
outfile_items (1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
tg->playing = false; |
|
|
|
tg->playing = false; |
|
|
|
trk->tonegens[tgnum] = false; |
|
|
|
trk->tonegens[tgnum] = false; |
|
|
|
} |
|
|
|
} |
|
|
@ -1294,6 +1314,7 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's! |
|
|
|
tg->playing = true; |
|
|
|
tg->playing = true; |
|
|
|
tg->track = tracknum; |
|
|
|
tg->track = tracknum; |
|
|
|
tg->note = trk->note; |
|
|
|
tg->note = trk->note; |
|
|
|
|
|
|
|
tg->stopnote_pending = false; |
|
|
|
trk->tonegens[tgnum] = true; |
|
|
|
trk->tonegens[tgnum] = true; |
|
|
|
trk->preferred_tonegen = tgnum; |
|
|
|
trk->preferred_tonegen = tgnum; |
|
|
|
++note_on_commands; |
|
|
|
++note_on_commands; |
|
|
@ -1359,6 +1380,7 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's! |
|
|
|
while (tracks_done < num_tracks); |
|
|
|
while (tracks_done < num_tracks); |
|
|
|
|
|
|
|
|
|
|
|
// generate the end-of-score command and some commentary
|
|
|
|
// generate the end-of-score command and some commentary
|
|
|
|
|
|
|
|
gen_stopnotes(); /* flush out any pending "stop note" commands */ |
|
|
|
outfile_bytecount++; |
|
|
|
outfile_bytecount++; |
|
|
|
if (binaryoutput) |
|
|
|
if (binaryoutput) |
|
|
|
putc (gen_restart ? CMD_RESTART : CMD_STOP, outfile); |
|
|
|
putc (gen_restart ? CMD_RESTART : CMD_STOP, outfile); |
|
|
|