Skip to content

Commit c6e5eee

Browse files
committed
Don't generate unnecessary "note off" commands
For the Playtune format, don't generate a "note off" if it is immediately followed by a "note on" for the same tone generator without a delay between. This change is ported from MIDITONES V1.14. Also: - Rename variable "restart" to "gen_restart" to match MIDITONES. - Update miditones_scroll.c to V1.6 from MIDITONES. - Create Windows 64 bit and 32 bit executables for midi2tones and miditones_scroll using Mingw-w64, and include documentation on how they were compiled.
1 parent da99337 commit c6e5eee

File tree

9 files changed

+77
-30
lines changed

9 files changed

+77
-30
lines changed

README.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@
157157
* If the high-order bit of the byte is 1, then it is one of the following commands:
158158
*
159159
* 9t nn [vv]
160-
* Start playing note nn on tone generator t. Generators are numbered
161-
* starting with 0. The note numbers are the MIDI numbers for the chromatic
162-
* scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz).
160+
* Start playing note nn on tone generator t, replacing any previous note.
161+
* Generators are numbered starting with 0. The note numbers are the MIDI
162+
* numbers for the chromatic scale, with decimal 69 being Middle A (440 Hz).
163163
* If the -v option was given, a second byte is added to indicate note volume.
164164
*
165165
* 8t Stop playing the note on tone generator t.

WINDOWS_EXE.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Windows executable files were created using Mingw-w64
2+
https://mingw-w64.org/
3+
4+
The following commands were used to create 64 bit and 32 bit executables:
5+
6+
x86_64-w64-mingw32-gcc -O2 -Wall -o midi2tones_64bit.exe midi2tones.c
7+
8+
i686-w64-mingw32-gcc -O2 -Wall -o midi2tones_32bit.exe midi2tones.c
9+
10+
x86_64-w64-mingw32-gcc -O2 -Wall -o miditones_scroll_64bit.exe miditones_scroll.c
11+
12+
i686-w64-mingw32-gcc -O2 -Wall -o miditones_scroll_32bit.exe miditones_scroll.c
13+

midi2tones.c

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@
157157
* If the high-order bit of the byte is 1, then it is one of the following commands:
158158
*
159159
* 9t nn [vv]
160-
* Start playing note nn on tone generator t. Generators are numbered
161-
* starting with 0. The note numbers are the MIDI numbers for the chromatic
162-
* scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz).
160+
* Start playing note nn on tone generator t, replacing any previous note.
161+
* Generators are numbered starting with 0. The note numbers are the MIDI
162+
* numbers for the chromatic scale, with decimal 69 being Middle A (440 Hz).
163163
* If the -v option was given, a second byte is added to indicate note volume.
164164
*
165165
* 8t Stop playing the note on tone generator t.
@@ -322,6 +322,9 @@
322322
* -Add alternate frequency/duration pair output format and options to support it
323323
* -Add -r option to terminate output with "restart" instead of "stop"
324324
* -Allow hex and octal entry, in addition to decimal, for -cn option
325+
* -Prevent unnecessary "note off" commands from being generated by delaying
326+
* them until we see if a "note on" is generated before the next wait.
327+
* Ported from MIDITONES V1.14
325328
*/
326329
#define VERSION "1.0.0"
327330

@@ -434,7 +437,7 @@ struct track_header {
434437

435438
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem,
436439
velocityoutput, instrumentoutput, percussion_ignore, percussion_translate, do_header,
437-
alt_out, restart, freq_style_a, freq_style_b, option_n;
440+
alt_out, gen_restart, freq_style_a, freq_style_b, option_n;
438441
FILE *infile, *outfile, *logfile;
439442
uint8_t *buffer, *hdrptr;
440443
unsigned long buflen;
@@ -459,6 +462,7 @@ int alt_out_channel; /* alt out format: MIDI channel used */
459462

460463
struct tonegen_status { /* current status of a tone generator */
461464
bool playing; /* is it playing? */
465+
bool stopnote_pending; /* do we need to stop this generator before the next wait? */
462466
int track; /* if so, which track is the note from? */
463467
int note; /* what note is playing? */
464468
int instrument; /* what instrument? */
@@ -472,11 +476,11 @@ struct track_status { /* current processing point of a MIDI track */
472476
uint8_t *trkend; /* ptr past the end of the track */
473477
unsigned long time; /* what time we're at in the score */
474478
unsigned long tempo; /* the tempo last set, in usec per qnote */
475-
unsigned int preferred_tonegen; /* for strategy2, try to use this generator */
476-
unsigned char cmd; /* CMD_xxxx next to do */
479+
unsigned int preferred_tonegen; /* for strategy2, try to use this generator */
480+
unsigned char cmd; /* CMD_xxxx next to do */
477481
unsigned char note; /* for which note */
478482
unsigned char chan; /* from which channel it was */
479-
unsigned char velocity;
483+
unsigned char velocity; /* the current volume */
480484
unsigned char last_event; /* the last event, for MIDI's "running status" */
481485
bool tonegens[MAX_TONEGENS]; /* which tone generators our notes are playing on */
482486
} track[MAX_TRACKS] = {
@@ -719,7 +723,7 @@ does not start with a dash or a slash*/
719723
goto opterror;
720724
break;
721725
case 'R':
722-
restart = true;
726+
gen_restart = true;
723727
if (argv[i][2] != '\0')
724728
goto opterror;
725729
break;
@@ -1145,6 +1149,26 @@ void find_note (int tracknum) {
11451149
}
11461150

11471151

1152+
/* generate "stop note" commands for any channels that have them pending */
1153+
1154+
void gen_stopnotes(void) {
1155+
struct tonegen_status *tg;
1156+
for (int tgnum = 0; tgnum < num_tonegens; ++tgnum) {
1157+
tg = &tonegen[tgnum];
1158+
if (tg->stopnote_pending) {
1159+
if (binaryoutput) {
1160+
putc (CMD_STOPNOTE | tgnum, outfile);
1161+
outfile_bytecount += 1;
1162+
} else {
1163+
fprintf (outfile, " 0x%02X,", CMD_STOPNOTE | tgnum);
1164+
outfile_items (1);
1165+
}
1166+
tg->stopnote_pending = false;
1167+
}
1168+
}
1169+
}
1170+
1171+
11481172
/********************* main ****************************/
11491173

11501174
int main (int argc, char *argv[]) {
@@ -1291,7 +1315,7 @@ int main (int argc, char *argv[]) {
12911315
}
12921316
} else if (do_header) { // write the binary file header
12931317
for (int i = 0; i < sizeof (file_header); ++i)
1294-
fputc (((unsigned char *) &file_header)[i], outfile);
1318+
putc (((unsigned char *) &file_header)[i], outfile);
12951319
file_header_num_tgens_position = (char *) &file_header.num_tgens - (char *) &file_header;
12961320
outfile_bytecount += sizeof (file_header);
12971321
}
@@ -1371,6 +1395,8 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's!
13711395

13721396
delta_time = earliest_time - timenow;
13731397
if (delta_time) {
1398+
if (!alt_out)
1399+
gen_stopnotes(); /* first check if any tone generators have "stop note" commands pending */
13741400
/* Convert ticks to milliseconds based on the current tempo */
13751401
unsigned long long temp;
13761402
temp = ((unsigned long long) delta_time * tempo) / ticks_per_beat;
@@ -1449,19 +1475,11 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's!
14491475
fprintf (logfile,
14501476
"->Stop note %d, generator %d, track %d\n",
14511477
tg->note, tgnum, tracknum);
1452-
if (!alt_out) {
1453-
if (binaryoutput) {
1454-
putc (CMD_STOPNOTE | tgnum, outfile);
1455-
outfile_bytecount += 1;
1456-
} else {
1457-
fprintf (outfile, " 0x%02X,", CMD_STOPNOTE | tgnum);
1458-
outfile_items (1);
1459-
}
1460-
} else {
1461-
pending_note = pending_velocity = 0;
1462-
}
1478+
tg->stopnote_pending = true; /* must stop the current note if another doesn't start first */
14631479
tg->playing = false;
14641480
trk->tonegens[tgnum] = false;
1481+
if (alt_out)
1482+
pending_note = pending_velocity = 0;
14651483
}
14661484
}
14671485
find_note (tracknum); // use up the note
@@ -1507,6 +1525,7 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's!
15071525
tg->playing = true;
15081526
tg->track = tracknum;
15091527
tg->note = trk->note;
1528+
tg->stopnote_pending = false;
15101529
trk->tonegens[tgnum] = true;
15111530
trk->preferred_tonegen = tgnum;
15121531
++note_on_commands;
@@ -1580,25 +1599,28 @@ This is not unlike multiway merging used for tape sorting algoritms in the 50's!
15801599
} /* !parseonly do */
15811600
while (tracks_done < num_tracks);
15821601

1602+
if (!alt_out)
1603+
gen_stopnotes(); /* flush out any pending "stop note" commands */
1604+
15831605
// generate the end-of-score command and some commentary
15841606
if (binaryoutput) {
15851607
if (!alt_out) {
1586-
putc (restart ? CMD_RESTART : CMD_STOP, outfile);
1608+
putc (gen_restart ? CMD_RESTART : CMD_STOP, outfile);
15871609
outfile_bytecount++;
15881610
} else {
1589-
output_word (restart ? TONES_REPEAT : TONES_END);
1611+
output_word (gen_restart ? TONES_REPEAT : TONES_END);
15901612
}
15911613
} else {
15921614
if (outfile_itemcount != 0)
15931615
putc ('\n', outfile);
15941616
if (!alt_out) {
1595-
fprintf (outfile, " 0x%02x", restart ? CMD_RESTART : CMD_STOP);
1617+
fprintf (outfile, " 0x%02x", gen_restart ? CMD_RESTART : CMD_STOP);
15961618
outfile_bytecount++;
15971619
} else {
15981620
if (freq_style_b)
1599-
fprintf (outfile, " 0x%04x", restart ? TONES_REPEAT : TONES_END);
1621+
fprintf (outfile, " 0x%04x", gen_restart ? TONES_REPEAT : TONES_END);
16001622
else
1601-
fprintf (outfile, " %s", restart ? "TONES_REPEAT" : "TONES_END");
1623+
fprintf (outfile, " %s", gen_restart ? "TONES_REPEAT" : "TONES_END");
16021624
outfile_bytecount += 2;
16031625
}
16041626
fprintf (outfile, "\n};\n// This score contains %ld bytes", outfile_bytecount);

midi2tones_32bit.exe

122 KB
Binary file not shown.

midi2tones_64bit.exe

144 KB
Binary file not shown.

miditones_scroll.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@
104104
* 6 August 2016, L. Shustek, V1.5
105105
* - Handle optional instrument change information.
106106
* - Look for the optional self-describing file header.
107+
* 30 September 2016, L. Shustek, V1.6
108+
* - Count the number of unnecessary "stop note" commands in the bytestream
107109
*/
108110

109-
#define VERSION "1.5"
111+
#define VERSION "1.6"
110112

111113
#include <stdio.h>
112114
#include <stdlib.h>
@@ -126,13 +128,15 @@ int gen_note[MAX_TONEGENS]; // the note we're playing, or SILENT
126128
int gen_volume[MAX_TONEGENS]; // the volume we're playing
127129
int gen_instrument[MAX_TONEGENS]; // the instrument we're playing
128130
bool gen_instrument_changed[MAX_TONEGENS];
131+
bool gen_did_stopnote[MAX_TONEGENS]; // did we just do a stopnote?
129132

130133
FILE *infile, *outfile;
131134
unsigned char *buffer, *bufptr;
132135
unsigned long buflen;
133136
unsigned int num_tonegens = 6; // default number of generators
134137
unsigned int max_tonegen_found = 0;
135-
unsigned int notes_skipped;
138+
unsigned int notes_skipped = 0;
139+
unsigned int stopnotes_before_startnote = 0;
136140

137141
unsigned long timenow = 0;
138142
unsigned char cmd, gen;
@@ -512,6 +516,8 @@ int main (int argc, char *argv[]) {
512516
delay = ((unsigned int) cmd << 8) + *++bufptr;
513517
print_status (); // tone generator status now
514518
timenow += delay; // advance time
519+
for (gen = 0; gen < MAX_TONEGENS; ++gen)
520+
gen_did_stopnote[gen] = false;
515521
} else if (cmd != 0xf0) { /* a command */
516522
gen = cmd & 0x0f;
517523
if (gen > max_tonegen_found)
@@ -520,6 +526,10 @@ int main (int argc, char *argv[]) {
520526
if (cmd == 0x90) { /* note on */
521527
gen_note[gen] = *++bufptr; // note number
522528
tonegens_used |= 1 << gen; // record that we used this generator at least once
529+
if (gen_did_stopnote[gen]) {
530+
++stopnotes_before_startnote;
531+
// printf("unnecessary stopnote on gen %d\n", gen);
532+
}
523533
if (expect_volume)
524534
gen_volume[gen] = *++bufptr; // volume
525535
if (gen >= num_tonegens)
@@ -528,6 +538,7 @@ int main (int argc, char *argv[]) {
528538
if (gen_note[gen] == SILENT)
529539
file_error ("tone generator not on", bufptr);
530540
gen_note[gen] = SILENT;
541+
gen_did_stopnote[gen] = true;
531542
} else if (cmd == 0xc0) { /* change instrument */
532543
gen_instrument[gen] = *++bufptr & 0x7f;
533544
gen_instrument_changed[gen] = true;
@@ -555,5 +566,6 @@ int main (int argc, char *argv[]) {
555566
if (notes_skipped)
556567
printf ("%u notes were not displayed because we were told to show only %u generators.\n",
557568
notes_skipped, num_tonegens);
569+
printf("%u stopnote commands were unnecessary.\n", stopnotes_before_startnote);
558570
printf ("Done.\n");
559571
}

miditones_scroll.exe

-179 KB
Binary file not shown.

miditones_scroll_32bit.exe

112 KB
Binary file not shown.

miditones_scroll_64bit.exe

137 KB
Binary file not shown.

0 commit comments

Comments
 (0)