add assembler file format output; other minor changes

master
Len Shustek 1 month ago
parent 5d5acb2e84
commit 6b41ad2c16
  1. 14
      README.txt
  2. 117
      miditones.c
  3. BIN
      miditones.exe

@ -64,7 +64,7 @@
MIDITONES may also prove useful for other simple music synthesizers. There are MIDITONES may also prove useful for other simple music synthesizers. There are
various forks of this code, and of the Playtune players, on Githib. various forks of this code, and of the Playtune players, on Githib.
*** THE PROGRAM *** THE PROGRAM
MIDITONES is written in standard ANSI C and is meant to be executed from the MIDITONES is written in standard ANSI C and is meant to be executed from the
@ -104,8 +104,8 @@
If the user specifies the full .mid filename, the .mid or .MID extension If the user specifies the full .mid filename, the .mid or .MID extension
will be dropped and the remaining name will be used as <basefilename>. will be dropped and the remaining name will be used as <basefilename>.
The input file is <basefilename>.mid, and the output filename(s) The input file is <basefilename>.mid, and the output filename(s) are
are the base file name with .c, .h, .bin, and/or .log extensions. the base file name with .c, .h, .bin, .asm, .inc, and/or .log extensions.
The following commonly-used command-line options can be specified: The following commonly-used command-line options can be specified:
@ -122,7 +122,8 @@
Playtune players that can check the header to know what data to expect. Playtune players that can check the header to know what data to expect.
-b Generate a binary file with the name <basefilename>.bin, instead of a -b Generate a binary file with the name <basefilename>.bin, instead of a
C-language source file with the name <basefilename>.c. C-language source file with the name <basefilename>.c. This is useful
to create a file that can be input to Miditones_scroll.
-t=n Generate the bytestream so that at most "n" tone generators are used. -t=n Generate the bytestream so that at most "n" tone generators are used.
The default is 6 tone generators, and the maximum is 16. The program The default is 6 tone generators, and the maximum is 16. The program
@ -140,6 +141,9 @@
-dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream. -dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream.
-asm1802 Generate assembler code for the Cosmac Elf 1802 microprocessor
-asm6502 Generate assembler code for the MOS technology 6502 microprocessor
-k=n Change the musical key of the output by n chromatic notes. -k=n Change the musical key of the output by n chromatic notes.
-k=-12 goes one octave down, -k=12 goes one octave up, etc. -k=-12 goes one octave down, -k=12 goes one octave up, etc.
@ -256,4 +260,4 @@
Any subsequent header bytes included in the length are currently undefined Any subsequent header bytes included in the length are currently undefined
and should be ignored by players. and should be ignored by players.
Len Shustek, 2011 to 2021; see the change log. Len Shustek, 2011 to 2025; see the change log.

@ -66,6 +66,7 @@
MIDITONES may also prove useful for other simple music synthesizers. There are MIDITONES may also prove useful for other simple music synthesizers. There are
various forks of this code, and of the Playtune players, on Githib. various forks of this code, and of the Playtune players, on Githib.
*** THE PROGRAM *** THE PROGRAM
@ -106,8 +107,8 @@
If the user specifies the full .mid filename, the .mid or .MID extension If the user specifies the full .mid filename, the .mid or .MID extension
will be dropped and the remaining name will be used as <basefilename>. will be dropped and the remaining name will be used as <basefilename>.
The input file is <basefilename>.mid, and the output filename(s) The input file is <basefilename>.mid, and the output filename(s) are
are the base file name with .c, .h, .bin, and/or .log extensions. the base file name with .c, .h, .bin, .asm, .inc, and/or .log extensions.
The following commonly-used command-line options can be specified: The following commonly-used command-line options can be specified:
@ -124,7 +125,8 @@
Playtune players that can check the header to know what data to expect. Playtune players that can check the header to know what data to expect.
-b Generate a binary file with the name <basefilename>.bin, instead of a -b Generate a binary file with the name <basefilename>.bin, instead of a
C-language source file with the name <basefilename>.c. C-language source file with the name <basefilename>.c. This is useful
to create a file that can be input to Miditones_scroll.
-t=n Generate the bytestream so that at most "n" tone generators are used. -t=n Generate the bytestream so that at most "n" tone generators are used.
The default is 6 tone generators, and the maximum is 16. The program The default is 6 tone generators, and the maximum is 16. The program
@ -142,6 +144,9 @@
-dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream. -dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream.
-asm1802 Generate assembler code for the Cosmac Elf 1802 microprocessor
-asm6502 Generate assembler code for the MOS technology 6502 microprocessor
-k=n Change the musical key of the output by n chromatic notes. -k=n Change the musical key of the output by n chromatic notes.
-k=-12 goes one octave down, -k=12 goes one octave up, etc. -k=-12 goes one octave down, -k=12 goes one octave up, etc.
@ -258,11 +263,11 @@
Any subsequent header bytes included in the length are currently undefined Any subsequent header bytes included in the length are currently undefined
and should be ignored by players. and should be ignored by players.
Len Shustek, 2011 to 2021; see the change log. Len Shustek, 2011 to 2025; see the change log.
*---------------------------------------------------------------------------------------- *----------------------------------------------------------------------------------------
* The MIT License (MIT) * The MIT License (MIT)
* Copyright (c) 2011,2013,2015,2016,2019,2021 Len Shustek * Copyright (c) 2011,2013,2015,2016,2019,2021,2025 Len Shustek
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -394,10 +399,13 @@
(Thanks to Jonathan Oakley for providing an example of the problem.) (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 -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. identical notes. So add a -noduplicates option to do just that.
10 April 2025, Len Shustek, V2.5
-Put the length of the song as a comment at the end of the generated source code.
-Add -asm1802 and -asm6502 options, adopting changes from GitHub user fourstix.
future version ideas future version ideas
-Perhaps elide "note off/note on" event sequences for the same note that -Perhaps elide "note off/note on" event sequences for the same note that
become adjacent because of -delaymin. Does that happen much, or at all? become adjacent because of -delaymin. Does that happen much, or at all?
-Allow the flexibility to specify note timing on a track-by-track or -Allow the flexibility to specify note timing on a track-by-track or
@ -409,7 +417,7 @@ future version ideas
channel 8 // organ channel 8 // organ
options -attacktime=1000 -sustainlevel=80% -releasetime=100 -notemin=200 options -attacktime=1000 -sustainlevel=80% -releasetime=100 -notemin=200
*/ */
#define VERSION "2.4" #define VERSION "2.5"
/*-------------------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------------------
@ -601,7 +609,10 @@ struct track_header {
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem, bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem,
volume_output, instrumentoutput, percussion_ignore, percussion_translate, do_header, volume_output, instrumentoutput, percussion_ignore, percussion_translate, do_header,
gen_restart, scorename, showskipped, noduplicates; gen_restart, scorename, showskipped, noduplicates, asm_output;
char *comment = "//"; // default start of comment
char *hex = "0x"; // default start of hex constant
char *byteop = ""; // what assembler psuedo-op indicates a byte constant
FILE *infile, *outfile, *logfile; FILE *infile, *outfile, *logfile;
uint8_t *buffer, *hdrptr; uint8_t *buffer, *hdrptr;
unsigned long buflen; unsigned long buflen;
@ -708,7 +719,7 @@ void SayUsage(char *programName) {
"", "",
"Use: miditones <options> <basefilename>", "Use: miditones <options> <basefilename>",
" input file will be <basefilename>.mid", " input file will be <basefilename>.mid",
" output file will be <basefilename>.bin or .c or .h", " output file will be <basefilename>.bin, .c, .h, .asm, or .inc",
" log file will be <basefilename>.log", " log file will be <basefilename>.log",
"", "",
"Commonly-used options:", "Commonly-used options:",
@ -733,6 +744,8 @@ void SayUsage(char *programName) {
" -r terminate output file with \"restart\" instead of \"stop\" command", " -r terminate output file with \"restart\" instead of \"stop\" command",
" -s1 strategy 1: favor track 1", " -s1 strategy 1: favor track 1",
" -s2 strategy 2: try to assign tracks to specific tone generators", " -s2 strategy 2: try to assign tracks to specific tone generators",
" -asm1802 generate assembler code for the 1802 microprocessor",
" -asm6502 generate assembler code for the 6502 microprocessor",
" -showskipped display information about each note that had to be skipped", " -showskipped display information about each note that had to be skipped",
" -noduplicates remove identical notes playing on different channels", " -noduplicates remove identical notes playing on different channels",
" -delaymin=x minimum delay is x msec, to save bytestream space", " -delaymin=x minimum delay is x msec, to save bytestream space",
@ -771,6 +784,10 @@ int HandleOptions (int argc, char *argv[]) {
else if (opt_key(arg, "pi")) percussion_ignore = true; else if (opt_key(arg, "pi")) percussion_ignore = true;
else if (opt_key(arg, "pt")) percussion_translate = true; else if (opt_key(arg, "pt")) percussion_translate = true;
else if (opt_key(arg, "r")) gen_restart = true; else if (opt_key(arg, "r")) gen_restart = true;
else if (opt_key(arg, "asm1802")) {
asm_output = true; comment = ";"; byteop = " db "; hex = "$"; outfile_maxitems = 16; }
else if (opt_key(arg, "asm6502")) {
asm_output = true; comment = ";"; byteop = " .byte "; hex = "$"; outfile_maxitems = 16; }
else if (opt_int(arg, "delaymin", &tempint, 1, 1000)) delaymin_usec = tempint * 1000; else if (opt_int(arg, "delaymin", &tempint, 1, 1000)) delaymin_usec = tempint * 1000;
else if (opt_int(arg, "releasetime", &tempint, 0, INT_MAX)) releasetime_usec = tempint * 1000; else if (opt_int(arg, "releasetime", &tempint, 0, INT_MAX)) releasetime_usec = tempint * 1000;
else if (opt_int(arg, "notemin", &tempint, 0, INT_MAX)) notemin_usec = tempint * 1000; else if (opt_int(arg, "notemin", &tempint, 0, INT_MAX)) notemin_usec = tempint * 1000;
@ -801,7 +818,7 @@ int HandleOptions (int argc, char *argv[]) {
return firstnonoption; } return firstnonoption; }
void print_command_line (FILE *file, int argc, char *argv[]) { void print_command_line (FILE *file, int argc, char *argv[]) {
fprintf (file, "// command line: "); fprintf (file, "%s command line: ", comment);
for (int i = 0; i < argc; i++) fprintf (file, "%s ", argv[i]); for (int i = 0; i < argc; i++) fprintf (file, "%s ", argv[i]);
fprintf (file, "\n"); } fprintf (file, "\n"); }
@ -888,12 +905,15 @@ uint32_t rev_long (uint32_t val) {
/* account for new items in the non-binary output file /* account for new items in the non-binary output file
and generate a newline every so often. */ and generate a newline every so often. */
void outfile_items (int n) { void outfile_items(int n) {
outfile_bytecount += n; outfile_bytecount += n;
outfile_itemcount += n; outfile_itemcount += n;
if (!binaryoutput && outfile_itemcount >= outfile_maxitems) { if (!binaryoutput) {
fprintf (outfile, "\n"); if (outfile_itemcount >= outfile_maxitems) {
outfile_itemcount = 0; } } if (asm_output) fprintf(outfile, "\n%s", byteop);
else fprintf(outfile, ",\n");
outfile_itemcount = 0; }
else fprintf(outfile, ", "); } }
//******* structures for recording track, channel, and tone generator status //******* structures for recording track, channel, and tone generator status
@ -1076,8 +1096,13 @@ void remove_queue_entry(int ndx) { // remove the oldest queue entry
putc(CMD_INSTRUMENT | tgnum, outfile); putc(CMD_INSTRUMENT | tgnum, outfile);
putc(tg->note.instrument, outfile); putc(tg->note.instrument, outfile);
outfile_bytecount += 2; } outfile_bytecount += 2; }
else if (asm_output) {
fprintf(outfile, "%s%02X", hex, CMD_INSTRUMENT | tgnum);
outfile_items(1);
fprintf(outfile, "%3d", tg->note.instrument);
outfile_items(1); }
else { else {
fprintf(outfile, "0x%02X,%d, ", CMD_INSTRUMENT | tgnum, tg->note.instrument); fprintf(outfile, "0x%02X,%d", CMD_INSTRUMENT | tgnum, tg->note.instrument);
outfile_items(2); } } } outfile_items(2); } } }
if (loggen) fprintf(logfile, " play tgen %d %s\n", tgnum, describe(&q->note)); if (loggen) fprintf(logfile, " play tgen %d %s\n", tgnum, describe(&q->note));
tg->playing = true; tg->playing = true;
@ -1093,12 +1118,20 @@ void remove_queue_entry(int ndx) { // remove the oldest queue entry
if (volume_output) { if (volume_output) {
putc(tg->note.volume, outfile); putc(tg->note.volume, outfile);
outfile_bytecount +=1; } } outfile_bytecount +=1; } }
else if (asm_output) {
fprintf(outfile, "%s%02X", hex, CMD_PLAYNOTE | tgnum);
outfile_items(1);
fprintf(outfile, "%3d", tg->note.note);
outfile_items(1);
if (volume_output) {
fprintf(outfile, "%3d", tg->note.volume);
outfile_items(1); } }
else { else {
if (volume_output == 0) { if (volume_output == 0) {
fprintf(outfile, "0x%02X,%d, ", CMD_PLAYNOTE | tgnum, tg->note.note); fprintf(outfile, "0x%02X,%d", CMD_PLAYNOTE | tgnum, tg->note.note);
outfile_items(2); } outfile_items(2); }
else { else {
fprintf(outfile, "0x%02X,%d,%d, ", CMD_PLAYNOTE | tgnum, tg->note.note, tg->note.volume); fprintf(outfile, "0x%02X,%d,%d", CMD_PLAYNOTE | tgnum, tg->note.note, tg->note.volume);
outfile_items(3); } } } outfile_items(3); } } }
else { else {
if (loggen) fprintf(logfile, " *** at %lu.%03lu msec no free generator; skipping %s\n", if (loggen) fprintf(logfile, " *** at %lu.%03lu msec no free generator; skipping %s\n",
@ -1117,8 +1150,13 @@ void generate_delay(unsigned long delta_msec) { // output a delay command
putc((byte)(delta_msec >> 8), outfile); putc((byte)(delta_msec >> 8), outfile);
putc((byte)(delta_msec & 0xff), outfile); putc((byte)(delta_msec & 0xff), outfile);
outfile_bytecount += 2; } outfile_bytecount += 2; }
else if (asm_output) {
fprintf(outfile, "%s%02X", hex, (byte)(delta_msec >> 8));
outfile_items(1);
fprintf(outfile, "%s%02X", hex, (byte)(delta_msec & 0xff));
outfile_items(1); }
else { else {
fprintf(outfile, "%ld,%ld, ", delta_msec >> 8, delta_msec & 0xff); fprintf(outfile, "%ld,%ld", delta_msec >> 8, delta_msec & 0xff);
outfile_items(2); } } } outfile_items(2); } } }
// output all queue elements which are at the oldest time or at most "delaymin" later // output all queue elements which are at the oldest time or at most "delaymin" later
@ -1156,7 +1194,7 @@ void pull_queue(void) {
putc(CMD_STOPNOTE | tgnum, outfile); putc(CMD_STOPNOTE | tgnum, outfile);
outfile_bytecount += 1; } outfile_bytecount += 1; }
else { else {
fprintf(outfile, "0x%02X, ", CMD_STOPNOTE | tgnum); fprintf(outfile, "%s%02X", hex, CMD_STOPNOTE | tgnum);
outfile_items(1); } outfile_items(1); }
if (loggen) fprintf(logfile, " stop tgen %d %s\n", tgnum, describe(&tg->note)); if (loggen) fprintf(logfile, " stop tgen %d %s\n", tgnum, describe(&tg->note));
tg->stopnote_pending = false; tg->stopnote_pending = false;
@ -1356,7 +1394,7 @@ void find_next_note (int tracknum) {
tag = "copyright"; goto show_text; tag = "copyright"; goto show_text;
case 0x03: case 0x03:
tag = "track name"; tag = "track name";
if (tracknum == 0 && !parseonly && !binaryoutput) { if (tracknum == 0 && !parseonly && !binaryoutput && !asm_output) {
/* Incredibly, MIDI has no standard for recording the name of the piece! /* Incredibly, MIDI has no standard for recording the name of the piece!
Track 0's "trackname" is often used for that so we output it to the C file as documentation. */ Track 0's "trackname" is often used for that so we output it to the C file as documentation. */
fprintf (outfile, "// "); fprintf (outfile, "// ");
@ -1622,8 +1660,9 @@ void process_track_data(void) {
putc(gen_restart ? CMD_RESTART : CMD_STOP, outfile); putc(gen_restart ? CMD_RESTART : CMD_STOP, outfile);
outfile_bytecount +=1; } outfile_bytecount +=1; }
else { else {
fprintf(outfile, "0x%02X};", gen_restart ? CMD_RESTART : CMD_STOP); fprintf(outfile, "%s%02X", hex, gen_restart ? CMD_RESTART : CMD_STOP);
outfile_items(1); ++outfile_bytecount;
if (!asm_output) fprintf(outfile, "};");
fprintf(outfile, "\n"); } } fprintf(outfile, "\n"); } }
@ -1647,7 +1686,7 @@ int main (int argc, char *argv[]) {
SayUsage (argv[0]); SayUsage (argv[0]);
exit (4); } exit (4); }
filebasename = argv[argno]; filebasename = argv[argno];
// strip off trailing .mid or .MID extension if provided by user // strip off trailing .mid or .MID extension if provided by user
basenamelen = strlength(filebasename); basenamelen = strlength(filebasename);
if (basenamelen > 4 && if (basenamelen > 4 &&
@ -1696,7 +1735,7 @@ int main (int argc, char *argv[]) {
miditones_strlcat (filename, ".bin", MAXPATH); miditones_strlcat (filename, ".bin", MAXPATH);
outfile = fopen (filename, "wb"); } outfile = fopen (filename, "wb"); }
else { else {
miditones_strlcat (filename, scorename ? ".h" : ".c", MAXPATH); miditones_strlcat (filename, asm_output ? (scorename ? ".inc" : ".asm") : (scorename ? ".h" : ".c"), MAXPATH);
outfile = fopen (filename, "w"); } outfile = fopen (filename, "w"); }
if (!outfile) { if (!outfile) {
fprintf (stderr, "Unable to open output file %s\n", filename); fprintf (stderr, "Unable to open output file %s\n", filename);
@ -1708,27 +1747,29 @@ int main (int argc, char *argv[]) {
if (!binaryoutput) { /* create header of C file that initializes score data */ if (!binaryoutput) { /* create header of C file that initializes score data */
time_t rawtime; time_t rawtime;
time (&rawtime); time (&rawtime);
fprintf (outfile, "// Playtune bytestream for file \"%s.mid\" ", filebasename); fprintf (outfile, "%s Playtune bytestream for file \"%s.mid\" ", comment, filebasename);
fprintf (outfile, "created by MIDITONES V%s on %s", VERSION, fprintf (outfile, "created by MIDITONES V%s on %s", VERSION,
asctime (localtime (&rawtime))); asctime (localtime (&rawtime)));
print_command_line (outfile, argc, argv); print_command_line (outfile, argc, argv);
if (channel_mask != 0xffff) if (channel_mask != 0xffff)
fprintf (outfile, "// Only the masked channels were processed: %04X\n", channel_mask); fprintf (outfile, "%s Only the masked channels were processed: %04X\n", comment, channel_mask);
if (keyshift != 0) if (keyshift != 0)
fprintf (outfile, "// Keyshift was %d chromatic notes\n", keyshift); fprintf (outfile, "%s Keyshift was %d chromatic notes\n", comment, keyshift);
if (define_progmem) { if (define_progmem) {
fprintf (outfile, "#ifdef __AVR__\n"); fprintf (outfile, "#ifdef __AVR__\n");
fprintf (outfile, "#include <avr/pgmspace.h>\n"); fprintf (outfile, "#include <avr/pgmspace.h>\n");
fprintf (outfile, "#else\n"); fprintf (outfile, "#else\n");
fprintf (outfile, "#define PROGMEM\n"); fprintf (outfile, "#define PROGMEM\n");
fprintf (outfile, "#endif\n"); } fprintf (outfile, "#endif\n"); }
fprintf (outfile, "const unsigned char PROGMEM %s [] = {\n", if (asm_output) // create the assembler label for the score
fprintf(outfile, "\n%s:\n%s", filebasename, byteop);
else fprintf (outfile, "const unsigned char PROGMEM %s [] = {\n",
scorename ? filebasename : "score"); scorename ? filebasename : "score");
if (do_header) { // write the C initialization for the file header if (do_header) { // write the initialization for the file header
fprintf (outfile, "'P','t', 6, 0x%02X, 0x%02X, ", file_header.f1, file_header.f2); fprintf (outfile, "'P','t', 6, %s%02X, %s%02X, ", hex, file_header.f1, hex, file_header.f2);
fflush (outfile); fflush (outfile);
file_header_num_tgens_position = ftell (outfile); // remember where the number of tone generators is file_header_num_tgens_position = ftell (outfile); // remember where the number of tone generators is
fprintf (outfile, "%2d, // (Playtune file header)\n", file_header.num_tgens); fprintf (outfile, "%2d%s %s (Playtune file header)\n%s", file_header.num_tgens, asm_output ? "" : ",", comment, byteop);
outfile_bytecount += 6; } } outfile_bytecount += 6; } }
else if (do_header) { // write the binary file header else if (do_header) { // write the binary file header
int i; int i;
@ -1773,11 +1814,14 @@ int main (int argc, char *argv[]) {
// generate the ending commentary // generate the ending commentary
if (!binaryoutput) { if (!binaryoutput) {
fprintf(outfile, "\n// This %ld byte score contains %d notes and uses %d tone generator%s\n", fprintf(outfile, "\n%s This %ld byte score contains %d notes and uses %d tone generator%s.\n",
outfile_bytecount, note_on_commands, num_tonegens_used, comment, outfile_bytecount, note_on_commands, num_tonegens_used,
num_tonegens_used == 1 ? "" : "s"); num_tonegens_used == 1 ? "" : "s");
fprintf(outfile, "%s It encodes %u.%03u seconds of music with %d tempo change%s.\n",
comment, (unsigned)(timenow_usec / 1000000), (unsigned)(timenow_usec / 1000 % 1000),
tempo_changes, tempo_changes == 1 ? "" : "s");
if (notes_skipped) if (notes_skipped)
fprintf(outfile, "// %d notes had to be skipped\n", notes_skipped); } fprintf(outfile, "%s %d notes had to be skipped.\n", comment, notes_skipped); }
printf(" %s %d tone generators were used.\n", printf(" %s %d tone generators were used.\n",
num_tonegens_used < num_tonegens ? "Only" : "All", num_tonegens_used); num_tonegens_used < num_tonegens ? "Only" : "All", num_tonegens_used);
if (notes_skipped) if (notes_skipped)
@ -1793,8 +1837,9 @@ int main (int argc, char *argv[]) {
" (Consider recompiling with MAX_TRACKNOTES bigger than %d, to allow more simultaneous notes.)\n", " (Consider recompiling with MAX_TRACKNOTES bigger than %d, to allow more simultaneous notes.)\n",
noteinfo_overflow, noteinfo_notfound, MAX_CHANNELNOTES); noteinfo_overflow, noteinfo_notfound, MAX_CHANNELNOTES);
printf(" %ld bytes of score data were generated, ", outfile_bytecount); printf(" %ld bytes of score data were generated, ", outfile_bytecount);
printf("representing %u.%03u seconds of music with %d tempo changes\n", printf("encoding %u.%03u seconds of music with %d tempo change%s.\n",
(unsigned)(timenow_usec / 1000000), (unsigned)(timenow_usec / 1000 % 1000), tempo_changes); (unsigned)(timenow_usec / 1000000), (unsigned)(timenow_usec / 1000 % 1000),
tempo_changes, tempo_changes == 1 ? "" : "s");
if (delaymin_usec) if (delaymin_usec)
printf(" %ld delays were removed because the minimum delay of %u msec caused events to be merged\n", printf(" %ld delays were removed because the minimum delay of %u msec caused events to be merged\n",
delays_saved, (unsigned)(delaymin_usec / 1000)); delays_saved, (unsigned)(delaymin_usec / 1000));

Binary file not shown.
Loading…
Cancel
Save