[PATCH 7/7] winepulse: Move pulse stream initialization to unix lib.

Andrew Eikum aeikum at codeweavers.com
Wed May 12 11:37:41 CDT 2021


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Tue, May 11, 2021 at 06:31:45PM +0200, Jacek Caban wrote:
> Signed-off-by: Jacek Caban <jacek at 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,
> +                              &This->channel_count, &This->pulse_stream);
>      free(name);
> -    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,
> +                                    struct pulse_stream **ret);
>      void (WINAPI *release_stream)(struct pulse_stream *stream, HANDLE timer);
>      HRESULT (WINAPI *test_connect)(const char *name, struct pulse_config *config);
>  };
> 




More information about the wine-devel mailing list