From 5d5acb2e845fc84fcb1e8b7fb6e39260a4f02fd7 Mon Sep 17 00:00:00 2001 From: Len Shustek Date: Fri, 7 May 2021 22:08:12 -0700 Subject: [PATCH] fix bug that eliminated duplicate notes unintentionally; add -noduplicates option --- README.txt | 39 ++++++----- miditones.c | 155 +++++++++++++++++++++++++++++++------------ miditones.exe | Bin 31232 -> 33280 bytes miditones_scroll.c | 36 +++++++--- miditones_scroll.exe | Bin 20992 -> 21504 bytes 5 files changed, 162 insertions(+), 68 deletions(-) diff --git a/README.txt b/README.txt index 7ae092d..4fab864 100644 --- a/README.txt +++ b/README.txt @@ -44,7 +44,7 @@ (from MIDI channel 10) are generated from longer sampled waveforms of a complete instrument strike. Each generator's volume is independently adjusted according to the MIDI velocity of the note being played before all channels are mixed. - + www.github.com/LenShustek/Playtune_Teensy The fifth version is for the Teensy 3.1/3.2, and uses the four Periodic Interval Timers in the Cortex M4 processor to support up to 4 simultaneous notes. @@ -52,7 +52,6 @@ (This was written to experiment with multi-channel multi-Tesla Coil music playing, where I use Flexible Timer Module FTM0 for generating precise one-shot pulses. But I ultimately switched to the polling version to play more simultaneous notes.) - www.github.com/LenShustek/Playtune_Teensy www.github.com/LenShustek/ATtiny-playtune This is a much simplified version that fits, with a small song, into an ATtiny @@ -64,7 +63,7 @@ (Imagine what you can do with the $1 8-pin ATtiny85 with a whopping 8K!) MIDITONES may also prove useful for other simple music synthesizers. There are - various forks of the code, and the Playtune players, on Githib. + various forks of this code, and of the Playtune players, on Githib. *** THE PROGRAM @@ -136,8 +135,8 @@ The following are lesser-used command-line options: -c=n Only process the channel numbers whose bits are on in the number "n". - For example, -c3 means "only process channels 0 and 1". In addition to decimal, - "n" can be also specified in hex using a 0x prefix. + For example, -c3 means "only process channels 0 and 1". In addition to + decimal, "n" can be also specified in hex using a 0x prefix. -dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream. @@ -151,10 +150,10 @@ -n=x Put about "x" items on each line of the C file output -p Only parse the MIDI file, and don't generate an output file. - Tracks are processed sequentially instead of being merged into chronological order. - This is mostly useful for debugging MIDI file parsing problems. + Tracks are processed sequentially instead of being merged into chronological + order. This is mostly useful for debugging MIDI file parsing problems. - -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some) + -pi Ignore notes in the MIDI percussion track 9 (also called 10 in some documents) -r Terminate the output file with a "restart" command instead of a "stop" command. @@ -164,14 +163,18 @@ -h Give command-line help. - -showskipped Display information to the console each note that had to be skipped - because there weren't enough tone generators. + -showskipped Display information to the console about each note that had to be + skipped because there weren't enough tone generators. + + -noduplicates Remove identical notes played on identical instruments for the same time + that come from different tracks. This can reduce the number of tone + generators needed, and make the file smaller. -delaymin=x Don't generate delays less than x milliseconds long, to reduce the number of "delay" commands and thus make the bytestream smaller, at the expense of moving notes slightly. The deficits are accumulated and eventually used, so that there is no loss of synchronization in the long term. - The default is 0, which means timing is exact to the millisecond. + The default is 0, which means note timing is exact to the millisecond. -releasetime=x Stop each note x milliseconds before it is supposed to end. This results in better sound separation between notes. It might also allow more notes to @@ -187,10 +190,10 @@ too short. (Only valid with -v.) -attacknotemax=x Notes larger than x milliseconds won't have the attack/sustain profile - applied. That allows sustained organ-like pedaling. + applied. That allows sustained organ-like pedaling. (Only valid with -v.) -sustainlevel=p The volume level during the sustain phase is p percent of the starting - note volume. (Only valid with -v.) + note volume. The default is 50. (Only valid with -v.) -scorename Use as the name of the score in the generated C code instead of "score", and name the file .h instead of @@ -203,7 +206,7 @@ *** THE SCORE BYTESTREAM - The generated bytestream is a series of commands to turn notes on and off, + The generated bytestream is a series of commands that turn notes on and off, change instruments, and request a delay until the next event time. Here are the details, with numbers shown in hexadecimal. @@ -218,8 +221,8 @@ 8t Stop playing the note on tone generator t. - Ct ii Change tone generator t to play instrument ii from now on. This will only - be generated if the -i option was given. + Ct ii Change tone generator t to play instrument ii from now on. This will + only be generated if the -i option was given. F0 End of score; stop playing. @@ -250,7 +253,7 @@ ff2 Another byte of flags, currently undefined tt The number (in one byte) of tone generators actually used in this music. - Any subsequent header bytes covered by the count, if present, are currently undefined + Any subsequent header bytes included in the length are currently undefined and should be ignored by players. - Len Shustek, 2011 to 2021; see the change log. \ No newline at end of file + Len Shustek, 2011 to 2021; see the change log. diff --git a/miditones.c b/miditones.c index 2dc4c22..9844af1 100644 --- a/miditones.c +++ b/miditones.c @@ -46,7 +46,7 @@ (from MIDI channel 10) are generated from longer sampled waveforms of a complete instrument strike. Each generator's volume is independently adjusted according to the MIDI velocity of the note being played before all channels are mixed. - + www.github.com/LenShustek/Playtune_Teensy The fifth version is for the Teensy 3.1/3.2, and uses the four Periodic Interval Timers in the Cortex M4 processor to support up to 4 simultaneous notes. @@ -54,7 +54,6 @@ (This was written to experiment with multi-channel multi-Tesla Coil music playing, where I use Flexible Timer Module FTM0 for generating precise one-shot pulses. But I ultimately switched to the polling version to play more simultaneous notes.) - www.github.com/LenShustek/Playtune_Teensy www.github.com/LenShustek/ATtiny-playtune This is a much simplified version that fits, with a small song, into an ATtiny @@ -66,7 +65,7 @@ (Imagine what you can do with the $1 8-pin ATtiny85 with a whopping 8K!) MIDITONES may also prove useful for other simple music synthesizers. There are - various forks of the code, and the Playtune players, on Githib. + various forks of this code, and of the Playtune players, on Githib. *** THE PROGRAM @@ -138,8 +137,8 @@ The following are lesser-used command-line options: -c=n Only process the channel numbers whose bits are on in the number "n". - For example, -c3 means "only process channels 0 and 1". In addition to decimal, - "n" can be also specified in hex using a 0x prefix. + For example, -c3 means "only process channels 0 and 1". In addition to + decimal, "n" can be also specified in hex using a 0x prefix. -dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream. @@ -153,10 +152,10 @@ -n=x Put about "x" items on each line of the C file output -p Only parse the MIDI file, and don't generate an output file. - Tracks are processed sequentially instead of being merged into chronological order. - This is mostly useful for debugging MIDI file parsing problems. + Tracks are processed sequentially instead of being merged into chronological + order. This is mostly useful for debugging MIDI file parsing problems. - -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some) + -pi Ignore notes in the MIDI percussion track 9 (also called 10 in some documents) -r Terminate the output file with a "restart" command instead of a "stop" command. @@ -166,14 +165,18 @@ -h Give command-line help. - -showskipped Display information to the console each note that had to be skipped - because there weren't enough tone generators. + -showskipped Display information to the console about each note that had to be + skipped because there weren't enough tone generators. + + -noduplicates Remove identical notes played on identical instruments for the same time + that come from different tracks. This can reduce the number of tone + generators needed, and make the file smaller. -delaymin=x Don't generate delays less than x milliseconds long, to reduce the number of "delay" commands and thus make the bytestream smaller, at the expense of moving notes slightly. The deficits are accumulated and eventually used, so that there is no loss of synchronization in the long term. - The default is 0, which means timing is exact to the millisecond. + The default is 0, which means note timing is exact to the millisecond. -releasetime=x Stop each note x milliseconds before it is supposed to end. This results in better sound separation between notes. It might also allow more notes to @@ -189,10 +192,10 @@ too short. (Only valid with -v.) -attacknotemax=x Notes larger than x milliseconds won't have the attack/sustain profile - applied. That allows sustained organ-like pedaling. + applied. That allows sustained organ-like pedaling. (Only valid with -v.) -sustainlevel=p The volume level during the sustain phase is p percent of the starting - note volume. (Only valid with -v.) + note volume. The default is 50. (Only valid with -v.) -scorename Use as the name of the score in the generated C code instead of "score", and name the file .h instead of @@ -205,7 +208,7 @@ *** THE SCORE BYTESTREAM - The generated bytestream is a series of commands to turn notes on and off, + The generated bytestream is a series of commands that turn notes on and off, change instruments, and request a delay until the next event time. Here are the details, with numbers shown in hexadecimal. @@ -220,8 +223,8 @@ 8t Stop playing the note on tone generator t. - Ct ii Change tone generator t to play instrument ii from now on. This will only - be generated if the -i option was given. + Ct ii Change tone generator t to play instrument ii from now on. This will + only be generated if the -i option was given. F0 End of score; stop playing. @@ -252,7 +255,7 @@ ff2 Another byte of flags, currently undefined tt The number (in one byte) of tone generators actually used in this music. - Any subsequent header bytes covered by the count, if present, are currently undefined + Any subsequent header bytes included in the length are currently undefined and should be ignored by players. Len Shustek, 2011 to 2021; see the change log. @@ -382,6 +385,15 @@ 22 April 2021, Len Shustek, V2.2 -Add -showskipped to log the places where notes had to be discarded because there aren't enough tone generators. + 25 April 2021, Len Shustek, V2.3 + -Report how many notes were generated + 5 May 2021, Len Shustek, V2.4 + -Fix bug: when finding an idle tone generator, makes sure that the track and + instrument of the currently playing note matches before deciding it's a sustain, + otherwise multiple identical notes on identical instruments will disappear. + (Thanks to Jonathan Oakley for providing an example of the problem.) + -But sometimes, to reduce the number of tone generators, it's helpful to eliminate + identical notes. So add a -noduplicates option to do just that. future version ideas @@ -397,7 +409,7 @@ future version ideas channel 8 // organ options -attacktime=1000 -sustainlevel=80% -releasetime=100 -notemin=200 */ -#define VERSION "2.2" +#define VERSION "2.4" /*-------------------------------------------------------------------------------------------- @@ -408,7 +420,7 @@ Gleaned from http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfilefor but also check out http://midi.teragonaudio.com/tech/miditech.htm Notation: - is 1-4 bytes of 7-bit data, concatenated into one 7- to 28-bit number. The high bit of the last byte is 0. + is 1-4 bytes of 7-bit data, concatenated into one 7 to 28-bit number. The high bit of the last byte is 0. lower case letters are hex digits. If preceeded by 0, only low 7 bits are used. "xx" are ascii text characters {xxx}... means indefinite repeat of xxx @@ -457,28 +469,31 @@ a Sysex event track_event is: a meta event track_event is: FF 00 02 ssss specify sequence number - FF 01 "xx"... arbitrary text + FF 01 "xx"... arbitrary description text FF 02 "xx"... copyright notice FF 03 "xx"... sequence or track name FF 04 "xx"... instrument name FF 05 "xx"... lyric to be sung FF 06 "xx"... name of marked point in the score FF 07 "xx"... description of cue point in the score + FF 08 "xx"... program name + FF 09 "xx"... device (port) name FF 20 01 0c default channel for subsequent events without a channel is c FF 21 01 pp MIDI port is pp FF 2F 00 end of track - FF 51 03 tttttt set tempo in microseconds per quarter-note, for all tracks + FF 51 03 tttttt set tempo in microseconds per beat (quarter-note), for all tracks FF 54 05 hhmmssfrff set SMPTE time to start the track FF 58 04 nnddccbb set time signature FF 59 02 sfmi set key signature FF 7F data sequencer-specific data Note that "set tempo" events are supposed to occur in only one track (generally the first), -which may or may not also contain MIDI note events. +which may or may not also contain MIDI note events. That isn't always true, however, See https://stackoverflow.com/questions/1080297/how-does-midi-tempo-message-apply-to-other-tracks --------------------------------------------------------------------------------------------*/ /*--------------- processing outline ----------------------------------- + Lots of details are omitted. Note that MIDI track parsing is based on counting "ticks", but our queueing is based on real-time seconds. The number of ticks per second changes with the tempo. @@ -586,7 +601,7 @@ struct track_header { bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem, volume_output, instrumentoutput, percussion_ignore, percussion_translate, do_header, - gen_restart, scorename, showskipped; + gen_restart, scorename, showskipped, noduplicates; FILE *infile, *outfile, *logfile; uint8_t *buffer, *hdrptr; unsigned long buflen; @@ -719,6 +734,7 @@ void SayUsage(char *programName) { " -s1 strategy 1: favor track 1", " -s2 strategy 2: try to assign tracks to specific tone generators", " -showskipped display information about each note that had to be skipped", + " -noduplicates remove identical notes playing on different channels", " -delaymin=x minimum delay is x msec, to save bytestream space", " -attacktime=x the high volume attack phase lasts x msec", " -attacknotemax=x notes over x msec don't use the attack/sustain profile", @@ -733,7 +749,8 @@ void SayUsage(char *programName) { int HandleOptions (int argc, char *argv[]) { /* returns the index of the first argument that is not an option, i.e. does not start with a dash or a slash */ - int i, firstnonoption = 0; for (i = 1; i < argc; i++) { + int i, firstnonoption = 0; + for (i = 1; i < argc; i++) { if (argv[i][0] == '/' || argv[i][0] == '-') { int tempint; char *arg = argv[i] + 1; @@ -769,6 +786,7 @@ int HandleOptions (int argc, char *argv[]) { else if (opt_key(arg, "s2")) strategy2 = true; else if (opt_key(arg, "scorename")) scorename = true; else if (opt_key(arg, "showskipped")) showskipped = true; + else if (opt_key(arg, "noduplicates")) noduplicates = true; else if (opt_int(arg, "t", &num_tonegens, 1, MAX_TONEGENS)) printf("Using %d tone generators\n", num_tonegens); else if (opt_key(arg, "v")) volume_output = true; @@ -879,7 +897,11 @@ void outfile_items (int n) { //******* structures for recording track, channel, and tone generator status -// Note that the tempo can change as notes are played, maybe many times. +// Note that the tempo can change while notes are being played, maybe many times. +// Although the score timing is specified in the MIDI file in ticks, we queue and +// issue note events based on microseconds since the start of the song. We convert +// ticks to microseconds, using the current tempo, as the MIDI events from the +// various tracks are processsed in tick order. // In order to keep track of how long notes are to play, we have to incrementally // accumulate the duration of all playing notes every time the tempo changes, and // then one final time when the "stop note" event occurs. @@ -889,6 +911,7 @@ struct noteinfo { // everything we might care about as a note int track, channel, note, instrument, volume; // all the nitty-gritty about it }; + struct tonegen_status { // current status of a tone generator bool playing; // is it playing? bool stopnote_pending; // are we due to issue a stop note command? @@ -899,7 +922,7 @@ struct track_status { // current status of a MIDI track uint8_t *trkptr; // ptr to the next event we care about uint8_t *trkend; // ptr just past the end of the track unsigned long time; // what time we're at in the score, in ticks - unsigned long tempo; // the last tempo set on this track + unsigned long tempo; // the last tempo set by this track int preferred_tonegen; // for strategy2: try to use this generator byte cmd; // next CMD_xxxx event coming up byte chan, note, volume; // if it is CMD_PLAYNOTE or CMD_STOPNOTE, the note info @@ -945,6 +968,7 @@ still plenty long. struct queue_entry { // the format of each queue entry byte cmd; // CMD_PLAY or CMD_STOP + byte delete; // delete this command due to "-noduplicates"? struct noteinfo note; // info about the note, including the action time } queue[QUEUE_SIZE]; @@ -960,7 +984,7 @@ void show_queue(void) { // for debugging: dump the whole event queue int ndx = queue_oldest_ndx; if (queue_numitems > 0) while (1) { struct queue_entry *q = &queue[ndx]; - fprintf(fid, "%2d: %s %s\n", ndx, q->cmd == CMD_PLAYNOTE ? "PLAY" : "STOP", describe(&q->note)); + fprintf(fid, "%2d: %s %s %s\n", ndx, q->cmd == CMD_PLAYNOTE ? "PLAY" : "STOP", q->delete ? "deleted" : "", describe(&q->note)); if (ndx == queue_newest_ndx) break; if (++ndx >= QUEUE_SIZE) ndx = 0; } } @@ -984,7 +1008,8 @@ int find_idle_tgen(struct noteinfo *np) { // returns -1 if there isn't one for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { // first, is this note already playing on this channel? tg = &tonegen[tgnum]; if (tg->playing - && tg->note.note == np->note && tg->note.channel == np->channel) { + && tg->note.note == np->note && tg->note.channel == np->channel + && tg->note.track == np->track && tg->note.instrument == np->instrument) { // this must be the start of the sustain phase of a playing note ++playnotes_without_stopnotes; if (loggen) fprintf(logfile, " *** playnote without stopnote, tgen %d, %s\n", @@ -1014,8 +1039,9 @@ int find_idle_tgen(struct noteinfo *np) { // returns -1 if there isn't one void remove_queue_entry(int ndx) { // remove the oldest queue entry struct queue_entry *q = &queue[ndx]; - + if (q->delete) return; // if marked for deletion, just ignore it if (q->cmd == CMD_STOPNOTE) { + if (loggen) fprintf(logfile, " dequeue stopnote for %s\n", describe(&q->note)); // find the tone generator playing this note, and record a pending stop int tgnum; for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { @@ -1036,6 +1062,7 @@ void remove_queue_entry(int ndx) { // remove the oldest queue entry else { // CMD_PLAYNOTE assert(q->cmd == CMD_PLAYNOTE, "bad cmd in remove_queue_entry"); + if (loggen) fprintf(logfile, " dequeue playnote for %s\n", describe(&q->note)); int tgnum = find_idle_tgen(&q->note); struct tonegen_status *tg = &tonegen[tgnum]; if (tgnum >= 0) { // we found a tone generator we can use @@ -1120,7 +1147,7 @@ void pull_queue(void) { --queue_numitems; } while (queue_numitems > 0 && queue[queue_oldest_ndx].note.time_usec <= oldtime + (timestamp)delaymin_usec); - // do any "stop notes" still needed to be generated? + // do any "stop notes" still need to be generated? for (int tgnum = 0; tgnum < num_tonegens; ++tgnum) { struct tonegen_status *tg = &tonegen[tgnum]; if (tg->stopnote_pending) { // got one @@ -1138,12 +1165,57 @@ void pull_queue(void) { void flush_queue(void) { // empty the queue while (queue_numitems > 0) pull_queue(); } +// find the queue entry for the playnote matching the stopnote in queue[search_ndx] +int queue_find_playnote(int search_ndx) { + if (loggen) fprintf(logfile, " queue_find_playnote(%d), %s: ", search_ndx, describe(&queue[search_ndx].note)); + for (int ndx = queue_newest_ndx;;) { + if (ndx != search_ndx // don't look at the entry we're trying to match + && !queue[ndx].delete // don't re-find queue entries already deleted + && queue[ndx].cmd == CMD_PLAYNOTE // must match playnote + && queue[ndx].note.channel == queue[search_ndx].note.channel //also channel, track, note, and instrument + && queue[ndx].note.track == queue[search_ndx].note.track + && queue[ndx].note.note == queue[search_ndx].note.note + && queue[ndx].note.instrument == queue[search_ndx].note.instrument ) { + if (loggen) fprintf(logfile, "found ndx %d\n", ndx); + return ndx; } + if (ndx == queue_oldest_ndx) break; + if (--ndx < 0) ndx = QUEUE_SIZE - 1; } + if (loggen) fprintf(logfile, "not found\n"); + return -1; } + +// find a queue entry for another stopnote matching the stopnote in queue[search_ndx] +int queue_find_stopnote(int search_ndx) { + if (loggen) fprintf(logfile, " queue_find_stopnote(%d), %s: ", search_ndx, describe(&queue[search_ndx].note)); + for (int ndx = queue_newest_ndx;;) { + if (ndx != search_ndx // don't look at the entry we're trying to match + && !queue[ndx].delete // don't re-find queue entries already deleted + && queue[ndx].cmd == CMD_STOPNOTE // must match stopnote + && queue[ndx].note.time_usec == queue[search_ndx].note.time_usec // also time, note, and instrument + && queue[ndx].note.note == queue[search_ndx].note.note + && queue[ndx].note.instrument == queue[search_ndx].note.instrument) { + if (loggen) fprintf(logfile, "found ndx %d\n", ndx); + return ndx; } + if (ndx == queue_oldest_ndx) break; + if (--ndx < 0) ndx = QUEUE_SIZE - 1; } + if (loggen) fprintf(logfile, "not found\n"); + return -1; } + +// For -noduplicates, mark for deletion any note start/stop identical to the CMD_STOPNOTE we just queued +void remove_queue_duplicates(int stop_ndx) { + int play_ndx, dup_play_ndx, dup_stop_ndx; + if ((play_ndx = queue_find_playnote(stop_ndx)) >= 0 // find its matching playnote + && (dup_stop_ndx = queue_find_stopnote(stop_ndx)) >= 0 // find another stopnote for the same note at the same time + && (dup_play_ndx = queue_find_playnote(dup_stop_ndx)) >= 0 // find its matching playnote + && queue[dup_play_ndx].note.time_usec == queue[play_ndx].note.time_usec) { // if it starts at the same time, delete it + if (loggen) fprintf(logfile, " remove duplicate, ndxs %d, %d: %s\n", dup_play_ndx, dup_stop_ndx, describe(&queue[dup_play_ndx].note)); + queue[dup_play_ndx].delete = queue[dup_stop_ndx].delete = true; } } + // queue a "note on" or "note off" command void queue_cmd(byte cmd, struct noteinfo *np) { if (loggen) fprintf(logfile, " queue %s %s\n", cmd == CMD_PLAYNOTE ? "PLAY" : cmd == CMD_STOPNOTE ? "STOP" : "????", describe(np)); - if (queue_numitems == QUEUE_SIZE) pull_queue(); + if (queue_numitems >= QUEUE_SIZE) pull_queue(); assert(queue_numitems < QUEUE_SIZE, "no room in queue"); timestamp horizon = output_usec + output_deficit_usec; if (np->time_usec < horizon) { // don't allow revisionist history @@ -1175,9 +1247,10 @@ void queue_cmd(byte cmd, struct noteinfo *np) { if (++ndx >= QUEUE_SIZE) ndx = 0; } insert: // store the item at ndx ++queue_numitems; - queue[ndx].cmd = cmd; // fille in the queue entry + queue[ndx].cmd = cmd; // fill in the queue entry + queue[ndx].delete = false; queue[ndx].note = *np; // structure copy of the note -} + if (noduplicates && cmd == CMD_STOPNOTE) remove_queue_duplicates(ndx); } void show_queue_cmd(timestamp time_usec, byte cmd, int note) { printf("debug queue %s note %02X at %6ld\n", cmd == CMD_PLAYNOTE ? "PLAY" : "STOP", note, time_usec); @@ -1364,7 +1437,7 @@ show_hex: note_off: if (logparse) fprintf(logfile, "note %d (0x%02X) off, channel %d, volume %d\n", t->note, t->note, chan, t->volume); if ((1 << chan) & channel_mask // we're processing this channel && (!percussion_ignore || chan != PERCUSSION_TRACK)) { // and not ignoring percussion - if (!instrumentoutput) t->chan = 0; // if no insruments, force all notes to channel 0 + if (!instrumentoutput) t->chan = 0; // if no instruments, force all notes to channel 0 t->cmd = CMD_STOPNOTE; /* stop processing and return */ return; } break; @@ -1376,7 +1449,7 @@ note_off: if (logparse) fprintf(logfile, "note %d (0x%02X) off, channel %d, vo if (logparse) fprintf(logfile, "note %d (0x%02X) on, channel %d, volume %d\n", t->note, t->note, chan, t->volume); if ((1 << chan) & channel_mask // we're processing this channel && (!percussion_ignore || chan != PERCUSSION_TRACK)) { // and not ignoring percussion - if (!instrumentoutput) t->chan = 0; // if no insruments, force all notes to channel 0 + if (!instrumentoutput) t->chan = 0; // if no instruments, force all notes to channel 0 t->cmd = CMD_PLAYNOTE; /* stop processing and return */ return; } break; @@ -1492,7 +1565,7 @@ void process_track_data(void) { ++noteinfo_notfound; // presumably the array overflowed on input if (loggen) fprintf(logfile, " *** noteinfo slot not found to stop track %d note %d (%02X) channel %d\n", tracknum, trk->note, trk->note, trk->chan); } - else { + else { // found the playing note for this stopnote // Analyze the sustain and release parameters. We might generate another "note on" // command with reduced volume, and/or move the stopnote command earlier than now. struct noteinfo *np = &cp->notes_playing[ndx]; @@ -1563,7 +1636,7 @@ int main (int argc, char *argv[]) { #define MAXPATH 120 char filename[MAXPATH]; - printf ("MIDITONES V%s, (C) 2011-2020 Len Shustek\n", VERSION); + printf ("MIDITONES V%s, (C) 2011-2021 Len Shustek\n", VERSION); if (argc == 1) { // no arguments SayUsage (argv[0]); return 1; } @@ -1700,11 +1773,11 @@ int main (int argc, char *argv[]) { // generate the ending commentary if (!binaryoutput) { - fprintf(outfile, "\n// This score contains %ld bytes, and %d tone generator%s used.\n", - outfile_bytecount, num_tonegens_used, - num_tonegens_used == 1 ? " is" : "s are"); + fprintf(outfile, "\n// This %ld byte score contains %d notes and uses %d tone generator%s\n", + outfile_bytecount, note_on_commands, num_tonegens_used, + num_tonegens_used == 1 ? "" : "s"); if (notes_skipped) - fprintf(outfile, "// %d notes had to be skipped.\n", notes_skipped); } + fprintf(outfile, "// %d notes had to be skipped\n", notes_skipped); } printf(" %s %d tone generators were used.\n", num_tonegens_used < num_tonegens ? "Only" : "All", num_tonegens_used); if (notes_skipped) diff --git a/miditones.exe b/miditones.exe index ba15dfc19444ff9c297fc8f0adfd3fbe36754ec1..07e6d9ce2cddbffe9c6591747a32bb0793c16e2d 100644 GIT binary patch literal 33280 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 fu #include @@ -153,6 +159,8 @@ bool codeoutput = false; bool expect_volume = false; bool ignore_volume = false; bool showhex = false; +bool showbytestream = true; +bool got_instruments = false; unsigned max_vol = 0, min_vol = 255; struct file_hdr_t { /* what the optional file header looks like */ @@ -235,6 +243,7 @@ void SayUsage (char *programName) { " -vi expects and ignores volume information", " -c creates an annotated C source file as .c", " -x show notes in hex instead of octave/note", + " -n don't show the bytestream data", "" }; int i = 0; while (usage[i][0] != '\0') @@ -260,6 +269,9 @@ int HandleOptions (int argc, char *argv[]) { case 'X': showhex = true; break; + case 'N': + showbytestream = false; + break; case 'T': if (sscanf (&argv[i][2], "%d", &num_tonegens) != 1 || num_tonegens < 1 || num_tonegens > MAX_TONEGENS) @@ -383,12 +395,13 @@ void print_status (void) { else fprintf (outfile, " v%-3d", gen_volume[gen]); } // display the hex commands that created these changes - fprintf (outfile, "%3u.%03u %c%04X: ", delay/1000, delay%1000, warning ? '!' : ' ', (unsigned int) (lastbufptr - buffer)); + fprintf (outfile, "%3u.%03u %c", delay/1000, delay%1000, warning ? '!' : ' '); + if (showbytestream) fprintf (outfile, "%04X: ", (unsigned int) (lastbufptr - buffer)); warning = false; if (codeoutput) fprintf (outfile, "*/ "); // end comment - for (; lastbufptr <= bufptr; ++lastbufptr) - fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); + if (showbytestream) for (; lastbufptr <= bufptr; ++lastbufptr) + fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); fprintf (outfile, "\n"); lastbufptr = bufptr + 1; } @@ -413,6 +426,7 @@ int main (int argc, char *argv[]) { return 1; } argno = HandleOptions (argc, argv); /* process options */ + if (codeoutput) showbytestream = true; filebasename = argv[argno]; strlcpy (filename, filebasename, MAXPATH); // Open the input file @@ -487,7 +501,9 @@ int main (int argc, char *argv[]) { fprintf (outfile, " time "); for (unsigned i = 0; i < num_tonegens; ++i) fprintf (outfile, expect_volume && !ignore_volume ? " gen%-5d" : " gen%-2d", i); - fprintf (outfile, "delay addr bytestream code\n\n"); + fprintf(outfile, "delay"); + if (showbytestream) fprintf(outfile, " addr bytestream code"); + fprintf(outfile, "\n\n"); for (gen = 0; gen < num_tonegens; ++gen) gen_note[gen] = SILENT; @@ -533,6 +549,7 @@ int main (int argc, char *argv[]) { gen_note[gen] = SILENT; gen_did_stopnote[gen] = true; } else if (cmd == 0xc0) { /* change instrument */ + got_instruments = true; gen_instrument[gen] = *++bufptr & 0x7f; gen_instrument_changed[gen] = true; } else { @@ -562,10 +579,11 @@ int main (int argc, char *argv[]) { fprintf (infofile, "%u stopnote commands were unnecessary.\n", stopnotes_before_startnote); fprintf (infofile, "%u consecutive delays could have been merged.\n", consecutive_delays); if (stopnotes_before_startnote + consecutive_delays > 0) fprintf (infofile, "(Those locations are marked with \"!\")\n"); - fprintf(infofile, "instruments used:\n"); - for (int i = 0; i < 128; ++i) - if (instrument_count[i]) { - fprintf(infofile, " %s (%3d, 0x%02X) %7d\n", instrumentname[i], i, i, instrument_count[i]); } + if (got_instruments) { + fprintf(infofile, "instruments used:\n"); + for (int i = 0; i < 128; ++i) + if (instrument_count[i]) { + fprintf(infofile, " %s (%3d, 0x%02X) %7d\n", instrumentname[i], i, i, instrument_count[i]); } } if (expect_volume) fprintf(infofile, "volume ranged from %d to %d\n", min_vol, max_vol); printf ("Done.\n"); } diff --git a/miditones_scroll.exe b/miditones_scroll.exe index 8235666ac0120ea54ae2bbc9f94b268d326f4dea..420279cd7f21bad153576ea3cc4bfb96e9b33a33 100644 GIT binary patch delta 7233 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 delta 6892 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