Using Multimedia OS/2 for sound

By Roger Orr

Introduction
One of the features which has gone from being a rare item to one of the standard cards in many new PCs is a sound card. This can be a disaster if you work in an open plan office...

OS/2 WARP comes with out-of-the-box support for many standard sound cards, and allows you to configure a range of different sounds when certain actions occur, such as your desktop starting, windows closing, etc.

For example, you may want to change the noise provided by the system when you get an error message box.

This is generated by code such as this fragment: WinMessageBox( HWND_DESKTOP, HWND_DESKTOP, "Example", NULL,               0, MB_ERROR | MB_OK | MB_MOVEABLE ); To change the sound produced, open the following: 'OS/2 System' -> 'System Setup' -> 'Sound' and select the 'Error' line in the 'System events' list box. The default sound for this is BELLS.WAV - I prefer CUCKOO.WAV so I select it in the lower list box and close the window.

Now if I get an error I will hear a cuckoo noise. OK, simple to use and you can spend hours of time configuring your machine and annoying your friends.

But if you are a programmer you might wish to add such noises to your own programs - how can you do this? Fortunately OS/2 provides a very simple to use API for the multi-media subsystem.

The simplest Multi-Media API - using REXX
MMPM/2 (as the multi-media support for OS/2 is called) provides an MCI (Media Control Interface) which has two primary interfaces: one using strings and the other a procedural interface.

As it is the easiest to use (and can be used from REXX, C, C++, etc) I will use the string interface for this article. Most of the API is available to this interface, only some of the functions requiring pointers or other programming concepts are not available.

First of all in REXX we can use the MMPM/2 API with the REXX function 'mciRxSendString'. This takes as main argument a command string to go to the MCI.

For example, mciRxSendString( "play myfile.wav wait", "RetStr", 0, 0 ) might be used to play a wave sound file 'myfile.wav'. That was easy, wasn't it!

I thought a very simple practical problem might be a useful example, and picked on that of inserting one sound into the middle of another to create a new, composite, sound.

I wrote a simple REXX script which does this, see below.

The main points to highlight are: (a) You must initialise and terminate the REXX interface - see the RXFUNCADD, mciRxInit and mciRxExit calls.
 * InsertRx.cmd

(b) I have used a single function 'SendW' to wrapper the calls to mciRxSendString - I can put error handling in one place. For simplicity it jumps to 'terminate' if any error occurs.

I tested this with the following command: pmrexx insertrx.cmd \mmos2\sounds\beeoong.wav \mmos2\sounds\eeerrupp.wav
 * Note:You MUST use PMREXX since this example uses the clipboard, which is only available for PM programs. If you miss off the PMREXX line you will get a message like: "paste wave" failed - rc = 70544, ErrStVar = System out of memory.

You can test REXX scripts very easily using PMREXX, and also play with the MCI string interface interactively by using the "mcistrng" program which comes with the MMPM/2 toolkit. (For example on DEVCON 7 CD #3 in \TOOLKITS\WARPTLKT\TOOLKIT\SAMPLES\MM\MCISTRNG)

The next step - using C
However, many of us are writing in C (or C++) and might want to do the same thing in C. It is quite easy - the string interface is again provided, this time as mciSendString, which takes very similar parameters to the REXX function. One thing you will be more concerned about with a C program is the synchronous nature of the MCI interface when used with 'wait'. If you start playing a 30 second piece you do not want to freeze your application (or the entire user interface) while you do so.

A general solution is to make use of the asynchronous commands, and let MMPM/2 notify you when a command finishes or a cue point is reached. I decided for this program to continue to use the synchronous ('wait') commands but to use two threads, one for the user interface and one for the MCI interface. This maintains the responsiveness to the user while the background thread is working on the sounds.

I compiled the program with the IBM C/Set++ compiler as: icc /Gm /B"/pm:pm" insertc.c mmpm2.lib The main points to highlight from the C program are:
 * InsertC.c
 * (a) The two lines at the top: #define INCL_MCIOS2 #include  are required to define the MMPM/2 MCI interface.
 * (b) I am using a listbox as the client window for simplicity, and lprintf simply adds lines to the listbox. (This can be a useful technique for test programs!)
 * (c) I have used a global variable 'RetStr' for readability.

The program basically does the same thing as the REXX script.

For more complex ideas or more control over the interface you might need to look at the procedural interface, but there is quite a lot you can do even with the simple string interface.

Conclusion
MMPM/2 gives you a simple way to start to experiment with sounds. There are many places where using a sound file rather than merely beeping might aid in program usability or support, and I would encourage you to try out some of the ideas in this article.

Once you have an example of the use of sound I hope you will look further into MMPM/2 - perhaps using the online help (MMPM Programming Reference or Multimedia with REXX) and/or the sample code in the toolkit.

Roger Orr 31 Aug 1995