|
|
|
@ -1,9 +1,9 @@
|
|
|
|
|
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..c99c1da
|
|
|
|
|
index 0000000..80a751d
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/dlls/winepulse.drv/Makefile.in
|
|
|
|
|
@@ -0,0 +1,15 @@
|
|
|
|
|
@@ -0,0 +1,16 @@
|
|
|
|
|
+TOPSRCDIR = @top_srcdir@
|
|
|
|
|
+TOPOBJDIR = ../..
|
|
|
|
|
+SRCDIR = @srcdir@
|
|
|
|
@ -11,6 +11,7 @@ index 0000000..c99c1da
|
|
|
|
|
+MODULE = winepulse.drv
|
|
|
|
|
+IMPORTS = winmm user32 kernel32
|
|
|
|
|
+EXTRALIBS = @PULSELIBS@
|
|
|
|
|
+EXTRACFLAGS = @PULSEINCL@
|
|
|
|
|
+
|
|
|
|
|
+C_SRCS = waveout.c \
|
|
|
|
|
+ wavein.c \
|
|
|
|
@ -21,10 +22,10 @@ index 0000000..c99c1da
|
|
|
|
|
+@DEPENDENCIES@ # everything below this line is overwritten by make depend
|
|
|
|
|
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..3dcb086
|
|
|
|
|
index 0000000..9dd1f80
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/dlls/winepulse.drv/pulse.c
|
|
|
|
|
@@ -0,0 +1,788 @@
|
|
|
|
|
@@ -0,0 +1,805 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * Wine Driver for PulseAudio
|
|
|
|
|
+ * http://pulseaudio.org/
|
|
|
|
@ -418,6 +419,23 @@ index 0000000..3dcb086
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * PULSE_StreamRequestCallback
|
|
|
|
|
+ *
|
|
|
|
|
+ * Called by the pulse mainloop whenever it wants/has audio data.
|
|
|
|
|
+ */
|
|
|
|
|
+void PULSE_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata) {
|
|
|
|
|
+ WINE_WAVEINST *ww = (WINE_WAVEINST*)userdata;
|
|
|
|
|
+
|
|
|
|
|
+ TRACE("Server has %u bytes\n", nbytes);
|
|
|
|
|
+
|
|
|
|
|
+ /* Make sure that the player/recorder is running */
|
|
|
|
|
+ if (ww->hThread != INVALID_HANDLE_VALUE && ww->msgRing.messages) {
|
|
|
|
|
+ PULSE_AddRingMessage(&ww->msgRing, WINE_WM_FEED, (DWORD)nbytes, FALSE);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * PULSE_StreamSuspendedCallback [internal]
|
|
|
|
|
+ *
|
|
|
|
|
+ * Called by the pulse mainloop any time stream playback is intentionally
|
|
|
|
@ -476,11 +494,11 @@ index 0000000..3dcb086
|
|
|
|
|
+ TRACE("Stream %p ready\n", userdata);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case PA_STREAM_TERMINATED:
|
|
|
|
|
+ case PA_STREAM_TERMINATED: /* Stream closed normally */
|
|
|
|
|
+ TRACE("Stream %p terminated\n", userdata);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case PA_STREAM_FAILED:
|
|
|
|
|
+ case PA_STREAM_FAILED: /* Stream closed not-normally */
|
|
|
|
|
+ ERR("Stream %p failed!\n", userdata);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
@ -532,7 +550,7 @@ index 0000000..3dcb086
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case PA_CONTEXT_FAILED:
|
|
|
|
|
+ ERR("Context failure: %s\n", pa_strerror(pa_context_errno(c)));
|
|
|
|
|
+ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
|
|
|
|
|
+ pa_threaded_mainloop_signal(PULSE_ml, 0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
@ -559,7 +577,7 @@ index 0000000..3dcb086
|
|
|
|
|
+ memset(&(wdi->caps.in), 0, sizeof(wdi->caps.in));
|
|
|
|
|
+ snprintf(wdi->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name);
|
|
|
|
|
+ wdi->device_name = pa_xstrdup(device);
|
|
|
|
|
+ MultiByteToWideChar(CP_ACP, 0, description, -1, wdi->caps.in.szPname, sizeof(wdi->caps.in.szPname)/sizeof(WCHAR));
|
|
|
|
|
+ MultiByteToWideChar(CP_UTF8, 0, description, -1, wdi->caps.in.szPname, sizeof(wdi->caps.in.szPname)/sizeof(WCHAR));
|
|
|
|
|
+ wdi->caps.in.szPname[sizeof(wdi->caps.in.szPname)/sizeof(WCHAR) - 1] = '\0';
|
|
|
|
|
+ wdi->caps.in.wMid = MM_CREATIVE;
|
|
|
|
|
+ wdi->caps.in.wPid = MM_CREATIVE_SBP16_WAVEOUT;
|
|
|
|
@ -608,7 +626,7 @@ index 0000000..3dcb086
|
|
|
|
|
+ wdo->volume.channels = v->channels;
|
|
|
|
|
+ for (x = 0; x < v->channels; x++) wdo->volume.values[x] = v->values[x];
|
|
|
|
|
+ snprintf(wdo->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name);
|
|
|
|
|
+ MultiByteToWideChar(CP_ACP, 0, description, -1, wdo->caps.out.szPname, sizeof(wdo->caps.out.szPname)/sizeof(WCHAR));
|
|
|
|
|
+ MultiByteToWideChar(CP_UTF8, 0, description, -1, wdo->caps.out.szPname, sizeof(wdo->caps.out.szPname)/sizeof(WCHAR));
|
|
|
|
|
+ wdo->caps.out.szPname[sizeof(wdo->caps.out.szPname)/sizeof(WCHAR) - 1] = '\0';
|
|
|
|
|
+ wdo->caps.out.wMid = MM_CREATIVE;
|
|
|
|
|
+ wdo->caps.out.wPid = MM_CREATIVE_SBP16_WAVEOUT;
|
|
|
|
@ -719,7 +737,7 @@ index 0000000..3dcb086
|
|
|
|
|
+ PULSE_ml = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ if (!(PULSE_ml = pa_threaded_mainloop_new())) {
|
|
|
|
|
+ WARN("Failed to create mainloop object.");
|
|
|
|
|
+ ERR("Failed to create mainloop object.");
|
|
|
|
|
+ return DRV_FAILURE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
@ -748,7 +766,6 @@ index 0000000..3dcb086
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+
|
|
|
|
|
+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(PULSE_context), PA_API_VERSION);
|
|
|
|
|
+ TRACE("Attempting to connect to pulseaudio server.\n");
|
|
|
|
|
+ if (pa_context_connect(PULSE_context, NULL, 0, NULL) < 0)
|
|
|
|
|
+ goto fail;
|
|
|
|
|
+
|
|
|
|
@ -784,7 +801,8 @@ index 0000000..3dcb086
|
|
|
|
|
+
|
|
|
|
|
+fail:
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ ERR("Failed to connect to server\n");
|
|
|
|
|
+ /* Only warn, because if we failed wine may still choose the next driver */
|
|
|
|
|
+ WARN("Failed to connect to server\n");
|
|
|
|
|
+ return DRV_FAILURE;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
@ -815,10 +833,10 @@ index 0000000..3dcb086
|
|
|
|
|
+}
|
|
|
|
|
diff --git a/dlls/winepulse.drv/wavein.c b/dlls/winepulse.drv/wavein.c
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..7cbc781
|
|
|
|
|
index 0000000..8aea538
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/dlls/winepulse.drv/wavein.c
|
|
|
|
|
@@ -0,0 +1,595 @@
|
|
|
|
|
@@ -0,0 +1,588 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * Wine Driver for PulseAudio - WaveIn Functionality
|
|
|
|
|
+ * http://pulseaudio.org/
|
|
|
|
@ -890,196 +908,188 @@ index 0000000..7cbc781
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * widRecorder_NextFragment [internal]
|
|
|
|
|
+ *
|
|
|
|
|
+ * Gets the next fragment of data from the server.
|
|
|
|
|
+ */
|
|
|
|
|
+static size_t widRecorder_NextFragment(WINE_WAVEINST *wwi) {
|
|
|
|
|
+ size_t nbytes;
|
|
|
|
|
+
|
|
|
|
|
+ TRACE("()\n");
|
|
|
|
|
+
|
|
|
|
|
+ if (wwi->buffer)
|
|
|
|
|
+ pa_stream_drop(wwi->stream);
|
|
|
|
|
+
|
|
|
|
|
+ pa_stream_peek(wwi->stream, &wwi->buffer, &nbytes);
|
|
|
|
|
+ wwi->buffer_length = nbytes;
|
|
|
|
|
+ wwi->buffer_read_offset = 0;
|
|
|
|
|
+
|
|
|
|
|
+ return nbytes;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * widRecorder_CopyData [internal]
|
|
|
|
|
+ *
|
|
|
|
|
+ * Copys data from the fragments pulse returns to queued buffers.
|
|
|
|
|
+ */
|
|
|
|
|
+static void widRecorder_CopyData(WINE_WAVEINST *wwi) {
|
|
|
|
|
+ LPWAVEHDR lpWaveHdr = wwi->lpQueuePtr;
|
|
|
|
|
+ size_t nbytes;
|
|
|
|
|
+
|
|
|
|
|
+ while (lpWaveHdr && wwi->state == WINE_WS_PLAYING) {
|
|
|
|
|
+ size_t bytes_avail;
|
|
|
|
|
+
|
|
|
|
|
+ nbytes = min(wwi->buffer_length - wwi->buffer_read_offset, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
|
|
|
|
|
+ if (nbytes == 0) break;
|
|
|
|
|
+ /* Get this value once and trust it. Note that the total available is made
|
|
|
|
|
+ * of one _or more_ fragments. These fragments will probably not align with
|
|
|
|
|
+ * the wavehdr buffer sizes. */
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ bytes_avail = pa_stream_readable_size(wwi->stream);
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+
|
|
|
|
|
+ TRACE("%u bytes from %p to %p\n",
|
|
|
|
|
+ nbytes,
|
|
|
|
|
+ (PBYTE)wwi->buffer + wwi->buffer_read_offset,
|
|
|
|
|
+ lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded);
|
|
|
|
|
+ if (bytes_avail == -1) {
|
|
|
|
|
+ ERR("pa_stream_readable_size() returned -1, record stream has failed.\n");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, (PBYTE)wwi->buffer + wwi->buffer_read_offset, nbytes);
|
|
|
|
|
+ /* If there is an already peeked buffer, add it to the total */
|
|
|
|
|
+ if (wwi->buffer)
|
|
|
|
|
+ bytes_avail += wwi->buffer_length - wwi->buffer_read_offset;
|
|
|
|
|
+
|
|
|
|
|
+ lpWaveHdr->dwBytesRecorded += nbytes;
|
|
|
|
|
+ wwi->buffer_read_offset += nbytes;
|
|
|
|
|
+ for (;bytes_avail && lpWaveHdr; lpWaveHdr = wwi->lpQueuePtr) {
|
|
|
|
|
+ size_t peek_avail;
|
|
|
|
|
+
|
|
|
|
|
+ if (wwi->buffer_read_offset == wwi->buffer_length) {
|
|
|
|
|
+ if (!wwi->buffer) {
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ pa_stream_drop(wwi->stream);
|
|
|
|
|
+ if (pa_stream_readable_size(wwi->stream))
|
|
|
|
|
+ widRecorder_NextFragment(wwi);
|
|
|
|
|
+ else {
|
|
|
|
|
+ wwi->buffer = NULL;
|
|
|
|
|
+ wwi->buffer_length = 0;
|
|
|
|
|
+ wwi->buffer_read_offset = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ pa_stream_peek(wwi->stream, &wwi->buffer, &wwi->buffer_length);
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ wwi->buffer_read_offset = 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (!wwi->buffer || !wwi->buffer_length) {
|
|
|
|
|
+ WARN("pa_stream_peek failed\n");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ peek_avail = min(wwi->buffer_length - wwi->buffer_read_offset,
|
|
|
|
|
+ lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
|
|
|
|
|
+
|
|
|
|
|
+ memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
|
|
|
|
|
+ (PBYTE)wwi->buffer + wwi->buffer_read_offset,
|
|
|
|
|
+ peek_avail);
|
|
|
|
|
+
|
|
|
|
|
+ wwi->buffer_read_offset += peek_avail;
|
|
|
|
|
+ lpWaveHdr->dwBytesRecorded += peek_avail;
|
|
|
|
|
+ bytes_avail -= peek_avail;
|
|
|
|
|
+
|
|
|
|
|
+ if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) {
|
|
|
|
|
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
|
|
|
|
+ lpWaveHdr->dwFlags |= WHDR_DONE;
|
|
|
|
|
+ wwi->lpQueuePtr = lpWaveHdr->lpNext;
|
|
|
|
|
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
|
|
|
|
|
+ lpWaveHdr = wwi->lpQueuePtr;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (wwi->buffer_read_offset == wwi->buffer_length) {
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ pa_stream_drop(wwi->stream);
|
|
|
|
|
+ wwi->buffer = NULL;
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ }
|
|
|
|
|
+ } /* for(bytes_avail && lpWaveHdr) */
|
|
|
|
|
+
|
|
|
|
|
+ return;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * widRecorder [internal]
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD CALLBACK widRecorder(LPVOID lpParam) {
|
|
|
|
|
+ WINE_WAVEINST *wwi = (WINE_WAVEINST*)lpParam;
|
|
|
|
|
+ LPWAVEHDR lpWaveHdr;
|
|
|
|
|
+static void widRecorder_ProcessMessages(WINE_WAVEINST* wwi) {
|
|
|
|
|
+ LPWAVEHDR lpWaveHdr;
|
|
|
|
|
+ enum win_wm_message msg;
|
|
|
|
|
+ DWORD param;
|
|
|
|
|
+ HANDLE ev;
|
|
|
|
|
+ DWORD wait = INFINITE;
|
|
|
|
|
+
|
|
|
|
|
+ wwi->state = WINE_WS_STOPPED;
|
|
|
|
|
+ SetEvent(wwi->hStartUpEvent);
|
|
|
|
|
+
|
|
|
|
|
+ for (;;) {
|
|
|
|
|
+
|
|
|
|
|
+ if (wwi->state != WINE_WS_PLAYING) {
|
|
|
|
|
+ wait = INFINITE;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (wwi->buffer == NULL && pa_stream_readable_size(wwi->stream)) {
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ wait = pa_bytes_to_usec(widRecorder_NextFragment(wwi), &wwi->sample_spec)/1000;
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ while (PULSE_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) {
|
|
|
|
|
+ TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
|
|
|
|
|
+
|
|
|
|
|
+ widRecorder_CopyData(wwi);
|
|
|
|
|
+ switch (msg) {
|
|
|
|
|
+ case WINE_WM_FEED:
|
|
|
|
|
+ /* Spin the loop in widRecorder */
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ PULSE_WaitRingMessage(&wwi->msgRing, wait);
|
|
|
|
|
+ case WINE_WM_STARTING:
|
|
|
|
|
+ wwi->dwLastReset = wwi->timing_info->read_index;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 0, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ wwi->state = WINE_WS_PLAYING;
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ while (PULSE_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) {
|
|
|
|
|
+ TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
|
|
|
|
|
+ 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;
|
|
|
|
|
+
|
|
|
|
|
+ switch (msg) {
|
|
|
|
|
+ case WINE_WM_FEED:
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case WINE_WM_STARTING:
|
|
|
|
|
+ wwi->state = WINE_WS_PLAYING;
|
|
|
|
|
+ if (wwi->lpQueuePtr)
|
|
|
|
|
+ wait = pa_bytes_to_usec(wwi->lpQueuePtr->dwBufferLength, &wwi->sample_spec)/1000;
|
|
|
|
|
+ else
|
|
|
|
|
+ wait = INFINITE;
|
|
|
|
|
+ wwi->dwLastReset = wwi->timing_info->read_index;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 0, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ wwi->state = WINE_WS_STOPPED;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+
|
|
|
|
|
+ /* 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)lpWaveHdr, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case WINE_WM_RESETTING:
|
|
|
|
|
+ if (wwi->state != WINE_WS_STOPPED) {
|
|
|
|
|
+ case WINE_WM_STOPPING:
|
|
|
|
|
+ if (wwi->state != WINE_WS_STOPPED) {
|
|
|
|
|
+ wwi->state = WINE_WS_STOPPED;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ if (wwi->buffer) {
|
|
|
|
|
+ pa_stream_drop(wwi->stream);
|
|
|
|
|
+ wwi->buffer = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+
|
|
|
|
|
+ /* return all buffers to the app */
|
|
|
|
|
+ for (lpWaveHdr = wwi->lpPlayPtr ? wwi->lpPlayPtr : wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = wwi->lpQueuePtr) {
|
|
|
|
|
+ /* return only the current buffer to app */
|
|
|
|
|
+ if ((lpWaveHdr = wwi->lpQueuePtr)) {
|
|
|
|
|
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
|
|
|
|
|
+ TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
|
|
|
|
|
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
|
|
|
|
+ lpWaveHdr->dwFlags |= WHDR_DONE;
|
|
|
|
|
+ wwi->lpQueuePtr = lpWaveHdr->lpNext;
|
|
|
|
|
+ wwi->lpQueuePtr = lpNext;
|
|
|
|
|
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case WINE_WM_CLOSING:
|
|
|
|
|
+ wwi->hThread = 0;
|
|
|
|
|
+ if ((DWORD)param == 1) {
|
|
|
|
|
+ /* If we are here, the stream failed */
|
|
|
|
|
+ wwi->state = WINE_WS_FAILED;
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ PULSE_DestroyRingMessage(&wwi->msgRing);
|
|
|
|
|
+ widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
|
|
|
|
|
+ wwi->lpPlayPtr = wwi->lpQueuePtr = NULL;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ pa_stream_disconnect(wwi->stream);
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ TRACE("Thread exiting because of failure.\n");
|
|
|
|
|
+ ExitThread(1);
|
|
|
|
|
+ case WINE_WM_RESETTING:
|
|
|
|
|
+ if (wwi->state != WINE_WS_STOPPED) {
|
|
|
|
|
+ wwi->state = WINE_WS_STOPPED;
|
|
|
|
|
+ pa_threaded_mainloop_lock(PULSE_ml);
|
|
|
|
|
+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL));
|
|
|
|
|
+ if (wwi->buffer) {
|
|
|
|
|
+ pa_stream_drop(wwi->stream);
|
|
|
|
|
+ wwi->buffer = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ wwi->state = WINE_WS_CLOSED;
|
|
|
|
|
+ SetEvent(ev);
|
|
|
|
|
+ ExitThread(0);
|
|
|
|
|
+ /* shouldn't go here */
|
|
|
|
|
+ default:
|
|
|
|
|
+ FIXME("unknown message %d\n", msg);
|
|
|
|
|
+ break;
|
|
|
|
|
+ } /* switch(msg) */
|
|
|
|
|
+ } /* while(PULSE_RetrieveRingMessage()) */
|
|
|
|
|
+ } /* for (;;) */
|
|
|
|
|
+ pa_threaded_mainloop_unlock(PULSE_ml);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* return all the buffers to the app */
|
|
|
|
|
+ lpWaveHdr = wwi->lpPlayPtr ? wwi->lpPlayPtr : wwi->lpQueuePtr;
|
|
|
|
|
+ for (; lpWaveHdr; lpWaveHdr = wwi->lpQueuePtr) {
|
|
|
|
|
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
|
|
|
|
+ lpWaveHdr->dwFlags |= WHDR_DONE;
|
|
|
|
|
+ wwi->lpQueuePtr = lpWaveHdr->lpNext;
|
|
|
|
|
+ widNotifyClient(wwi, WIM_DATA, (DWORD)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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * widRecorder [internal]
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD CALLBACK widRecorder(LPVOID lpParam) {
|
|
|
|
|
+ WINE_WAVEINST *wwi = (WINE_WAVEINST*)lpParam;
|
|
|
|
|
+
|
|
|
|
|
+ wwi->state = WINE_WS_STOPPED;
|
|
|
|
|
+ SetEvent(wwi->hStartUpEvent);
|
|
|
|
|
+
|
|
|
|
|
+ for (;;) {
|
|
|
|
|
+ PULSE_WaitRingMessage(&wwi->msgRing, INFINITE);
|
|
|
|
|
+ widRecorder_ProcessMessages(wwi);
|
|
|
|
|
+ if (wwi->state == WINE_WS_PLAYING && wwi->lpQueuePtr)
|
|
|
|
|
+ widRecorder_CopyData(wwi);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
@ -1139,7 +1149,8 @@ index 0000000..7cbc781
|
|
|
|
|
+ goto exit;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pa_stream_set_state_callback(wwi->stream, PULSE_StreamStateCallback, wwi);
|
|
|
|
|
+ pa_stream_set_state_callback(wwi->stream, PULSE_StreamStateCallback, wwi);
|
|
|
|
|
+ pa_stream_set_read_callback (wwi->stream, PULSE_StreamRequestCallback, wwi);
|
|
|
|
|
+
|
|
|
|
|
+ wwi->buffer_attr.maxlength = (uint32_t)-1;
|
|
|
|
|
+ wwi->buffer_attr.fragsize = pa_bytes_per_second(&wwi->sample_spec) / 100;
|
|
|
|
@ -1327,7 +1338,7 @@ index 0000000..7cbc781
|
|
|
|
|
+ * Context-sanity check here, as if we respond with 0, WINE will move on
|
|
|
|
|
+ * to the next wavein driver.
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD widGetNumDevs() {
|
|
|
|
|
+static DWORD widGetNumDevs(void) {
|
|
|
|
|
+ if (pa_context_get_state(PULSE_context) != PA_CONTEXT_READY)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
@ -1340,7 +1351,7 @@ index 0000000..7cbc781
|
|
|
|
|
+static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
|
|
|
|
|
+ TRACE("(%u, %p)\n", wDevID, dwParam1);
|
|
|
|
|
+
|
|
|
|
|
+ *dwParam1 = MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ *dwParam1 = MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ NULL, 0 ) * sizeof(WCHAR);
|
|
|
|
|
+ return MMSYSERR_NOERROR;
|
|
|
|
|
+}
|
|
|
|
@ -1349,10 +1360,10 @@ index 0000000..7cbc781
|
|
|
|
|
+ * widDevInterface [internal]
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) {
|
|
|
|
|
+ if (dwParam2 >= MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ if (dwParam2 >= MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ NULL, 0 ) * sizeof(WCHAR))
|
|
|
|
|
+ {
|
|
|
|
|
+ MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1,
|
|
|
|
|
+ dwParam1, dwParam2 / sizeof(WCHAR));
|
|
|
|
|
+ return MMSYSERR_NOERROR;
|
|
|
|
|
+ }
|
|
|
|
@ -1416,10 +1427,10 @@ index 0000000..7cbc781
|
|
|
|
|
+#endif /* HAVE_PULSEAUDIO */
|
|
|
|
|
diff --git a/dlls/winepulse.drv/waveout.c b/dlls/winepulse.drv/waveout.c
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..e7454fd
|
|
|
|
|
index 0000000..989dff5
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/dlls/winepulse.drv/waveout.c
|
|
|
|
|
@@ -0,0 +1,1040 @@
|
|
|
|
|
@@ -0,0 +1,1024 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * Wine Driver for PulseAudio - WaveOut Functionality
|
|
|
|
|
+ * http://pulseaudio.org/
|
|
|
|
@ -1490,22 +1501,6 @@ index 0000000..e7454fd
|
|
|
|
|
+ *======================================================================*/
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * WAVEOUT_StreamRequestCallback
|
|
|
|
|
+ *
|
|
|
|
|
+ * Called by the pulse mainloop whenever it wants audio data.
|
|
|
|
|
+ */
|
|
|
|
|
+static void WAVEOUT_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata) {
|
|
|
|
|
+ WINE_WAVEINST *ww = (WINE_WAVEINST*)userdata;
|
|
|
|
|
+
|
|
|
|
|
+ TRACE("Asking to be fed %u bytes\n", nbytes);
|
|
|
|
|
+
|
|
|
|
|
+ /* Make sure that the player/recorder is running */
|
|
|
|
|
+ if (ww->hThread != INVALID_HANDLE_VALUE && ww->msgRing.messages) {
|
|
|
|
|
+ PULSE_AddRingMessage(&ww->msgRing, WINE_WM_FEED, (DWORD)nbytes, FALSE);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**************************************************************************
|
|
|
|
|
+ * WAVEOUT_SinkInputInfoCallback [internal]
|
|
|
|
|
+ *
|
|
|
|
|
+ * Called by the pulse thread. Used for wodGetVolume.
|
|
|
|
@ -2033,7 +2028,7 @@ index 0000000..e7454fd
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Setup callbacks */
|
|
|
|
|
+ pa_stream_set_write_callback (wwo->stream, WAVEOUT_StreamRequestCallback, wwo);
|
|
|
|
|
+ pa_stream_set_write_callback (wwo->stream, PULSE_StreamRequestCallback, wwo);
|
|
|
|
|
+ pa_stream_set_state_callback (wwo->stream, PULSE_StreamStateCallback, wwo);
|
|
|
|
|
+ pa_stream_set_underflow_callback (wwo->stream, PULSE_StreamUnderflowCallback, wwo);
|
|
|
|
|
+ pa_stream_set_moved_callback (wwo->stream, PULSE_StreamMovedCallback, wwo);
|
|
|
|
@ -2373,7 +2368,7 @@ index 0000000..e7454fd
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
|
|
|
|
|
+
|
|
|
|
|
+ *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1, NULL, 0) * sizeof(WCHAR);
|
|
|
|
|
+ *dwParam1 = MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1, NULL, 0) * sizeof(WCHAR);
|
|
|
|
|
+ return MMSYSERR_NOERROR;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
@ -2381,10 +2376,10 @@ index 0000000..e7454fd
|
|
|
|
|
+ * wodDevInterface [internal]
|
|
|
|
|
+ */
|
|
|
|
|
+static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) {
|
|
|
|
|
+ if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
|
|
|
|
|
+ if (dwParam2 >= MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1,
|
|
|
|
|
+ NULL, 0 ) * sizeof(WCHAR))
|
|
|
|
|
+ {
|
|
|
|
|
+ MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
|
|
|
|
|
+ MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1,
|
|
|
|
|
+ dwParam1, dwParam2 / sizeof(WCHAR));
|
|
|
|
|
+ return MMSYSERR_NOERROR;
|
|
|
|
|
+ }
|
|
|
|
@ -2471,10 +2466,10 @@ index 0000000..1b49460
|
|
|
|
|
+@ stdcall -private widMessage(long long long long long long) PULSE_widMessage
|
|
|
|
|
diff --git a/dlls/winepulse.drv/winepulse.h b/dlls/winepulse.drv/winepulse.h
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..0aa7e86
|
|
|
|
|
index 0000000..b83de5d
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/dlls/winepulse.drv/winepulse.h
|
|
|
|
|
@@ -0,0 +1,196 @@
|
|
|
|
|
@@ -0,0 +1,197 @@
|
|
|
|
|
+/* Definitions for PulseAudio Wine Driver
|
|
|
|
|
+ *
|
|
|
|
|
+ * Copyright 2009 Arthur Taylor <theycallhimart@gmail.com>
|
|
|
|
@ -2648,6 +2643,7 @@ index 0000000..0aa7e86
|
|
|
|
|
+DWORD PULSE_WidNumDevs;
|
|
|
|
|
+
|
|
|
|
|
+/* pulse.c: PulseAudio Async Callbacks */
|
|
|
|
|
+void PULSE_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata);
|
|
|
|
|
+void PULSE_StreamSuccessCallback(pa_stream *s, int success, void *userdata);
|
|
|
|
|
+void PULSE_StreamStateCallback(pa_stream *s, void *userdata);
|
|
|
|
|
+void PULSE_StreamUnderflowCallback(pa_stream *s, void *userdata);
|