📄 audio.c
字号:
switch (wMsg) { case WOM_OPEN: case WOM_CLOSE: case WOM_DONE: if (wwo->wFlags != DCB_NULL && !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) { WARN("can't notify client !\n"); return MMSYSERR_ERROR; } break; default: FIXME("Unknown callback message %u\n", wMsg); return MMSYSERR_INVALPARAM; } return MMSYSERR_NOERROR;}/************************************************************************** * wodHelper_BeginWaveHdr [internal] * * Makes the specified lpWaveHdr the currently playing wave header. * If the specified wave header is a begin loop and we're not already in * a loop, setup the loop. */static void wodHelper_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr){ EnterCriticalSection(&wwo->access_crst); wwo->lpPlayPtr = lpWaveHdr; if (!lpWaveHdr) { LeaveCriticalSection(&wwo->access_crst); return; } if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) { if (wwo->lpLoopPtr) { WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); } else { TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr); wwo->lpLoopPtr = lpWaveHdr; /* Windows does not touch WAVEHDR.dwLoops, * so we need to make an internal copy */ wwo->dwLoops = lpWaveHdr->dwLoops; } } wwo->dwPartialOffset = 0; LeaveCriticalSection(&wwo->access_crst);}/************************************************************************** * wodHelper_PlayPtrNext [internal] * * Advance the play pointer to the next waveheader, looping if required. */static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo){ LPWAVEHDR lpWaveHdr; EnterCriticalSection(&wwo->access_crst); lpWaveHdr = wwo->lpPlayPtr; wwo->dwPartialOffset = 0; if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) { /* We're at the end of a loop, loop if required */ if (--wwo->dwLoops > 0) { wwo->lpPlayPtr = wwo->lpLoopPtr; } else { /* Handle overlapping loops correctly */ if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) { FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n"); /* shall we consider the END flag for the closing loop or for * the opening one or for both ??? * code assumes for closing loop only */ } else { lpWaveHdr = lpWaveHdr->lpNext; } wwo->lpLoopPtr = NULL; wodHelper_BeginWaveHdr(wwo, lpWaveHdr); } } else { /* We're not in a loop. Advance to the next wave header */ TRACE("not inside of a loop, advancing to next wave header\n"); wodHelper_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext); } LeaveCriticalSection(&wwo->access_crst); return lpWaveHdr;}/* if force is TRUE then notify the client that all the headers were completed */static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force){ LPWAVEHDR lpWaveHdr; DWORD retval; TRACE("called\n"); EnterCriticalSection(&wwo->access_crst); /* Start from lpQueuePtr and keep notifying until: * - we hit an unwritten wavehdr * - we hit the beginning of a running loop * - we hit a wavehdr which hasn't finished playing */ while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr))) { wwo->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; TRACE("calling notify client\n"); wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0); } retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ? 0 : INFINITE; LeaveCriticalSection(&wwo->access_crst); return retval;}/************************************************************************** * wodHelper_Reset [internal] * * Resets current output stream. */static void wodHelper_Reset(WINE_WAVEOUT* wwo, BOOL reset){ EnterCriticalSection(&wwo->access_crst); /* updates current notify list */ wodHelper_NotifyCompletions(wwo, FALSE); if (reset) { /* remove all wave headers and notify client that all headers were completed */ wodHelper_NotifyCompletions(wwo, TRUE); wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; wwo->state = WINE_WS_STOPPED; wwo->dwPlayedTotal = wwo->dwWrittenTotal = wwo->bytesInJack = 0; wwo->dwPartialOffset = 0; /* Clear partial wavehdr */ } else { if (wwo->lpLoopPtr) { /* complicated case, not handled yet (could imply modifying the loop counter) */ FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n"); wwo->lpPlayPtr = wwo->lpLoopPtr; wwo->dwPartialOffset = 0; wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */ } else { LPWAVEHDR ptr; DWORD sz = wwo->dwPartialOffset; /* reset all the data as if we had written only up to lpPlayedTotal bytes */ /* compute the max size playable from lpQueuePtr */ for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) { sz += ptr->dwBufferLength; } /* because the reset lpPlayPtr will be lpQueuePtr */ if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("doh\n"); wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal); wwo->dwWrittenTotal = wwo->dwPlayedTotal; wwo->lpPlayPtr = wwo->lpQueuePtr; } wwo->state = WINE_WS_PAUSED; } LeaveCriticalSection(&wwo->access_crst);}/************************************************************************** * wodGetDevCaps [internal] */static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize){ TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); if (lpCaps == NULL) return MMSYSERR_NOTENABLED; if (wDevID >= MAX_WAVEOUTDRV) { TRACE("MAX_WAVOUTDRV reached !\n"); return MMSYSERR_BADDEVICEID; } memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); return MMSYSERR_NOERROR;}/************************************************************************** * wodOpen [internal] */static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags){ WINE_WAVEOUT* wwo; DWORD retval; TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } if (wDevID >= MAX_WAVEOUTDRV) { TRACE("MAX_WAVOUTDRV reached !\n"); return MMSYSERR_BADDEVICEID; }#if JACK_CLOSE_HACK if(WOutDev[wDevID].client && WOutDev[wDevID].in_use)#else if(WOutDev[wDevID].client)#endif { TRACE("device %d already allocated\n", wDevID); return MMSYSERR_ALLOCATED; } /* make sure we aren't being opened in 8 bit mode */ if(lpDesc->lpFormat->wBitsPerSample == 8) { TRACE("8bits per sample unsupported, returning WAVERR_BADFORMAT\n"); return WAVERR_BADFORMAT; } wwo = &WOutDev[wDevID]; wwo->wDevID = wDevID; /* Set things up before we call JACK_OpenDevice because */ /* we will start getting callbacks before JACK_OpenDevice */ /* even returns and we want to be initialized before then */ wwo->state = WINE_WS_STOPPED; /* start in a stopped state */ wwo->dwPlayedTotal = 0; /* zero out these totals */ wwo->dwWrittenTotal = 0; wwo->bytesInJack = 0; wwo->tickCountMS = 0; InitializeCriticalSection(&wwo->access_crst); /* initialize the critical section */ /* open up jack ports for this device */ if (!JACK_OpenDevice(&WOutDev[wDevID])) { ERR("JACK_OpenDevice(%d) failed\n", wDevID); return MMSYSERR_ERROR; /* return unspecified error */ } /* only PCM format is supported so far... */ if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM || lpDesc->lpFormat->nChannels == 0 || lpDesc->lpFormat->nSamplesPerSec == 0) { WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return WAVERR_BADFORMAT; } if (dwFlags & WAVE_FORMAT_QUERY) { TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return MMSYSERR_NOERROR; } dwFlags &= ~WAVE_DIRECTSOUND; /* direct sound not supported, ignore the flag */ EnterCriticalSection(&wwo->access_crst); wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC)); memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT)); LeaveCriticalSection(&wwo->access_crst); /* display the current wave format */ TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n", wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec, wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels, wwo->format.wf.nBlockAlign); /* make sure that we have the same sample rate in our audio stream */ /* as we do in the jack server */ if(wwo->format.wf.nSamplesPerSec != wwo->sample_rate) { TRACE("error: jack server sample rate is '%ld', wave sample rate is '%ld'\n", wwo->sample_rate, wwo->format.wf.nSamplesPerSec);#if JACK_CLOSE_HACK JACK_CloseDevice(wwo, FALSE); /* close this device, don't force the client to close */#else JACK_CloseDevice(wwo); /* close this device */#endif return WAVERR_BADFORMAT; } /* check for an invalid number of bits per sample */ if (wwo->format.wBitsPerSample == 0) { WARN("Resetting zeroed wBitsPerSample to 16\n"); wwo->format.wBitsPerSample = 16 * (wwo->format.wf.nAvgBytesPerSec / wwo->format.wf.nSamplesPerSec) / wwo->format.wf.nChannels; } EnterCriticalSection(&wwo->access_crst); retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L); LeaveCriticalSection(&wwo->access_crst); return retval;}/************************************************************************** * wodClose [internal] */static DWORD wodClose(WORD wDevID){ DWORD ret = MMSYSERR_NOERROR; WINE_WAVEOUT* wwo; TRACE("(%u);\n", wDevID); if (wDevID >= MAX_WAVEOUTDRV || !WOutDev[wDevID].client) { WARN("bad device ID !\n"); return MMSYSERR_BADDEVICEID; } wwo = &WOutDev[wDevID]; if (wwo->lpQueuePtr) { WARN("buffers still playing !\n"); ret = WAVERR_STILLPLAYING; } else { /* sanity check: this should not happen since the device must have been reset before */ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); wwo->state = WINE_WS_CLOSED; /* mark the device as closed */#if JACK_CLOSE_HACK JACK_CloseDevice(wwo, FALSE); /* close the jack device, DO NOT force the client to close */#else JACK_CloseDevice(wwo); /* close the jack device */ DeleteCriticalSection(&wwo->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */#endif ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L); } return ret;}/************************************************************************** * wodWrite [internal] * */static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize){ LPWAVEHDR*wh; WINE_WAVEOUT *wwo; TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); /* first, do the sanity checks... */ if (wDevID >= MAX_WAVEOUTDRV || !WOutDev[wDevID].client) { WARN("bad dev ID !\n"); return MMSYSERR_BADDEVICEID; } wwo = &WOutDev[wDevID]; if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) { TRACE("unprepared\n"); return WAVERR_UNPREPARED; } if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -