home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
genmidi.zip
/
genmidi.INF
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1995-08-04
|
21KB
|
515 lines
ΓòÉΓòÉΓòÉ 1. Introduction ΓòÉΓòÉΓòÉ
GENMIDI Programmer's Manual
General MIDI is a specification that standardizes the setup of sound modules
such that a MIDI file (containing Program Change, Controller, Pitch Wheel, and
Note events) designed to be played upon one GM module should sound relatively
similiar upon another GM module, even an entirely different model made by
another manufacturer. For a lengthy description of General MIDI, please read
my "MIDI Book" (a collection of OS/2 INF files to be read by VIEW, detailing
the MIDI spec). This manual assumes that you're familiar with General MIDI.
Since many MIDI applications need to deal with General MIDI, often displaying
those 128 GM Patch names, defined Controller names, and the GM Drum Key names,
it made sense to put all of those ascii strings into one shared dynamic link
library. But more than that, I found that I was using several related routines
in all of my MIDI apps. For example, frequently I needed to look up the
program number that was associated with a given GM Patch name. For example, if
the user typed "Grand Piano" into some entry control, I needed to look up what
program number that was (ie, 0), in order to construct a MIDI Program Change
message to select the Grand Piano patch. So, I decided that, in addition to
putting the full set of GM Patch names, defined MIDI Controller names, and GM
Drum Key names in the DLL, I also would put some related routines in the DLL.
There's a routine to look up a program number based upon a GM Patch name.
There's a complementary routine to look up a GM Patch name based upon a program
number. There's a routine to look up a controller number based upon a defined
Controller name. There's a complementary routine to look up a Controller name
based upon a controller number. There's a routine to look up a note number
based upon a GM Drum Key name. There's a complementary routine to look up a GM
Drum Key name based upon a note number. In addition, there are two
complementary routines; one takes a MIDI note number and returns a note name
(as a musician might specify it, with a note letter and octave, for example
"C3"), and the other takes a note name and returns its respective MIDI note
number.
GENMIDI.DLL is an OS/2 2.X Dynamic Link Library (DLL) that a programmer can use
to simplify writing OS/2 applications that deal with General MIDI and MIDI
Controllers.
Many programs can be using that one copy of the DLL simultaneously, thus
reducing redundant code that eats up RAM needlessly. Furthermore, the DLL
helps to eliminate discrepancies and incompatibilities in the way that two
programs name GM Patches, GM Drum Keys, and MIDI Controllers, as well as
assigning note names to MIDI note number, if both programs use this DLL, since
both will be using the same ascii strings and look-up routines.
To avoid source code name conflicts with DLL functions and data, avoid naming
any of your functions and data starting with the letters Midi.
ΓòÉΓòÉΓòÉ 2. Copyright Notice ΓòÉΓòÉΓòÉ
This OS/2 Online Book and the related files, GENMIDI.DLL, GENMIDI.H,
GENMIDI.LIB, and examples for using GENMIDI.DLL are all copyright 1995 by Jeff
Glatt. These files are freely redistributable, and may be used by and
distributed along with any software, be it commercial or otherwise, provided
that these files are not internally modified, nor specifically sold as a
complete product by themselves. The only price that you have to pay is the one
that you're already paying by spending all of your time in front of a computer
instead of developing healthier outlets.
NOT SO STANDARD DISCLAIMER:
These programs are provided "as is" without warranty of any kind either
expressed or implied or tatooed in a place that only a few people have ever
seen, including but not limited to the implied warranties of merchantability,
fitness for a particular purpose, and the dubious assumption that the software
has been created by a sane individual who would never do anything that may hurt
you. The entire risk as to the results and performance of the programs is
assumed by you or someone who looks exactly like you. Jeff Glatt does not
guarantee that the functions in these programs will meet your requirements,
especially if your requirements involve lots of latex and some docile,
domesticated animal. Nor does Jeff Glatt warranty the programs to be
uninterruptable or error-free, although mercifully free of "General Protection
Faults". If you use said programs, you can not say anything nasty about the
author, even if the programs inadvertently cause the erasure of your collection
of X-rated GIFs of a conservative, overweight and overrated TV "personality"
plooking himself vigorously with his royalty checks from some rancid paperback.
Jeff Glatt is not responsible for any damages as a result of anything that he
has done, or hasn't done, or was supposed to do but never got around to it, and
furthermore, he doesn't even care so leave him alone, ratface. You may have
more or less protections in certain states of the union, depending upon how far
your local politician is willing to bend over for some bribe from a business
lobbyist. Just remember that Jeff Glatt has no money, so don't bother suing
him as a result of any damages caused by this OS/2 program. Tell your greasy
lawyer to go after IBM, and make sure that you pick 12 really stupid pinheads
for the jury. If swallowed, induce vomiting immediately by contemplating the
asthetics of Microsoft Windows.
OS/2 is a trademark of International Business Machines Corporation.
Windows is a trademark of Microsoft Incorporated, and furthermore, Bill Gates
is to blame for it.
If you have suggestions, comments, criticisms, and anything else other than
dollar bills, then send them to someone else because you got it for free, and
you know what you get for nothing? But, if you do need to contact the author,
then either phone some of the more prominent psychiatrict clinics in central
New York state, or try this:
Jeff Glatt
6 Sycamore Drive East
New Hartford, NY 13413
(315) 735-5350
ΓòÉΓòÉΓòÉ 3. Archive ΓòÉΓòÉΓòÉ
The GENMIDI archive consists of the DLL and several example applications with
source code. The DLL itself is named GENMIDI.DLL, and must be copied to one of
the directories specified by the LIBPATH statement in your config.sys file.
Usually, one of the specified directories is .;, which means that the DLL can
be placed in the same directory as the applications. The applications will not
run if the DLL isn't placed somewhere in your LIBPATH. These applications are
designed to be run from an OS/2 command line.
For compiling apps, you'll need GENMIDI.H and GENMIDI.LIB copied to where you
keep your C include files and C link libraries respectively, although these
files aren't needed by the executables you produce. Of course, GENMIDI.DLL
should be included with your executable if you distribute that executable, and
the user should be instructed to copy GENMIDI.DLL to his LIBPATH.
ΓòÉΓòÉΓòÉ 4. Functions for GM Patches ΓòÉΓòÉΓòÉ
There are two functions dealing with GM Patches. MidiGetPgmNum() looks up a
program number based upon a GM Patch name passed to it. MidiGetPgmStr() looks
up a GM Patch name based upon a program number passed to it.
ΓòÉΓòÉΓòÉ 4.1. MidiGetPgmNum() ΓòÉΓòÉΓòÉ
Template
UCHAR MidiGetPgmNum(UCHAR * PgmName);
Description
This is passed a null-terminated string containing a Patch name, for example
"Grand Piano". It tries to find a match to one of the defined GM Patch names,
and returns the respective Program number (ie, to be used in a MIDI Program
Change message) for that Patch. Leading and trailing blanks upon the string
are ignored. Also, upper or lower case is irrelevant. Furthermore, the string
can be a substring of a GM Patch name. For example, if the string is simply
"Clav", then the DLL considers this to match the first GM Patch with "Clav"
somewhere in its name, which would be "Clavinet". Or, if the string is "Bell",
then the DLL would match this with "Tubular Bells". Note that the GM Patch
name containing the substring closest to the beginning of the GM name is
matched. For example, "Harp" matches "Harpsichord" rather than "Orch. Harp".
If the passed string does not match any GM Patch name (or isn't a substring of
one), then a Program number of 0xFF is returned. Being that allowable MIDI
Program numbers are 0 to 127, 0xFF can only be construed as "no matching GM
Patch found".
Example
printf("Grand Piano is Program %d\n", MidiGetPgmNum("Grand"));
The example PGMNUM.C demonstrates using MidiGetPgmNum().
ΓòÉΓòÉΓòÉ 4.2. MidiGetPgmStr() ΓòÉΓòÉΓòÉ
Template
UCHAR * MidiGetPgmStr(UCHAR PgmNumber);
Description
This is passed a program number (ie, 0 to 127 inclusive), and it returns a
null-terminated string containing the respective GM Patch name, for example a
program number of 0 returns "Grand Piano". Program numbers > 127 return a null
string (ie, pointer to a null byte).
Example
printf("Program 1 is %s\n", MidiGetPgmStr(1));
The example PGMNAME.C demonstrates using MidiGetPgmStr(). It also can be used
to display all 128 GM Patch names (ie, the ascii strings) within GENMIDI.DLL.
ΓòÉΓòÉΓòÉ 5. Functions for GM Drum Keys ΓòÉΓòÉΓòÉ
There are two functions dealing with GM Drum Keys. MidiGetDrumNum() looks up a
note number based upon a GM Drum Key name passed to it. MidiGetDrumStr() looks
up a GM Drum Key name based upon a note number passed to it.
ΓòÉΓòÉΓòÉ 5.1. MidiGetDrumNum() ΓòÉΓòÉΓòÉ
Template
UCHAR MidiGetDrumNum(UCHAR * DrumArray, UCHAR * DrumKeyName);
Description
This is passed a null-terminated string containing a Drum Key name, for example
"Kick 1". It tries to find a match to one of the defined GM Drum Key names,
and returns the respective note number (ie, to be used in MIDI note messages to
play that drum sound). Leading and trailing blanks upon the string are
ignored. Also, upper or lower case is irrelevant. Furthermore, the string can
be a substring of a Drum Key name. For example, if the string is simply
"Snare", then the DLL considers this to match the first Drum Key with "Snare"
somewhere in its name, which would be "Snare 1". Or, if the string is "Tom",
then the DLL would match this with "Low 2 Tom". Note that the GM Drum Key name
containing the substring closest to the beginning of the GM name is matched.
For example, "HiHat" matches "Open HiHat" rather than "Closed HiHat" or "Pedal
HiHat".
Not all 128 possible MIDI note numbers have a defined drum sound assigned to
them. For example, note numbers below 35 and above 81 have no drum sounds
assigned to them by the GM specification. On the other hand, many
manufacturers make modules that have a larger drum kit than the GM spec, and
they use these unassigned note numbers to play the extra drum sounds.
MidiGetDrumNum() therefore allows a second parameter to be passed to it,
pointing to an array that defines the names of any extra Drum Keys (ie, sounds)
in a kit, plus their note numbers. This is useful if your software is used for
a specific module that extends the GM Drum Kit. For each drum sound, there is
a length byte of its name, (counting the terminating null), then a byte
representing the MIDI Note number that the sound is mapped to, and then the
null-terminated name. After the last drum sound, an extra null byte ends the
array.
For example, assume that you have a module with 2 extra drum sounds which you
wish to name "Big Boom" and "Little Boom". These are assigned to MIDI note
numbers 126 and 127 respectively. You would make an array as so:
UCHAR my_array[] = { 9,126,'B','i','g',' ','B','o','o','m',0,
12,127,'L','i','t','t','l','e',' ','B','o','o','m',0, 0 };
Now you can pass this to MidiGetDrumNum(), which will search this array first
when matching any passed string. For example, the following call returns a
note number of 127.
note_number = MidiGetDrumNum(&my_array[0], "Little");
Note that substring matching and case-insensitivity applies to matching the
contents of your array.
Because the contents of the array are searched before the standard GM Drum Key
names, you can even replace any of the standard names. For example, in the
standard Kit, note number 43 is "Low 1 Tom". Perhaps you have a module that
has various drum kits. Switching to an "Orchestral Kit" causes the Low 1 Tom
sound to be replaced by a Timpani, but the rest of the Kit's Drum Keys remain
the same as the standard kit. Therefore, you can replace just the Low 1 Tom by
passing an array that sets note number 43 to "Timpani", as so:
UCHAR my_array[] = { 8,43,'T','i','m','p','a','n','i',0, 0 };
note_number = MidiGetDrumNum(&my_array[0], "TIMPANI"); /* returns note number
43 */
If you have no extensions or replacements to the GM Kit, then simply pass a 0
instead of a pointer to an array.
If the passed string does not match any GM Drum Key name (or isn't a substring
of one), then a note number of 0xFF is returned. Being that allowable MIDI
note numbers are 0 to 127, 0xFF can only be construed as "no matching GM Drum
Key found".
Example
The example DRUMNUM.C demonstrates using MidiGetDrumNum().
ΓòÉΓòÉΓòÉ 5.2. MidiGetDrumStr() ΓòÉΓòÉΓòÉ
Template
UCHAR * MidiGetDrumStr(UCHAR * DrumArray, UCHAR NoteNumber);
Description
This is passed a note number (ie, 0 to 127 inclusive), and it returns a
null-terminated string containing the respective Drum Key name, for example a
note number of 36 returns "Kick 1". Note numbers > 127, or a note number for
which there is no defined Drum Key in the GM Kit, return a null string (ie,
pointer to a null byte).
A pointer to an array that defines the names of any extra Drum Keys (ie,
sounds) in a kit, plus their note numbers, may be passed as well. See the
discussion in MidiGetDrumNum() for the purpose and setup of the array. If
passed a 0, then only the standard GM Drum Kit is considered.
Example
printf("Drum Key 36 is %s\n", MidiGetDrumStr(0, 36));
The example DRUMKEY.C demonstrates using MidiGetDrumStr(). It also can be used
to display all defined GM Drum Key names (ie, the ascii strings) within
GENMIDI.DLL.
ΓòÉΓòÉΓòÉ 6. Functions for MIDI Controllers ΓòÉΓòÉΓòÉ
There are four functions dealing with MIDI Controllers. MidiGetCtlNum() or
MidiGetCtlNum2() looks up a controller number based upon a Controller name
passed to it. MidiGetCtlStr() or MidiGetCtlStr2() looks up a Controller name
based upon a controller number passed to it.
ΓòÉΓòÉΓòÉ 6.1. MidiGetCtlNum() ΓòÉΓòÉΓòÉ
Template
UCHAR MidiGetCtlNum(UCHAR * CtlName);
Description
This is passed a null-terminated string containing a Controller name, for
example "Mod H". It tries to find a match to one of the defined MIDI
Controller names, and returns the respective controller number (ie, to be used
in a MIDI Controller message). Leading and trailing blanks upon the string are
ignored. Also, upper or lower case is irrelevant. Furthermore, the string can
be a substring of the Controller name. For example, if the string is simply
"Mod", then the DLL considers this to match the first Controller whose name
begins with "Mod", which would be "Mod H". The substring match must be at the
head of the string. For example, "+" does not match "Data +", but "Dat" does.
If the passed string does not match any MIDI Controller name (or isn't a
substring of one), then a controller number of 0xFF is returned. Being that
allowable MIDI controller numbers are 0 to 127, 0xFF can only be construed as
"no matching Controller found".
Example
printf("The Mod H Controller is #%d\n", MidiGetCtlNum("Mod"));
The example CTLNUM.C demonstrates using MidiGetCtlNum().
ΓòÉΓòÉΓòÉ 6.2. MidiGetCtlStr() ΓòÉΓòÉΓòÉ
Template
UCHAR * MidiGetCtlStr(UCHAR CtlNumber);
Description
This is passed a controller number (ie, 0 to 127 inclusive), and it returns a
null-terminated string containing the respective MIDI Controller name, for
example a controller number of 1 returns "Mod H". Controller numbers > 127, or
a controller number for which there is no defined Controller assigned to it,
return a null string (ie, pointer to a null byte).
Example
printf("Controller #1 is %s\n", MidiGetCtlStr(1));
The example CTLNAME.C demonstrates using MidiGetCtlStr(). It also can be used
to display all defined Controller names (ie, the ascii strings) within
GENMIDI.DLL.
ΓòÉΓòÉΓòÉ 6.3. MidiGetCtlNum2() ΓòÉΓòÉΓòÉ
Template
UCHAR MidiGetCtlNum2(UCHAR * CtlArray, UCHAR * CtlName);
Description
Not all 128 possible MIDI controller numbers have a defined parameter assigned
to them. For example, controller number 105 has no defined parameter assigned
to it by the MIDI specification. On the other hand, many manufacturers make
modules that allow these unassigned controller numbers to be used to control
some parameter. MidiGetCtlNum2() therefore allows a second parameter to be
passed to it, pointing to an array that defines the names of any extra
controllers, plus their controller numbers. This is useful if your software is
used for a specific module that uses these extra controller numbers. For each
controller, there is a length byte of its name, (counting the terminating
null), then a byte representing its MIDI controller number, and then the
null-terminated name. After the last controller, an extra null byte ends the
array.
For example, assume that you have a module with 2 extra controllers which you
wish to name "Filt Cutoff" and "Filt Res". You want these assigned to MIDI
controller numbers 105 and 106 respectively. You would make an array as so:
UCHAR my_array[] = { 12,105,'F','i','l','t',' ','C','u','t','o','f','f',0,
9,106,'F','i','l','t',' ','R','e','s',0, 0 };
Now you can pass this to MidiGetCtlNum2(), which will search this array first
when matching any passed string. For example, the following call returns a
controller number of 105.
note_number = MidiGetDrumNum(&my_array[0], "Cutoff");
Note that substring matching and case-insensitivity applies to matching the
contents of your array. Furthermore, substring matching is not limited to the
head of the Controller name (for your array names only).
Because the contents of the array are searched before the defined Controller
names, you can even replace any of the defined names. For example, controller
number 16 is "Genr 1", a general purpose slider. Perhaps you have a module
that allows that to control LFO Rate. Therefore, you can replace just the Genr
1 name by passing an array that sets controller number 16 to "LFO Rate", as so:
UCHAR my_array[] = { 9,16,'L','F','O',' ','R','a','t','e',0, 0 };
ctl_number = MidiGetCtlNum2(&my_array[0], "LFO"); /* returns controller number
16 */
If you have no extensions or replacements to the defined Controllers, then
simply pass a 0 instead of a pointer to an array.
ΓòÉΓòÉΓòÉ 6.4. MidiGetCtlStr2() ΓòÉΓòÉΓòÉ
Template
UCHAR * MidiGetCtlStr2(UCHAR * CtlArray, UCHAR CtlNumber);
Description
MidiGetCtlStr2() allows a second parameter to be passed to it, pointing to an
array that defines the names of any extra controllers, plus their controller
numbers. See the discussion in MidiGetCtlNum2() for the purpose and setup of
the array.
ΓòÉΓòÉΓòÉ 7. Functions for MIDI Note names ΓòÉΓòÉΓòÉ
There are two functions dealing with MIDI note names and numbers.
MidiGetNoteStr() returns a note name based upon a MIDI note number passed to
it. MidiGetNoteNum() returns a MIDI note number for a given note name.
ΓòÉΓòÉΓòÉ 7.1. MidiGetNoteStr() ΓòÉΓòÉΓòÉ
Template
ULONG MidiGetNoteStr(UCHAR * Buffer, UCHAR NoteNumber, CHAR Key);
Description
This is passed a MIDI note number (ie, 0 to 127 inclusive), as well as a
pointer to a buffer into which MidiGetNoteStr() places a null-terminated string
representing the respective note name. For example, a note number of 60
returns "C 3" (meaning the C in the third octave of a piano). The buffer
should be at least 5 characters long to hold the largest possible returned
string. MidiGetNoteStr() returns the length the note name (not counting the
null byte).
The Key arg is a negative number if flats are desired, or a positive number if
sharps are desired, whenever an accidental is used.
A program should display MIDI notes with their respective note names (rather
than numbers), as this is how a musician names notes.
Example
ULONG Len;
UCHAR Buffer[5];
Len = MidiGetNoteStr(&Buffer[0], 60, 0);
The example NOTENAME.C demonstrates using MidiGetNoteStr().
ΓòÉΓòÉΓòÉ 7.2. MidiGetNoteNum() ΓòÉΓòÉΓòÉ
Template
UCHAR MidiGetNoteNum(UCHAR * NoteName, UCHAR * DefaultOct, UCHAR ** EndPtr);
Description
This is passed a string (does not have to be null-terminated, but there should
definitely be a non-numeric character after the note name, whether it be a
space, null, or some other non-numeric value) containing a note name, for
example "C 3". It returns the respective MIDI note number (ie, 0 to 127). If
the passed note name is an illegal one (ie, H4), then the value 0xFE is
returned. If the note name is out of range (ie, the lowest note is C-2 and the
highest note is G8, so "C9" would be out of range), then the value 0xFF is
returned.
If the passed handle EndPtr is not null, then MidiGetNoteNum() returns a
pointer to where the note name ends in the buffer. This is handy if you have
one long string containing numerous note names, and you wish to repeatedly call
MidiGetNoteNum() to extract each respective note number from that string. You
can use the updated handle to pass back to MidiGetNoteNum(), to extract the
next note name in the string. When there are no more note names,
MidiGetNoteNum() will end up returning 0xFE.
MidiGetNoteNum() requires a pointer to a UCHAR containing the default octave to
be used if the note name omits the octave. For example, the user can simply
specify "E". If the default octave is 60 (ie, middle C octave), then the note
number returned will be for the E above middle C (ie, 64). MidiGetNoteNum()
updates the passed default octave whenever the octave is specified upon the
passed note name.
Example
Here we pull the four note names out of MyNames[] and stuff the 4 respective
MIDI note numbers in MyNumbers[]. (Note that # is used to denote "sharp" and
b, a small B, is used to denote flat. Double sharps or flats are also
supported. Also, any amount of blank space may be used in between each note
name. Lower or upper case letters may be used. Spaces may appear inbetween
the note letter, any sharp or flat, and the octave. Octaves can be omitted, in
which case the default octave is used).
UCHAR MyNames[] = "G#2 A -1 d5 E b";
UCHAR MyNumbers[5];
UCHAR Cnt;
UCHAR DefOct;
UCHAR * EndPtr;
Cnt = DefOct = 0;
EndPtr = &MyNames[0];
while ( ( MyNumbers[Cnt] = MidiGetNoteNum(EndPtr, &DefOct, &EndPtr) ) < 128 )
Cnt++;
The example NOTENAME.C demonstrates using MidiGetNoteNum().