Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Tue, May 11, 2021 at 06:31:45PM +0200, Jacek Caban wrote:
Signed-off-by: Jacek Caban jacek@codeweavers.com
dlls/winepulse.drv/mmdevdrv.c | 339 +------------------------------ dlls/winepulse.drv/pulse.c | 363 +++++++++++++++++++++++++++++++++- dlls/winepulse.drv/unixlib.h | 13 +- 3 files changed, 377 insertions(+), 338 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 4c962069c97..d0d497c4bbb 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -127,13 +127,6 @@ typedef struct _AudioSessionWrapper { AudioSession *session; } AudioSessionWrapper;
-typedef struct _ACPacket {
- struct list entry;
- UINT64 qpcpos;
- BYTE *data;
- UINT32 discont;
-} ACPacket;
struct ACImpl { IAudioClient3 IAudioClient3_iface; IAudioRenderClient IAudioRenderClient_iface; @@ -221,34 +214,6 @@ static DWORD CALLBACK pulse_mainloop_thread(void *tmp) { return 0; }
-static void pulse_stream_state(pa_stream *s, void *user) -{
- pa_stream_state_t state = pa_stream_get_state(s);
- TRACE("Stream state changed to %i\n", state);
- pulse->broadcast();
-}
-static const enum pa_channel_position pulse_pos_from_wfx[] = {
- PA_CHANNEL_POSITION_FRONT_LEFT,
- PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER,
- PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT,
- PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
- PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
- PA_CHANNEL_POSITION_REAR_CENTER,
- PA_CHANNEL_POSITION_SIDE_LEFT,
- PA_CHANNEL_POSITION_SIDE_RIGHT,
- PA_CHANNEL_POSITION_TOP_CENTER,
- PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
- PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
- PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
- PA_CHANNEL_POSITION_TOP_REAR_LEFT,
- PA_CHANNEL_POSITION_TOP_REAR_CENTER,
- PA_CHANNEL_POSITION_TOP_REAR_RIGHT
-};
static char *get_application_name(void) { WCHAR path[MAX_PATH], *name; @@ -419,26 +384,12 @@ write: return pa_stream_write(This->pulse_stream->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); }
-static void dump_attr(const pa_buffer_attr *attr) {
- TRACE("maxlength: %u\n", attr->maxlength);
- TRACE("minreq: %u\n", attr->minreq);
- TRACE("fragsize: %u\n", attr->fragsize);
- TRACE("tlength: %u\n", attr->tlength);
- TRACE("prebuf: %u\n", attr->prebuf);
-}
static void pulse_op_cb(pa_stream *s, int success, void *user) { TRACE("Success: %i\n", success); *(int*)user = success; pulse->broadcast(); }
-static void pulse_attr_update(pa_stream *s, void *user) {
- const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
- TRACE("New attributes or device moved:\n");
- dump_attr(attr);
-}
static void pulse_write(ACImpl *This) { /* write as much data to PA as we can */ @@ -483,21 +434,6 @@ static void pulse_write(ACImpl *This) This->pulse_stream->pa_held_bytes -= to_write; }
-static void pulse_underflow_callback(pa_stream *s, void *userdata) -{
- ACImpl *This = userdata;
- WARN("%p: Underflow\n", userdata);
- This->pulse_stream->just_underran = TRUE;
- /* re-sync */
- This->pulse_stream->pa_offs_bytes = This->pulse_stream->lcl_offs_bytes;
- This->pulse_stream->pa_held_bytes = This->pulse_stream->held_bytes;
-}
-static void pulse_started_callback(pa_stream *s, void *userdata) -{
- TRACE("%p: (Re)started playing\n", userdata);
-}
static void pulse_read(ACImpl *This) { size_t bytes = pa_stream_readable_size(This->pulse_stream->stream); @@ -680,58 +616,6 @@ static DWORD WINAPI pulse_timer_cb(void *user) return 0; }
-static HRESULT pulse_stream_connect(ACImpl *This, pa_context *pulse_ctx, UINT32 period_bytes) {
- int ret;
- char buffer[64];
- static LONG number;
- pa_buffer_attr attr;
- if (This->pulse_stream->stream) {
pa_stream_disconnect(This->pulse_stream->stream);
while (pa_stream_get_state(This->pulse_stream->stream) == PA_STREAM_READY)
pulse->cond_wait();
pa_stream_unref(This->pulse_stream->stream);
- }
- ret = InterlockedIncrement(&number);
- sprintf(buffer, "audio stream #%i", ret);
- This->pulse_stream->stream = pa_stream_new(pulse_ctx, buffer, &This->pulse_stream->ss, &This->pulse_stream->map);
- if (!This->pulse_stream->stream) {
WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- }
- pa_stream_set_state_callback(This->pulse_stream->stream, pulse_stream_state, This);
- pa_stream_set_buffer_attr_callback(This->pulse_stream->stream, pulse_attr_update, This);
- pa_stream_set_moved_callback(This->pulse_stream->stream, pulse_attr_update, This);
- /* PulseAudio will fill in correct values */
- attr.minreq = attr.fragsize = period_bytes;
- attr.tlength = period_bytes * 3;
- attr.maxlength = This->pulse_stream->bufsize_frames * pa_frame_size(&This->pulse_stream->ss);
- attr.prebuf = pa_frame_size(&This->pulse_stream->ss);
- dump_attr(&attr);
- if (This->dataflow == eRender)
ret = pa_stream_connect_playback(This->pulse_stream->stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY, NULL, NULL);
- else
ret = pa_stream_connect_record(This->pulse_stream->stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY);
- if (ret < 0) {
WARN("Returns %i\n", ret);
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- }
- while (pa_stream_get_state(This->pulse_stream->stream) == PA_STREAM_CREATING)
pulse->cond_wait();
- if (pa_stream_get_state(This->pulse_stream->stream) != PA_STREAM_READY)
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- if (This->dataflow == eRender) {
pa_stream_set_underflow_callback(This->pulse_stream->stream, pulse_underflow_callback, This);
pa_stream_set_started_callback(This->pulse_stream->stream, pulse_started_callback, This);
- }
- return S_OK;
-}
HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys, UINT *num, UINT *def_index) { @@ -936,32 +820,6 @@ static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt) return ret; }
-static DWORD get_channel_mask(unsigned int channels) -{
- switch(channels) {
- case 0:
return 0;
- case 1:
return KSAUDIO_SPEAKER_MONO;
- case 2:
return KSAUDIO_SPEAKER_STEREO;
- case 3:
return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
- case 4:
return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
- case 5:
return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
- case 6:
return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
- case 7:
return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
- case 8:
return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
- }
- FIXME("Unknown speaker configuration: %u\n", channels);
- return 0;
-}
static void session_init_vols(AudioSession *session, UINT channels) { if (session->channel_count < channels) { @@ -1041,129 +899,21 @@ static HRESULT get_audio_session(const GUID *sessionguid, return S_OK; }
-static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) -{
- pa_channel_map_init(&This->pulse_stream->map);
- This->pulse_stream->ss.rate = fmt->nSamplesPerSec;
- This->pulse_stream->ss.format = PA_SAMPLE_INVALID;
- switch(fmt->wFormatTag) {
- case WAVE_FORMAT_IEEE_FLOAT:
if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
break;
This->pulse_stream->ss.format = PA_SAMPLE_FLOAT32LE;
pa_channel_map_init_auto(&This->pulse_stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- case WAVE_FORMAT_PCM:
if (!fmt->nChannels || fmt->nChannels > 2)
break;
if (fmt->wBitsPerSample == 8)
This->pulse_stream->ss.format = PA_SAMPLE_U8;
else if (fmt->wBitsPerSample == 16)
This->pulse_stream->ss.format = PA_SAMPLE_S16LE;
else
return AUDCLNT_E_UNSUPPORTED_FORMAT;
pa_channel_map_init_auto(&This->pulse_stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- case WAVE_FORMAT_EXTENSIBLE: {
WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
DWORD mask = wfe->dwChannelMask;
DWORD i = 0, j;
if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
break;
if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
(!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
fmt->wBitsPerSample == 32)
This->pulse_stream->ss.format = PA_SAMPLE_FLOAT32LE;
else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
DWORD valid = wfe->Samples.wValidBitsPerSample;
if (!valid)
valid = fmt->wBitsPerSample;
if (!valid || valid > fmt->wBitsPerSample)
break;
switch (fmt->wBitsPerSample) {
case 8:
if (valid == 8)
This->pulse_stream->ss.format = PA_SAMPLE_U8;
break;
case 16:
if (valid == 16)
This->pulse_stream->ss.format = PA_SAMPLE_S16LE;
break;
case 24:
if (valid == 24)
This->pulse_stream->ss.format = PA_SAMPLE_S24LE;
break;
case 32:
if (valid == 24)
This->pulse_stream->ss.format = PA_SAMPLE_S24_32LE;
else if (valid == 32)
This->pulse_stream->ss.format = PA_SAMPLE_S32LE;
break;
default:
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
}
This->pulse_stream->map.channels = fmt->nChannels;
if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
mask = get_channel_mask(fmt->nChannels);
for (j = 0; j < ARRAY_SIZE(pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
if (mask & (1 << j))
This->pulse_stream->map.map[i++] = pulse_pos_from_wfx[j];
}
/* Special case for mono since pulse appears to map it differently */
if (mask == SPEAKER_FRONT_CENTER)
This->pulse_stream->map.map[0] = PA_CHANNEL_POSITION_MONO;
if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
This->pulse_stream->map.channels = 0;
ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
break;
}
break;
}
- case WAVE_FORMAT_ALAW:
- case WAVE_FORMAT_MULAW:
if (fmt->wBitsPerSample != 8) {
FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
if (fmt->nChannels != 1 && fmt->nChannels != 2) {
FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
This->pulse_stream->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
pa_channel_map_init_auto(&This->pulse_stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- default:
WARN("Unhandled tag %x\n", fmt->wFormatTag);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- This->channel_count = This->pulse_stream->ss.channels = This->pulse_stream->map.channels;
- if (!pa_channel_map_valid(&This->pulse_stream->map) || This->pulse_stream->ss.format == PA_SAMPLE_INVALID) {
ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->pulse_stream->map), This->pulse_stream->ss.format);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- return S_OK;
-}
static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period, const WAVEFORMATEX *fmt, const GUID *sessionguid) { ACImpl *This = impl_from_IAudioClient3(iface);
pa_context *pulse_ctx; char *name; HRESULT hr = S_OK;
UINT32 bufsize_bytes;
TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags, wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
if (!fmt) return E_POINTER;
dump_fmt(fmt);
if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) return E_INVALIDARG;
@@ -1204,92 +954,19 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, }
name = get_application_name();
- hr = pulse->connect(name, &pulse_ctx);
- hr = pulse->create_stream(name, This->dataflow, mode, flags, duration, period, fmt,
free(name);&This->channel_count, &This->pulse_stream);
- if (FAILED(hr)) {
pulse->unlock();
return hr;
- }
- if (!(This->pulse_stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->pulse_stream))))
return E_OUTOFMEMORY;
- This->pulse_stream->dataflow = This->dataflow;
- hr = pulse_spec_from_waveformat(This, fmt);
- TRACE("Obtaining format returns %08x\n", hr);
- dump_fmt(fmt);
- if (FAILED(hr))
goto exit;
- period = pulse_config.modes[This->dataflow == eCapture].def_period;
- if (duration < 3 * period)
duration = 3 * period;
- This->pulse_stream->period_bytes = pa_frame_size(&This->pulse_stream->ss) * MulDiv(period, This->pulse_stream->ss.rate, 10000000);
- This->pulse_stream->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
- bufsize_bytes = This->pulse_stream->bufsize_frames * pa_frame_size(&This->pulse_stream->ss);
- This->pulse_stream->mmdev_period_usec = period / 10;
- This->pulse_stream->share = mode;
- This->pulse_stream->flags = flags;
- hr = pulse_stream_connect(This, pulse_ctx, This->pulse_stream->period_bytes); if (SUCCEEDED(hr)) {
UINT32 unalign;
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->pulse_stream->stream);
This->pulse_stream->attr = *attr;
/* Update frames according to new size */
dump_attr(attr);
if (This->dataflow == eRender) {
This->pulse_stream->real_bufsize_bytes = This->pulse_stream->bufsize_frames * 2 * pa_frame_size(&This->pulse_stream->ss);
This->pulse_stream->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->pulse_stream->real_bufsize_bytes);
if(!This->pulse_stream->local_buffer)
hr = E_OUTOFMEMORY;
hr = get_audio_session(sessionguid, This->parent, This->channel_count, &This->session);
if (SUCCEEDED(hr)) {
list_add_tail(&This->session->clients, &This->entry); } else {
UINT32 i, capture_packets;
if ((unalign = bufsize_bytes % This->pulse_stream->period_bytes))
bufsize_bytes += This->pulse_stream->period_bytes - unalign;
This->pulse_stream->bufsize_frames = bufsize_bytes / pa_frame_size(&This->pulse_stream->ss);
This->pulse_stream->real_bufsize_bytes = bufsize_bytes;
capture_packets = This->pulse_stream->real_bufsize_bytes / This->pulse_stream->period_bytes;
This->pulse_stream->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->pulse_stream->real_bufsize_bytes + capture_packets * sizeof(ACPacket));
if (!This->pulse_stream->local_buffer)
hr = E_OUTOFMEMORY;
else {
ACPacket *cur_packet = (ACPacket*)((char*)This->pulse_stream->local_buffer + This->pulse_stream->real_bufsize_bytes);
BYTE *data = This->pulse_stream->local_buffer;
silence_buffer(This->pulse_stream->ss.format, This->pulse_stream->local_buffer, This->pulse_stream->real_bufsize_bytes);
list_init(&This->pulse_stream->packet_free_head);
list_init(&This->pulse_stream->packet_filled_head);
for (i = 0; i < capture_packets; ++i, ++cur_packet) {
list_add_tail(&This->pulse_stream->packet_free_head, &cur_packet->entry);
cur_packet->data = data;
data += This->pulse_stream->period_bytes;
}
}
pulse->release_stream(This->pulse_stream, NULL);
}This->pulse_stream = NULL; }
- if (SUCCEEDED(hr))
hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
- if (SUCCEEDED(hr))
list_add_tail(&This->session->clients, &This->entry);
-exit:
- if (FAILED(hr)) {
HeapFree(GetProcessHeap(), 0, This->pulse_stream->local_buffer);
This->pulse_stream->local_buffer = NULL;
if (This->pulse_stream->stream) {
pa_stream_disconnect(This->pulse_stream->stream);
pa_stream_unref(This->pulse_stream->stream);
}
HeapFree(GetProcessHeap(), 0, This->pulse_stream);
This->pulse_stream = NULL;
- } pulse->unlock(); return hr;
} diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 23b362c8689..260723815f6 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -24,6 +24,7 @@
#include <stdarg.h> #include <pthread.h> +#include <math.h> #include <poll.h>
#include <pulse/pulseaudio.h> @@ -77,6 +78,39 @@ static void WINAPI pulse_broadcast(void) pthread_cond_broadcast(&pulse_cond); }
+static void dump_attr(const pa_buffer_attr *attr) +{
- TRACE("maxlength: %u\n", attr->maxlength);
- TRACE("minreq: %u\n", attr->minreq);
- TRACE("fragsize: %u\n", attr->fragsize);
- TRACE("tlength: %u\n", attr->tlength);
- TRACE("prebuf: %u\n", attr->prebuf);
+}
+/* copied from kernelbase */ +static int muldiv(int a, int b, int c) +{
- LONGLONG ret;
- if (!c) return -1;
- /* We want to deal with a positive divisor to simplify the logic. */
- if (c < 0)
- {
a = -a;
c = -c;
- }
- /* If the result is positive, we "add" to round. else, we subtract to round. */
- if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
ret = (((LONGLONG)a * b) + (c / 2)) / c;
- else
ret = (((LONGLONG)a * b) - (c / 2)) / c;
- if (ret > 2147483647 || ret < -2147483647) return -1;
- return ret;
+}
/* Following pulseaudio design here, mainloop has the lock taken whenever
- it is handling something for pulse, and the lock is required whenever
- doing any pa_* call that can affect the state in any way
@@ -145,12 +179,36 @@ static void pulse_stream_state(pa_stream *s, void *user) pulse_broadcast(); }
-static HRESULT WINAPI pulse_connect(const char *name, pa_context **ctx) +static void pulse_attr_update(pa_stream *s, void *user) {
- const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
- TRACE("New attributes or device moved:\n");
- dump_attr(attr);
+}
+static void pulse_underflow_callback(pa_stream *s, void *userdata) {
- if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) {
*ctx = pulse_ctx;
- struct pulse_stream *stream = userdata;
- WARN("%p: Underflow\n", userdata);
- stream->just_underran = TRUE;
- /* re-sync */
- stream->pa_offs_bytes = stream->lcl_offs_bytes;
- stream->pa_held_bytes = stream->held_bytes;
+}
+static void pulse_started_callback(pa_stream *s, void *userdata) +{
- TRACE("%p: (Re)started playing\n", userdata);
+}
+static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes) +{
- memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
+}
+static HRESULT pulse_connect(const char *name) +{
- if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) return S_OK;
- } if (pulse_ctx) pa_context_unref(pulse_ctx);
@@ -180,7 +238,6 @@ static HRESULT WINAPI pulse_connect(const char *name, pa_context **ctx) TRACE("Connected to server %s with protocol version: %i.\n", pa_context_get_server(pulse_ctx), pa_context_get_server_protocol_version(pulse_ctx));
- *ctx = pulse_ctx; return S_OK;
fail: @@ -477,6 +534,300 @@ fail: return E_FAIL; }
+static DWORD get_channel_mask(unsigned int channels) +{
- switch(channels) {
- case 0:
return 0;
- case 1:
return KSAUDIO_SPEAKER_MONO;
- case 2:
return KSAUDIO_SPEAKER_STEREO;
- case 3:
return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
- case 4:
return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
- case 5:
return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
- case 6:
return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
- case 7:
return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
- case 8:
return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
- }
- FIXME("Unknown speaker configuration: %u\n", channels);
- return 0;
+}
+static const enum pa_channel_position pulse_pos_from_wfx[] = {
- PA_CHANNEL_POSITION_FRONT_LEFT,
- PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER,
- PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT,
- PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
- PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
- PA_CHANNEL_POSITION_REAR_CENTER,
- PA_CHANNEL_POSITION_SIDE_LEFT,
- PA_CHANNEL_POSITION_SIDE_RIGHT,
- PA_CHANNEL_POSITION_TOP_CENTER,
- PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
- PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
- PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
- PA_CHANNEL_POSITION_TOP_REAR_LEFT,
- PA_CHANNEL_POSITION_TOP_REAR_CENTER,
- PA_CHANNEL_POSITION_TOP_REAR_RIGHT
+};
+static HRESULT pulse_spec_from_waveformat(struct pulse_stream *stream, const WAVEFORMATEX *fmt) +{
- pa_channel_map_init(&stream->map);
- stream->ss.rate = fmt->nSamplesPerSec;
- stream->ss.format = PA_SAMPLE_INVALID;
- switch(fmt->wFormatTag) {
- case WAVE_FORMAT_IEEE_FLOAT:
if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
break;
stream->ss.format = PA_SAMPLE_FLOAT32LE;
pa_channel_map_init_auto(&stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- case WAVE_FORMAT_PCM:
if (!fmt->nChannels || fmt->nChannels > 2)
break;
if (fmt->wBitsPerSample == 8)
stream->ss.format = PA_SAMPLE_U8;
else if (fmt->wBitsPerSample == 16)
stream->ss.format = PA_SAMPLE_S16LE;
else
return AUDCLNT_E_UNSUPPORTED_FORMAT;
pa_channel_map_init_auto(&stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- case WAVE_FORMAT_EXTENSIBLE: {
WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
DWORD mask = wfe->dwChannelMask;
DWORD i = 0, j;
if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
break;
if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
(!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
fmt->wBitsPerSample == 32)
stream->ss.format = PA_SAMPLE_FLOAT32LE;
else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
DWORD valid = wfe->Samples.wValidBitsPerSample;
if (!valid)
valid = fmt->wBitsPerSample;
if (!valid || valid > fmt->wBitsPerSample)
break;
switch (fmt->wBitsPerSample) {
case 8:
if (valid == 8)
stream->ss.format = PA_SAMPLE_U8;
break;
case 16:
if (valid == 16)
stream->ss.format = PA_SAMPLE_S16LE;
break;
case 24:
if (valid == 24)
stream->ss.format = PA_SAMPLE_S24LE;
break;
case 32:
if (valid == 24)
stream->ss.format = PA_SAMPLE_S24_32LE;
else if (valid == 32)
stream->ss.format = PA_SAMPLE_S32LE;
break;
default:
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
}
stream->map.channels = fmt->nChannels;
if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
mask = get_channel_mask(fmt->nChannels);
for (j = 0; j < ARRAY_SIZE(pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
if (mask & (1 << j))
stream->map.map[i++] = pulse_pos_from_wfx[j];
}
/* Special case for mono since pulse appears to map it differently */
if (mask == SPEAKER_FRONT_CENTER)
stream->map.map[0] = PA_CHANNEL_POSITION_MONO;
if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
stream->map.channels = 0;
ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
break;
}
break;
}
- case WAVE_FORMAT_ALAW:
- case WAVE_FORMAT_MULAW:
if (fmt->wBitsPerSample != 8) {
FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
if (fmt->nChannels != 1 && fmt->nChannels != 2) {
FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
stream->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
pa_channel_map_init_auto(&stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
- default:
WARN("Unhandled tag %x\n", fmt->wFormatTag);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- stream->ss.channels = stream->map.channels;
- if (!pa_channel_map_valid(&stream->map) || stream->ss.format == PA_SAMPLE_INVALID) {
ERR("Invalid format! Channel spec valid: %i, format: %i\n",
pa_channel_map_valid(&stream->map), stream->ss.format);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- return S_OK;
+}
+static HRESULT pulse_stream_connect(struct pulse_stream *stream, UINT32 period_bytes) +{
- int ret;
- char buffer[64];
- static LONG number;
- pa_buffer_attr attr;
- ret = InterlockedIncrement(&number);
- sprintf(buffer, "audio stream #%i", ret);
- stream->stream = pa_stream_new(pulse_ctx, buffer, &stream->ss, &stream->map);
- if (!stream->stream) {
WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- }
- pa_stream_set_state_callback(stream->stream, pulse_stream_state, stream);
- pa_stream_set_buffer_attr_callback(stream->stream, pulse_attr_update, stream);
- pa_stream_set_moved_callback(stream->stream, pulse_attr_update, stream);
- /* PulseAudio will fill in correct values */
- attr.minreq = attr.fragsize = period_bytes;
- attr.tlength = period_bytes * 3;
- attr.maxlength = stream->bufsize_frames * pa_frame_size(&stream->ss);
- attr.prebuf = pa_frame_size(&stream->ss);
- dump_attr(&attr);
- if (stream->dataflow == eRender)
ret = pa_stream_connect_playback(stream->stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY, NULL, NULL);
- else
ret = pa_stream_connect_record(stream->stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY);
- if (ret < 0) {
WARN("Returns %i\n", ret);
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- }
- while (pa_stream_get_state(stream->stream) == PA_STREAM_CREATING)
pulse_cond_wait();
- if (pa_stream_get_state(stream->stream) != PA_STREAM_READY)
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- if (stream->dataflow == eRender) {
pa_stream_set_underflow_callback(stream->stream, pulse_underflow_callback, stream);
pa_stream_set_started_callback(stream->stream, pulse_started_callback, stream);
- }
- return S_OK;
+}
+static HRESULT WINAPI pulse_create_stream(const char *name, EDataFlow dataflow, AUDCLNT_SHAREMODE mode,
DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period,
const WAVEFORMATEX *fmt, UINT32 *channel_count,
struct pulse_stream **ret)
+{
- struct pulse_stream *stream;
- unsigned int bufsize_bytes;
- HRESULT hr;
- if (FAILED(hr = pulse_connect(name)))
return hr;
- if (!(stream = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*stream))))
return E_OUTOFMEMORY;
- stream->dataflow = dataflow;
- hr = pulse_spec_from_waveformat(stream, fmt);
- TRACE("Obtaining format returns %08x\n", hr);
- if (FAILED(hr))
goto exit;
- period = pulse_def_period[dataflow == eCapture];
- if (duration < 3 * period)
duration = 3 * period;
- stream->period_bytes = pa_frame_size(&stream->ss) * muldiv(period, stream->ss.rate, 10000000);
- stream->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
- bufsize_bytes = stream->bufsize_frames * pa_frame_size(&stream->ss);
- stream->mmdev_period_usec = period / 10;
- stream->share = mode;
- stream->flags = flags;
- hr = pulse_stream_connect(stream, stream->period_bytes);
- if (SUCCEEDED(hr)) {
UINT32 unalign;
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(stream->stream);
stream->attr = *attr;
/* Update frames according to new size */
dump_attr(attr);
if (dataflow == eRender) {
stream->real_bufsize_bytes = stream->bufsize_frames * 2 * pa_frame_size(&stream->ss);
stream->local_buffer = RtlAllocateHeap(GetProcessHeap(), 0, stream->real_bufsize_bytes);
if(!stream->local_buffer)
hr = E_OUTOFMEMORY;
} else {
UINT32 i, capture_packets;
if ((unalign = bufsize_bytes % stream->period_bytes))
bufsize_bytes += stream->period_bytes - unalign;
stream->bufsize_frames = bufsize_bytes / pa_frame_size(&stream->ss);
stream->real_bufsize_bytes = bufsize_bytes;
capture_packets = stream->real_bufsize_bytes / stream->period_bytes;
stream->local_buffer = RtlAllocateHeap(GetProcessHeap(), 0, stream->real_bufsize_bytes + capture_packets * sizeof(ACPacket));
if (!stream->local_buffer)
hr = E_OUTOFMEMORY;
else {
ACPacket *cur_packet = (ACPacket*)((char*)stream->local_buffer + stream->real_bufsize_bytes);
BYTE *data = stream->local_buffer;
silence_buffer(stream->ss.format, stream->local_buffer, stream->real_bufsize_bytes);
list_init(&stream->packet_free_head);
list_init(&stream->packet_filled_head);
for (i = 0; i < capture_packets; ++i, ++cur_packet) {
list_add_tail(&stream->packet_free_head, &cur_packet->entry);
cur_packet->data = data;
data += stream->period_bytes;
}
}
}
- }
+exit:
- if (FAILED(hr)) {
free(stream->local_buffer);
if (stream->stream) {
pa_stream_disconnect(stream->stream);
pa_stream_unref(stream->stream);
RtlFreeHeap(GetProcessHeap(), 0, stream);
}
return hr;
- }
- *channel_count = stream->ss.channels;
- *ret = stream;
- return S_OK;
+}
static void WINAPI pulse_release_stream(struct pulse_stream *stream, HANDLE timer) { if(timer) { @@ -507,7 +858,7 @@ static const struct unix_funcs unix_funcs = pulse_cond_wait, pulse_broadcast, pulse_main_loop,
- pulse_connect,
- pulse_create_stream, pulse_release_stream, pulse_test_connect,
}; diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index d02ab4fdf07..51c850ac1ae 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -29,6 +29,14 @@ struct pulse_config unsigned int speakers_mask; };
+typedef struct _ACPacket +{
- struct list entry;
- UINT64 qpcpos;
- BYTE *data;
- UINT32 discont;
+} ACPacket;
struct pulse_stream { EDataFlow dataflow; @@ -64,7 +72,10 @@ struct unix_funcs int (WINAPI *cond_wait)(void); void (WINAPI *broadcast)(void); void (WINAPI *main_loop)(void);
- HRESULT (WINAPI *connect)(const char *name, pa_context **ret);
- HRESULT (WINAPI *create_stream)(const char *name, EDataFlow dataflow, AUDCLNT_SHAREMODE mode,
DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period,
const WAVEFORMATEX *fmt, UINT32 *channel_count,
void (WINAPI *release_stream)(struct pulse_stream *stream, HANDLE timer); HRESULT (WINAPI *test_connect)(const char *name, struct pulse_config *config);struct pulse_stream **ret);
};