Merge changes from miditones used in the Arduino/Teensy library (PaulStoffregen/Audio),

which includes a velocity output option
pull/5/head
Dean Blackketter 9 years ago
parent e0940116df
commit 8a9403e368
  1. 5
      Makefile
  2. 47
      miditones.c

@ -0,0 +1,5 @@
miditones: miditones.c
gcc -O2 -Wall -o miditones miditones.c
clean:
rm -f miditones

@ -5,7 +5,7 @@
* Convert a MIDI file into a bytestream of notes * Convert a MIDI file into a bytestream of notes
* *
* *
* (C) Copyright 2011,2013,2015, Len Shustek * (C) Copyright 2011,2013,2015,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
@ -52,9 +52,11 @@
* Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x. * Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x.
* 23 January 2016, D. Blackketter, V1.8 * 23 January 2016, D. Blackketter, V1.8
* -Fix warnings and errors building on Mac OS X via "gcc miditones.c" * -Fix warnings and errors building on Mac OS X via "gcc miditones.c"
* 25 January 2016, D. Blackketter, Paul Stoffregen, V1.9
-Merge in velocity output option from Arduino/Teensy Audio Library
*/ */
#define VERSION "1.8" #define VERSION "1.9"
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
@ -146,6 +148,8 @@
* -kn Change the musical key of the output by n chromatic notes. * -kn Change the musical key of the output by n chromatic notes.
* -k-12 goes one octave down, -k12 goes one octave up, etc. * -k-12 goes one octave down, -k12 goes one octave up, etc.
* *
* -v Add velocity information to output
*
* *
* ***** The score bytestream ***** * ***** The score bytestream *****
* *
@ -155,9 +159,10 @@
* *
* If the high-order bit of the byte is 1, then it is one of the following commands: * If the high-order bit of the byte is 1, then it is one of the following commands:
* *
* 9t nn Start playing note nn on tone generator t. Generators are numbered * 9t nn [vv] Start playing note nn on tone generator t. Generators are numbered
* starting with 0. The notes numbers are the MIDI numbers for the chromatic * starting with 0. The notes numbers are the MIDI numbers for the chromatic
* scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz). * scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz).
* if the -v option is enabled, a second byte is added to indicate velocity
* *
* 8t Stop playing the note on tone generator t. * 8t Stop playing the note on tone generator t.
* *
@ -213,7 +218,7 @@ struct track_header {
#define DEFAULT_TONEGENS 6 /* default number of tone generators */ #define DEFAULT_TONEGENS 6 /* default number of tone generators */
#define MAX_TRACKS 24 /* max number of MIDI tracks we will process */ #define MAX_TRACKS 24 /* max number of MIDI tracks we will process */
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput; bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, velocityoutput;
FILE *infile, *outfile, *logfile; FILE *infile, *outfile, *logfile;
uint8_t *buffer, *hdrptr; uint8_t *buffer, *hdrptr;
unsigned long buflen; unsigned long buflen;
@ -235,7 +240,7 @@ struct tonegen_status { /* current status of a tone generator */
int note; /* what note is playing? */ int note; /* what note is playing? */
} }
tonegen [MAX_TONEGENS] = { tonegen [MAX_TONEGENS] = {
0}; {0}};
struct track_status { /* current processing point of a MIDI track */ struct track_status { /* current processing point of a MIDI track */
uint8_t *trkptr; /* ptr to the next note change */ uint8_t *trkptr; /* ptr to the next note change */
@ -245,11 +250,12 @@ struct track_status { /* current processing point of a MIDI track */
unsigned int preferred_tonegen; /* for strategy2: try to use this generator */ unsigned int preferred_tonegen; /* for strategy2: try to use this generator */
unsigned char cmd; /* CMD_xxxx next to do */ unsigned char cmd; /* CMD_xxxx next to do */
unsigned char note; /* for which note */ unsigned char note; /* for which note */
unsigned char velocity;
unsigned char last_event; /* the last event, for MIDI's "running status" */ unsigned char last_event; /* the last event, for MIDI's "running status" */
bool tonegens[MAX_TONEGENS];/* which tone generators our notes are playing on */ bool tonegens[MAX_TONEGENS];/* which tone generators our notes are playing on */
} }
track[MAX_TRACKS] = { track[MAX_TRACKS] = {
0}; {0}};
/* output bytestream commands, which are also stored in track_status.cmd */ /* output bytestream commands, which are also stored in track_status.cmd */
@ -271,7 +277,7 @@ track[MAX_TRACKS] = {
void SayUsage(char *programName){ void SayUsage(char *programName){
static char *usage[] = { static char *usage[] = {
"Convert MIDI files to an Arduino PLAYTUNE bytestream", "Convert MIDI files to an Arduino PLAYTUNE bytestream",
"miditones [-p] [-lg] [-lp] [-s1] [-tn] <basefilename>", "miditones [-p] [-lg] [-lp] [-s1] [-tn] [-v] <basefilename>",
" -p parse only, don't generate bytestream", " -p parse only, don't generate bytestream",
" -lp log input parsing", " -lp log input parsing",
" -lg log output generation", " -lg log output generation",
@ -281,6 +287,7 @@ void SayUsage(char *programName){
" -b binary file output instead of C source text", " -b binary file output instead of C source text",
" -cn mask for which tracks to process, e.g. -c3 for only 0 and 1", " -cn mask for which tracks to process, e.g. -c3 for only 0 and 1",
" -kn key shift in chromatic notes, positive or negative", " -kn key shift in chromatic notes, positive or negative",
" -v include velocity data in play note commands",
"input file: <basefilename>.mid", "input file: <basefilename>.mid",
"output file: <basefilename>.bin or .c", "output file: <basefilename>.bin or .c",
"log file: <basefilename>.log", "log file: <basefilename>.log",
@ -314,6 +321,9 @@ int HandleOptions(int argc,char *argv[]) {
case 'B': case 'B':
binaryoutput = true; binaryoutput = true;
break; break;
case 'V':
velocityoutput = true;
break;
case 'S': case 'S':
if (argv[i][2] == '1') strategy1 = true; if (argv[i][2] == '1') strategy1 = true;
else if (argv[i][2] == '2') strategy2 = true; else if (argv[i][2] == '2') strategy2 = true;
@ -413,7 +423,7 @@ size_t miditones_strlcat(char *dst, const char *src, size_t siz) {
/* match a constant character sequence */ /* match a constant character sequence */
int charcmp (char *buf, char *match) { int charcmp (const char *buf, const char *match) {
int len, i; int len, i;
len = strlen (match); len = strlen (match);
for (i=0; i<len; ++i) for (i=0; i<len; ++i)
@ -501,7 +511,7 @@ void start_track (int tracknum) {
chk_bufdata(hdrptr, sizeof(struct track_header)); chk_bufdata(hdrptr, sizeof(struct track_header));
hdr = (struct track_header *) hdrptr; hdr = (struct track_header *) hdrptr;
if (!charcmp((char*)hdr->MTrk,"MTrk")) midi_error("Missing 'MTrk'", hdrptr); if (!charcmp((char *)(hdr->MTrk),"MTrk")) midi_error("Missing 'MTrk'", hdrptr);
tracklen = rev_long(hdr->track_size); tracklen = rev_long(hdr->track_size);
if (logparse) fprintf (logfile, "\nTrack %d length %ld\n", tracknum, tracklen); if (logparse) fprintf (logfile, "\nTrack %d length %ld\n", tracknum, tracklen);
@ -640,6 +650,7 @@ note_off:
t->note = *t->trkptr++; t->note = *t->trkptr++;
velocity = *t->trkptr++; velocity = *t->trkptr++;
if (velocity == 0) /* some scores use note-on with zero velocity for off! */ goto note_off; if (velocity == 0) /* some scores use note-on with zero velocity for off! */ goto note_off;
t->velocity = velocity;
if (logparse) fprintf (logfile, "note %02X on, chan %d, velocity %d\n", t->note, chan, velocity); if (logparse) fprintf (logfile, "note %02X on, chan %d, velocity %d\n", t->note, chan, velocity);
if ((1<<chan) & channel_mask) { // if we're processing this channel if ((1<<chan) & channel_mask) { // if we're processing this channel
t->cmd = CMD_PLAYNOTE; t->cmd = CMD_PLAYNOTE;
@ -697,7 +708,7 @@ int main(int argc,char *argv[]) {
unsigned long earliest_time; unsigned long earliest_time;
int notes_skipped = 0; int notes_skipped = 0;
printf("MIDITONES V%s, (C) 2011,2015 Len Shustek\n", VERSION); printf("MIDITONES V%s, (C) 2011,2015,2016 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]);
@ -773,7 +784,12 @@ int main(int argc,char *argv[]) {
fprintf(outfile, "// Only the masked channels were processed: %04X\n", channel_mask); fprintf(outfile, "// Only the masked channels were processed: %04X\n", channel_mask);
if (keyshift != 0) if (keyshift != 0)
fprintf(outfile, "// Keyshift was %d chromatic notes\n", keyshift); fprintf(outfile, "// Keyshift was %d chromatic notes\n", keyshift);
fprintf(outfile, "const byte PROGMEM score [] = {\n"); fprintf(outfile, "#ifdef __AVR__\n");
fprintf(outfile, "#include <avr/pgmspace.h>\n");
fprintf(outfile, "#else\n");
fprintf(outfile, "#define PROGMEM\n");
fprintf(outfile, "#endif\n");
fprintf(outfile, "const unsigned char PROGMEM score [] = {\n");
} }
} }
@ -932,10 +948,19 @@ int main(int argc,char *argv[]) {
putc (CMD_PLAYNOTE | tgnum, outfile); putc (CMD_PLAYNOTE | tgnum, outfile);
putc (shifted_note, outfile); putc (shifted_note, outfile);
outfile_bytecount += 2; outfile_bytecount += 2;
if (velocityoutput) {
putc (shifted_note, outfile);
outfile_bytecount++;
}
} }
else { else {
if (velocityoutput == 0) {
fprintf (outfile, "0x%02X,%d, ", CMD_PLAYNOTE | tgnum, shifted_note); fprintf (outfile, "0x%02X,%d, ", CMD_PLAYNOTE | tgnum, shifted_note);
outfile_items(2); outfile_items(2);
} else {
fprintf (outfile, "0x%02X,%d,%d, ", CMD_PLAYNOTE | tgnum, shifted_note, trk->velocity);
outfile_items(3);
}
} }
} }
else { else {

Loading…
Cancel
Save