[PATCH 2/7] winepulse: Move pulse_test_connect to unix lib.
Andrew Eikum
aeikum at codeweavers.com
Wed May 12 11:36:16 CDT 2021
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
On Tue, May 11, 2021 at 06:30:00PM +0200, Jacek Caban wrote:
> Signed-off-by: Jacek Caban <jacek at 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)
> + name = path;
> else
> - 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];
> + *defperiod = pulse_config.modes[This->dataflow == eCapture].def_period;
> if (minperiod)
> - *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);
> };
>
More information about the wine-devel
mailing list