copyright 2001, D. Glenn Arthur Jr.
[What's new at this site] Last updated 2005-11-18:.

[ About Donations ]
(Back to source code index)

Associating MIDI Instrument Numbers With Their Sounds

A little hack I wrote to create a MIDI file that plays, in each available sound, the MIDI instrument number for that sound as a binary number encoded as a sequence of notes. Most of what I could possibly write about it up here is already in the comments or the printf() statements. I don't expect many people to find this program useful itself, though folks may be interested in the MIDI file that resulted from it. The source code is here mostly because by the time I finished writing comments I was saddened by the thought of nobody else ever seeing them ...

(Display/download just this code without any HTML markup or window dressing)

/* ******************************************************************** */
/*                                                                      */
/* 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 <stdio.h>

/* 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.01, 2005-11-18, D. Glenn Arthur Jr., http://www.dglenn.org/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.dglenn.org/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@panix.com\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.      */

	}

(Display/download just this code without any HTML markup or window dressing)

(Back to source code index)
(* email D. Glenn Arthur Jr. * Map Of My Web Pages * my main page * about me * musings and observations * me the musician * writings * events * humour *)
[ About Donations ]