AUDPLAY.C

/************************************************************************** 
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/

/*--------------------------------------------------------------+
| audplay.c Simple routines to play audio using an AVIStream to |
| get data. Uses global variables, so only one instance at a|
| time. (Usually, there's only one sound card, so this isn't|
| so bad).|
+--------------------------------------------------------------*/

#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <vfw.h>

#include "audplay.h"
#include "muldiv32.h"

/*--------------------------------------------------------------+
| ****************** AUDIO PLAYING SUPPORT ******************** |
+--------------------------------------------------------------*/

staticHWAVEOUTshWaveOut = 0;/* Current MCI device ID */
staticLONGslBegin;
staticLONGslCurrent;
staticLONGslEnd;
staticBOOLsfLooping;
staticBOOLsfPlaying = FALSE;

#define MAX_AUDIO_BUFFERS16
#define MIN_AUDIO_BUFFERS2
#define AUDIO_BUFFER_SIZE16384

staticUINTswBuffers; // total # buffers
staticUINTswBuffersOut; // buffers device has
staticUINTswNextBuffer; // next buffer to fill
staticLPWAVEHDRsalpAudioBuf[MAX_AUDIO_BUFFERS];

staticPAVISTREAMspavi; // stream we're playing
staticLONGslSampleSize; // size of an audio sample

staticLONGsdwBytesPerSec;
staticLONGsdwSamplesPerSec;

/*---------------------------------------------------------------+
| aviaudioCloseDevice -- close the open audio device, if any. |
+---------------------------------------------------------------*/
void aviaudioCloseDevice(void)
{
UINTw;

if (shWaveOut) {
while (swBuffers > 0) {
--swBuffers;
waveOutUnprepareHeader(shWaveOut, salpAudioBuf[swBuffers],
sizeof(WAVEHDR));
GlobalFreePtr((LPBYTE) salpAudioBuf[swBuffers]);
}

w = waveOutClose(shWaveOut);

// DPF("AudioCloseDevice: waveOutClose returns %u\n", w);
shWaveOut = NULL;
}
}

/*--------------------------------------------------------------+
| aviaudioOpenDevice -- get ready to play waveform data.|
+--------------------------------------------------------------*/
BOOL aviaudioOpenDevice(HWND hwnd, PAVISTREAM pavi)
{
UINTw;
LPVOIDlpFormat;
LONGcbFormat;
AVISTREAMINFOstrhdr;

if (!pavi)// no wave data to play
return FALSE;

if (shWaveOut)// already something playing
return TRUE;

spavi = pavi;

AVIStreamInfo(pavi, &strhdr, sizeof(strhdr));

slSampleSize = (LONG) strhdr.dwSampleSize;
if (slSampleSize <= 0 || slSampleSize > AUDIO_BUFFER_SIZE)
return FALSE;

AVIStreamFormatSize(pavi, 0, &cbFormat);

lpFormat = GlobalAllocPtr(GHND, cbFormat);
if (!lpFormat)
return FALSE;

AVIStreamReadFormat(pavi, 0, lpFormat, &cbFormat);

sdwSamplesPerSec = ((LPWAVEFORMAT) lpFormat)->nSamplesPerSec;
sdwBytesPerSec = ((LPWAVEFORMAT) lpFormat)->nAvgBytesPerSec;

w = waveOutOpen(&shWaveOut, WAVE_MAPPER, lpFormat,
(DWORD) (UINT) hwnd, 0L, CALLBACK_WINDOW);

//
// Maybe we failed because someone is playing sound already.
// Shut any sound off, and try once more before giving up.
//
if (w) {
sndPlaySound(NULL, 0);
w = waveOutOpen(&shWaveOut, WAVE_MAPPER, lpFormat,
(DWORD) (UINT) hwnd, 0L, CALLBACK_WINDOW);
}

// DPF("waveOutOpen returns %u, shWaveOut = %u\n", w, shWaveOut);

if (w != 0) {
/* Show error message here? */

return FALSE;
}

for (swBuffers = 0; swBuffers < MAX_AUDIO_BUFFERS; swBuffers++) {
if (!(salpAudioBuf[swBuffers] =
(LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD)(sizeof(WAVEHDR) + AUDIO_BUFFER_SIZE))))
break;
salpAudioBuf[swBuffers]->dwFlags = WHDR_DONE;
salpAudioBuf[swBuffers]->lpData = (LPBYTE) salpAudioBuf[swBuffers]
+ sizeof(WAVEHDR);
salpAudioBuf[swBuffers]->dwBufferLength = AUDIO_BUFFER_SIZE;
if (!waveOutPrepareHeader(shWaveOut, salpAudioBuf[swBuffers],
sizeof(WAVEHDR)))
continue;

GlobalFreePtr((LPBYTE) salpAudioBuf[swBuffers]);
break;
}

// DPF("Allocated %u %lu-byte buffers.\n", swBuffers, (DWORD) AUDIO_BUFFER_SIZE);

if (swBuffers < MIN_AUDIO_BUFFERS) {
aviaudioCloseDevice();
return FALSE;
}

swBuffersOut = 0;
swNextBuffer = 0;

sfPlaying = FALSE;

return TRUE;
}


//
// Return the time in milliseconds corresponding to the currently playing
// audio sample, or -1 if no audio is playing.
// WARNING: Some sound cards are pretty inaccurate!
//
LONG aviaudioTime(void)
{
MMTIMEmmtime;

if (!sfPlaying)
return -1;

mmtime.wType = TIME_SAMPLES;

waveOutGetPosition(shWaveOut, &mmtime, sizeof(mmtime));

if (mmtime.wType == TIME_SAMPLES)
return AVIStreamSampleToTime(spavi, slBegin)
+ muldiv32(mmtime.u.sample, 1000, sdwSamplesPerSec);
else if (mmtime.wType == TIME_BYTES)
return AVIStreamSampleToTime(spavi, slBegin)
+ muldiv32(mmtime.u.cb, 1000, sdwBytesPerSec);
else
return -1;
}


//
// Fill up any empty audio buffers and ship them out to the device.
//
BOOL aviaudioiFillBuffers(void)
{
LONGlRead;
UINTw;
LONGlSamplesToPlay;

/* We're not playing, so do nothing. */
if (!sfPlaying)
return TRUE;

// DPF3("%u/%u (%lu-%lu)\n", swBuffersOut, swBuffers, slCurrent, slEnd);

while (swBuffersOut < swBuffers) {
if (slCurrent >= slEnd) {
if (sfLooping) {
/* Looping, so go to the beginning. */
slCurrent = slBegin;
} else
break;
}

/* Figure out how much data should go in this buffer */
lSamplesToPlay = slEnd - slCurrent;
if (lSamplesToPlay > AUDIO_BUFFER_SIZE / slSampleSize)
lSamplesToPlay = AUDIO_BUFFER_SIZE / slSampleSize;


AVIStreamRead(spavi, slCurrent, lSamplesToPlay,
salpAudioBuf[swNextBuffer]->lpData,
AUDIO_BUFFER_SIZE,
&(LONG)salpAudioBuf[swNextBuffer]->dwBufferLength,
&lRead);

if (lRead != lSamplesToPlay) {
// DPF("Error from WAVE_READ\n");
return FALSE;
}
slCurrent += lRead;

w = waveOutWrite(shWaveOut, salpAudioBuf[swNextBuffer],sizeof(WAVEHDR));

if (w != 0) {
// DPF("Error from waveOutWrite\n");
return FALSE;
}

++swBuffersOut;
++swNextBuffer;
if (swNextBuffer >= swBuffers)
swNextBuffer = 0;
}

if (swBuffersOut == 0 && slCurrent >= slEnd)
aviaudioStop();

/* We've filled all of the buffers we can or want to. */
return TRUE;
}

/*--------------------------------------------------------------+
| aviaudioPlay -- Play audio, starting at a given frame|
||
+--------------------------------------------------------------*/
BOOL aviaudioPlay(HWND hwnd, PAVISTREAM pavi, LONG lStart, LONG lEnd, BOOL fWait)
{
if (lStart < 0)
lStart = AVIStreamStart(pavi);

if (lEnd < 0)
lEnd = AVIStreamEnd(pavi);

// DPF2("Audio play%s from %ld to %ld (samples)\n", ((LPSTR) (fWait ? " wait" : "")), lStart, lEnd);

if (lStart >= lEnd)
return FALSE;

if (!aviaudioOpenDevice(hwnd, pavi))
return FALSE;

if (!sfPlaying) {

//
// We're beginning play, so pause until we've filled the buffers
// for a seamless start
//
waveOutPause(shWaveOut);

slBegin = lStart;
slCurrent = lStart;
slEnd = lEnd;
sfPlaying = TRUE;
} else {
if (lStart > slEnd) {
// DPF("Gap in wave that is supposed to be played!\n");
}
slEnd = lEnd;
}

// sfLooping = fLoop;

aviaudioiFillBuffers();

//
// Now unpause the audio and away it goes!
//
waveOutRestart(shWaveOut);

//
// Caller wants us not to return until play is finished
//
if (fWait) {
while (swBuffersOut > 0)
Yield();
}

return TRUE;
}

/*--------------------------------------------------------------+
| aviaudioMessage -- handle wave messages received by|
| window controlling audio playback. When audio buffers are|
| done, this routine calls aviaudioiFillBuffers to fill them|
| up again.|
+--------------------------------------------------------------*/
void aviaudioMessage(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
if (msg == MM_WOM_DONE) {
--swBuffersOut;
aviaudioiFillBuffers();
}
}


/*--------------------------------------------------------------+
| aviaudioStop -- stop playing, close the device.|
+--------------------------------------------------------------*/
void aviaudioStop(void)
{
UINTw;

if (shWaveOut != 0) {

w = waveOutReset(shWaveOut);

sfPlaying = FALSE;

// DPF("AudioStop: waveOutReset() returns %u \n", w);

aviaudioCloseDevice();
}
}