Digital Sound Music Interface for OS/2 Adds Multichannel Support for Multimedia Applications

From EDM2
Jump to: navigation, search

by Julien Pierre

Typically, the audio capabilities of multimedia applications are restricted by hardware. The Digital Sound Music Interface (DSMI) for OS/2 changes all that.

What DSMI Offers

DSMI was created to overcome the limitations of the sound hardware and let programmers access a greater number of channels. DSMI allows you to use up to 32 PCM channels simultaneously, even if your hardware only has one channel.

DSMI also offers APIs to load the music module files Modules are conceptually similar to Musical Instrument Digital Interface (MIDI) files in that they contain a full song with the list of its notes, duration, effects, and other information. But unlike MIDI files, which are limited to the sounds your MIDI synthesizer offers, module files also contain information about each instrument, stored as PCM samples. That means you can use any sound you record from your sound card as an instrument in the song - such as your voice. For this reason, and because of the limitations of the MIDI hardware found on most PC sound cards, modules typically sound much better than MIDI.

How DSMI Works

DSMI has several layers:

  • Application
  • Advanced Module Player
  • Channel Distributor Interface
  • Multi Channel Player
  • Dynamic Driver System
  • Sound Device Interface

This section briefly describes all of these layers except for the application. A typical DSMI application uses the Advanced Module Player and/or the Channel Distributor Interface.

Advanced Module Player

The Advanced Module Player (AMP) is a set of APIs that lets you manipulate the loading and playback of module files. It can handle a variety of module formats like *.MOD, *.NST, *.STM, *.S3M, *.MTM, *.FAR, *.669, and *.AMF. If you just want to play music using modules, you do not need to use the other layers.

AMP also provides functions to pause and resume the music, and to get the position in the music, so that you can synchronize your program with the music. You can also change the global tempo and volume of the song, go forward or backward in the song, enable or disable certain channels in the song, or set the panning of a channel.

The panning is the position of a channel on a stereo system DSMI supports panning values from -63 for full-left channels to +63 for full-right channels. Intermediate values mean that the channel is played on both left and right channels, with the according volume. A panning of 0 means that the channel is set to the middle (i.e., it is played at full volume on both left and right speakers) In addition, DSMI supports Dolby Prologic Surround If you set the panning of a channel to 100, the channel will be played on the surround speaker. If you do not have a Dolby Prologic Surround system, you will still hear the channel on both speakers, like a middle channel. Internally, the AMP uses Channel Distributor Interface (CDI) calls to play the music.

Channel Distributor Interface

The Channel Distributor Interface (CDI) is a low-level interface It does not control music like AMP, but instead gives you full control of individual channels. You will want to use CDI for sound-effects playback.

CDI lets you independently access the DSMI channels On each channel, you can set the digital instrument that will be used, control the playback frequency, get and set the panning, position and volume, get a status, as well as mute, unmute, play, and stop a note (sound). CDI also lets you globally apply digital effects, like reverberation, filters, or chorus You can even write your own digital effects.

Multi Channel Player

The Multi Channel Player (MCP) is essentially a software digital mixer. For devices without hardware mixing, MCP is registered as the mixing system that CDI will use Devices with hardware mixing support bypass the MCP and provide their own mixing system.

Conceptually, mixing in MCP is accomplished simply by adding the digital waveforms and shifting them so they do not clip. However, the implementation of MCP is in reality much more complex, because MCP also allows you to control the playback sampling rate, volume, panning, and many other effects, on each channel. If you consider the high-data rates of audio - 176,400 bytes per second at 16 bits, 44 kHz stereo (CD quality) - and the fact that MCP has to mix each of the 32 possible channels at that data rate, you can see why it can keep your computer very busy. For that reason, the MCP and several other DSMI components were written in 32-bit assembly language.

Dynamic Driver System

The Dynamic Driver System (DDS) is a means of passing functions between different parts of DSMI. It is especially used to register and get access to the Sound Device Interface (SDI) functions when you use them.

Sound Device Interface

DSMI provides device independence by means of the Sound Device Interface (SDI). CDI makes calls to the mixing system - usually MCP, which in turn calls the SDI to actually reach the sound hardware.

DSMI has four built-in SDIs Nosound, File, DART, and MMPM/2. The Nosound SDI does not use a sound card. This SDI mostly helps you debug your programs. The other three SDIs all register MCP as their mixing system because they do not inherently support mixing The DART and MMPM/2 SDIs provide true device independence and allow DSMI to work with all sound cards that are supported by OS/2; you do not need an SDI for each device.

If you have a sound card with hardware mixing support, like the MWave, you might want to write a special SDI for it that would not register the MCP for mixing and would offload the mixing from the main CPU to the sound card.

A Practical Program Example

This multimedia sample program is written in text mode to be simple, so that you can concentrate on the DSMI APIs you are interested in.

This program is called Simple Module Player, and the full source is available in DSMI\PROGRAMS\SOURCE\SMP\SIMPLE.CPP after you have installed DSMI. It demonstrates the initialization of DSMI and loading and playback of module files. You can use it to play the DSMI\PROGRAMS\EXAMPLE.AMF module file that comes with DSMI, or thousands of other module files that you can get from ftp://ftp.cdrom.com/pub/demos/music/songs/ or other Internet sites.

First, you need to include the DSMI headers

#include dsmi h // headers for DSMI DLL
#include init h // headers for DRIVERS DLL

Then, declare a pointer to a MODULE structure to hold the information about the module music file.

MODULE *mod;

You will also want a function to report errors and quit the program.

void quit(char *string, int errorcode)// function to handle errors
{
 puts(string); // display error message
 exit(errorcode); // quit the program
};

DSMI needs to be initialized to work properly. Here is a function to initialize DSMI:

void init()// function to initialize the DSMI sound system
{
 int result;
 char channels=2,// stereo
 resolution=2, // 16 bits
 samplingrate=SR_44K;// 44 kHz
 long buffer=0; // autodetect DART buffer
 SDIFUNC SDIStartOutput;
 result = initDSMI( channels, resolution,
 samplingrate, QUALITY_ENABLED, AMP_AUTO, NULL,
 ID_DART, buffer, AUTO_DETECT);
 // try to initialize DSMI with DART
 if (result!=0)
 result = initDSMI( channels, resolution,
          samplingrate, QUALITY_ENABLED, AMP_AUTO, NULL,
          ID_MMPM2, NULL, AUTO_DETECT);
 // try to initialize DSMI with MMPM/2
 if (result!=0)
 quit( Error initializing DSMI n ,2);
 // could not initialize DSMI with either driver
 DDSAccessFunction( SDIStartOutput ,
 // access the start output function
 (void**) SDIStartOutput);
 if (SDIStartOutput()!=0)// start sound
 {
   closeDSMI();// close DSMI sound system
   quit( Error starting playback n ,3);
 };
};

DSMI is initialized by way of a call to initDSMI. First, it tries to initialize it in DART mode*. If this call fails, it tries to initialize DSMI using regular MMPM/2 mode. If this call fails too, the program is terminated.

The first argument to initDSMI is a pointer to the number of channels 1 for mono, 2 for stereo. The second argument is the pointer to the resolution you want to use 1 for 8 bits, 2 for 16 bits. The third argument is a pointer to the sampling rate Sampling rates are defined by constants in INIT.H. A sampling rate of SR_44K is used for 44 kHz output in this sample program.

The next parameter tells DSMI if you want quality mode or not. Quality mode is required for 8-bit mode In 16-bit mode, it improves the quality of the mixing, but at the expense of CPU cycles. Next comes the amplification mode. Constants for this mode are also defined in INIT.H.

The next parameter is only used by the File SDI to pass the name of the filename you want to direct the output to; it is set to NULL since DART and MMPM/2 SDIs ignore it The following parameter is very important because it specifies which SDI to use Four SDI constants are available ID_NOSOUND, ID_FILE, ID_DART, and ID_MMPM2. Then, you need to pass a pointer to a variable holding the DART buffer size you want The smaller the DART buffer, the more real-time your program will be. This pointer is only useful when you initialize the DART SDI; so it is set to NULL for MMPM/2

The last parameter specifies whether auto detection is to be used. When you pass the AUTO_DETECT parameter, the values you specify in channel, resolution, and sampling rate are ignored, and the initDSMI call will try to automatically detect the capabilities of the device and get the best possible quality on the SDI. It will then return what it set in the variables you gave it pointers to.

Next, you call DDSAccessFunction to get a pointer to the SDI s start output function, and you call it to actually start the sound. At this point, you have not sent any data to play to DSMI. Therefore, your speakers will remain silent, but the device will be playing even if you do not notice it.

This section of the program loads and plays the music module file.

void module(char *module)// function to load and play music module file
{
 int result;
 result = ampLoadModule(module,0, mod);
 // load the module
 if (result!=MERR_NONE)
 {
   closeDSMI(); // close DSMI sound system
   quit( Error loading module ,4);
 };
 cdiSetupChannels( 0, mod->channelCount);
 // setup the channels
 ampPlayModule(mod,0); // play the module without looping
};

You just need one call to ampLoadModule to load your file with the filename, the load options (set to 0 for the default behavior), and a pointer to a pointer to a MODULE structure. It will try to load the file in every format supported by DSMI until it returns an error. When it succeeds, it allocates a MODULE structure, and returns a pointer to it at the address specified by the third argument.

After the module is successfully loaded, you call cdiSetupChannels to allocate the number of sound channels that you want. The first parameter specifies the number of sound devices you want to use This parameter is presently ignored because DSMI only supports one device at a time. The second parameter specifies the number of channels that you want This parameter must be between 0 and 32. In this code, the number of channels that the module uses is passed to the CDI layer.

You then call ampPlayModule with the MODULE structure as its argument and the playback options In this example, the playback options are set to 0 because we do not want the module to loop. A value of PM_LOOP can be used for looping.

Here is the function that you call at the end of the program, after the music finishes playing or the user interrupts it.

void stopmodule()
{
 ampStopModule();// stop the module
 ampFreeModule(mod);// free the module memory
 closeDSMI();// close DSMI sound system
};

Next is the main() function of the program

int main(int argc,char*argv[])
{
 char ch=0;
 puts( Simple Module Player 1 0 for OS/2, based on DSMI 3 1 n );
 puts( (c) Julien Pierre, June 1996 n );
 if (argc!=2)
  quit( Syntax SMP module-filename n ,1);
 init(); // initialize DSMI
 module(argv[1]); // play the module file
 puts( Press ESC to stop module n );
 do {
  DosSleep(1);
  if (kbhit())
  ch = getch();
 } while( (ch != 27)// wait for the escape key
 (ampGetModuleStatus() MD_PLAYING) );
 // or until the module is finished playing
 stopmodule();// stop the module
 return 0;
};

The program takes the module file name as its argument. If the number of arguments is correct, it tries to initialize DSMI and load the file. It then loops until the user presses the escape key, or until the module finishes playing. The loop uses the ampGetModuleStatus() call in AMP to know when playback is over.

Although Simple Module Player has a very basic user interface, it provides a good program example. If you want more control and a flashier interface, get Dual Module Player for Presentation Manager in DSMI\PROGRAMS\DMPPM.

You can direct any questions about DSMI to Julien Pierre by e-mail at madbrain.netsrq.com or through the Web site at http://www.netsrq.com/~madbrain Julien often is on the Internet Relay Chat (IRDC) as madbrain on channel #os2prog and is very active in the comp.os.os2.programmer* news groups.

Footnote

  • Please refer to the articles by Linden deCarmo in The Developer Connection News Volumes 8 and 9 to learn more about Direct Audio Real Time (DART). These articles can be found online on your DevCon for OS/2 CD.

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation