Mercurial > audio-send
comparison Alc/backends/pulseaudio.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f9476ff7637e |
---|---|
1 /** | |
2 * OpenAL cross platform audio library | |
3 * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com> | |
4 * Copyright (C) 2010 by Chris Robinson <chris.kcat@gmail.com> | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Library General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Library General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Library General Public | |
16 * License along with this library; if not, write to the | |
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 * Boston, MA 02111-1307, USA. | |
19 * Or go to http://www.gnu.org/copyleft/lgpl.html | |
20 */ | |
21 | |
22 #include "config.h" | |
23 | |
24 #include "alMain.h" | |
25 | |
26 #include <pulse/pulseaudio.h> | |
27 | |
28 #if PA_API_VERSION == 11 | |
29 #define PA_STREAM_ADJUST_LATENCY 0x2000U | |
30 #define PA_STREAM_EARLY_REQUESTS 0x4000U | |
31 static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) | |
32 { | |
33 return (x == PA_STREAM_CREATING || x == PA_STREAM_READY); | |
34 } | |
35 static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) | |
36 { | |
37 return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || | |
38 x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY); | |
39 } | |
40 #define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD | |
41 #define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD | |
42 #elif PA_API_VERSION != 12 | |
43 #error Invalid PulseAudio API version | |
44 #endif | |
45 | |
46 #ifndef PA_CHECK_VERSION | |
47 #define PA_CHECK_VERSION(major,minor,micro) \ | |
48 ((PA_MAJOR > (major)) || \ | |
49 (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ | |
50 (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) | |
51 #endif | |
52 | |
53 static void *pa_handle; | |
54 #ifdef HAVE_DYNLOAD | |
55 #define MAKE_FUNC(x) static typeof(x) * p##x | |
56 MAKE_FUNC(pa_context_unref); | |
57 MAKE_FUNC(pa_sample_spec_valid); | |
58 MAKE_FUNC(pa_stream_drop); | |
59 MAKE_FUNC(pa_strerror); | |
60 MAKE_FUNC(pa_context_get_state); | |
61 MAKE_FUNC(pa_stream_get_state); | |
62 MAKE_FUNC(pa_threaded_mainloop_signal); | |
63 MAKE_FUNC(pa_stream_peek); | |
64 MAKE_FUNC(pa_threaded_mainloop_wait); | |
65 MAKE_FUNC(pa_threaded_mainloop_unlock); | |
66 MAKE_FUNC(pa_threaded_mainloop_in_thread); | |
67 MAKE_FUNC(pa_context_new); | |
68 MAKE_FUNC(pa_threaded_mainloop_stop); | |
69 MAKE_FUNC(pa_context_disconnect); | |
70 MAKE_FUNC(pa_threaded_mainloop_start); | |
71 MAKE_FUNC(pa_threaded_mainloop_get_api); | |
72 MAKE_FUNC(pa_context_set_state_callback); | |
73 MAKE_FUNC(pa_stream_write); | |
74 MAKE_FUNC(pa_xfree); | |
75 MAKE_FUNC(pa_stream_connect_record); | |
76 MAKE_FUNC(pa_stream_connect_playback); | |
77 MAKE_FUNC(pa_stream_readable_size); | |
78 MAKE_FUNC(pa_stream_writable_size); | |
79 MAKE_FUNC(pa_stream_cork); | |
80 MAKE_FUNC(pa_stream_is_suspended); | |
81 MAKE_FUNC(pa_stream_get_device_name); | |
82 MAKE_FUNC(pa_path_get_filename); | |
83 MAKE_FUNC(pa_get_binary_name); | |
84 MAKE_FUNC(pa_threaded_mainloop_free); | |
85 MAKE_FUNC(pa_context_errno); | |
86 MAKE_FUNC(pa_xmalloc); | |
87 MAKE_FUNC(pa_stream_unref); | |
88 MAKE_FUNC(pa_threaded_mainloop_accept); | |
89 MAKE_FUNC(pa_stream_set_write_callback); | |
90 MAKE_FUNC(pa_threaded_mainloop_new); | |
91 MAKE_FUNC(pa_context_connect); | |
92 MAKE_FUNC(pa_stream_set_buffer_attr); | |
93 MAKE_FUNC(pa_stream_get_buffer_attr); | |
94 MAKE_FUNC(pa_stream_get_sample_spec); | |
95 MAKE_FUNC(pa_stream_get_time); | |
96 MAKE_FUNC(pa_stream_set_read_callback); | |
97 MAKE_FUNC(pa_stream_set_state_callback); | |
98 MAKE_FUNC(pa_stream_set_moved_callback); | |
99 MAKE_FUNC(pa_stream_set_underflow_callback); | |
100 MAKE_FUNC(pa_stream_new); | |
101 MAKE_FUNC(pa_stream_disconnect); | |
102 MAKE_FUNC(pa_threaded_mainloop_lock); | |
103 MAKE_FUNC(pa_channel_map_init_auto); | |
104 MAKE_FUNC(pa_channel_map_parse); | |
105 MAKE_FUNC(pa_channel_map_snprint); | |
106 MAKE_FUNC(pa_channel_map_equal); | |
107 MAKE_FUNC(pa_context_get_server_info); | |
108 MAKE_FUNC(pa_context_get_sink_info_by_name); | |
109 MAKE_FUNC(pa_context_get_sink_info_list); | |
110 MAKE_FUNC(pa_context_get_source_info_list); | |
111 MAKE_FUNC(pa_operation_get_state); | |
112 MAKE_FUNC(pa_operation_unref); | |
113 #if PA_CHECK_VERSION(0,9,15) | |
114 MAKE_FUNC(pa_channel_map_superset); | |
115 MAKE_FUNC(pa_stream_set_buffer_attr_callback); | |
116 #endif | |
117 #if PA_CHECK_VERSION(0,9,16) | |
118 MAKE_FUNC(pa_stream_begin_write); | |
119 #endif | |
120 #undef MAKE_FUNC | |
121 | |
122 #define pa_context_unref ppa_context_unref | |
123 #define pa_sample_spec_valid ppa_sample_spec_valid | |
124 #define pa_stream_drop ppa_stream_drop | |
125 #define pa_strerror ppa_strerror | |
126 #define pa_context_get_state ppa_context_get_state | |
127 #define pa_stream_get_state ppa_stream_get_state | |
128 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal | |
129 #define pa_stream_peek ppa_stream_peek | |
130 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait | |
131 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock | |
132 #define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread | |
133 #define pa_context_new ppa_context_new | |
134 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop | |
135 #define pa_context_disconnect ppa_context_disconnect | |
136 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start | |
137 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api | |
138 #define pa_context_set_state_callback ppa_context_set_state_callback | |
139 #define pa_stream_write ppa_stream_write | |
140 #define pa_xfree ppa_xfree | |
141 #define pa_stream_connect_record ppa_stream_connect_record | |
142 #define pa_stream_connect_playback ppa_stream_connect_playback | |
143 #define pa_stream_readable_size ppa_stream_readable_size | |
144 #define pa_stream_writable_size ppa_stream_writable_size | |
145 #define pa_stream_cork ppa_stream_cork | |
146 #define pa_stream_is_suspended ppa_stream_is_suspended | |
147 #define pa_stream_get_device_name ppa_stream_get_device_name | |
148 #define pa_path_get_filename ppa_path_get_filename | |
149 #define pa_get_binary_name ppa_get_binary_name | |
150 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free | |
151 #define pa_context_errno ppa_context_errno | |
152 #define pa_xmalloc ppa_xmalloc | |
153 #define pa_stream_unref ppa_stream_unref | |
154 #define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept | |
155 #define pa_stream_set_write_callback ppa_stream_set_write_callback | |
156 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new | |
157 #define pa_context_connect ppa_context_connect | |
158 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr | |
159 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr | |
160 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec | |
161 #define pa_stream_get_time ppa_stream_get_time | |
162 #define pa_stream_set_read_callback ppa_stream_set_read_callback | |
163 #define pa_stream_set_state_callback ppa_stream_set_state_callback | |
164 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback | |
165 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback | |
166 #define pa_stream_new ppa_stream_new | |
167 #define pa_stream_disconnect ppa_stream_disconnect | |
168 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock | |
169 #define pa_channel_map_init_auto ppa_channel_map_init_auto | |
170 #define pa_channel_map_parse ppa_channel_map_parse | |
171 #define pa_channel_map_snprint ppa_channel_map_snprint | |
172 #define pa_channel_map_equal ppa_channel_map_equal | |
173 #define pa_context_get_server_info ppa_context_get_server_info | |
174 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name | |
175 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list | |
176 #define pa_context_get_source_info_list ppa_context_get_source_info_list | |
177 #define pa_operation_get_state ppa_operation_get_state | |
178 #define pa_operation_unref ppa_operation_unref | |
179 #if PA_CHECK_VERSION(0,9,15) | |
180 #define pa_channel_map_superset ppa_channel_map_superset | |
181 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback | |
182 #endif | |
183 #if PA_CHECK_VERSION(0,9,16) | |
184 #define pa_stream_begin_write ppa_stream_begin_write | |
185 #endif | |
186 | |
187 #endif | |
188 | |
189 #ifndef PATH_MAX | |
190 #define PATH_MAX 4096 | |
191 #endif | |
192 | |
193 typedef struct { | |
194 char *device_name; | |
195 | |
196 ALCuint samples; | |
197 ALCuint frame_size; | |
198 | |
199 RingBuffer *ring; | |
200 | |
201 pa_buffer_attr attr; | |
202 pa_sample_spec spec; | |
203 | |
204 pa_threaded_mainloop *loop; | |
205 | |
206 ALvoid *thread; | |
207 volatile ALboolean killNow; | |
208 | |
209 pa_stream *stream; | |
210 pa_context *context; | |
211 } pulse_data; | |
212 | |
213 typedef struct { | |
214 char *name; | |
215 char *device_name; | |
216 } DevMap; | |
217 | |
218 | |
219 static const ALCchar pulse_device[] = "PulseAudio Default"; | |
220 static DevMap *allDevNameMap; | |
221 static ALuint numDevNames; | |
222 static DevMap *allCaptureDevNameMap; | |
223 static ALuint numCaptureDevNames; | |
224 static pa_context_flags_t pulse_ctx_flags; | |
225 | |
226 | |
227 static void context_state_callback(pa_context *context, void *pdata) | |
228 { | |
229 pa_threaded_mainloop *loop = pdata; | |
230 pa_context_state_t state; | |
231 | |
232 state = pa_context_get_state(context); | |
233 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state)) | |
234 pa_threaded_mainloop_signal(loop, 0); | |
235 } | |
236 | |
237 static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) | |
238 { | |
239 const char *name = "OpenAL Soft"; | |
240 char path_name[PATH_MAX]; | |
241 pa_context_state_t state; | |
242 pa_context *context; | |
243 int err; | |
244 | |
245 if(pa_get_binary_name(path_name, sizeof(path_name))) | |
246 name = pa_path_get_filename(path_name); | |
247 | |
248 context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); | |
249 if(!context) | |
250 { | |
251 ERR("pa_context_new() failed\n"); | |
252 return NULL; | |
253 } | |
254 | |
255 pa_context_set_state_callback(context, context_state_callback, loop); | |
256 | |
257 if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0) | |
258 { | |
259 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY) | |
260 { | |
261 if(!PA_CONTEXT_IS_GOOD(state)) | |
262 { | |
263 err = pa_context_errno(context); | |
264 if(err > 0) err = -err; | |
265 break; | |
266 } | |
267 | |
268 pa_threaded_mainloop_wait(loop); | |
269 } | |
270 } | |
271 pa_context_set_state_callback(context, NULL, NULL); | |
272 | |
273 if(err < 0) | |
274 { | |
275 if(!silent) | |
276 ERR("Context did not connect: %s\n", pa_strerror(err)); | |
277 pa_context_unref(context); | |
278 return NULL; | |
279 } | |
280 | |
281 return context; | |
282 } | |
283 | |
284 | |
285 static ALCboolean pulse_load(void) //{{{ | |
286 { | |
287 ALCboolean ret = ALC_FALSE; | |
288 if(!pa_handle) | |
289 { | |
290 pa_threaded_mainloop *loop; | |
291 | |
292 #ifdef HAVE_DYNLOAD | |
293 | |
294 #ifdef _WIN32 | |
295 #define PALIB "libpulse-0.dll" | |
296 #elif defined(__APPLE__) && defined(__MACH__) | |
297 #define PALIB "libpulse.0.dylib" | |
298 #else | |
299 #define PALIB "libpulse.so.0" | |
300 #endif | |
301 pa_handle = LoadLib(PALIB); | |
302 if(!pa_handle) | |
303 return ALC_FALSE; | |
304 | |
305 #define LOAD_FUNC(x) do { \ | |
306 p##x = GetSymbol(pa_handle, #x); \ | |
307 if(!(p##x)) { \ | |
308 CloseLib(pa_handle); \ | |
309 pa_handle = NULL; \ | |
310 return ALC_FALSE; \ | |
311 } \ | |
312 } while(0) | |
313 LOAD_FUNC(pa_context_unref); | |
314 LOAD_FUNC(pa_sample_spec_valid); | |
315 LOAD_FUNC(pa_stream_drop); | |
316 LOAD_FUNC(pa_strerror); | |
317 LOAD_FUNC(pa_context_get_state); | |
318 LOAD_FUNC(pa_stream_get_state); | |
319 LOAD_FUNC(pa_threaded_mainloop_signal); | |
320 LOAD_FUNC(pa_stream_peek); | |
321 LOAD_FUNC(pa_threaded_mainloop_wait); | |
322 LOAD_FUNC(pa_threaded_mainloop_unlock); | |
323 LOAD_FUNC(pa_threaded_mainloop_in_thread); | |
324 LOAD_FUNC(pa_context_new); | |
325 LOAD_FUNC(pa_threaded_mainloop_stop); | |
326 LOAD_FUNC(pa_context_disconnect); | |
327 LOAD_FUNC(pa_threaded_mainloop_start); | |
328 LOAD_FUNC(pa_threaded_mainloop_get_api); | |
329 LOAD_FUNC(pa_context_set_state_callback); | |
330 LOAD_FUNC(pa_stream_write); | |
331 LOAD_FUNC(pa_xfree); | |
332 LOAD_FUNC(pa_stream_connect_record); | |
333 LOAD_FUNC(pa_stream_connect_playback); | |
334 LOAD_FUNC(pa_stream_readable_size); | |
335 LOAD_FUNC(pa_stream_writable_size); | |
336 LOAD_FUNC(pa_stream_cork); | |
337 LOAD_FUNC(pa_stream_is_suspended); | |
338 LOAD_FUNC(pa_stream_get_device_name); | |
339 LOAD_FUNC(pa_path_get_filename); | |
340 LOAD_FUNC(pa_get_binary_name); | |
341 LOAD_FUNC(pa_threaded_mainloop_free); | |
342 LOAD_FUNC(pa_context_errno); | |
343 LOAD_FUNC(pa_xmalloc); | |
344 LOAD_FUNC(pa_stream_unref); | |
345 LOAD_FUNC(pa_threaded_mainloop_accept); | |
346 LOAD_FUNC(pa_stream_set_write_callback); | |
347 LOAD_FUNC(pa_threaded_mainloop_new); | |
348 LOAD_FUNC(pa_context_connect); | |
349 LOAD_FUNC(pa_stream_set_buffer_attr); | |
350 LOAD_FUNC(pa_stream_get_buffer_attr); | |
351 LOAD_FUNC(pa_stream_get_sample_spec); | |
352 LOAD_FUNC(pa_stream_get_time); | |
353 LOAD_FUNC(pa_stream_set_read_callback); | |
354 LOAD_FUNC(pa_stream_set_state_callback); | |
355 LOAD_FUNC(pa_stream_set_moved_callback); | |
356 LOAD_FUNC(pa_stream_set_underflow_callback); | |
357 LOAD_FUNC(pa_stream_new); | |
358 LOAD_FUNC(pa_stream_disconnect); | |
359 LOAD_FUNC(pa_threaded_mainloop_lock); | |
360 LOAD_FUNC(pa_channel_map_init_auto); | |
361 LOAD_FUNC(pa_channel_map_parse); | |
362 LOAD_FUNC(pa_channel_map_snprint); | |
363 LOAD_FUNC(pa_channel_map_equal); | |
364 LOAD_FUNC(pa_context_get_server_info); | |
365 LOAD_FUNC(pa_context_get_sink_info_by_name); | |
366 LOAD_FUNC(pa_context_get_sink_info_list); | |
367 LOAD_FUNC(pa_context_get_source_info_list); | |
368 LOAD_FUNC(pa_operation_get_state); | |
369 LOAD_FUNC(pa_operation_unref); | |
370 #undef LOAD_FUNC | |
371 #define LOAD_OPTIONAL_FUNC(x) do { \ | |
372 p##x = GetSymbol(pa_handle, #x); \ | |
373 } while(0) | |
374 #if PA_CHECK_VERSION(0,9,15) | |
375 LOAD_OPTIONAL_FUNC(pa_channel_map_superset); | |
376 LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback); | |
377 #endif | |
378 #if PA_CHECK_VERSION(0,9,16) | |
379 LOAD_OPTIONAL_FUNC(pa_stream_begin_write); | |
380 #endif | |
381 #undef LOAD_OPTIONAL_FUNC | |
382 | |
383 #else /* HAVE_DYNLOAD */ | |
384 pa_handle = (void*)0xDEADBEEF; | |
385 #endif | |
386 | |
387 if((loop=pa_threaded_mainloop_new()) && | |
388 pa_threaded_mainloop_start(loop) >= 0) | |
389 { | |
390 pa_context *context; | |
391 | |
392 pa_threaded_mainloop_lock(loop); | |
393 context = connect_context(loop, AL_TRUE); | |
394 if(context) | |
395 { | |
396 ret = ALC_TRUE; | |
397 | |
398 pa_context_disconnect(context); | |
399 pa_context_unref(context); | |
400 } | |
401 pa_threaded_mainloop_unlock(loop); | |
402 pa_threaded_mainloop_stop(loop); | |
403 } | |
404 if(loop) | |
405 pa_threaded_mainloop_free(loop); | |
406 | |
407 if(!ret) | |
408 { | |
409 #ifdef HAVE_DYNLOAD | |
410 CloseLib(pa_handle); | |
411 #endif | |
412 pa_handle = NULL; | |
413 } | |
414 } | |
415 return ret; | |
416 } //}}} | |
417 | |
418 // PulseAudio Event Callbacks //{{{ | |
419 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ | |
420 { | |
421 pa_threaded_mainloop *loop = pdata; | |
422 pa_stream_state_t state; | |
423 | |
424 state = pa_stream_get_state(stream); | |
425 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state)) | |
426 pa_threaded_mainloop_signal(loop, 0); | |
427 }//}}} | |
428 | |
429 static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{ | |
430 { | |
431 ALCdevice *Device = pdata; | |
432 pulse_data *data = Device->ExtraData; | |
433 (void)stream; | |
434 | |
435 pa_threaded_mainloop_signal(data->loop, 0); | |
436 }//}}} | |
437 | |
438 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{ | |
439 { | |
440 ALCdevice *Device = pdata; | |
441 pulse_data *data = Device->ExtraData; | |
442 | |
443 LockDevice(Device); | |
444 | |
445 data->attr = *(pa_stream_get_buffer_attr(stream)); | |
446 Device->UpdateSize = data->attr.minreq / data->frame_size; | |
447 Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize; | |
448 if(Device->NumUpdates <= 1) | |
449 { | |
450 Device->NumUpdates = 1; | |
451 ERR("PulseAudio returned minreq > tlength/2; expect break up\n"); | |
452 } | |
453 | |
454 UnlockDevice(Device); | |
455 }//}}} | |
456 | |
457 static void stream_device_callback(pa_stream *stream, void *pdata) //{{{ | |
458 { | |
459 ALCdevice *Device = pdata; | |
460 pulse_data *data = Device->ExtraData; | |
461 | |
462 free(data->device_name); | |
463 data->device_name = strdup(pa_stream_get_device_name(stream)); | |
464 }//}}} | |
465 | |
466 static void context_state_callback2(pa_context *context, void *pdata) //{{{ | |
467 { | |
468 ALCdevice *Device = pdata; | |
469 pulse_data *data = Device->ExtraData; | |
470 | |
471 if(pa_context_get_state(context) == PA_CONTEXT_FAILED) | |
472 { | |
473 ERR("Received context failure!\n"); | |
474 aluHandleDisconnect(Device); | |
475 } | |
476 pa_threaded_mainloop_signal(data->loop, 0); | |
477 }//}}} | |
478 | |
479 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{ | |
480 { | |
481 ALCdevice *Device = pdata; | |
482 pulse_data *data = Device->ExtraData; | |
483 | |
484 if(pa_stream_get_state(stream) == PA_STREAM_FAILED) | |
485 { | |
486 ERR("Received stream failure!\n"); | |
487 aluHandleDisconnect(Device); | |
488 } | |
489 pa_threaded_mainloop_signal(data->loop, 0); | |
490 }//}}} | |
491 | |
492 static void stream_success_callback(pa_stream *stream, int success, void *pdata) //{{{ | |
493 { | |
494 ALCdevice *Device = pdata; | |
495 pulse_data *data = Device->ExtraData; | |
496 (void)stream; | |
497 (void)success; | |
498 | |
499 pa_threaded_mainloop_signal(data->loop, 0); | |
500 }//}}} | |
501 | |
502 static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ | |
503 { | |
504 ALCdevice *device = pdata; | |
505 pulse_data *data = device->ExtraData; | |
506 char chanmap_str[256] = ""; | |
507 const struct { | |
508 const char *str; | |
509 enum DevFmtChannels chans; | |
510 } chanmaps[] = { | |
511 { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right", | |
512 DevFmtX71 }, | |
513 { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right", | |
514 DevFmtX61 }, | |
515 { "front-left,front-right,front-center,lfe,rear-left,rear-right", | |
516 DevFmtX51 }, | |
517 { "front-left,front-right,front-center,lfe,side-left,side-right", | |
518 DevFmtX51Side }, | |
519 { "front-left,front-right,rear-left,rear-right", DevFmtQuad }, | |
520 { "front-left,front-right", DevFmtStereo }, | |
521 { "mono", DevFmtMono }, | |
522 { NULL, 0 } | |
523 }; | |
524 int i; | |
525 (void)context; | |
526 | |
527 if(eol) | |
528 { | |
529 pa_threaded_mainloop_signal(data->loop, 0); | |
530 return; | |
531 } | |
532 | |
533 for(i = 0;chanmaps[i].str;i++) | |
534 { | |
535 pa_channel_map map; | |
536 if(!pa_channel_map_parse(&map, chanmaps[i].str)) | |
537 continue; | |
538 | |
539 if(pa_channel_map_equal(&info->channel_map, &map) | |
540 #if PA_CHECK_VERSION(0,9,15) | |
541 || (pa_channel_map_superset && | |
542 pa_channel_map_superset(&info->channel_map, &map)) | |
543 #endif | |
544 ) | |
545 { | |
546 device->FmtChans = chanmaps[i].chans; | |
547 return; | |
548 } | |
549 } | |
550 | |
551 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); | |
552 ERR("Failed to find format for channel map:\n %s\n", chanmap_str); | |
553 }//}}} | |
554 | |
555 static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ | |
556 { | |
557 pa_threaded_mainloop *loop = pdata; | |
558 char str[1024]; | |
559 void *temp; | |
560 int count; | |
561 ALuint i; | |
562 | |
563 (void)context; | |
564 | |
565 if(eol) | |
566 { | |
567 pa_threaded_mainloop_signal(loop, 0); | |
568 return; | |
569 } | |
570 | |
571 count = 0; | |
572 do { | |
573 if(count == 0) | |
574 snprintf(str, sizeof(str), "%s", info->description); | |
575 else | |
576 snprintf(str, sizeof(str), "%s #%d", info->description, count+1); | |
577 count++; | |
578 | |
579 for(i = 0;i < numDevNames;i++) | |
580 { | |
581 if(strcmp(str, allDevNameMap[i].name) == 0) | |
582 break; | |
583 } | |
584 } while(i != numDevNames); | |
585 | |
586 temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); | |
587 if(temp) | |
588 { | |
589 allDevNameMap = temp; | |
590 allDevNameMap[numDevNames].name = strdup(str); | |
591 allDevNameMap[numDevNames].device_name = strdup(info->name); | |
592 numDevNames++; | |
593 } | |
594 }//}}} | |
595 | |
596 static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{ | |
597 { | |
598 pa_threaded_mainloop *loop = pdata; | |
599 char str[1024]; | |
600 void *temp; | |
601 int count; | |
602 ALuint i; | |
603 | |
604 (void)context; | |
605 | |
606 if(eol) | |
607 { | |
608 pa_threaded_mainloop_signal(loop, 0); | |
609 return; | |
610 } | |
611 | |
612 count = 0; | |
613 do { | |
614 if(count == 0) | |
615 snprintf(str, sizeof(str), "%s", info->description); | |
616 else | |
617 snprintf(str, sizeof(str), "%s #%d", info->description, count+1); | |
618 count++; | |
619 | |
620 for(i = 0;i < numCaptureDevNames;i++) | |
621 { | |
622 if(strcmp(str, allCaptureDevNameMap[i].name) == 0) | |
623 break; | |
624 } | |
625 } while(i != numCaptureDevNames); | |
626 | |
627 temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); | |
628 if(temp) | |
629 { | |
630 allCaptureDevNameMap = temp; | |
631 allCaptureDevNameMap[numCaptureDevNames].name = strdup(str); | |
632 allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); | |
633 numCaptureDevNames++; | |
634 } | |
635 }//}}} | |
636 //}}} | |
637 | |
638 // PulseAudio I/O Callbacks //{{{ | |
639 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ | |
640 { | |
641 ALCdevice *Device = pdata; | |
642 pulse_data *data = Device->ExtraData; | |
643 (void)stream; | |
644 (void)len; | |
645 | |
646 pa_threaded_mainloop_signal(data->loop, 0); | |
647 } //}}} | |
648 //}}} | |
649 | |
650 static ALuint PulseProc(ALvoid *param) | |
651 { | |
652 ALCdevice *Device = param; | |
653 pulse_data *data = Device->ExtraData; | |
654 ssize_t len; | |
655 | |
656 SetRTPriority(); | |
657 | |
658 pa_threaded_mainloop_lock(data->loop); | |
659 do { | |
660 len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); | |
661 len -= len%(Device->UpdateSize*data->frame_size); | |
662 if(len == 0) | |
663 { | |
664 pa_threaded_mainloop_wait(data->loop); | |
665 continue; | |
666 } | |
667 | |
668 while(len > 0) | |
669 { | |
670 size_t newlen = len; | |
671 void *buf; | |
672 pa_free_cb_t free_func = NULL; | |
673 | |
674 #if PA_CHECK_VERSION(0,9,16) | |
675 if(!pa_stream_begin_write || | |
676 pa_stream_begin_write(data->stream, &buf, &newlen) < 0) | |
677 #endif | |
678 { | |
679 buf = pa_xmalloc(newlen); | |
680 free_func = pa_xfree; | |
681 } | |
682 pa_threaded_mainloop_unlock(data->loop); | |
683 | |
684 aluMixData(Device, buf, newlen/data->frame_size); | |
685 | |
686 pa_threaded_mainloop_lock(data->loop); | |
687 pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); | |
688 len -= newlen; | |
689 } | |
690 } while(Device->Connected && !data->killNow); | |
691 pa_threaded_mainloop_unlock(data->loop); | |
692 | |
693 return 0; | |
694 } | |
695 | |
696 static pa_stream *connect_playback_stream(ALCdevice *device, | |
697 pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, | |
698 pa_channel_map *chanmap) | |
699 { | |
700 pulse_data *data = device->ExtraData; | |
701 pa_stream_state_t state; | |
702 pa_stream *stream; | |
703 | |
704 stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap); | |
705 if(!stream) | |
706 { | |
707 ERR("pa_stream_new() failed: %s\n", | |
708 pa_strerror(pa_context_errno(data->context))); | |
709 return NULL; | |
710 } | |
711 | |
712 pa_stream_set_state_callback(stream, stream_state_callback, data->loop); | |
713 | |
714 if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0) | |
715 { | |
716 ERR("Stream did not connect: %s\n", | |
717 pa_strerror(pa_context_errno(data->context))); | |
718 pa_stream_unref(stream); | |
719 return NULL; | |
720 } | |
721 | |
722 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) | |
723 { | |
724 if(!PA_STREAM_IS_GOOD(state)) | |
725 { | |
726 ERR("Stream did not get ready: %s\n", | |
727 pa_strerror(pa_context_errno(data->context))); | |
728 pa_stream_unref(stream); | |
729 return NULL; | |
730 } | |
731 | |
732 pa_threaded_mainloop_wait(data->loop); | |
733 } | |
734 pa_stream_set_state_callback(stream, NULL, NULL); | |
735 | |
736 return stream; | |
737 } | |
738 | |
739 static void probe_devices(ALboolean capture) | |
740 { | |
741 pa_threaded_mainloop *loop; | |
742 | |
743 if(capture == AL_FALSE) | |
744 allDevNameMap = malloc(sizeof(DevMap) * 1); | |
745 else | |
746 allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); | |
747 | |
748 if((loop=pa_threaded_mainloop_new()) && | |
749 pa_threaded_mainloop_start(loop) >= 0) | |
750 { | |
751 pa_context *context; | |
752 | |
753 pa_threaded_mainloop_lock(loop); | |
754 context = connect_context(loop, AL_FALSE); | |
755 if(context) | |
756 { | |
757 pa_operation *o; | |
758 | |
759 if(capture == AL_FALSE) | |
760 o = pa_context_get_sink_info_list(context, sink_device_callback, loop); | |
761 else | |
762 o = pa_context_get_source_info_list(context, source_device_callback, loop); | |
763 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) | |
764 pa_threaded_mainloop_wait(loop); | |
765 pa_operation_unref(o); | |
766 | |
767 pa_context_disconnect(context); | |
768 pa_context_unref(context); | |
769 } | |
770 pa_threaded_mainloop_unlock(loop); | |
771 pa_threaded_mainloop_stop(loop); | |
772 } | |
773 if(loop) | |
774 pa_threaded_mainloop_free(loop); | |
775 } | |
776 | |
777 | |
778 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{ | |
779 { | |
780 pulse_data *data = pa_xmalloc(sizeof(pulse_data)); | |
781 memset(data, 0, sizeof(*data)); | |
782 | |
783 if(!(data->loop = pa_threaded_mainloop_new())) | |
784 { | |
785 ERR("pa_threaded_mainloop_new() failed!\n"); | |
786 goto out; | |
787 } | |
788 if(pa_threaded_mainloop_start(data->loop) < 0) | |
789 { | |
790 ERR("pa_threaded_mainloop_start() failed\n"); | |
791 goto out; | |
792 } | |
793 | |
794 pa_threaded_mainloop_lock(data->loop); | |
795 device->ExtraData = data; | |
796 | |
797 data->context = connect_context(data->loop, AL_FALSE); | |
798 if(!data->context) | |
799 { | |
800 pa_threaded_mainloop_unlock(data->loop); | |
801 goto out; | |
802 } | |
803 pa_context_set_state_callback(data->context, context_state_callback2, device); | |
804 | |
805 device->szDeviceName = strdup(device_name); | |
806 | |
807 pa_threaded_mainloop_unlock(data->loop); | |
808 return ALC_TRUE; | |
809 | |
810 out: | |
811 if(data->loop) | |
812 { | |
813 pa_threaded_mainloop_stop(data->loop); | |
814 pa_threaded_mainloop_free(data->loop); | |
815 } | |
816 | |
817 device->ExtraData = NULL; | |
818 pa_xfree(data); | |
819 return ALC_FALSE; | |
820 } //}}} | |
821 | |
822 static void pulse_close(ALCdevice *device) //{{{ | |
823 { | |
824 pulse_data *data = device->ExtraData; | |
825 | |
826 pa_threaded_mainloop_lock(data->loop); | |
827 | |
828 if(data->stream) | |
829 { | |
830 pa_stream_disconnect(data->stream); | |
831 pa_stream_unref(data->stream); | |
832 } | |
833 | |
834 pa_context_disconnect(data->context); | |
835 pa_context_unref(data->context); | |
836 | |
837 pa_threaded_mainloop_unlock(data->loop); | |
838 | |
839 pa_threaded_mainloop_stop(data->loop); | |
840 pa_threaded_mainloop_free(data->loop); | |
841 | |
842 DestroyRingBuffer(data->ring); | |
843 free(data->device_name); | |
844 | |
845 device->ExtraData = NULL; | |
846 pa_xfree(data); | |
847 } //}}} | |
848 //}}} | |
849 | |
850 // OpenAL {{{ | |
851 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{ | |
852 { | |
853 char *pulse_name = NULL; | |
854 pa_sample_spec spec; | |
855 pulse_data *data; | |
856 | |
857 if(!allDevNameMap) | |
858 probe_devices(AL_FALSE); | |
859 | |
860 if(!device_name) | |
861 device_name = pulse_device; | |
862 else if(strcmp(device_name, pulse_device) != 0) | |
863 { | |
864 ALuint i; | |
865 | |
866 for(i = 0;i < numDevNames;i++) | |
867 { | |
868 if(strcmp(device_name, allDevNameMap[i].name) == 0) | |
869 { | |
870 pulse_name = allDevNameMap[i].device_name; | |
871 break; | |
872 } | |
873 } | |
874 if(i == numDevNames) | |
875 return ALC_FALSE; | |
876 } | |
877 | |
878 if(pulse_open(device, device_name) == ALC_FALSE) | |
879 return ALC_FALSE; | |
880 | |
881 data = device->ExtraData; | |
882 | |
883 pa_threaded_mainloop_lock(data->loop); | |
884 | |
885 spec.format = PA_SAMPLE_S16NE; | |
886 spec.rate = 44100; | |
887 spec.channels = 2; | |
888 | |
889 data->device_name = pulse_name; | |
890 pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL); | |
891 if(!stream) | |
892 { | |
893 pa_threaded_mainloop_unlock(data->loop); | |
894 goto fail; | |
895 } | |
896 | |
897 if(pa_stream_is_suspended(stream)) | |
898 { | |
899 ERR("Device is suspended\n"); | |
900 pa_stream_disconnect(stream); | |
901 pa_stream_unref(stream); | |
902 pa_threaded_mainloop_unlock(data->loop); | |
903 goto fail; | |
904 } | |
905 data->device_name = strdup(pa_stream_get_device_name(stream)); | |
906 | |
907 pa_stream_disconnect(stream); | |
908 pa_stream_unref(stream); | |
909 | |
910 pa_threaded_mainloop_unlock(data->loop); | |
911 | |
912 return ALC_TRUE; | |
913 | |
914 fail: | |
915 pulse_close(device); | |
916 return ALC_FALSE; | |
917 } //}}} | |
918 | |
919 static void pulse_close_playback(ALCdevice *device) //{{{ | |
920 { | |
921 pulse_close(device); | |
922 } //}}} | |
923 | |
924 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ | |
925 { | |
926 pulse_data *data = device->ExtraData; | |
927 pa_stream_flags_t flags = 0; | |
928 pa_channel_map chanmap; | |
929 | |
930 pa_threaded_mainloop_lock(data->loop); | |
931 | |
932 if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) | |
933 { | |
934 pa_operation *o; | |
935 o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); | |
936 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) | |
937 pa_threaded_mainloop_wait(data->loop); | |
938 pa_operation_unref(o); | |
939 } | |
940 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) | |
941 flags |= PA_STREAM_FIX_RATE; | |
942 | |
943 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); | |
944 data->attr.prebuf = -1; | |
945 data->attr.fragsize = -1; | |
946 data->attr.minreq = device->UpdateSize * data->frame_size; | |
947 data->attr.tlength = data->attr.minreq * device->NumUpdates; | |
948 if(data->attr.tlength < data->attr.minreq*2) | |
949 data->attr.tlength = data->attr.minreq*2; | |
950 data->attr.maxlength = data->attr.tlength; | |
951 flags |= PA_STREAM_EARLY_REQUESTS; | |
952 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; | |
953 | |
954 switch(device->FmtType) | |
955 { | |
956 case DevFmtByte: | |
957 device->FmtType = DevFmtUByte; | |
958 /* fall-through */ | |
959 case DevFmtUByte: | |
960 data->spec.format = PA_SAMPLE_U8; | |
961 break; | |
962 case DevFmtUShort: | |
963 device->FmtType = DevFmtShort; | |
964 /* fall-through */ | |
965 case DevFmtShort: | |
966 data->spec.format = PA_SAMPLE_S16NE; | |
967 break; | |
968 case DevFmtFloat: | |
969 data->spec.format = PA_SAMPLE_FLOAT32NE; | |
970 break; | |
971 } | |
972 data->spec.rate = device->Frequency; | |
973 data->spec.channels = ChannelsFromDevFmt(device->FmtChans); | |
974 | |
975 if(pa_sample_spec_valid(&data->spec) == 0) | |
976 { | |
977 ERR("Invalid sample format\n"); | |
978 pa_threaded_mainloop_unlock(data->loop); | |
979 return ALC_FALSE; | |
980 } | |
981 | |
982 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) | |
983 { | |
984 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); | |
985 pa_threaded_mainloop_unlock(data->loop); | |
986 return ALC_FALSE; | |
987 } | |
988 SetDefaultWFXChannelOrder(device); | |
989 | |
990 data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); | |
991 if(!data->stream) | |
992 { | |
993 pa_threaded_mainloop_unlock(data->loop); | |
994 return ALC_FALSE; | |
995 } | |
996 | |
997 pa_stream_set_state_callback(data->stream, stream_state_callback2, device); | |
998 | |
999 data->spec = *(pa_stream_get_sample_spec(data->stream)); | |
1000 if(device->Frequency != data->spec.rate) | |
1001 { | |
1002 pa_operation *o; | |
1003 | |
1004 if((device->Flags&DEVICE_FREQUENCY_REQUEST)) | |
1005 ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); | |
1006 device->Flags &= ~DEVICE_FREQUENCY_REQUEST; | |
1007 | |
1008 /* Server updated our playback rate, so modify the buffer attribs | |
1009 * accordingly. */ | |
1010 data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * | |
1011 data->spec.rate / device->Frequency * data->frame_size; | |
1012 data->attr.tlength = data->attr.minreq * device->NumUpdates; | |
1013 data->attr.maxlength = data->attr.tlength; | |
1014 | |
1015 o = pa_stream_set_buffer_attr(data->stream, &data->attr, | |
1016 stream_success_callback, device); | |
1017 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) | |
1018 pa_threaded_mainloop_wait(data->loop); | |
1019 pa_operation_unref(o); | |
1020 | |
1021 device->Frequency = data->spec.rate; | |
1022 } | |
1023 | |
1024 stream_buffer_attr_callback(data->stream, device); | |
1025 #if PA_CHECK_VERSION(0,9,15) | |
1026 if(pa_stream_set_buffer_attr_callback) | |
1027 pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); | |
1028 #endif | |
1029 pa_stream_set_moved_callback(data->stream, stream_device_callback, device); | |
1030 pa_stream_set_write_callback(data->stream, stream_write_callback, device); | |
1031 pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); | |
1032 | |
1033 data->thread = StartThread(PulseProc, device); | |
1034 if(!data->thread) | |
1035 { | |
1036 #if PA_CHECK_VERSION(0,9,15) | |
1037 if(pa_stream_set_buffer_attr_callback) | |
1038 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); | |
1039 #endif | |
1040 pa_stream_set_moved_callback(data->stream, NULL, NULL); | |
1041 pa_stream_set_write_callback(data->stream, NULL, NULL); | |
1042 pa_stream_set_underflow_callback(data->stream, NULL, NULL); | |
1043 pa_stream_disconnect(data->stream); | |
1044 pa_stream_unref(data->stream); | |
1045 data->stream = NULL; | |
1046 | |
1047 pa_threaded_mainloop_unlock(data->loop); | |
1048 return ALC_FALSE; | |
1049 } | |
1050 | |
1051 pa_threaded_mainloop_unlock(data->loop); | |
1052 return ALC_TRUE; | |
1053 } //}}} | |
1054 | |
1055 static void pulse_stop_playback(ALCdevice *device) //{{{ | |
1056 { | |
1057 pulse_data *data = device->ExtraData; | |
1058 | |
1059 if(!data->stream) | |
1060 return; | |
1061 | |
1062 data->killNow = AL_TRUE; | |
1063 if(data->thread) | |
1064 { | |
1065 pa_threaded_mainloop_signal(data->loop, 0); | |
1066 StopThread(data->thread); | |
1067 data->thread = NULL; | |
1068 } | |
1069 data->killNow = AL_FALSE; | |
1070 | |
1071 pa_threaded_mainloop_lock(data->loop); | |
1072 | |
1073 #if PA_CHECK_VERSION(0,9,15) | |
1074 if(pa_stream_set_buffer_attr_callback) | |
1075 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); | |
1076 #endif | |
1077 pa_stream_set_moved_callback(data->stream, NULL, NULL); | |
1078 pa_stream_set_write_callback(data->stream, NULL, NULL); | |
1079 pa_stream_set_underflow_callback(data->stream, NULL, NULL); | |
1080 pa_stream_disconnect(data->stream); | |
1081 pa_stream_unref(data->stream); | |
1082 data->stream = NULL; | |
1083 | |
1084 pa_threaded_mainloop_unlock(data->loop); | |
1085 } //}}} | |
1086 | |
1087 | |
1088 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ | |
1089 { | |
1090 char *pulse_name = NULL; | |
1091 pulse_data *data; | |
1092 pa_stream_flags_t flags = 0; | |
1093 pa_stream_state_t state; | |
1094 pa_channel_map chanmap; | |
1095 | |
1096 if(!allCaptureDevNameMap) | |
1097 probe_devices(AL_TRUE); | |
1098 | |
1099 if(!device_name) | |
1100 device_name = pulse_device; | |
1101 else if(strcmp(device_name, pulse_device) != 0) | |
1102 { | |
1103 ALuint i; | |
1104 | |
1105 for(i = 0;i < numCaptureDevNames;i++) | |
1106 { | |
1107 if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) | |
1108 { | |
1109 pulse_name = allCaptureDevNameMap[i].device_name; | |
1110 break; | |
1111 } | |
1112 } | |
1113 if(i == numCaptureDevNames) | |
1114 return ALC_FALSE; | |
1115 } | |
1116 | |
1117 if(pulse_open(device, device_name) == ALC_FALSE) | |
1118 return ALC_FALSE; | |
1119 | |
1120 data = device->ExtraData; | |
1121 pa_threaded_mainloop_lock(data->loop); | |
1122 | |
1123 data->samples = device->UpdateSize * device->NumUpdates; | |
1124 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); | |
1125 if(data->samples < 100 * device->Frequency / 1000) | |
1126 data->samples = 100 * device->Frequency / 1000; | |
1127 | |
1128 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) | |
1129 { | |
1130 pa_threaded_mainloop_unlock(data->loop); | |
1131 goto fail; | |
1132 } | |
1133 | |
1134 data->attr.minreq = -1; | |
1135 data->attr.prebuf = -1; | |
1136 data->attr.maxlength = data->samples * data->frame_size; | |
1137 data->attr.tlength = -1; | |
1138 data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * | |
1139 data->frame_size; | |
1140 | |
1141 data->spec.rate = device->Frequency; | |
1142 data->spec.channels = ChannelsFromDevFmt(device->FmtChans); | |
1143 | |
1144 switch(device->FmtType) | |
1145 { | |
1146 case DevFmtUByte: | |
1147 data->spec.format = PA_SAMPLE_U8; | |
1148 break; | |
1149 case DevFmtShort: | |
1150 data->spec.format = PA_SAMPLE_S16NE; | |
1151 break; | |
1152 case DevFmtFloat: | |
1153 data->spec.format = PA_SAMPLE_FLOAT32NE; | |
1154 break; | |
1155 case DevFmtByte: | |
1156 case DevFmtUShort: | |
1157 ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); | |
1158 pa_threaded_mainloop_unlock(data->loop); | |
1159 goto fail; | |
1160 } | |
1161 | |
1162 if(pa_sample_spec_valid(&data->spec) == 0) | |
1163 { | |
1164 ERR("Invalid sample format\n"); | |
1165 pa_threaded_mainloop_unlock(data->loop); | |
1166 goto fail; | |
1167 } | |
1168 | |
1169 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) | |
1170 { | |
1171 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); | |
1172 pa_threaded_mainloop_unlock(data->loop); | |
1173 goto fail; | |
1174 } | |
1175 | |
1176 data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); | |
1177 if(!data->stream) | |
1178 { | |
1179 ERR("pa_stream_new() failed: %s\n", | |
1180 pa_strerror(pa_context_errno(data->context))); | |
1181 | |
1182 pa_threaded_mainloop_unlock(data->loop); | |
1183 goto fail; | |
1184 } | |
1185 | |
1186 pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); | |
1187 | |
1188 flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; | |
1189 if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) | |
1190 { | |
1191 ERR("Stream did not connect: %s\n", | |
1192 pa_strerror(pa_context_errno(data->context))); | |
1193 | |
1194 pa_stream_unref(data->stream); | |
1195 data->stream = NULL; | |
1196 | |
1197 pa_threaded_mainloop_unlock(data->loop); | |
1198 goto fail; | |
1199 } | |
1200 | |
1201 while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) | |
1202 { | |
1203 if(!PA_STREAM_IS_GOOD(state)) | |
1204 { | |
1205 ERR("Stream did not get ready: %s\n", | |
1206 pa_strerror(pa_context_errno(data->context))); | |
1207 | |
1208 pa_stream_unref(data->stream); | |
1209 data->stream = NULL; | |
1210 | |
1211 pa_threaded_mainloop_unlock(data->loop); | |
1212 goto fail; | |
1213 } | |
1214 | |
1215 pa_threaded_mainloop_wait(data->loop); | |
1216 } | |
1217 pa_stream_set_state_callback(data->stream, stream_state_callback2, device); | |
1218 | |
1219 pa_threaded_mainloop_unlock(data->loop); | |
1220 return ALC_TRUE; | |
1221 | |
1222 fail: | |
1223 pulse_close(device); | |
1224 return ALC_FALSE; | |
1225 } //}}} | |
1226 | |
1227 static void pulse_close_capture(ALCdevice *device) //{{{ | |
1228 { | |
1229 pulse_close(device); | |
1230 } //}}} | |
1231 | |
1232 static void pulse_start_capture(ALCdevice *device) //{{{ | |
1233 { | |
1234 pulse_data *data = device->ExtraData; | |
1235 pa_operation *o; | |
1236 | |
1237 pa_threaded_mainloop_lock(data->loop); | |
1238 o = pa_stream_cork(data->stream, 0, stream_success_callback, device); | |
1239 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) | |
1240 pa_threaded_mainloop_wait(data->loop); | |
1241 pa_operation_unref(o); | |
1242 pa_threaded_mainloop_unlock(data->loop); | |
1243 } //}}} | |
1244 | |
1245 static void pulse_stop_capture(ALCdevice *device) //{{{ | |
1246 { | |
1247 pulse_data *data = device->ExtraData; | |
1248 pa_operation *o; | |
1249 | |
1250 pa_threaded_mainloop_lock(data->loop); | |
1251 o = pa_stream_cork(data->stream, 1, stream_success_callback, device); | |
1252 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) | |
1253 pa_threaded_mainloop_wait(data->loop); | |
1254 pa_operation_unref(o); | |
1255 pa_threaded_mainloop_unlock(data->loop); | |
1256 } //}}} | |
1257 | |
1258 static ALCuint pulse_available_samples(ALCdevice *device) //{{{ | |
1259 { | |
1260 pulse_data *data = device->ExtraData; | |
1261 size_t samples; | |
1262 | |
1263 pa_threaded_mainloop_lock(data->loop); | |
1264 /* Capture is done in fragment-sized chunks, so we loop until we get all | |
1265 * that's available */ | |
1266 samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); | |
1267 while(samples > 0) | |
1268 { | |
1269 const void *buf; | |
1270 size_t length; | |
1271 | |
1272 if(pa_stream_peek(data->stream, &buf, &length) < 0) | |
1273 { | |
1274 ERR("pa_stream_peek() failed: %s\n", | |
1275 pa_strerror(pa_context_errno(data->context))); | |
1276 break; | |
1277 } | |
1278 | |
1279 WriteRingBuffer(data->ring, buf, length/data->frame_size); | |
1280 samples -= length; | |
1281 | |
1282 pa_stream_drop(data->stream); | |
1283 } | |
1284 pa_threaded_mainloop_unlock(data->loop); | |
1285 | |
1286 return RingBufferSize(data->ring); | |
1287 } //}}} | |
1288 | |
1289 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ | |
1290 { | |
1291 pulse_data *data = device->ExtraData; | |
1292 | |
1293 if(pulse_available_samples(device) >= samples) | |
1294 ReadRingBuffer(data->ring, buffer, samples); | |
1295 else | |
1296 alcSetError(device, ALC_INVALID_VALUE); | |
1297 } //}}} | |
1298 | |
1299 | |
1300 static const BackendFuncs pulse_funcs = { //{{{ | |
1301 pulse_open_playback, | |
1302 pulse_close_playback, | |
1303 pulse_reset_playback, | |
1304 pulse_stop_playback, | |
1305 pulse_open_capture, | |
1306 pulse_close_capture, | |
1307 pulse_start_capture, | |
1308 pulse_stop_capture, | |
1309 pulse_capture_samples, | |
1310 pulse_available_samples | |
1311 }; //}}} | |
1312 | |
1313 ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{ | |
1314 { | |
1315 if(!pulse_load()) | |
1316 return ALC_FALSE; | |
1317 | |
1318 *func_list = pulse_funcs; | |
1319 | |
1320 pulse_ctx_flags = 0; | |
1321 if(!GetConfigValueBool("pulse", "spawn-server", 0)) | |
1322 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; | |
1323 | |
1324 return ALC_TRUE; | |
1325 } //}}} | |
1326 | |
1327 void alc_pulse_deinit(void) //{{{ | |
1328 { | |
1329 ALuint i; | |
1330 | |
1331 for(i = 0;i < numDevNames;++i) | |
1332 { | |
1333 free(allDevNameMap[i].name); | |
1334 free(allDevNameMap[i].device_name); | |
1335 } | |
1336 free(allDevNameMap); | |
1337 allDevNameMap = NULL; | |
1338 numDevNames = 0; | |
1339 | |
1340 for(i = 0;i < numCaptureDevNames;++i) | |
1341 { | |
1342 free(allCaptureDevNameMap[i].name); | |
1343 free(allCaptureDevNameMap[i].device_name); | |
1344 } | |
1345 free(allCaptureDevNameMap); | |
1346 allCaptureDevNameMap = NULL; | |
1347 numCaptureDevNames = 0; | |
1348 | |
1349 #ifdef HAVE_DYNLOAD | |
1350 if(pa_handle) | |
1351 CloseLib(pa_handle); | |
1352 pa_handle = NULL; | |
1353 #endif | |
1354 } //}}} | |
1355 | |
1356 void alc_pulse_probe(enum DevProbe type) //{{{ | |
1357 { | |
1358 pa_threaded_mainloop *loop; | |
1359 ALuint i; | |
1360 | |
1361 switch(type) | |
1362 { | |
1363 case DEVICE_PROBE: | |
1364 if((loop=pa_threaded_mainloop_new()) && | |
1365 pa_threaded_mainloop_start(loop) >= 0) | |
1366 { | |
1367 pa_context *context; | |
1368 | |
1369 pa_threaded_mainloop_lock(loop); | |
1370 context = connect_context(loop, AL_FALSE); | |
1371 if(context) | |
1372 { | |
1373 AppendDeviceList(pulse_device); | |
1374 | |
1375 pa_context_disconnect(context); | |
1376 pa_context_unref(context); | |
1377 } | |
1378 pa_threaded_mainloop_unlock(loop); | |
1379 pa_threaded_mainloop_stop(loop); | |
1380 } | |
1381 if(loop) | |
1382 pa_threaded_mainloop_free(loop); | |
1383 break; | |
1384 | |
1385 case ALL_DEVICE_PROBE: | |
1386 for(i = 0;i < numDevNames;++i) | |
1387 { | |
1388 free(allDevNameMap[i].name); | |
1389 free(allDevNameMap[i].device_name); | |
1390 } | |
1391 free(allDevNameMap); | |
1392 allDevNameMap = NULL; | |
1393 numDevNames = 0; | |
1394 | |
1395 probe_devices(AL_FALSE); | |
1396 | |
1397 for(i = 0;i < numDevNames;i++) | |
1398 AppendAllDeviceList(allDevNameMap[i].name); | |
1399 break; | |
1400 | |
1401 case CAPTURE_DEVICE_PROBE: | |
1402 for(i = 0;i < numCaptureDevNames;++i) | |
1403 { | |
1404 free(allCaptureDevNameMap[i].name); | |
1405 free(allCaptureDevNameMap[i].device_name); | |
1406 } | |
1407 free(allCaptureDevNameMap); | |
1408 allCaptureDevNameMap = NULL; | |
1409 numCaptureDevNames = 0; | |
1410 | |
1411 probe_devices(AL_TRUE); | |
1412 | |
1413 for(i = 0;i < numCaptureDevNames;i++) | |
1414 AppendCaptureDeviceList(allCaptureDevNameMap[i].name); | |
1415 break; | |
1416 } | |
1417 } //}}} | |
1418 //}}} |