[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