2 * Copyright 2011-2012 Maarten Lankhorst
3 * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
4 * Copyright 2011 Andrew Eikum for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Pulseaudio driver support.. hell froze over
23 #define NONAMELESSUNION
34 #include <pulse/pulseaudio.h>
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42 #include "wine/list.h"
52 #include "mmdeviceapi.h"
53 #include "audioclient.h"
54 #include "endpointvolume.h"
55 #include "audiopolicy.h"
57 #include "wine/list.h"
59 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
61 WINE_DEFAULT_DEBUG_CHANNEL(pulse
);
63 static const REFERENCE_TIME MinimumPeriod
= 30000;
64 static const REFERENCE_TIME DefaultPeriod
= 100000;
66 static pa_context
*pulse_ctx
;
67 static pa_mainloop
*pulse_ml
;
69 static HANDLE pulse_thread
;
70 static pthread_mutex_t pulse_lock
= PTHREAD_MUTEX_INITIALIZER
;
71 static pthread_cond_t pulse_cond
= PTHREAD_COND_INITIALIZER
;
73 static DWORD pulse_stream_volume
;
75 const WCHAR pulse_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
76 'W','i','n','e','\\','P','u','l','s','e',0};
77 const WCHAR pulse_streamW
[] = { 'S','t','r','e','a','m','V','o','l',0 };
79 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
81 if (reason
== DLL_PROCESS_ATTACH
) {
83 if (RegOpenKeyW(HKEY_CURRENT_USER
, pulse_keyW
, &key
) == ERROR_SUCCESS
) {
84 DWORD size
= sizeof(pulse_stream_volume
);
85 RegQueryValueExW(key
, pulse_streamW
, 0, NULL
,
86 (BYTE
*)&pulse_stream_volume
, &size
);
89 DisableThreadLibraryCalls(dll
);
90 } else if (reason
== DLL_PROCESS_DETACH
) {
92 pa_context_disconnect(pulse_ctx
);
93 pa_context_unref(pulse_ctx
);
96 pa_mainloop_quit(pulse_ml
, 0);
97 CloseHandle(pulse_thread
);
103 static const WCHAR defaultW
[] = {'P','u','l','s','e','a','u','d','i','o',0};
105 /* Following pulseaudio design here, mainloop has the lock taken whenever
106 * it is handling something for pulse, and the lock is required whenever
107 * doing any pa_* call that can affect the state in any way
109 * pa_cond_wait is used when waiting on results, because the mainloop needs
110 * the same lock taken to affect the state
112 * This is basically the same as the pa_threaded_mainloop implementation,
113 * but that cannot be used because it uses pthread_create directly
115 * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
116 * pa_threaded_mainloop_signal -> pthread_cond_signal
117 * pa_threaded_mainloop_wait -> pthread_cond_wait
120 static int pulse_poll_func(struct pollfd
*ufds
, unsigned long nfds
, int timeout
, void *userdata
) {
122 pthread_mutex_unlock(&pulse_lock
);
123 r
= poll(ufds
, nfds
, timeout
);
124 pthread_mutex_lock(&pulse_lock
);
128 static DWORD CALLBACK
pulse_mainloop_thread(void *tmp
) {
130 pulse_ml
= pa_mainloop_new();
131 pa_mainloop_set_poll_func(pulse_ml
, pulse_poll_func
, NULL
);
132 pthread_mutex_lock(&pulse_lock
);
133 pthread_cond_signal(&pulse_cond
);
134 pa_mainloop_run(pulse_ml
, &ret
);
135 pthread_mutex_unlock(&pulse_lock
);
136 pa_mainloop_free(pulse_ml
);
137 CloseHandle(pulse_thread
);
141 static void pulse_contextcallback(pa_context
*c
, void *userdata
);
143 static HRESULT
pulse_connect(void)
146 WCHAR path
[PATH_MAX
], *name
;
151 if (!(pulse_thread
= CreateThread(NULL
, 0, pulse_mainloop_thread
, NULL
, 0, NULL
)))
153 ERR("Failed to create mainloop thread.");
156 SetThreadPriority(pulse_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
157 pthread_cond_wait(&pulse_cond
, &pulse_lock
);
160 if (pulse_ctx
&& PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx
)))
163 pa_context_unref(pulse_ctx
);
165 GetModuleFileNameW(NULL
, path
, sizeof(path
)/sizeof(*path
));
166 name
= strrchrW(path
, '\\');
171 len
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
);
172 str
= pa_xmalloc(len
);
173 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, str
, len
, NULL
, NULL
);
174 TRACE("Name: %s\n", str
);
175 pulse_ctx
= pa_context_new(pa_mainloop_get_api(pulse_ml
), str
);
178 ERR("Failed to create context\n");
182 pa_context_set_state_callback(pulse_ctx
, pulse_contextcallback
, NULL
);
184 TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx
), PA_API_VERSION
);
185 if (pa_context_connect(pulse_ctx
, NULL
, 0, NULL
) < 0)
188 /* Wait for connection */
189 while (pthread_cond_wait(&pulse_cond
, &pulse_lock
)) {
190 pa_context_state_t state
= pa_context_get_state(pulse_ctx
);
192 if (state
== PA_CONTEXT_FAILED
|| state
== PA_CONTEXT_TERMINATED
)
195 if (state
== PA_CONTEXT_READY
)
199 TRACE("Connected to server %s with protocol version: %i.\n",
200 pa_context_get_server(pulse_ctx
),
201 pa_context_get_server_protocol_version(pulse_ctx
));
205 pa_context_unref(pulse_ctx
);
210 static void pulse_contextcallback(pa_context
*c
, void *userdata
) {
211 switch (pa_context_get_state(c
)) {
213 FIXME("Unhandled state: %i\n", pa_context_get_state(c
));
214 case PA_CONTEXT_CONNECTING
:
215 case PA_CONTEXT_UNCONNECTED
:
216 case PA_CONTEXT_AUTHORIZING
:
217 case PA_CONTEXT_SETTING_NAME
:
218 case PA_CONTEXT_TERMINATED
:
219 TRACE("State change to %i\n", pa_context_get_state(c
));
222 case PA_CONTEXT_READY
:
226 case PA_CONTEXT_FAILED
:
227 ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c
)));
230 pthread_cond_signal(&pulse_cond
);
233 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, void ***keys
,
234 UINT
*num
, UINT
*def_index
)
237 TRACE("%d %p %p %p\n", flow
, ids
, num
, def_index
);
239 pthread_mutex_lock(&pulse_lock
);
240 hr
= pulse_connect();
241 pthread_mutex_unlock(&pulse_lock
);
247 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
249 return E_OUTOFMEMORY
;
251 (*ids
)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW
));
253 HeapFree(GetProcessHeap(), 0, *ids
);
254 return E_OUTOFMEMORY
;
257 lstrcpyW((*ids
)[0], defaultW
);
259 *keys
= HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
265 int WINAPI
AUDDRV_GetPriority(void)
268 pthread_mutex_lock(&pulse_lock
);
269 hr
= pulse_connect();
270 pthread_mutex_unlock(&pulse_lock
);
271 return SUCCEEDED(hr
) ? 3 : 0;
274 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(void *key
, IMMDevice
*dev
,
275 EDataFlow dataflow
, IAudioClient
**out
)
277 TRACE("%p %p %d %p\n", key
, dev
, dataflow
, out
);
278 if (dataflow
!= eRender
&& dataflow
!= eCapture
)
285 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
286 IAudioSessionManager2
**out
)