Commit 0282825f authored by Francois Jacques's avatar Francois Jacques Committed by Alexandre Julliard

- All audio device handles are initialized to -1 and set to -1 when closed.

- WINE_WM_HEADER event should *NOT* make the recording thread start. The thread should only start through waveInStart. The application calling waveInAddBuffer might not be in a state to provide another buffer in a period of time short enough to avoid buffer underrun in widRecorder thread. - widRecorder - improved robustness of widRecorder to avoid some data loss that occured when not reading one full fragment from the OSS audio driver.
parent 135dfd75
...@@ -161,8 +161,16 @@ LONG OSS_WaveInit(void) ...@@ -161,8 +161,16 @@ LONG OSS_WaveInit(void)
int bytespersmpl; int bytespersmpl;
int caps; int caps;
int mask; int mask;
int i;
/* start with output device */
/* start with output device */
/* initialize all device handles to -1 */
for (i = 0; i < MAX_WAVEOUTDRV; ++i)
{
WOutDev[i].unixdev = -1;
}
/* FIXME: only one device is supported */ /* FIXME: only one device is supported */
memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps)); memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
...@@ -258,7 +266,14 @@ LONG OSS_WaveInit(void) ...@@ -258,7 +266,14 @@ LONG OSS_WaveInit(void)
/* then do input device */ /* then do input device */
samplesize = 16; samplesize = 16;
dsp_stereo = 1; dsp_stereo = 1;
for (i = 0; i < MAX_WAVEINDRV; ++i)
{
WInDev[i].unixdev = -1;
}
memset(&WInDev[0].caps, 0, sizeof(WInDev[0].caps));
if (access(SOUND_DEV,0) != 0 || if (access(SOUND_DEV,0) != 0 ||
(audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) { (audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) {
TRACE("Couldn't open in %s (%s)\n", SOUND_DEV, strerror(errno)); TRACE("Couldn't open in %s (%s)\n", SOUND_DEV, strerror(errno));
...@@ -835,7 +850,7 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) ...@@ -835,7 +850,7 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
if (fragment_size == -1) { if (fragment_size == -1) {
WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n"); WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
close(audio); close(audio);
wwo->unixdev = 0; wwo->unixdev = -1;
return MMSYSERR_NOTENABLED; return MMSYSERR_NOTENABLED;
} }
wwo->dwFragmentSize = fragment_size; wwo->dwFragmentSize = fragment_size;
...@@ -905,7 +920,7 @@ static DWORD wodClose(WORD wDevID) ...@@ -905,7 +920,7 @@ static DWORD wodClose(WORD wDevID)
} }
close(wwo->unixdev); close(wwo->unixdev);
wwo->unixdev = 0; wwo->unixdev = -1;
wwo->dwFragmentSize = 0; wwo->dwFragmentSize = 0;
if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
WARN("can't notify client !\n"); WARN("can't notify client !\n");
...@@ -1668,52 +1683,127 @@ static DWORD CALLBACK widRecorder(LPVOID pmt) ...@@ -1668,52 +1683,127 @@ static DWORD CALLBACK widRecorder(LPVOID pmt)
MSG msg; MSG msg;
DWORD bytesRead; DWORD bytesRead;
audio_buf_info info;
LPVOID buffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
wwi->dwFragmentSize);
LPVOID pOffset = buffer;
PeekMessageA(&msg, 0, 0, 0, 0); PeekMessageA(&msg, 0, 0, 0, 0);
wwi->state = WINE_WS_STOPPED; wwi->state = WINE_WS_STOPPED;
wwi->dwTotalRecorded = 0; wwi->dwTotalRecorded = 0;
TRACE("imhere[0]\n"); TRACE("imhere[0]\n");
SetEvent(wwi->hEvent); SetEvent(wwi->hEvent);
/* make sleep time to be # of ms to output a fragment */ /* make sleep time to be # of ms to output a fragment */
dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec; dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
for (;;) {
for (; ; ) {
/* wait for dwSleepTime or an event in thread's queue */ /* wait for dwSleepTime or an event in thread's queue */
/* FIXME: could improve wait time depending on queue state, /* FIXME: could improve wait time depending on queue state,
* ie, number of queued fragments * ie, number of queued fragments
*/ */
TRACE("imhere[1]\n"); TRACE("imhere[1]\n");
if (wwi->lpQueuePtr != NULL) { if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING) {
lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr = wwi->lpQueuePtr;
TRACE("recording buf=%p size=%lu/read=%lu \n",
lpWaveHdr->lpData, wwi->lpQueuePtr->dwBufferLength, lpWaveHdr->dwBytesRecorded); ioctl(wwi->unixdev, SNDCTL_DSP_GETISPACE, &info);
bytesRead = read(wwi->unixdev, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, if (info.fragments > 1)
lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded); {
if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded > wwi->dwFragmentSize)
if (bytesRead != (DWORD) -1) { {
TRACE("Read=%lu (%ld)\n", bytesRead, lpWaveHdr->dwBufferLength); /* directly read fragment in wavehdr */
lpWaveHdr->dwBytesRecorded += bytesRead; bytesRead = read(wwi->unixdev,
wwi->dwTotalRecorded += bytesRead; lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) { wwi->dwFragmentSize);
/* removes the current block from the queue */
wwi->lpQueuePtr = lpWaveHdr->lpNext; if (bytesRead != (DWORD) -1)
{
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; /* update number of bytes recorded in current buffer and by this device */
lpWaveHdr->dwFlags |= WHDR_DONE; lpWaveHdr->dwBytesRecorded += bytesRead;
wwi->dwTotalRecorded += bytesRead;
if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
WARN("can't notify client !\n"); /* buffer is full. notify client */
} if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
{
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
if (OSS_NotifyClient(uDevID,
WIM_DATA,
(DWORD)lpWaveHdr,
lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR)
{
WARN("can't notify client !\n");
}
lpWaveHdr = wwi->lpQueuePtr = lpWaveHdr->lpNext;
}
}
}
else
{
/* read fragment in our local buffer */
bytesRead = read(wwi->unixdev, buffer, wwi->dwFragmentSize);
pOffset = buffer;
/* copy data in client buffers */
while (bytesRead != (DWORD) -1 && bytesRead > 0)
{
DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
pOffset,
dwToCopy);
/* update number of bytes recorded in current buffer and by this device */
lpWaveHdr->dwBytesRecorded += dwToCopy;
wwi->dwTotalRecorded += dwToCopy;
bytesRead -= dwToCopy;
pOffset += dwToCopy;
/* client buffer is full. notify client */
if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
{
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
if (OSS_NotifyClient(uDevID,
WIM_DATA,
(DWORD)lpWaveHdr,
lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR)
{
WARN("can't notify client !\n");
}
if (lpWaveHdr->lpNext)
{
lpWaveHdr = lpWaveHdr->lpNext;
wwi->lpQueuePtr = lpWaveHdr;
}
else
{
/* no more buffer to copy data to, but we did read more.
* what hasn't been copied will be dropped
*/
if (bytesRead) WARN("buffer over run! %lu bytes dropped.\n", bytesRead);
wwi->lpQueuePtr = NULL;
break;
}
}
}
}
} }
} else { else {
TRACE("No data (%s)\n", strerror(errno)); TRACE("No data (%s)\n", strerror(errno));
} }
} }
MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE); MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE);
TRACE("imhere[2] (q=%p)\n", wwi->lpQueuePtr); TRACE("imhere[2] (q=%p)\n", wwi->lpQueuePtr);
...@@ -1737,8 +1827,6 @@ static DWORD CALLBACK widRecorder(LPVOID pmt) ...@@ -1737,8 +1827,6 @@ static DWORD CALLBACK widRecorder(LPVOID pmt)
for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
*wh = lpWaveHdr; *wh = lpWaveHdr;
} }
if (wwi->state == WINE_WS_STOPPED)
wwi->state = WINE_WS_PLAYING;
break; break;
case WINE_WM_RESETTING: case WINE_WM_RESETTING:
wwi->state = WINE_WS_STOPPED; wwi->state = WINE_WS_STOPPED;
...@@ -1760,6 +1848,7 @@ static DWORD CALLBACK widRecorder(LPVOID pmt) ...@@ -1760,6 +1848,7 @@ static DWORD CALLBACK widRecorder(LPVOID pmt)
wwi->hThread = 0; wwi->hThread = 0;
wwi->state = WINE_WS_CLOSED; wwi->state = WINE_WS_CLOSED;
SetEvent(wwi->hEvent); SetEvent(wwi->hEvent);
HeapFree(GetProcessHeap(), 0, buffer);
ExitThread(0); ExitThread(0);
/* shouldn't go here */ /* shouldn't go here */
default: default:
...@@ -1861,7 +1950,7 @@ static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) ...@@ -1861,7 +1950,7 @@ static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
if (fragment_size == -1) { if (fragment_size == -1) {
WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n"); WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
close(audio); close(audio);
wwi->unixdev = 0; wwi->unixdev = -1;
return MMSYSERR_NOTENABLED; return MMSYSERR_NOTENABLED;
} }
wwi->dwFragmentSize = fragment_size; wwi->dwFragmentSize = fragment_size;
...@@ -1906,7 +1995,7 @@ static DWORD widClose(WORD wDevID) ...@@ -1906,7 +1995,7 @@ static DWORD widClose(WORD wDevID)
WaitForSingleObject(wwi->hEvent, INFINITE); WaitForSingleObject(wwi->hEvent, INFINITE);
CloseHandle(wwi->hEvent); CloseHandle(wwi->hEvent);
close(wwi->unixdev); close(wwi->unixdev);
wwi->unixdev = 0; wwi->unixdev = -1;
wwi->dwFragmentSize = 0; wwi->dwFragmentSize = 0;
if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
WARN("can't notify client !\n"); WARN("can't notify client !\n");
...@@ -1934,10 +2023,12 @@ static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) ...@@ -1934,10 +2023,12 @@ static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
TRACE("header already in use !\n"); TRACE("header already in use !\n");
return WAVERR_STILLPLAYING; return WAVERR_STILLPLAYING;
} }
lpWaveHdr->dwFlags |= WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_INQUEUE;
lpWaveHdr->dwFlags &= ~WHDR_DONE; lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->dwBytesRecorded = 0; lpWaveHdr->dwBytesRecorded = 0;
lpWaveHdr->lpNext = NULL; lpWaveHdr->lpNext = NULL;
PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr); PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
return MMSYSERR_NOERROR; return MMSYSERR_NOERROR;
} }
...@@ -1988,10 +2079,9 @@ static DWORD widStart(WORD wDevID) ...@@ -1988,10 +2079,9 @@ static DWORD widStart(WORD wDevID)
WARN("can't start recording !\n"); WARN("can't start recording !\n");
return MMSYSERR_INVALHANDLE; return MMSYSERR_INVALHANDLE;
} }
PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0); PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE); WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
return MMSYSERR_NOERROR; return MMSYSERR_NOERROR;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment