/****************************************************************** * ALSA_AddRingMessage * * Inserts a new message into the ring (should be called from DriverProc derived routines) */ int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD_PTR param, BOOL wait) { HANDLE hEvent = NULL; EnterCriticalSection(&omr->msg_crst); if (omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)) { int old_ring_buffer_size = omr->ring_buffer_size; omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT; omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG)); /* Now we need to rearrange the ring buffer so that the new buffers just allocated are in between omr->msg_tosave and omr->msg_toget. */ if (omr->msg_tosave < omr->msg_toget) { memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]), &(omr->messages[omr->msg_toget]), sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget) ); omr->msg_toget += ALSA_RING_BUFFER_INCREMENT; } } if (wait) { hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (!hEvent) { ERR("can't create event !?\n"); LeaveCriticalSection(&omr->msg_crst); return 0; } if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER) FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n", omr->msg_toget,ALSA_getCmdString(omr->messages[omr->msg_toget].msg), omr->msg_tosave,ALSA_getCmdString(omr->messages[omr->msg_tosave].msg)); /* fast messages have to be added at the start of the queue */ omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size; omr->messages[omr->msg_toget].msg = msg; omr->messages[omr->msg_toget].param = param; omr->messages[omr->msg_toget].hEvent = hEvent; } else { omr->messages[omr->msg_tosave].msg = msg; omr->messages[omr->msg_tosave].param = param; omr->messages[omr->msg_tosave].hEvent = NULL; omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size; } LeaveCriticalSection(&omr->msg_crst); /* signal a new message */ SIGNAL_OMR(omr); if (wait) { /* wait for playback/record thread to have processed the message */ WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); } return 1; }
/************************************************************************** * widRecorder [internal] */ static DWORD CALLBACK widRecorder(LPVOID pmt) { WORD uDevID = (DWORD_PTR)pmt; WINE_WAVEDEV* wwi = &WInDev[uDevID]; WAVEHDR* lpWaveHdr; DWORD dwSleepTime; DWORD bytesRead; enum win_wm_message msg; DWORD_PTR param; HANDLE ev; DWORD frames_per_period; wwi->state = WINE_WS_STOPPED; InterlockedExchange((LONG*)&wwi->dwTotalRecorded, 0); wwi->lpQueuePtr = NULL; SetEvent(wwi->hStartUpEvent); /* make sleep time to be # of ms to output a period */ dwSleepTime = (wwi->dwPeriodSize * 1000) / wwi->format.Format.nAvgBytesPerSec; frames_per_period = snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize); TRACE("sleeptime=%d ms, total buffer length=%d ms (%d bytes)\n", dwSleepTime, wwi->dwBufferSize * 1000 / wwi->format.Format.nAvgBytesPerSec, wwi->dwBufferSize); for (;;) { /* wait for dwSleepTime or an event in thread's queue */ if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING) { DWORD frames; DWORD bytes; DWORD read; lpWaveHdr = wwi->lpQueuePtr; /* read all the fragments accumulated so far */ frames = snd_pcm_avail_update(wwi->pcm); bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames); TRACE("frames = %d bytes = %d state=%d\n", frames, bytes, snd_pcm_state(wwi->pcm)); if (snd_pcm_state(wwi->pcm) == SND_PCM_STATE_XRUN) { FIXME("Recovering from XRUN!\n"); snd_pcm_prepare(wwi->pcm); frames = snd_pcm_avail_update(wwi->pcm); bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames); snd_pcm_start(wwi->pcm); snd_pcm_forward(wwi->pcm, frames - snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize)); continue; } while (frames > 0 && wwi->lpQueuePtr) { TRACE("bytes = %d\n", bytes); if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded < bytes) { bytes = lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded; frames = snd_pcm_bytes_to_frames(wwi->pcm, bytes); } /* directly read fragment in wavehdr */ read = wwi->read(wwi->pcm, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames); bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read); TRACE("bytesRead=(%d(%d)/(%d)) -> (%d/%d)\n", bytesRead, read, frames, lpWaveHdr->dwBufferLength, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded); if (read != (DWORD) -1) { /* update number of bytes recorded in current buffer and by this device */ lpWaveHdr->dwBytesRecorded += bytesRead; InterlockedExchangeAdd((LONG*)&wwi->dwTotalRecorded, bytesRead); frames -= read; bytes -= bytesRead; /* buffer is full. notify client */ if (!snd_pcm_bytes_to_frames(wwi->pcm, lpWaveHdr->dwBytesRecorded - lpWaveHdr->dwBufferLength)) { /* must copy the value of next waveHdr, because we have no idea of what * will be done with the content of lpWaveHdr in callback */ LPWAVEHDR lpNext = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; wwi->lpQueuePtr = lpNext; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); lpWaveHdr = lpNext; } } else { WARN("read(%s, %p, %d) failed (%d/%s)\n", wwi->pcmname, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames, frames, snd_strerror(read)); } } } ALSA_WaitRingMessage(&wwi->msgRing, dwSleepTime); while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) { TRACE("msg=%s param=0x%lx\n", ALSA_getCmdString(msg), param); switch (msg) { case WINE_WM_PAUSING: wwi->state = WINE_WS_PAUSED; /*FIXME("Device should stop recording\n");*/ SetEvent(ev); break; case WINE_WM_STARTING: wwi->state = WINE_WS_PLAYING; snd_pcm_start(wwi->pcm); SetEvent(ev); break; case WINE_WM_HEADER: lpWaveHdr = (LPWAVEHDR)param; lpWaveHdr->lpNext = 0; /* insert buffer at the end of queue */ { LPWAVEHDR* wh; for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } break; case WINE_WM_STOPPING: if (wwi->state != WINE_WS_STOPPED) { snd_pcm_drain(wwi->pcm); /* read any headers in queue */ widRecorder_ReadHeaders(wwi); /* return current buffer to app */ lpWaveHdr = wwi->lpQueuePtr; if (lpWaveHdr) { LPWAVEHDR lpNext = lpWaveHdr->lpNext; TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; wwi->lpQueuePtr = lpNext; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); } } wwi->state = WINE_WS_STOPPED; SetEvent(ev); break; case WINE_WM_RESETTING: if (wwi->state != WINE_WS_STOPPED) { snd_pcm_drain(wwi->pcm); } wwi->state = WINE_WS_STOPPED; wwi->dwTotalRecorded = 0; /* read any headers in queue */ widRecorder_ReadHeaders(wwi); /* return all buffers to the app */ while (wwi->lpQueuePtr) { lpWaveHdr = wwi->lpQueuePtr; TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); wwi->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0); } SetEvent(ev); break; case WINE_WM_CLOSING: wwi->hThread = 0; wwi->state = WINE_WS_CLOSED; SetEvent(ev); ExitThread(0); /* shouldn't go here */ default: FIXME("unknown message %d\n", msg); break; } } } ExitThread(0); /* just for not generating compilation warnings... should never be executed */ return 0; }
/************************************************************************** * wodPlayer_ProcessMessages [internal] */ static void wodPlayer_ProcessMessages(WINE_WAVEDEV* wwo) { LPWAVEHDR lpWaveHdr; enum win_wm_message msg; DWORD_PTR param; HANDLE ev; int err; while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { TRACE("Received %s %lx\n", ALSA_getCmdString(msg), param); switch (msg) { case WINE_WM_PAUSING: if ( snd_pcm_state(wwo->pcm) == SND_PCM_STATE_RUNNING ) { if ( snd_pcm_hw_params_can_pause(wwo->hw_params) ) { err = snd_pcm_pause(wwo->pcm, 1); if ( err < 0 ) ERR("pcm_pause failed: %s\n", snd_strerror(err)); wwo->state = WINE_WS_PAUSED; } else { wodPlayer_Reset(wwo,FALSE); } } SetEvent(ev); break; case WINE_WM_RESTARTING: if (wwo->state == WINE_WS_PAUSED) { if ( snd_pcm_state(wwo->pcm) == SND_PCM_STATE_PAUSED ) { err = snd_pcm_pause(wwo->pcm, 0); if ( err < 0 ) ERR("pcm_pause failed: %s\n", snd_strerror(err)); } wwo->state = WINE_WS_PLAYING; } SetEvent(ev); break; case WINE_WM_HEADER: lpWaveHdr = (LPWAVEHDR)param; /* insert buffer at the end of queue */ { LPWAVEHDR* wh; for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } if (!wwo->lpPlayPtr) wodPlayer_BeginWaveHdr(wwo,lpWaveHdr); if (wwo->state == WINE_WS_STOPPED) wwo->state = WINE_WS_PLAYING; break; case WINE_WM_RESETTING: wodPlayer_Reset(wwo,TRUE); SetEvent(ev); break; case WINE_WM_BREAKLOOP: if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) { /* ensure exit at end of current loop */ wwo->dwLoops = 1; } SetEvent(ev); break; case WINE_WM_CLOSING: /* 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->hThread = 0; wwo->state = WINE_WS_CLOSED; SetEvent(ev); ExitThread(0); /* shouldn't go here */ default: FIXME("unknown message %d\n", msg); break; } } }