diff --git a/.gitignore b/.gitignore index 7eb7a4b..764ba8b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ /wine-1.3.23.tar.bz2 /wine-1.3.24.tar.bz2 /wine-1.3.24.tar.bz2.sign +/wine-1.3.25.tar.bz2 +/wine-1.3.25.tar.bz2.sign diff --git a/dlls_winepulse.drv_Makefile.in b/dlls_winepulse.drv_Makefile.in new file mode 100644 index 0000000..0f595f1 --- /dev/null +++ b/dlls_winepulse.drv_Makefile.in @@ -0,0 +1,9 @@ +MODULE = winepulse.drv +IMPORTS = dxguid uuid winmm user32 advapi32 ole32 +EXTRALIBS = @PULSELIBS@ @LIBPTHREAD@ +EXTRAINCL = @PULSEINCL@ + +C_SRCS = \ + mmdevdrv.c + +@MAKE_DLL_RULES@ diff --git a/dlls_winepulse.drv_mmdevdrv.c b/dlls_winepulse.drv_mmdevdrv.c new file mode 100644 index 0000000..8ba2a92 --- /dev/null +++ b/dlls_winepulse.drv_mmdevdrv.c @@ -0,0 +1,2096 @@ +/* + * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers + * Copyright 2011 Andrew Eikum for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Pulseaudio driver support.. hell froze over + */ + +#define NONAMELESSUNION +#define COBJMACROS +#include "config.h" +#include +#include + +#include +#include +#include +#include + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winreg.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/list.h" + +#include "ole2.h" +#include "dshow.h" +#include "dsound.h" +#include "propsys.h" + +#include "initguid.h" +#include "ks.h" +#include "ksmedia.h" +#include "mmdeviceapi.h" +#include "audioclient.h" +#include "endpointvolume.h" +#include "audiopolicy.h" + +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(pulse); + +static const REFERENCE_TIME MinimumPeriod = 100000; + +static pa_context *pulse_ctx; +static pa_mainloop *pulse_ml; + +static HANDLE pulse_thread; +static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER; + +static struct list session_list = LIST_INIT( session_list ); + +typedef struct _AudioSession { + GUID guid; + + EDataFlow dataflow; + + float master_vol; + UINT32 channel_count; + float *channel_vols; + + struct list entry; +} AudioSession; + +typedef struct ACImpl { + IAudioClient IAudioClient_iface; + IAudioRenderClient IAudioRenderClient_iface; + IAudioCaptureClient IAudioCaptureClient_iface; + IAudioSessionControl2 IAudioSessionControl2_iface; + ISimpleAudioVolume ISimpleAudioVolume_iface; + IAudioClock IAudioClock_iface; + IAudioClock2 IAudioClock2_iface; + + LONG ref; + + IMMDevice *parent; + + EDataFlow dataflow; + DWORD flags; + AUDCLNT_SHAREMODE share; + HANDLE event; + + BOOL initted, started; + UINT32 bufsize_frames; + BYTE *locked_ptr, *tmp_buffer; + UINT32 locked, peeked, extra_buffered; + UINT64 play_ofs; + + pa_stream *stream; + pa_sample_spec ss; + pa_channel_map map; + + /* Mixer format + period times */ + pa_sample_spec mix_ss; + pa_channel_map mix_map; + REFERENCE_TIME min_period, def_period; +} ACImpl; + +static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0}; + +static const IAudioClientVtbl AudioClient_Vtbl; +static const IAudioRenderClientVtbl AudioRenderClient_Vtbl; +static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl; +static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl; +static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl; +static const IAudioClockVtbl AudioClock_Vtbl; +static const IAudioClock2Vtbl AudioClock2_Vtbl; + +static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface); +} + +static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface); +} + +static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface); +} + +static inline ACImpl *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioSessionControl2_iface); +} + +static inline ACImpl *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, ISimpleAudioVolume_iface); +} + +static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface); +} + +static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface) +{ + return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface); +} + +/* 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 + * + * pa_cond_wait is used when waiting on results, because the mainloop needs + * the same lock taken to affect the state + * + * This is basically the same as the pa_threaded_mainloop implementation, + * but that cannot be used because it uses pthread_create directly + * + * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock + * pa_threaded_mainloop_signal -> pthread_cond_signal + * pa_threaded_mainloop_wait -> pthread_cond_wait + */ + +static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) { + int r; + pthread_mutex_unlock(&pulse_lock); + r = poll(ufds, nfds, timeout); + pthread_mutex_lock(&pulse_lock); + return r; +} + +static DWORD CALLBACK pulse_mainloop_thread(void *tmp) { + int ret; + pulse_ml = pa_mainloop_new(); + pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL); + pthread_mutex_lock(&pulse_lock); + pthread_cond_signal(&pulse_cond); + pa_mainloop_run(pulse_ml, &ret); + pthread_mutex_unlock(&pulse_lock); + pa_mainloop_free(pulse_ml); + CloseHandle(pulse_thread); + return ret; +} + +static void pulse_contextcallback(pa_context *c, void *userdata); + +static HRESULT pulse_connect(void) +{ + int len; + WCHAR path[PATH_MAX], *name; + char *str; + + if (!pulse_thread) + { + if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL))) + { + ERR("Failed to create mainloop thread."); + return E_FAIL; + } + pthread_cond_wait(&pulse_cond, &pulse_lock); + } + + if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) + return S_OK; + if (pulse_ctx) + pa_context_unref(pulse_ctx); + + GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*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"); + 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 (pthread_cond_wait(&pulse_cond, &pulse_lock)) { + 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; + } + + TRACE("Connected to server %s with protocol version: %i.\n", + pa_context_get_server(pulse_ctx), + pa_context_get_server_protocol_version(pulse_ctx)); + return S_OK; + +fail: + pa_context_unref(pulse_ctx); + pulse_ctx = NULL; + return E_FAIL; +} + +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)); + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + TRACE("State change to %i\n", pa_context_get_state(c)); + return; + + case PA_CONTEXT_READY: + TRACE("Ready\n"); + break; + + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c))); + } + pthread_cond_signal(&pulse_cond); +} + +static void pulse_stream_state(pa_stream *s, void *user); + +static HRESULT pulse_stream_valid(ACImpl *This) { + if (!This->initted) + return AUDCLNT_E_NOT_INITIALIZED; + if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY) + return AUDCLNT_E_DEVICE_INVALIDATED; + return S_OK; +} + +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; + pthread_cond_signal(&pulse_cond); +} + +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 HRESULT pulse_stream_connect(ACImpl *This, REFERENCE_TIME period) { + int ret; + char buffer[64]; + static LONG number; + pa_buffer_attr attr; + if (This->stream) { + pa_stream_disconnect(This->stream); + while (pa_stream_get_state(This->stream) == PA_STREAM_READY) + pthread_cond_wait(&pulse_cond, &pulse_lock); + pa_stream_unref(This->stream); + } + ret = InterlockedIncrement(&number); + sprintf(buffer, "audio stream #%i", ret); + This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map); + pa_stream_set_state_callback(This->stream, pulse_stream_state, This); + pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This); + pa_stream_set_moved_callback(This->stream, pulse_attr_update, This); + + attr.maxlength = -1; + attr.tlength = This->bufsize_frames * pa_frame_size(&This->ss); + if (This->def_period > period) + period = This->def_period; + attr.minreq = attr.fragsize = pa_usec_to_bytes(period/10, &This->ss); + attr.prebuf = 0; + dump_attr(&attr); + if (This->dataflow == eRender) + ret = pa_stream_connect_playback(This->stream, NULL, &attr, + PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL); + else + ret = pa_stream_connect_record(This->stream, NULL, &attr, + PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS); + if (ret < 0) { + WARN("Returns %i\n", ret); + return AUDCLNT_E_ENDPOINT_CREATE_FAILED; + } + while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING) + pthread_cond_wait(&pulse_cond, &pulse_lock); + if (pa_stream_get_state(This->stream) != PA_STREAM_READY) + return AUDCLNT_E_ENDPOINT_CREATE_FAILED; + return S_OK; +} + +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); + pthread_cond_signal(&pulse_cond); +} + +HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys, + UINT *num, UINT *def_index) +{ + HRESULT hr = S_OK; + TRACE("%d %p %p %p\n", flow, ids, num, def_index); + + pthread_mutex_lock(&pulse_lock); + hr = pulse_connect(); + pthread_mutex_unlock(&pulse_lock); + if (FAILED(hr)) + return hr; + *num = 1; + *def_index = 0; + + *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *)); + if(!*ids) + return E_OUTOFMEMORY; + + (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW)); + if(!(*ids)[0]){ + HeapFree(GetProcessHeap(), 0, *ids); + return E_OUTOFMEMORY; + } + + lstrcpyW((*ids)[0], defaultW); + + *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *)); + (*keys)[0] = NULL; + + return S_OK; +} + +HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, + EDataFlow dataflow, IAudioClient **out) +{ + HRESULT hr; + ACImpl *This; + + TRACE("%p %p %d %p\n", key, dev, dataflow, out); + + *out = NULL; + pthread_mutex_lock(&pulse_lock); + hr = pulse_connect(); + pthread_mutex_unlock(&pulse_lock); + if (FAILED(hr)) + return hr; + + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl)); + if(!This) + return E_OUTOFMEMORY; + + This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl; + This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl; + This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl; + This->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl; + This->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl; + This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl; + This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl; + + This->dataflow = dataflow; + + if(dataflow != eRender && dataflow != eCapture) { + HeapFree(GetProcessHeap(), 0, This); + return E_UNEXPECTED; + } + + This->parent = dev; + IMMDevice_AddRef(This->parent); + + *out = &This->IAudioClient_iface; + IAudioClient_AddRef(&This->IAudioClient_iface); + + return S_OK; +} + +static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface, + REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface) +{ + ACImpl *This = impl_from_IAudioClient(iface); + ULONG ref; + ref = InterlockedIncrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + return ref; +} + +static ULONG WINAPI AudioClient_Release(IAudioClient *iface) +{ + ACImpl *This = impl_from_IAudioClient(iface); + ULONG ref; + ref = InterlockedDecrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + if(!ref){ + IAudioClient_Stop(iface); + if (This->stream) { + pthread_mutex_lock(&pulse_lock); + if (pa_stream_get_state(This->stream) == PA_STREAM_READY) + pa_stream_disconnect(This->stream); + pa_stream_unref(This->stream); + pthread_mutex_unlock(&pulse_lock); + } + IMMDevice_Release(This->parent); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static void dump_fmt(const WAVEFORMATEX *fmt) +{ + TRACE("wFormatTag: 0x%x (", fmt->wFormatTag); + switch(fmt->wFormatTag){ + case WAVE_FORMAT_PCM: + TRACE("WAVE_FORMAT_PCM"); + break; + case WAVE_FORMAT_IEEE_FLOAT: + TRACE("WAVE_FORMAT_IEEE_FLOAT"); + break; + case WAVE_FORMAT_EXTENSIBLE: + TRACE("WAVE_FORMAT_EXTENSIBLE"); + break; + default: + TRACE("Unknown"); + break; + } + TRACE(")\n"); + + TRACE("nChannels: %u\n", fmt->nChannels); + TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec); + TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec); + TRACE("nBlockAlign: %u\n", fmt->nBlockAlign); + TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample); + TRACE("cbSize: %u\n", fmt->cbSize); + + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){ + WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt; + TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask); + TRACE("Samples: %04x\n", fmtex->Samples.wReserved); + TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat)); + } +} + +static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt) +{ + WAVEFORMATEX *ret; + size_t size; + + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + size = sizeof(WAVEFORMATEXTENSIBLE); + else + size = sizeof(WAVEFORMATEX); + + ret = HeapAlloc(GetProcessHeap(), 0, size); + if(!ret) + return NULL; + + memcpy(ret, fmt, size); + + ret->cbSize = size - sizeof(WAVEFORMATEX); + + return ret; +} + +static DWORD get_channel_mask(unsigned int channels) +{ + switch(channels){ + case 0: + return 0; + case 1: + return SPEAKER_FRONT_CENTER; + case 2: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + case 3: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | + SPEAKER_LOW_FREQUENCY; + case 4: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + case 5: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; + case 6: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER; + case 7: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | + SPEAKER_BACK_CENTER; + case 8: + return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; + } + FIXME("Unknown speaker configuration: %u\n", channels); + return 0; +} + +static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, + AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, + REFERENCE_TIME period, const WAVEFORMATEX *fmt, + const GUID *sessionguid) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr = S_OK; + + 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; + + if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) + return AUDCLNT_E_NOT_INITIALIZED; + + if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS | + AUDCLNT_STREAMFLAGS_LOOPBACK | + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | + AUDCLNT_STREAMFLAGS_NOPERSIST | + AUDCLNT_STREAMFLAGS_RATEADJUST | + AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | + AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE | + AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){ + TRACE("Unknown flags: %08x\n", flags); + return E_INVALIDARG; + } + + pthread_mutex_lock(&pulse_lock); + if(This->initted){ + pthread_mutex_unlock(&pulse_lock); + return AUDCLNT_E_ALREADY_INITIALIZED; + } + pa_channel_map_init(&This->map); + This->ss.rate = fmt->nSamplesPerSec; + This->ss.format = PA_SAMPLE_INVALID; + switch(fmt->wFormatTag){ + case WAVE_FORMAT_PCM: + if(fmt->wBitsPerSample == 8) + This->ss.format = PA_SAMPLE_U8; + else if(fmt->wBitsPerSample == 16) + This->ss.format = PA_SAMPLE_S16LE; + if (fmt->nChannels == 1 || fmt->nChannels == 2) + pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); + break; + case WAVE_FORMAT_IEEE_FLOAT: + This->ss.format = PA_SAMPLE_FLOAT32LE; + if (fmt->nChannels == 1 || fmt->nChannels == 2) + pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); + break; + case WAVE_FORMAT_EXTENSIBLE: { + WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt; + DWORD mask = wfe->dwChannelMask; + DWORD i = 0; + if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe)) + break; + if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + This->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->ss.format = PA_SAMPLE_U8; + break; + case 16: + if (valid == 16) + This->ss.format = PA_SAMPLE_S16LE; + break; + case 24: + if (valid == 24) + This->ss.format = PA_SAMPLE_S24LE; + break; + case 32: + if (valid == 24) + This->ss.format = PA_SAMPLE_S24_32LE; + else if (valid == 32) + This->ss.format = PA_SAMPLE_S32LE; + default: + break; + } + } + This->map.channels = fmt->nChannels; + if (!mask) + mask = get_channel_mask(fmt->nChannels); + if (mask & SPEAKER_FRONT_LEFT) This->map.map[i++] = PA_CHANNEL_POSITION_FRONT_LEFT; + if (mask & SPEAKER_FRONT_RIGHT) This->map.map[i++] = PA_CHANNEL_POSITION_FRONT_RIGHT; + if (mask & SPEAKER_FRONT_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_FRONT_CENTER; + if (mask & SPEAKER_LOW_FREQUENCY) This->map.map[i++] = PA_CHANNEL_POSITION_SUBWOOFER; + if (mask & SPEAKER_BACK_LEFT) This->map.map[i++] = PA_CHANNEL_POSITION_REAR_LEFT; + if (mask & SPEAKER_BACK_RIGHT) This->map.map[i++] = PA_CHANNEL_POSITION_REAR_RIGHT; + if (mask & SPEAKER_BACK_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_REAR_CENTER; + if (mask & SPEAKER_FRONT_LEFT_OF_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + if (mask & SPEAKER_FRONT_RIGHT_OF_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + if (mask & SPEAKER_BACK_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_REAR_CENTER; + if (mask & SPEAKER_SIDE_LEFT) This->map.map[i++] = PA_CHANNEL_POSITION_SIDE_LEFT; + if (mask & SPEAKER_SIDE_RIGHT) This->map.map[i++] = PA_CHANNEL_POSITION_SIDE_RIGHT; + if (mask & SPEAKER_TOP_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_CENTER; + if (mask & SPEAKER_TOP_FRONT_LEFT) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + if (mask & SPEAKER_TOP_FRONT_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + if (mask & SPEAKER_TOP_FRONT_RIGHT) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + if (mask & SPEAKER_TOP_BACK_LEFT) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_REAR_LEFT; + if (mask & SPEAKER_TOP_BACK_CENTER) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_REAR_CENTER; + if (mask & SPEAKER_TOP_BACK_RIGHT) This->map.map[i++] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + if (mask & SPEAKER_ALL) { + This->map.map[i++] = PA_CHANNEL_POSITION_MONO; + FIXME("Is the 'all' channel mapped correctly?\n"); + } + if (i != fmt->nChannels || mask & SPEAKER_RESERVED) { + This->map.channels = 0; + FIXME("Invalid channel mask: %i/%i and %x\n", i, fmt->nChannels, mask); + break; + } + /* Special case for mono since pulse appears to map it differently */ + if (mask == SPEAKER_FRONT_CENTER) + This->map.map[0] = PA_CHANNEL_POSITION_MONO; + break; + } + default: FIXME("Unhandled tag %x\n", fmt->wFormatTag); + } + This->ss.channels = This->map.channels; + hr = AUDCLNT_E_UNSUPPORTED_FORMAT; + if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) { + WARN("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format); + dump_fmt(fmt); + goto exit; + } + if (duration < 5000000) + This->bufsize_frames = fmt->nSamplesPerSec/2; + else if (duration < 20000000) + This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec); + else + This->bufsize_frames = 2 * fmt->nSamplesPerSec; + + hr = pulse_stream_connect(This, period); + if (SUCCEEDED(hr)) { + /* Update frames according to new size */ + This->bufsize_frames = pa_stream_get_buffer_attr(This->stream)->tlength / pa_frame_size(&This->ss); + //hr = AudioSession_CreateSession(This, sessionguid ? sessionguid : &GUID_NULL); + if (SUCCEEDED(hr)) + This->initted = TRUE; + } + This->share = mode; + This->flags = flags; + +exit: + if(FAILED(hr)) { + if (This->stream) { + pa_stream_disconnect(This->stream); + pa_stream_unref(This->stream); + This->stream = NULL; + } + } + pthread_mutex_unlock(&pulse_lock); + return hr; +} + +static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface, + UINT32 *out) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr; + + TRACE("(%p)->(%p)\n", This, out); + + if(!out) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (SUCCEEDED(hr)) + *out = This->bufsize_frames; + pthread_mutex_unlock(&pulse_lock); + + return hr; +} + +static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface, + REFERENCE_TIME *latency) +{ + ACImpl *This = impl_from_IAudioClient(iface); + const pa_buffer_attr *attr; + REFERENCE_TIME lat; + HRESULT hr; + + TRACE("(%p)->(%p)\n", This, latency); + + if(!latency) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + attr = pa_stream_get_buffer_attr(This->stream); + if (This->dataflow == eCapture) + lat = attr->fragsize / pa_frame_size(&This->ss); + else + lat = attr->minreq / pa_frame_size(&This->ss); + *latency = 10000000; + *latency *= lat; + *latency /= This->ss.rate; + pthread_mutex_unlock(&pulse_lock); + TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000)); + return S_OK; +} + +static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface, + UINT32 *out) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr; + + TRACE("(%p)->(%p)\n", This, out); + + if(!out) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + + if(This->dataflow == eRender){ + UINT32 avail = pa_stream_writable_size(This->stream) / pa_frame_size(&This->ss); + if (avail + This->extra_buffered >= This->bufsize_frames) + *out = 0; + else + *out = This->bufsize_frames - avail - This->extra_buffered; + }else if(This->dataflow == eCapture){ + if (!This->peeked) { + DWORD frag, readable = pa_stream_readable_size(This->stream); + pa_stream_peek(This->stream, (const void**)&This->locked_ptr, &frag); + if (frag != readable) { + DWORD done = frag; + This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, readable); + memcpy(This->tmp_buffer, This->locked_ptr, frag); + pa_stream_drop(This->stream); + while (done < readable) { + pa_stream_peek(This->stream, (const void **)&This->locked_ptr, &frag); + memcpy(This->tmp_buffer + done, This->locked_ptr, frag); + pa_stream_drop(This->stream); + done += frag; + } + if (done > readable) + ERR("Read %u instead of %u\n", done, This->peeked); + This->locked_ptr = NULL; + } + This->peeked = readable; + } + *out = This->peeked / pa_frame_size(&This->ss); + }else{ + pthread_mutex_unlock(&pulse_lock); + return E_UNEXPECTED; + } + pthread_mutex_unlock(&pulse_lock); + + TRACE("Pad: %u\n", *out); + + return S_OK; +} + +static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, + AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, + WAVEFORMATEX **out) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr = S_OK; + WAVEFORMATEX *closest = NULL; + WAVEFORMATEXTENSIBLE *wfe; + + TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); + + if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out)) + return E_POINTER; + + if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) + return E_INVALIDARG; + + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) + return E_INVALIDARG; + + dump_fmt(fmt); + + closest = clone_format(fmt); + if(!closest){ + hr = E_OUTOFMEMORY; + goto exit; + } + wfe = (WAVEFORMATEXTENSIBLE*)closest; + if (closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE && !wfe->dwChannelMask) { + wfe->dwChannelMask = get_channel_mask(closest->nChannels); + hr = S_FALSE; + WARN("Fixed up channel mask %p -> %p\n", fmt, closest); + } + +exit: + if(hr == S_OK || !out){ + HeapFree(GetProcessHeap(), 0, closest); + if(out) + *out = NULL; + }else if(closest){ + closest->nBlockAlign = + closest->nChannels * closest->wBitsPerSample / 8; + closest->nAvgBytesPerSec = + closest->nBlockAlign * closest->nSamplesPerSec; + *out = closest; + } + + TRACE("returning: %08x %p\n", hr, out ? *out : NULL); + return hr; +} + +static void pulse_probe_settings(ACImpl *This) { + pa_stream *stream; + pa_channel_map map; + pa_sample_spec ss; + pa_buffer_attr attr; + int ret; + unsigned int length = 0; + + if (This->mix_ss.rate) + return; + + 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 (This->dataflow == eRender) + ret = pa_stream_connect_playback(stream, NULL, &attr, + PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_FORMAT|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_FORMAT|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS); + if (ret >= 0) { + while (pa_stream_get_state(stream) == PA_STREAM_CREATING) + pthread_cond_wait(&pulse_cond, &pulse_lock); + if (pa_stream_get_state(stream) == PA_STREAM_READY) { + ss = *pa_stream_get_sample_spec(stream); + map = *pa_stream_get_channel_map(stream); + if (This->dataflow == eRender) + length = pa_stream_get_buffer_attr(stream)->minreq; + else + length = pa_stream_get_buffer_attr(stream)->fragsize; + pa_stream_disconnect(stream); + while (pa_stream_get_state(stream) == PA_STREAM_READY) + pthread_cond_wait(&pulse_cond, &pulse_lock); + } + } + if (stream) + pa_stream_unref(stream); + This->mix_ss = ss; + This->mix_map = map; + if (length) + This->def_period = This->min_period = pa_bytes_to_usec(10 * length, &This->mix_ss); + else + This->min_period = MinimumPeriod; + if (This->def_period <= MinimumPeriod) + This->def_period = MinimumPeriod; +} + +static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface, + WAVEFORMATEX **pwfx) +{ + ACImpl *This = impl_from_IAudioClient(iface); + WAVEFORMATEXTENSIBLE *fmt; + HRESULT hr = S_OK; + int i; + + TRACE("(%p)->(%p)\n", This, pwfx); + + if(!pwfx) + return E_POINTER; + + *pwfx = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE)); + if(!*pwfx) + return E_OUTOFMEMORY; + + fmt = (WAVEFORMATEXTENSIBLE*)*pwfx; + + pthread_mutex_lock(&pulse_lock); + pulse_probe_settings(This); + pthread_mutex_unlock(&pulse_lock); + + (*pwfx)->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + (*pwfx)->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + (*pwfx)->nChannels = This->mix_ss.channels; + (*pwfx)->wBitsPerSample = 8 * pa_sample_size_of_format(This->mix_ss.format); + (*pwfx)->nSamplesPerSec = This->mix_ss.rate; + (*pwfx)->nBlockAlign = (*pwfx)->nChannels * (*pwfx)->wBitsPerSample / 8; + (*pwfx)->nAvgBytesPerSec = (*pwfx)->nSamplesPerSec * (*pwfx)->nBlockAlign; + if (This->mix_ss.format != PA_SAMPLE_S24_32LE) + fmt->Samples.wValidBitsPerSample = (*pwfx)->wBitsPerSample; + else + fmt->Samples.wValidBitsPerSample = 24; + if (This->mix_ss.format == PA_SAMPLE_FLOAT32LE) + fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + fmt->dwChannelMask = 0; + for (i = 0; i < This->mix_map.channels; ++i) + switch (This->mix_map.map[i]) { + default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(This->mix_map.map[i])); break; + case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break; + case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break; + case PA_CHANNEL_POSITION_MONO: + case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break; + case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break; + case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break; + case PA_CHANNEL_POSITION_SUBWOOFER: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break; + case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break; + case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break; + } + dump_fmt((WAVEFORMATEX*)fmt); + if(FAILED(hr)) { + CoTaskMemFree(*pwfx); + *pwfx = NULL; + } + + return hr; +} + +static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface, + REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod) +{ + ACImpl *This = impl_from_IAudioClient(iface); + + TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod); + + if(!defperiod && !minperiod) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + pulse_probe_settings(This); + if(defperiod) + *defperiod = This->def_period; + if(minperiod) + *minperiod = This->min_period; + pthread_mutex_unlock(&pulse_lock); + + return S_OK; +} + +static HRESULT WINAPI AudioClient_Start(IAudioClient *iface) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr = S_OK; + int success; + pa_operation *o; + + TRACE("(%p)\n", This); + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + + if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){ + pthread_mutex_unlock(&pulse_lock); + return AUDCLNT_E_EVENTHANDLE_NOT_SET; + } + + if(This->started){ + pthread_mutex_unlock(&pulse_lock); + return AUDCLNT_E_NOT_STOPPED; + } + + o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success); + if (o) { + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pthread_cond_wait(&pulse_cond, &pulse_lock); + pa_operation_unref(o); + } else + success = 0; + if (!success) + hr = E_FAIL; + if (SUCCEEDED(hr)) + This->started = TRUE; + if (This->event) + SetEvent(This->event); + pthread_mutex_unlock(&pulse_lock); + return hr; +} + +static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr = S_OK; + pa_operation *o; + int success; + + TRACE("(%p)\n", This); + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + + if(!This->started){ + pthread_mutex_unlock(&pulse_lock); + return S_FALSE; + } + + o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success); + if (o) { + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pthread_cond_wait(&pulse_cond, &pulse_lock); + pa_operation_unref(o); + } else + success = 0; + if (!success) + hr = E_FAIL; + if (SUCCEEDED(hr)) + This->started = FALSE; + pthread_mutex_unlock(&pulse_lock); + return hr; +} + +static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface) +{ + ACImpl *This = impl_from_IAudioClient(iface); + pa_usec_t time; + pa_operation *o; + int success; + HRESULT hr = S_OK; + + TRACE("(%p)\n", This); + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + + if(This->started){ + pthread_mutex_unlock(&pulse_lock); + return AUDCLNT_E_NOT_STOPPED; + } + + if (pa_stream_get_time(This->stream, &time) >= 0) + This->play_ofs += time * This->ss.rate / 1000000; + + o = pa_stream_flush(This->stream, pulse_op_cb, &success); + if (o) { + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pthread_cond_wait(&pulse_cond, &pulse_lock); + pa_operation_unref(o); + } else + success = 0; + if (!success) + hr = S_FALSE; + pthread_mutex_unlock(&pulse_lock); + + return hr; +} + +static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface, + HANDLE event) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr; + + TRACE("(%p)->(%p)\n", This, event); + + if(!event) + return E_INVALIDARG; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if (FAILED(hr)) { + pthread_mutex_unlock(&pulse_lock); + return hr; + } + + if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){ + pthread_mutex_unlock(&pulse_lock); + return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED; + } + This->event = event; + pthread_mutex_unlock(&pulse_lock); + return S_OK; +} + +static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid, + void **ppv) +{ + ACImpl *This = impl_from_IAudioClient(iface); + HRESULT hr; + + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + pthread_mutex_unlock(&pulse_lock); + if (FAILED(hr)) + return hr; + + if(IsEqualIID(riid, &IID_IAudioRenderClient)){ + if(This->dataflow != eRender) + return AUDCLNT_E_WRONG_ENDPOINT_TYPE; + *ppv = &This->IAudioRenderClient_iface; + }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){ + if(This->dataflow != eCapture) + return AUDCLNT_E_WRONG_ENDPOINT_TYPE; + *ppv = &This->IAudioCaptureClient_iface; + }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){ + *ppv = &This->IAudioSessionControl2_iface; + }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){ + *ppv = &This->ISimpleAudioVolume_iface; + }else if(IsEqualIID(riid, &IID_IAudioClock)){ + *ppv = &This->IAudioClock_iface; + } + + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + FIXME("stub %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static const IAudioClientVtbl AudioClient_Vtbl = +{ + AudioClient_QueryInterface, + AudioClient_AddRef, + AudioClient_Release, + AudioClient_Initialize, + AudioClient_GetBufferSize, + AudioClient_GetStreamLatency, + AudioClient_GetCurrentPadding, + AudioClient_IsFormatSupported, + AudioClient_GetMixFormat, + AudioClient_GetDevicePeriod, + AudioClient_Start, + AudioClient_Stop, + AudioClient_Reset, + AudioClient_SetEventHandle, + AudioClient_GetService +}; + +static HRESULT WINAPI AudioRenderClient_QueryInterface( + IAudioRenderClient *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioRenderClient)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface) +{ + ACImpl *This = impl_from_IAudioRenderClient(iface); + return AudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface) +{ + ACImpl *This = impl_from_IAudioRenderClient(iface); + return AudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface, + UINT32 frames, BYTE **data) +{ + ACImpl *This = impl_from_IAudioRenderClient(iface); + UINT32 requested, avail; + HRESULT hr = S_OK; + + TRACE("(%p)->(%u, %p)\n", This, frames, data); + + if(!data) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if(FAILED(hr) || This->locked){ + pthread_mutex_unlock(&pulse_lock); + return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER; + } + avail = pa_stream_writable_size(This->stream) / pa_frame_size(&This->ss); + if (avail < frames){ + pthread_mutex_unlock(&pulse_lock); + WARN("Wanted to write %u, but only %u available\n", frames, avail); + return AUDCLNT_E_BUFFER_TOO_LARGE; + } + + requested = frames * pa_frame_size(&This->ss); + pa_stream_begin_write(This->stream, (void**)data, &requested); + This->locked = frames; + if (requested / pa_frame_size(&This->ss) < frames) { + pa_stream_cancel_write(This->stream); + FIXME("Unable to allocate all (%u/%u) preparing our own buffer\n", requested / pa_frame_size(&This->ss), frames); + *data = This->locked_ptr = This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, frames * pa_frame_size(&This->ss)); + } else { + This->locked_ptr = *data; + } + pthread_mutex_unlock(&pulse_lock); + return hr; +} + +static void free_heap(void *p) +{ + HeapFree(GetProcessHeap(), 0, p); +} + +static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( + IAudioRenderClient *iface, UINT32 written_frames, DWORD flags) +{ + ACImpl *This = impl_from_IAudioRenderClient(iface); + int written; + + TRACE("(%p)->(%u, %x)\n", This, written_frames, flags); + + pthread_mutex_lock(&pulse_lock); + if(!This->locked || !written_frames){ + if (This->tmp_buffer) { + HeapFree(GetProcessHeap(), 0, This->tmp_buffer); + This->tmp_buffer = NULL; + } else if (This->locked) + pa_stream_cancel_write(This->stream); + This->locked = 0; + pthread_mutex_unlock(&pulse_lock); + return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK; + } + + if(flags & AUDCLNT_BUFFERFLAGS_SILENT){ + if(This->ss.format == PA_SAMPLE_U8) + memset(This->locked_ptr, 128, written_frames * pa_frame_size(&This->ss)); + else + memset(This->locked_ptr, 0, written_frames * pa_frame_size(&This->ss)); + } + + This->locked = 0; + if (!This->tmp_buffer) + written = pa_stream_write(This->stream, This->locked_ptr, written_frames * pa_frame_size(&This->ss), NULL, 0, PA_SEEK_RELATIVE); + else + written = pa_stream_write(This->stream, This->locked_ptr, written_frames * pa_frame_size(&This->ss), free_heap, 0, PA_SEEK_RELATIVE); + This->tmp_buffer = NULL; + TRACE("Released %u, wrote %i\n", written_frames * pa_frame_size(&This->ss), written); + pthread_mutex_unlock(&pulse_lock); + + return S_OK; +} + +static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = { + AudioRenderClient_QueryInterface, + AudioRenderClient_AddRef, + AudioRenderClient_Release, + AudioRenderClient_GetBuffer, + AudioRenderClient_ReleaseBuffer +}; + +static HRESULT WINAPI AudioCaptureClient_QueryInterface( + IAudioCaptureClient *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioCaptureClient)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface) +{ + ACImpl *This = impl_from_IAudioCaptureClient(iface); + return IAudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface) +{ + ACImpl *This = impl_from_IAudioCaptureClient(iface); + return IAudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface, + BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos, + UINT64 *qpcpos) +{ + ACImpl *This = impl_from_IAudioCaptureClient(iface); + HRESULT hr; + + TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags, + devpos, qpcpos); + + if(!data || !frames || !flags) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + hr = pulse_stream_valid(This); + if(FAILED(hr) || This->locked){ + pthread_mutex_unlock(&pulse_lock); + return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER; + } + *data = NULL; + *flags = 0; + *frames = This->peeked / pa_frame_size(&This->ss); + if (*frames) + *data = This->locked_ptr ? This->locked_ptr : This->tmp_buffer; + This->locked = *frames; + pthread_mutex_unlock(&pulse_lock); + if(devpos || qpcpos) + IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos); + + return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY; +} + +static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer( + IAudioCaptureClient *iface, UINT32 done) +{ + ACImpl *This = impl_from_IAudioCaptureClient(iface); + + TRACE("(%p)->(%u)\n", This, done); + + pthread_mutex_lock(&pulse_lock); + if (done) { + if (This->locked_ptr) { + pa_stream_drop(This->stream); + This->locked_ptr = NULL; + } else { + HeapFree(GetProcessHeap(), 0, This->tmp_buffer); + This->tmp_buffer = NULL; + } + This->peeked = 0; + } + This->locked = 0; + pthread_mutex_unlock(&pulse_lock); + return S_OK; +} + +static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize( + IAudioCaptureClient *iface, UINT32 *frames) +{ + ACImpl *This = impl_from_IAudioCaptureClient(iface); + + TRACE("(%p)->(%p)\n", This, frames); + return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames); +} + +static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl = +{ + AudioCaptureClient_QueryInterface, + AudioCaptureClient_AddRef, + AudioCaptureClient_Release, + AudioCaptureClient_GetBuffer, + AudioCaptureClient_ReleaseBuffer, + AudioCaptureClient_GetNextPacketSize +}; + +static HRESULT WINAPI SimpleAudioVolume_QueryInterface( + ISimpleAudioVolume *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISimpleAudioVolume)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + return IAudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + return IAudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume( + ISimpleAudioVolume *iface, float level, const GUID *context) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + + FIXME("(%p)->(%f, %p) - stub\n", This, level, context); + + return E_NOTIMPL; +} + +static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume( + ISimpleAudioVolume *iface, float *level) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + + FIXME("(%p)->(%p) - stub\n", This, level); + + return E_NOTIMPL; +} + +static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface, + BOOL mute, const GUID *context) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + + FIXME("(%p)->(%u, %p) - stub\n", This, mute, context); + + return E_NOTIMPL; +} + +static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface, + BOOL *mute) +{ + ACImpl *This = impl_from_ISimpleAudioVolume(iface); + + FIXME("(%p)->(%p) - stub\n", This, mute); + + return E_NOTIMPL; +} + +static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl = +{ + SimpleAudioVolume_QueryInterface, + SimpleAudioVolume_AddRef, + SimpleAudioVolume_Release, + SimpleAudioVolume_SetMasterVolume, + SimpleAudioVolume_GetMasterVolume, + SimpleAudioVolume_SetMute, + SimpleAudioVolume_GetMute +}; + +static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface, + REFIID riid, void **ppv) +{ + ACImpl *This = impl_from_IAudioClock(iface); + + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock)) + *ppv = iface; + else if(IsEqualIID(riid, &IID_IAudioClock2)) + *ppv = &This->IAudioClock2_iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface) +{ + ACImpl *This = impl_from_IAudioClock(iface); + return IAudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI AudioClock_Release(IAudioClock *iface) +{ + ACImpl *This = impl_from_IAudioClock(iface); + return IAudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq) +{ + ACImpl *This = impl_from_IAudioClock(iface); + + TRACE("(%p)->(%p)\n", This, freq); + + *freq = This->ss.rate; + return S_OK; +} + +static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos, + UINT64 *qpctime) +{ + ACImpl *This = impl_from_IAudioClock(iface); + pa_usec_t time; + + TRACE("(%p)->(%p, %p)\n", This, pos, qpctime); + + if(!pos) + return E_POINTER; + + pthread_mutex_lock(&pulse_lock); + if (pa_stream_get_time(This->stream, &time) >= 0) + *pos = time * This->ss.rate / 1000000 - This->play_ofs; + else + *pos = This->play_ofs; + TRACE("Position: %u\n", (unsigned)*pos); + pthread_mutex_unlock(&pulse_lock); + + if(qpctime){ + LARGE_INTEGER stamp, freq; + QueryPerformanceCounter(&stamp); + QueryPerformanceFrequency(&freq); + *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart; + } + + return S_OK; +} + +static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface, + DWORD *chars) +{ + ACImpl *This = impl_from_IAudioClock(iface); + + TRACE("(%p)->(%p)\n", This, chars); + + if(!chars) + return E_POINTER; + + *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ; + + return S_OK; +} + +static const IAudioClockVtbl AudioClock_Vtbl = +{ + AudioClock_QueryInterface, + AudioClock_AddRef, + AudioClock_Release, + AudioClock_GetFrequency, + AudioClock_GetPosition, + AudioClock_GetCharacteristics +}; + +static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface, + REFIID riid, void **ppv) +{ + ACImpl *This = impl_from_IAudioClock2(iface); + return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv); +} + +static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface) +{ + ACImpl *This = impl_from_IAudioClock2(iface); + return IAudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface) +{ + ACImpl *This = impl_from_IAudioClock2(iface); + return IAudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface, + UINT64 *pos, UINT64 *qpctime) +{ + ACImpl *This = impl_from_IAudioClock2(iface); + return AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime); +} + +static const IAudioClock2Vtbl AudioClock2_Vtbl = +{ + AudioClock2_QueryInterface, + AudioClock2_AddRef, + AudioClock2_Release, + AudioClock2_GetDevicePosition +}; + +static HRESULT WINAPI AudioSessionControl_QueryInterface( + IAudioSessionControl2 *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioSessionControl) || + IsEqualIID(riid, &IID_IAudioSessionControl2)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + return IAudioClient_AddRef(&This->IAudioClient_iface); +} + +static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + return IAudioClient_Release(&This->IAudioClient_iface); +} + +static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface, + AudioSessionState *state) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, state); + + if(!state) + return E_POINTER; + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetDisplayName( + IAudioSessionControl2 *iface, WCHAR **name) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, name); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetDisplayName( + IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetIconPath( + IAudioSessionControl2 *iface, WCHAR **path) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, path); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetIconPath( + IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetGroupingParam( + IAudioSessionControl2 *iface, GUID *group) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, group); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetGroupingParam( + IAudioSessionControl2 *iface, const GUID *group, const GUID *session) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group), + debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification( + IAudioSessionControl2 *iface, IAudioSessionEvents *events) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, events); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification( + IAudioSessionControl2 *iface, IAudioSessionEvents *events) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, events); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier( + IAudioSessionControl2 *iface, WCHAR **id) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier( + IAudioSessionControl2 *iface, WCHAR **id) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetProcessId( + IAudioSessionControl2 *iface, DWORD *pid) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)->(%p)\n", This, pid); + + if(!pid) + return E_POINTER; + + *pid = GetCurrentProcessId(); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession( + IAudioSessionControl2 *iface) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)\n", This); + + return S_FALSE; +} + +static HRESULT WINAPI AudioSessionControl_SetDuckingPreference( + IAudioSessionControl2 *iface, BOOL optout) +{ + ACImpl *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)->(%d)\n", This, optout); + + return S_OK; +} + +static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl = +{ + AudioSessionControl_QueryInterface, + AudioSessionControl_AddRef, + AudioSessionControl_Release, + AudioSessionControl_GetState, + AudioSessionControl_GetDisplayName, + AudioSessionControl_SetDisplayName, + AudioSessionControl_GetIconPath, + AudioSessionControl_SetIconPath, + AudioSessionControl_GetGroupingParam, + AudioSessionControl_SetGroupingParam, + AudioSessionControl_RegisterAudioSessionNotification, + AudioSessionControl_UnregisterAudioSessionNotification, + AudioSessionControl_GetSessionIdentifier, + AudioSessionControl_GetSessionInstanceIdentifier, + AudioSessionControl_GetProcessId, + AudioSessionControl_IsSystemSoundsSession, + AudioSessionControl_SetDuckingPreference +}; + +typedef struct _SessionMgr { + IAudioSessionManager2 IAudioSessionManager2_iface; + + LONG ref; + + IMMDevice *device; +} SessionMgr; + +HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface, + REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if(!ppv) + return E_POINTER; + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioSessionManager) || + IsEqualIID(riid, &IID_IAudioSessionManager2)) + *ppv = iface; + if(*ppv){ + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface) +{ + return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface); +} + +ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + ULONG ref; + ref = InterlockedIncrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + return ref; +} + +ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + ULONG ref; + ref = InterlockedDecrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + if(!ref) + HeapFree(GetProcessHeap(), 0, This); + return ref; +} + +HRESULT WINAPI AudioSessionManager_GetAudioSessionControl( + IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, + IAudioSessionControl **out) +{ +#if 0 + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + AudioSession *session; + AudioSessionWrapper *wrapper; + HRESULT hr; + + TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), + flags, out); + + hr = get_audio_session(session_guid, This->device, 0, &session); + if(FAILED(hr)) + return hr; + + wrapper = AudioSessionWrapper_Create(NULL); + if(!wrapper) + return E_OUTOFMEMORY; + + wrapper->session = session; + + *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface; + + return S_OK; +#else + FIXME("stub\n"); + return E_NOTIMPL; +#endif +} + +HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume( + IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, + ISimpleAudioVolume **out) +{ +#if 0 + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + AudioSession *session; + AudioSessionWrapper *wrapper; + HRESULT hr; + + TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), + flags, out); + + hr = get_audio_session(session_guid, This->device, 0, &session); + if(FAILED(hr)) + return hr; + + wrapper = AudioSessionWrapper_Create(NULL); + if(!wrapper) + return E_OUTOFMEMORY; + + wrapper->session = session; + + *out = &wrapper->ISimpleAudioVolume_iface; + + return S_OK; +#else + FIXME("stub\n"); + return E_NOTIMPL; +#endif +} + +HRESULT WINAPI AudioSessionManager_GetSessionEnumerator( + IAudioSessionManager2 *iface, IAudioSessionEnumerator **out) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, out); + return E_NOTIMPL; +} + +HRESULT WINAPI AudioSessionManager_RegisterSessionNotification( + IAudioSessionManager2 *iface, IAudioSessionNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification( + IAudioSessionManager2 *iface, IAudioSessionNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +HRESULT WINAPI AudioSessionManager_RegisterDuckNotification( + IAudioSessionManager2 *iface, const WCHAR *session_id, + IAudioVolumeDuckNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification( + IAudioSessionManager2 *iface, + IAudioVolumeDuckNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl = +{ + AudioSessionManager_QueryInterface, + AudioSessionManager_AddRef, + AudioSessionManager_Release, + AudioSessionManager_GetAudioSessionControl, + AudioSessionManager_GetSimpleAudioVolume, + AudioSessionManager_GetSessionEnumerator, + AudioSessionManager_RegisterSessionNotification, + AudioSessionManager_UnregisterSessionNotification, + AudioSessionManager_RegisterDuckNotification, + AudioSessionManager_UnregisterDuckNotification +}; + +HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, + IAudioSessionManager2 **out) +{ + SessionMgr *This; + + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr)); + if(!This) + return E_OUTOFMEMORY; + + This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl; + This->device = device; + This->ref = 1; + + *out = &This->IAudioSessionManager2_iface; + + return S_OK; +} diff --git a/dlls_winepulse.drv_winepulse.drv b/dlls_winepulse.drv_winepulse.drv new file mode 100644 index 0000000..819ea12 --- /dev/null +++ b/dlls_winepulse.drv_winepulse.drv @@ -0,0 +1,4 @@ +# MMDevAPI driver functions +@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs +@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint +@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager diff --git a/sources b/sources index 5e16a24..c732477 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -c9850862748bde7994bd757574e8974e wine-1.3.24.tar.bz2 -a9db1ff36532dace37b93771408a7620 wine-1.3.24.tar.bz2.sign +15471f78fee9d211c63381da815cbfb6 wine-1.3.25.tar.bz2 +ba96a3c648f23df05fc5c9d6c412292e wine-1.3.25.tar.bz2.sign diff --git a/wine-README-fedora-pulseaudio b/wine-README-fedora-pulseaudio deleted file mode 100644 index 41645fb..0000000 --- a/wine-README-fedora-pulseaudio +++ /dev/null @@ -1,61 +0,0 @@ -Wine and Pulseaudio Support ---------------------------- -Currently wine does not have native support for pulseaudio. However, some -patches exist to make wine use a native pulseaudio backend (see [1]) -These have been included into the fedora wine package. If you have problems -please do _not_ report them to the wine project. - -Here are some useful configuration options taken from [1]: - -HKCU\Software\Wine\Drivers:Audio -A string which contains a comma seperated list of audio backends for wine to -try, in order of preference. For example “pulse,alsa,esd”. Settable through -winecfg. - -HKCU\Software\Wine\Pulse Driver:MonitorDevices -A string which contains Y or N. If Y, sink monitors will show up as input -devices. If N, only capture sources will be show up as input devices. -Default is Y. - -HKCU\Software\Wine\DirectSound:HardwareAcceleration -A string. Also settable in winecfg. When set to “Emulation” WaveOut will be -used for directsound, which is more likely to work, but will have more latency. -When set to “Full” the directsound driver is tried for directsound support, -resulting in less latency, but possibley failure or bad sound. - -[1] - http://art.ified.ca/?page_id=40 - -Other ways to get wine working with pulseaudio are described below: - -ALSA ----- -To achieve sound output via the wine alsa driver you need to add -a pulseaudio alsa device to the alsa configuration and activate it in wine. See -http://www.pulseaudio.org/wiki/PerfectSetup#ALSAApplications on how to do this. -For this alsa-plugins-pulseaudio.i386 should be installed. - - -Esound ------- -If the pulseaudio-esound-compat package is installed selecting the EsounD driver -in wine should enable sound via pulseaudio. - -OSS ---- -To use the oss driver of wine copy the following script to ~/bin/wine and make -sure that ~/bin is in your path with a higher priority then /usr/bin. The script -will call the pulseaudio dsp wrapper padsp. - -#!/bin/sh -if [ -x /usr/lib*/alsa-lib/libasound_module_pcm_pulse.so ] && [ -x "/usr/bin/padsp" ] ; then - echo "Running padsp as pulseaudio wrapper for wine" - exec padsp -n Wine -- /usr/bin/wine "$@" -else - exec /usr/bin/wine "$@" -fi - -For more information on a good pulseaudio setup see: -http://www.pulseaudio.org/wiki/PerfectSetup - -If you think you have found a bug related to this take a look at the bug -reporting procedure at http://fedoraproject.org/wiki/AndreasBierfert/Wine. diff --git a/wine-pulseaudio-configure.patch b/wine-pulseaudio-configure.patch new file mode 100644 index 0000000..eda998f --- /dev/null +++ b/wine-pulseaudio-configure.patch @@ -0,0 +1,223 @@ +--- wine-1.3.25/configure.winepulse-configure 2011-07-22 19:32:43.000000000 +0200 ++++ wine-1.3.25/configure 2011-08-01 19:53:18.268715283 +0200 +@@ -628,6 +628,8 @@ + ALSALIBS + GSTREAMER_INCL + GSTREAMER_LIBS ++PULSEINCL ++PULSELIBS + LIBGETTEXTPO + ZLIB + FREETYPEINCL +@@ -799,6 +801,7 @@ + with_oss + with_png + with_pthread ++with_pulse + with_sane + with_tiff + with_v4l +@@ -1488,6 +1491,7 @@ + --without-oss do not use the OSS sound support + --without-png do not use PNG + --without-pthread do not use the pthread library ++ --without-pulse do not use PulseAudio sound support + --without-sane do not use SANE (scanner support) + --without-tiff do not use TIFF + --without-v4l do not use v4l1 (v4l support) +@@ -2661,6 +2665,12 @@ + fi + + ++# Check whether --with-pulse was given. ++if test "${with_pulse+set}" = set; then : ++ withval=$with_pulse; ++fi ++ ++ + # Check whether --with-sane was given. + if test "${with_sane+set}" = set; then : + withval=$with_sane; +@@ -10504,6 +10514,87 @@ + fi + fi + ++PULSELIBS="" ++ ++PULSEINCL="" ++ ++if test "x$with_pulse" != "xno"; ++then ++ ac_save_CPPFLAGS="$CPPFLAGS" ++ if test "$PKG_CONFIG" != "false"; ++ then ++ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`" ++ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`" ++ ++ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags" ++ for ac_header in pulse/pulseaudio.h ++do : ++ ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default" ++if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then : ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_PULSE_PULSEAUDIO_H 1 ++_ACEOF ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5 ++$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; } ++if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lpulse $ac_pulse_libs $LIBS" ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char pa_stream_is_corked (); ++int ++main () ++{ ++return pa_stream_is_corked (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_pulse_pa_stream_is_corked=yes ++else ++ ac_cv_lib_pulse_pa_stream_is_corked=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5 ++$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; } ++if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then : ++ ++$as_echo "#define HAVE_PULSEAUDIO 1" >>confdefs.h ++ ++ PULSELIBS="$ac_pulse_libs" ++ PULSEINCL="$ac_pulse_cflags" ++fi ++ ++ ++fi ++ ++done ++ ++ fi ++ CPPFLAGS="$ac_save_CPPFLAGS" ++fi ++if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then : ++ case "x$with_pulse" in ++ x) as_fn_append wine_warnings "|libpulse ${notice_platform}development files not found or too old, Pulse won't be supported." ;; ++ xno) ;; ++ *) as_fn_error $? "libpulse ${notice_platform}development files not found or too old, Pulse won't be supported. ++This is an error since --with-pulse was requested." "$LINENO" 5 ;; ++esac ++fi ++ + if test "x$with_gstreamer" != "xno" + then + ac_save_CPPFLAGS="$CPPFLAGS" +@@ -11742,12 +11833,13 @@ + + test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no} + test -n "$COREAUDIO" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no} ++test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no} + test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no} + test "$ac_cv_header_linux_joystick_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} + +-if test "x$ALSALIBS$COREAUDIOLIBS" = "x" -a \ ++if test "x$ALSALIBS$COREAUDIOLIBS$PULSELIBS" = "x" -a \ + "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \ +- "x$with_alsa$with_coreaudio$with_oss" != xnonono ++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnonono + then + as_fn_append wine_warnings "|No sound system was found. Windows applications will be silent." + fi +@@ -15155,10 +15247,12 @@ + wine_fn_config_dll winegstreamer enable_winegstreamer + wine_fn_config_dll winejoystick.drv enable_winejoystick_drv + wine_fn_config_dll winemapi enable_winemapi ++wine_fn_config_dll winemmaudio.drv enable_winemmaudio_drv + wine_fn_config_dll winemp3.acm enable_winemp3_acm + wine_fn_config_dll wineoss.drv enable_wineoss_drv + wine_fn_config_dll wineps.drv enable_wineps_drv install-lib + wine_fn_config_dll wineps16.drv16 enable_win16 ++wine_fn_config_dll winepulse.drv enable_winepulse_drv + wine_fn_config_dll wineqtdecoder enable_wineqtdecoder + wine_fn_config_dll winequartz.drv enable_winequartz_drv + wine_fn_config_dll winex11.drv enable_winex11_drv +--- wine-1.3.25/configure.ac.winepulse-configure 2011-07-22 19:32:43.000000000 +0200 ++++ wine-1.3.25/configure.ac 2011-08-01 19:51:41.206755129 +0200 +@@ -73,6 +73,7 @@ + [if test "x$withval" = "xno"; then ac_cv_header_png_h=no; fi]) + AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]), + [if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi]) ++AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support])) + AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)])) + AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]), + [if test "x$withval" = "xno"; then ac_cv_header_tiffio_h=no; fi]) +@@ -1435,6 +1436,30 @@ + WINE_WARNING_WITH(gettextpo,[test "x$LIBGETTEXTPO" = "x"],[GetText ${notice_platform}development files not found (or too old). Internationalization won't be fully supported.]) + fi + ++dnl **** Check for PulseAudio **** ++AC_SUBST(PULSELIBS,"") ++AC_SUBST(PULSEINCL,"") ++if test "x$with_pulse" != "xno"; ++then ++ ac_save_CPPFLAGS="$CPPFLAGS" ++ if test "$PKG_CONFIG" != "false"; ++ then ++ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`" ++ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`" ++ ++ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags" ++ AC_CHECK_HEADERS(pulse/pulseaudio.h, ++ [AC_CHECK_LIB(pulse, pa_stream_is_corked, ++ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio]) ++ PULSELIBS="$ac_pulse_libs" ++ PULSEINCL="$ac_pulse_cflags"],,$ac_pulse_libs) ++ ]) ++ fi ++ CPPFLAGS="$ac_save_CPPFLAGS" ++fi ++WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"], ++ [libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.]) ++ + dnl **** Check for gstreamer **** + if test "x$with_gstreamer" != "xno" + then +@@ -1636,13 +1661,14 @@ + dnl **** Disable unsupported winmm drivers **** + test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no} + test -n "$COREAUDIO" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no} ++test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no} + test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no} + test "$ac_cv_header_linux_joystick_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} + + dnl **** Check for any sound system **** +-if test "x$ALSALIBS$COREAUDIOLIBS" = "x" -a \ ++if test "x$ALSALIBS$COREAUDIOLIBS$PULSELIBS" = "x" -a \ + "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \ +- "x$with_alsa$with_coreaudio$with_oss" != xnonono ++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnonono + then + WINE_WARNING([No sound system was found. Windows applications will be silent.]) + fi +@@ -2832,6 +2858,7 @@ + WINE_CONFIG_DLL(wineoss.drv) + WINE_CONFIG_DLL(wineps.drv,,[install-lib]) + WINE_CONFIG_DLL(wineps16.drv16,enable_win16) ++WINE_CONFIG_DLL(winepulse.drv) + WINE_CONFIG_DLL(wineqtdecoder) + WINE_CONFIG_DLL(winequartz.drv) + WINE_CONFIG_DLL(winex11.drv) diff --git a/wine-udisks1.patch b/wine-udisks1.patch new file mode 100644 index 0000000..597271a --- /dev/null +++ b/wine-udisks1.patch @@ -0,0 +1,116 @@ +From: Detlef Riekenberg +Subject: [PATCH 1/2] mountmgr: Make parse_uuid visible, when hal or dbus/udisks is used [try 4] +Message-Id: <1311335756-9824-1-git-send-email-wine.dev@web.de> +Date: Fri, 22 Jul 2011 13:55:55 +0200 + +try 2: +make parse_uuid only visible, when hal or dbus/udisks is used +try 3: +resend without changes +try 4: +Add a comment as reminder to parse NTFS serial number + +-- +By by ... Detlef +--- + dlls/mountmgr.sys/device.c | 32 ++++++++++++++++++++++++++++++++ + dlls/mountmgr.sys/hal.c | 27 --------------------------- + dlls/mountmgr.sys/mountmgr.h | 1 + + 3 files changed, 33 insertions(+), 27 deletions(-) + +diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c +index a3aa81c..9537551 100644 +--- a/dlls/mountmgr.sys/device.c ++++ b/dlls/mountmgr.sys/device.c +@@ -121,6 +121,38 @@ static char *strdupA( const char *str ) + return ret; + } + ++#if defined (SONAME_LIBDBUS_1) || defined (SONAME_LIBHAL) ++GUID *parse_uuid( GUID *guid, const char *str ) ++{ ++ /* standard uuid format */ ++ if (strlen(str) == 36) ++ { ++ UNICODE_STRING strW; ++ WCHAR buffer[39]; ++ ++ if (MultiByteToWideChar( CP_UNIXCP, 0, str, 36, buffer + 1, 36 )) ++ { ++ buffer[0] = '{'; ++ buffer[37] = '}'; ++ buffer[38] = 0; ++ RtlInitUnicodeString( &strW, buffer ); ++ if (!RtlGUIDFromString( &strW, guid )) return guid; ++ } ++ } ++ ++ /* ToDo: Check for NTFS serial number */ ++ ++ /* check for xxxx-xxxx format (FAT serial number) */ ++ if (strlen(str) == 9 && str[4] == '-') ++ { ++ memset( guid, 0, sizeof(*guid) ); ++ if (sscanf( str, "%hx-%hx", &guid->Data2, &guid->Data3 ) == 2) return guid; ++ } ++ ++ return NULL; ++} ++#endif ++ + static const GUID *get_default_uuid( int letter ) + { + static GUID guid; +diff --git a/dlls/mountmgr.sys/hal.c b/dlls/mountmgr.sys/hal.c +index 82a70e9..ff10a3e 100644 +--- a/dlls/mountmgr.sys/hal.c ++++ b/dlls/mountmgr.sys/hal.c +@@ -106,33 +106,6 @@ static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr) + return EXCEPTION_CONTINUE_SEARCH; + } + +-static GUID *parse_uuid( GUID *guid, const char *str ) +-{ +- /* standard uuid format */ +- if (strlen(str) == 36) +- { +- UNICODE_STRING strW; +- WCHAR buffer[39]; +- +- if (MultiByteToWideChar( CP_UNIXCP, 0, str, 36, buffer + 1, 36 )) +- { +- buffer[0] = '{'; +- buffer[37] = '}'; +- buffer[38] = 0; +- RtlInitUnicodeString( &strW, buffer ); +- if (!RtlGUIDFromString( &strW, guid )) return guid; +- } +- } +- +- /* check for xxxx-xxxx format (FAT serial number) */ +- if (strlen(str) == 9 && str[4] == '-') +- { +- memset( guid, 0, sizeof(*guid) ); +- if (sscanf( str, "%hx-%hx", &guid->Data2, &guid->Data3 ) == 2) return guid; +- } +- return NULL; +-} +- + /* HAL callback for new device */ + static void new_device( LibHalContext *ctx, const char *udi ) + { +diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h +index a47a3e3..a8e4389 100644 +--- a/dlls/mountmgr.sys/mountmgr.h ++++ b/dlls/mountmgr.sys/mountmgr.h +@@ -52,6 +52,7 @@ enum device_type + DEVICE_RAMDISK + }; + ++extern GUID *parse_uuid( GUID *guid, const char *str ) DECLSPEC_HIDDEN; + extern NTSTATUS add_volume( const char *udi, const char *device, const char *mount_point, + enum device_type type, const GUID *guid ) DECLSPEC_HIDDEN; + extern NTSTATUS remove_volume( const char *udi ) DECLSPEC_HIDDEN; +-- +1.7.5.4 + diff --git a/wine-udisks2.patch b/wine-udisks2.patch new file mode 100644 index 0000000..e83b339 --- /dev/null +++ b/wine-udisks2.patch @@ -0,0 +1,719 @@ +From: Detlef Riekenberg +Subject: [PATCH 2/2] mountmgr: Support the dbus service udisks for dynamic devices :-) [try 4] +Message-Id: <1311335756-9824-2-git-send-email-wine.dev@web.de> +Date: Fri, 22 Jul 2011 13:55:56 +0200 + +udisks is prefered, but libhal support is still present as fallback. +This allow the libhal requirement on linux to die. + +libhal support in binary packages is only needed, when udisk is +optional for a linux distribution. + +The logic to add/remove the devices/volumes is the same as present in +our libhal support. + +autoheader/autoconf are needed before compilation + +try 2: +remove a trailing space in the code an a trailing " \" in Makefile.in +try 3: +changes for configure.ac where missing in try 2 +try 4 +sync to current git (wineesd was removed) + +-- +By by ... Detlef +--- + configure.ac | 20 ++- + dlls/mountmgr.sys/Makefile.in | 5 +- + dlls/mountmgr.sys/hal.c | 15 ++ + dlls/mountmgr.sys/mountmgr.c | 2 + + dlls/mountmgr.sys/mountmgr.h | 3 + + dlls/mountmgr.sys/udisks.c | 554 +++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 596 insertions(+), 3 deletions(-) + create mode 100644 dlls/mountmgr.sys/udisks.c + +diff --git a/configure.ac b/configure.ac +index b213923..d9d2472 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -42,6 +42,7 @@ AC_ARG_WITH(coreaudio, AS_HELP_STRING([--without-coreaudio],[do not use the Core + AC_ARG_WITH(cups, AS_HELP_STRING([--without-cups],[do not use CUPS])) + AC_ARG_WITH(curses, AS_HELP_STRING([--without-curses],[do not use (n)curses]), + [if test "x$withval" = "xno"; then ac_cv_header_ncurses_h=no; ac_cv_header_curses_h=no; fi]) ++AC_ARG_WITH(dbus, AS_HELP_STRING([--without-dbus],[do not use dbus (dynamic device support)])) + AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig]), + [if test "x$withval" = "xno"; then ac_cv_header_fontconfig_fontconfig_h=no; fi]) + AC_ARG_WITH(freetype, AS_HELP_STRING([--without-freetype],[do not use the FreeType library])) +@@ -1196,6 +1197,23 @@ fi + WINE_WARNING_WITH(xslt,[test "x$ac_cv_lib_soname_xslt" = "x"], + [libxslt ${notice_platform}development files not found, xslt won't be supported.]) + ++dnl **** Check for libdbus **** ++AC_SUBST(DBUSINCL,"") ++if test "x$with_dbus" != "xno" ++then ++ ac_save_CPPFLAGS="$CPPFLAGS" ++ if test "$PKG_CONFIG" != "false" ++ then ++ ac_dbus_libs="`$PKG_CONFIG --libs dbus-1 2>/dev/null`" ++ ac_dbus_cflags="`$PKG_CONFIG --cflags dbus-1 2>/dev/null`" ++ CPPFLAGS="$CPPFLAGS $ac_dbus_cflags" ++ fi ++ AC_CHECK_HEADER(dbus/dbus.h, ++ [WINE_CHECK_SONAME(dbus-1,dbus_bus_get,[DBUSINCL="$ac_dbus_cflags"],,[$ac_dbus_libs])]) ++ ++ CPPFLAGS="$ac_save_CPPFLAGS" ++fi ++ + dnl **** Check for libhal **** + AC_SUBST(HALINCL,"") + if test "x$with_hal" != "xno" +@@ -1216,7 +1234,7 @@ then + fi + CPPFLAGS="$ac_save_CPPFLAGS" + fi +-WINE_NOTICE_WITH(hal,[test "x$ac_cv_lib_soname_hal" = "x" -a "x$ac_cv_header_DiskArbitration_DiskArbitration_h" != "xyes"], ++WINE_NOTICE_WITH(hal,[test "x$ac_cv_lib_soname_hal" = "x" -a "x$ac_cv_lib_soname_dbus_1" = "x" -a "x$ac_cv_header_DiskArbitration_DiskArbitration_h" != "xyes"], + [libhal/libdbus ${notice_platform}development files not found, no dynamic device support.]) + + dnl **** Check for libgnutls **** +diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in +index 91203e1..f3355e0 100644 +--- a/dlls/mountmgr.sys/Makefile.in ++++ b/dlls/mountmgr.sys/Makefile.in +@@ -2,13 +2,14 @@ MODULE = mountmgr.sys + IMPORTS = uuid advapi32 ntoskrnl.exe + DELAYIMPORTS = user32 + EXTRADLLFLAGS = -Wb,--subsystem,native +-EXTRADEFS = @HALINCL@ ++EXTRADEFS = @HALINCL@ @DBUSINCL@ + EXTRALIBS = @DISKARBITRATIONLIB@ + + C_SRCS = \ + device.c \ + diskarb.c \ + hal.c \ +- mountmgr.c ++ mountmgr.c \ ++ udisks.c + + @MAKE_DLL_RULES@ +diff --git a/dlls/mountmgr.sys/hal.c b/dlls/mountmgr.sys/hal.c +index ff10a3e..8e31624 100644 +--- a/dlls/mountmgr.sys/hal.c ++++ b/dlls/mountmgr.sys/hal.c +@@ -245,6 +245,21 @@ void initialize_hal(void) + { + HANDLE handle; + ++ /* wait for udisks, when needed */ ++ if (dbus_present) ++ { ++ DWORD res; ++ ++ TRACE("waiting for udisks\n"); ++ res = WaitForSingleObject(dbus_present, 100); ++ ++ if (!res && *udisks_version) ++ { ++ TRACE("Skipping, found udisks\n"); ++ return; ++ } ++ } ++ + if (!load_functions()) return; + if (!(handle = CreateThread( NULL, 0, hal_thread, NULL, 0, NULL ))) return; + CloseHandle( handle ); +diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c +index 4e9900f..df0dbd3 100644 +--- a/dlls/mountmgr.sys/mountmgr.c ++++ b/dlls/mountmgr.sys/mountmgr.c +@@ -443,6 +443,8 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) + RtlInitUnicodeString( &nameW, harddiskW ); + status = IoCreateDriver( &nameW, harddisk_driver_entry ); + ++ /* start udisks before hal */ ++ initialize_udisks(); + initialize_hal(); + initialize_diskarbitration(); + +diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h +index a8e4389..3c3c564 100644 +--- a/dlls/mountmgr.sys/mountmgr.h ++++ b/dlls/mountmgr.sys/mountmgr.h +@@ -35,8 +35,11 @@ + #define WINE_MOUNTMGR_EXTENSIONS + #include "ddk/mountmgr.h" + ++extern void initialize_udisks(void) DECLSPEC_HIDDEN; + extern void initialize_hal(void) DECLSPEC_HIDDEN; + extern void initialize_diskarbitration(void) DECLSPEC_HIDDEN; ++extern HANDLE dbus_present; ++extern char udisks_version[]; + + /* device functions */ + +diff --git a/dlls/mountmgr.sys/udisks.c b/dlls/mountmgr.sys/udisks.c +new file mode 100644 +index 0000000..898f41d +--- /dev/null ++++ b/dlls/mountmgr.sys/udisks.c +@@ -0,0 +1,554 @@ ++/* ++ * udisks devices support ++ * ++ * Copyright 2006 Alexandre Julliard ++ * Copyright 2011 Detlef Riekenberg ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include "config.h" ++#include "wine/port.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mountmgr.h" ++#include "winnls.h" ++#include "excpt.h" ++ ++#include "wine/library.h" ++#include "wine/exception.h" ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); ++ ++HANDLE dbus_present = NULL; ++char udisks_version[32]; ++ ++#ifdef SONAME_LIBDBUS_1 ++ ++#include ++ ++/* ########## */ ++ ++typedef struct properties_s { ++ CHAR *device_file; ++ CHAR *id_usage; ++ CHAR *id_type; ++ CHAR *id_uuid; ++ BOOL device_is_mounted; ++ BOOL device_is_optical_disc; ++ BOOL device_is_removable; ++ CHAR *device_mount_paths; ++ CHAR *drive_media; ++ CHAR *drive_media_compatibility; ++ int depth; ++ const char * last_name; ++}properties_t; ++ ++static const char *my_match_rule = "interface=org.freedesktop.UDisks"; ++static const char *dest_udisks_device = "org.freedesktop.UDisks.Device"; ++static const char *dest_udisks = "org.freedesktop.UDisks"; ++static const char *path_udisks = "/org/freedesktop/UDisks"; ++static const char *iface_dbus_prop = "org.freedesktop.DBus.Properties"; ++static const char *daemonversion = "DaemonVersion"; ++ ++/* ########## */ ++ ++#define DBUS_FUNCS \ ++ DO_FUNC(dbus_bus_add_match); \ ++ DO_FUNC(dbus_bus_get); \ ++ DO_FUNC(dbus_bus_remove_match); \ ++ DO_FUNC(dbus_connection_pop_message); \ ++ DO_FUNC(dbus_connection_read_write_dispatch); \ ++ DO_FUNC(dbus_connection_send_with_reply_and_block); \ ++ DO_FUNC(dbus_connection_unref); \ ++ DO_FUNC(dbus_error_free); \ ++ DO_FUNC(dbus_error_init); \ ++ DO_FUNC(dbus_error_is_set); \ ++ DO_FUNC(dbus_message_append_args); \ ++ DO_FUNC(dbus_message_get_member); \ ++ DO_FUNC(dbus_message_iter_get_arg_type); \ ++ DO_FUNC(dbus_message_iter_get_basic); \ ++ DO_FUNC(dbus_message_iter_init); \ ++ DO_FUNC(dbus_message_iter_next); \ ++ DO_FUNC(dbus_message_iter_recurse); \ ++ DO_FUNC(dbus_message_new_method_call); \ ++ DO_FUNC(dbus_message_type_to_string); \ ++ DO_FUNC(dbus_message_unref) ++ ++#define DO_FUNC(f) static typeof(f) * p_##f ++DBUS_FUNCS; ++#undef DO_FUNC ++ ++ ++static BOOL load_dbus_functions(void) ++{ ++ void *dbus_handle; ++ char error[128]; ++ ++ if (!(dbus_handle = wine_dlopen(SONAME_LIBDBUS_1, RTLD_NOW|RTLD_GLOBAL, error, sizeof(error)))) ++ goto failed; ++ ++#define DO_FUNC(f) if (!(p_##f = wine_dlsym(RTLD_DEFAULT, #f, error, sizeof(error)))) goto failed ++ DBUS_FUNCS; ++#undef DO_FUNC ++ ++ return TRUE; ++ ++failed: ++ WARN("failed to load udisks support: %s\n", error); ++ return FALSE; ++} ++ ++/* ########## */ ++ ++static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr) ++{ ++ if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION) ++ return EXCEPTION_EXECUTE_HANDLER; ++ return EXCEPTION_CONTINUE_SEARCH; ++} ++ ++/* ######################################### ++ * get_properties_from_iter [internal] ++ * ++ * NOTES ++ * format of args in a reply from GetAll: ++ * an ARRAY of DICT_ENTRY ++ * each DICT_ENTRY has a STRING (property name) and a VARIANT (property value) ++ * each VARIANT has a BOOLEAN or a STRING or an ARRAY of STRING or an here unused value ++ */ ++static BOOL get_properties_from_iter(properties_t * p, DBusMessageIter * iter) ++{ ++ DBusMessageIter sub; ++ int arg_type = p_dbus_message_iter_get_arg_type(iter); ++ ++ p->depth++; ++ while (arg_type != DBUS_TYPE_INVALID) ++ { ++ if ((arg_type == DBUS_TYPE_ARRAY) || ++ (arg_type == DBUS_TYPE_DICT_ENTRY) || ++ (arg_type == DBUS_TYPE_VARIANT)) ++ { ++ p_dbus_message_iter_recurse(iter, &sub); ++ if (!get_properties_from_iter(p, &sub)) ++ { ++ p->depth--; ++ return FALSE; ++ } ++ } ++ else if (arg_type == DBUS_TYPE_STRING) ++ { ++ char * data; ++ p_dbus_message_iter_get_basic(iter, &data); ++ if (p->depth == 3) p->last_name = data; ++ else if (p->last_name) ++ { ++ if (!strcmp(p->last_name, "DeviceFile")) ++ p->device_file = data; ++ else if (!strcmp(p->last_name, "DeviceMountPaths")) ++ p->device_mount_paths = data; /* use only the first entry */ ++ else if (!strcmp(p->last_name, "DriveMedia")) ++ p->drive_media = data; ++ else if (!strcmp(p->last_name, "DriveMediaCompatibility")) ++ p->drive_media_compatibility = data; /* use only the first entry */ ++ else if (!strcmp(p->last_name, "IdType")) ++ p->id_type = data; ++ else if (!strcmp(p->last_name, "IdUsage")) ++ p->id_usage = data; ++ else if (!strcmp(p->last_name, "IdUuid")) ++ p->id_uuid = data; ++ ++ p->last_name = NULL; ++ } ++ } ++ else if (arg_type == DBUS_TYPE_BOOLEAN) ++ { ++ dbus_bool_t data; ++ if (p->last_name) ++ { ++ p_dbus_message_iter_get_basic(iter, &data); ++ if (!strcmp(p->last_name, "DeviceIsMounted")) ++ p->device_is_mounted = data; ++ else if (!strcmp(p->last_name, "DeviceIsOpticalDisc")) ++ p->device_is_optical_disc = data; ++ else if (!strcmp(p->last_name, "DeviceIsRemovable")) ++ p->device_is_removable = data; ++ ++ p->last_name = NULL; ++ } ++ } ++ ++ p_dbus_message_iter_next(iter); ++ arg_type = p_dbus_message_iter_get_arg_type(iter); ++ } ++ p->depth--; ++ return TRUE; ++} ++ ++/* ########### */ ++ ++static DBusMessage * get_properties_from_path(properties_t * p, DBusConnection *ctx, const char * path) ++{ ++ DBusMessage *request; ++ DBusMessage *reply = NULL; ++ DBusMessageIter iter; ++ DBusError error; ++ ++ TRACE("(%p, %p, %s)\n", p, ctx, path); ++ ++ memset(p, 0, sizeof(properties_t)); ++ request = p_dbus_message_new_method_call(dest_udisks, path, iface_dbus_prop, "GetAll"); ++ if (request) ++ { ++ if (p_dbus_message_append_args(request, DBUS_TYPE_STRING, &dest_udisks_device, DBUS_TYPE_INVALID)) ++ { ++ p_dbus_error_init(&error); ++ if ((reply = p_dbus_connection_send_with_reply_and_block(ctx, request, -1, &error))) ++ { ++ p_dbus_message_iter_init(reply, &iter); ++ get_properties_from_iter(p, &iter); ++ } ++ else ++ WARN("no reply for %s\n", path); ++ ++ p_dbus_error_free(&error); ++ } ++ else ++ WARN("dbus_message_append_args failed for 'GetAll'\n"); ++ ++ p_dbus_message_unref(request); ++ } ++ return reply; ++} ++ ++/* ########### */ ++ ++static int get_drive_type(properties_t * p) ++{ ++ /* examples: optical_cd, optical_cd_rw, optical_dvd_plus_r_dl */ ++ if (p->device_is_optical_disc && p->drive_media && !memcmp(p->drive_media, "optical_", 8)) ++ { ++ if (!memcmp(p->drive_media + 8, "cd", 2)) ++ return DEVICE_CDROM; ++ else ++ return DEVICE_DVD; ++ } ++ else if (p->drive_media_compatibility && !strcmp(p->drive_media_compatibility, "floppy")) ++ return DEVICE_FLOPPY; ++ else if (!p->device_is_removable && p->id_usage && !strcmp(p->id_usage, "filesystem")) ++ return DEVICE_HARDDISK_VOL; ++ ++ return DEVICE_UNKNOWN; ++} ++ ++/* ########### */ ++ ++static void udisks_add_device(DBusConnection *ctx, const char *path) ++{ ++ DBusMessage *reply; ++ properties_t p; ++ GUID guid; ++ GUID *guid_ptr = NULL; ++ ++ TRACE("%s\n", debugstr_a(path)); ++ ++ reply = get_properties_from_path(&p, ctx, path); ++ if (reply) ++ { ++ int drive_type = get_drive_type(&p); ++ ++ TRACE("DeviceFile: %s\n", p.device_file); ++ TRACE("IdUsage: %s\n", p.id_usage); ++ TRACE("IdType: %s\n", p.id_type); ++ TRACE("IdUuid: %s\n", p.id_uuid); ++ TRACE("DeviceIsMounted: %d (%s)\n", p.device_is_mounted, p.device_is_mounted ? "true" : "false"); ++ TRACE("DeviceIsOpticalDisc: %d (%s)\n", p.device_is_optical_disc, p.device_is_optical_disc ? "true" : "false"); ++ TRACE("DeviceIsRemovable: %d (%s)\n", p.device_is_removable, p.device_is_removable ? "true" : "false"); ++ TRACE("DeviceMountPaths: %s\n", p.device_mount_paths); ++ TRACE("DriveMedia: %s\n", p.drive_media); ++ TRACE("DriveMediaCompatibility: %s\n", p.drive_media_compatibility); ++ TRACE("using drive_type: %d\n", drive_type); ++ ++ if (p.device_is_mounted && p.device_mount_paths) ++ { ++ if (p.id_uuid) ++ guid_ptr = parse_uuid(&guid, p.id_uuid); ++ ++ if (p.device_is_removable) ++ add_dos_device(-1, path, p.device_file, p.device_mount_paths, drive_type, guid_ptr); ++ else if (guid_ptr) ++ add_volume(path, p.device_file, p.device_mount_paths, DEVICE_HARDDISK_VOL, guid_ptr); ++ ++ } ++ p_dbus_message_unref(reply); ++ } ++} ++ ++static void udisks_remove_device(DBusConnection *ctx, const char *path) ++{ ++ TRACE("%s\n", debugstr_a(path)); ++ ++ if (remove_dos_device(-1, path)) ++ remove_volume(path); ++} ++ ++static void udisks_change_device(DBusConnection *ctx, const char *path) ++{ ++ DBusMessage *reply; ++ properties_t p; ++ ++ TRACE("%s\n", debugstr_a(path)); ++ ++ reply = get_properties_from_path(&p, ctx, path); ++ if (reply) ++ { ++ int drive_type = get_drive_type(&p); ++ ++ if (p.device_is_mounted && p.device_mount_paths) ++ udisks_add_device(ctx, path); ++ else ++ { ++ TRACE("DeviceFile: %s\n", p.device_file); ++ TRACE("IdUsage: %s\n", p.id_usage); ++ TRACE("IdType: %s\n", p.id_type); ++ TRACE("IdUuid: %s\n", p.id_uuid); ++ TRACE("DeviceIsMounted: %d (%s)\n", p.device_is_mounted, p.device_is_mounted ? "true" : "false"); ++ TRACE("DeviceIsOpticalDisc: %d (%s)\n", p.device_is_optical_disc, p.device_is_optical_disc ? "true" : "false"); ++ TRACE("DeviceIsRemovable: %d (%s)\n", p.device_is_removable, p.device_is_removable ? "true" : "false"); ++ TRACE("DeviceMountPaths: %s\n", p.device_mount_paths); ++ TRACE("DriveMedia: %s\n", p.drive_media); ++ TRACE("DriveMediaCompatibility: %s\n", p.drive_media_compatibility); ++ TRACE("using drive_type: %d\n", drive_type); ++ ++ udisks_remove_device(ctx, path); ++ } ++ p_dbus_message_unref(reply); ++ } ++} ++ ++/* ########### */ ++ ++static void udisks_get_all_devices(DBusConnection *ctx) ++{ ++ DBusMessage *request; ++ DBusMessage *reply; ++ DBusMessageIter iter; ++ DBusMessageIter sub; ++ DBusError error; ++ int arg_type; ++ ++ request = p_dbus_message_new_method_call(dest_udisks, path_udisks, dest_udisks, "EnumerateDevices"); ++ if (request) ++ { ++ p_dbus_error_init(&error); ++ if ((reply = p_dbus_connection_send_with_reply_and_block(ctx, request, -1, &error))) ++ { ++ p_dbus_message_iter_init(reply, &iter); ++ arg_type = p_dbus_message_iter_get_arg_type(&iter); ++ if (arg_type == DBUS_TYPE_ARRAY) ++ { ++ p_dbus_message_iter_recurse(&iter, &sub); ++ while ((arg_type = p_dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_OBJECT_PATH) ++ { ++ char * data; ++ p_dbus_message_iter_get_basic(&sub, &data); ++ udisks_add_device(ctx, data); ++ p_dbus_message_iter_next(&sub); ++ } ++ } ++ else ++ WARN("expected ARRAY, got %c\n", arg_type); ++ ++ p_dbus_message_unref(reply); ++ } ++ p_dbus_error_free(&error); ++ p_dbus_message_unref(request); ++ } ++} ++ ++/* ########## */ ++ ++static void udisks_get_version(DBusConnection *ctx) ++{ ++ DBusMessage *request; ++ DBusMessage *reply; ++ DBusMessageIter iter; ++ DBusMessageIter sub; ++ DBusError error; ++ int arg_type; ++ ++ request = p_dbus_message_new_method_call(dest_udisks, path_udisks, iface_dbus_prop, "Get"); ++ if (request) ++ { ++ if (p_dbus_message_append_args(request, DBUS_TYPE_STRING, &dest_udisks, ++ DBUS_TYPE_STRING, &daemonversion, ++ DBUS_TYPE_INVALID)) ++ { ++ p_dbus_error_init(&error); ++ if ((reply = p_dbus_connection_send_with_reply_and_block(ctx, request, -1, &error))) ++ { ++ p_dbus_message_iter_init(reply, &iter); ++ arg_type = p_dbus_message_iter_get_arg_type(&iter); ++ if (arg_type == DBUS_TYPE_VARIANT) ++ { ++ p_dbus_message_iter_recurse(&iter, &sub); ++ arg_type = p_dbus_message_iter_get_arg_type(&sub); ++ if (arg_type == DBUS_TYPE_STRING) ++ { ++ char * data; ++ p_dbus_message_iter_get_basic(&sub, &data); ++ lstrcpynA(udisks_version, data, sizeof(udisks_version) - 1); ++ TRACE("found udisks daemon %s\n", udisks_version); ++ } ++ else ++ WARN("expected STRING, got %c\n", arg_type); ++ ++ } ++ else ++ WARN("expected VARIANT, got %c\n", arg_type); ++ ++ p_dbus_message_unref(reply); ++ } ++ p_dbus_error_free(&error); ++ } ++ else ++ WARN("dbus_message_append_args failed\n"); ++ ++ p_dbus_message_unref(request); ++ } ++ return; ++ ++} ++ ++/* ##### */ ++ ++static DWORD WINAPI udisks_thread( void *arg ) ++{ ++ DBusConnection *ctx; ++ DBusMessage *msg; ++ DBusMessageIter iter; ++ DBusError error; ++ const char *member; ++ int arg_type; ++ char *data; ++ ++ p_dbus_error_init(&error); ++ ctx = p_dbus_bus_get(DBUS_BUS_SYSTEM, &error); ++ ++ if (!ctx) ++ { ++ SetEvent(dbus_present); /* wakeup hal support */ ++ WARN("failed to get system dbus connection: %s\n", error.message); ++ p_dbus_error_free(&error); ++ return 1; ++ } ++ ++ p_dbus_bus_add_match(ctx, my_match_rule, &error); ++ if (p_dbus_error_is_set(&error)) ++ { ++ SetEvent(dbus_present); /* wakeup hal support */ ++ WARN("add dbus filter failed: %s\n", error.message); ++ p_dbus_error_free(&error); ++ p_dbus_connection_unref(ctx); ++ return 1; ++ } ++ ++ udisks_get_version(ctx); ++ SetEvent(dbus_present); /* wakeup hal support */ ++ ++ if (!*udisks_version) ++ { ++ TRACE("udisks service not available\n"); ++ p_dbus_bus_remove_match(ctx, my_match_rule, NULL); ++ p_dbus_error_free(&error); ++ p_dbus_connection_unref(ctx); ++ return 1; ++ } ++ ++ __TRY ++ { ++ /* retrieve all existing devices */ ++ udisks_get_all_devices(ctx); ++ ++ while (p_dbus_connection_read_write_dispatch(ctx, -1 )) ++ { ++ while ((msg = p_dbus_connection_pop_message(ctx))) ++ { ++ member = p_dbus_message_get_member(msg); ++ p_dbus_message_iter_init(msg, &iter); ++ arg_type = p_dbus_message_iter_get_arg_type(&iter); ++ ++ if (arg_type == DBUS_TYPE_OBJECT_PATH) ++ p_dbus_message_iter_get_basic(&iter, &data); ++ ++ if (!lstrcmpA(member, "DeviceChanged")) ++ udisks_change_device(ctx, data); ++ else if (!lstrcmpA(member, "DeviceAdded")) ++ udisks_add_device(ctx, data); ++ else if (!lstrcmpA(member, "DeviceRemoved")) ++ udisks_remove_device(ctx, data); ++ else if (lstrcmpA(member, "DeviceJobChanged")) ++ WARN("got signal for %s\n", member); ++ ++ p_dbus_message_unref(msg); ++ } ++ } ++ } ++ __EXCEPT(assert_fault) ++ { ++ WARN("dbus assertion failure, disabling UDisks support\n"); ++ return 1; ++ } ++ __ENDTRY; ++ ++ p_dbus_bus_remove_match(ctx, my_match_rule, NULL); ++ p_dbus_error_free(&error); ++ p_dbus_connection_unref(ctx); ++ return 0; ++} ++ ++void initialize_udisks(void) ++{ ++ HANDLE handle = NULL; ++ ++ dbus_present = NULL; ++ *udisks_version = 0; ++ if (!load_dbus_functions()) return; ++ ++ /* delay hal support, start udisks thread */ ++ if (!(dbus_present = CreateEventW(NULL, TRUE, FALSE, NULL)) || ++ !(handle = CreateThread(NULL, 0, udisks_thread, NULL, 0, NULL))) ++ { ++ /* something failed: wakeup hal now */ ++ if (dbus_present) SetEvent(dbus_present); ++ return; ++ } ++ CloseHandle(handle); ++} ++ ++#else /* SONAME_LIBDBUS_1 */ ++ ++void initialize_udisks(void) ++{ ++ TRACE("Skipping, DBUS support not compiled in\n"); ++} ++ ++#endif /* SONAME_LIBDBUS_1 */ +-- +1.7.5.4 + diff --git a/wine.spec b/wine.spec index c75aac4..5432bdb 100644 --- a/wine.spec +++ b/wine.spec @@ -1,6 +1,6 @@ %global no64bit 0 Name: wine -Version: 1.3.24 +Version: 1.3.25 Release: 1%{?dist} Summary: A Windows 16/32/64 bit emulator @@ -37,14 +37,20 @@ Source300: wine-mime-msi.desktop Patch200: wine-imagemagick-6.5.patch -# explain how to use wine with pulseaudio -# see http://bugs.winehq.org/show_bug.cgi?id=10495 -# and http://art.ified.ca/?page_id=40 -Patch400: http://art.ified.ca/downloads/winepulse/winepulse-configure.ac-1.3.22.patch -Patch401: http://art.ified.ca/downloads/winepulse/winepulse-0.40.patch -Patch402: http://art.ified.ca/downloads/winepulse/winepulse-winecfg-1.3.11.patch -Source402: wine-README-fedora-pulseaudio - +# pull pulse parts from Maarten Lankhorst multimedia repository +# http://repo.or.cz/w/wine/multimedia.git +Patch400: wine-pulseaudio-configure.patch +Source401: dlls_winepulse.drv_Makefile.in +Source402: dlls_winepulse.drv_mmdevdrv.c +Source403: dlls_winepulse.drv_winepulse.drv + +# add udisks support +# https://bugzilla.redhat.com/show_bug.cgi?id=712755 +# http://bugs.winehq.org/show_bug.cgi?id=21713 +# http://source.winehq.org/patches/data/76788 +# http://source.winehq.org/patches/data/76787 +Patch410: wine-udisks1.patch +Patch411: wine-udisks2.patch # smooth tahoma (#693180) # disable embedded bitmaps @@ -66,7 +72,6 @@ BuildRequires: autoconf BuildRequires: desktop-file-utils BuildRequires: alsa-lib-devel BuildRequires: audiofile-devel -BuildRequires: esound-devel BuildRequires: freeglut-devel BuildRequires: lcms-devel BuildRequires: libieee1284-devel @@ -84,7 +89,6 @@ BuildRequires: sane-backends-devel BuildRequires: zlib-devel BuildRequires: fontforge freetype-devel BuildRequires: libgphoto2-devel -BuildRequires: jack-audio-connection-kit-devel # #217338 BuildRequires: isdn4k-utils-devel # modular x @@ -181,6 +185,11 @@ Obsoletes: wine-arts < 0.9.34 Provides: wine-arts = %{version}-%{release} Obsoletes: wine-tools <= 1.1.27 Provides: wine-tools = %{version}-%{release} +# removed as of 1.3.25 (new sound api) +Obsoletes: wine-esd <= 1.3.24 +Provides: wine-esd = %{version}-%{release} +Obsoletes: wine-jack <= 1.3.24 +Provides: wine-jack = %{version}-%{release} # removed as of 1.3.19 (we don't support oss4) Obsoletes: wine-oss <= 1.3.18 Provides: wine-oss = %{version}-%{release} @@ -358,28 +367,6 @@ BuildArch: noarch %description common Common wine files and scripts. -%package esd -Summary: ESD sound support for wine -Group: System Environment/Libraries -Requires: wine-core = %{version}-%{release} - -%description esd -ESD sound support for wine - -%package jack -Summary: JACK sound support for wine -Group: System Environment/Libraries -Requires: wine-core = %{version}-%{release} -%ifarch %{ix86} -Requires: jack-audio-connection-kit(x86-32) -%endif -%ifarch x86_64 -Requires: jack-audio-connection-kit(x86-64) -%endif - -%description jack -JACK sound support for wine - %package ldap Summary: LDAP support for wine Group: System Environment/Libraries @@ -452,10 +439,17 @@ This package adds an openal driver for wine. %prep %setup -q +%patch400 -p1 -b .winepulse-configure +mkdir -p dlls/winepulse.drv +cp -p %{SOURCE401} dlls/winepulse.drv/Makefile.in +cp -p %{SOURCE402} dlls/winepulse.drv/mmdevdrv.c +cp -p %{SOURCE403} dlls/winepulse.drv/winepulse.drv.spec + %patch200 -b .imagemagick -%patch400 -p1 -b .winepulse -%patch401 -p1 -b .winepulse -%patch402 -p1 -b .winepulse + +%patch410 -p1 -b .mountmgr +%patch411 -p1 -b .mountmgr + autoreconf @@ -471,6 +465,7 @@ export CFLAGS="`echo $RPM_OPT_FLAGS | sed -e 's/-Wp,-D_FORTIFY_SOURCE=2//'` -Wno --x-includes=%{_includedir} --x-libraries=%{_libdir} \ --with-pulse \ --with-x \ + --without-xinput2 \ %ifarch x86_64 --enable-win64 \ %endif @@ -627,7 +622,6 @@ desktop-file-install \ # deploy pulseaudio readme cp %{SOURCE3} README-FEDORA -cp %{SOURCE402} README-FEDORA-PulseAudio cp %{SOURCE502} README-tahoma mkdir -p %{buildroot}%{_sysconfdir}/ld.so.conf.d/ @@ -732,12 +726,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %post core -p /sbin/ldconfig %postun core -p /sbin/ldconfig -%post esd -p /sbin/ldconfig -%postun esd -p /sbin/ldconfig - -%post jack -p /sbin/ldconfig -%postun jack -p /sbin/ldconfig - %post ldap -p /sbin/ldconfig %postun ldap -p /sbin/ldconfig @@ -1342,17 +1330,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %endif %{_initrddir}/wine - -# esd subpackage -%files esd -%defattr(-,root,root,-) -%{_libdir}/wine/wineesd.drv.so - -# jack subpackage -%files jack -%defattr(-,root,root,-) -%{_libdir}/wine/winejack.drv.so - # ldap subpackage %files ldap %defattr(-,root,root,-) @@ -1406,8 +1383,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %files pulseaudio %defattr(-,root,root,-) -# winepulse documentation -%doc README-FEDORA-PulseAudio %{_libdir}/wine/winepulse.drv.so %files alsa @@ -1421,6 +1396,15 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %endif %changelog +* Fri Jul 22 2011 Andreas Bierfert +- 1.3.25-1 +- version upgrade +- remove -jack and -esd (retired upstream) +- rebase to Maarten Lankhorst's winepulse +- drop obsolete winepulse readme +- add udisks support from pending patches (winehq#21713, rhbz#712755) +- disable xinput2 (broken) + * Sun Jul 10 2011 Andreas Bierfert - 1.3.24-1 - version upgrade diff --git a/winepulse-0.40.patch b/winepulse-0.40.patch deleted file mode 100644 index 1831943..0000000 --- a/winepulse-0.40.patch +++ /dev/null @@ -1,2757 +0,0 @@ -diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in -new file mode 100644 -index 0000000..a6fdbf8 ---- /dev/null -+++ b/dlls/winepulse.drv/Makefile.in -@@ -0,0 +1,10 @@ -+MODULE = winepulse.drv -+IMPORTS = winmm user32 kernel32 -+EXTRALIBS = @PULSELIBS@ -+EXTRAINCL = @PULSEINCL@ -+ -+C_SRCS = waveout.c \ -+ wavein.c \ -+ pulse.c -+ -+@MAKE_DLL_RULES@ -diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c -new file mode 100644 -index 0000000..04676a6 ---- /dev/null -+++ b/dlls/winepulse.drv/pulse.c -@@ -0,0 +1,880 @@ -+/* -+ * Wine Driver for PulseAudio -+ * http://pulseaudio.org/ -+ * -+ * Copyright 2009 Arthur Taylor -+ * -+ * Contains code from other wine sound drivers. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+ -+#include -+#include -+ -+#include "windef.h" -+#include "winbase.h" -+#include "wingdi.h" -+#include "winuser.h" -+#include "winreg.h" -+#include "mmddk.h" -+#include "ks.h" -+#include "ksguid.h" -+#include "ksmedia.h" -+ -+#ifdef HAVE_UNISTD_H -+# include -+#endif -+#include -+ -+#ifdef HAVE_PULSEAUDIO -+ -+#include "wine/unicode.h" -+#include "wine/debug.h" -+#include "wine/library.h" -+ -+#include -+#include -+WINE_DEFAULT_DEBUG_CHANNEL(wave); -+ -+/* These strings used only for tracing */ -+const char * PULSE_getCmdString(enum win_wm_message msg) { -+ static char unknown[32]; -+#define MSG_TO_STR(x) case x: return #x -+ switch(msg) { -+ MSG_TO_STR(WINE_WM_PAUSING); -+ MSG_TO_STR(WINE_WM_RESTARTING); -+ MSG_TO_STR(WINE_WM_RESETTING); -+ MSG_TO_STR(WINE_WM_HEADER); -+ MSG_TO_STR(WINE_WM_BREAKLOOP); -+ MSG_TO_STR(WINE_WM_CLOSING); -+ MSG_TO_STR(WINE_WM_STARTING); -+ MSG_TO_STR(WINE_WM_STOPPING); -+ MSG_TO_STR(WINE_WM_XRUN); -+ MSG_TO_STR(WINE_WM_FEED); -+ } -+#undef MSG_TO_STR -+ sprintf(unknown, "UNKNOWN(0x%08x)", msg); -+ return unknown; -+} -+ -+/*======================================================================* -+ * Ring Buffer Functions - copied from winealsa.drv * -+ *======================================================================*/ -+ -+/* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */ -+#define USE_PIPE_SYNC -+ -+#ifdef USE_PIPE_SYNC -+#define INIT_OMR(omr) do { if (pipe(omr->msg_pipe) < 0) { omr->msg_pipe[0] = omr->msg_pipe[1] = -1; } } while (0) -+#define CLOSE_OMR(omr) do { close(omr->msg_pipe[0]); close(omr->msg_pipe[1]); } while (0) -+#define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0) -+#define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0) -+#define RESET_OMR(omr) do { } while (0) -+#define WAIT_OMR(omr, sleep) \ -+ do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \ -+ pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0) -+#else -+#define INIT_OMR(omr) do { omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL); } while (0) -+#define CLOSE_OMR(omr) do { CloseHandle(omr->msg_event); } while (0) -+#define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0) -+#define CLEAR_OMR(omr) do { } while (0) -+#define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0) -+#define WAIT_OMR(omr, sleep) \ -+ do { WaitForSingleObject((omr)->msg_event, sleep); } while (0) -+#endif -+ -+#define PULSE_RING_BUFFER_INCREMENT 64 -+ -+/****************************************************************** -+ * PULSE_InitRingMessage -+ * -+ * Initialize the ring of messages for passing between driver's caller -+ * and playback/record thread -+ */ -+int PULSE_InitRingMessage(PULSE_MSG_RING* omr) -+{ -+ omr->msg_toget = 0; -+ omr->msg_tosave = 0; -+ INIT_OMR(omr); -+ omr->ring_buffer_size = PULSE_RING_BUFFER_INCREMENT; -+ omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(PULSE_MSG)); -+ -+ InitializeCriticalSection(&omr->msg_crst); -+ omr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PULSE_MSG_RING.msg_crst"); -+ return 0; -+} -+ -+/****************************************************************** -+ * PULSE_DestroyRingMessage -+ * -+ */ -+int PULSE_DestroyRingMessage(PULSE_MSG_RING* omr) -+{ -+ CLOSE_OMR(omr); -+ HeapFree(GetProcessHeap(),0,omr->messages); -+ omr->messages = NULL; -+ omr->ring_buffer_size = PULSE_RING_BUFFER_INCREMENT; -+ omr->msg_crst.DebugInfo->Spare[0] = 0; -+ DeleteCriticalSection(&omr->msg_crst); -+ return 0; -+} -+/****************************************************************** -+ * PULSE_ResetRingMessage -+ * -+ */ -+void PULSE_ResetRingMessage(PULSE_MSG_RING* omr) -+{ -+ RESET_OMR(omr); -+} -+ -+/****************************************************************** -+ * PULSE_WaitRingMessage -+ * -+ */ -+void PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep) -+{ -+ WAIT_OMR(omr, sleep); -+} -+ -+/****************************************************************** -+ * PULSE_AddRingMessage -+ * -+ * Inserts a new message into the ring (should be called from DriverProc derived routines) -+ */ -+int PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait) -+{ -+ HANDLE hEvent = INVALID_HANDLE_VALUE; -+ -+ EnterCriticalSection(&omr->msg_crst); -+ if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size))) -+ { -+ int old_ring_buffer_size = omr->ring_buffer_size; -+ omr->ring_buffer_size += PULSE_RING_BUFFER_INCREMENT; -+ omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(PULSE_MSG)); -+ /* Now we need to rearrange the ring buffer so that the new -+ buffers just allocated are in between omr->msg_tosave and -+ omr->msg_toget. -+ */ -+ if (omr->msg_tosave < omr->msg_toget) -+ { -+ memmove(&(omr->messages[omr->msg_toget + PULSE_RING_BUFFER_INCREMENT]), -+ &(omr->messages[omr->msg_toget]), -+ sizeof(PULSE_MSG)*(old_ring_buffer_size - omr->msg_toget) -+ ); -+ omr->msg_toget += PULSE_RING_BUFFER_INCREMENT; -+ } -+ } -+ if (wait) -+ { -+ hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); -+ if (hEvent == INVALID_HANDLE_VALUE) -+ { -+ ERR("can't create event !?\n"); -+ LeaveCriticalSection(&omr->msg_crst); -+ return 0; -+ } -+ /* fast messages have to be added at the start of the queue */ -+ omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size; -+ -+ omr->messages[omr->msg_toget].msg = msg; -+ omr->messages[omr->msg_toget].param = param; -+ omr->messages[omr->msg_toget].hEvent = hEvent; -+ } -+ else -+ { -+ omr->messages[omr->msg_tosave].msg = msg; -+ omr->messages[omr->msg_tosave].param = param; -+ omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE; -+ omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size; -+ } -+ LeaveCriticalSection(&omr->msg_crst); -+ /* signal a new message */ -+ SIGNAL_OMR(omr); -+ if (wait) -+ { -+ /* wait for playback/record thread to have processed the message */ -+ WaitForSingleObject(hEvent, INFINITE); -+ CloseHandle(hEvent); -+ } -+ return 1; -+} -+ -+/****************************************************************** -+ * PULSE_RetrieveRingMessage -+ * -+ * Get a message from the ring. Should be called by the playback/record thread. -+ */ -+int PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr, -+ enum win_wm_message *msg, DWORD *param, HANDLE *hEvent) -+{ -+ EnterCriticalSection(&omr->msg_crst); -+ -+ if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */ -+ { -+ LeaveCriticalSection(&omr->msg_crst); -+ return 0; -+ } -+ -+ *msg = omr->messages[omr->msg_toget].msg; -+ omr->messages[omr->msg_toget].msg = 0; -+ *param = omr->messages[omr->msg_toget].param; -+ *hEvent = omr->messages[omr->msg_toget].hEvent; -+ omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size; -+ CLEAR_OMR(omr); -+ LeaveCriticalSection(&omr->msg_crst); -+ return 1; -+} -+ -+/************************************************************************** -+ * Win32 threaded mainloop implementation -+ *************************************************************************/ -+ -+static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) { -+ int r; -+ -+ PULSE_MainloopUnlock(); -+ r = poll(ufds, nfds, timeout); -+ PULSE_MainloopLock(); -+ -+ return r; -+} -+ -+static DWORD CALLBACK PULSE_Mainloop(LPVOID lpParam) { -+ TRACE("Mainloop thread started\n"); -+ -+ pa_mainloop_set_poll_func(PULSE_ml, poll_func, NULL); -+ -+ PULSE_MainloopLock(); -+ pa_mainloop_run(PULSE_ml, NULL); -+ PULSE_MainloopUnlock(); -+ -+ return 0; -+} -+ -+void PULSE_MainloopStart(void) { -+ TRACE("Mainloop starting\n"); -+ -+ PULSE_ml_hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); -+ PULSE_ml_hMutex = CreateMutexW(NULL, FALSE, NULL); -+ PULSE_ml_hThread = CreateThread(NULL, 0, PULSE_Mainloop, NULL, 0, NULL); -+ -+ TRACE("Mainloop started\n"); -+} -+ -+void PULSE_MainloopStop(void) { -+ TRACE("Mainloop stopping\n"); -+ -+ PULSE_MainloopLock(); -+ pa_mainloop_quit(PULSE_ml, 0); -+ PULSE_MainloopUnlock(); -+ -+ WaitForSingleObject(PULSE_ml_hThread, INFINITE); -+ CloseHandle(PULSE_ml_hThread); -+ CloseHandle(PULSE_ml_hEvent); -+ CloseHandle(PULSE_ml_hMutex); -+ -+ TRACE("Mainloop stopped\n"); -+} -+ -+void PULSE_MainloopLock(void) { -+ DWORD res = WaitForSingleObject(PULSE_ml_hMutex, INFINITE); -+ if (res != WAIT_OBJECT_0) -+ ERR("PULSE_MainloopLock mutex acquire failed: 0x%x\n", res); -+} -+ -+void PULSE_MainloopUnlock(void) { -+ if (!ReleaseMutex(PULSE_ml_hMutex)) -+ ERR("PULSE_MainloopUnlock mutex release failed\n"); -+} -+ -+void PULSE_MainloopWait(void) { -+ DWORD res = SignalObjectAndWait(PULSE_ml_hMutex, PULSE_ml_hEvent, INFINITE, FALSE); -+ if (res != WAIT_OBJECT_0) -+ ERR("PULSE_MainloopWait failed: 0x%x\n", res); -+ -+ PULSE_MainloopLock(); -+} -+ -+void PULSE_MainloopSignal(void) { -+ if (!PulseEvent(PULSE_ml_hEvent)) -+ ERR("PULSE_MainloopSignal failed\n"); -+} -+ -+/************************************************************************** -+ * Utility Functions -+ *************************************************************************/ -+ -+/****************************************************************** -+ * PULSE_SetupFormat -+ * -+ * Checks to see if the audio format in wf is supported, and if so set up the -+ * pa_sample_spec at ss to that format. -+ */ -+BOOL PULSE_SetupFormat(LPWAVEFORMATEX wf, pa_sample_spec *ss) { -+ WAVEFORMATEXTENSIBLE *wfex; -+ -+ ss->channels = wf->nChannels; -+ ss->rate = wf->nSamplesPerSec; -+ ss->format = PA_SAMPLE_INVALID; -+ -+ if (ss->rate < DSBFREQUENCY_MIN || ss->rate > DSBFREQUENCY_MAX) return FALSE; -+ -+ switch (wf->wFormatTag) { -+ case WAVE_FORMAT_PCM: -+ /* MSDN says that for WAVE_FORMAT_PCM, nChannels must be 1 or 2 and -+ * wBitsPerSample must be 8 or 16, yet other values are used by some -+ * applications in the wild for surround. */ -+ if (ss->channels > 6 || ss->channels < 1) return FALSE; -+ ss->format = wf->wBitsPerSample == 8 ? PA_SAMPLE_U8 -+ : wf->wBitsPerSample == 16 ? PA_SAMPLE_S16NE -+ : wf->wBitsPerSample == 32 ? PA_SAMPLE_S32NE -+ : PA_SAMPLE_INVALID; -+ break; -+ -+ case WAVE_FORMAT_MULAW: -+ if (wf->wBitsPerSample == 8) ss->format = PA_SAMPLE_ULAW; -+ break; -+ -+ case WAVE_FORMAT_ALAW: -+ if (wf->wBitsPerSample == 8) ss->format = PA_SAMPLE_ALAW; -+ break; -+ -+ case WAVE_FORMAT_EXTENSIBLE: -+ if (wf->cbSize > 22) return FALSE; -+ if (ss->channels < 1 || ss->channels > 6) return FALSE; -+ wfex = (WAVEFORMATEXTENSIBLE *)wf; -+ if (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { -+ if (wf->wBitsPerSample == wfex->Samples.wValidBitsPerSample) { -+ ss->format = wf->wBitsPerSample == 8 ? PA_SAMPLE_U8 -+ : wf->wBitsPerSample == 16 ? PA_SAMPLE_S16NE -+ : wf->wBitsPerSample == 32 ? PA_SAMPLE_S32NE -+ : PA_SAMPLE_INVALID; -+ } else { -+ return FALSE; -+ } -+ } else if (wf->wBitsPerSample != wfex->Samples.wValidBitsPerSample) { -+ return FALSE; -+ } else if ((IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) { -+ ss->format = PA_SAMPLE_FLOAT32NE; -+ } else { -+ WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT of WAVE_FORMAT_EXTENSIBLE supported\n"); -+ return FALSE; -+ } -+ break; -+ -+ default: -+ WARN("only WAVE_FORMAT_PCM, WAVE_FORMAT_MULAW, WAVE_FORMAT_ALAW and WAVE_FORMAT_EXTENSIBLE supported\n"); -+ return FALSE; -+ } -+ -+ if (!pa_sample_spec_valid(ss)) return FALSE; -+ if (wf->nBlockAlign != pa_frame_size(ss)) { -+ ERR("wf->nBlockAlign != the format frame size!\n"); -+ return FALSE; -+ } -+ -+ return TRUE; -+} -+ -+/****************************************************************** -+ * PULSE_SetupFormat -+ * -+ * Converts the current time to a MMTIME structure. lpTime shold be validated -+ * before calling. -+ */ -+HRESULT PULSE_UsecToMMTime(pa_usec_t time, LPMMTIME lpTime, const pa_sample_spec *ss) { -+ pa_usec_t temp; -+ size_t bytes; -+ -+ /* Convert to milliseconds */ -+ time /= 1000; -+ -+ bytes = time * pa_bytes_per_second(ss) / 1000; -+ /* Align to frame size */ -+ bytes -= bytes % pa_frame_size(ss); -+ -+ switch (lpTime->wType) { -+ case TIME_SAMPLES: -+ lpTime->u.sample = bytes / pa_frame_size(ss); -+ TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample); -+ break; -+ case TIME_MS: -+ lpTime->u.ms = time; -+ TRACE("TIME_MS=%u\n", lpTime->u.ms); -+ break; -+ case TIME_SMPTE: -+ lpTime->u.smpte.fps = 30; -+ temp = bytes / pa_frame_size(ss); -+ temp += ss->rate / lpTime->u.smpte.fps - 1; -+ lpTime->u.smpte.sec = time/1000; -+ temp -= lpTime->u.smpte.sec * ss->rate; -+ lpTime->u.smpte.min = lpTime->u.smpte.sec / 60; -+ lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min; -+ lpTime->u.smpte.hour = lpTime->u.smpte.min / 60; -+ lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour; -+ lpTime->u.smpte.frame = temp * lpTime->u.smpte.fps / ss->rate; -+ TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n", -+ lpTime->u.smpte.hour, lpTime->u.smpte.min, -+ lpTime->u.smpte.sec, lpTime->u.smpte.frame); -+ break; -+ default: -+ WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType); -+ lpTime->wType = TIME_BYTES; -+ /* fall through */ -+ case TIME_BYTES: -+ lpTime->u.cb = bytes; -+ TRACE("TIME_BYTES=%u\n", lpTime->u.cb); -+ break; -+ } -+ -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * PULSE_WaitForOperation -+ * -+ * Waits for pa operations to complete, and dereferences the operation. -+ */ -+void PULSE_WaitForOperation(pa_operation *o) { -+ if (!o) return; -+ -+ for (;;) { -+ if (pa_operation_get_state(o) != PA_OPERATION_RUNNING) -+ break; -+ PULSE_MainloopWait(); -+ } -+ pa_operation_unref(o); -+} -+ -+/************************************************************************** -+ * Common Callbacks -+ */ -+ -+/************************************************************************** -+ * PULSE_StreamRequestCallback -+ * -+ * Called by the pulse mainloop whenever it wants/has audio data. -+ */ -+void PULSE_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata) { -+ WINE_WAVEINST *ww = (WINE_WAVEINST*)userdata; -+ -+ TRACE("Server has %u bytes\n", nbytes); -+ -+ /* Make sure that the player/recorder is running */ -+ if (ww->hThread != INVALID_HANDLE_VALUE && ww->msgRing.messages) { -+ PULSE_AddRingMessage(&ww->msgRing, WINE_WM_FEED, (DWORD)nbytes, FALSE); -+ } -+} -+ -+ -+/************************************************************************** -+ * PULSE_StreamSuspendedCallback [internal] -+ * -+ * Called by the pulse mainloop any time stream playback is intentionally -+ * suspended or resumed from being suspended. -+ */ -+void PULSE_StreamSuspendedCallback(pa_stream *s, void *userdata) { -+ WINE_WAVEINST *wwo = (WINE_WAVEINST*)userdata; -+ assert(s && wwo); -+ -+ /* Currently we handle this kinda like an underrun. Perhaps we should -+ * tell the client somehow so it doesn't just hang? */ -+ -+ if (!pa_stream_is_suspended(s) && wwo->hThread != INVALID_HANDLE_VALUE && wwo->msgRing.ring_buffer_size > 0) -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_XRUN, 0, FALSE); -+} -+ -+/************************************************************************** -+ * PULSE_StreamUnderflowCallback [internal] -+ * -+ * Called by the pulse mainloop when the prebuf runs out of data. -+ */ -+void PULSE_StreamUnderflowCallback(pa_stream *s, void *userdata) { -+ WINE_WAVEINST *wwo = (WINE_WAVEINST*)userdata; -+ assert(s && wwo); -+ -+ /* If we aren't playing, don't care ^_^ */ -+ if (wwo->state != WINE_WS_PLAYING) return; -+ -+ TRACE("Underrun occurred.\n"); -+ -+ if (wwo->hThread != INVALID_HANDLE_VALUE && wwo->msgRing.ring_buffer_size > 0) -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_XRUN, 0, FALSE); -+} -+ -+/************************************************************************** -+ * PULSE_StreamMovedCallback [internal] -+ * -+ * Called by the pulse mainloop when the stream gets moved, resulting in -+ * possibly different metrics. -+ */ -+void PULSE_StreamMovedCallback(pa_stream *s, void *userdata) { -+ FIXME("stub"); -+} -+ -+ -+/****************************************************************** -+ * PULSE_StreamStateCallback -+ * -+ * Called by pulse whenever the state of the stream changes. -+ */ -+void PULSE_StreamStateCallback(pa_stream *s, void *userdata) { -+ assert(s); -+ -+ switch (pa_stream_get_state(s)) { -+ case PA_STREAM_READY: -+ TRACE("Stream %p ready\n", userdata); -+ break; -+ -+ case PA_STREAM_TERMINATED: /* Stream closed normally */ -+ TRACE("Stream %p terminated\n", userdata); -+ break; -+ -+ case PA_STREAM_FAILED: /* Stream closed not-normally */ -+ ERR("Stream %p failed!\n", userdata); -+ break; -+ -+ case PA_STREAM_UNCONNECTED: -+ case PA_STREAM_CREATING: -+ return; -+ } -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * PULSE_StreamSucessCallback -+ */ -+void PULSE_StreamSuccessCallback(pa_stream *s, int success, void *userdata) { -+ if (!success) -+ WARN("Stream %p operation failed: %s\n", userdata, pa_strerror(pa_context_errno(PULSE_context))); -+ -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * PULSE_ContextSuccessCallback -+ */ -+void PULSE_ContextSuccessCallback(pa_context *c, int success, void *userdata) { -+ if (!success) ERR("Context operation failed: %s\n", pa_strerror(pa_context_errno(c))); -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * Connection management and sink / source management. -+ */ -+ -+/************************************************************************** -+ * PULSE_ContextStateCallback [internal] -+ */ -+static void PULSE_ContextStateCallback(pa_context *c, void *userdata) { -+ assert(c); -+ -+ switch (pa_context_get_state(c)) { -+ case PA_CONTEXT_CONNECTING: -+ case PA_CONTEXT_UNCONNECTED: -+ case PA_CONTEXT_AUTHORIZING: -+ case PA_CONTEXT_SETTING_NAME: -+ break; -+ -+ case PA_CONTEXT_READY: -+ case PA_CONTEXT_TERMINATED: -+ PULSE_MainloopSignal(); -+ break; -+ -+ case PA_CONTEXT_FAILED: -+ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c))); -+ PULSE_MainloopSignal(); -+ break; -+ } -+} -+ -+/************************************************************************** -+ * PULSE_AllocateWaveinDevice [internal] -+ * -+ * Creates or adds a device to WInDev based on the pa_source_info. -+ */ -+static void PULSE_AllocateWaveinDevice(const char *name, const char *device, const char *description, const pa_cvolume *v) { -+ WINE_WAVEDEV *wdi; -+ -+ if (WInDev) -+ wdi = HeapReAlloc(GetProcessHeap(), 0, WInDev, sizeof(WINE_WAVEDEV) * (PULSE_WidNumDevs + 1)); -+ else -+ wdi = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV)); -+ -+ if (!wdi) return; -+ -+ WInDev = wdi; -+ wdi = &WInDev[PULSE_WidNumDevs++]; -+ memset(wdi, 0, sizeof(WINE_WAVEDEV)); -+ memset(&(wdi->caps.in), 0, sizeof(wdi->caps.in)); -+ snprintf(wdi->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name); -+ wdi->device_name = pa_xstrdup(device); -+ MultiByteToWideChar(CP_UTF8, 0, description, -1, wdi->caps.in.szPname, sizeof(wdi->caps.in.szPname)/sizeof(WCHAR)); -+ wdi->caps.in.szPname[sizeof(wdi->caps.in.szPname)/sizeof(WCHAR) - 1] = '\0'; -+ wdi->caps.in.wMid = MM_CREATIVE; -+ wdi->caps.in.wPid = MM_CREATIVE_SBP16_WAVEOUT; -+ wdi->caps.in.vDriverVersion = 0x0100; -+ wdi->caps.in.wChannels = v->channels == 1 ? 1 : 2; -+ wdi->caps.in.dwFormats = PULSE_ALL_FORMATS; -+ memset(&wdi->ds_desc, 0, sizeof(DSDRIVERDESC)); -+ memcpy(wdi->ds_desc.szDesc, description, min(sizeof(wdi->ds_desc.szDesc) - 1, strlen(description))); -+ memcpy(wdi->ds_desc.szDrvname, "winepulse.drv", 14); -+ wdi->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN; -+ wdi->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX; -+ wdi->ds_caps.dwPrimaryBuffers = 1; -+ wdi->ds_caps.dwFlags = \ -+ DSCAPS_PRIMARYMONO | -+ DSCAPS_PRIMARYSTEREO | -+ DSCAPS_PRIMARY8BIT | -+ DSCAPS_PRIMARY16BIT | -+ DSCAPS_SECONDARYMONO | -+ DSCAPS_SECONDARYSTEREO | -+ DSCAPS_SECONDARY8BIT | -+ DSCAPS_SECONDARY16BIT | -+ DSCCAPS_MULTIPLECAPTURE; -+} -+ -+/************************************************************************** -+ * PULSE_AllocateWaveoutDevice [internal] -+ * -+ * Creates or adds a sink to the WOutDev array. -+ */ -+static void PULSE_AllocateWaveoutDevice(const char *name, const char *device, const char *description, const pa_cvolume *v) { -+ WINE_WAVEDEV *wdo; -+ int x; -+ -+ if (WOutDev) -+ wdo = HeapReAlloc(GetProcessHeap(), 0, WOutDev, sizeof(WINE_WAVEDEV) * (PULSE_WodNumDevs + 1)); -+ else -+ wdo = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV)); -+ -+ if (!wdo) return; -+ -+ WOutDev = wdo; -+ wdo = &WOutDev[PULSE_WodNumDevs++]; -+ memset(wdo, 0, sizeof(WINE_WAVEDEV)); -+ -+ wdo->device_name = pa_xstrdup(device); -+ wdo->volume.channels = v->channels; -+ for (x = 0; x < v->channels; x++) wdo->volume.values[x] = v->values[x]; -+ snprintf(wdo->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name); -+ MultiByteToWideChar(CP_UTF8, 0, description, -1, wdo->caps.out.szPname, sizeof(wdo->caps.out.szPname)/sizeof(WCHAR)); -+ wdo->caps.out.szPname[sizeof(wdo->caps.out.szPname)/sizeof(WCHAR) - 1] = '\0'; -+ wdo->caps.out.wMid = MM_CREATIVE; -+ wdo->caps.out.wPid = MM_CREATIVE_SBP16_WAVEOUT; -+ wdo->caps.out.vDriverVersion = 0x0100; -+ wdo->caps.out.dwSupport = WAVECAPS_VOLUME | WAVECAPS_SAMPLEACCURATE;// | WAVECAPS_DIRECTSOUND; -+ if (v->channels >= 2) { -+ wdo->caps.out.wChannels = 2; -+ wdo->caps.out.dwSupport |= WAVECAPS_LRVOLUME; -+ } else -+ wdo->caps.out.wChannels = 1; -+ wdo->caps.out.dwFormats = PULSE_ALL_FORMATS; -+ memset(&wdo->ds_desc, 0, sizeof(DSDRIVERDESC)); -+ memcpy(wdo->ds_desc.szDesc, description, min(sizeof(wdo->ds_desc.szDesc) - 1, strlen(description))); -+ memcpy(wdo->ds_desc.szDrvname, "winepulse.drv", 14); -+ wdo->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN; -+ wdo->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX; -+ wdo->ds_caps.dwPrimaryBuffers = 1; -+ wdo->ds_caps.dwFlags = \ -+ DSCAPS_PRIMARYMONO | -+ DSCAPS_PRIMARYSTEREO | -+ DSCAPS_PRIMARY8BIT | -+ DSCAPS_PRIMARY16BIT | -+ DSCAPS_SECONDARYMONO | -+ DSCAPS_SECONDARYSTEREO | -+ DSCAPS_SECONDARY8BIT | -+ DSCAPS_SECONDARY16BIT | -+ DSCAPS_CONTINUOUSRATE; -+} -+ -+/************************************************************************** -+ * PULSE_SourceInfoCallback [internal] -+ */ -+static void PULSE_SourceInfoCallback(pa_context *c, const pa_source_info *i, int eol, void *userdata) { -+ -+ if (!eol && i) -+ PULSE_AllocateWaveinDevice(i->name, i->name, i->description, &i->volume); -+ -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * PULSE_SinkInfoCallback [internal] -+ */ -+static void PULSE_SinkInfoCallback(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { -+ -+ if (!eol && i) -+ PULSE_AllocateWaveoutDevice(i->name, i->name, i->description, &i->volume); -+ -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * PULSE_ContextNotifyCallback [internal] -+ */ -+static void PULSE_ContextNotifyCallback(pa_context *c, void *userdata) { -+ PULSE_MainloopSignal(); -+} -+ -+/************************************************************************** -+ * PULSE_WaveClose [internal] -+ * -+ * Disconnect from the server, deallocated the WaveIn/WaveOut devices, stop and -+ * free the mainloop. -+ */ -+static LONG PULSE_WaveClose(void) { -+ int x; -+ TRACE("()\n"); -+ if (!PULSE_ml) return DRV_FAILURE; -+ -+ PULSE_MainloopLock(); -+ /* device_name is allocated with pa_xstrdup, free with pa_xfree */ -+ for (x = 0; x < PULSE_WodNumDevs; x++) pa_xfree(WOutDev[x].device_name); -+ for (x = 0; x < PULSE_WidNumDevs; x++) pa_xfree(WInDev[x].device_name); -+ HeapFree(GetProcessHeap(), 0, WOutDev); -+ HeapFree(GetProcessHeap(), 0, WInDev); -+ if (PULSE_context) { -+ PULSE_WaitForOperation(pa_context_drain(PULSE_context, PULSE_ContextNotifyCallback, NULL)); -+ pa_context_disconnect(PULSE_context); -+ pa_context_unref(PULSE_context); -+ PULSE_context = NULL; -+ } -+ -+ PULSE_MainloopUnlock(); -+ PULSE_MainloopStop(); -+ pa_mainloop_free(PULSE_ml); -+ PULSE_ml = NULL; -+ -+ return DRV_SUCCESS; -+} -+ -+/************************************************************************** -+ * PULSE_WaveInit [internal] -+ * -+ * Connects to the pulseaudio server, tries to discover sinks and sources and -+ * allocates the WaveIn/WaveOut devices. -+ */ -+static LONG PULSE_WaveInit(void) { -+ char *app_name; -+ char path[PATH_MAX]; -+ char *offset = NULL; -+ pa_cvolume fake_cvolume; -+ -+ WOutDev = NULL; -+ WInDev = NULL; -+ PULSE_WodNumDevs = 0; -+ PULSE_WidNumDevs = 0; -+ PULSE_context = NULL; -+ PULSE_ml = NULL; -+ -+ if (!(PULSE_ml = pa_mainloop_new())) { -+ ERR("Failed to create mainloop object."); -+ return DRV_FAILURE; -+ } -+ -+ /* Application name giving to pulse should be unique to the binary so that -+ * pulse-*-restore can be useful */ -+ -+ /* Get binary path, and remove path a-la strrchr */ -+ if (GetModuleFileNameA(NULL, path, PATH_MAX)) -+ offset = strrchr(path, '\\'); -+ -+ if (offset && ++offset && offset < path + PATH_MAX) { -+ app_name = pa_xmalloc(strlen(offset) + 8); -+ snprintf(app_name, strlen(offset) + 8, "WINE [%s]", offset); -+ } else -+ app_name = pa_xstrdup("WINE Application"); -+ -+ TRACE("App name is \"%s\"\n", app_name); -+ -+ PULSE_MainloopStart(); -+ PULSE_context = pa_context_new(pa_mainloop_get_api(PULSE_ml), app_name); -+ assert(PULSE_context); -+ pa_xfree(app_name); -+ -+ pa_context_set_state_callback(PULSE_context, PULSE_ContextStateCallback, NULL); -+ -+ PULSE_MainloopLock(); -+ -+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(PULSE_context), PA_API_VERSION); -+ if (pa_context_connect(PULSE_context, NULL, 0, NULL) < 0) -+ goto fail; -+ -+ /* Wait for connection */ -+ for (;;) { -+ pa_context_state_t state = pa_context_get_state(PULSE_context); -+ -+ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) -+ goto fail; -+ -+ if (state == PA_CONTEXT_READY) -+ break; -+ -+ PULSE_MainloopWait(); -+ } -+ -+ TRACE("Connected to server %s with protocol version: %i.\n", -+ pa_context_get_server(PULSE_context), -+ pa_context_get_server_protocol_version(PULSE_context)); -+ -+ fake_cvolume.channels = 2; -+ pa_cvolume_reset(&fake_cvolume, 2); -+ /* FIXME Translations? */ -+ PULSE_AllocateWaveoutDevice("default", NULL, "Default", &fake_cvolume); -+ PULSE_AllocateWaveinDevice("default", NULL, "Default", &fake_cvolume); -+ PULSE_WaitForOperation(pa_context_get_sink_info_list(PULSE_context, PULSE_SinkInfoCallback, &PULSE_WodNumDevs)); -+ PULSE_WaitForOperation(pa_context_get_source_info_list(PULSE_context, PULSE_SourceInfoCallback, &PULSE_WidNumDevs)); -+ TRACE("Found %u output and %u input device(s).\n", PULSE_WodNumDevs - 1, PULSE_WidNumDevs - 1); -+ -+ PULSE_MainloopUnlock(); -+ -+ return DRV_SUCCESS; -+ -+fail: -+ PULSE_MainloopUnlock(); -+ /* Only warn, because if we failed wine may still choose the next driver */ -+ WARN("Failed to connect to server\n"); -+ return DRV_FAILURE; -+} -+ -+#endif /* HAVE_PULSEAUDIO */ -+ -+/************************************************************************** -+ * DriverProc (WINEPULSE.@) -+ */ -+LRESULT CALLBACK PULSE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, -+ LPARAM dwParam1, LPARAM dwParam2) { -+ -+ switch(wMsg) { -+#ifdef HAVE_PULSEAUDIO -+ case DRV_LOAD: return PULSE_WaveInit(); -+ case DRV_FREE: return PULSE_WaveClose(); -+ case DRV_OPEN: return 1; -+ case DRV_CLOSE: return 1; -+ case DRV_ENABLE: return 1; -+ case DRV_DISABLE: return 1; -+ case DRV_QUERYCONFIGURE: return 1; -+ case DRV_CONFIGURE: MessageBoxA(0, "PulseAudio MultiMedia Driver !", "PulseAudio Driver", MB_OK); return 1; -+ case DRV_INSTALL: return DRVCNF_RESTART; -+ case DRV_REMOVE: return DRVCNF_RESTART; -+#endif -+ default: -+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); -+ } -+} -diff --git a/dlls/winepulse.drv/wavein.c b/dlls/winepulse.drv/wavein.c -new file mode 100644 -index 0000000..82bff06 ---- /dev/null -+++ b/dlls/winepulse.drv/wavein.c -@@ -0,0 +1,589 @@ -+/* -+ * Wine Driver for PulseAudio - WaveIn Functionality -+ * http://pulseaudio.org/ -+ * -+ * Copyright 2009 Arthur Taylor -+ * -+ * Contains code from other wine multimedia drivers. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+ -+#include -+ -+#include "windef.h" -+#include "winbase.h" -+#include "wingdi.h" -+#include "winuser.h" -+#include "winnls.h" -+#include "mmddk.h" -+ -+#include -+ -+#include "wine/debug.h" -+ -+WINE_DEFAULT_DEBUG_CHANNEL(wave); -+ -+#if HAVE_PULSEAUDIO -+ -+/*======================================================================* -+ * WAVE IN specific PulseAudio Callbacks * -+ *======================================================================*/ -+ -+/************************************************************************** -+ * widNotifyClient [internal] -+*/ -+static DWORD widNotifyClient(WINE_WAVEINST* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2) { -+ TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2); -+ -+ switch (wMsg) { -+ case WIM_OPEN: -+ case WIM_CLOSE: -+ case WIM_DATA: -+ if (wwi->wFlags != DCB_NULL && -+ !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave, -+ wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) { -+ WARN("can't notify client !\n"); -+ return MMSYSERR_ERROR; -+ } -+ break; -+ default: -+ FIXME("Unknown callback message %u\n", wMsg); -+ return MMSYSERR_INVALPARAM; -+ } -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widRecorder_CopyData [internal] -+ * -+ * Copys data from the fragments pulse returns to queued buffers. -+ */ -+static void widRecorder_CopyData(WINE_WAVEINST *wwi) { -+ LPWAVEHDR lpWaveHdr = wwi->lpQueuePtr; -+ size_t bytes_avail; -+ -+ /* Get this value once and trust it. Note that the total available is made -+ * of one _or more_ fragments. These fragments will probably not align with -+ * the wavehdr buffer sizes. */ -+ PULSE_MainloopLock(); -+ bytes_avail = pa_stream_readable_size(wwi->stream); -+ PULSE_MainloopUnlock(); -+ -+ if (bytes_avail == -1) { -+ ERR("pa_stream_readable_size() returned -1, record stream has failed.\n"); -+ return; -+ } -+ -+ /* If there is an already peeked buffer, add it to the total */ -+ if (wwi->buffer) -+ bytes_avail += wwi->buffer_length - wwi->buffer_read_offset; -+ -+ for (;bytes_avail && lpWaveHdr; lpWaveHdr = wwi->lpQueuePtr) { -+ size_t peek_avail; -+ -+ if (!wwi->buffer) { -+ PULSE_MainloopLock(); -+ pa_stream_peek(wwi->stream, &wwi->buffer, &wwi->buffer_length); -+ PULSE_MainloopUnlock(); -+ wwi->buffer_read_offset = 0; -+ -+ if (!wwi->buffer || !wwi->buffer_length) { -+ WARN("pa_stream_peek failed\n"); -+ break; -+ } -+ } -+ -+ peek_avail = min(wwi->buffer_length - wwi->buffer_read_offset, -+ lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded); -+ -+ memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, -+ (PBYTE)wwi->buffer + wwi->buffer_read_offset, -+ peek_avail); -+ -+ wwi->buffer_read_offset += peek_avail; -+ lpWaveHdr->dwBytesRecorded += peek_avail; -+ bytes_avail -= peek_avail; -+ -+ if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) { -+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; -+ lpWaveHdr->dwFlags |= WHDR_DONE; -+ wwi->lpQueuePtr = lpWaveHdr->lpNext; -+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); -+ } -+ -+ if (wwi->buffer_read_offset == wwi->buffer_length) { -+ PULSE_MainloopLock(); -+ pa_stream_drop(wwi->stream); -+ wwi->buffer = NULL; -+ PULSE_MainloopUnlock(); -+ } -+ } /* for(bytes_avail && lpWaveHdr) */ -+ -+ return; -+} -+ -+static void widRecorder_ProcessMessages(WINE_WAVEINST* wwi) { -+ LPWAVEHDR lpWaveHdr; -+ enum win_wm_message msg; -+ DWORD param; -+ HANDLE ev; -+ -+ -+ while (PULSE_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) { -+ TRACE("Received %s %x\n", PULSE_getCmdString(msg), param); -+ -+ switch (msg) { -+ case WINE_WM_FEED: -+ /* Spin the loop in widRecorder */ -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_STARTING: -+ wwi->dwLastReset = wwi->timing_info->read_index; -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 0, PULSE_StreamSuccessCallback, NULL)); -+ PULSE_MainloopUnlock(); -+ wwi->state = WINE_WS_PLAYING; -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_HEADER: -+ lpWaveHdr = (LPWAVEHDR)param; -+ lpWaveHdr->lpNext = 0; -+ /* insert buffer at the end of queue */ -+ { -+ LPWAVEHDR *wh; -+ for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); -+ *wh = lpWaveHdr; -+ } -+ break; -+ -+ case WINE_WM_STOPPING: -+ if (wwi->state != WINE_WS_STOPPED) { -+ wwi->state = WINE_WS_STOPPED; -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL)); -+ if (wwi->buffer) { -+ pa_stream_drop(wwi->stream); -+ wwi->buffer = NULL; -+ } -+ PULSE_MainloopUnlock(); -+ -+ /* return only the current buffer to app */ -+ if ((lpWaveHdr = wwi->lpQueuePtr)) { -+ LPWAVEHDR lpNext = lpWaveHdr->lpNext; -+ TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); -+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; -+ lpWaveHdr->dwFlags |= WHDR_DONE; -+ wwi->lpQueuePtr = lpNext; -+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); -+ } -+ } -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_RESETTING: -+ if (wwi->state != WINE_WS_STOPPED) { -+ wwi->state = WINE_WS_STOPPED; -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_StreamSuccessCallback, NULL)); -+ if (wwi->buffer) { -+ pa_stream_drop(wwi->stream); -+ wwi->buffer = NULL; -+ } -+ PULSE_MainloopUnlock(); -+ } -+ -+ /* return all the buffers to the app */ -+ lpWaveHdr = wwi->lpPlayPtr ? wwi->lpPlayPtr : wwi->lpQueuePtr; -+ for (; lpWaveHdr; lpWaveHdr = wwi->lpQueuePtr) { -+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; -+ lpWaveHdr->dwFlags |= WHDR_DONE; -+ wwi->lpQueuePtr = lpWaveHdr->lpNext; -+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); -+ } -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_CLOSING: -+ wwi->hThread = 0; -+ wwi->state = WINE_WS_CLOSED; -+ SetEvent(ev); -+ ExitThread(0); -+ /* shouldn't go here */ -+ -+ default: -+ FIXME("unknown message %d\n", msg); -+ break; -+ } -+ } -+} -+ -+/************************************************************************** -+ * widRecorder [internal] -+ */ -+static DWORD CALLBACK widRecorder(LPVOID lpParam) { -+ WINE_WAVEINST *wwi = (WINE_WAVEINST*)lpParam; -+ -+ wwi->state = WINE_WS_STOPPED; -+ SetEvent(wwi->hStartUpEvent); -+ -+ for (;;) { -+ PULSE_WaitRingMessage(&wwi->msgRing, INFINITE); -+ widRecorder_ProcessMessages(wwi); -+ if (wwi->state == WINE_WS_PLAYING && wwi->lpQueuePtr) -+ widRecorder_CopyData(wwi); -+ } -+ -+ return 0; -+} -+ -+/************************************************************************** -+ * widOpen [internal] -+ */ -+static DWORD widOpen(WORD wDevID, DWORD_PTR *lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) { -+ WINE_WAVEDEV *wdi; -+ WINE_WAVEINST *wwi = NULL; -+ DWORD ret = MMSYSERR_NOERROR; -+ -+ TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags); -+ if (lpDesc == NULL) { -+ WARN("Invalid Parameter !\n"); -+ return MMSYSERR_INVALPARAM; -+ } -+ -+ if (wDevID >= PULSE_WidNumDevs) { -+ TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WidNumDevs); -+ return MMSYSERR_BADDEVICEID; -+ } -+ wdi = &WInDev[wDevID]; -+ -+ wwi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_WAVEINST)); -+ if (!wwi) return MMSYSERR_NOMEM; -+ *lpdwUser = (DWORD_PTR)wwi; -+ -+ /* check to see if format is supported and make pa_sample_spec struct */ -+ if (!PULSE_SetupFormat(lpDesc->lpFormat, &wwi->sample_spec)) { -+ WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", -+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, -+ lpDesc->lpFormat->nSamplesPerSec); -+ ret = WAVERR_BADFORMAT; -+ goto exit; -+ } -+ -+ if (TRACE_ON(wave)) { -+ char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; -+ pa_sample_spec_snprint(t, sizeof(t), &wwi->sample_spec); -+ TRACE("Sample spec '%s'\n", t); -+ } -+ -+ if (dwFlags & WAVE_FORMAT_QUERY) { -+ TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", -+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, -+ lpDesc->lpFormat->nSamplesPerSec); -+ ret = MMSYSERR_NOERROR; -+ goto exit; -+ } -+ -+ wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); -+ wwi->waveDesc = *lpDesc; -+ PULSE_InitRingMessage(&wwi->msgRing); -+ -+ wwi->stream = pa_stream_new(PULSE_context, "WaveIn", &wwi->sample_spec, NULL); -+ if (!wwi->stream) { -+ ret = WAVERR_BADFORMAT; -+ goto exit; -+ } -+ -+ pa_stream_set_state_callback(wwi->stream, PULSE_StreamStateCallback, wwi); -+ pa_stream_set_read_callback (wwi->stream, PULSE_StreamRequestCallback, wwi); -+ -+ wwi->buffer_attr.maxlength = (uint32_t)-1; -+ wwi->buffer_attr.fragsize = pa_bytes_per_second(&wwi->sample_spec) / 100; -+ -+ PULSE_MainloopLock(); -+ TRACE("Asking to open %s for recording.\n", wdi->device_name); -+ pa_stream_connect_record(wwi->stream, wdi->device_name, &wwi->buffer_attr, -+ PA_STREAM_START_CORKED | -+ PA_STREAM_AUTO_TIMING_UPDATE | -+ PA_STREAM_ADJUST_LATENCY); -+ -+ for (;;) { -+ pa_context_state_t cstate = pa_context_get_state(PULSE_context); -+ pa_stream_state_t sstate = pa_stream_get_state(wwi->stream); -+ -+ if (cstate == PA_CONTEXT_FAILED || cstate == PA_CONTEXT_TERMINATED || -+ sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED) { -+ ERR("Failed to connect context object: %s\n", pa_strerror(pa_context_errno(PULSE_context))); -+ ret = MMSYSERR_NODRIVER; -+ PULSE_MainloopUnlock(); -+ goto exit; -+ } -+ -+ if (sstate == PA_STREAM_READY) -+ break; -+ -+ PULSE_MainloopWait(); -+ } -+ TRACE("(%p)->stream connected for recording.\n", wwi); -+ -+ PULSE_WaitForOperation(pa_stream_update_timing_info(wwi->stream, PULSE_StreamSuccessCallback, wwi)); -+ -+ wwi->timing_info = pa_stream_get_timing_info(wwi->stream); -+ assert(wwi->timing_info); -+ PULSE_MainloopUnlock(); -+ -+ wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); -+ wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)wwi, 0, &(wwi->dwThreadID)); -+ if (wwi->hThread) -+ SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL); -+ else { -+ ERR("Thread creation for the widRecorder failed!\n"); -+ ret = MMSYSERR_NOMEM; -+ goto exit; -+ } -+ WaitForSingleObject(wwi->hStartUpEvent, INFINITE); -+ CloseHandle(wwi->hStartUpEvent); -+ wwi->hStartUpEvent = INVALID_HANDLE_VALUE; -+ -+ return widNotifyClient(wwi, WIM_OPEN, 0L, 0L); -+ -+exit: -+ if (!wwi) -+ return ret; -+ -+ if (wwi->hStartUpEvent != INVALID_HANDLE_VALUE) -+ CloseHandle(wwi->hStartUpEvent); -+ -+ if (wwi->msgRing.ring_buffer_size > 0) -+ PULSE_DestroyRingMessage(&wwi->msgRing); -+ -+ if (wwi->stream) { -+ if (pa_stream_get_state(wwi->stream) == PA_STREAM_READY) -+ pa_stream_disconnect(wwi->stream); -+ pa_stream_unref(wwi->stream); -+ } -+ HeapFree(GetProcessHeap(), 0, wwi); -+ -+ return ret; -+} -+/************************************************************************** -+ * widClose [internal] -+ */ -+static DWORD widClose(WORD wDevID, WINE_WAVEINST *wwi) { -+ DWORD ret; -+ -+ TRACE("(%u, %p);\n", wDevID, wwi); -+ if (wDevID >= PULSE_WidNumDevs) { -+ WARN("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs); -+ return MMSYSERR_INVALHANDLE; -+ } else if (!wwi) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (wwi->state != WINE_WS_FAILED) { -+ if (wwi->lpQueuePtr) { -+ WARN("buffers recording recording !\n"); -+ return WAVERR_STILLPLAYING; -+ } -+ -+ PULSE_MainloopLock(); -+ if (pa_stream_get_state(wwi->stream) == PA_STREAM_READY) -+ pa_stream_drop(wwi->stream); -+ pa_stream_disconnect(wwi->stream); -+ PULSE_MainloopUnlock(); -+ -+ if (wwi->hThread != INVALID_HANDLE_VALUE) -+ PULSE_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE); -+ -+ PULSE_DestroyRingMessage(&wwi->msgRing); -+ } -+ ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L); -+ -+ pa_stream_unref(wwi->stream); -+ TRACE("Deallocating record instance.\n"); -+ HeapFree(GetProcessHeap(), 0, wwi); -+ return ret; -+} -+ -+/************************************************************************** -+ * widAddBuffer [internal] -+ * -+ */ -+static DWORD widAddBuffer(WINE_WAVEINST* wwi, LPWAVEHDR lpWaveHdr, DWORD dwSize) { -+ TRACE("(%p, %p, %08X);\n", wwi, lpWaveHdr, dwSize); -+ -+ if (!wwi || wwi->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) -+ return WAVERR_UNPREPARED; -+ -+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) -+ return WAVERR_STILLPLAYING; -+ -+ lpWaveHdr->dwFlags &= ~WHDR_DONE; -+ lpWaveHdr->dwFlags |= WHDR_INQUEUE; -+ lpWaveHdr->dwBytesRecorded = 0; -+ lpWaveHdr->lpNext = 0; -+ -+ PULSE_AddRingMessage(&wwi->msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE); -+ -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widRecorderMessage [internal] -+ */ -+static DWORD widRecorderMessage(WINE_WAVEINST *wwi, enum win_wm_message message) { -+ if (!wwi || wwi->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ PULSE_AddRingMessage(&wwi->msgRing, message, 0, TRUE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widGetPosition [internal] -+ */ -+static DWORD widGetPosition(WINE_WAVEINST *wwi, LPMMTIME lpTime, DWORD uSize) { -+ -+ if (!wwi || wwi->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (lpTime == NULL) return MMSYSERR_INVALPARAM; -+ -+ return PULSE_UsecToMMTime(pa_bytes_to_usec(wwi->timing_info->read_index - wwi->dwLastReset, &wwi->sample_spec), lpTime, &wwi->sample_spec); -+} -+ -+/************************************************************************** -+ * widGetDevCaps [internal] -+ */ -+static DWORD widGetDevCaps(DWORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize) { -+ TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize); -+ -+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED; -+ -+ if (wDevID >= PULSE_WidNumDevs) { -+ TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WidNumDevs); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ memcpy(lpCaps, &(WInDev[wDevID].caps.in), min(dwSize, sizeof(*lpCaps))); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widGetNumDevs [internal] -+ * Context-sanity check here, as if we respond with 0, WINE will move on -+ * to the next wavein driver. -+ */ -+static DWORD widGetNumDevs(void) { -+ if (pa_context_get_state(PULSE_context) != PA_CONTEXT_READY) -+ return 0; -+ -+ return PULSE_WidNumDevs; -+} -+ -+/************************************************************************** -+ * widDevInterfaceSize [internal] -+ */ -+static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) { -+ TRACE("(%u, %p)\n", wDevID, dwParam1); -+ -+ *dwParam1 = MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1, -+ NULL, 0 ) * sizeof(WCHAR); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widDevInterface [internal] -+ */ -+static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) { -+ if (dwParam2 >= MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1, -+ NULL, 0 ) * sizeof(WCHAR)) -+ { -+ MultiByteToWideChar(CP_UTF8, 0, WInDev[wDevID].interface_name, -1, -+ dwParam1, dwParam2 / sizeof(WCHAR)); -+ return MMSYSERR_NOERROR; -+ } -+ return MMSYSERR_INVALPARAM; -+} -+ -+/************************************************************************** -+ * widDsDesc [internal] -+ */ -+DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc) -+{ -+ *desc = WInDev[wDevID].ds_desc; -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * widMessage (WINEPULSE.@) -+ */ -+DWORD WINAPI PULSE_widMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, -+ DWORD_PTR dwParam1, DWORD_PTR dwParam2) { -+ -+ switch (wMsg) { -+ case DRVM_INIT: -+ case DRVM_EXIT: -+ case DRVM_ENABLE: -+ case DRVM_DISABLE: -+ /* FIXME: Pretend this is supported */ -+ return 0; -+ case WIDM_OPEN: return widOpen (wDevID, (DWORD_PTR*)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2); -+ case WIDM_CLOSE: return widClose (wDevID, (WINE_WAVEINST*)dwUser); -+ case WIDM_ADDBUFFER: return widAddBuffer ((WINE_WAVEINST*)dwUser, (LPWAVEHDR)dwParam1, dwParam2); -+ case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED; -+ case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED; -+ case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2); -+ case WIDM_GETNUMDEVS: return widGetNumDevs(); -+ case WIDM_GETPOS: return widGetPosition ((WINE_WAVEINST*)dwUser, (LPMMTIME)dwParam1, dwParam2); -+ case WIDM_RESET: return widRecorderMessage((WINE_WAVEINST*)dwUser, WINE_WM_RESETTING); -+ case WIDM_START: return widRecorderMessage((WINE_WAVEINST*)dwUser, WINE_WM_STARTING); -+ case WIDM_STOP: return widRecorderMessage((WINE_WAVEINST*)dwUser, WINE_WM_STOPPING); -+ case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize(wDevID, (LPDWORD)dwParam1); -+ case DRV_QUERYDEVICEINTERFACE: return widDevInterface(wDevID, (PWCHAR)dwParam1, dwParam2); -+ case DRV_QUERYDSOUNDIFACE: return MMSYSERR_NOTSUPPORTED; /* Use emulation, as there is no advantage */ -+ case DRV_QUERYDSOUNDDESC: return widDsDesc(wDevID, (PDSDRIVERDESC)dwParam1); -+ default: -+ FIXME("unknown message %d!\n", wMsg); -+ } -+ return MMSYSERR_NOTSUPPORTED; -+} -+ -+#else /* HAVE_PULSEAUDIO */ -+ -+/************************************************************************** -+ * widMessage (WINEPULSE.@) -+ */ -+DWORD WINAPI PULSE_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, -+ DWORD dwParam1, DWORD dwParam2) { -+ FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); -+ return MMSYSERR_NOTENABLED; -+} -+ -+#endif /* HAVE_PULSEAUDIO */ -diff --git a/dlls/winepulse.drv/waveout.c b/dlls/winepulse.drv/waveout.c -new file mode 100644 -index 0000000..3f52c83 ---- /dev/null -+++ b/dlls/winepulse.drv/waveout.c -@@ -0,0 +1,1029 @@ -+/* -+ * Wine Driver for PulseAudio - WaveOut Functionality -+ * http://pulseaudio.org/ -+ * -+ * Copyright 2009 Arthur Taylor -+ * -+ * Contains code from other wine multimedia drivers. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+ -+#include -+ -+#include "windef.h" -+#include "winbase.h" -+#include "wingdi.h" -+#include "winuser.h" -+#include "winnls.h" -+#include "winerror.h" -+#include "mmddk.h" -+ -+#include -+ -+#include "wine/debug.h" -+ -+WINE_DEFAULT_DEBUG_CHANNEL(wave); -+ -+#if HAVE_PULSEAUDIO -+ -+/* Use this to make the infinite wait not so infinite */ -+#define PULSE_INFINITE 10000 -+ -+/* state diagram for waveOut writing: -+ * -+ * +---------+-------------+---------------+---------------------------------+ -+ * | state | function | event | new state | -+ * +---------+-------------+---------------+---------------------------------+ -+ * | | open() | | STOPPED | -+ * | PAUSED | write() | | PAUSED | -+ * | STOPPED | write() | | PLAYING | -+ * | PLAYING | write() | HEADER | PLAYING | -+ * | (other) | write() | | | -+ * | (any) | pause() | PAUSING | PAUSED | -+ * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) | -+ * | PAUSED | reset() | RESETTING | PAUSED | -+ * | (other) | reset() | RESETTING | STOPPED | -+ * | (any) | close() | CLOSING | CLOSED | -+ * +---------+-------------+---------------+---------------------------------+ -+ */ -+ -+/* -+ * - It is currently unknown if pausing in a loop works the same as expected. -+ */ -+ -+/*======================================================================* -+ * WAVE OUT specific PulseAudio Callbacks * -+ *======================================================================*/ -+ -+/************************************************************************** -+ * WAVEOUT_SinkInputInfoCallback [internal] -+ * -+ * Called by the pulse thread. Used for wodGetVolume. -+ */ -+static void WAVEOUT_SinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { -+ WINE_WAVEINST* wwo = (WINE_WAVEINST*)userdata; -+ if (!eol && i) { -+ for (wwo->volume.channels = 0; wwo->volume.channels != i->volume.channels; wwo->volume.channels++) -+ wwo->volume.values[wwo->volume.channels] = i->volume.values[wwo->volume.channels]; -+ PULSE_MainloopSignal(); -+ } -+} -+ -+/*======================================================================* -+ * "Low level" WAVE OUT implementation * -+ *======================================================================*/ -+ -+/************************************************************************** -+ * wodPlayer_NotifyClient [internal] -+ */ -+static DWORD wodPlayer_NotifyClient(WINE_WAVEINST* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2) { -+ /* TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2); */ -+ -+ switch (wMsg) { -+ case WOM_OPEN: -+ case WOM_CLOSE: -+ case WOM_DONE: -+ if (wwo->wFlags != DCB_NULL && -+ !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave, -+ wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) { -+ WARN("can't notify client !\n"); -+ return MMSYSERR_ERROR; -+ } -+ break; -+ default: -+ FIXME("Unknown callback message %u\n", wMsg); -+ return MMSYSERR_INVALPARAM; -+ } -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodPlayer_BeginWaveHdr [internal] -+ * -+ * Makes the specified lpWaveHdr the currently playing wave header. -+ * If the specified wave header is a begin loop and we're not already in -+ * a loop, setup the loop. -+ */ -+static void wodPlayer_BeginWaveHdr(WINE_WAVEINST* wwo, LPWAVEHDR lpWaveHdr) { -+ wwo->lpPlayPtr = lpWaveHdr; -+ -+ if (!lpWaveHdr) return; -+ -+ if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) { -+ if (wwo->lpLoopPtr) { -+ WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); -+ } else { -+ TRACE("Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr); -+ wwo->lpLoopPtr = lpWaveHdr; -+ /* Windows does not touch WAVEHDR.dwLoops, -+ * so we need to make an internal copy */ -+ wwo->dwLoops = lpWaveHdr->dwLoops; -+ } -+ } -+ wwo->dwPartialOffset = 0; -+} -+ -+/************************************************************************** -+ * wodPlayer_PlayPtrNext [internal] -+ * -+ * Advance the play pointer to the next waveheader, looping if required. -+ */ -+static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEINST* wwo) { -+ LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr; -+ -+ wwo->dwPartialOffset = 0; -+ if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) { -+ /* We're at the end of a loop, loop if required */ -+ if (--wwo->dwLoops > 0) { -+ wwo->lpPlayPtr = wwo->lpLoopPtr; -+ } else { -+ /* Handle overlapping loops correctly */ -+ if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) { -+ FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n"); -+ /* shall we consider the END flag for the closing loop or for -+ * the opening one or for both ??? -+ * code assumes for closing loop only -+ */ -+ } else { -+ lpWaveHdr = lpWaveHdr->lpNext; -+ } -+ wwo->lpLoopPtr = NULL; -+ wodPlayer_BeginWaveHdr(wwo, lpWaveHdr); -+ } -+ } else { -+ /* We're not in a loop. Advance to the next wave header */ -+ wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext); -+ } -+ -+ return lpWaveHdr; -+} -+ -+/************************************************************************** -+ * wodPlayer_CheckReleasing [internal] -+ * -+ * Check to make sure that playback has not stalled. If stalled ask to reduce -+ * the size of the buffer on the pulse server side. -+ */ -+static void wodPlayer_CheckReleasing(WINE_WAVEINST *wwo) { -+ LPWAVEHDR lpWaveHdr; -+ -+ if (wwo->buffer_attr.tlength == -1) { -+ PULSE_MainloopLock(); -+ if (!wwo->timing_info->playing) { -+ -+ /* Calculate how large a buffer the application has made so far */ -+ wwo->buffer_attr.tlength = 0; -+ wwo->buffer_attr.minreq = wwo->lpQueuePtr->dwBufferLength; -+ for (lpWaveHdr = wwo->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) -+ wwo->buffer_attr.tlength += lpWaveHdr->dwBufferLength; -+ -+ WARN("Asking for new buffer target length of %llums (%u bytes)\n", -+ pa_bytes_to_usec(wwo->buffer_attr.tlength, &wwo->sample_spec) / 1000, -+ wwo->buffer_attr.tlength); -+ -+ /* Try and adjust the buffer attributes so that playback can start. -+ * Because of bugs pa_stream_set_buffer_attr() does not work on started -+ * streams for server version 0.9.11 to 0.9.14 */ -+ PULSE_WaitForOperation(pa_stream_set_buffer_attr(wwo->stream, &wwo->buffer_attr, PULSE_StreamSuccessCallback, wwo)); -+ TRACE("Triggering stream and hoping for the best\n"); -+ PULSE_WaitForOperation(pa_stream_trigger(wwo->stream, PULSE_StreamSuccessCallback, wwo)); -+ } -+ PULSE_MainloopUnlock(); -+ } -+} -+ -+/************************************************************************** -+ * wodPlayer_NotifyCompletions [internal] -+ * -+ * Notifies the client of wavehdr completion starting from lpQueuePtr and -+ * stopping when hitting an unwritten wavehdr, the beginning of a loop or a -+ * wavehdr that has not been played, when referenced to the time parameter. -+ */ -+static DWORD wodPlayer_NotifyCompletions(WINE_WAVEINST* wwo, BOOL force, pa_usec_t time) { -+ LPWAVEHDR lpWaveHdr = wwo->lpQueuePtr; -+ pa_usec_t wait; -+ -+ while (lpWaveHdr) { -+ if (!force) { -+ /* Start from lpQueuePtr and keep notifying until: -+ * - we hit an unwritten wavehdr -+ * - we hit the beginning of a running loop -+ * - we hit a wavehdr which hasn't finished playing -+ */ -+ if (lpWaveHdr == wwo->lpLoopPtr) { TRACE("loop %p\n", lpWaveHdr); return PULSE_INFINITE; } -+ if (lpWaveHdr == wwo->lpPlayPtr) { TRACE("play %p\n", lpWaveHdr); return PULSE_INFINITE; } -+ -+ /* See if this data has been played, and if not, return when it will have been */ -+ wait = pa_bytes_to_usec(lpWaveHdr->reserved + lpWaveHdr->dwBufferLength, &wwo->sample_spec); -+ if (wait >= time) { -+ wait = ((wait - time) + (pa_usec_t)999) / (pa_usec_t)1000; -+ return wait ?: 1; -+ } -+ } -+ TRACE("Returning %p.[%i]\n", lpWaveHdr, (DWORD)lpWaveHdr->reserved); -+ -+ /* return the wavehdr */ -+ wwo->lpQueuePtr = lpWaveHdr->lpNext; -+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; -+ lpWaveHdr->dwFlags |= WHDR_DONE; -+ -+ wodPlayer_NotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0); -+ lpWaveHdr = wwo->lpQueuePtr; -+ } -+ /* No more wavehdrs */ -+ TRACE("Empty queue\n"); -+ return PULSE_INFINITE; -+} -+ -+/************************************************************************** -+ * wodPlayer_WriteMax [internal] -+ * -+ * Write either how much free space or how much data we have, depending on -+ * which is less -+ */ -+static DWORD wodPlayer_WriteMax(WINE_WAVEINST *wwo, size_t *space) { -+ LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr; -+ size_t nbytes; -+ -+ nbytes = min(lpWaveHdr->dwBufferLength - wwo->dwPartialOffset, *space); -+ -+ TRACE("Writing wavehdr %p.%u[%u]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength); -+ pa_stream_write(wwo->stream, lpWaveHdr->lpData + wwo->dwPartialOffset, nbytes, NULL, 0, PA_SEEK_RELATIVE); -+ -+ /* Check to see if we wrote all of the wavehdr */ -+ if ((wwo->dwPartialOffset += nbytes) >= lpWaveHdr->dwBufferLength) -+ wodPlayer_PlayPtrNext(wwo); -+ -+ *space -= nbytes; -+ -+ return nbytes; -+} -+ -+/************************************************************************** -+ * wodPlayer_Feed [internal] -+ * -+ * Feed as much sound data as we can into pulse using wodPlayer_WriteMax. -+ * size_t space _must_ have come from either pa_stream_writable_size() or -+ * the value from a stream write callback, as if it isn't you run the risk -+ * of a buffer overflow in which audio data will be lost. -+ */ -+static void wodPlayer_Feed(WINE_WAVEINST* wwo, size_t space) { -+ -+ if (!space || !wwo->stream || !PULSE_context || -+ pa_context_get_state(PULSE_context) != PA_CONTEXT_READY || -+ pa_stream_get_state(wwo->stream) != PA_STREAM_READY) -+ return; -+ -+ PULSE_MainloopLock(); -+ /* Feed from a partial wavehdr */ -+ if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) -+ wodPlayer_WriteMax(wwo, &space); -+ -+ /* Feed wavehdrs until we run out of wavehdrs or buffer space */ -+ if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) { -+ do { -+ wwo->lpPlayPtr->reserved = wwo->timing_info->write_index; -+ } while (wodPlayer_WriteMax(wwo, &space) && wwo->lpPlayPtr && space > 0); -+ } -+ -+ PULSE_MainloopUnlock(); -+} -+ -+/************************************************************************** -+ * wodPlayer_Reset [internal] -+ * -+ * wodPlayer helper. Resets current output stream. -+ */ -+static void wodPlayer_Reset(WINE_WAVEINST* wwo) { -+ enum win_wm_message msg; -+ DWORD param; -+ HANDLE ev; -+ -+ TRACE("(%p)\n", wwo); -+ -+ /* Remove any buffer */ -+ wodPlayer_NotifyCompletions(wwo, TRUE, 0); -+ -+ wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; -+ if (wwo->state != WINE_WS_PAUSED) -+ wwo->state = WINE_WS_STOPPED; -+ -+ wwo->dwPartialOffset = 0; -+ -+ if (!wwo->stream || -+ !PULSE_context || -+ pa_context_get_state(PULSE_context) != PA_CONTEXT_READY || -+ pa_stream_get_state(wwo->stream) != PA_STREAM_READY) { -+ return; -+ } -+ -+ PULSE_MainloopLock(); -+ -+ /* Flush the output buffer of written data*/ -+ PULSE_WaitForOperation(pa_stream_flush(wwo->stream, PULSE_StreamSuccessCallback, NULL)); -+ -+ /* Reset the written byte count as some data may have been flushed */ -+ if (wwo->timing_info->write_index_corrupt) -+ PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_StreamSuccessCallback, wwo)); -+ -+ wwo->dwLastReset = wwo->timing_info->write_index; -+ -+ /* Return all pending headers in queue */ -+ EnterCriticalSection(&wwo->msgRing.msg_crst); -+ while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { -+ if (msg != WINE_WM_HEADER) { -+ SetEvent(ev); -+ continue; -+ } -+ ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE; -+ ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE; -+ wodPlayer_NotifyClient(wwo, WOM_DONE, param, 0); -+ } -+ PULSE_ResetRingMessage(&wwo->msgRing); -+ LeaveCriticalSection(&wwo->msgRing.msg_crst); -+ -+ PULSE_MainloopUnlock(); -+} -+ -+/************************************************************************** -+ * wodPlayer_GetStreamTime [internal] -+ * -+ * Returns how many microseconds into the playback the audio stream is. Does -+ * not reset to 0 on Reset() calls. Better than pa_stream_get_time() as it is -+ * more constant. -+ */ -+static pa_usec_t wodPlayer_GetStreamTime(WINE_WAVEINST *wwo) { -+ pa_usec_t time, temp; -+ const pa_timing_info *t; -+ -+ t = wwo->timing_info; -+ -+ PULSE_MainloopLock(); -+ -+ time = pa_bytes_to_usec(t->read_index, &wwo->sample_spec); -+ if (t->read_index_corrupt) { -+ WARN("Read index corrupt?!\n"); -+ PULSE_MainloopUnlock(); -+ return time; -+ } -+ -+ if (t->playing) { -+ time += pa_timeval_age(&t->timestamp); -+ temp = t->transport_usec + t->configured_sink_usec; -+ if (temp > wwo->buffer_attr.tlength) temp = wwo->buffer_attr.tlength; -+ if (time > temp) time -= temp; else time = 0; -+ } -+ -+ /* Make sure we haven't claimed to have played more than we have written */ -+ temp = pa_bytes_to_usec(t->write_index, &wwo->sample_spec); -+ if (time > temp) time = temp; -+ -+ /* No queued buffer shows an underrun, so we lie */ -+ if (!wwo->lpQueuePtr) time = temp; -+ -+ PULSE_MainloopUnlock(); -+ -+ return time; -+} -+ -+/************************************************************************** -+ * wodPlayer_ProcessMessages [internal] -+ */ -+static void wodPlayer_ProcessMessages(WINE_WAVEINST* wwo) { -+ LPWAVEHDR lpWaveHdr; -+ enum win_wm_message msg; -+ DWORD param; -+ HANDLE ev; -+ -+ while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { -+ TRACE("Received %s %x\n", PULSE_getCmdString(msg), param); -+ -+ switch (msg) { -+ case WINE_WM_PAUSING: -+ wwo->state = WINE_WS_PAUSED; -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 1, PULSE_StreamSuccessCallback, wwo)); -+ PULSE_MainloopUnlock(); -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_RESTARTING: -+ if (wwo->state == WINE_WS_PAUSED) { -+ wwo->state = WINE_WS_PLAYING; -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 0, PULSE_StreamSuccessCallback, wwo)); -+ /* If the serverside buffer was near full before pausing, we -+ * need to have space to write soon, so force playback start */ -+ PULSE_WaitForOperation(pa_stream_trigger(wwo->stream, PULSE_StreamSuccessCallback, wwo)); -+ PULSE_MainloopUnlock(); -+ } -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_HEADER: -+ lpWaveHdr = (LPWAVEHDR)param; -+ /* insert buffer at the end of queue */ -+ { -+ LPWAVEHDR *wh; -+ for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); -+ *wh = lpWaveHdr; -+ } -+ -+ if (!wwo->lpPlayPtr) -+ wodPlayer_BeginWaveHdr(wwo,lpWaveHdr); -+ if (wwo->state == WINE_WS_STOPPED) -+ wwo->state = WINE_WS_PLAYING; -+ -+ wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream)); -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_RESETTING: -+ wodPlayer_Reset(wwo); -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_BREAKLOOP: -+ if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) -+ /* ensure exit at end of current loop */ -+ wwo->dwLoops = 1; -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_FEED: /* Sent by the pulse thread */ -+ wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream)); -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_XRUN: /* Sent by the pulse thread */ -+ WARN("Trying to recover from underrun.\n"); -+ /* Return all the queued wavehdrs, so the app will send more data */ -+ wodPlayer_NotifyCompletions(wwo, FALSE, (pa_usec_t)-1); -+ -+ SetEvent(ev); -+ break; -+ -+ case WINE_WM_CLOSING: -+ wwo->hThread = NULL; -+ wwo->state = WINE_WS_CLOSED; -+ /* sanity check: this should not happen since the device must have been reset before */ -+ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); -+ SetEvent(ev); -+ TRACE("Thread exiting.\n"); -+ ExitThread(0); -+ /* shouldn't go here */ -+ -+ default: -+ FIXME("unknown message %d\n", msg); -+ break; -+ } -+ } -+} -+ -+/************************************************************************** -+ * wodPlayer [internal] -+ * -+ * The thread which is responsible for returning WaveHdrs via DriverCallback, -+ * the writing of queued WaveHdrs, and all pause / reset stream management. -+ */ -+static DWORD CALLBACK wodPlayer(LPVOID lpParam) { -+ WINE_WAVEINST *wwo = (WINE_WAVEINST*)lpParam; -+ DWORD dwSleepTime = INFINITE; -+ int64_t delta_write; -+ -+ wwo->state = WINE_WS_STOPPED; -+ SetEvent(wwo->hStartUpEvent); -+ -+ /* Wait for the shortest time before an action is required. If there are -+ * no pending actions, wait forever for a command. */ -+ for (;;) { -+ TRACE("Waiting %u ms\n", dwSleepTime); -+ PULSE_WaitRingMessage(&wwo->msgRing, dwSleepTime); -+ -+ delta_write = wwo->timing_info->write_index; -+ wodPlayer_ProcessMessages(wwo); -+ -+ /* Check for a stall situaiton */ -+ if (delta_write == wwo->timing_info->write_index -+ && wwo->lpQueuePtr && !wwo->lpPlayPtr -+ && wwo->state != WINE_WS_STOPPED) -+ wodPlayer_CheckReleasing(wwo); -+ -+ /* If there is audio playing, return headers and get next timeout */ -+ if (wwo->state == WINE_WS_PLAYING) { -+ dwSleepTime = wodPlayer_NotifyCompletions(wwo, FALSE, wodPlayer_GetStreamTime(wwo)); -+ } else -+ dwSleepTime = INFINITE; -+ } -+ -+ return 0; -+} -+ -+/************************************************************************** -+ * wodOpen [internal] -+ * -+ * Create a new pa_stream and connect it to a sink while creating a new -+ * WINE_WAVEINST to represent the device to the windows application. -+ */ -+static DWORD wodOpen(WORD wDevID, DWORD_PTR lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) { -+ WINE_WAVEDEV *wdo; -+ WINE_WAVEINST *wwo = NULL; -+ DWORD ret = MMSYSERR_NOERROR; -+ -+ TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags); -+ if (lpDesc == NULL) { -+ WARN("Invalid Parameter!\n"); -+ return MMSYSERR_INVALPARAM; -+ } -+ -+ if (wDevID >= PULSE_WodNumDevs) { -+ WARN("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs); -+ return MMSYSERR_BADDEVICEID; -+ } -+ wdo = &WOutDev[wDevID]; -+ -+ wwo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_WAVEINST)); -+ if (!wwo) { -+ WARN("Out of memory?!\n"); -+ return MMSYSERR_NOMEM; -+ } -+ *(WINE_WAVEINST**)lpdwUser = wwo; -+ -+ /* check to see if format is supported and make pa_sample_spec struct */ -+ if (!PULSE_SetupFormat(lpDesc->lpFormat, &wwo->sample_spec)) { -+ WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", -+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, -+ lpDesc->lpFormat->nSamplesPerSec); -+ ret = WAVERR_BADFORMAT; -+ goto exit; -+ } -+ -+ /* Check to see if this was just a query */ -+ if (dwFlags & WAVE_FORMAT_QUERY) { -+ TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", -+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, -+ lpDesc->lpFormat->nSamplesPerSec); -+ ret = MMSYSERR_NOERROR; -+ goto exit; -+ } -+ -+ if (TRACE_ON(wave)) { -+ char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; -+ pa_sample_spec_snprint(t, sizeof(t), &wwo->sample_spec); -+ TRACE("Sample spec '%s'\n", t); -+ } -+ -+ wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); -+ wwo->waveDesc = *lpDesc; -+ PULSE_InitRingMessage(&wwo->msgRing); -+ -+ wwo->stream = pa_stream_new(PULSE_context, "WaveOut", &wwo->sample_spec, NULL); -+ /* If server doesn't support sample_spec, it will error out here (re: 24bit) */ -+ if (!wwo->stream) { -+ ret = WAVERR_BADFORMAT; -+ goto exit; -+ } -+ -+ /* Setup callbacks */ -+ pa_stream_set_write_callback (wwo->stream, PULSE_StreamRequestCallback, wwo); -+ pa_stream_set_state_callback (wwo->stream, PULSE_StreamStateCallback, wwo); -+ pa_stream_set_underflow_callback (wwo->stream, PULSE_StreamUnderflowCallback, wwo); -+ pa_stream_set_moved_callback (wwo->stream, PULSE_StreamMovedCallback, wwo); -+ pa_stream_set_suspended_callback (wwo->stream, PULSE_StreamSuspendedCallback, wwo); -+ -+ /* Blank Buffer Attributes */ -+ wwo->buffer_attr.prebuf = (uint32_t)-1; -+ wwo->buffer_attr.tlength = (uint32_t)-1; -+ wwo->buffer_attr.minreq = (uint32_t)-1; -+ wwo->buffer_attr.maxlength = (uint32_t)-1; -+ -+ /* Try and connect */ -+ TRACE("Connecting stream for playback on %s.\n", wdo->device_name); -+ PULSE_MainloopLock(); -+ pa_stream_connect_playback(wwo->stream, wdo->device_name, &wwo->buffer_attr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY, NULL, NULL); -+ -+ /* Wait for connection */ -+ for (;;) { -+ pa_context_state_t cstate = pa_context_get_state(PULSE_context); -+ pa_stream_state_t sstate = pa_stream_get_state(wwo->stream); -+ -+ if (cstate == PA_CONTEXT_FAILED || cstate == PA_CONTEXT_TERMINATED || -+ sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED) { -+ ERR("Failed to connect stream context object: %s\n", pa_strerror(pa_context_errno(PULSE_context))); -+ PULSE_MainloopUnlock(); -+ ret = MMSYSERR_NODRIVER; -+ goto exit; -+ } -+ -+ if (sstate == PA_STREAM_READY) -+ break; -+ -+ PULSE_MainloopWait(); -+ } -+ TRACE("(%p)->stream connected for playback.\n", wwo); -+ -+ /* Get the pa_timing_info structure */ -+ PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_StreamSuccessCallback, wwo)); -+ wwo->timing_info = pa_stream_get_timing_info(wwo->stream); -+ assert(wwo->timing_info); -+ PULSE_MainloopUnlock(); -+ -+ /* Create and start the wodPlayer() thread to manage playback */ -+ wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); -+ wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)wwo, 0, &(wwo->dwThreadID)); -+ if (wwo->hThread) -+ SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL); -+ else { -+ ERR("Thread creation for the wodPlayer failed!\n"); -+ ret = MMSYSERR_NOMEM; -+ goto exit; -+ } -+ WaitForSingleObject(wwo->hStartUpEvent, INFINITE); -+ CloseHandle(wwo->hStartUpEvent); -+ wwo->hStartUpEvent = INVALID_HANDLE_VALUE; -+ -+ return wodPlayer_NotifyClient (wwo, WOM_OPEN, 0L, 0L); -+ -+exit: -+ if (!wwo) -+ return ret; -+ -+ if (wwo->hStartUpEvent != INVALID_HANDLE_VALUE) -+ CloseHandle(wwo->hStartUpEvent); -+ -+ if (wwo->msgRing.ring_buffer_size > 0) -+ PULSE_DestroyRingMessage(&wwo->msgRing); -+ -+ if (wwo->stream) { -+ if (pa_stream_get_state(wwo->stream) == PA_STREAM_READY) -+ pa_stream_disconnect(wwo->stream); -+ pa_stream_unref(wwo->stream); -+ wwo->stream = NULL; -+ } -+ HeapFree(GetProcessHeap(), 0, wwo); -+ -+ return ret; -+} -+ -+/************************************************************************** -+ * wodClose [internal] -+ */ -+static DWORD wodClose(WINE_WAVEINST *wwo) { -+ DWORD ret; -+ -+ TRACE("(%p);\n", wwo); -+ if (!wwo) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (wwo->state != WINE_WS_FAILED) { -+ if (wwo->lpQueuePtr && wwo->lpPlayPtr) { -+ WARN("buffers still playing !\n"); -+ return WAVERR_STILLPLAYING; -+ } -+ -+ PULSE_MainloopLock(); -+ PULSE_WaitForOperation(pa_stream_drain(wwo->stream, PULSE_StreamSuccessCallback, NULL)); -+ pa_stream_disconnect(wwo->stream); -+ PULSE_MainloopUnlock(); -+ -+ if (wwo->hThread != INVALID_HANDLE_VALUE) -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE); -+ -+ PULSE_DestroyRingMessage(&wwo->msgRing); -+ } -+ -+ if (wwo->stream) -+ pa_stream_unref(wwo->stream); -+ ret = wodPlayer_NotifyClient(wwo, WOM_CLOSE, 0L, 0L); -+ -+ HeapFree(GetProcessHeap(), 0, wwo); -+ -+ return ret; -+} -+ -+/************************************************************************** -+ * wodWrite [internal] -+ */ -+static DWORD wodWrite(WINE_WAVEINST *wwo, LPWAVEHDR lpWaveHdr, DWORD dwSize) { -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) -+ return WAVERR_UNPREPARED; -+ -+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) -+ return WAVERR_STILLPLAYING; -+ -+ lpWaveHdr->dwFlags &= ~WHDR_DONE; -+ lpWaveHdr->dwFlags |= WHDR_INQUEUE; -+ lpWaveHdr->lpNext = 0; -+ lpWaveHdr->reserved = 0; -+ -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodPause [internal] -+ */ -+static DWORD wodPause(WINE_WAVEINST *wwo) { -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_PAUSING, 0, TRUE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodGetPosition [internal] -+ */ -+static DWORD wodGetPosition(WINE_WAVEINST *wwo, LPMMTIME lpTime, DWORD uSize) { -+ pa_usec_t time, temp; -+ -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (lpTime == NULL) return MMSYSERR_INVALPARAM; -+ -+ time = wodPlayer_GetStreamTime(wwo); -+ -+ temp = pa_bytes_to_usec(wwo->dwLastReset, &wwo->sample_spec); -+ if (time > temp) time -= temp; else time = 0; -+ -+ return PULSE_UsecToMMTime(time, lpTime, &wwo->sample_spec); -+} -+/************************************************************************** -+ * wodBreakLoop [internal] -+ */ -+static DWORD wodBreakLoop(WINE_WAVEINST *wwo) { -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_BREAKLOOP, 0, TRUE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodGetDevCaps [internal] -+ */ -+static DWORD wodGetDevCaps(DWORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize) { -+ TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize); -+ -+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED; -+ -+ if (wDevID >= PULSE_WodNumDevs) { -+ TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ memcpy(lpCaps, &(WOutDev[wDevID].caps.out), min(dwSize, sizeof(*lpCaps))); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodGetNumDevs [internal] -+ * Context-sanity check here, as if we respond with 0, WINE will move on -+ * to the next waveout driver. -+ */ -+static DWORD wodGetNumDevs(void) { -+ if (!PULSE_ml || !PULSE_context || pa_context_get_state(PULSE_context) != PA_CONTEXT_READY) -+ return 0; -+ -+ return PULSE_WodNumDevs; -+} -+ -+/************************************************************************** -+ * wodGetVolume [internal] -+ */ -+static DWORD wodGetVolume(WINE_WAVEINST *wwo, LPDWORD lpdwVol) { -+ double value1, value2; -+ DWORD wleft, wright; -+ -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ TRACE("(%p, %p);\n", wwo, lpdwVol); -+ -+ if (lpdwVol == NULL) -+ return MMSYSERR_NOTENABLED; -+ -+ PULSE_MainloopLock(); -+ if (wwo->stream && PULSE_context && pa_context_get_state(PULSE_context) == PA_CONTEXT_READY && -+ pa_stream_get_state(wwo->stream) == PA_STREAM_READY) { -+ PULSE_WaitForOperation(pa_context_get_sink_input_info(PULSE_context, pa_stream_get_index(wwo->stream), WAVEOUT_SinkInputInfoCallback, wwo)); -+ } -+ PULSE_MainloopUnlock(); -+ -+ -+ if (wwo->volume.channels == 2) { -+ value1 = pa_sw_volume_to_linear(wwo->volume.values[0]); -+ value2 = pa_sw_volume_to_linear(wwo->volume.values[1]); -+ } else { -+ value1 = pa_sw_volume_to_linear(pa_cvolume_avg(&wwo->volume)); -+ value2 = value1; -+ } -+ -+ wleft = 0xFFFFl * value1; -+ wright = 0xFFFFl * value2; -+ -+ if (wleft > 0xFFFFl) -+ wleft = 0xFFFFl; -+ if (wright > 0xFFFFl) -+ wright = 0xFFFFl; -+ -+ *lpdwVol = (WORD)wleft + (WORD)(wright << 16); -+ -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodSetVolume [internal] -+ */ -+static DWORD wodSetVolume(WINE_WAVEINST *wwo, DWORD dwParam1) { -+ double value1, value2; -+ -+ TRACE("(%p, %08X);\n", wwo, dwParam1); -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ value1 = (double)LOWORD(dwParam1)/(double)0xFFFFl; -+ value2 = (double)HIWORD(dwParam1)/(double)0xFFFFl; -+ -+ if (wwo->sample_spec.channels == 2) { -+ wwo->volume.channels = 2; -+ wwo->volume.values[0] = pa_sw_volume_from_linear(value1); -+ wwo->volume.values[1] = pa_sw_volume_from_linear(value2); -+ } else { -+ if (value1 != value2) FIXME("Non-stereo streams can't pan!\n"); -+ wwo->volume.channels = wwo->sample_spec.channels; -+ pa_cvolume_set(&wwo->volume, wwo->volume.channels, pa_sw_volume_from_linear(value1 > value2 ? value1 : value2)); -+ } -+ -+ if (TRACE_ON(wave)) { -+ char s[PA_CVOLUME_SNPRINT_MAX]; -+ pa_cvolume_snprint(s, PA_CVOLUME_SNPRINT_MAX, &wwo->volume); -+ TRACE("%s\n", s); -+ } -+ -+ PULSE_MainloopLock(); -+ if (!wwo->stream || !PULSE_context || pa_context_get_state(PULSE_context) != PA_CONTEXT_READY || -+ pa_stream_get_state(wwo->stream) != PA_STREAM_READY || !pa_cvolume_valid(&wwo->volume)) { -+ PULSE_MainloopUnlock(); -+ return MMSYSERR_NOERROR; -+ } -+ -+ PULSE_WaitForOperation(pa_context_set_sink_input_volume(PULSE_context, -+ pa_stream_get_index(wwo->stream), &wwo->volume, -+ PULSE_ContextSuccessCallback, wwo)); -+ PULSE_MainloopUnlock(); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodRestart [internal] -+ */ -+static DWORD wodRestart(WINE_WAVEINST *wwo) { -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ if (wwo->state == WINE_WS_PAUSED) -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESTARTING, 0, TRUE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodReset [internal] -+ */ -+static DWORD wodReset(WINE_WAVEINST *wwo) { -+ if (!wwo || wwo->state == WINE_WS_FAILED) { -+ WARN("Stream instance invalid.\n"); -+ return MMSYSERR_INVALHANDLE; -+ } -+ -+ PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESETTING, 0, TRUE); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodDevInterfaceSize [internal] -+ */ -+static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) { -+ -+ *dwParam1 = MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1, NULL, 0) * sizeof(WCHAR); -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodDevInterface [internal] -+ */ -+static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) { -+ if (dwParam2 >= MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1, -+ NULL, 0 ) * sizeof(WCHAR)) -+ { -+ MultiByteToWideChar(CP_UTF8, 0, WOutDev[wDevID].interface_name, -1, -+ dwParam1, dwParam2 / sizeof(WCHAR)); -+ return MMSYSERR_NOERROR; -+ } -+ return MMSYSERR_INVALPARAM; -+} -+ -+DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) { -+ TRACE("(%u, %p)\n", wDevID, desc); -+ *desc = WOutDev[wDevID].ds_desc; -+ return MMSYSERR_NOERROR; -+} -+ -+/************************************************************************** -+ * wodMessage (WINEPULSE.@) -+ */ -+DWORD WINAPI PULSE_wodMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { -+ -+ switch (wMsg) { -+ -+ case DRVM_INIT: -+ case DRVM_EXIT: -+ case DRVM_ENABLE: -+ case DRVM_DISABLE: -+ return 0; -+ -+ /* WaveOut Playback related functions */ -+ case WODM_OPEN: return wodOpen (wDevID, dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2); -+ case WODM_CLOSE: return wodClose ((WINE_WAVEINST*)dwUser); -+ case WODM_WRITE: return wodWrite ((WINE_WAVEINST*)dwUser, (LPWAVEHDR)dwParam1, dwParam2); -+ case WODM_PAUSE: return wodPause ((WINE_WAVEINST*)dwUser); -+ case WODM_GETPOS: return wodGetPosition ((WINE_WAVEINST*)dwUser, (LPMMTIME)dwParam1, dwParam2); -+ case WODM_BREAKLOOP: return wodBreakLoop ((WINE_WAVEINST*)dwUser); -+ case WODM_RESTART: return wodRestart ((WINE_WAVEINST*)dwUser); -+ case WODM_RESET: return wodReset ((WINE_WAVEINST*)dwUser); -+ -+ case WODM_GETVOLUME: return wodGetVolume ((WINE_WAVEINST*)dwUser, (LPDWORD)dwParam1); -+ case WODM_SETVOLUME: return wodSetVolume ((WINE_WAVEINST*)dwUser, dwParam1); -+ -+ case WODM_PREPARE: -+ case WODM_UNPREPARE: -+ -+ case WODM_GETPITCH: -+ case WODM_SETPITCH: -+ -+ case WODM_GETPLAYBACKRATE: -+ case WODM_SETPLAYBACKRATE: -+ return MMSYSERR_NOTSUPPORTED; -+ -+ /* Device enumeration, directsound and capabilities */ -+ case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2); -+ case WODM_GETNUMDEVS: return wodGetNumDevs (); -+ case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1); -+ case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2); -+ case DRV_QUERYDSOUNDIFACE: return MMSYSERR_NOTSUPPORTED; -+ case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1); -+ -+ default: -+ FIXME("unknown message %d!\n", wMsg); -+ } -+ return MMSYSERR_NOTSUPPORTED; -+} -+ -+#else /* !HAVE_PULSEAUDIO */ -+ -+/************************************************************************** -+ * wodMessage (WINEPULSE.@) -+ */ -+DWORD WINAPI PULSE_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, -+ DWORD dwParam1, DWORD dwParam2) { -+ FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, -+ dwParam1, dwParam2); -+ return MMSYSERR_NOTENABLED; -+} -+ -+#endif /* HAVE_PULSEAUDIO */ -diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec -new file mode 100644 -index 0000000..1b49460 ---- /dev/null -+++ b/dlls/winepulse.drv/winepulse.drv.spec -@@ -0,0 +1,3 @@ -+@ stdcall -private DriverProc(long long long long long long) PULSE_DriverProc -+@ stdcall -private wodMessage(long long long long long long) PULSE_wodMessage -+@ stdcall -private widMessage(long long long long long long) PULSE_widMessage -diff --git a/dlls/winepulse.drv/winepulse.h b/dlls/winepulse.drv/winepulse.h -new file mode 100644 -index 0000000..30eacfd ---- /dev/null -+++ b/dlls/winepulse.drv/winepulse.h -@@ -0,0 +1,210 @@ -+/* Definitions for PulseAudio Wine Driver -+ * -+ * Copyright 2009 Arthur Taylor -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#ifndef __WINE_CONFIG_H -+# error You must include config.h to use this header -+#endif -+ -+#if defined(HAVE_PULSEAUDIO) && !defined(__WINEPULSE_H) -+#define __WINEPULSE_H -+ -+#include "mmreg.h" -+#include "dsound.h" -+#include "dsdriver.h" -+ -+#include "ks.h" -+#include "ksmedia.h" -+#include "ksguid.h" -+ -+#include -+ -+/* state diagram for waveOut writing: -+ * -+ * +---------+-------------+---------------+---------------------------------+ -+ * | state | function | event | new state | -+ * +---------+-------------+---------------+---------------------------------+ -+ * | | open() | | STOPPED | -+ * | PAUSED | write() | | PAUSED | -+ * | STOPPED | write() | | PLAYING | -+ * | PLAYING | write() | HEADER | PLAYING | -+ * | (other) | write() | | | -+ * | (any) | pause() | PAUSING | PAUSED | -+ * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) | -+ * | (any) | reset() | RESETTING | STOPPED | -+ * | (any) | close() | CLOSING | CLOSED | -+ * +---------+-------------+---------------+---------------------------------+ -+ */ -+ -+/* states of the playing device */ -+#define WINE_WS_PLAYING 1 -+#define WINE_WS_PAUSED 2 -+#define WINE_WS_STOPPED 3 -+#define WINE_WS_CLOSED 4 -+#define WINE_WS_FAILED 5 -+ -+#define PULSE_ALL_FORMATS \ -+ WAVE_FORMAT_1M08 | /* Mono 11025Hz 8-bit */\ -+ WAVE_FORMAT_1M16 | /* Mono 11025Hz 16-bit */\ -+ WAVE_FORMAT_1S08 | /* Stereo 11025Hz 8-bit */\ -+ WAVE_FORMAT_1S16 | /* Stereo 11025Hz 16-bit */\ -+ WAVE_FORMAT_2M08 | /* Mono 22050Hz 8-bit */\ -+ WAVE_FORMAT_2M16 | /* Mono 22050Hz 16-bit */\ -+ WAVE_FORMAT_2S08 | /* Stereo 22050Hz 8-bit */\ -+ WAVE_FORMAT_2S16 | /* Stereo 22050Hz 16-bit */\ -+ WAVE_FORMAT_4M08 | /* Mono 44100Hz 8-bit */\ -+ WAVE_FORMAT_4M16 | /* Mono 44100Hz 16-bit */\ -+ WAVE_FORMAT_4S08 | /* Stereo 44100Hz 8-bit */\ -+ WAVE_FORMAT_4S16 | /* Stereo 44100Hz 16-bit */\ -+ WAVE_FORMAT_48M08 | /* Mono 48000Hz 8-bit */\ -+ WAVE_FORMAT_48S08 | /* Stereo 48000Hz 8-bit */\ -+ WAVE_FORMAT_48M16 | /* Mono 48000Hz 16-bit */\ -+ WAVE_FORMAT_48S16 | /* Stereo 48000Hz 16-bit */\ -+ WAVE_FORMAT_96M08 | /* Mono 96000Hz 8-bit */\ -+ WAVE_FORMAT_96S08 | /* Stereo 96000Hz 8-bit */\ -+ WAVE_FORMAT_96M16 | /* Mono 96000Hz 16-bit */\ -+ WAVE_FORMAT_96S16 /* Stereo 96000Hz 16-bit */ -+ -+/* events to be sent to device */ -+enum win_wm_message { -+ WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER, -+ WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING, WINE_WM_XRUN, WINE_WM_FEED -+}; -+ -+typedef struct { -+ enum win_wm_message msg; /* message identifier */ -+ DWORD param; /* parameter for this message */ -+ HANDLE hEvent; /* if message is synchronous, handle of event for synchro */ -+} PULSE_MSG; -+ -+/* implement an in-process message ring for better performance -+ * (compared to passing thru the server) -+ * this ring will be used by the input (resp output) record (resp playback) routine -+ */ -+typedef struct { -+ PULSE_MSG * messages; -+ int ring_buffer_size; -+ int msg_tosave; -+ int msg_toget; -+/* Either pipe or event is used, but that is defined in pulse.c, -+ * since this is a global header we define both here */ -+ int msg_pipe[2]; -+ HANDLE msg_event; -+ CRITICAL_SECTION msg_crst; -+} PULSE_MSG_RING; -+ -+typedef struct WINE_WAVEDEV WINE_WAVEDEV; -+typedef struct WINE_WAVEINST WINE_WAVEINST; -+ -+/* Per-playback/record device */ -+struct WINE_WAVEDEV { -+ char interface_name[MAXPNAMELEN * 2]; -+ char *device_name; -+ pa_cvolume volume; -+ -+ union { -+ WAVEOUTCAPSW out; -+ WAVEINCAPSW in; -+ } caps; -+ -+ /* DirectSound stuff */ -+ DSDRIVERDESC ds_desc; -+ DSDRIVERCAPS ds_caps; -+}; -+ -+/* Per-playback/record instance */ -+struct WINE_WAVEINST { -+ INT state; /* one of the WINE_WS_ manifest constants */ -+ WAVEOPENDESC waveDesc; -+ WORD wFlags; -+ -+ /* PulseAudio specific data */ -+ pa_stream *stream; /* The PulseAudio stream */ -+ const pa_timing_info *timing_info; /* The timing info structure for the stream */ -+ pa_sample_spec sample_spec; /* Sample spec of this stream / device */ -+ pa_cvolume volume; /* Software volume of the stream */ -+ pa_buffer_attr buffer_attr; /* Buffer attribute, may not be used */ -+ -+ /* waveIn / waveOut wavaHdr */ -+ LPWAVEHDR lpQueuePtr; /* Start of queued WAVEHDRs (waiting to be notified) */ -+ LPWAVEHDR lpPlayPtr; /* Start of not yet fully written buffers */ -+ DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */ -+ LPWAVEHDR lpLoopPtr; /* Pointer of first buffer in loop, if any */ -+ DWORD dwLoops; /* Private copy of loop counter */ -+ DWORD dwLastReset; /* When the last reset occured, as pa stream time doesn't reset */ -+ -+ /* waveIn specific */ -+ const void *buffer; /* Pointer to the latest data fragment for recording streams */ -+ DWORD buffer_length; /* How large the latest data fragment is */ -+ DWORD buffer_read_offset; /* How far into latest data fragment we last read */ -+ -+ /* Thread communication and synchronization stuff */ -+ HANDLE hStartUpEvent; -+ HANDLE hThread; -+ DWORD dwThreadID; -+ PULSE_MSG_RING msgRing; -+}; -+ -+/* We establish one context per instance, so make it global to the lib */ -+pa_context *PULSE_context; /* Connection Context */ -+pa_mainloop *PULSE_ml; /* PA Runtime information */ -+ -+/* Win32 mainloop handles */ -+HANDLE PULSE_ml_hThread; -+HANDLE PULSE_ml_hEvent; -+HANDLE PULSE_ml_hMutex; -+ -+/* WaveIn / WaveOut devices */ -+WINE_WAVEDEV *WOutDev; -+WINE_WAVEDEV *WInDev; -+DWORD PULSE_WodNumDevs; -+DWORD PULSE_WidNumDevs; -+ -+/* pulse.c: PulseAudio Async Callbacks */ -+void PULSE_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata); -+void PULSE_StreamSuccessCallback(pa_stream *s, int success, void *userdata); -+void PULSE_StreamStateCallback(pa_stream *s, void *userdata); -+void PULSE_StreamUnderflowCallback(pa_stream *s, void *userdata); -+void PULSE_StreamSuspendedCallback(pa_stream *s, void *userdata); -+void PULSE_StreamMovedCallback(pa_stream *s, void *userdata); -+void PULSE_ContextSuccessCallback(pa_context *c, int success, void *userdata); -+ -+/* pulse.c: General Functions */ -+void PULSE_WaitForOperation(pa_operation *o); -+BOOL PULSE_SetupFormat(LPWAVEFORMATEX wf, pa_sample_spec *ss); -+HRESULT PULSE_UsecToMMTime(pa_usec_t time, LPMMTIME lpTime, const pa_sample_spec *ss); -+ -+/* pulse.c: Win32 Mainloop */ -+void PULSE_MainloopStart(void); -+void PULSE_MainloopStop(void); -+void PULSE_MainloopLock(void); -+void PULSE_MainloopUnlock(void); -+void PULSE_MainloopWait(void); -+void PULSE_MainloopSignal(void); -+ -+/* pulse.c: Message Ring */ -+int PULSE_InitRingMessage(PULSE_MSG_RING* omr); -+int PULSE_DestroyRingMessage(PULSE_MSG_RING* omr); -+void PULSE_ResetRingMessage(PULSE_MSG_RING* omr); -+void PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep); -+int PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait); -+int PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr, enum win_wm_message *msg, DWORD *param, HANDLE *hEvent); -+ -+/* pulse.c: Tracing */ -+const char * PULSE_getCmdString(enum win_wm_message msg); -+#endif diff --git a/winepulse-configure.ac-1.3.22.patch b/winepulse-configure.ac-1.3.22.patch deleted file mode 100644 index 32afe85..0000000 --- a/winepulse-configure.ac-1.3.22.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index 2e20f24..fe07d5b 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -78,6 +78,7 @@ AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]), - [if test "x$withval" = "xno"; then ac_cv_header_png_h=no; fi]) - AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]), - [if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi]) -+AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support])) - AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)])) - AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]), - [if test "x$withval" = "xno"; then ac_cv_header_tiffio_h=no; fi]) -@@ -1479,6 +1480,30 @@ then - CFLAGS="$save_CFLAGS" - fi - -+dnl **** Check for PulseAudio **** -+AC_SUBST(PULSELIBS,"") -+AC_SUBST(PULSEINCL,"") -+if test "x$with_pulse" != "xno"; -+then -+ ac_save_CPPFLAGS="$CPPFLAGS" -+ if test "$PKG_CONFIG" != "false"; -+ then -+ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`" -+ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`" -+ -+ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags" -+ AC_CHECK_HEADERS(pulse/pulseaudio.h, -+ [AC_CHECK_LIB(pulse, pa_stream_is_corked, -+ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio]) -+ PULSELIBS="$ac_pulse_libs" -+ PULSEINCL="$ac_pulse_cflags"],,$ac_pulse_libs) -+ ]) -+ fi -+ CPPFLAGS="$ac_save_CPPFLAGS" -+fi -+WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"], -+ [libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.]) -+ - dnl **** Check for gstreamer **** - if test "x$with_gstreamer" != "xno" - then -@@ -1693,7 +1718,7 @@ test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=$ - test "$ac_cv_header_linux_joystick_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} - - dnl **** Check for any sound system **** --if test "x$ALSALIBS$COREAUDIO$NASLIBS$ESDLIBS$ac_cv_lib_soname_jack" = "x" -a \ -+if test "x$ALSALIBS$COREAUDIO$NASLIBS$ESDLIBS$PULSELIBS$ac_cv_lib_soname_jack" = "x" -a \ - "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \ - "x$with_alsa$with_coreaudio$with_nas$with_esd$with_jack$with_oss" != xnononononono - then -@@ -2883,6 +2908,7 @@ WINE_CONFIG_DLL(winenas.drv) - WINE_CONFIG_DLL(wineoss.drv) - WINE_CONFIG_DLL(wineps.drv,,[install-lib]) - WINE_CONFIG_DLL(wineps16.drv16,enable_win16) -+WINE_CONFIG_DLL(winepulse.drv) - WINE_CONFIG_DLL(wineqtdecoder) - WINE_CONFIG_DLL(winequartz.drv) - WINE_CONFIG_DLL(winex11.drv) diff --git a/winepulse-winecfg-1.3.11.patch b/winepulse-winecfg-1.3.11.patch deleted file mode 100644 index 3c5c3f8..0000000 --- a/winepulse-winecfg-1.3.11.patch +++ /dev/null @@ -1,49 +0,0 @@ -diff --git a/programs/winecfg/audio.c b/programs/winecfg/audio.c -index 4c90282..fcf3236 100644 ---- a/programs/winecfg/audio.c -+++ b/programs/winecfg/audio.c -@@ -90,6 +90,7 @@ typedef struct - } AUDIO_DRIVER; - - static AUDIO_DRIVER sAudioDrivers[] = { -+ {IDS_DRIVER_PULSE, "pulse"}, - {IDS_DRIVER_ALSA, "alsa"}, - {IDS_DRIVER_OSS, "oss"}, - {IDS_DRIVER_COREAUDIO, "coreaudio"}, -diff --git a/programs/winecfg/libraries.c b/programs/winecfg/libraries.c -index e402b4e..f0264ca 100644 ---- a/programs/winecfg/libraries.c -+++ b/programs/winecfg/libraries.c -@@ -73,6 +73,7 @@ static const char * const builtin_only[] = - "winedos", - "winemp3.acm", - "wineps", -+ "winepulse.drv", - "winmm", - "wintab32", - "wnaspi32", -diff --git a/programs/winecfg/resource.h b/programs/winecfg/resource.h -index 3bed6aa..2f347d9 100644 ---- a/programs/winecfg/resource.h -+++ b/programs/winecfg/resource.h -@@ -186,7 +186,7 @@ - #define IDS_ACCEL_BASIC 8302 - #define IDS_ACCEL_EMULATION 8303 - #define IDS_DRIVER_ALSA 8304 -- -+#define IDS_DRIVER_PULSE 8305 - #define IDS_DRIVER_ESOUND 8306 - #define IDS_DRIVER_OSS 8307 - #define IDS_DRIVER_JACK 8308 -diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc -index f98a14d..314bd65 100644 ---- a/programs/winecfg/winecfg.rc -+++ b/programs/winecfg/winecfg.rc -@@ -97,6 +97,7 @@ BEGIN - IDS_ACCEL_STANDARD "Standard" - IDS_ACCEL_BASIC "Basic" - IDS_ACCEL_EMULATION "Emulation" -+ IDS_DRIVER_PULSE "PulseAudio Driver" - IDS_DRIVER_ALSA "ALSA Driver" - IDS_DRIVER_ESOUND "EsounD Driver" - IDS_DRIVER_OSS "OSS Driver"