|
|
@ -2,94 +2,135 @@ |
|
|
|
* |
|
|
|
* |
|
|
|
* MIDITONES_SCROLL |
|
|
|
* MIDITONES_SCROLL |
|
|
|
* |
|
|
|
* |
|
|
|
* Decode a PLAYTUNES bytestream of notes as a time-ordered scroll, sort of like a |
|
|
|
* Decode a PLAYTUNE bytestream of notes as a time-ordered scroll, sort of like a |
|
|
|
* piano roll with non-uniform time. This is a command-line program with no GUI. |
|
|
|
* piano roll with non-uniform time. This is a command-line program with no GUI. |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* There are two primary uses: |
|
|
|
* There are two primary uses: |
|
|
|
* |
|
|
|
* |
|
|
|
* (1) To debug programming errors that cause some MIDI scripts to sound strange. |
|
|
|
* (1) To debug errors that cause some MIDI scripts to sound strange. |
|
|
|
* |
|
|
|
* |
|
|
|
* (2) To create a C-program array initialized with the bytestream, but annotated |
|
|
|
* (2) To create a C-program array initialized with the bytestream, but annotated |
|
|
|
* with the original notes. This is semantically the same as the normal output |
|
|
|
* with the original notes. This is semantically the same as the normal output |
|
|
|
* of MIDITONES, but is easier to edit manually. The downside is that the C |
|
|
|
* of MIDITONES, but is easier to edit manually. The downside is that the C |
|
|
|
* source code file is much larger. |
|
|
|
* source code file is much larger. |
|
|
|
* |
|
|
|
* |
|
|
|
* In both cases it reads a .bin file that was created from a .mid file by MIDITONES |
|
|
|
* In both cases it reads a xxx.bin file that was created from a MIDI file by |
|
|
|
* using the -b option. |
|
|
|
* MIDITONES using the -b option. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* For use case (1), just invoke the program with the base filename. The output is |
|
|
|
|
|
|
|
* to the console, which can be directed to a file using the usual >file redirection. |
|
|
|
|
|
|
|
* For example, starting with the original midi file "song.mid", say this: |
|
|
|
* |
|
|
|
* |
|
|
|
* For use case (1), just invoke the program with the base filename. The output is to the |
|
|
|
|
|
|
|
* console, which can be directed to a file using the usual >file redirection. |
|
|
|
|
|
|
|
* Starting with the original midi file "song.mid", say this: |
|
|
|
|
|
|
|
* miditones -b song |
|
|
|
* miditones -b song |
|
|
|
* miditones_scroll song >song.txt |
|
|
|
* miditones_scroll song >song.txt |
|
|
|
|
|
|
|
* |
|
|
|
* and then the file "song.txt" will contain the piano roll. |
|
|
|
* and then the file "song.txt" will contain the piano roll. |
|
|
|
* |
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
* For use case (2), use the -c option to create a <basefile>.c file. |
|
|
|
* For use case (2), use the -c option to create a <basefile>.c file. |
|
|
|
* Starting with the original midi file "song.mid", say this: |
|
|
|
* For example, starting with the original midi file "song.mid", say this: |
|
|
|
|
|
|
|
* |
|
|
|
* miditones -b song |
|
|
|
* miditones -b song |
|
|
|
* miditones_scroll -c song |
|
|
|
* miditones_scroll -c song |
|
|
|
|
|
|
|
* |
|
|
|
* and then the file "song.c" will contain the PLAYTUNE bytestream C code. |
|
|
|
* and then the file "song.c" will contain the PLAYTUNE bytestream C code. |
|
|
|
* |
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Other command-line options: |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* -tn says that up to n tone generators should be displayed. The default |
|
|
|
|
|
|
|
* is 6 and the maximum is 16. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* -v says that we expect the binary file to contain encoded note volume |
|
|
|
|
|
|
|
* information as generated from Miditones with the -v option. That |
|
|
|
|
|
|
|
* volume information is displayed next to each note. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* -vi says that we expect volume information to be in the file, but we |
|
|
|
|
|
|
|
* should ignore it when creating the display. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* For source code to this and related programs, see |
|
|
|
|
|
|
|
* https://github.com/LenShustek/arduino-playtune
|
|
|
|
|
|
|
|
* https://github.com/LenShustek/miditones
|
|
|
|
|
|
|
|
* |
|
|
|
*---------------------------------------------------------------------------------- |
|
|
|
*---------------------------------------------------------------------------------- |
|
|
|
* (C) Copyright 2011,2013,2015 Len Shustek |
|
|
|
* (C) Copyright 2011,2013,2016 Len Shustek |
|
|
|
* |
|
|
|
* |
|
|
|
* This program is free software: you can redistribute it and/or modify |
|
|
|
* This program is free software. You can redistribute it and/or modify |
|
|
|
* it under the terms of version 3 of the GNU General Public License as |
|
|
|
* it under the terms of version 3 of the GNU General Public License as |
|
|
|
* published by the Free Software Foundation at http://www.gnu.org/licenses,
|
|
|
|
* published by the Free Software Foundation at www.gnu.org/licenses, |
|
|
|
* with Additional Permissions under term 7(b) that the original copyright |
|
|
|
* with Additional Permissions under term 7(b) that the original copyright |
|
|
|
* notice and author attibution must be preserved and under term 7(c) that |
|
|
|
* notice and author attibution must be preserved and under term 7(c) that |
|
|
|
* modified versions be marked as different from the original. |
|
|
|
* modified versions be marked as different from the original. |
|
|
|
* |
|
|
|
* |
|
|
|
* This program is distributed in the hope that it will be useful, |
|
|
|
* This program is distributed in the hope that it will be useful, |
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
* but WITHOUT ANY WARRANTY,without even the implied warranty of |
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
* GNU General Public License for more details. |
|
|
|
* GNU General Public License for more details. |
|
|
|
* |
|
|
|
* |
|
|
|
***********************************************************************************/ |
|
|
|
***********************************************************************************/ |
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Change log |
|
|
|
* Change log |
|
|
|
|
|
|
|
* |
|
|
|
* 26 February 2011, L.Shustek, V1.0 |
|
|
|
* 26 February 2011, L.Shustek, V1.0 |
|
|
|
* -Initial release |
|
|
|
* - Initial release |
|
|
|
* 29 December 2013, L.Shustek, V1.1 |
|
|
|
* 29 December 2013, L.Shustek, V1.1 |
|
|
|
* - Add a "-c" option to create C code output. |
|
|
|
* - Add a "-c" option to create C code output. |
|
|
|
* Thanks go to mats.engstrom for the idea. |
|
|
|
* Thanks go to mats.engstrom for the idea. |
|
|
|
* xx yyyyyyyy zzzz, L.Shustek, V1.2 |
|
|
|
|
|
|
|
* - ??? |
|
|
|
|
|
|
|
* 04 April 2015, L. Shustek, V1.3 |
|
|
|
* 04 April 2015, L. Shustek, V1.3 |
|
|
|
* -Made friendlier to other compilers: import source of strlcpy and strlcat, |
|
|
|
* - Made friendlier to other compilers: import source of strlcpy and strlcat, |
|
|
|
* fixed various type mismatches that the LCC compiler didn't fret about. |
|
|
|
* and fix various type mismatches that the LCC compiler didn't fret about. |
|
|
|
* Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x. |
|
|
|
* Generate "const" data initialization for compatibility with Arduino IDE v1.6.x. |
|
|
|
* |
|
|
|
* 5 August 2016, L. Shustek, V1.4 |
|
|
|
|
|
|
|
* - Add -v and -vi options to handle optional volume codes that Miditones can |
|
|
|
|
|
|
|
* now generate. |
|
|
|
|
|
|
|
* - Handle notes>127 as percussion, which Miditones can now generate |
|
|
|
|
|
|
|
* - Make the number of generators be 16 maximum, but the number actually displayed |
|
|
|
|
|
|
|
* can be specified by the -tn command-line option. |
|
|
|
|
|
|
|
* - Remove string.h because OS X has macros for strlcpy; thus had to add strlen(). |
|
|
|
|
|
|
|
* (It's tough to write even non-GUI code for many environments!) |
|
|
|
|
|
|
|
* - Add casts for where address subtraction is a long, like OS X. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#define VERSION "1.3" |
|
|
|
#define VERSION "1.4" |
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
#include <ctype.h> |
|
|
|
#include <ctype.h> |
|
|
|
#include <stdbool.h> |
|
|
|
#include <stdbool.h> |
|
|
|
#include <time.h> |
|
|
|
#include <time.h> |
|
|
|
|
|
|
|
#include <inttypes.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********** Global variables ******************/ |
|
|
|
/*********** Global variables ******************/ |
|
|
|
|
|
|
|
|
|
|
|
#define MAX_TONEGENS 6 /* max tone generators to display */ |
|
|
|
#define MAX_TONEGENS 16 /* max tone generators we could display */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int gen_note[MAX_TONEGENS]; // the note we're playing, or SILENT
|
|
|
|
#define SILENT -1 |
|
|
|
#define SILENT -1 |
|
|
|
static int gen_status[MAX_TONEGENS]; |
|
|
|
int gen_volume[MAX_TONEGENS]; // the volume we're playing
|
|
|
|
|
|
|
|
|
|
|
|
FILE *infile, *outfile; |
|
|
|
FILE *infile, *outfile; |
|
|
|
unsigned char *buffer, *bufptr; |
|
|
|
unsigned char *buffer, *bufptr; |
|
|
|
unsigned long buflen; |
|
|
|
unsigned long buflen; |
|
|
|
|
|
|
|
unsigned int num_tonegens = 6; // default number of generators
|
|
|
|
|
|
|
|
unsigned int max_tonegen_found = 0; |
|
|
|
|
|
|
|
unsigned int notes_skipped; |
|
|
|
|
|
|
|
|
|
|
|
unsigned long timenow = 0; |
|
|
|
unsigned long timenow = 0; |
|
|
|
unsigned char cmd, gen; |
|
|
|
unsigned char cmd, gen; |
|
|
|
unsigned char *lastbufptr; |
|
|
|
unsigned char *lastbufptr; |
|
|
|
unsigned delay; |
|
|
|
unsigned delay; |
|
|
|
bool codeoutput = false; |
|
|
|
bool codeoutput = false; |
|
|
|
|
|
|
|
bool expect_volume = false; |
|
|
|
|
|
|
|
bool ignore_volume = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *notename[256] = { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// map from MIDI note number to octave and note name,
|
|
|
|
|
|
|
|
|
|
|
|
static char *notename[128] = { // map from MIDI note number to octave and note name
|
|
|
|
|
|
|
|
"-1C ","-1C#","-1D ","-1D#","-1E ","-1F ","-1F#","-1G ","-1G#","-1A ","-1A#","-1B ", |
|
|
|
"-1C ","-1C#","-1D ","-1D#","-1E ","-1F ","-1F#","-1G ","-1G#","-1A ","-1A#","-1B ", |
|
|
|
" 0C "," 0C#"," 0D "," 0D#"," 0E "," 0F "," 0F#"," 0G "," 0G#"," 0A "," 0A#"," 0B ", |
|
|
|
" 0C "," 0C#"," 0D "," 0D#"," 0E "," 0F "," 0F#"," 0G "," 0G#"," 0A "," 0A#"," 0B ", |
|
|
|
" 1C "," 1C#"," 1D "," 1D#"," 1E "," 1F "," 1F#"," 1G "," 1G#"," 1A "," 1A#"," 1B ", |
|
|
|
" 1C "," 1C#"," 1D "," 1D#"," 1E "," 1F "," 1F#"," 1G "," 1G#"," 1A "," 1A#"," 1B ", |
|
|
@ -100,7 +141,26 @@ static char *notename[128] = { // map from MIDI note number to octave and note n |
|
|
|
" 6C "," 6C#"," 6D "," 6D#"," 6E "," 6F "," 6F#"," 6G "," 6G#"," 6A "," 6A#"," 6B ", |
|
|
|
" 6C "," 6C#"," 6D "," 6D#"," 6E "," 6F "," 6F#"," 6G "," 6G#"," 6A "," 6A#"," 6B ", |
|
|
|
" 7C "," 7C#"," 7D "," 7D#"," 7E "," 7F "," 7F#"," 7G "," 7G#"," 7A "," 7A#"," 7B ", |
|
|
|
" 7C "," 7C#"," 7D "," 7D#"," 7E "," 7F "," 7F#"," 7G "," 7G#"," 7A "," 7A#"," 7B ", |
|
|
|
" 8C "," 8C#"," 8D "," 8D#"," 8E "," 8F "," 8F#"," 8G "," 8G#"," 8A "," 8A#"," 8B ", |
|
|
|
" 8C "," 8C#"," 8D "," 8D#"," 8E "," 8F "," 8F#"," 8G "," 8G#"," 8A "," 8A#"," 8B ", |
|
|
|
" 9C "," 9C#"," 9D "," 9D#"," 9E "," 9F "," 9F#"," 9G " |
|
|
|
" 9C "," 9C#"," 9D "," 9D#"," 9E "," 9F "," 9F#"," 9G ", |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// or to channel 9 percussion notes as relocated by Miditones to notes 128..255
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"P000 ", "P001 ", "P002 ", "P003 ", "P004 ", "P005 ", "P006 ", "P007 ", |
|
|
|
|
|
|
|
"P008 ", "P009 ", "P010 ", "P011 ", "P012 ", "P013 ", "P014 ", "P015 ", |
|
|
|
|
|
|
|
"P016 ", "P017 ", "P018 ", "P019 ", "P020 ", "P021 ", "P022 ", "P023 ", |
|
|
|
|
|
|
|
"P024 ", "P025 ", "P026 ", "Laser", "Whip ", "ScrPu", "ScrPl", "Stick", |
|
|
|
|
|
|
|
"MetCk", "P033 ", "MetBl", "BassD", "KickD", "SnaSt", "SnaD ", "Clap ", |
|
|
|
|
|
|
|
"ESnaD", "FTom2", "HHatC", "FTom1", "HHatF", "LTom ", "HHatO", "LMTom", |
|
|
|
|
|
|
|
"HMTom", "CrCym", "HTom ", "RiCym", "ChCym", "RiBel", "Tamb ", "SpCym", |
|
|
|
|
|
|
|
"CowBl", "CrCym", "VSlap", "RiCym", "HBong", "LBong", "CongD", "Conga", |
|
|
|
|
|
|
|
"Tumba", "HTimb", "LTimb", "HAgog", "LAgog", "Cabas", "Marac", "SWhis", |
|
|
|
|
|
|
|
"LWhis", "SGuir", "LGuir", "Clave", "HWood", "LWood", "HCuic", "LCuic", |
|
|
|
|
|
|
|
"MTria", "OTria", "Shakr", "Sleig", "BelTr", "Casta", "SirdD", "Sirdu", |
|
|
|
|
|
|
|
"P088 ", "P089 ", "P090 ", "SnDmR", "OcDrm", "SmDrB", "P094 ", "P095 ", |
|
|
|
|
|
|
|
"P096 ", "P097 ", "P098 ", "P099 ", "P100 ", "P101 ", "P102 ", "P103 ", |
|
|
|
|
|
|
|
"P104 ", "P105 ", "P106 ", "P107 ", "P108 ", "P109 ", "P110 ", "P111 ", |
|
|
|
|
|
|
|
"P112 ", "P113 ", "P114 ", "P115 ", "P116 ", "P117 ", "P118 ", "P119 ", |
|
|
|
|
|
|
|
"P120 ", "P121 ", "P122 ", "P123 ", "P124 ", "P125 ", "P126 ", "P127" |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -111,7 +171,10 @@ void SayUsage(char *programName){ |
|
|
|
"Display a MIDITONES bytestream", |
|
|
|
"Display a MIDITONES bytestream", |
|
|
|
"Usage: miditones_scroll <basefilename>", |
|
|
|
"Usage: miditones_scroll <basefilename>", |
|
|
|
" reads <basefilename>.bin", |
|
|
|
" reads <basefilename>.bin", |
|
|
|
"-c option creates an annotated C source file as <basefile>.c", |
|
|
|
" -tn displays up to n tone generators", |
|
|
|
|
|
|
|
" -v expects and displays volume information", |
|
|
|
|
|
|
|
" -vi expects and ignores volume information", |
|
|
|
|
|
|
|
" -c option creates an annotated C source file as <basefile>.c", |
|
|
|
"" }; |
|
|
|
"" }; |
|
|
|
int i=0; |
|
|
|
int i=0; |
|
|
|
while (usage[i][0] != '\0') fprintf(stderr, "%s\n", usage[i++]); |
|
|
|
while (usage[i][0] != '\0') fprintf(stderr, "%s\n", usage[i++]); |
|
|
@ -127,13 +190,24 @@ int HandleOptions(int argc,char *argv[]) { |
|
|
|
for (i=1; i< argc;i++) { |
|
|
|
for (i=1; i< argc;i++) { |
|
|
|
if (argv[i][0] == '/' || argv[i][0] == '-') { |
|
|
|
if (argv[i][0] == '/' || argv[i][0] == '-') { |
|
|
|
switch (toupper(argv[i][1])) { |
|
|
|
switch (toupper(argv[i][1])) { |
|
|
|
case 'C': |
|
|
|
|
|
|
|
codeoutput = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'H': |
|
|
|
case 'H': |
|
|
|
case '?': |
|
|
|
case '?': |
|
|
|
SayUsage(argv[0]); |
|
|
|
SayUsage(argv[0]); |
|
|
|
exit(1); |
|
|
|
exit(1); |
|
|
|
|
|
|
|
case 'C': |
|
|
|
|
|
|
|
codeoutput = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'T': |
|
|
|
|
|
|
|
if (sscanf(&argv[i][2],"%d",&num_tonegens) != 1 || num_tonegens <1 || num_tonegens > MAX_TONEGENS) goto opterror; |
|
|
|
|
|
|
|
printf("Displaying %d tone generators.\n", num_tonegens); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'V': |
|
|
|
|
|
|
|
expect_volume = true; |
|
|
|
|
|
|
|
if (argv[i][2] == '\0') break; |
|
|
|
|
|
|
|
if (toupper(argv[i][2]) == 'I') ignore_volume = true; |
|
|
|
|
|
|
|
else goto opterror; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
/* add more option switches here */ |
|
|
|
/* add more option switches here */ |
|
|
|
opterror: |
|
|
|
opterror: |
|
|
|
default: |
|
|
|
default: |
|
|
@ -150,50 +224,48 @@ opterror: |
|
|
|
return firstnonoption; |
|
|
|
return firstnonoption; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*************** portable string length *****************/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int strlength(const char *str) { |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
for (i=0; str[i] != '\0'; ++i) ; |
|
|
|
|
|
|
|
return i; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*************** safe string copy *****************/ |
|
|
|
/*************** safe string copy *****************/ |
|
|
|
|
|
|
|
|
|
|
|
size_t strlcpy(char *dst, const char *src, size_t siz) { |
|
|
|
unsigned int strlcpy(char *dst, const char *src, unsigned int siz) { |
|
|
|
char *d = dst; |
|
|
|
char *d = dst; |
|
|
|
const char *s = src; |
|
|
|
const char *s = src; |
|
|
|
size_t n = siz; |
|
|
|
unsigned int n = siz; |
|
|
|
/* Copy as many bytes as will fit */ |
|
|
|
/* Copy as many bytes as will fit */ |
|
|
|
if (n != 0) |
|
|
|
if (n != 0) { |
|
|
|
{ |
|
|
|
while (--n != 0) { |
|
|
|
while (--n != 0) |
|
|
|
if ((*d++ = *s++) == '\0') break; |
|
|
|
{ |
|
|
|
|
|
|
|
if ((*d++ = *s++) == '\0') |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
/* Not enough room in dst, add NUL and traverse rest of src */ |
|
|
|
/* Not enough room in dst, add NUL and traverse rest of src */ |
|
|
|
if (n == 0) |
|
|
|
if (n == 0) { |
|
|
|
{ |
|
|
|
if (siz != 0) *d = '\0'; /* NUL-terminate dst */ |
|
|
|
if (siz != 0) |
|
|
|
while (*s++) ; |
|
|
|
*d = '\0'; /* NUL-terminate dst */ |
|
|
|
|
|
|
|
while (*s++) |
|
|
|
|
|
|
|
; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return (s - src - 1); /* count does not include NUL */ |
|
|
|
return (s - src - 1); /* count does not include NUL */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*************** safe string concatenation *****************/ |
|
|
|
/*************** safe string concatenation *****************/ |
|
|
|
|
|
|
|
|
|
|
|
size_t strlcat(char *dst, const char *src, size_t siz) { |
|
|
|
unsigned int strlcat(char *dst, const char *src, unsigned int siz) { |
|
|
|
char *d = dst; |
|
|
|
char *d = dst; |
|
|
|
const char *s = src; |
|
|
|
const char *s = src; |
|
|
|
size_t n = siz; |
|
|
|
unsigned int n = siz; |
|
|
|
size_t dlen; |
|
|
|
unsigned int dlen; |
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */ |
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */ |
|
|
|
while (n-- != 0 && *d != '\0') |
|
|
|
while (n-- != 0 && *d != '\0') d++; |
|
|
|
d++; |
|
|
|
|
|
|
|
dlen = d - dst; |
|
|
|
dlen = d - dst; |
|
|
|
n = siz - dlen; |
|
|
|
n = siz - dlen; |
|
|
|
if (n == 0) |
|
|
|
if (n == 0) return (dlen + strlength(s)); |
|
|
|
return (dlen + strlen(s)); |
|
|
|
while (*s != '\0') { |
|
|
|
while (*s != '\0') |
|
|
|
if (n != 1) { |
|
|
|
{ |
|
|
|
|
|
|
|
if (n != 1) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
*d++ = *s; |
|
|
|
*d++ = *s; |
|
|
|
n--; |
|
|
|
n--; |
|
|
|
} |
|
|
|
} |
|
|
@ -208,7 +280,8 @@ size_t strlcat(char *dst, const char *src, size_t siz) { |
|
|
|
|
|
|
|
|
|
|
|
void file_error (char *msg, unsigned char *bufptr) { |
|
|
|
void file_error (char *msg, unsigned char *bufptr) { |
|
|
|
unsigned char *ptr; |
|
|
|
unsigned char *ptr; |
|
|
|
fprintf(stderr, "\n---> file format error at position %04X (%d): %s\n", bufptr-buffer, bufptr-buffer, msg); |
|
|
|
fprintf(stderr, "\n---> file format error at position %04X (%d): %s\n", |
|
|
|
|
|
|
|
(unsigned int)(bufptr-buffer), (unsigned int)(bufptr-buffer), msg); |
|
|
|
/* print some bytes surrounding the error */ |
|
|
|
/* print some bytes surrounding the error */ |
|
|
|
ptr = bufptr - 16; |
|
|
|
ptr = bufptr - 16; |
|
|
|
if (ptr < buffer) ptr = buffer; |
|
|
|
if (ptr < buffer) ptr = buffer; |
|
|
@ -226,15 +299,18 @@ void print_status(void) { |
|
|
|
// print the current timestamp
|
|
|
|
// print the current timestamp
|
|
|
|
fprintf (outfile, "%5d %7d.%03d ", delay, timenow/1000, timenow%1000); |
|
|
|
fprintf (outfile, "%5d %7d.%03d ", delay, timenow/1000, timenow%1000); |
|
|
|
// print the current status of all tone generators
|
|
|
|
// print the current status of all tone generators
|
|
|
|
for (gen=0; gen<MAX_TONEGENS; ++gen) { |
|
|
|
for (gen=0; gen<num_tonegens; ++gen) { |
|
|
|
fprintf (outfile, "%6s", gen_status[gen] == SILENT ? " " : notename[gen_status[gen]]); |
|
|
|
fprintf (outfile, "%6s", gen_note[gen] == SILENT ? " " : notename[gen_note[gen]]); |
|
|
|
|
|
|
|
if (expect_volume && !ignore_volume) if (gen_note[gen] == SILENT) fprintf (outfile, " "); |
|
|
|
|
|
|
|
else fprintf (outfile, " v%-3d", gen_volume[gen]); |
|
|
|
} |
|
|
|
} |
|
|
|
// display the hex commands that created these changes
|
|
|
|
// display the hex commands that created these changes
|
|
|
|
fprintf (outfile, " %04X: ", lastbufptr-buffer); // offset
|
|
|
|
fprintf (outfile, " %04X: ", (unsigned int)(lastbufptr-buffer)); // offset
|
|
|
|
if (codeoutput) fprintf (outfile, "*/ "); // end comment
|
|
|
|
if (codeoutput) fprintf (outfile, "*/ "); // end comment
|
|
|
|
for (; lastbufptr <= bufptr; ++lastbufptr) fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); |
|
|
|
for (; lastbufptr <= bufptr; ++lastbufptr) fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); |
|
|
|
fprintf (outfile, "\n"); |
|
|
|
fprintf (outfile, "\n"); |
|
|
|
lastbufptr = bufptr+1; |
|
|
|
lastbufptr = bufptr+1; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -256,8 +332,7 @@ int main(int argc,char *argv[]) { |
|
|
|
unsigned int tonegens_used; // bitmap of tone generators used
|
|
|
|
unsigned int tonegens_used; // bitmap of tone generators used
|
|
|
|
unsigned int num_tonegens_used; // count of tone generators used
|
|
|
|
unsigned int num_tonegens_used; // count of tone generators used
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
printf("MIDITONES_SCROLL V%s, (C) 2011,2016 Len Shustek\n", VERSION); |
|
|
|
printf("MIDITONES_SCROLL V%s, (C) 2011,2015 Len Shustek\n", VERSION); |
|
|
|
|
|
|
|
printf("See the source code for license information.\n\n"); |
|
|
|
printf("See the source code for license information.\n\n"); |
|
|
|
if (argc == 1) { /* no arguments */ |
|
|
|
if (argc == 1) { /* no arguments */ |
|
|
|
SayUsage(argv[0]); |
|
|
|
SayUsage(argv[0]); |
|
|
@ -313,20 +388,21 @@ int main(int argc,char *argv[]) { |
|
|
|
fprintf(outfile, "const byte PROGMEM score [] = {\n"); |
|
|
|
fprintf(outfile, "const byte PROGMEM score [] = {\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Process the commmands sequentially */ |
|
|
|
/* Do the titles */ |
|
|
|
|
|
|
|
|
|
|
|
fprintf(outfile, "\n"); |
|
|
|
fprintf(outfile, "\n"); |
|
|
|
if (codeoutput) fprintf(outfile, "//"); |
|
|
|
if (codeoutput) fprintf(outfile, "//"); |
|
|
|
fprintf(outfile, "duration time "); |
|
|
|
fprintf(outfile, "duration time "); |
|
|
|
for (i=0; i< MAX_TONEGENS; ++i) |
|
|
|
for (i=0; i< num_tonegens; ++i) |
|
|
|
fprintf(outfile, " gen%-2d", i); |
|
|
|
fprintf(outfile, expect_volume && !ignore_volume ? " gen%-5d" : " gen%-2d", i); |
|
|
|
fprintf(outfile," bytestream code\n\n"); |
|
|
|
fprintf(outfile," bytestream code\n\n"); |
|
|
|
|
|
|
|
for (gen=0; gen<num_tonegens; ++gen) |
|
|
|
for (gen=0; gen<MAX_TONEGENS; ++gen) |
|
|
|
gen_note[gen] = SILENT; |
|
|
|
gen_status[gen] = SILENT; |
|
|
|
|
|
|
|
tonegens_used = 0; |
|
|
|
tonegens_used = 0; |
|
|
|
lastbufptr = buffer; |
|
|
|
lastbufptr = buffer; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Process the commmands sequentially */ |
|
|
|
|
|
|
|
|
|
|
|
for (bufptr = buffer; bufptr < buffer+buflen; ++bufptr) { |
|
|
|
for (bufptr = buffer; bufptr < buffer+buflen; ++bufptr) { |
|
|
|
cmd = *bufptr; |
|
|
|
cmd = *bufptr; |
|
|
|
if (cmd < 0x80) { //***** delay
|
|
|
|
if (cmd < 0x80) { //***** delay
|
|
|
@ -334,25 +410,26 @@ int main(int argc,char *argv[]) { |
|
|
|
print_status(); // tone generator status now
|
|
|
|
print_status(); // tone generator status now
|
|
|
|
timenow += delay; // advance time
|
|
|
|
timenow += delay; // advance time
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else if (cmd != 0xf0) { // a note command
|
|
|
|
gen = cmd & 0x0f; |
|
|
|
gen = cmd & 0x0f; |
|
|
|
if (gen >= MAX_TONEGENS) file_error ("too many tone generators used", bufptr); |
|
|
|
if (gen > max_tonegen_found) max_tonegen_found = gen; |
|
|
|
cmd = cmd & 0xf0; |
|
|
|
cmd = cmd & 0xf0; |
|
|
|
if (cmd == 0x90) { //****** note on
|
|
|
|
if (cmd == 0x90) { //****** note on
|
|
|
|
gen_status[gen] = *++bufptr; // note number
|
|
|
|
gen_note[gen] = *++bufptr; // note number
|
|
|
|
if (gen_status[gen] > 127) file_error ("note higher than 127", bufptr); |
|
|
|
|
|
|
|
tonegens_used |= 1<<gen; // record that we used this generator at least once
|
|
|
|
tonegens_used |= 1<<gen; // record that we used this generator at least once
|
|
|
|
|
|
|
|
if (expect_volume) gen_volume[gen] = *++bufptr; // volume
|
|
|
|
|
|
|
|
if (gen >= num_tonegens) ++notes_skipped; // won't be displaying this note
|
|
|
|
} |
|
|
|
} |
|
|
|
else if (cmd == 0x80) { //****** note off
|
|
|
|
else if (cmd == 0x80) { //****** note off
|
|
|
|
if (gen_status[gen] == SILENT) file_error ("tone generator not on", bufptr); |
|
|
|
|
|
|
|
gen_status[gen] = SILENT; |
|
|
|
if (gen_note[gen] == SILENT) file_error ("tone generator not on", bufptr); |
|
|
|
} |
|
|
|
gen_note[gen] = SILENT; |
|
|
|
else if (cmd == 0xf0) { //****** end of score
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else file_error ("unknownn command", bufptr); |
|
|
|
else file_error ("unknown command", bufptr); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Do the final cleanup */ |
|
|
|
/* Do the final cleanup */ |
|
|
|
|
|
|
|
|
|
|
|
delay = 0; |
|
|
|
delay = 0; |
|
|
@ -365,5 +442,8 @@ int main(int argc,char *argv[]) { |
|
|
|
fprintf(outfile, "// This score contains %ld bytes, and %d tone generator%s used.\n", |
|
|
|
fprintf(outfile, "// This score contains %ld bytes, and %d tone generator%s used.\n", |
|
|
|
buflen, num_tonegens_used, num_tonegens_used == 1 ? " is" : "s are"); |
|
|
|
buflen, num_tonegens_used, num_tonegens_used == 1 ? " is" : "s are"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
printf("\nAt most %u tone generators were used.\n", max_tonegen_found+1); |
|
|
|
|
|
|
|
if (notes_skipped) printf("%u notes were not displayed because we were told to show only %u generators.\n", notes_skipped, num_tonegens); |
|
|
|
printf ("Done.\n"); |
|
|
|
printf ("Done.\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|