📄 mciwave.c
字号:
} } } else { wmw->hFile = 0; } } else { CHAR szTmpPath[MAX_PATH]; CHAR szPrefix[4] = "TMP\0"; pszTmpFileName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof(*pszTmpFileName)); if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) { WARN("can't retrieve temp path!\n"); HeapFree(GetProcessHeap(), 0, pszTmpFileName); return MCIERR_FILE_NOT_FOUND; } if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) { WARN("can't retrieve temp file name!\n"); HeapFree(GetProcessHeap(), 0, pszTmpFileName); return MCIERR_FILE_NOT_FOUND; } wmw->bTemporaryFile = TRUE; TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName); if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) { wmw->hFile = mmioOpenA(pszTmpFileName, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE); if (wmw->hFile == 0) { /* temporary file could not be created. clean filename. */ HeapFree(GetProcessHeap(), 0, pszTmpFileName); WARN("can't create file='%s' !\n", pszTmpFileName); dwRet = MCIERR_FILE_NOT_FOUND; } } } } } TRACE("hFile=%p\n", wmw->hFile); memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA)); if (wmw->bTemporaryFile == TRUE) { /* Additional openParms is temporary file's name */ wmw->openParms.lpstrElementName = pszTmpFileName; } if (dwRet == 0) { if (wmw->lpWaveFormat) { switch (wmw->lpWaveFormat->wFormatTag) { case WAVE_FORMAT_PCM: if (wmw->lpWaveFormat->nAvgBytesPerSec != wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n", wmw->lpWaveFormat->nAvgBytesPerSec, wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign); wmw->lpWaveFormat->nAvgBytesPerSec = wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign; } break; } } wmw->dwPosition = 0; wmw->dwStatus = MCI_MODE_STOP; } else { wmw->nUseCount--; if (wmw->hFile != 0) mmioClose(wmw->hFile, 0); wmw->hFile = 0; } return dwRet;}/************************************************************************** * WAVE_mciCue [internal] */static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms){ /* FIXME This routine is far from complete. At the moment only a check is done on the MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that is the default. The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT are ignored */ DWORD dwRet; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; /* always close elements ? */ if (wmw->hFile != 0) { mmioClose(wmw->hFile, 0); wmw->hFile = 0; } dwRet = MMSYSERR_NOERROR; /* assume success */ if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) { dwRet = waveOutClose(wmw->hWave); if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; wmw->fInput = TRUE; } else if (wmw->fInput) { dwRet = waveInClose(wmw->hWave); if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; wmw->fInput = FALSE; } wmw->hWave = 0; return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;}/************************************************************************** * WAVE_mciStop [internal] */static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms){ DWORD dwRet = 0; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; /* wait for playback thread (if any) to exit before processing further */ switch (wmw->dwStatus) { case MCI_MODE_PAUSE: case MCI_MODE_PLAY: case MCI_MODE_RECORD: { int oldStat = wmw->dwStatus; wmw->dwStatus = MCI_MODE_NOT_READY; if (oldStat == MCI_MODE_PAUSE) dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave); } while (wmw->dwStatus != MCI_MODE_STOP) Sleep(10); break; } wmw->dwPosition = 0; /* sanity resets */ wmw->dwStatus = MCI_MODE_STOP; if ((dwFlags & MCI_NOTIFY) && lpParms) { mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL); } return dwRet;}/************************************************************************** * WAVE_mciClose [internal] */static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms){ DWORD dwRet = 0; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmw->dwStatus != MCI_MODE_STOP) { dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms); } wmw->nUseCount--; if (wmw->nUseCount == 0) { if (wmw->hFile != 0) { mmioClose(wmw->hFile, 0); wmw->hFile = 0; } } /* That string got allocated in mciOpen because no filename was specified * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was * allocated by mciOpen, *NOT* the application. */ if (wmw->bTemporaryFile) { HeapFree(GetProcessHeap(), 0, (char*)wmw->openParms.lpstrElementName); wmw->openParms.lpstrElementName = NULL; } HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); wmw->lpWaveFormat = NULL; if ((dwFlags & MCI_NOTIFY) && lpParms) { mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wmw->openParms.wDeviceID, (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); } return 0;}/************************************************************************** * WAVE_mciPlayCallback [internal] */static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2){ WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; switch (uMsg) { case WOM_OPEN: case WOM_CLOSE: break; case WOM_DONE: InterlockedIncrement(&wmw->dwEventCount); TRACE("Returning waveHdr=%lx\n", dwParam1); SetEvent(wmw->hEvent); break; default: ERR("Unknown uMsg=%d\n", uMsg); }}/****************************************************************** * WAVE_mciPlayWaitDone * * */static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw){ for (;;) { ResetEvent(wmw->hEvent); if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { break; } InterlockedIncrement(&wmw->dwEventCount); WaitForSingleObject(wmw->hEvent, INFINITE); }}/************************************************************************** * WAVE_mciPlay [internal] */static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms){ DWORD end; LONG bufsize, count, left; DWORD dwRet = 0; LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); int whidx; TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; /* FIXME : since there is no way to determine in which mode the device is * open (recording/playback) automatically switch from a mode to another */ wmw->fInput = FALSE; if (wmw->fInput) { WARN("cannot play on input device\n"); return MCIERR_NONAPPLICABLE_FUNCTION; } if (wmw->hFile == 0) { WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName); return MCIERR_FILE_NOT_FOUND; } if (wmw->dwStatus == MCI_MODE_PAUSE) { /* FIXME: parameters (start/end) in lpParams may not be used */ return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); } /** This function will be called again by a thread when async is used. * We have to set MCI_MODE_PLAY before we do this so that the app can spin * on MCI_STATUS, so we have to allow it here if we're not going to start this thread. */ if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) { return MCIERR_INTERNAL; } wmw->dwStatus = MCI_MODE_PLAY; if (!(dwFlags & MCI_WAIT)) { return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms, sizeof(MCI_PLAY_PARMS)); } end = 0xFFFFFFFF; if (lpParms && (dwFlags & MCI_FROM)) { wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); } if (lpParms && (dwFlags & MCI_TO)) { end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); } TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end); if (end <= wmw->dwPosition) return TRUE;#define WAVE_ALIGN_ON_BLOCK(wmw,v) \((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition); wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize); if (dwRet == 0) { if (wmw->lpWaveFormat) { switch (wmw->lpWaveFormat->wFormatTag) { case WAVE_FORMAT_PCM: if (wmw->lpWaveFormat->nAvgBytesPerSec != wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n", wmw->lpWaveFormat->nAvgBytesPerSec, wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign); wmw->lpWaveFormat->nAvgBytesPerSec = wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign; } break; } } } else { TRACE("can't retrieve wave format %ld\n", dwRet); goto cleanUp; } /* go back to beginning of chunk plus the requested position */ /* FIXME: I'm not sure this is correct, notably because some data linked to * the decompression state machine will not be correcly initialized. * try it this way (other way would be to decompress from 0 up to dwPosition * and to start sending to hWave when dwPosition is reached) */ mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ /* By default the device will be opened for output, the MCI_CUE function is there to * change from output to input and back */ /* FIXME: how to choose between several output channels ? here mapper is forced */ dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION); if (dwRet != 0) { TRACE("Can't open low level audio device %ld\n", dwRet); dwRet = MCIERR_DEVICE_OPEN; wmw->hWave = 0; goto cleanUp; } /* make it so that 3 buffers per second are needed */ bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { dwRet = MCIERR_INTERNAL; goto cleanUp; } whidx = 0; left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition); wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); wmw->dwEventCount = 1L; /* for first buffer */ TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left); /* FIXME: this doesn't work if wmw->dwPosition != 0 */ while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left)); TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count); if (count < 1) break; /* count is always <= bufsize, so this is correct regarding the * waveOutPrepareHeader function */ waveHdr[whidx].dwBufferLength = count; waveHdr[whidx].dwFlags &= ~WHDR_DONE; TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n", &waveHdr[whidx], waveHdr[whidx].dwBufferLength, waveHdr[whidx].dwBytesRecorded); dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR)); left -= count; wmw->dwPosition += count;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -