Robert Inventor


Home Music 3D Animations Utilities Code Search Forums About Contact Accessibility
Mem Leak Check
Play Midi Lib
Com Ports Monitor
Win Ming
Pop Pal Fast
Gle with time out
Time64

Play Midi Lib

Last Updated July 13 2003 (UK time)

PlayMidiLib now has support for QuickTime! Also non melodic percussion in QT.

Intro, Test program and downloads, Calling windows dlls from other languages, Updates

Intro

This library is mainly of interest to microtonalists - those who are interested in the use of fine distinctions of pitch. finer than a semitone. If you are new to such concepts - musical intervals, are usually measured using either frequency ratios or cents - both notations are used in this library.

With ratios, one note is your 1/1 and if for instance the harmonic series is 2/1, 3/1, 4/1, 5/1 3/1 etc for the notes c' g' c'' e'' g'' etc, then the pure major third between the c'' and the e'' in the harmonic series for instance is 5/4. With cents, then there are 100 cents to a semitone, so in the standard twelve equal temperament of a modern piano say, c'' to e'' is an interval of 400 cents. The pure harmonic series 5/4 turns out to be about 386 cents, so they differ by about a seventh of a semitone.

If you are new to microtonal intervals and want to find out more, Tune Smithy has many ways to explore microtonal tunings for you to try out. See the Fractal Tune Smithy download pageThe Play Midi Lib library assumes a basic acquaintance with scales and tunings, and cents and ratios.

This is a library of subroutines for use by c programmers to quickly write new programs that can play notes in any scale or tuning. It can be used by beginner c programmers, even in your first "hello world" type programs . They can also be used by advanced programmers to add retuning capabilities to your program, or as example code to show how it can be done. More advanced programamers will probably want to modify the library to suit your particular needs - the library has been kept small and minimalist so that you can easily see how it all works. However, even in its present form, it may be useful for a more advanced programmer to get together a small monotimbral microtonal program quickly. Be sure to contact me at support@tunesmithy.co.uk if you have any questions about it.

To play a septimal minor chord (using the septimal minor third 7/6 instead of the normal minor third 6/5) for one second, by way of example, you would just go:

if(StartMidiOut())
{
	SetMidiVoice(6);// Harpsichord 
	SetMidiScale("7/6 3/2 2/1");
	PlayScaleDegree(0);
	PlayScaleDegree(1);	
	PlayScaleDegree(2);
	SleepSeconds(1);
	StopScaleDegree(0);
	StopScaleDegree(1);
	StopScaleDegree(2);
	StopMidiOut();
}

and the libary will take care of all the pitch bends and channel remapping for you. They get played via midi out on one of the midi devices on the user's computer.

It uses the instant pitch bend method - to retune a note a pitch bend is sent just before the note is played - or a while in advance if possible. Since the midi standard only lets you have one pitch bend in play per channel at a time (corresponding to the position of a pitch bend wheel for a keyboard user) then a retuning program of this type needs to move notes around to new channels to ensure that each one can be played accordingly. But if you use this library you don't need to get involved with the details of how that works. If you want to know how it is done then you can examine the source code.

Note that if the user uses this library to play on QuickTime, then this may result in irregular timings when you play fast-ish tunes. I'm not sure why - the timing in QT is fine if you play a midi clip -or if you construct a sequence of all the notes you watn to play in advance and play that in its tune player - but if you output the notes one note at a time like this, they can be irregular.

So if you include the dll which adds a quicktime device to your program then bear in mind that playback one note at a time may not always have quite the crisp timing it does when you play a midi clip in QT, probably depending on the setup. That irregular timing can actually be quite attractive sometimes in fractal tunes :-). One solution here, which I use in FTS, is to give the user an option to save the result as a midi file and then show it in a web page where the QT timings will be accurately played as it is no longer real time (midi save not included as part of the library).

Another thing to bear in mind. It uses Sleep(..) instead of the higher priority timeSetEvent(..). This means that the timing resolution isn't as good as it could be in normal circumstances where other programs are running in the background at the same time. I'll maybe do another upload of it presently using timeSetEvent as I have it in FTS.

If I get a keen response from some users then I'll probably develop it further and improve it somewhat. Meanwhile here it is at the stage it is at present.

top

Test program and downloads

The files are all just a few KB.

Intro to library, Test program as executable, Source code, Calling the Dll, Some of the routines, Source code for the dlls

Intro to library

There are two ways to use the library - include the source code in your project, or use it as a dll.

Use the dll if your compiler can't compile Windows c programs. You should be able to use this dll with projects in any language if you can figure out the appropriate bindings to call routines in a dll. See Calling windows dlls from other languages.

top, start of section

Test program as executable

Here is an example to let you just try it out and find out how it works: TestPlayMidiLib.zip (contains Windows executable and dll).

The demo program in that zip calls the library from a console mode app, so that it plays music from the MSDOS prompt - you can use it equally well in a windows program.

Download the zip, unzip the program and its dll into the same folder, and run it. The dll included is one that adds a QuickTime device to the list of devices for the program, if you have an appropriate installation of QT. PlayMidiLib itself is part of the executable.

top, start of section

Source Code

Now here is the source code plus a MSVC project for it. This is the one to use if you want to include the actual source code for the library as part of your program.

PlayMidi.zip

In MSVC, just unzip, open the project and make it.

The rest of this section is to help users without MSVC

Just ignore or delete the dsp and dsw files in all these zips, and include the .c and .h files in your project. A good free compiler for building C and Windows C programs is LCC. It's a compiler for C only, not C++.

In LCC, make a Windows executable with no files in the project, and add all the files from the zip to it. Then, if you use F5 to make the project and start the debugger, LCC will pop up a dialog to say that various symbols are undefined and found in winmm.lib - click the Add Libs button at that point to add it in. Or go to Project | Configuration | Linker | Additional files to be included in the link, and add it to the end of that line.

Finally if you want to add Quicktime to the list of devices to use, you need to copy QTMusic.dll into the same folder as the working directory for your program so that it can find it.. (You could alternatively place it into your System folder along with the other dlls there).

To make a program that uses the library, add the files PlayMidiLib.h and PlayMidiLib.c to your project.

Then you need to add Winmm.lib to the list of library modules for the linker. In Visual C++ you add a library using Project | Settings | Link | Object / Library modules. This is all already done for the Visual C++ project in the zip.

Adding Winmm.lib lets your compiiler resolve references to the midi routines in the Windows dynamic link library winmm.dll for multimedia routines.

top, start of section

Calling the DLL.

Now, here it is as a test program which calls the dll. When used in this form, you need to install the dll in the same folder as your executable which uses (or in your System folder or the like).

PlayMidiCallsDll.zip

With this one, you must add playmidlib.lib to the list of library modules for your project, but don't need to add winmm.lib any more as the linking to winmm is done in the dll itself.

Again, you need to copy QTMusic.dll into the same folder as your program so that it can find it. This is needed if you want to add Quicktime to the list of devices to use.

top, start of section

Some of the routines

The main routines you use are:

int nNumOutDevices (); - number of Midi out devices on your computer

char * szNameForDevice ( iDev, szDeviceName, iMAX_Name); - use to find the name for the device to display to user. You can loop around through all the devices up to nNumOutDevices to give a list of all the ones avaialble.

StartMidiOutForiDev(iDevSel); - opens a particular device.

Returns MIDI_OUT_ERROR if it can't be opened.

TestPitchBendRange (); - lets user test the pitch bend range - plays a series of notes which will sound identical in pitch if the pitch bend range is set correctly to +- two semitones.

StartMidiOut (void); - open the currently selected device

StopMidiOut (void); - close currently selected device

SetMidiVoice (int iVoice); - set the current instrument to play from one of the Midi Instrument numbers 0 to 127. You'll find a list of them all in the source code, and can display them to user using

int iVoice = FindMidiVoice(string,szVoiceFound,MAX_szVoiceFound);

- searches through the list of voices for the best match or partial match for the string.

So for instance to switch to Koto you could use

SetMidiVoice( FindMidiVoice ("Koto", szBuf, MAX_BUF) ); - and just have a buffer there to receive the information and discard it.

SleepSeconds (dSecs); - sleep for that number of seconds (can be fractional e.g. 0.2 secs or whatever0.

Set1o1 (d1o1Freq); - set the 1/1 of the scale to a particular frequency in hertz.

SetMidiScale (szScale); - set the current scale to use as a string in the SCALA notation - i.e understood as ratios unless a decimal point is included in which case the interval is understood as being in cents. (1/1 not needed)

Example:

SetMidiScale("9/8 5/4 4/3 3/2 5/3 15/8 2/1");

GetszMostRecentScale() - use to obtain a string for the most recent scale in use.

nScaleNotes=GetnScaleNotes();

- use to find the number of noes in current scale.

PlayScaleDegree (int ipos_in_scale, int iVelocity);
StopScaleDegree (int ipos_in_scale, int iVelocity);
StopAllNotesPlayed (int iVelocity);

- pretty self explanatory

DoBendForScaleDegree(i);

- use to apply a bend for this scale degree - the idea here is that it is often good to set up the pitch bends well in advance before you play the notes to avoid pitch bend artefacts which can happen on a few devices. This means that later when you play the note, one of the channels has already been set up with an appropriate pitch bend ready to play your note.

You could apply pitch bends for all the scale degrees every time you change the scale (that is what I do in FTS).

PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,iVelocity);

plays a non melodic percussion note. Each number here gives a different instrument.

StopNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,iVelocity);

Quite a few other ones which you can find in the source code.

top, start of section

Source code for the dlls

Here now is the source code for the Dll. You only need this if you want to modify the dll yourself. It has two projects in the work space - PlayMidiLib and TestPlayMidiLib. To build the test program + the library in one go just choose the TestPlayMidiLib project as the one to build from the drop list.

PlayMidiBuildsDll.zip

Finally, here is the source code for the QuickTime QTMusic.dll as a MSVC project QTMusicBuildsDll.zip

You can freely copy and modify the source code for all of these, to use in your own programs.

top, start of section

Calling windows dlls from other languages

When one calls Windows dlls from some other language, one needs to be aware that the functions in the library are declared as __stdcall

Here is the summary for __stdcall from the documentation:

Argument-passing order Right to left.
Argument-passing convention By value, unless a pointer or reference type is passed.
Stack-maintenance responsibility Called function pops its own arguments from the stack.
Name-decoration convention An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number
of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows:
_func@12
Case-translation convention None

Another thing to be aware of: long and int are both four byte integers. Only short is two bytes. DWORD is an unsigned long (or int). If you need to know the size of other types, they can be found in the Windows headers.

If you need to know the decorated function names for a dll to call them, you can figure them out from the convention, or indeed, you can just open the dll in Wordpad and you will find them amongst the binary data in the file..

Here are the decorated function names for PlayMidiLib.dll

_CheckForQTLibrary@0, _CloseMidiOut@0, _DoBendForScaleDegree@4, _DoBendForScaleDegreeInLoop@4, _FillComboWithMidiOutDevices@4, _FillComboWithMidiVoices@4, _FillListWithMidiOutDevices@4, _FillListWithMidiVoices@4, _FindMidiVoice@12, _GetComboSelMidiOutDevice@8, _GetComboSelMidiVoice@8, _GetListSelMidiOutDevice@8, _GetListSelMidiVoice@8, _GetMidiVoiceName@12, _GetScaleFromText@16, _GetszMostRecentScale@0, _GetnScaleNotes@0, _OpenMidiOut@4, _PlayNonMelodicPercussion@8, _PlayScaleDegree@8, _PrintNotesInPlay@0, _PrintVoices@0, _Set1o1@8, _SetComboSelMidiOutDevice@8, _SetComboSelMidiVoice@8, _SetListSelMidiOutDevice@8, _SetListSelMidiVoice@8, _SetMidiScale@4, _SetMidiVoice@4, _SleepSeconds@8, _StartMidiOut@0, _StartMidiOutForiDev@4, _StartQT@0, StopAllNotesPlayed, StopAllNotesPlayedInChannel, _StopMidiOut@0, _StopNonMelodicPercussion@8, _StopQT@0, _StopScaleDegree@8, _TestPitchBendRange@0, _dSecsSinceStartMidiOut@0, _nChannelsAvailable@0, _nNumOutDevices@0, _szNameForDevice@12.

Updates

JULY 13 2003 (UK time)

Had a look at this page afresh after returning to it after a number of months and realised it could be more clearly expressed. Particularly, it gave no list of the routines to use. I have provided a short list, which could be expanded and made more comprehensive if there is interest in the project. That's all this update does - improve the presentation on this page. Downloads as before.

APRIL 5 2003 (UK time)

Some work making TestPlayMidiLib nice to use for a user of the program. Also added a new routine GetszMostRecentScale. Also, replaced Default by the likes of Standard or Preset in anything presented to the user - this is probably a good thing to do for a demo program as I find some users of my programs don't know the computing term "Default" as preset or standard setting. It's use in ordinary non technical English has a somewhat different meaning, various shades of meaning in fact, but one of the ones that spring to mind amongst literate users is: "neglect to do what duty or law requires" - it is rather confusing if that is what you think it means.

DEC 6 (UK time)

Just replaced the definition of HWND in PlayMidiLib.h with #include <windows.h> - this is for compatability with some compilers. If you wish to use the header to call the library from a non windows console app you can just comment out the line

#define PM_ADD_TO_WINDOWS_PROJECT

In QTMusicLib.h, simply replaced BOOL by int everywhere, so that you can use this header file with any compiler that recognises c headers.

DEC 4 PM (UK time)

Added routines to fill a combo or list with the Midi out devices or the voices, and to change the midi device in response to change of selection in the list.

DEC 4 (UK time)

When using Quicktime you can now play up to 31 channels simultaneously so 31 simultaneous pitch bends.

Added routines to show the list of midi devices you have for the out menu - and included out menu list in the example program.

Some tidying up of the code.

DEC 1 (UK time)

Added non melodic percussion. There the trick is that in QuickTime you use a patch to select a non melodic percussion bank for the channel - patches in the range kFirstDrumkit+1 for GM percussion up to kFirstDrumkit+128. So to send GM percussion you first set the instrument for the channel to kFirstDrumkit+1, and then play notes in it.

Some bug fixes - most important - still not recognising the decimal point for cents convention.- fixed. Another one is that QT requires the patch numbers to have a 1 added to them all - so Acoustic Grand Piano for instance is patch 1 rather than 0.

Nov 29 PM (UK time)

Some bug fixes - most important - last one didn't recognise the decimal point for cents convention - fixed.

Later in the day - added in CheckForQTLibrary(void); so that you can check if the QuickTime library is present before prompting user if they want to use QT. Updated the test program accofdingly

top

Bookmark this page: - and many more - Bookmark and Share What are these?
Disclaimer
Acceptable Use Policy
Special Offers
Contact
Copyright
Privacy
 
Site Designed with advice from Sojo Media (Thanks!)
© Robert Walker 2008
tool tips by overlib
Robert the Inventor's programs
support@robertinventor.com

Share/Bookmark

NEW metronome software - Download Bounce Metronome Pro with bounces and conducting patterns to help you stay in time.