|
|
@ -1,4 +1,4 @@ |
|
|
|
/*********************************************************************************
|
|
|
|
/***************************************************************************************
|
|
|
|
* |
|
|
|
* |
|
|
|
* MIDITONES_SCROLL |
|
|
|
* MIDITONES_SCROLL |
|
|
|
* |
|
|
|
* |
|
|
@ -50,27 +50,36 @@ |
|
|
|
* -vi says that we expect volume information to be in the file, but we |
|
|
|
* -vi says that we expect volume information to be in the file, but we |
|
|
|
* should ignore it when creating the display. |
|
|
|
* should ignore it when creating the display. |
|
|
|
* |
|
|
|
* |
|
|
|
|
|
|
|
* -ii says we should not display instrument information we find |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* For source code to this and related programs, see |
|
|
|
* For source code to this and related programs, see |
|
|
|
* https://github.com/LenShustek/arduino-playtune
|
|
|
|
* www.github.com/LenShustek/miditones |
|
|
|
* https://github.com/LenShustek/miditones
|
|
|
|
* www.github.com/LenShustek/arduino-playtune |
|
|
|
* |
|
|
|
* |
|
|
|
*---------------------------------------------------------------------------------- |
|
|
|
*---------------------------------------------------------------------------------------- |
|
|
|
* (C) Copyright 2011,2013,2016 Len Shustek |
|
|
|
* The MIT License (MIT) |
|
|
|
|
|
|
|
* Copyright (c) 2011,2013,2015,2016, Len Shustek |
|
|
|
* |
|
|
|
* |
|
|
|
* This program is free software. You can redistribute it and/or modify |
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|
|
* it under the terms of version 3 of the GNU General Public License as |
|
|
|
* of this software and associated documentation files (the "Software"), to deal |
|
|
|
* published by the Free Software Foundation at www.gnu.org/licenses, |
|
|
|
* in the Software without restriction, including without limitation the rights |
|
|
|
* with Additional Permissions under term 7(b) that the original copyright |
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
|
|
* notice and author attibution must be preserved and under term 7(c) that |
|
|
|
* copies of the Software, and to permit persons to whom the Software is |
|
|
|
* modified versions be marked as different from the original. |
|
|
|
* furnished to do so, subject to the following conditions: |
|
|
|
* |
|
|
|
* |
|
|
|
* This program is distributed in the hope that it will be useful, |
|
|
|
* The above copyright notice and this permission notice shall be included in all |
|
|
|
* but WITHOUT ANY WARRANTY,without even the implied warranty of |
|
|
|
* copies or substantial portions of the Software. |
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
|
|
|
|
* GNU General Public License for more details. |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
***********************************************************************************/ |
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
|
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
|
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
|
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|
|
|
|
|
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR |
|
|
|
|
|
|
|
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
|
|
|
|
******************************************************************************************/ |
|
|
|
|
|
|
|
// formatted with: indent miditones_scroll.c -br -brf -brs -ce -npsl -nut -i3 -l100 -lc100
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Change log |
|
|
|
* Change log |
|
|
|
* |
|
|
|
* |
|
|
@ -80,7 +89,7 @@ |
|
|
|
* - 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. |
|
|
|
* 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, |
|
|
|
* and fix 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" 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 |
|
|
|
* 5 August 2016, L. Shustek, V1.4 |
|
|
@ -90,11 +99,14 @@ |
|
|
|
* - Make the number of generators be 16 maximum, but the number actually displayed |
|
|
|
* - Make the number of generators be 16 maximum, but the number actually displayed |
|
|
|
* can be specified by the -tn command-line option. |
|
|
|
* can be specified by the -tn command-line option. |
|
|
|
* - Remove string.h because OS X has macros for strlcpy; thus had to add strlen(). |
|
|
|
* - 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!) |
|
|
|
* It's tough to write even non-GUI code for many environments! |
|
|
|
* - Add casts for where address subtraction is a long, like OS X. |
|
|
|
* - Add casts for where address subtraction is a long, like OS X. |
|
|
|
|
|
|
|
* 6 August 2016, L. Shustek, V1.5 |
|
|
|
|
|
|
|
* - Handle optional instrument change information. |
|
|
|
|
|
|
|
* - Look for the optional self-describing file header. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#define VERSION "1.4" |
|
|
|
#define VERSION "1.5" |
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
@ -108,9 +120,12 @@ |
|
|
|
|
|
|
|
|
|
|
|
#define MAX_TONEGENS 16 /* max tone generators we could display */ |
|
|
|
#define MAX_TONEGENS 16 /* max tone generators we could display */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define SILENT -1 /* "silent note" code */ |
|
|
|
|
|
|
|
|
|
|
|
int gen_note[MAX_TONEGENS]; // the note we're playing, or SILENT
|
|
|
|
int gen_note[MAX_TONEGENS]; // the note we're playing, or SILENT
|
|
|
|
#define SILENT -1 |
|
|
|
|
|
|
|
int gen_volume[MAX_TONEGENS]; // the volume we're playing
|
|
|
|
int gen_volume[MAX_TONEGENS]; // the volume we're playing
|
|
|
|
|
|
|
|
int gen_instrument[MAX_TONEGENS]; // the instrument we're playing
|
|
|
|
|
|
|
|
bool gen_instrument_changed[MAX_TONEGENS]; |
|
|
|
|
|
|
|
|
|
|
|
FILE *infile, *outfile; |
|
|
|
FILE *infile, *outfile; |
|
|
|
unsigned char *buffer, *bufptr; |
|
|
|
unsigned char *buffer, *bufptr; |
|
|
@ -126,8 +141,22 @@ unsigned delay; |
|
|
|
bool codeoutput = false; |
|
|
|
bool codeoutput = false; |
|
|
|
bool expect_volume = false; |
|
|
|
bool expect_volume = false; |
|
|
|
bool ignore_volume = false; |
|
|
|
bool ignore_volume = false; |
|
|
|
|
|
|
|
bool ignore_instruments = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct file_hdr_t { /* what the optional file header looks like */ |
|
|
|
|
|
|
|
char id1; // 'P'
|
|
|
|
|
|
|
|
char id2; // 't'
|
|
|
|
|
|
|
|
unsigned char hdr_length; // length of whole file header
|
|
|
|
|
|
|
|
unsigned char f1; // flag byte 1
|
|
|
|
|
|
|
|
unsigned char f2; // flag byte 2
|
|
|
|
|
|
|
|
unsigned char num_tgens; // how many tone generators are used by this score
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
#define HDR_F1_VOLUME_PRESENT 0x80 |
|
|
|
|
|
|
|
#define HDR_F1_INSTRUMENTS_PRESENT 0x40 |
|
|
|
|
|
|
|
#define HDR_F1_PERCUSSION_PRESENT 0x20 |
|
|
|
|
|
|
|
|
|
|
|
static char *notename[256] = { |
|
|
|
|
|
|
|
|
|
|
|
static char *notename[256] = { /* maximum 5 characters */ |
|
|
|
|
|
|
|
|
|
|
|
// map from MIDI note number to octave and note name,
|
|
|
|
// map from MIDI note number to octave and note name,
|
|
|
|
|
|
|
|
|
|
|
@ -163,6 +192,25 @@ static char *notename[256] = { |
|
|
|
"P120 ", "P121 ", "P122 ", "P123 ", "P124 ", "P125 ", "P126 ", "P127" |
|
|
|
"P120 ", "P121 ", "P122 ", "P123 ", "P124 ", "P125 ", "P126 ", "P127" |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *instrumentname[128] = { /* maximum 6 characters */ |
|
|
|
|
|
|
|
"APiano", "BPiano", "EPiano", "HPiano", "E1Pian", "E2Pian", "Harpsi", "Clavic", |
|
|
|
|
|
|
|
"Celest", "Glockn", "MusBox", "Vibrap", "Marimb", "Xyloph", "TubBel", "Dulcim", |
|
|
|
|
|
|
|
"DOrgan", "POrgan", "ROrgan", "COrgan", "dOrgan", "Accord", "Harmon", "TAccor", |
|
|
|
|
|
|
|
"NyGuit", "StGuit", "JzGuit", "ClGuit", "MuGuit", "OvGuit", "DsGuit", "HaGuit", |
|
|
|
|
|
|
|
"AcBass", "FiBass", "PiBass", "FrBass", "S1Bass", "S2Bass", "y1Bass", "y2Bass", |
|
|
|
|
|
|
|
"Violin", "Viola ", "Cello ", "CnBass", "TrStng", "PzStng", "OrHarp", "Timpan", |
|
|
|
|
|
|
|
"S1Ensb", "S1Ensb", "y1Strg", "y2Strg", "ChAhhs", "VcOohs", "SyVoic", "OrcHit", |
|
|
|
|
|
|
|
"Trumpt", "Trombn", "Tuba ", "MuTrum", "FrHorn", "Brass ", "y1Bras", "y2Bras", |
|
|
|
|
|
|
|
"SopSax", "AltSax", "TenSax", "BarSax", "Oboe ", "EnHorn", "Basson", "Clarin", |
|
|
|
|
|
|
|
"Piccol", "Flute ", "Record", "PFlute", "BlBotl", "Shakuh", "Whistl", "Ocarin", |
|
|
|
|
|
|
|
"Square", "Sawtoo", "Callip", "Chiff ", "Charag", "Voice ", "Fifths", "BassLd", |
|
|
|
|
|
|
|
"Pad1 ", "Pad2 ", "Pad3 ", "Pad4 ", "Pad5 ", "Pad6 ", "Pad7 ", "Pad 8 ", |
|
|
|
|
|
|
|
"FX1 ", "FX2 ", "FX3 ", "FX4 ", "FX5 ", "FX6 ", "FX7 ", "FX8 ", |
|
|
|
|
|
|
|
"Sitar ", "Banjo ", "Shamis", "Koto ", "Kalimb", "Bagpip", "Fiddle", "Shanai", |
|
|
|
|
|
|
|
"TnkBel", "Agogo ", "StDrum", "WdBlok", "TaiDrm", "MelTom", "SynDrm", "RevCym", |
|
|
|
|
|
|
|
"GuitFr", "Breath", "Seashr", "BirdTw", "Phone ", "Copter", "Claps ", "Guns " |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************** command-line processing *******************/ |
|
|
|
/************** command-line processing *******************/ |
|
|
|
|
|
|
|
|
|
|
@ -175,9 +223,11 @@ void SayUsage(char *programName){ |
|
|
|
" -v expects and displays volume information", |
|
|
|
" -v expects and displays volume information", |
|
|
|
" -vi expects and ignores volume information", |
|
|
|
" -vi expects and ignores volume information", |
|
|
|
" -c option creates an annotated C source file as <basefile>.c", |
|
|
|
" -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++]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int HandleOptions (int argc, char *argv[]) { |
|
|
|
int HandleOptions (int argc, char *argv[]) { |
|
|
@ -198,14 +248,19 @@ int HandleOptions(int argc,char *argv[]) { |
|
|
|
codeoutput = true; |
|
|
|
codeoutput = true; |
|
|
|
break; |
|
|
|
break; |
|
|
|
case 'T': |
|
|
|
case 'T': |
|
|
|
if (sscanf(&argv[i][2],"%d",&num_tonegens) != 1 || num_tonegens <1 || num_tonegens > MAX_TONEGENS) goto opterror; |
|
|
|
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); |
|
|
|
printf ("Displaying %d tone generators.\n", num_tonegens); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case 'V': |
|
|
|
case 'V': |
|
|
|
expect_volume = true; |
|
|
|
expect_volume = true; |
|
|
|
if (argv[i][2] == '\0') break; |
|
|
|
if (argv[i][2] == '\0') |
|
|
|
if (toupper(argv[i][2]) == 'I') ignore_volume = true; |
|
|
|
break; |
|
|
|
else goto opterror; |
|
|
|
if (toupper (argv[i][2]) == 'I') |
|
|
|
|
|
|
|
ignore_volume = true; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
goto opterror; |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
/* add more option switches here */ |
|
|
|
/* add more option switches here */ |
|
|
@ -215,8 +270,7 @@ opterror: |
|
|
|
SayUsage (argv[0]); |
|
|
|
SayUsage (argv[0]); |
|
|
|
exit (4); |
|
|
|
exit (4); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
else { |
|
|
|
|
|
|
|
firstnonoption = i; |
|
|
|
firstnonoption = i; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -241,12 +295,14 @@ unsigned int strlcpy(char *dst, const char *src, unsigned int 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) |
|
|
|
|
|
|
|
*d = '\0'; /* NUL-terminate dst */ |
|
|
|
while (*s++); |
|
|
|
while (*s++); |
|
|
|
} |
|
|
|
} |
|
|
|
return (s - src - 1); /* count does not include NUL */ |
|
|
|
return (s - src - 1); /* count does not include NUL */ |
|
|
@ -260,10 +316,12 @@ unsigned int strlcat(char *dst, const char *src, unsigned int siz) { |
|
|
|
unsigned int n = siz; |
|
|
|
unsigned int n = siz; |
|
|
|
unsigned int 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') d++; |
|
|
|
while (n-- != 0 && *d != '\0') |
|
|
|
|
|
|
|
d++; |
|
|
|
dlen = d - dst; |
|
|
|
dlen = d - dst; |
|
|
|
n = siz - dlen; |
|
|
|
n = siz - dlen; |
|
|
|
if (n == 0) return (dlen + strlength(s)); |
|
|
|
if (n == 0) |
|
|
|
|
|
|
|
return (dlen + strlength (s)); |
|
|
|
while (*s != '\0') { |
|
|
|
while (*s != '\0') { |
|
|
|
if (n != 1) { |
|
|
|
if (n != 1) { |
|
|
|
*d++ = *s; |
|
|
|
*d++ = *s; |
|
|
@ -284,8 +342,10 @@ void file_error (char *msg, unsigned char *bufptr) { |
|
|
|
(unsigned int) (bufptr - buffer), (unsigned int) (bufptr - buffer), msg); |
|
|
|
(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) |
|
|
|
for (; ptr <= bufptr+16 && ptr < buffer+buflen; ++ptr) fprintf (stderr, ptr==bufptr ? " [%02X] ":"%02X ", *ptr); |
|
|
|
ptr = buffer; |
|
|
|
|
|
|
|
for (; ptr <= bufptr + 16 && ptr < buffer + buflen; ++ptr) |
|
|
|
|
|
|
|
fprintf (stderr, ptr == bufptr ? " [%02X] " : "%02X ", *ptr); |
|
|
|
fprintf (stderr, "\n"); |
|
|
|
fprintf (stderr, "\n"); |
|
|
|
exit (8); |
|
|
|
exit (8); |
|
|
|
} |
|
|
|
} |
|
|
@ -295,25 +355,50 @@ void file_error (char *msg, unsigned char *bufptr) { |
|
|
|
// show the current time, status of all the tone generators, and the bytestream data that got us here
|
|
|
|
// show the current time, status of all the tone generators, and the bytestream data that got us here
|
|
|
|
|
|
|
|
|
|
|
|
void print_status (void) { |
|
|
|
void print_status (void) { |
|
|
|
if (codeoutput) fprintf (outfile, "/*"); // start comment
|
|
|
|
int gen; |
|
|
|
|
|
|
|
bool any_instr_changed = false; |
|
|
|
|
|
|
|
for (gen = 0; gen < num_tonegens; ++gen) |
|
|
|
|
|
|
|
any_instr_changed |= gen_instrument_changed[gen]; |
|
|
|
|
|
|
|
if (any_instr_changed) { |
|
|
|
|
|
|
|
if (codeoutput) |
|
|
|
|
|
|
|
fprintf (outfile, "//"); |
|
|
|
|
|
|
|
fprintf (outfile, "%21s", ""); |
|
|
|
|
|
|
|
for (gen = 0; gen < num_tonegens; ++gen) { |
|
|
|
|
|
|
|
if (gen_instrument_changed[gen]) { |
|
|
|
|
|
|
|
gen_instrument_changed[gen] = false; |
|
|
|
|
|
|
|
fprintf (outfile, "%6s", instrumentname[gen_instrument[gen]]); |
|
|
|
|
|
|
|
} else |
|
|
|
|
|
|
|
fprintf (outfile, "%6s", ""); |
|
|
|
|
|
|
|
if (expect_volume && !ignore_volume) |
|
|
|
|
|
|
|
fprintf (outfile, "%5s", ""); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fprintf (outfile, "\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (codeoutput) |
|
|
|
|
|
|
|
fprintf (outfile, "/*"); // start comment
|
|
|
|
// 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 < num_tonegens; ++gen) { |
|
|
|
for (gen = 0; gen < num_tonegens; ++gen) { |
|
|
|
fprintf (outfile, "%6s", gen_note[gen] == SILENT ? " " : notename[gen_note[gen]]); |
|
|
|
fprintf (outfile, "%6s", gen_note[gen] == SILENT ? " " : notename[gen_note[gen]]); |
|
|
|
if (expect_volume && !ignore_volume) if (gen_note[gen] == SILENT) fprintf (outfile, " "); |
|
|
|
if (expect_volume && !ignore_volume) |
|
|
|
else fprintf (outfile, " v%-3d", gen_volume[gen]); |
|
|
|
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: ", (unsigned int) (lastbufptr - buffer)); // offset
|
|
|
|
fprintf (outfile, " %04X: ", (unsigned int) (lastbufptr - buffer)); // offset
|
|
|
|
if (codeoutput) fprintf (outfile, "*/ "); // end comment
|
|
|
|
if (codeoutput) |
|
|
|
for (; lastbufptr <= bufptr; ++lastbufptr) fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); |
|
|
|
fprintf (outfile, "*/ "); // end comment
|
|
|
|
|
|
|
|
for (; lastbufptr <= bufptr; ++lastbufptr) |
|
|
|
|
|
|
|
fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); |
|
|
|
fprintf (outfile, "\n"); |
|
|
|
fprintf (outfile, "\n"); |
|
|
|
lastbufptr = bufptr + 1; |
|
|
|
lastbufptr = bufptr + 1; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int countbits (unsigned int bitmap) { |
|
|
|
int countbits (unsigned int bitmap) { |
|
|
|
int count; |
|
|
|
int count; |
|
|
|
for (count = 0; bitmap; bitmap >>= 1) |
|
|
|
for (count = 0; bitmap; bitmap >>= 1) |
|
|
@ -333,7 +418,6 @@ int main(int argc,char *argv[]) { |
|
|
|
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,2016 Len Shustek\n", VERSION); |
|
|
|
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]); |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
@ -364,8 +448,8 @@ int main(int argc,char *argv[]) { |
|
|
|
fprintf (stderr, "Unable to open output file %s", filename); |
|
|
|
fprintf (stderr, "Unable to open output file %s", filename); |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else |
|
|
|
else outfile = stdout; |
|
|
|
outfile = stdout; |
|
|
|
|
|
|
|
|
|
|
|
/* Read the whole input file into memory */ |
|
|
|
/* Read the whole input file into memory */ |
|
|
|
|
|
|
|
|
|
|
@ -379,19 +463,38 @@ int main(int argc,char *argv[]) { |
|
|
|
} |
|
|
|
} |
|
|
|
fread (buffer, buflen, 1, infile); |
|
|
|
fread (buffer, buflen, 1, infile); |
|
|
|
fclose (infile); |
|
|
|
fclose (infile); |
|
|
|
printf("Processing %s.bin, %ld bytes\n", filebasename, buflen); |
|
|
|
printf ("Processing %s.bin, %ld bytes.\n", filebasename, buflen); |
|
|
|
if (codeoutput) { |
|
|
|
if (codeoutput) { |
|
|
|
time_t rawtime; |
|
|
|
time_t rawtime; |
|
|
|
time (&rawtime); |
|
|
|
time (&rawtime); |
|
|
|
fprintf (outfile, "// Playtune bytestream for file \"%s.bin\"", filebasename); |
|
|
|
fprintf (outfile, "// Playtune bytestream for file \"%s.bin\"", filebasename); |
|
|
|
fprintf(outfile, " created by MIDITONES_SCROLL V%s on %s\n", VERSION, asctime(localtime(&rawtime))); |
|
|
|
fprintf (outfile, " created by MIDITONES_SCROLL V%s on %s\n", VERSION, |
|
|
|
|
|
|
|
asctime (localtime (&rawtime))); |
|
|
|
fprintf (outfile, "const byte PROGMEM score [] = {\n"); |
|
|
|
fprintf (outfile, "const byte PROGMEM score [] = {\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check for the optional self-describing file header */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bufptr = buffer; |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
struct file_hdr_t *hdrptr = (struct file_hdr_t *) buffer; |
|
|
|
|
|
|
|
if (buflen > sizeof (struct file_hdr_t) && hdrptr->id1 == 'P' && hdrptr->id2 == 't') { |
|
|
|
|
|
|
|
printf ("Found Pt self-describing file header with flags %02X %02X, # tone gens = %d\n", |
|
|
|
|
|
|
|
hdrptr->f1, hdrptr->f2, hdrptr->num_tgens); |
|
|
|
|
|
|
|
expect_volume = hdrptr->f1 & HDR_F1_VOLUME_PRESENT; |
|
|
|
|
|
|
|
bufptr += hdrptr->hdr_length; |
|
|
|
|
|
|
|
if (codeoutput) { |
|
|
|
|
|
|
|
fprintf (outfile, "'P','t', 6, 0x%02X, 0x%02X, %2d, // (Playtune file header)\n", |
|
|
|
|
|
|
|
hdrptr->f1, hdrptr->f2, hdrptr->num_tgens); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Do the titles */ |
|
|
|
/* 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 < num_tonegens; ++i) |
|
|
|
for (i = 0; i < num_tonegens; ++i) |
|
|
|
fprintf (outfile, expect_volume && !ignore_volume ? " gen%-5d" : " gen%-2d", i); |
|
|
|
fprintf (outfile, expect_volume && !ignore_volume ? " gen%-5d" : " gen%-2d", i); |
|
|
@ -403,29 +506,33 @@ int main(int argc,char *argv[]) { |
|
|
|
|
|
|
|
|
|
|
|
/* Process the commmands sequentially */ |
|
|
|
/* Process the commmands sequentially */ |
|
|
|
|
|
|
|
|
|
|
|
for (bufptr = buffer; bufptr < buffer+buflen; ++bufptr) { |
|
|
|
for (; bufptr < buffer + buflen; ++bufptr) { |
|
|
|
cmd = *bufptr; |
|
|
|
cmd = *bufptr; |
|
|
|
if (cmd < 0x80) { //***** delay
|
|
|
|
if (cmd < 0x80) { /* delay */ |
|
|
|
delay = ((unsigned int) cmd << 8) + *++bufptr; |
|
|
|
delay = ((unsigned int) cmd << 8) + *++bufptr; |
|
|
|
print_status (); // tone generator status now
|
|
|
|
print_status (); // tone generator status now
|
|
|
|
timenow += delay; // advance time
|
|
|
|
timenow += delay; // advance time
|
|
|
|
} |
|
|
|
} else if (cmd != 0xf0) { /* a command */ |
|
|
|
else if (cmd != 0xf0) { // a note command
|
|
|
|
|
|
|
|
gen = cmd & 0x0f; |
|
|
|
gen = cmd & 0x0f; |
|
|
|
if (gen > max_tonegen_found) max_tonegen_found = gen; |
|
|
|
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_note[gen] = *++bufptr; // note number
|
|
|
|
gen_note[gen] = *++bufptr; // note number
|
|
|
|
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 (expect_volume) |
|
|
|
if (gen >= num_tonegens) ++notes_skipped; // won't be displaying this note
|
|
|
|
gen_volume[gen] = *++bufptr; // volume
|
|
|
|
} |
|
|
|
if (gen >= num_tonegens) |
|
|
|
else if (cmd == 0x80) { //****** note off
|
|
|
|
++notes_skipped; // won't be displaying this note
|
|
|
|
|
|
|
|
} else if (cmd == 0x80) { /* note off */ |
|
|
|
if (gen_note[gen] == SILENT) file_error ("tone generator not on", bufptr); |
|
|
|
if (gen_note[gen] == SILENT) |
|
|
|
|
|
|
|
file_error ("tone generator not on", bufptr); |
|
|
|
gen_note[gen] = SILENT; |
|
|
|
gen_note[gen] = SILENT; |
|
|
|
} |
|
|
|
} else if (cmd == 0xc0) { /* change instrument */ |
|
|
|
else file_error ("unknown command", bufptr); |
|
|
|
gen_instrument[gen] = *++bufptr & 0x7f; |
|
|
|
|
|
|
|
gen_instrument_changed[gen] = true; |
|
|
|
|
|
|
|
} else |
|
|
|
|
|
|
|
file_error ("unknown command", bufptr); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -434,16 +541,19 @@ int main(int argc,char *argv[]) { |
|
|
|
|
|
|
|
|
|
|
|
delay = 0; |
|
|
|
delay = 0; |
|
|
|
--bufptr; |
|
|
|
--bufptr; |
|
|
|
if (codeoutput) --bufptr; //don't do 0xF0 for code, because we don't want the trailing comma
|
|
|
|
if (codeoutput) |
|
|
|
|
|
|
|
--bufptr; //don't do 0xf0 for code, because we don't want the trailing comma
|
|
|
|
print_status (); // print final status
|
|
|
|
print_status (); // print final status
|
|
|
|
if (codeoutput) { |
|
|
|
if (codeoutput) { |
|
|
|
fprintf (outfile, " 0xf0};\n"); |
|
|
|
fprintf (outfile, " 0xf0};\n"); |
|
|
|
num_tonegens_used = countbits (tonegens_used); |
|
|
|
num_tonegens_used = countbits (tonegens_used); |
|
|
|
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"); |
|
|
|
} |
|
|
|
} else |
|
|
|
printf("\nAt most %u tone generators were used.\n", max_tonegen_found+1); |
|
|
|
fprintf (outfile, "\n"); |
|
|
|
if (notes_skipped) printf("%u notes were not displayed because we were told to show only %u generators.\n", notes_skipped, num_tonegens); |
|
|
|
printf ("At 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"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|