winepulse: Add initial stub for pulseaudio support
[wine/multimedia.git] / dlls / winepulse.drv / mmdevdrv.c
blobd187bdc6018a8129daf8d0445f963d90f60d7b74
1 /*
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
24 #define COBJMACROS
25 #include "config.h"
26 #include <poll.h>
27 #include <pthread.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <stdio.h>
34 #include <pulse/pulseaudio.h>
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winreg.h"
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42 #include "wine/list.h"
44 #include "ole2.h"
45 #include "dshow.h"
46 #include "dsound.h"
47 #include "propsys.h"
49 #include "initguid.h"
50 #include "ks.h"
51 #include "ksmedia.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) {
82 HKEY key;
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);
87 RegCloseKey(key);
89 DisableThreadLibraryCalls(dll);
90 } else if (reason == DLL_PROCESS_DETACH) {
91 if (pulse_ctx) {
92 pa_context_disconnect(pulse_ctx);
93 pa_context_unref(pulse_ctx);
95 if (pulse_ml)
96 pa_mainloop_quit(pulse_ml, 0);
97 CloseHandle(pulse_thread);
99 return TRUE;
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) {
121 int r;
122 pthread_mutex_unlock(&pulse_lock);
123 r = poll(ufds, nfds, timeout);
124 pthread_mutex_lock(&pulse_lock);
125 return r;
128 static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
129 int ret;
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);
138 return ret;
141 static void pulse_contextcallback(pa_context *c, void *userdata);
143 static HRESULT pulse_connect(void)
145 int len;
146 WCHAR path[PATH_MAX], *name;
147 char *str;
149 if (!pulse_thread)
151 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
153 ERR("Failed to create mainloop thread.");
154 return E_FAIL;
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)))
161 return S_OK;
162 if (pulse_ctx)
163 pa_context_unref(pulse_ctx);
165 GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
166 name = strrchrW(path, '\\');
167 if (!name)
168 name = path;
169 else
170 name++;
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);
176 pa_xfree(str);
177 if (!pulse_ctx) {
178 ERR("Failed to create context\n");
179 return E_FAIL;
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)
186 goto fail;
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)
193 goto fail;
195 if (state == PA_CONTEXT_READY)
196 break;
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));
202 return S_OK;
204 fail:
205 pa_context_unref(pulse_ctx);
206 pulse_ctx = NULL;
207 return E_FAIL;
210 static void pulse_contextcallback(pa_context *c, void *userdata) {
211 switch (pa_context_get_state(c)) {
212 default:
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));
220 return;
222 case PA_CONTEXT_READY:
223 TRACE("Ready\n");
224 break;
226 case PA_CONTEXT_FAILED:
227 ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
228 break;
230 pthread_cond_signal(&pulse_cond);
233 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
234 UINT *num, UINT *def_index)
236 HRESULT hr = S_OK;
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);
242 if (FAILED(hr))
243 return hr;
244 *num = 1;
245 *def_index = 0;
247 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
248 if (!*ids)
249 return E_OUTOFMEMORY;
251 (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
252 if (!(*ids)[0]) {
253 HeapFree(GetProcessHeap(), 0, *ids);
254 return E_OUTOFMEMORY;
257 lstrcpyW((*ids)[0], defaultW);
259 *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
260 (*keys)[0] = NULL;
262 return S_OK;
265 int WINAPI AUDDRV_GetPriority(void)
267 HRESULT hr;
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)
279 return E_UNEXPECTED;
281 *out = NULL;
282 return E_NOTIMPL;
285 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
286 IAudioSessionManager2 **out)
288 *out = NULL;
289 return E_NOTIMPL;