The WaveMix DLL

Skip to main content (skip navigation menu)






The WaveMix DLL

 

This is release 1.80 of WaveMix, a slightly updated version of the DLL that Microsoft distributed on its FTP and WWW servers (which was version 1.50). This documentation is valid for both version 1.50 and version 1.80. The extensions that are only valid for 1.80 (or incompatibilities between the versions 1.50 and 1.80) are noted in the text.

The bulk of the this text comes from the WaveMix documentation, which was marked up, and partially rewritten to clear up confusing areas. I have tried to verify whether documented error return values were really returned and whether the flags, options and limitations that the original document mention were (still) accurate. I do not claim that this document is now without errors, but I do think that it is better than the version that you may already have.

Why WaveMix at all now that DirectSound exists? For simple programs that need simple sound mixing (without strict low-latency requirements), WaveMix worked fairly well and it is simple to install with a program. WaveMix is easy to use for a programmer, because it directly supports standard .WAV files. In educational areas you still find a lot of machines that run only Windows 3.1x, so the availability of a 16-bit solution can also be an advantage of WaveMix.

WaveMix is infamous for its high latency. In my experience, the latency is acceptable if the "Remix" setting is 1. Latency is indeed high for sound card that require the setting "Remix=2", most sound cards do not require this.

WaveMix uses the standard sound drivers, it is not dependent of special drivers. The advantage is that it works directly with any version of Windows, starting from 3.1 and with virtually any sound card. The disadvantage is that a lot of lousy sound drivers have been written. Indeed, the real trouble with WaveMix, as I see it, is that WaveMix needs to be configured to a sound card/driver. Read more about this in a separate section.

Version 1.80 of WaveMix has not been widely spread. (In fact, this may be the only distribution of it right now.) The new installation rules for WaveMix encourage you to install the WaveMix DLL and the configuration file in the application's subdirectory and not in the Windows "system" directory. Both the filename and the (internal) module name of version 1.80 are different from earlier releases, as to avoid conflicts with earlier WaveMix versions that may already be present on the system.

In closing, you are invited to download the WaveMix toolkit version 1.80 (404 kBytes) and use it for whatever task you wish. You may also wish to download MixCfg, a configuration utility, or read more about configuring WaveMix.

The WaveMix DLL is a utility that allows multiple WAV files to be played simultaneously. It is designed to be as simple to use as possible but still have the power to do what is required by games. The DLL supports 8 channels of simultaneous wave play, the ability to queue up waves along the same channel and wave completion notification.

The WaveMix DLL currently supports 11.025 kHz, 22.05 kHz and 44.1 kHz sampling rates, mono and stereo, 8-bit PCM resolution. The output sampling rate is fixed when WaveMix is initialized. The default sampling rate is set in WAVEMIX.INI. See WAVEMIX.INI for details.

There are now two versions of WaveMix: WAVMIX16.DLL (16 bit 80x86 code) and WAVMIX32.DLL (32 bit 80x86 code). WaveMix versions up to version 1.72 contained code to make certain that the DLL was installed in the Windows system directory. This release (1.80) does not have that restriction.

WaveMix uses the configuration file WAVEMIX.INI. This file must either be present in the same directory as the WaveMix DLL (WaveMix looks there first) or in the windows directory. If not found, WaveMix will use default settings. (Note: WaveMix versions before 1.80 require that the file WAVEMIX.INI exists and that it resides in the Windows' directory). You can tweak WAVEMIX.INI to get the best performance with your sound driver.

When installing WaveMix, your setup program should check the version stamp on the DLL to prevent overwriting WaveMix with an older version.

WaveMix was originally developed by Angel M. Diaz, Jr., SDE Microsoft Corporation.

 

Downloads

 

Using WaveMix

In order to play a file a program must:

  1. Initialize the DLL with WaveMixInit() or WaveMixConfigureInit()
  2. Open a wave file with WaveMixOpenWave()
  3. Open a channel with WaveMixOpenChannel()
  4. Call WaveMixActivate(hMixSession,TRUE) to tell WaveMix to actually get the wave output device (see note 1 below)
  5. Play the file using WaveMixPlay()

A given channel can be silenced by calling WaveMixFlushChannel() at any time. When the program is done with playing sounds, it should:

  1. Close the open channels using WaveMixCloseChannel()
  2. Free the memory for the waves using WaveMixFreeWave()
  3. End the session with WaveMixCloseSession()
Note 1:
An application should call WaveMixActivate(hMixSession,FALSE) when it loses the focus so that the waveform device can be freed for other applications. When focus is regained the application can call WaveMixActivate(hMixSession,TRUE).
Note 2:
An application that does not release the processor can cause the sound to "skip" To avoid this it can call WaveMixPump() frequently.
Note 3:
Setting ShowDevices=1 in WAVEMIX.INI enables error message box support in the WaveMix DLL. This is especially useful if WaveMixOpenWave() fails.

Typical mixing strategies

There are two typical ways that programs use to mix sounds with WaveMix. This section describes them on a fairly high level.

One option is to divide all sounds in up to 8 specific categories, and then play each of the categories on a specific channel. For example, background sounds go to channel 0, sounds invoked by a mouse click on channel 1 and sounds invoked by a keyboard action on channel 2. When there is a keyboard action and a sound is already playing on channel 2, the existing sound on channel 2 is pre-empted. In the parameters to WaveMixPlay() you specify the flags WMIX_CLEARQUEUE and WMIX_HIGHPRIORITY, and you set the channel number. This is the strategy that MixCfg uses.

In a billiard ball demo for AniSprite, there are three balls that bounce against the edges of the billiard table and against each other. A ball bouncing against the edge of the table gives a different sound than a ball that hits another ball. However, using one channel for each kind of sound didn't work well. Two balls could hit the edge of a table almost simulatenously, and it was annoying to clearly hear the first bounce be pre-empted by the second bounce. These two "bounce" sounds should mix.

A quick fix would be to have a channel per ball: play the sounds for the first ball on channel 0, those for the second ball on channel 1 and the third ball's sounds go to channel 3. If two balls hit each other, you have to decide which of the two balls you let produce the sound. There is a slight complication, however: it occurs fairly frequently that a ball that moves slowly along an edge of the billiard table gets hit by another fast moving ball. Due to the impact, the (first) ball moves towards the edge of the table, bounces off it and hits the other ball again. Now, that is three sounds, and just two balls. The "one channel per ball" strategy does not work well in this case.

An alternative strategy for a program is to open just the number of channels for the maximum number of sounds that it expects to play simultaneously. In the above example, of the billiard table simulation, I set it to three. Then, whenever a sound must play, the program lets WaveMix choose a channel number which is vacant and, when all three channels are filled, it lets WaveMix clear the channel that plays the "oldest" sound. In the call to WaveMixPlay(), the flags WMIX_USELRUCHANNEL, WMIX_CLEARQUEUE and WMIX_HIGHPRIORITY, are all set and the channel number holds just a dummy value (typically, you set it to 0).

 
activate/inactivate WaveMix
WaveMixActivate

This function should be called when an application loses the focus or otherwise becomes inactive. This will permit other applications to acquire the wave output device. Calling this function keeps all the channels open.

Syntax:
UINT WaveMixActivate(HANDLE hMixSession, BOOL fActivate)
hMixSession
Handle that was returned by WaveMixInit().
fActivate
If TRUE, the application is being activated and wishes to regain the wave output device. If FALSE, the application is not active and wishes to be a good Windows Citizen and allow other applications to use the wave output device.
Returns:
Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
MMSYSERR_ALLOCATED
Specified resource is already allocated to a process. try again later.
MMSYSERR_NOMEM
Unable to allocate or lock memory.
MMSYSERR_INVALHANDLE
The hMixSession passed in was invalid
MMSYSERR_ERROR
an internal error
Notes:
The WaveMix DLL will keep sounds that were queued while the application was active in the queue. When WaveMix play is subsequently reactivated the sounds will continue from where they were. (there will be a small amount of data loss equivalent to the amount of data that was buffered in the wave driver).

Calls to WaveMixPlay() that are made while the application is not active will not be queued.

The Application can call WaveMixFlush() if it does not wish the data to be pending while it is inactive.

close a single channel
WaveMixCloseChannel

WaveMixCloseChannel() empties the queue of any waves waiting to play on the specified channel and then closes the channel.

Syntax:
UINT WaveMixCloseChannel(HANDLE hMixSession, int iChannel, DWORD dwFlags)
hMixSession
Handle that was returned by WaveMixInit().
iChannel
An integer which specifies a previously opened channel.
dwFlags
Options, zero or more of the following flags:
WMIX_ALL
Causes all the channels to be closed; iChannel is ignored.
WMIX_NOREMIX
Prevents the currently submitted blocks from being remixed to exclude the removed channel.
Returns:
Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
MMSYSERR_INVALHANDLE
The specified channel has not been opened.
MMSYSERR_INVALPARAM
One of the parameters passed to the function was out of range or invalid.
close WaveMix
WaveMixCloseSession

WaveMixCloseSession() pairs up with WaveMixInit(). It should be called before the application terminates.

Syntax:
UINT WaveMixCloseSession(HANDLE hMixSession)
hMixSession
Handle that was returned by WaveMixInit().
Returns:
Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
MMSYSERR_INVALHANDLE
The specified channel has not been opened.
initialize with specific settings
WaveMixConfigureInit

WaveMixConfigureInit initializes the WaveMix DLL with custom settings. Applications that require more control over the DLL should call this function instead of WaveMixInit(). It allows the application to specify if the DLL should support stereo output and allows the application to specify the sampling rate.

Syntax:
HANDLE WaveMixConfigureInit(LPMIXCONFIG lpConfig)
lpConfig
A pointer to a MIXCONFIG structure with the custom settings (see below).
Returns:
A valid handle, or NULL on failure.
Notes:
The MIXCONFIG structure has the following fields:
wSize
(WORD) should be set to sizeof(MIXCONFIG) before calling this function.
dwFlags
(DWORD) this parameter is the bitwise OR of the remaining fields in the structure that are valid. It must be a combination of the following flags:
  • WMIX_CONFIG_CHANNELS (0x0001)
  • WMIX_CONFIG_SAMPLINGRATE (0x0002)
  • wChannels
    (WORD) The number of output channels to use; 1 = MONO, 2 = STEREO. The flag WMIX_CONFIG_CHANNELS must be set in dwFlags to use this parameter.
    wSamplingRate
    (WORD) The output sampling rate you want to play at. This parameter can be set to 11, 22, or 44. The flag WMIX_CONFIG_SAMPLINGRATE must be set in dwFlags to use this parameter.

    If you initialize WaveMix with a sampling frequency that is higher than the frequency set in the configuration file (WAVEMIX.INI), the other parameters in the configuration file may not be adequate anymore; especially the WaveBlocks setting must often be adjusted. Consequently, you are advised to only configure WaveMix to lower sampling rates than the one(s) set in the configuration file.

    This function is a replacement to WaveMixInit(), only one of them should be called.

    convert a WAVE format
    WaveMixConvert

    WaveMixConvert() converts the input "WAVE" file to the format that WaveMix requires. WaveMix calls this function internally, so it is only necessary after opening a "raw samples" block (see WaveMixOpenWave() with the flag WMIX_RAWDATA). WaveMixConvert() is a new function for version 1.80.

    Syntax:
    UINT WaveMixConvert(HANDLE hMixSession, LPMIXWAVE lpMixWave, LPWAVEFORMAT lpFmt)
    hMixSession
    Handle that was returned by WaveMixInit().
    lpMixWave
    A pointer that was created by WaveMixOpenWave().
    lpFmt
    A pointer to the WAVEFORMAT structure that describes the format of the samples that lpMixWave holds.
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    An invalid handle hMixSession was specified.
    MMSYSERR_NOMEM
    Insufficient memory.
    Notes:
    The input (raw) samples should be in 11025 Hz, 22050 Hz or 44100 Hz, 8-bit or 16-bit PCM, mono or stereo. The converted sample data replaces the data in lpMixWave.
    silence and empty a channel
    WaveMixFlushChannel

    WaveMixFlushChannel() empties the queue of any waves waiting to play on the specified channel.

    Syntax:
    UINT WaveMixFlushChannel(HANDLE hMixSession, int iChannel, DWORD dwFlags)
    hMixSession
    Handle that was returned by WaveMixInit().
    iChannel
    An integer which specifies a previously opened channel.
    dwFlags
    Options, zero or more of the following flags:
    WMIX_ALL
    Causes all the channels to be closed; iChannel is ignored.
    WMIX_NOREMIX
    Prevents the currently submitted blocks from being remixed to exclude the removed channel.
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    The specified channel has not been opened.
    MMSYSERR_INVALPARAM
    One of the parameters passed to the function was out of range or invalid.
    free WAVE data
    WaveMixFreeWave

    WaveMixFreeWave() frees the memory allocated for WAVE data .

    Syntax:
    UINT WaveMixFreeWave(HANDLE hMixSession, LPMIXWAVE lpMixWave)
    hMixSession
    Handle that was returned by WaveMixInit().
    lpMixWave
    A pointer that was created by WaveMixOpenWave().
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    The specified channel has not been opened.
    return current settings
    WaveMixGetInfo
    Syntax:
    WORD WaveMixGetInfo(LPWAVEMIXINFO lpWaveMixInfo)
    lpWaveMixInfo
    A pointer to a WAVEMIXINFO structure that will get filled in by the WaveMix DLL (see below).
    Returns:
    Zero will be returned if the size fields match and the structure will be filled in.

    If the size fields do not match the DLL will return the size that it expected. None of the fields in the structure will be filled in.

    Notes:
    The WAVEMIXINFO structure has the following fields:
    wSize
    (WORD) should be set to sizeof(WAVEMIXINFO) before calling WaveMixGetInfo().
    bVersionMajor
    (BYTE) the major version of the DLL. This value is set to "2" for the current release.
    bVersionMinor
    (BYTE) the minor version of the DLL. This value is set to "0" for the current release.

    Note: For unknown reasons, the version returned by WaveMixGetInfo() is "2.0" for this release (version 1.80) and for various earlier releases.

    szDate
    (char [12]) The compilation date of the DLL, a zero terminated string in form "Mmm~dd~yyyy" where Mmm is one of the following: "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec".
    dwFormats
    (DWORD) Specifies which standard formats are supported as input. The supported formats are specified with a logical OR of the flags listed in WAVEOUTCAPS documentation. For version 1 and 2, the following wave formats are supported:
      WAVE_FORMAT_1M08  /* 11.025 kHz, Mono,   8-bit  */
      WAVE_FORMAT_1S08  /* 11.025 kHz, Stereo, 8-bit  */
      WAVE_FORMAT_1M16  /* 11.025 kHz, Mono,   16-bit */
      WAVE_FORMAT_1S16  /* 11.025 kHz, Stereo, 16-bit */
      WAVE_FORMAT_2M08  /* 22.05  kHz, Mono,   8-bit  */
      WAVE_FORMAT_2S08  /* 22.05  kHz, Stereo, 8-bit  */
      WAVE_FORMAT_2M16  /* 22.05  kHz, Mono,   16-bit */
      WAVE_FORMAT_2S16  /* 22.05  kHz, Stereo, 16-bit */
      WAVE_FORMAT_4M08  /* 44.1   kHz, Mono,   8-bit  */
      WAVE_FORMAT_4S08  /* 44.1   kHz, Stereo, 8-bit  */
      WAVE_FORMAT_4M16  /* 44.1   kHz, Mono,   16-bit */
      WAVE_FORMAT_4S16  /* 44.1   kHz, Stereo, 16-bit */

    This is done by converting the wave format of the input wave to the current output format when the wave is opened.

    read a setting
    WaveMixGetProfileInt

    WaveMixGetProfileInt() reads a setting from the configuration file (WAVEMIX.INI).

    Syntax:
    int WaveMixGetProfileInt(LPSTR lpszKey, int iDefValue)
    lpszKey
    the "key" name.
    iDefValue
    the value that is returned when the setting is not present.
    Returns:

    The value of the "key name" under either the specific section for the sound card in the configuration file, or under the "default" section.

    Notes:
    This function first searches the key under the specific sound driver name in the WAVEMIX.INI file. If not found, it searches the key in the "default" section in WAVEMIX.INI. If still not found, it returns iDefValue.
    initialize with default settings
    WaveMixInit

    WaveMixInit() or WaveMixConfigureInit() should be called before any of the other functions. It will return a handle that should be used for subsequent API calls. This function will configure the DLL using default parameters and parameters specified in WAVEMIX.INI.

    Syntax:
    HANDLE WaveMixInit(void)
    Returns:
    A valid handle, or NULL on failure.
    Notes:
    This function sets the output for 1 channel (mono) with the sampling rate noted in WAVEMIX.INI. To set the DLL up with other parameters, use WaveMixConfigureInit() instead.
    read a channel
    WaveMixOpenChannel

    WaveMixOpenChannel() opens one of the eight channels, or several channels at a time.

    Syntax:
    UINT WaveMixOpenChannel(HANDLE hMixSession, int iChannel, DWORD dwFlags)
    hMixSession
    Handle that was returned by WaveMixInit().
    iChannel
    Specifies a number which indicates which channel should be opened. Currently the DLL supports channels 0 though 7.
    dwFlags
    One of the following:
    WMIX_OPENSINGLE
    iChannel specifies the single channel to be opened.
    WMIX_ALL
    All the available channels will be opened; iChannel is ignored.
    WMIX_OPENCOUNT
    iChannel specifies the number of channels to open. For example, if iChannel==4 then channels 0 through 3 will be opened.
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    An invalid handle hMixSession was specified.
    MMSYSERR_INVALFLAG
    An invalid value in dwFlags.
    MMSYSERR_INVALPARAM
    An invalid value in iChannel.
    MMSYSERR_ALLOCATED
    The specified resource is already allocated, or the channel has already been opened.
    MMSYSERR_ERROR
    An internal error.
    Notes:
    It is not necessary to open or close channels in any particular order.
    read a WAVE data block
    WaveMixOpenWave

    WaveMixOpenWave() reads a "wave" data block from a file / a resource / a memory block.

    Syntax:
    LPMIXWAVE WaveMixOpenWave(HANDLE hMixSession, LPSTR szWaveFilename, HINSTANCE hInst, DWORD dwFlags)
    hMixSession
    Handle that was returned by WaveMixInit().
    szWaveFilename
    The name of a wave file to open, the name of a WAVE resource to open or an integer ID of a WAVE resource to open, or a pointer to a memory block with the WAVE data. See the dwFlags parameter for details.
    hInst
    Identifies the instance of the module whose executable file contains the resource. See the dwFlags parameter for details.
    dwFlags
    A combination of the following:
    WMIX_FILE
    If this bit is set then szWaveFileName specifies a far pointer to a string containing the filename of the file to open. hInst is ignored.

    This flag should not be set with WMIX_RESOURCE.

    WMIX_RESOURCE
    If this bit is set then szWaveFileName specifies a WAVE resource to be opened in hInst.

    If the high-order word of the szWaveFileName is zero, the low-order word specifies the integer identifier of the name or type of the given resource. Otherwise, the parameter is a long pointers to a zero-terminated string. If the first character of the string is a pound sign (#), the remaining characters represent a decimal number that specifies the integer identifier of the resource's name or type. For example, the string #258 represents the integer ID 258.

    To embed a wave file in a resource use the following format in your .RC file:

      GameSound WAVE gamesnd.wav

    Note: to reduce the amount of memory required for the resources used by an application, the application should refer to the resources by integer identifier instead of by name.

    WMIX_MEMORY
    If this bit is set then szWaveFileName is expected to be a far pointer to a MMIOINFO struct (see multimedia mmioOpen and MMIOINFO documentation). The parameters should be filled in similar to:
      mmioInfo.pchBuffer=lp;               /* pointer to the memory file */
      mmioInfo.cchBuffer=GlobalSize(hMem); /* size of buffer */
      mmioInfo.fccIOProc=FOURCC_MEM;       /* I/O procedure */

    All other fields should be 0. WaveMixOpenWave() will pass this pointer to mmioOpen in the following manner:

      hmmio = mmioOpen(NULL, (MMIOINFO FAR* )szWaveFilename, MMIO_READ)));

    It will then read the data from hmmio, and convert it for internal use. The original memory will not be modified.

    WMIX_RAWDATA (version 1.80)
    If this bit is set then szWaveFileName must be a pointer to the raw sample data (uncompressed, and without any header). WaveMixOpenWave() assumes that the sample data is already in the format of the current session (sampling frequency, resolution and number of channels), or that you convert it to the correct format by calling WaveMixConvert() explicitly.

    The WMIX_RAWDATA flag is the highest bit in the 32-bit dwFlags parameter. If this bit is set, the lower 31 bits of dwFlags give the size of the sample data block in bytes.

    Returns:
    A pointer to an internal structure, or NULL on failure.
    Notes:
    This function can only handle uncompressed WAVE files (WAVE_FORMAT_PCM).

    For best performance, the input files should have the same sampling rate, number of channels and bit resolution as those with which WaveMix is configured. If the input WAVE file does not match the settings of WaveMix, WaveMixOpenWave() converts the input file (there is an exception for the case that dwFlags contains the bit WMIX_RAWDATA).

    WaveMix can only convert an input files if it is in a supported format (which are the standard Windows formats: 8-bit & 16-bit, mono & stereo, 11025/22050/44100 Hz). Use WaveMixGetInfo() to check whether a format is supported. WaveMixOpenWave() can corrupt data (or even crash) if the format of the input file is unsupported.

    You can query the default configuration of WaveMix with WaveMixGetProfileInt.

    queue audio, play a channe
    WaveMixPlay

    WaveMixPlay() stores audio data in the queue of a channel and starts playback of that channel (if desired).

    Syntax:
    UINT WaveMixPlay(LPMIXPLAYPARAMS lpMixPlayParams)
    lpMixPlayParams
    a pointer to a MIXPLAYPARAMS structure, see below.
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    The specified channel has not been opened or the parameter lpMixWave was invalid.
    MMSYSERR_NOMEM
    WaveMix has run out of internal memory to queue up waves. Wait until some sounds complete and then try again.
    MMSYSERR_ALLOCATED
    The wave out device has not been allocated by this session. This can be done with WaveMixActivate().
    Notes:
    The MIXPLAYPARAMS structure has the following fields:
    wSize
    (WORD) Must be set to sizeof(MIXPLAYPARAMS).
    hMixSession
    (HANDLE) Handle that was returned by WaveMixInit().
    iChannel
    (int) The channel on which the wave should be played. This value is ignored if the WMIX_USELRUCHANNEL flag was set (see below).
    lpMixWave
    (LPMIXWAVE) A wave which was opened using WaveMixOpenWave().
    hWndNotify
    (HWND) a window handle to receive the MM_WOM_DONE message when the wave completes. If this value is set to NULL then the message will not be posted.
    When sent, the MM_WOM_DONE message contains the channel number in wParam and a pointer to the LPMIXWAVE structure in the lParam. You can use the pointer in lParam in the call to WaveMixFreeWave().
    Note: the value of MM_WOM_DONE is 0x3BD (hexadecimal).
    dwFlags
    (DWORD) A combination of the following:
    WMIX_QUEUEWAVE
    The wave will be placed on the specified channel and played after all waves which are currently waiting to play on that channel.
    WMIX_CLEARQUEUE
    This wave will pre-empt all waves currently playing on the specified channel. Notification messages will not be sent for any waves that get dumped. This message should not be combined with WMIX_QUEUEWAVE.
    WMIX_HIGHPRIORITY
    Play this wave immediately. This flag will interrupt the data buffered in the wave driver and remix the sound. If this flag is not set you could experience up to a half second delay before sound is played. Note: if WMIX_QUEUEWAVE is set with this flag then a sound playing on the channel will not be pre-empted, but it will begin immediately after the previous sound finishes. If no sound is currently playing on this channel then a remix will occur.
    WMIX_USELRUCHANNEL
    The wave should be played on any available channel or played on the channel that was least recently used. This flag should be combined with WMIX_QUEUEWAVE or WMIX_CLEARQUEUE.
    WMIX_WAIT
    Setting this flag cause WaveMixPlay() to put the play information on a "waiting list" to play. When WaveMixPlay() is called without this flag set it will process the calls in the order they were received. This is useful if you want to play multiple sounds simultaneously. Note 1: This flag is not a "pause". Waves that are playing will continue to play regardless of how many files are on the wait list. Note 2: Since the waves that are submitted with this flag set are not checked until WaveMixPlay is called without the flag set you should be careful about using other API calls before playing the sounds. For example, WaveMixFlushChannel() and WaveMixCloseChannel do not process the wait list.
    wLoops
    (WORD) The number of times the wave should be repeated. If wLoops is set to 0xFFFF the wave will loop until the channel it is on is flushed, pre-empted, or closed.
    give WaveMix a time slice to refill the queues
    WaveMixPump

    WaveMixPump() causes the WaveMix DLL to mix the next slice of wave data and submit it to the sound driver. This function can be called if the application does a lot of processing and fails to allow messages to reach the WaveMix DLL in time to avoid drying up the wave queue.

    Syntax:
    void WaveMixPump(void)
    write a setting
    WaveMixSetProfileInt

    WaveMixSetProfileInt() writes a setting to the configuration file (WAVEMIX.INI).

    Syntax:
    int WaveMixSetProfileInt(LPSTR lpszKey, int iValue)
    lpszKey
    the "key" name.
    iValue
    the value of the setting.
    Returns:

    TRUE on success and FALSE on failure.

    Notes:
    This function always writes the setting under the section of the specific sound driver name in the WAVEMIX.INI file. It creates the section if it is not already present.
    change volume and balance
    WaveMixVolume

    WaveMixVolume() changes the volume level of one or both channels. By using different volume levels, you can adjust the balance of the sound data. WaveMixVolume() is a new function for version 1.80.

    Syntax:
    UINT WaveMixVolume(HANDLE hMixSession, LPMIXWAVE lpMixWave, int iLeft, int iRight)
    hMixSession
    Handle that was returned by WaveMixInit().
    lpMixWave
    A pointer that was created by WaveMixOpenWave().
    iLeft
    The new volume of the left channel, or the new volume of the single channel if WaveMix is configured for monaural audio. See "notes" below.
    iRight
    The new volume of the right channel. This parameter is ignored if WaveMix is not configured for stereo audio. See "notes" below.
    Returns:
    Returns zero if the function was successful. Otherwise, it returns an error number. Possible error returns are:
    MMSYSERR_INVALHANDLE
    The specified session has not been opened or the parameter lpMixWave was invalid.
    MMSYSERR_INVALPARAM
    The left or right volume is invalid.
    Notes:
    WaveMix supports 32 volume levels; the parameters iLeft and iRight must be set to a value between 0 and 31. Volume levels below 16 make the sound softer with steps of approximately 1.5 dB; volume levels above 16 make the sound louder, again with a step of 1.5 dB per level. Volume level 16 is the "identity" level; it does not change the volume. Simple arithmetic shows that WaveMix can reduce the volume of sound with 24 dB or increase it with 22.5 dB. (Note that the volume range of 8-bit audio is, at best, 24 dB).

    WaveMixVolume() adjusts the volume of a channel according to a logarithmic curve. This is common for volume adjustment, but "balance" is usually expected to be a linear weighting between the channels. The effect of an extra speaker is an increase in volume with 3 dB. These effects should be taken into consideration when changing the balance of a sound.

    get the length of a sound
    WaveMixWaveLength

    WaveMixWaveLength() determines the length (or duration) of an opened wave file, either in samples or in milliseconds. WaveMixWaveLength() is a new function for version 1.80.

    Syntax:
    DWORD WaveMixWaveLength(HANDLE hMixSession, LPMIXWAVE lpMixWave, DWORD dwFlags)
    hMixSession
    Handle that was returned by WaveMixInit().
    lpMixWave
    A pointer that was created by WaveMixOpenWave().
    dwFlags
    The format in which the duration of the wave data must be returned: use WMIX_TIME for milliseconds or WMIX_SAMPLES for samples.
    Returns:
    Returns the length of the wave data, either in samples or in milliseconds.
     

    Configuring WaveMix

    As already written in the introduction, WaveMix uses the standard sound drivers, unlike DirectSound that uses special sound drivers. Most sound drivers were not written with real-time aspects in mind, so the primary problem that WaveMix has to deal with are sound driver anomalies. If the settings in WAVEMIX.INI are not set correctly, WaveMix simply sounds awful. In other words, you cannot run WaveMix "out of the box" and expect good results. This is a major disadvantage.

    WaveMix first tried to solve the problem by keeping multiple configurations, each of which is specific to a particular sound driver. The idea is to build a "database" with optimal sound card settings. This is a fine idea, although the WAVEMIX.INI that Microsoft distributed contained only five or six sound cards.

    There is one twist: sound drivers sometimes change the internal name for a new version or model, even though there is hardly a change in functioning or features. So the number of entries in the database could grow very quickly. WaveMix's solution to this was the introduction of the [Driver Xref] section. Now you can map all compatible devices/drivers to a single configuration section.

    Several drivers also use the names of devices that they are incompatible with. For example, a cheap sound card came with a driver that claimed to be a driver for the "Windows Sound System". It did not run well with the WaveMix settings for the Windows Sound System, however. For one thing, the GoodWavePos setting was incorrect: the cheap sound card did not return accurate sample positions.

    Another effect is that the multimedia layer ("MMSYSTEM") works differently when called from 16-bit software than when called from 32-bit software. The configuration that is optimal for 16-bit software may not be that optimal when used in 32-bit software. The multimedia layer may also function (slightly) different under Windows 3.1/95/98 versus Windows NT/2000.

    This is where the database scheme breaks. You can come up with a database that works well for, say, 80% of the cases, but for the remaining 20% of the users of your software, you must provide a means to easily configure WaveMix themselves. To this end, WaveMix provides the functions WaveMixGetProfileInt() and WaveMixSetProfileInt().

    MixCfg

    MixCfg is a small utility that allows you to change the configuration of WaveMix and to test the new settings on the fly. The software is copyrighted by my company, but it may be freely used and/or distributed with other software. The software is provided as is (no warranties, no liability, etc.). I have built both 16-bit and 32-bit versions. If you translate it to a different language or improve it in other ways, I would be glad to hear about it. You can contact me at: thiadmer @ compuphase.com.

     

    The configuration file: WAVEMIX.INI

    The WAVEMIX.INI file has the following standard sections:

    [General]
    The system settings (applicable to all drivers).
    [Default]
    The default settings, may be overridden by those in a driver-specific section.
    [Driver Xref]
    When the optimal settings of a driver differ from the default settings, WAVEMIX.INI may include a specific section for that driver. However, many drivers/sound cards are similar, even if their names differ. The [Driver Xref] section maps a driver name to another (more general) driver name. This allows to map several drivers of a single specific block of settings in the WAVEMIX.INI file. The [Driver Xref] section is new for WaveMix version 1.80.
    [Not compatible]
    A list of driver names that are known to be incompatible with WaveMix. Currently, the only known sound driver that is incompatible with WaveMix is the "Wave driver for PC Speaker".

    The configuration file also contains a section for every sound card (or more accurately, for every sound driver) whose settings differ from the default. These sections contain one or more of the same keys as in the [Default] section.

    Starting with version 1.80, it is advisable to write a WAVEMIX.INI file in the same directory where you install the WaveMix DLL, so that WaveMix will use those settings (and not the settings that may be in a WAVEMIX.INI that appears in the Windows directory and that is probably valid for version 1.50 of WaveMix).

    [General]
    ShowDevices

    If ShowDevices exists and is non-zero then will display wave out devices and other configuration information. This is useful for testing, or for checking whether the name of the device driver matches with a specific section in the configuration file.

    Default:
    ShowDevices=0
    [General]
    WaveOutDevice

    If WaveOutDevice exists it should either be -1 or a value between zero and one less than the number of devices, where zero means the first device, one the second device, and so forth. The value -1 specifies the WAVE_MAPPER (WaveMix identifies it with "Unknown Device").

    Default:
    WaveOutDevice=0
    [General]
    cmixit

    WaveMix will detect if it is running on a 286 and not use any instructions defined for the 386, 486 and Pentium processors. You can force it to use the 286-only code on a 386+ processor by setting cmixit=1.

    Default:
    cmixit=0
    [General]
    debug

    Controls the amount of debug information shown, provided that you have a "debugging" version of the WaveMix DLL installed. The level can be set to a value between 0 and 3, where 0 means "no debug messages at all", 1 shows only errors, 2 shows errors and "events" and 3 also gives the parameters of each event.

    Default:
    debug=0
    [Default]
    Remix

    The algorithm that WaveMix uses to remix blocks that had already been prepared for the sound driver. It must be one of the following:

    Remix=1
    Reset the sound driver to remove queued buffers and remix them. This is the "college book" way to handle remixing, but it may produce audible clicks.
    Remix=2
    Remix only the buffers that were not yet sent to the sound driver. This option has no clicks and it always works, but it suffers from high latency.
    Remix=3
    Remix all buffers, regardless of whether they have been sent to the sound driver. Do not reset the driver. This strategy may not work with all drivers or with all versions of Microsoft Windows. This setting is only available in WaveMix 1.80 and above.
    Default:
    Remix=1
    [Default]
    GoodWavePos

    When set to 1, WaveMix uses waveOutGetPosition() to check the current sample position. When set to 0, WaveMix estimates the sample position from the sampling rate and the timestamp returned by timeGetTime().

    The GoodWavePos setting is mostly an issue for old sound drivers, which often give an inaccurate sample position for waveOutGetPosition(). Estimating the sample position from the sample rate and linear time needs to rely on an accurate sampling rate, and the same old sound cards/drivers often have a problem here, too. Modern sound cards and their associated drivers have often both good values for waveOutGetPosition(), as well as accurate sampling frequencies, so the setting for GoodWavePos is not very relevant.

    Default:
    GoodWavePos=0
    [Default]
    WaveBlocks

    The number of ping-pong buffers to use. A value between 2 and 6. The optimal value depends on the (a href="#REMIX">"remix" method. With the setting Remix=2, you should choose the lowest number of buffers that does not produce gaps in the sound; 3 is usually a good value. With Remix=1 or Remix=2, choose 5 or 6.

    Default:
    WaveBlocks=3
    [Default]
    WaveBlockLen

    The size (in bytes) of each of ping-pong buffer. It must be zero or a value between 512 and 4096. If zero (or if a number is not specified) WaveMix will try to determine the size of the DMA buffer of the sound card.

    The code to determine the DMA buffer size does not always work correctly. In general, it is advisable to set a specific size. If unknown, a size of 688 is a good compromise, as most sound cards have this buffer size.

    Default:
    WaveBlockLen=0
    [Default]
    SamplesPerSec

    The playback sampling frequency in kHz. It must be 11, 22 or 44; the actual sampling frequencies are respectively 11025 Hz, 22050 Hz and 44100 Hz, but beware that many sound cards are not very precise in their sampling rate.

    If the remix method does not remix buffers that were already sent to the audio card (Remix=2), it choosing a higher sampling frequency reduces latency. For all remixing cases, a higher sampling frequency also

    Default:
    SamplesPerSec=11
     

    Below is an example WAVEMIX.INI file that is adapted from the versions that Microsoft has distributed for some of its game sets. The default settings in this file are probably a good starting point.

    Standard WAVEMIX.INI file
    [General]
    ShowDevices=0
    WaveOutDevice=0
    
    [Default]
    Remix=2
    GoodWavePos=0
    WaveBlocks=3
    WaveBlockLen=688
    SamplesPerSec=11
    
    [Driver Xref]
    Windows Sound System Playback=Windows Sound System
    Sound System Playback=Windows Sound System
    SB16 Wave Out=SoundBlaster 16
    SB16 Playback=SoundBlaster 16
    AWE64 Wave Out=SoundBlaster 16
    
    [Windows Sound System]
    Remix=1
    GoodWavePos=1
    WaveBlocks=3
    SamplesPerSec=22
    
    [SoundBlaster 16]
    Remix=1
    GoodWavePos=0
    WaveBlocks=5
    SamplesPerSec=22
    
    [SBPro Wave Out]
    Remix=1
    GoodWavePos=0
    WaveBlocks=5
    
    [MultiSound Wave Out]
    Remix=2
    GoodWavePos=1
    WaveBlocks=3
    
    [Media Vision Waveform Output]
    Remix=1
    GoodWavePos=0
    WaveBlocks=3
    
    [UltraSound Waveform Output]
    Remix=2
    GoodWavePos=0
    WaveBlocks=3
    SamplesPerSec=11
    
    [Maestro Playback]
    Remix=2
    GoodWavePos=0
    WaveBlocks=5
    ContinuousStream=1
    
    [Not compatible]
    Wave driver for PC Speaker=1