Signed-off-by: Jacek Caban jacek@codeweavers.com --- dlls/winepulse.drv/mmdevdrv.c | 327 +++---------------------------- dlls/winepulse.drv/pulse.c | 353 ++++++++++++++++++++++++++++++++++ dlls/winepulse.drv/unixlib.h | 12 ++ 3 files changed, 391 insertions(+), 301 deletions(-)
Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Tue, May 11, 2021 at 06:30:00PM +0200, Jacek Caban wrote:
Signed-off-by: Jacek Caban jacek@codeweavers.com
dlls/winepulse.drv/mmdevdrv.c | 327 +++---------------------------- dlls/winepulse.drv/pulse.c | 353 ++++++++++++++++++++++++++++++++++ dlls/winepulse.drv/unixlib.h | 12 ++ 3 files changed, 391 insertions(+), 301 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 9eb056ab506..59806f28465 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -72,8 +72,7 @@ enum DriverPriority { Priority_Preferred };
-static const REFERENCE_TIME MinimumPeriod = 30000; -static const REFERENCE_TIME DefaultPeriod = 100000; +static struct pulse_config pulse_config;
static pa_context *pulse_ctx; static pa_mainloop *pulse_ml; @@ -81,12 +80,6 @@ static pa_mainloop *pulse_ml; static HANDLE pulse_thread; static struct list g_sessions = LIST_INIT(g_sessions);
-static UINT g_phys_speakers_mask = 0;
-/* Mixer format + period times */ -static WAVEFORMATEXTENSIBLE pulse_fmt[2]; -static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
static GUID pulse_render_guid = { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } }; static GUID pulse_capture_guid = @@ -340,198 +333,23 @@ static const enum pa_channel_position pulse_pos_from_wfx[] = { PA_CHANNEL_POSITION_TOP_REAR_RIGHT };
-static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) -{
- int i;
- DWORD mask = 0;
- for (i = 0; i < map->channels; ++i) {
switch (map->map[i]) {
default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
case PA_CHANNEL_POSITION_MONO:
case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
}
- }
- return mask;
-}
-/* For most hardware on Windows, users must choose a configuration with an even
- number of channels (stereo, quad, 5.1, 7.1). Users can then disable
- channels, but those channels are still reported to applications from
- GetMixFormat! Some applications behave badly if given an odd number of
- channels (e.g. 2.1). Here, we find the nearest configuration that Windows
- would report for a given channel layout. */
-static void convert_channel_map(const pa_channel_map *pa_map, WAVEFORMATEXTENSIBLE *fmt) +static char *get_application_name(void) {
- DWORD pa_mask = pulse_channel_map_to_channel_mask(pa_map);
- TRACE("got mask for PA: 0x%x\n", pa_mask);
- if (pa_map->channels == 1)
- {
fmt->Format.nChannels = 1;
fmt->dwChannelMask = pa_mask;
return;
- }
- /* compare against known configurations and find smallest configuration
* which is a superset of the given speakers */
- if (pa_map->channels <= 2 &&
(pa_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
- {
fmt->Format.nChannels = 2;
fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
return;
- }
- if (pa_map->channels <= 4 &&
(pa_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
- {
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
return;
- }
- if (pa_map->channels <= 4 &&
(pa_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
- {
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
return;
- }
- if (pa_map->channels <= 6 &&
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
- {
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
return;
- }
- if (pa_map->channels <= 6 &&
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
- {
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
return;
- }
- if (pa_map->channels <= 8 &&
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
- {
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
return;
- }
- if (pa_map->channels <= 8 &&
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
- {
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
return;
- }
- /* oddball format, report truthfully */
- fmt->Format.nChannels = pa_map->channels;
- fmt->dwChannelMask = pa_mask;
-}
-static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
- WAVEFORMATEX *wfx = &fmt->Format;
- pa_stream *stream;
- pa_channel_map map;
- pa_sample_spec ss;
- pa_buffer_attr attr;
- int ret;
- unsigned int length = 0;
- pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
- ss.rate = 48000;
- ss.format = PA_SAMPLE_FLOAT32LE;
- ss.channels = map.channels;
- attr.maxlength = -1;
- attr.tlength = -1;
- attr.minreq = attr.fragsize = pa_frame_size(&ss);
- attr.prebuf = 0;
- stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
- if (stream)
pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
- if (!stream)
ret = -1;
- else if (render)
ret = pa_stream_connect_playback(stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
- else
ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
- if (ret >= 0) {
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_CREATING)
{}
if (pa_stream_get_state(stream) == PA_STREAM_READY) {
ss = *pa_stream_get_sample_spec(stream);
map = *pa_stream_get_channel_map(stream);
if (render)
length = pa_stream_get_buffer_attr(stream)->minreq;
else
length = pa_stream_get_buffer_attr(stream)->fragsize;
pa_stream_disconnect(stream);
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_READY)
{}
}
- }
- if (stream)
pa_stream_unref(stream);
- if (length)
pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
- if (pulse_min_period[!render] < MinimumPeriod)
pulse_min_period[!render] = MinimumPeriod;
- if (pulse_def_period[!render] < DefaultPeriod)
pulse_def_period[!render] = DefaultPeriod;
- wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- convert_channel_map(&map, fmt);
- WCHAR path[MAX_PATH], *name;
- size_t len;
- char *str;
- wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
- wfx->nSamplesPerSec = ss.rate;
- wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8;
- wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
- if (ss.format != PA_SAMPLE_S24_32LE)
fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
- else
fmt->Samples.wValidBitsPerSample = 24;
- if (ss.format == PA_SAMPLE_FLOAT32LE)
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
- name = strrchrW(path, '\');
- if (!name)
elsename = path;
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
name++;
- len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
- if (!(str = malloc(len)))
return NULL;
- WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
- return str;
}
static HRESULT pulse_connect(void) @@ -601,100 +419,6 @@ fail: return E_FAIL; }
-/* For default PulseAudio render device, OR together all of the
- PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
-static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) -{
- if (i)
g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
-}
-/* some poorly-behaved applications call audio functions during DllMain, so we
- have to do as much as possible without creating a new thread. this function
- sets up a synchronous connection to verify the server is running and query
- static data. */
-static HRESULT pulse_test_connect(void) -{
- int len, ret;
- WCHAR path[MAX_PATH], *name;
- char *str;
- pa_operation *o;
- pulse_ml = pa_mainloop_new();
- pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
- GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
- name = strrchrW(path, '\');
- if (!name)
name = path;
- else
name++;
- len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
- str = pa_xmalloc(len);
- WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
- TRACE("Name: %s\n", str);
- pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
- pa_xfree(str);
- if (!pulse_ctx) {
ERR("Failed to create context\n");
pa_mainloop_free(pulse_ml);
pulse_ml = NULL;
return E_FAIL;
- }
- pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
- TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
- if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
goto fail;
- /* Wait for connection */
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
pa_context_state_t state = pa_context_get_state(pulse_ctx);
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
goto fail;
if (state == PA_CONTEXT_READY)
break;
- }
- if (pa_context_get_state(pulse_ctx) != PA_CONTEXT_READY)
goto fail;
- TRACE("Test-connected to server %s with protocol version: %i.\n",
pa_context_get_server(pulse_ctx),
pa_context_get_server_protocol_version(pulse_ctx));
- pulse_probe_settings(1, &pulse_fmt[0]);
- pulse_probe_settings(0, &pulse_fmt[1]);
- g_phys_speakers_mask = 0;
- o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
- if (o) {
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_operation_get_state(o) == PA_OPERATION_RUNNING)
{}
pa_operation_unref(o);
- }
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
- return S_OK;
-fail:
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
- return E_FAIL;
-}
static HRESULT pulse_stream_valid(ACImpl *This) { if (!This->stream) return AUDCLNT_E_NOT_INITIALIZED; @@ -1196,10 +920,12 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
int WINAPI AUDDRV_GetPriority(void) {
- char *name; HRESULT hr;
- pulse->lock();
- hr = pulse_test_connect();
- pulse->unlock();
- name = get_application_name();
- hr = pulse->test_connect(name, &pulse_config);
- free(name); return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
}
@@ -1640,7 +1366,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, if (FAILED(hr)) goto exit;
- period = pulse_def_period[This->dataflow == eCapture];
- period = pulse_config.modes[This->dataflow == eCapture].def_period; if (duration < 3 * period) duration = 3 * period;
@@ -1757,7 +1483,7 @@ static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface, *latency = 10000000; *latency *= lat; *latency /= This->ss.rate;
- *latency += pulse_def_period[0];
- *latency += pulse_config.modes[0].def_period; pulse->unlock(); TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000)); return S_OK;
@@ -1965,14 +1691,13 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface, WAVEFORMATEX **pwfx) { ACImpl *This = impl_from_IAudioClient3(iface);
WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
TRACE("(%p)->(%p)\n", This, pwfx);
if (!pwfx) return E_POINTER;
*pwfx = clone_format(&fmt->Format);
- *pwfx = clone_format(&pulse_config.modes[This->dataflow == eCapture].format.Format); if (!*pwfx) return E_OUTOFMEMORY; dump_fmt(*pwfx);
@@ -1990,9 +1715,9 @@ static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface, return E_POINTER;
if (defperiod)
*defperiod = pulse_def_period[This->dataflow == eCapture];
if (minperiod)*defperiod = pulse_config.modes[This->dataflow == eCapture].def_period;
*minperiod = pulse_min_period[This->dataflow == eCapture];
*minperiod = pulse_config.modes[This->dataflow == eCapture].min_period;
return S_OK;
} @@ -3699,7 +3424,7 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) { out->vt = VT_UI4;
out->ulVal = g_phys_speakers_mask;
out->ulVal = pulse_config.speakers_mask; return out->ulVal ? S_OK : E_FAIL;
}
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index f6f2a45264e..4b4d2497314 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -24,13 +24,35 @@
#include <stdarg.h> #include <pthread.h> +#include <poll.h>
+#include <pulse/pulseaudio.h>
#include "ntstatus.h" #define WIN32_NO_STATUS #include "winternl.h"
+#include "initguid.h" +#include "audioclient.h"
#include "unixlib.h"
+#include "wine/debug.h"
+WINE_DEFAULT_DEBUG_CHANNEL(pulse);
+static pa_context *pulse_ctx; +static pa_mainloop *pulse_ml;
+/* Mixer format + period times */ +static WAVEFORMATEXTENSIBLE pulse_fmt[2]; +static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
+static UINT g_phys_speakers_mask = 0;
+static const REFERENCE_TIME MinimumPeriod = 30000; +static const REFERENCE_TIME DefaultPeriod = 100000;
static pthread_mutex_t pulse_mutex; static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
@@ -54,12 +76,343 @@ static void WINAPI pulse_broadcast(void) pthread_cond_broadcast(&pulse_cond); }
+static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) +{
- int r;
- pulse_unlock();
- r = poll(ufds, nfds, timeout);
- pulse_lock();
- return r;
+}
+static void pulse_contextcallback(pa_context *c, void *userdata) +{
- switch (pa_context_get_state(c)) {
default:
FIXME("Unhandled state: %i\n", pa_context_get_state(c));
return;
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
case PA_CONTEXT_TERMINATED:
TRACE("State change to %i\n", pa_context_get_state(c));
return;
case PA_CONTEXT_READY:
TRACE("Ready\n");
break;
case PA_CONTEXT_FAILED:
WARN("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
break;
- }
- pulse_broadcast();
+}
+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 DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) +{
- int i;
- DWORD mask = 0;
- for (i = 0; i < map->channels; ++i) {
switch (map->map[i]) {
default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
case PA_CHANNEL_POSITION_MONO:
case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
}
- }
- return mask;
+}
+/* For default PulseAudio render device, OR together all of the
- PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
+static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) +{
- if (i)
g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
+}
+/* For most hardware on Windows, users must choose a configuration with an even
- number of channels (stereo, quad, 5.1, 7.1). Users can then disable
- channels, but those channels are still reported to applications from
- GetMixFormat! Some applications behave badly if given an odd number of
- channels (e.g. 2.1). Here, we find the nearest configuration that Windows
- would report for a given channel layout. */
+static void convert_channel_map(const pa_channel_map *pa_map, WAVEFORMATEXTENSIBLE *fmt) +{
- DWORD pa_mask = pulse_channel_map_to_channel_mask(pa_map);
- TRACE("got mask for PA: 0x%x\n", pa_mask);
- if (pa_map->channels == 1)
- {
fmt->Format.nChannels = 1;
fmt->dwChannelMask = pa_mask;
return;
- }
- /* compare against known configurations and find smallest configuration
* which is a superset of the given speakers */
- if (pa_map->channels <= 2 &&
(pa_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
- {
fmt->Format.nChannels = 2;
fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
return;
- }
- if (pa_map->channels <= 4 &&
(pa_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
- {
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
return;
- }
- if (pa_map->channels <= 4 &&
(pa_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
- {
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
return;
- }
- if (pa_map->channels <= 6 &&
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
- {
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
return;
- }
- if (pa_map->channels <= 6 &&
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
- {
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
return;
- }
- if (pa_map->channels <= 8 &&
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
- {
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
return;
- }
- if (pa_map->channels <= 8 &&
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
- {
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
return;
- }
- /* oddball format, report truthfully */
- fmt->Format.nChannels = pa_map->channels;
- fmt->dwChannelMask = pa_mask;
+}
+static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
- WAVEFORMATEX *wfx = &fmt->Format;
- pa_stream *stream;
- pa_channel_map map;
- pa_sample_spec ss;
- pa_buffer_attr attr;
- int ret;
- unsigned int length = 0;
- pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
- ss.rate = 48000;
- ss.format = PA_SAMPLE_FLOAT32LE;
- ss.channels = map.channels;
- attr.maxlength = -1;
- attr.tlength = -1;
- attr.minreq = attr.fragsize = pa_frame_size(&ss);
- attr.prebuf = 0;
- stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
- if (stream)
pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
- if (!stream)
ret = -1;
- else if (render)
ret = pa_stream_connect_playback(stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
- else
ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
- if (ret >= 0) {
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_CREATING)
{}
if (pa_stream_get_state(stream) == PA_STREAM_READY) {
ss = *pa_stream_get_sample_spec(stream);
map = *pa_stream_get_channel_map(stream);
if (render)
length = pa_stream_get_buffer_attr(stream)->minreq;
else
length = pa_stream_get_buffer_attr(stream)->fragsize;
pa_stream_disconnect(stream);
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_READY)
{}
}
- }
- if (stream)
pa_stream_unref(stream);
- if (length)
pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
- if (pulse_min_period[!render] < MinimumPeriod)
pulse_min_period[!render] = MinimumPeriod;
- if (pulse_def_period[!render] < DefaultPeriod)
pulse_def_period[!render] = DefaultPeriod;
- wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- convert_channel_map(&map, fmt);
- wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
- wfx->nSamplesPerSec = ss.rate;
- wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8;
- wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
- if (ss.format != PA_SAMPLE_S24_32LE)
fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
- else
fmt->Samples.wValidBitsPerSample = 24;
- if (ss.format == PA_SAMPLE_FLOAT32LE)
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+}
+/* some poorly-behaved applications call audio functions during DllMain, so we
- have to do as much as possible without creating a new thread. this function
- sets up a synchronous connection to verify the server is running and query
- static data. */
+static HRESULT WINAPI pulse_test_connect(const char *name, struct pulse_config *config) +{
- pa_operation *o;
- int ret;
- pulse_lock();
- pulse_ml = pa_mainloop_new();
- pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
- pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), name);
- if (!pulse_ctx) {
ERR("Failed to create context\n");
pa_mainloop_free(pulse_ml);
pulse_ml = NULL;
pulse_unlock();
return E_FAIL;
- }
- pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
- TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
- if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
goto fail;
- /* Wait for connection */
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
pa_context_state_t state = pa_context_get_state(pulse_ctx);
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
goto fail;
if (state == PA_CONTEXT_READY)
break;
- }
- if (pa_context_get_state(pulse_ctx) != PA_CONTEXT_READY)
goto fail;
- TRACE("Test-connected to server %s with protocol version: %i.\n",
pa_context_get_server(pulse_ctx),
pa_context_get_server_protocol_version(pulse_ctx));
- pulse_probe_settings(1, &pulse_fmt[0]);
- pulse_probe_settings(0, &pulse_fmt[1]);
- g_phys_speakers_mask = 0;
- o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
- if (o) {
while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
pa_operation_get_state(o) == PA_OPERATION_RUNNING)
{}
pa_operation_unref(o);
- }
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
- config->speakers_mask = g_phys_speakers_mask;
- config->modes[0].format = pulse_fmt[0];
- config->modes[0].def_period = pulse_def_period[0];
- config->modes[0].min_period = pulse_min_period[0];
- config->modes[1].format = pulse_fmt[1];
- config->modes[1].def_period = pulse_def_period[1];
- config->modes[1].min_period = pulse_min_period[1];
- pulse_unlock();
- return S_OK;
+fail:
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
- pulse_unlock();
- return E_FAIL;
+}
static const struct unix_funcs unix_funcs = { pulse_lock, pulse_unlock, pulse_cond_wait, pulse_broadcast,
- pulse_test_connect,
};
NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out) diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index 97595ed3cee..865a6f31ec6 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -16,10 +16,22 @@
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+struct pulse_config +{
- struct
- {
WAVEFORMATEXTENSIBLE format;
REFERENCE_TIME def_period;
REFERENCE_TIME min_period;
- } modes[2];
- unsigned int speakers_mask;
+};
struct unix_funcs { void (WINAPI *lock)(void); void (WINAPI *unlock)(void); int (WINAPI *cond_wait)(void); void (WINAPI *broadcast)(void);
- HRESULT (WINAPI *test_connect)(const char *name, struct pulse_config *config);
};