/* ******************************************************************** */ /* */ /* midi-test-gen.c Generates a test tune in ABC */ /* */ /* This C program writes an ABC melody to stdout, which can then be */ /* used with 'abc2midi' to create a MIDI file in which each instrument */ /* (numbers 0 to 131) plays its instrument-number as a binary sequence */ /* of eight tones: 0 == middle-C, 1 == A 21 semitones higher. Notes */ /* are provided as "lyrics" printed at the bottom of the last page, */ /* just in case anyone runs the file through any of the various ABC */ /* typesetting or viewing programs. (A little more info is provided in */ /* "history" fields, just to have a convenient place for a little more */ /* documentation in case the binary or its output find their ways out */ /* into the wild.) */ /* */ /* USAGE: */ /* cc -o midi-test-gen midi-test-gen.c */ /* ./midi-test-gen > miditest.abc */ /* abc2midi miditest.abc -o miditest.mid */ /* */ /* Then play the 'miditest.mid' file through whatever MIDI program or */ /* device you want to hear the instrument list of. */ /* */ /* (Yeah, I could have made it open a file to write to then exec */ /* abc2midi on that file, but I decided to spend that energy writing */ /* comments instead.) */ /* */ /* REVISION HISTORY: */ /* 2005-11-17 DGA Wrote first draft, and used it. */ /* 2005-11-18 DGA Added comments, added explanation to output. */ /* */ /* ******************************************************************** */ #include /* Make sure this ID string appears in the executable in case someone */ /* finds a copy of the binary and wonders where it came from. *shrug* */ /* Ya never know ... */ static char version_id[] = "$Id: midi-test-gen.c, v1.0, 2005-11-18, D. Glenn Arthur Jr., http://www.radix.net/~dglenn/code/midi-test-gen.c $"; int main( int argc , char **argv ) { int counter; /* Just what it sounds like. */ char melody[9]; /* String of eight note values, plus terminator. */ /* Really simple version & usage garbage. If there are any command */ /* line arguments, the user probably doesn't know what this is. So */ /* let's be helpful (and point them to my web site for good measure */ /* while we're at it). */ if ( argc > 1 ) if ( (strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "-V") == 0)) { printf("%s\n", version_id); exit(1); } else { printf("\nUsage:\n"); printf(" %s > [filename].abc\n", argv[0]); printf(" abc2midi [filename].abc -o [filename].mid\n"); printf("(then play the MIDI file).\n\n"); printf("For version info:\n"); printf(" %s -v\n", argv[0]); printf("For an explanation of what this is for:\n"); printf(" %s | grep '[WHN]:' | sed -e 's/^.://' | more\n\n", argv[0]); exit(1); } /* Okay, all done with the cutesy; on to the main event. */ /* Print the ABC tune header. */ printf("%% This file isn't actually meant to be typset. "); printf("See notes in W fields for info.\n"); printf("X:999\n"); printf("T:Midi Instrument Test\n"); printf("C:D. Glenn Arthur Jr.\n"); printf("Z:Automatically generated by midi-test-gen.c\n"); printf("H:This is the output of the midi-test-gen program \n"); printf("H:(at http://www.radix.net/~dglenn/code/midi-test-gen.c if you\n"); printf("H:want a peek at the code). It's in ABC notation -- if that\n"); printf("H:is unfamiliar, see http://abc.sourceforge.net/resources.html\n"); printf("H:for links to explanations, relevant software, and if you're\n"); printf("H:in the market for sheet music, a starting point for finding\n"); printf("H:the thousands of tunes available in this notation online.\n"); printf("H:\n"); printf("W:Each measure plays in the MIDI instrument corresponding\n"); printf("W:to the (zero-based) measure number, which is also encoded\n"); printf("W:in the note pattern for the measure. When listening to\n"); printf("W:the MIDI file generated from this, one can thus hear the\n"); printf("W:mapping of instrument sounds to instrument numbers, as long.\n"); printf("W:as one is halfway decent at counting in binary.\n"); printf("N:(If anyone feels like sending me a routine to output Morse\n"); printf("N:code instead, I'll make that a command-line option.)\n"); printf("W:At 70 beats per minute, it plays for about eleven minutes.\n"); printf("W:\n"); printf("W:This is one tune that should not need measure numbers\n"); printf("W:above the staff (it's not even really meant to be printed\n"); printf("W:out), but if you want 'em, I suggest typesetting this file\n"); printf("W:using the '-b0' option to abcm2ps so that the printed\n"); printf("W:measure numbers are zero-based as well.\n"); printf("W:\n"); printf("W:dglenn@radix.net\n"); printf("M:6/8\n"); printf("L:1/16\n"); printf("Q:1/8=70\n"); printf("K:C\n"); /* Initialize the melody string to CCCCCCCC (eight 16th-notes of */ /* middle-C) to represent 00000000(two). Add the null-terminator. */ for (counter=0 ; counter<8 ; counter++) melody[counter]='C'; melody[8]='\0'; /* Start counting from zero in binary ... Print the first value, the */ /* zero, outside the loop so that the loop is nothing more than "add */ /* one and carry", without my having to be awake enough to start at */ /* -1 and have the loop increment that to 0 on the first pass. */ counter=0; printf ("%%%%MIDI program %d\n %s z4 | \\\n", counter, melody); /* (If that pritnf() doesn't make sense, go read the ABC notation */ /* standard.) */ /* Now do the promised "add one and carry" loop from 1 to 131. Why */ /* 131? Because that way if someone does print out the sheet music, */ /* at either 4 or 6 measures per line, it will come out even on the */ /* page, and I wanted to go to at least 128 because the list I've */ /* got numbers 'em from 1 to 128 instead of 0 to 127, and that's */ /* part of what I was checking. (Silly of me perhaps, but having a */ /* lone measure sticking out at the end of the last page bothered me */ /* even if the output of this program isn't really inteded to ever */ /* be printed.) If your device has more than 128 instruments, just */ /* change it in the for() statement here. */ /* Do the loop the brute-force way because I was half asleep when I */ /* wrote it. (If the last byte of the melody is a 'C' -- "zero" -- */ /* set it to 'a' -- "one". If it was already an 'a', set it to 'C' */ /* and repeat the logic on the byte to the left. This should really */ /* just do eight shift-and-tests on the counter variable to fill the */ /* string, but as I mentioned, I was half asleep. */ for (counter=1 ; counter<132 ; counter++) { if (melody[7]=='C') melody[7]='a'; else { melody[7]='C'; if (melody[6]=='C') melody[6]='a'; else { melody[6]='C'; if (melody[5]=='C') melody[5]='a'; else { melody[5]='C'; if (melody[4]=='C') melody[4]='a'; else { melody[4]='C'; if (melody[3]=='C') melody[3]='a'; else { melody[3]='C'; if (melody[2]=='C') melody[2]='a'; else { melody[2]='C'; if (melody[1]=='C') melody[1]='a'; else { melody[1]='C'; melody[0]='a'; } } } } } } } /* Print the resulting string, preceeded by a MIDI instrument */ /* change command and followed by a rest and a bar line. */ printf ("%%%%MIDI program %d\n %s z4 | \\\n", counter, melody); } /* C'est tout. At this point I've spent about three times as long on */ /* the internal documentation as I did writing the code itself. */ }