Jump to content

MMAPG - Amplifier-Mixer Device

From EDM2
Multimedia Application Programming Guide
  1. Introduction
  2. What's New...
  3. Multimedia Application Programming Environment
  4. Media Control Interface
  5. Multimedia Logical Devices
  6. Amplifier-Mixer Device
  7. Waveform Audio Device
  8. Sequencer Device
  9. CD Audio Device
  10. CD-XA Device
  11. Videodisc Device
  12. Digital Video Device
  13. Direct Interface Video Extensions (DIVE)
  14. Captioning
  15. OS/2 Multimedia Controls
  16. Multimedia I/O File Services
  17. Resource Interchange File Format (RIFF) Services
  18. Sample Application Programs
  19. Installing a Program Using MINSTALL
  20. Caption DLL
  21. High-Level Service API
  22. Notices
  23. Glossary

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

The OS/2 amplifier-mixer (ampmix) device is similar to a home stereo amplifier-mixer. Components are plugged into the amplifier-mixer so that audio signals can be transferred to a pair of attached speakers, headphones, or perhaps another device. A comparable example of connecting to another device is playing an old phonograph record, and recording the sound on a new DAT (Digital Audio Tape) deck. The ampmix is the center of all audio signals and provides input or output switching and sound shaping services such as volume, treble, or bass control.

The logical ampmix device in OS/2 supports both analog and digital devices. Other OS/2 multimedia logical devices may be connected to the ampmix device. Similar to the previous example, the CD audio logical device could provide an analog input to the ampmix device, which could then be recorded by the digital waveform audio device.

Both a logical ampmix device and the audio adapter performs all the functions surfaced by the ampmix device. Two important points are the speaker and amp stream connectors.

Although there is actually no visible speaker jack on the back of the audio card, it is a convenient fiction for an application to view the PC internal speaker as another set of speakers that might be plugged into the back of the audio card. Using the previously defined concept of a connector, an application can view all flows of information into and out of the ampmix device in a similar fashion. Selecting the internal speaker as opposed to the external speakers may require the ampmix device to issue a completely different set of instructions to the actual hardware device. The application, however, remains completely device independent.

The other features of the ampmix device are provided by either issuing commands to the hardware device, or emulating in software. One example of software emulation is the support of changing the volume over a period of time, or fade in/fade out. The audio card may only support setting the volume to a particular value, however the ampmix device can send a series of values to achieve the fade effect.

The Amp Stream Connector

The amp stream connector represents the flow of digital information to and from the ampmix device. Again similar to home stereo amplifier-mixers, the ampmix device by itself is not especially notable until another device is attached. Information is transferred from the device and played back on a pair of attached speakers. Note that the ampmix device is a conduit of information, and relies on another device to provide the flow of information. Therefore, commands for the transport of information (such as play, seek, or stop), are sent to the attached device. Commands for transforming the information (such as treble or bass) are sent directly to the ampmix device.

As a nicety for applications, the attached device will provide volume control, so that the application need not provide ampmix functions unless some advanced audio functions are required. The volume command is transparently routed to the attached ampmix device. If the application needs to talk directly to the ampmix device, the value of the stream connector may be queried using the MCI_CONNECTION message, which returns a device context connection. If the string interface is being used, an alias can be established for the connected device. Ampmix commands may then be sent directly to the ampmix device.

Some devices also provide a connector service, which also alleviates the need to talk directly to the ampmix device for frequently requested function. An example of this is the waveaudio device, which attempts to process requests for speakers and several other connector types. If the service is available from the associated ampmix device it is routed; otherwise, the function fails. The connectors and connector services provided by each OS/2 multimedia logical device are discussed in the section for that device.

Sharing the Amplifier-Mixer Device

Because many components of OS/2 multimedia utilize the amplifier-mixer device, it is typically opened shareable so that several devices can use the ampmix device simultaneously, or serially, in an application-window-focus driven sharing scheme. The Media Device Manager (MDM) is responsible for allocating the resources of the underlying hardware correctly and informs an application with the MM_MCIPASSDEVICE message whenever use of the ampmix device is gained or lost.

When other media devices in an application use the ampmix device, the amplifier-mixer becomes a source of contention, depending on the capabilities of the underlying audio adapter. For example, the IBM M-Audio adapter supports the simultaneous playback of two mono 22 kHz PCM waveforms. However, if a third waveform is started, one of the previous two waveforms must be suspended. The application that opened the waveform audio (waveaudio) device receives a MM_MCIPASSDEVICE message with an event of MCI_LOSING_USE. Following completion of the third waveform, the second waveform is automatically restored and can then play to completion. See Device Sharing By Applications for information on device sharing.

The OS/2 multimedia system manages all device sharing, and informs the application when the device is temporarily unavailable.

Audio Attributes

The OS/2 ampmix device provides the following control of audio signals. Current values of audio attributes are retrieved using the MCI_STATUS message. New values are set using the MCI_SET command.

Support of these features can vary by manufacturer. Some companies may develop ampmix devices for use in OS/2 multimedia that provide additional capabilities. To determine whether the device supports a feature, use the MCI_GETDEVCAPS message. If the feature is not supported, MCIERR_UNSUPPORTED_FLAG is returned.

  • volume Sets mixer-channel volume level as a percentage of the maximum achievable effect. Volume of the left and right channels for stereo signals can be set simultaneously using the keyword all, or independently using keywords left and right. The keyword over can be added to fade the volume in or out over a specified period of time.
  • treble Sets treble as a percentage of the maximum achievable effect. The effect applies to the final output mix. Any specification of a channel is ignored.
  • bass Sets bass as a percentage of the maximum achievable effect. The effect applies to the final output mix. Any specification of a channel is ignored.
  • balance Sets balance. Zero is full left balance, 100 is full right balance. The effect applies to the final output mix. Any specification of a channel is ignored.
  • pitch Sets pitch as a percentage of the maximum achievable effect. The effect applies to the final output mix. Any specification of a channel is ignored.
  • gain Sets gain as a percentage of the maximum achievable effect for the currently selected input.
  • monitor Sets monitor on or off. This feature controls whether or not the signal from an input device is heard when it is routed to another device for recording.
  • mute Sets mute on or off.
  • loudness Sets loudness as a percentage of the maximum achievable effect.
  • mid Sets mid as a percentage of the maximum achievable effect.
  • reverb Sets reverb as a percentage of the maximum achievable effect.
  • auto level control Sets auto-level control (ALC) as a percentage of the maximum achievable effect.
  • chorus Sets chorus as a percentage of the maximum achievable effect.
  • crossover Sets crossover as a percentage of the maximum achievable effect.
  • custom1 Sets first custom effect as a percentage of the maximum achievable effect.
  • custom2 Sets second custom effect as a percentage of the maximum achievable effect.
  • custom3 Sets third custom effect as a percentage of the maximum achievable effect.
  • stereoenhance Sets stereo enhancement as a percentage of the maximum achievable effect.

Amp Mixer Connectors

The following connectors are typically supported by ampmix devices:

  • MCI_AMP_STREAM_CONNECTOR
  • MCI_HEADPHONES_CONNECTOR
  • MCI_LINE_IN_CONNECTOR
  • MCI_LINE_OUT_CONNECTOR
  • MCI_MICROPHONE_CONNECTOR
  • MCI_SPEAKERS_CONNECTOR

The number and type of connectors supported by an audio device varies by manufacturer. To determine which connectors are supported, an application can issue the MCI_CONNECTORINFO message.

The ampmix device provides audio attribute control for individual connectors. An application can set an audio attribute for a connector with MCI_SET by specifying the connector in the ulValue field of the MCI_AMP_SET_PARMS structure. If ulValue contains MCI_AMP_STREAM_CONNECTOR, the setting affects the global output of the device.

An application can query the capabilities of a connector by using the MCI_GETDEVCAPS_EXTENDED message in combination with the MCI_MIXER_LINE flag in the ulExtended field of the MCI_AMP_GETDEVCAPS_PARMS structure. The ulAttribute field contains the audio attribute, and the ulValue field contains the connector whose capabilities you are querying.

The following example illustrates how an application can determine whether it can set the volume for a particular connector.

ULONG                     rc;            /* Return code */
MCI_AMP_GETDEVCAPS_PARMS  mciAmpCaps;    /* Ampmix caps */
USHORT                    usDeviceID;    /* Device ID   */

/* Test mixer support for volume changes on the microphone */

mciAmpCaps.ulValue      = MCI_MICROPHONE_CONNECTOR;
mciAmpCaps.ulAttribute = MCI_AMP_CAN_SET_VOLUME;
mciAmpCaps.ulExtended  = MCI_MIXER_LINE;

rc = mciSendCommand(usDeviceID,
                    MCI_GETDEVCAPS,
                    MCI_WAIT|MCI_GETDEVCAPS_EXTENDED,
                    (ULONG) &mciAmpCaps,
                    0);

Synchronizing Audio Attribute Settings

In an organized multimedia environment applications must be able to synchronize their audio settings with other applications. Whenever a user makes changes to an application's audio settings, other applications need to be informed of the changes so they can update their settings to conform to the changes made by the user.

An application can request notification about mixer events by sending an MCI_MIXNOTIFY message with MCI_MIXNOTIFY_ON specified. The application will then receive an MM_MCIEVENT message whenever a mixer attribute is changed, or a connector is enabled or disabled. When the system passes the MM_MCIEVENT message, the usEventCode field of the MsgParam1 parameter contains MM_MIXEVENT, and the MsgParam2 parameter contains a pointer to MCI_MIXEVENT_PARMS.

Attention: To avoid creating a terminal loop, the application must not set an audio attribute while processing the MM_MCIEVENT message.

typedef struct_MCI_MIXEVENT_PARMS {

  ULONG   ulLength;         /* Length of structure             */
  HWND    hwndMixer;        /* Window to receive mixer changes */
  ULONG   ulFlags;          /* Either MCI_MIX_ATTRIBUTE
                               or MCI_MIX_CONNECTOR            */
  USHORT  usDeviceID;       /* Device ID to notify of change   */
  ULONG   ulDeviceType;     /* Device type that caused change  */
  ULONG   ulDeviceOrdinal;  /* Ordinal of device type          */
  ULONG   ulAttribute;      /* Attribute that changed          */
  ULONG   ulValue;          /* New value of changed attribute  */
  ULONG   ulConnectorType;  /* Connector type                  */
  ULONG   ulConnectorIndex; /* Connector index                 */
  ULONG   ulConnStatus;     /* Connector enabled/disabled      */

} MCI_MIXEVENT_PARMS;

typedef MCI_MIXEVENT_PARMS *PMCI_MIXEVENT_PARMS;

The ulFlags field contains one of the following values:

  • MIX_ATTRIBUTE
  • MIX_CONNECTOR

If the mixer event is the changing of an attribute, ulAttribute, ulDeviceType, and ulValue fields are valid.

If the mixer event is the changing of a connector, ulConnectorType, ulConnectorIndex, and ulConnStatus fields are valid.

The following example illustrates how an application can set up notification for every audio attribute change.

MCI_GENERIC_PARMS mixevent;

mixevent.hwndCallback = hwndmixer;

if (hMixer)
   {
   mciSendCommand(hMixer,
                  MCI_MIXNOTIFY,
                  MCI_WAIT | MCI_MIXNOTIFY_ON
                  (PVOID) &mixevent,
                  0);

When MCI_MIXNOTIFY_ON is specified, the hwndCallback field of MCI_GENERIC_PARMS must contain a valid window handle.

Direct Audio RouTines (DART) Interface

OS/2 multimedia provides application developers with a number of choices for playing and recording audio files. They can:

  • Open the waveaudio device
  • Open a memory playlist and use operation codes
  • Use the direct audio interface of the ampmix device

The waveaudio device provides an easy-to-use interface that works well for applications with simple audio requirements. However time-critical game applications usually demand a faster response time than the waveaudio device can achieve, because of the device-independent layers of software between it and the audio device.

The memory playlist method of audio playback and recording reduces some of the system overhead that the waveaudio device incurs, because the playlist can stream audio data directly from application memory buffers to audio device buffers. But the playlist is not the ideal solution when precise buffer flow control is needed.

The Direct Audio RouTines (DART) interface for the ampmix device enables games and multimedia applications to bypass the waveaudio device entirely and communicate directly with the amp mixer. Using this interface, applications get the high-speed audio response they require, while remaining compatible with existing OS/2 multimedia applications. Because DART uses the media control interface (MCI), applications using DART can share the audio device with other applications simply by processing the MM_MCIPASSDEVICE message.

DART offers the following advantages:

  • Preallocation of user memory
  • No threads
  • Function pointers instead of API
  • Time-critical notifications
  • Tailorable number of buffers

Using the DART Interface

To read and write audio data directly to the mixer device using the DART interface, the application does the following:

  1. Opens the mixer device with MCI_OPEN
  2. Initializes the mixer to use DART with MCI_MIX_SETUP
  3. Allocates memory buffers with MCI_BUFFER
  4. Uses function pointers to read and write data from the audio device
  5. Uses MCI_STATUS_POSITION for precision timing
  6. Controls data transfer with MCI_PAUSE, MCI_RESUME, MCI_STOP
  7. Deallocates memory buffers with MCI_BUFFER
  8. Closes the mixer device with MCI_CLOSE

Setting Up the Mixer

The application sends the MCI_MIX_SETUP message to the amp mixer to initialize the device for direct reading and writing of audio data in the correct mode and format—for example, PCM, MIDI, or MPEG audio.

If waveform audio data will be played or recorded, the application fills in the ulDeviceType field with MCI_DEVICETYPE_WAVEFORM_AUDIO. It must also provide values for the following digital-audio-specific fields: format tag, bits per sample, number of samples per second, and number of channels.

If MIDI data will be played, the application fills in the ulDeviceType field with a value of MCI_DEVICETYPE_SEQUENCER and places zeroes in the format specifications fields.

typedef struct_MCI_MIXSETUP_PARMS
  {
  HWND        hwndCallback;    /* IN  Window for notifications */
  ULONG       ulBitsPerSample; /* IN  Number of bits per sample */
  ULONG       ulFormatTag;     /* IN  Format tag */
  ULONG       ulSamplesPerSec  /* IN  Sampling rate */
  ULONG       ulChannels;      /* IN  Number of channels */
  ULONG       ulFormatMode;    /* IN  MCI_RECORD or MCI_PLAY */
  ULONG       ulDeviceType;    /* IN  MCI_DEVTYPE */
  ULONG       ulMixHandle;     /* OUT Read/Write handle */
  PMIXERPROC  pmixWrite;       /* OUT Write routine entry point */
  PMIXERPROC  pmixRead;        /* OUT Read routine entry point */
  PMIXEREVENT pmixEvent;       /* IN  Event routine entry point */
  PVOID       pExtendedInfo;   /* IN  Media-specific info */
  ULONG       ulBufferSize;    /* OUT Recommended buffer size */
  ULONG       ulNumBuffers;    /* OUT Recommended num buffers */
  } MCI_MIXSETUP_PARMS;

The application must also fill in the pmixEvent field of the MCI_MIXSETUP_PARMS structure with a function pointer for the mixer to use for event notification, such as a full buffer, empty buffer, or an error condition. If the call to the mixer is successful, it returns two function pointers to the application—one for reading data (mixRead) and the other for writing data (mixWrite) to the audio device.

An application can use the MCI_MIXSETUP_QUERYMODE flag to query a device to see if a particular mode is supported. The following example illustrates using MCI_MIXSETUP to query and prepare the audio device for playing 16-bit, 22050 KHz stereo mode.

   // MCI_MIXSETUP informs the mixer device of the entry point
   // to report buffers being read or written.
   // We will also need to tell the mixer which media type
   // we will be streaming.  In this case, we'll use
   // MCI_DEVTYPE_WAVEFORM_AUDIO.

    memset( &MixSetupParms, '\0', sizeof( MCI_MIXSETUP_PARMS ) );

   MixSetupParms.ulBitsPerSample = 16;
   MixSetupParms.ulFormatTag     = MCI_WAVE_FORMAT_PCM;
   MixSetupParms.ulSamplesPerSec = 22050;
   MixSetupParms.ulChannels = 2;    /* Stereo */
   MixSetupParms.ulFormatMode = MCI_PLAY;
   MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;


    rc = mciSendCommand( usDeviceID,
                         MCI_MIXSETUP,
                         MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
                         ( PVOID ) &MixSetupParms,
                         0 );


    if ( ULONG_LOWD( rc ) != MCIERR_SUCCESS )
           {
           CHAR  szError[255];
             // The device can't handle this format
             // get an English error message for the caller.
           mciGetErrorString( ULONG_LOWD( rc ), szError, 255 );
           printf('Can't play because of %s', szError );
           exit( 1 );
           }

   // The mixer will inform us of entry points to
   // read/write buffers to and also give us a
   // handle to use with these entry points.

   MixSetupParms.pmixEvent = MyEvent;


    rc = mciSendCommand( usDeviceID,
                         MCI_MIXSETUP,
                         MCI_WAIT | MCI_MIXSETUP_INIT,
                         ( PVOID ) &MixSetupParms,
                         0 );

Allocating Memory Buffers

After the mixer device is set up to use DART, the application instructs the device to allocate memory by sending the MCI_BUFFER message with the MCI_ALLOCATE_MEMORY flag set. The application uses the MCI_BUFFER_PARMS structure to specify the number of buffers it wants and the size to be used for each buffer.

Note: Because of device driver restrictions, buffers are limited to 64KB on Intel-based systems. No such limit exists on PowerPC systems.

The pBufList field contains a pointer to an array of MCI_MIX_BUFFER structures where the allocated information is to be returned.

typedef struct_MCI_BUFFER_PARMS {
  HWND   hwndCallback;    /* Window for notifications */
  ULONG  ulStructLength;  /* Length of MCI_BUFFER_PARMS */
  ULONG  ulNumBuffers;    /* Number of buffers to allocate (IN/OUT)*/
  ULONG  ulBufferSize;    /* Size of buffers mixer should use */
  ULONG  ulMintoStart;    /* Unused */
  ULONG  ulSrcStart;      /* Unused */
  ULONG  ulTgtStart;      /* Unused */
  PVOID  pBufList;        /* Pointer to array of buffers */
} MCI_BUFFER_PARMS;

typedef MCI_BUFFER_PARMS *PMCI_BUFFER_PARMS;

The following example illustrates using MCI_BUFFER to allocate memory.

   MCI_MIX_BUFFER   MyBuffers[ MAX_BUFFERS ];

    BufferParms.ulNumBuffers = 40;
    BufferParms.ulBufferSize = 4096;
    BufferParms.pBufList = MyBuffers;

     rc = mciSendCommand( usDeviceID,
                   MCI_BUFFER,
                   MCI_WAIT | MCI_ALLOCATE_MEMORY,
                   ( PVOID ) &BufferParms,
                   0 );

     if ( ULONG_LOWD( rc) != MCIERR_SUCCESS )
        {
        printf( 'Error allocating memory.  rc is : %d', rc );
        exit ( 1 );
        }

     // MCI driver will return the number of buffers it
     // was able to allocate
     // it will also return the size of the information
     // allocated with each buffer.

     ulNumBuffers = BufferParms.ulNumBuffers;

     for ( ulLoop = 0; ulLoop < ulNumBuffers; ulLoop++ )
        {
        rc = mmioRead ( hmmio,
                       MyBuffers[ ulLoop ].pBuffer,
                       MyBuffers[ ulLoop ].ulBufferLength);

        if ( !rc )
           {
           exit( rc );
           }
        MyBuffers[ ulLoop ].ulUserParm = ulLoop;

        }

Reading and Writing Data

The MCI_MIX_BUFFER structure is used for reading and writing data to and from the mixer.

Once the device is set up and memory has been allocated, the application can use the function pointers obtained during MCI_MIXSETUP to communicate with the mixer. During a playback operation, the application fills the buffers with audio data and then writes the buffers to the mixer device using the pmixWrite entry point. When audio data is being recorded, the mixer device fills the buffers using the pmixRead entry point. Each buffer returned the the application has a time stamp (in milliseconds) attached so the program can determine the current time of the device.

typedef struct_MCI_MIX_BUFFER {
  ULONG  ulStructLength;  /* Length of the structure */
  ULONG  pBuffer;          /* Pointer to a buffer */
  ULONG  ulBufferLength;  /* Length of the buffer */
  ULONG  ulFlags;          /* Flags */
  ULONG  ulUserParm;      /* User buffer parameter */
  ULONG  ulTime;          /* Device time in milliseconds */
  ULONG  ulReserved1;      /* Unused */
  ULONG  ulReserved2;      /* Unused */
  } MCI_MIX_BUFFER;
typedef MCI_MIX_BUFFER *PMCI_MIX_BUFFER

MCI_STOP, MCI_PAUSE, and MCI_RESUME are used to stop, pause, or resume the audio device, respectively. MCI_STOP and MCI_PAUSE can only be sent to the mixer device after mixRead and mixWrite have been called. MCI_RESUME will only work after MCI_PAUSE has been sent.

Note: After your application has completed data transfers, issue MCI_STOP to avoid a pause the next time the mixer device is started.

If your application needs more precise timing information than provided by the time stamp returned with each buffer, you can use MCI_STATUS with the MCI_STATUS_POSITION flag to retrieve the current time of the device in MMTIME units.

Master Volume and the Ampmix Device

The maximum volume level of all logical devices in the system are controlled by the Volume Control application supplied with OS/2 multimedia. The Volume Control application sets a scale by which all subsequent volume commands to the ampmix device are based. For instance, if the Volume Control sets the master volume at 50%, then all volume levels are cut in half.

Some devices may only support two levels of volume (on/off). These devices are off when the master volume is set to zero, and on at any other value.

Note: While the MCI_MASTERAUDIO message can be sent by any application, only the master volume application or a replacement should utilize this message to set the master volume. Master volume should only be controlled at the discretion of an end user as implemented in the Volume Control application. A parameter of the MCI_MASTERAUDIO message allows an application or a media driver to query the master volume level.

Amplifier-Mixer Command Messages

Message Description
MCI_BUFFER Allocates or deallocates memory for use with the audio device.
MCI_CONNECTOR Enables, disables, or queries the status of a connector on a device.
MCI_CLOSE Closes the amp mixer instance.
MCI_GETDEVCAPS Gets device capabilities.
MCI_INFO Gets device information.
MCI_MIX_SETUP Sets up the device in the correct mode (for example, PCM, MPEG audio, or MIDI).
MCI_OPEN Opens an instance of the amp mixer.
MCI_PAUSE Pauses playback or recording.
MCI_RESUME Resumes playback or recording.
MCI_SET Sets audio attributes using the MCI_AMP_SET_PARMS structure. For a list of supported attributes, see Audio Attributes.
MCI_STATUS Gets device status.
MCI_STOP Stops playback or recording.

M-Audio Adapter Specifics


  1. The speakers (1) connector is the external speakers jack on the back of the card. The speakers (2) connector is really the internal PC speaker.
  2. The Line OUT and speakers (1) connectors can be enabled or disabled by the ampmix device, although the adapter is incapable of actually switching the output. The ampmix device does report that the connector is actually enabled or disabled.
  3. The speakers(2) connector can be enabled or disabled, resulting in the PC internal speaker being turned on or off.
  4. The microphone and line IN connectors are mutually exclusive. Enabling one connector automatically disables the other. Disabling both connectors automatically enables the microphone.
  5. The amp stream connector represents the transfer of digital audio information to and from the M-Audio card. This connector is always enabled.
  6. The M-Audio adapter does not support independent control of volume for the left and right channels of a stereo signal. Any device connected to an M-Audio amplifier-mixer device returns MCIERR_UNSUPPORTED_FLAG if an attempt is made to independently control the volume of the left and right channels with the MCI_SET command.