/****************************************************************************** Copyright (C) Microsoft Corporation 1991-1992. All rights reserved. Title: avisound.c - Code for playing audio in AVI files. *****************************************************************************/ #include "graphic.h" #define AUDIO_PANIC 10 static UINT nAudioPanic; // // redefine StreamFromFOURCC to only handle 0-9 streams! // #undef StreamFromFOURCC #define StreamFromFOURCC(fcc) (UINT)(HIBYTE(LOWORD(fcc)) - (BYTE)'0') void FAR PASCAL _LOADDS mciaviWaveOutFunc(HWAVEOUT hWaveOut, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); #ifndef WIN32 #define GetDS() (HGLOBAL)HIWORD((DWORD)(LPVOID)&ghModule) #endif //WIN16 DWORD FAR PASCAL SetUpAudio(NPMCIGRAPHIC npMCI, BOOL fPlaying) { UINT w; LPWAVEHDR lpWaveHdr; STREAMINFO *psi; if (npMCI->nAudioStreams == 0) { npMCI->wABs = 0; npMCI->wABOptimal = 0; return 0L; } nAudioPanic = GetProfileInt(TEXT("MCIAVI"), TEXT("AudioPanic"), AUDIO_PANIC); psi = SI(npMCI->nAudioStream); Assert(psi->sh.fccType == streamtypeAUDIO); Assert(psi->cbFormat); Assert(psi->lpFormat); if (!npMCI->pWF) { npMCI->pWF = (NPWAVEFORMAT)LocalAlloc(LPTR, (UINT)psi->cbFormat); if (!npMCI->pWF) { return MCIERR_OUT_OF_MEMORY; } } hmemcpy(npMCI->pWF,psi->lpFormat,psi->cbFormat); npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames; npMCI->dwAudioLength = psi->sh.dwLength * psi->sh.dwSampleSize; if (npMCI->dwAudioLength < 1000L) { DPF(("AudioLength is bogus")); npMCI->dwAudioLength = muldiv32((npMCI->pWF->nAvgBytesPerSec + 100) * npMCI->lFrames,npMCI->dwMicroSecPerFrame,1000000L); } // // choose the audio playback method depending on how we are going to // recive audio data from the file. // switch (npMCI->wPlaybackAlg) { case MCIAVI_ALG_HARDDISK: case MCIAVI_ALG_AUDIOONLY: if (!npMCI->pf && !npMCI->hmmioAudio) { MMIOINFO mmioInfo; _fmemset(&mmioInfo, 0, sizeof(MMIOINFO)); mmioInfo.htask = (HANDLE) npMCI->hCallingTask; //ntmmsystem bug, should be threadid which is dword npMCI->hmmioAudio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE); if (npMCI->hmmioAudio == NULL) npMCI->hmmioAudio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ); if (!npMCI->hmmioAudio) { Assert(0); return MCIERR_DRIVER_INTERNAL; } } // fall through to CDROM case MCIAVI_ALG_CDROM: //!!!! we need to tune this!!!! // !!! We use four 1/2 second buffers. This is arbitrary. npMCI->wABs = 4; npMCI->wABOptimal = 0; npMCI->dwABSize = npMCI->pWF->nAvgBytesPerSec / 2; break; case MCIAVI_ALG_INTERLEAVED: /* Fix up some values based on the header information */ npMCI->dwABSize = muldiv32(npMCI->dwMicroSecPerFrame, npMCI->pWF->nAvgBytesPerSec,1000000L) + 2047; npMCI->dwABSize &= ~(2047L); npMCI->wABs = npMCI->wEarlyAudio + 2 + (WORD) npMCI->dwBufferedVideo; /* Soundblaster hack: waveoutdone only accurate to 2K. */ //!!!!!!!!!! is this right. if (npMCI->dwMicroSecPerFrame) { npMCI->wABOptimal = npMCI->wABs - (UINT) (muldiv32(2048, 1, muldiv32(npMCI->dwMicroSecPerFrame, npMCI->pWF->nAvgBytesPerSec,1000000L))); } else { npMCI->wABOptimal = 0; } //!!! hack so we can do burst reading, up to 1sec //npMCI->wABs += (int)muldiv32(1000000l, 1, npMCI->dwMicroSecPerFrame); DPF2(("Using %u audio buffers, of which %u should be full.\n", npMCI->wABs, npMCI->wABOptimal)); break; default: Assert(0); return 0L; } npMCI->dwABSize -= npMCI->dwABSize % npMCI->pWF->nBlockAlign; if (!fPlaying) return 0L; /* This code adjusts the wave format block to play ** the audio at the correct speed to match the frame rate. */ npMCI->pWF->nSamplesPerSec = muldiv32(npMCI->pWF->nSamplesPerSec, npMCI->dwMicroSecPerFrame, npMCI->dwPlayMicroSecPerFrame); npMCI->pWF->nAvgBytesPerSec = muldiv32(npMCI->pWF->nAvgBytesPerSec, npMCI->dwMicroSecPerFrame, npMCI->dwPlayMicroSecPerFrame); if (npMCI->pWF->wFormatTag == WAVE_FORMAT_PCM) { /* Make sure this is exactly right... */ npMCI->pWF->nAvgBytesPerSec = npMCI->pWF->nSamplesPerSec * npMCI->pWF->nBlockAlign; } /* Kill any currently playing sound */ sndPlaySound(NULL, 0); DPF2(("Opening wave device....\n")); /* Try to open a wave device. */ w = waveOutOpen(&npMCI->hWave, (UINT)WAVE_MAPPER, (LPWAVEFORMATEX) npMCI->pWF, //(const LPWAVEFORMATEX) npMCI->pWF, (DWORD) &mciaviWaveOutFunc, (DWORD) (LPMCIGRAPHIC) npMCI, (DWORD)CALLBACK_FUNCTION); if (w) { DPF(("Unable to open wave device.\n")); npMCI->hWave = NULL; return w == WAVERR_BADFORMAT ? MCIERR_WAVE_OUTPUTSUNSUITABLE : MCIERR_WAVE_OUTPUTSINUSE; } npMCI->dwFlags &= ~MCIAVI_LOSTAUDIO; #ifndef WIN32 // No need to lock it on NT - although we could with Virtual mem // functions // // page lock our DS so our wave callback function can // touch it without worry. see mciaviWaveOutFunc() // GlobalPageLock(GetDS()); #endif //WIN16 /* Pause the wave output device, so it won't start playing ** when we're loading up the buffers. */ if (waveOutPause(npMCI->hWave) != 0) { DPF(("Error from waveOutPause!\n")); return MCIERR_DRIVER_INTERNAL; } if (npMCI->dwFlags & MCIAVI_VOLUMESET) { DeviceSetVolume(npMCI, npMCI->dwVolume); } else { DeviceGetVolume(npMCI); } npMCI->lpAudio = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, npMCI->wABs * (npMCI->dwABSize + sizeof(WAVEHDR))); if (!npMCI->lpAudio) { return MCIERR_OUT_OF_MEMORY; } npMCI->dwAudioPlayed = 0L; npMCI->wNextAB = 0; npMCI->dwUsedThisAB = 0; /* Allocate and prepare our buffers */ for (w = 0; w < npMCI->wABs; w++) { lpWaveHdr = (LPWAVEHDR) (npMCI->lpAudio + (w * sizeof(WAVEHDR))); lpWaveHdr->lpData = (HPSTR) npMCI->lpAudio + npMCI->wABs * sizeof(WAVEHDR) + w * npMCI->dwABSize; lpWaveHdr->dwBufferLength = npMCI->dwABSize; lpWaveHdr->dwBytesRecorded = 0L; lpWaveHdr->dwUser = 0L; lpWaveHdr->dwFlags = 0L; lpWaveHdr->dwLoops = 0L; lpWaveHdr->lpNext = 0L; lpWaveHdr->reserved = 0; } for (w = 0; w < npMCI->wABs; w++) { lpWaveHdr = (LPWAVEHDR) (npMCI->lpAudio + (w * sizeof(WAVEHDR))); if (waveOutPrepareHeader(npMCI->hWave, lpWaveHdr, sizeof(WAVEHDR)) != 0) { return MCIERR_OUT_OF_MEMORY; } lpWaveHdr->dwFlags |= WHDR_DONE; } return 0L; } DWORD FAR PASCAL CleanUpAudio(NPMCIGRAPHIC npMCI) { UINT w; /* Clear flags relating to playing audio */ npMCI->dwFlags &= ~(MCIAVI_WAVEPAUSED); if (npMCI->lpAudio) { waveOutRestart(npMCI->hWave); // just in case we are paused waveOutReset(npMCI->hWave); for (w = 0; w < npMCI->wABs; w++) { LPWAVEHDR lpWaveHdr; lpWaveHdr = (LPWAVEHDR) (npMCI->lpAudio + (w * sizeof(WAVEHDR))); #if 0 lpWaveHdr->lpData = npMCI->lpAudio + npMCI->wABs * sizeof(WAVEHDR) + w * npMCI->dwABSize; lpWaveHdr->dwBufferLength = npMCI->dwABSize; #endif /* Do we need to check for an error from this? */ waveOutUnprepareHeader(npMCI->hWave, lpWaveHdr, sizeof(WAVEHDR)); } GlobalFreePtr(npMCI->lpAudio); npMCI->lpAudio = NULL; Assert(npMCI->wABFull == 0); } DPF2(("Closing wave device.\n")); waveOutClose(npMCI->hWave); npMCI->hWave = 0; #ifndef WIN32 GlobalPageUnlock(GetDS()); #endif //WIN16 return 0L; } BOOL NEAR PASCAL WaitForFreeAudioBuffer(NPMCIGRAPHIC npMCI, BOOL FAR *lpfHurry) { LPWAVEHDR lpWaveHdr; lpWaveHdr = (LPWAVEHDR) (npMCI->lpAudio + (npMCI->wNextAB * sizeof(WAVEHDR))); /* Use the number of full audio buffers to decide if we're behind. */ if (npMCI->wABFull < npMCI->wABOptimal) { *lpfHurry = TRUE; } /* If all of the audio buffers are full, we have to wait. */ if (npMCI->wABFull == npMCI->wABs) { DWORD time = timeGetTime(); #define AUDIO_WAIT_TIMEOUT 2000 DOUT2("waiting for audio buffer."); // we better not wait if the device is not playing! Assert(!(npMCI->dwFlags & MCIAVI_WAVEPAUSED)); #ifdef XDEBUG GetAsyncKeyState(VK_ESCAPE); GetAsyncKeyState(VK_F2); GetAsyncKeyState(VK_F3); GetAsyncKeyState(VK_F4); #endif while (npMCI->wABFull == npMCI->wABs) { if (npMCI->dwFlags & MCIAVI_STOP) return FALSE; aviTaskYield(); // // the "Fahrenheit VA Audio Wave Driver" may get confused // if you call waveOutPause() and waveOutRestart() alot // and it will stay paused no matter what you do, it has // all our buffers and it still does not make any sound // you can call waveOutRestart() until you are blue in // the face, it will do nothing. // // so this is why this routine can time out, after waiting // 2 seconds or so we just toss all the audio in the buffers // and start over. // if (timeGetTime() - time > AUDIO_WAIT_TIMEOUT) { DOUT("Gave up waiting, reseting wave device\n"); waveOutReset(npMCI->hWave); break; } #ifdef XDEBUG if (GetAsyncKeyState(VK_ESCAPE) & 0x0001) { DPF(("STOPPED WAITING! wABFull = %d, wABs = %d\n", npMCI->wABFull,npMCI->wABs)); return FALSE; } if (GetAsyncKeyState(VK_F2) & 0x0001) { DOUT("Trying waveOutRestart\n"); waveOutRestart(npMCI->hWave); } if (GetAsyncKeyState(VK_F3) & 0x0001) { DOUT("Trying waveOutReset\n"); waveOutReset(npMCI->hWave); } if (GetAsyncKeyState(VK_F4) & 0x0001) { int i,n; for (i=n=0; i<(int)npMCI->wABs; i++) { if (((LPWAVEHDR)npMCI->lpAudio)[i].dwFlags & WHDR_DONE) { DPF(("Buffer #%d is done!\n", i)); n++; } else { DPF(("Buffer #%d is not done\n", i)); } } if (n > 0) DPF(("%d buffers are done but our callback did not get called!\n", n)); } #endif } DOUT2("done\n"); } /* Debugging check that wave has finished playing--should never happen */ Assert(lpWaveHdr->dwFlags & WHDR_DONE); #if 0 lpWaveHdr->lpData = npMCI->lpAudio + npMCI->wABs * sizeof(WAVEHDR) + npMCI->wNextAB * npMCI->dwABSize; #endif return TRUE; } #ifndef WIN32 #pragma optimize("", off) #endif BOOL NEAR PASCAL ReadSomeAudio(NPMCIGRAPHIC npMCI, BYTE _huge * lpAudio, DWORD dwStart, DWORD FAR * pdwLength) { DWORD dwIndex = 0; DWORD ckidAudio; DWORD dwAudioPos = 0L; AVIINDEXENTRY far * lpIndexEntry; Assert(npMCI->wPlaybackAlg == MCIAVI_ALG_HARDDISK || npMCI->wPlaybackAlg == MCIAVI_ALG_AUDIOONLY); Assert(npMCI->hpIndex); /* ** Figure out what type of chunk we're looking for, */ ckidAudio = MAKEAVICKID(cktypeWAVEbytes, npMCI->nAudioStream); lpIndexEntry = (AVIINDEXENTRY FAR *) npMCI->hpIndex; for (dwIndex = 0; dwIndex < npMCI->macIndex; dwIndex++, ++((AVIINDEXENTRY _huge *) lpIndexEntry)) { if (lpIndexEntry->ckid != ckidAudio) continue; if (dwAudioPos + lpIndexEntry->dwChunkLength > dwStart) { DWORD dwLengthNow; DWORD dwSeekTo; dwLengthNow = lpIndexEntry->dwChunkLength; dwSeekTo = lpIndexEntry->dwChunkOffset + 8; if (dwAudioPos + dwLengthNow > dwStart + *pdwLength) { /* Attempted optimization: If we've already read some ** data, and we can't read the next whole chunk, let's ** leave it for later. */ if (dwAudioPos > dwStart && (!(npMCI->dwFlags & MCIAVI_REVERSE))) break; dwLengthNow = dwStart + *pdwLength - dwAudioPos; } if (dwAudioPos < dwStart) { dwLengthNow -= (dwStart - dwAudioPos); dwSeekTo += (dwStart - dwAudioPos); } mmioSeek(npMCI->hmmioAudio, dwSeekTo, SEEK_SET); if (mmioRead(npMCI->hmmioAudio, lpAudio, dwLengthNow) != (LONG) dwLengthNow) { DPF(("Error reading audio data (%lx bytes at %lx)\n", dwLengthNow, dwSeekTo)); return FALSE; } lpAudio += dwLengthNow; } dwAudioPos += lpIndexEntry->dwChunkLength; if (dwAudioPos >= dwStart + *pdwLength) return TRUE; } if (dwAudioPos < dwStart) *pdwLength = 0; // return FALSE? else *pdwLength = dwAudioPos - dwStart; return TRUE; } #ifndef WIN32 #pragma optimize("", on) #endif BOOL NEAR PASCAL ReverseWaveBuffer(NPMCIGRAPHIC npMCI, LPWAVEHDR lpWaveHdr) { DWORD dwLeft = lpWaveHdr->dwBufferLength; BYTE _huge *hp1; BYTE _huge *hp2; DWORD dwBlock = npMCI->pWF->nBlockAlign; BYTE bTemp; DWORD dw; Assert(npMCI->dwFlags & MCIAVI_REVERSE); Assert(npMCI->wPlaybackAlg == MCIAVI_ALG_HARDDISK || npMCI->wPlaybackAlg == MCIAVI_ALG_AUDIOONLY); /* This routine doesn't like it when the data doesn't end on a ** block boundary, so make it so. This should never happen. */ Assert((dwLeft % dwBlock) == 0); dwLeft -= dwLeft % dwBlock; hp1 = lpWaveHdr->lpData; hp2 = ((HPSTR) lpWaveHdr->lpData) + (dwLeft - dwBlock); while ((LONG) dwLeft > (LONG) dwBlock) { for (dw = 0; dw < dwBlock; dw++) { bTemp = *hp1; *hp1++ = *hp2; *hp2++ = bTemp; } hp2 -= dwBlock * 2; dwLeft -= dwBlock * 2; } return TRUE; } void FAR PASCAL BuildVolumeTable(NPMCIGRAPHIC npMCI) { int vol; int i; if (!npMCI->pWF || npMCI->pWF->wFormatTag != WAVE_FORMAT_PCM) return; if (((NPPCMWAVEFORMAT) npMCI->pWF)->wBitsPerSample != 8) return; vol = (LOWORD(npMCI->dwVolume) + HIWORD(npMCI->dwVolume)) / 2; vol = (int) (((LONG) vol * 256) / 500); if (!npMCI->pVolumeTable) npMCI->pVolumeTable = (void *)LocalAlloc(LPTR, 256); if (!npMCI->pVolumeTable) return; for (i = 0; i < 256; i++) { npMCI->pVolumeTable[i] = (BYTE) min(255, max(0, (int) ((((LONG) (i - 128) * vol) / 256) + 128))); } } BOOL NEAR PASCAL AdjustVolume(NPMCIGRAPHIC npMCI, LPWAVEHDR lpWaveHdr) { DWORD dwLeft = lpWaveHdr->dwBufferLength; BYTE FAR *pb; if (npMCI->pWF->wFormatTag != WAVE_FORMAT_PCM) return FALSE; if (!npMCI->pVolumeTable) return FALSE; if (((NPPCMWAVEFORMAT)npMCI->pWF)->wBitsPerSample != 8) return FALSE; pb = lpWaveHdr->lpData; #ifndef WIN32 if (OFFSETOF(pb) + dwLeft > 64l*1024) { while (dwLeft--) { *pb = npMCI->pVolumeTable[*pb]; ((BYTE _huge *)pb)++; } } else { while ((int)dwLeft--) *pb++ = npMCI->pVolumeTable[*pb]; } #else while ((int)dwLeft--) *pb++ = npMCI->pVolumeTable[*pb]; #endif return TRUE; } BOOL NEAR PASCAL PlaySomeAudio(NPMCIGRAPHIC npMCI, LPWAVEHDR lpWaveHdr) { if (npMCI->pVolumeTable) AdjustVolume(npMCI, lpWaveHdr); lpWaveHdr->dwFlags &= ~WHDR_DONE; /* If we're playing and we've used all of our audio buffers, pause the ** wave device until we can fill more of them up. ** ** we need to be carefull not to do this on the last frame!!! */ if ((npMCI->wTaskState == TASKPLAYING) && !(npMCI->dwFlags & MCIAVI_WAVEPAUSED) && (npMCI->wABFull == 0 || npMCI->nAudioBehind > nAudioPanic)) { if (npMCI->wABFull > 0) { DPF(("Audio panic stop\n")); } else { DPF(("Audio queue empty; pausing wave device\n")); } // // some audio cards dont like starving it confuses them // it is kind of rude any way. we are going to cause a audio break // anyway so if we lose a little bit of audio (a few frames or so) // no one will even notice (any worse than the audio break) // if (npMCI->wABFull <= 1) { DOUT("Trying audio hack!\n"); waveOutReset(npMCI->hWave); } ++npMCI->dwAudioBreaks; waveOutPause(npMCI->hWave); ICDrawStop(npMCI->hicDraw); npMCI->dwFlags |= MCIAVI_WAVEPAUSED; } if (waveOutWrite(npMCI->hWave, lpWaveHdr, sizeof(WAVEHDR)) != 0) { DPF(("Error from waveOutWrite!\n")); npMCI->dwTaskError = MCIERR_AVI_AUDIOERROR; return FALSE; } else { ++npMCI->wABFull; /* Use the next wave buffer next time */ ++npMCI->wNextAB; if (npMCI->wNextAB == npMCI->wABs) npMCI->wNextAB = 0; npMCI->dwUsedThisAB = 0; } if (npMCI->wABFull < min(npMCI->wABOptimal, npMCI->wABFull/2)) npMCI->nAudioBehind++; else npMCI->nAudioBehind=0; /* If we paused the wave device to let ourselves catch up, and ** we've caught up enough, restart the device. */ if ((npMCI->dwFlags & MCIAVI_WAVEPAUSED) && npMCI->wTaskState == TASKPLAYING && npMCI->wABFull == npMCI->wABs) { DPF2(("restarting wave device\n")); waveOutRestart(npMCI->hWave); ICDrawStart(npMCI->hicDraw); npMCI->dwFlags &= ~(MCIAVI_WAVEPAUSED); npMCI->nAudioBehind = 0; } return TRUE; } /* Play the current record's audio */ BOOL NEAR PASCAL PlayRecordAudio(NPMCIGRAPHIC npMCI, BOOL FAR *pfHurryUp, BOOL FAR *pfPlayedAudio) { LPWAVEHDR lpWaveHdr; FOURCC ckid; DWORD cksize; LPSTR lpSave; LPSTR lpData; BOOL fRet = TRUE; ////BOOL fSilence; LONG len; DWORD dwBytesTotal = 0L; DWORD dwBytesThisChunk; Assert(npMCI->wPlaybackAlg == MCIAVI_ALG_INTERLEAVED); lpSave = npMCI->lp; lpWaveHdr = ((LPWAVEHDR)npMCI->lpAudio) + npMCI->wNextAB; *pfPlayedAudio = FALSE; /* Remember! ** ** In the new file format, things shouldn't necessarily need to ** be ordered with the wave stuff always first. */ len = (LONG)npMCI->dwThisRecordSize; while (len > 3 * sizeof(DWORD)) { /* Look at the next chunk */ ckid = GET_DWORD(); cksize = GET_DWORD(); lpData = npMCI->lp; len -= ((cksize + 1) & ~1) + 8; SKIP_BYTES((cksize + 1) & ~1); if (StreamFromFOURCC(ckid) != (UINT)npMCI->nAudioStream) continue; dwBytesThisChunk = cksize; if (!dwBytesTotal) { if (!WaitForFreeAudioBuffer(npMCI, pfHurryUp)) /* We had to stop waiting--the stop flag was probably set. */ goto exit; } if (dwBytesThisChunk > npMCI->dwABSize - dwBytesTotal) { DPF(("Audio Record is too big!\n")); dwBytesThisChunk = npMCI->dwABSize - dwBytesTotal; } hmemcpy((BYTE _huge *)lpWaveHdr->lpData + dwBytesTotal, lpData, dwBytesThisChunk); dwBytesTotal += dwBytesThisChunk; } if (dwBytesTotal) { *pfPlayedAudio = TRUE; lpWaveHdr->dwBufferLength = dwBytesTotal; fRet = PlaySomeAudio(npMCI, lpWaveHdr); } /* Use the number of full audio buffers to decide if we're behind. */ if (npMCI->wABFull >= npMCI->wABOptimal) { *pfHurryUp = FALSE; } exit: npMCI->lp = lpSave; return fRet; } /* For "preload audio" or "random access audio" modes, do what needs ** to be done to keep our buffers full. */ BOOL NEAR PASCAL KeepPlayingAudio(NPMCIGRAPHIC npMCI) { LPWAVEHDR lpWaveHdr; DWORD dwBytesTotal = 0L; LONG lNewAudioPos; ////BOOL fFirstTime = TRUE; Assert(npMCI->wPlaybackAlg == MCIAVI_ALG_HARDDISK || npMCI->wPlaybackAlg == MCIAVI_ALG_AUDIOONLY); PlayMore: lpWaveHdr = ((LPWAVEHDR)npMCI->lpAudio) + npMCI->wNextAB; if (npMCI->dwFlags & MCIAVI_REVERSE) { lNewAudioPos = npMCI->dwAudioPos - npMCI->dwABSize; if (lNewAudioPos < 0) lNewAudioPos = 0; dwBytesTotal = npMCI->dwAudioPos - lNewAudioPos; } else { lNewAudioPos = npMCI->dwAudioPos + npMCI->dwABSize; if (lNewAudioPos > (LONG) npMCI->dwAudioLength) lNewAudioPos = npMCI->dwAudioLength; dwBytesTotal = lNewAudioPos - npMCI->dwAudioPos; } if (dwBytesTotal == 0) { if (npMCI->dwFlags & MCIAVI_WAVEPAUSED) { DOUT("no more audio to play, restarting wave device\n"); waveOutRestart(npMCI->hWave); ICDrawStart(npMCI->hicDraw); npMCI->dwFlags &= ~(MCIAVI_WAVEPAUSED); npMCI->nAudioBehind = 0; } return TRUE; } /* If all of the audio buffers are full, we have nothing to do */ if (npMCI->wABFull == npMCI->wABs) return TRUE; #if 0 //!!!! Should we be yielding at all in here? //!!! NO NO! not if updating!!!! if (!fFirstTime) { aviTaskYield(); } fFirstTime = FALSE; #endif if (npMCI->dwFlags & MCIAVI_REVERSE) npMCI->dwAudioPos = lNewAudioPos; #ifdef USEAVIFILE if (npMCI->pf) { LONG lPos; LONG lLength; lPos = npMCI->dwAudioPos / SH(npMCI->nAudioStream).dwSampleSize; lLength = dwBytesTotal / SH(npMCI->nAudioStream).dwSampleSize; AVIStreamRead(SI(npMCI->nAudioStream)->ps, lPos, lLength, lpWaveHdr->lpData, npMCI->dwABSize, NULL, NULL); } else #endif { if (!ReadSomeAudio(npMCI, lpWaveHdr->lpData, npMCI->dwAudioPos, &dwBytesTotal)) return FALSE; if (dwBytesTotal == 0) return TRUE; } if (!(npMCI->dwFlags & MCIAVI_REVERSE)) npMCI->dwAudioPos += dwBytesTotal; lpWaveHdr->dwBufferLength = dwBytesTotal; if (npMCI->dwFlags & MCIAVI_REVERSE) { ReverseWaveBuffer(npMCI, lpWaveHdr); } if (!PlaySomeAudio(npMCI, lpWaveHdr)) return FALSE; // return TRUE; goto PlayMore; } /* Play the current chunk's audio */ BOOL NEAR PASCAL HandleAudioChunk(NPMCIGRAPHIC npMCI) { LPWAVEHDR lpWaveHdr; FOURCC ckid; DWORD cksize; BYTE _huge *lpData; BOOL fRet = TRUE; BOOL fSilence; DWORD dwBytesTotal = 0L; DWORD dwBytesThisChunk; DWORD dwBytesThisBuffer; BOOL fHurryUp; Assert(npMCI->wPlaybackAlg == MCIAVI_ALG_CDROM); while ((DWORD) (npMCI->lp - npMCI->lpBuffer) < npMCI->dwThisRecordSize - 3 * sizeof(DWORD)) { /* Look at the next chunk */ ckid = GET_DWORD(); cksize = GET_DWORD(); lpData = npMCI->lp; SKIP_BYTES(cksize + (cksize & 1)); fSilence = (TWOCCFromFOURCC(ckid) == cktypeWAVEsilence); if (fSilence) { if (cksize != sizeof(DWORD)) { DPF(("Wave silence chunk of bad length!\n")); fRet = FALSE; npMCI->dwTaskError = MCIERR_INVALID_FILE; goto exit; } dwBytesThisChunk = PEEK_DWORD(); } else { dwBytesThisChunk = cksize; } while (dwBytesThisChunk > 0) { lpWaveHdr = ((LPWAVEHDR)npMCI->lpAudio) + npMCI->wNextAB; if (!WaitForFreeAudioBuffer(npMCI, &fHurryUp)) /* We had to stop waiting--the stop flag was probably set. */ goto exit; dwBytesThisBuffer = min(dwBytesThisChunk, npMCI->dwABSize - npMCI->dwUsedThisAB); if (!fSilence) { /* Move the data into the buffer */ hmemcpy((BYTE _huge *) lpWaveHdr->lpData + npMCI->dwUsedThisAB, lpData, dwBytesThisBuffer); lpData += dwBytesThisBuffer; } else { /* Fill the buffer with silence */ /* This isn't right for 16-bit! */ #ifndef WIN32 #pragma message("WAVE silence chunks don't work right now.") #endif // fmemfill((BYTE _huge *)lpWaveHdr->lpData + npMCI->dwUsedThisAB, // dwBytesThisBuffer, 0x80); } dwBytesThisChunk -= dwBytesThisBuffer; npMCI->dwUsedThisAB += dwBytesThisBuffer; // if (npMCI->dwUsedThisAB == npMCI->dwABSize) { lpWaveHdr->dwBufferLength = npMCI->dwUsedThisAB; fRet = PlaySomeAudio(npMCI, lpWaveHdr); // } } } exit: return fRet; } /****************************************************************************** *****************************************************************************/ /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api BOOL | StealWaveDevice | steal the audio device from another * instance of MCIAVI. * * @parm NPMCIGRAPHIC | npMCI | near ptr to the instance data * ***************************************************************************/ BOOL FAR PASCAL StealWaveDevice(NPMCIGRAPHIC npMCI) { extern NPMCIGRAPHIC npMCIList; // in graphic.c NPMCIGRAPHIC np; Assert(npMCI->hWave == NULL); DPF(("StealWaveDevice '%s' hTask=%04X\n", (LPSTR)npMCI->szFilename, GetCurrentTask())); // // walk the list of open MCIAVI instances and find one that // will give up the wave device // for (np=npMCIList; np; np = np->npMCINext) { if (np->hWave) { DPF(("**** Stealing the wave device from '%s'.\n", (LPSTR)np->szFilename)); //!!!should we call DeviceMute() or just call cleanup audio? // //!!!can this cause evil reenter cases? // //!!!we are calling this from another task, will this work ok? //!!!even in WIN32? mabey we should use SendMessage() #if 1 SendMessage(np->hwndDefault, WM_AUDIO_OFF, 0, 0); #else np->dwFlags |= MCIAVI_LOSTAUDIO; DeviceMute(np, TRUE); np->dwFlags |= MCIAVI_LOSTAUDIO; #endif return TRUE; } } DPF(("StealWaveDevice can't find a device to steal\n")); return FALSE; } /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api BOOL | GiveWaveDevice | give away the audio device * instance of MCIAVI. * * @parm NPMCIGRAPHIC | npMCI | near ptr to the instance data * ***************************************************************************/ BOOL FAR PASCAL GiveWaveDevice(NPMCIGRAPHIC npMCI) { extern NPMCIGRAPHIC npMCIList; // in graphic.c NPMCIGRAPHIC np; Assert(npMCI->hWave == NULL); DPF(("GiveWaveDevice '%s' hTask=%04X\n", (LPSTR)npMCI->szFilename, GetCurrentTask())); // // walk the list of open MCIAVI instances and find one that // will give up the wave device // for (np=npMCIList; np; np = np->npMCINext) { if (np->dwFlags & MCIAVI_LOSTAUDIO) { DPF(("**** Giving the wave device to '%s'.\n", (LPSTR)np->szFilename)); PostMessage(np->hwndDefault, WM_AUDIO_ON, 0, 0); return TRUE; } } return FALSE; } #ifndef WIN32 #pragma alloc_text(FIX, mciaviWaveOutFunc) #pragma optimize("", off) #endif void FAR PASCAL _LOADDS mciaviWaveOutFunc(HWAVEOUT hWaveOut, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { NPMCIGRAPHIC npMCI; LPWAVEHDR lpwh; #ifndef WIN32 #ifndef WANT_286 // If compiling -G3 we need to save the 386 registers _asm _emit 0x66 ; pushad _asm _emit 0x60 #endif #endif npMCI = (NPMCIGRAPHIC)(UINT)dwInstance; lpwh = (LPWAVEHDR) dwParam1; switch(wMsg) { case MM_WOM_DONE: npMCI->wABFull--; npMCI->dwAudioPlayed += lpwh->dwBufferLength; npMCI->dwTimingStart = timeGetTime(); break; } #ifndef WIN32 #ifndef WANT_286 // If compiling -G3 we need to restore the 386 registers _asm _emit 0x66 ; popad _asm _emit 0x61 #endif #endif } #ifndef WIN32 #pragma optimize("", off) #endif