- 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.
This commit is contained in:
parent
135dfd7576
commit
0282825f63
|
@ -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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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);
|
||||||
|
|
||||||
|
if (info.fragments > 1)
|
||||||
|
{
|
||||||
|
if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded > wwi->dwFragmentSize)
|
||||||
|
{
|
||||||
|
/* directly read fragment in wavehdr */
|
||||||
|
bytesRead = read(wwi->unixdev,
|
||||||
|
lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
|
||||||
|
wwi->dwFragmentSize);
|
||||||
|
|
||||||
bytesRead = read(wwi->unixdev, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
|
if (bytesRead != (DWORD) -1)
|
||||||
lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
|
{
|
||||||
|
/* update number of bytes recorded in current buffer and by this device */
|
||||||
|
lpWaveHdr->dwBytesRecorded += bytesRead;
|
||||||
|
wwi->dwTotalRecorded += bytesRead;
|
||||||
|
|
||||||
|
/* buffer is full. notify client */
|
||||||
|
if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
|
||||||
|
{
|
||||||
|
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
||||||
|
lpWaveHdr->dwFlags |= WHDR_DONE;
|
||||||
|
|
||||||
if (bytesRead != (DWORD) -1) {
|
if (OSS_NotifyClient(uDevID,
|
||||||
TRACE("Read=%lu (%ld)\n", bytesRead, lpWaveHdr->dwBufferLength);
|
WIM_DATA,
|
||||||
lpWaveHdr->dwBytesRecorded += bytesRead;
|
(DWORD)lpWaveHdr,
|
||||||
wwi->dwTotalRecorded += bytesRead;
|
lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR)
|
||||||
if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) {
|
{
|
||||||
/* removes the current block from the queue */
|
WARN("can't notify client !\n");
|
||||||
wwi->lpQueuePtr = lpWaveHdr->lpNext;
|
}
|
||||||
|
lpWaveHdr = wwi->lpQueuePtr = lpWaveHdr->lpNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* read fragment in our local buffer */
|
||||||
|
bytesRead = read(wwi->unixdev, buffer, wwi->dwFragmentSize);
|
||||||
|
pOffset = buffer;
|
||||||
|
|
||||||
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
/* copy data in client buffers */
|
||||||
lpWaveHdr->dwFlags |= WHDR_DONE;
|
while (bytesRead != (DWORD) -1 && bytesRead > 0)
|
||||||
|
{
|
||||||
if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
|
DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
|
||||||
WARN("can't notify client !\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue