Try to find a compatible format from Pulse's default sink
[openal-soft/openal-hmr.git] / Alc / pulseaudio.c
blob23ee1e55aae624eb57cf975f8efe7e11efcd91c5
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "alMain.h"
24 #ifdef HAVE_DLFCN_H
25 #include <dlfcn.h>
26 #endif
28 #include <pulse/pulseaudio.h>
30 #if PA_API_VERSION == 11
31 #define PA_STREAM_ADJUST_LATENCY 0x2000U
32 static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
34 return (x == PA_STREAM_CREATING || x == PA_STREAM_READY);
36 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
38 return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING ||
39 x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY);
41 #define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
42 #define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
43 #elif PA_API_VERSION != 12
44 #error Invalid PulseAudio API version
45 #endif
47 #ifndef PA_CHECK_VERSION
48 #define PA_CHECK_VERSION(major,minor,micro) \
49 ((PA_MAJOR > (major)) || \
50 (PA_MAJOR == (major) && PA_MINOR > (minor)) || \
51 (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
52 #endif
54 static void *pa_handle;
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_cork);
79 MAKE_FUNC(pa_path_get_filename);
80 MAKE_FUNC(pa_get_binary_name);
81 MAKE_FUNC(pa_threaded_mainloop_free);
82 MAKE_FUNC(pa_context_errno);
83 MAKE_FUNC(pa_xmalloc);
84 MAKE_FUNC(pa_stream_unref);
85 MAKE_FUNC(pa_threaded_mainloop_accept);
86 MAKE_FUNC(pa_stream_set_write_callback);
87 MAKE_FUNC(pa_threaded_mainloop_new);
88 MAKE_FUNC(pa_context_connect);
89 MAKE_FUNC(pa_stream_set_buffer_attr);
90 MAKE_FUNC(pa_stream_get_buffer_attr);
91 MAKE_FUNC(pa_stream_get_sample_spec);
92 MAKE_FUNC(pa_stream_set_read_callback);
93 MAKE_FUNC(pa_stream_set_state_callback);
94 MAKE_FUNC(pa_stream_new);
95 MAKE_FUNC(pa_stream_disconnect);
96 MAKE_FUNC(pa_threaded_mainloop_lock);
97 MAKE_FUNC(pa_channel_map_init_auto);
98 MAKE_FUNC(pa_channel_map_parse);
99 MAKE_FUNC(pa_channel_map_snprint);
100 MAKE_FUNC(pa_channel_map_superset);
101 MAKE_FUNC(pa_context_get_server_info);
102 MAKE_FUNC(pa_context_get_sink_info_by_name);
103 MAKE_FUNC(pa_operation_get_state);
104 MAKE_FUNC(pa_operation_unref);
105 #if PA_CHECK_VERSION(0,9,15)
106 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
107 #endif
108 #if PA_CHECK_VERSION(0,9,16)
109 MAKE_FUNC(pa_stream_begin_write);
110 #endif
111 #undef MAKE_FUNC
113 #ifndef PATH_MAX
114 #define PATH_MAX 4096
115 #endif
117 typedef struct {
118 ALCuint samples;
119 ALCuint frame_size;
121 RingBuffer *ring;
123 pa_buffer_attr attr;
124 pa_sample_spec spec;
126 char path_name[PATH_MAX];
127 const char *context_name;
128 const char *stream_name;
130 pa_threaded_mainloop *loop;
132 pa_stream *stream;
133 pa_context *context;
134 } pulse_data;
136 static const ALCchar pulse_device[] = "PulseAudio Software";
137 static const ALCchar pulse_capture_device[] = "PulseAudio Capture";
138 static volatile ALuint load_count;
141 void *pulse_load(void) //{{{
143 if(load_count == 0)
145 #ifdef _WIN32
146 pa_handle = LoadLibrary("libpulse-0.dll");
147 #define LOAD_FUNC(x) do { \
148 p##x = GetProcAddress(pa_handle, #x); \
149 if(!(p##x)) { \
150 AL_PRINT("Could not load %s from libpulse-0.dll\n", #x); \
151 FreeLibrary(pa_handle); \
152 pa_handle = NULL; \
153 return NULL; \
155 } while(0)
156 #define LOAD_OPTIONAL_FUNC(x) do { \
157 p##x = GetProcAddress(pa_handle, #x); \
158 } while(0)
160 #elif defined (HAVE_DLFCN_H)
162 const char *err;
163 #if defined(__APPLE__) && defined(__MACH__)
164 pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW);
165 #else
166 pa_handle = dlopen("libpulse.so.0", RTLD_NOW);
167 #endif
168 dlerror();
170 #define LOAD_FUNC(x) do { \
171 p##x = dlsym(pa_handle, #x); \
172 if((err=dlerror()) != NULL) { \
173 AL_PRINT("Could not load %s from libpulse: %s\n", #x, err); \
174 dlclose(pa_handle); \
175 pa_handle = NULL; \
176 return NULL; \
178 } while(0)
179 #define LOAD_OPTIONAL_FUNC(x) do { \
180 p##x = dlsym(pa_handle, #x); \
181 if((err=dlerror()) != NULL) { \
182 p##x = NULL; \
184 } while(0)
186 #else
188 pa_handle = (void*)0xDEADBEEF;
189 #define LOAD_FUNC(x) p##x = (x)
190 #define LOAD_OPTIONAL_FUNC(x) p##x = (x)
192 #endif
193 if(!pa_handle)
194 return NULL;
196 LOAD_FUNC(pa_context_unref);
197 LOAD_FUNC(pa_sample_spec_valid);
198 LOAD_FUNC(pa_stream_drop);
199 LOAD_FUNC(pa_strerror);
200 LOAD_FUNC(pa_context_get_state);
201 LOAD_FUNC(pa_stream_get_state);
202 LOAD_FUNC(pa_threaded_mainloop_signal);
203 LOAD_FUNC(pa_stream_peek);
204 LOAD_FUNC(pa_threaded_mainloop_wait);
205 LOAD_FUNC(pa_threaded_mainloop_unlock);
206 LOAD_FUNC(pa_threaded_mainloop_in_thread);
207 LOAD_FUNC(pa_context_new);
208 LOAD_FUNC(pa_threaded_mainloop_stop);
209 LOAD_FUNC(pa_context_disconnect);
210 LOAD_FUNC(pa_threaded_mainloop_start);
211 LOAD_FUNC(pa_threaded_mainloop_get_api);
212 LOAD_FUNC(pa_context_set_state_callback);
213 LOAD_FUNC(pa_stream_write);
214 LOAD_FUNC(pa_xfree);
215 LOAD_FUNC(pa_stream_connect_record);
216 LOAD_FUNC(pa_stream_connect_playback);
217 LOAD_FUNC(pa_stream_readable_size);
218 LOAD_FUNC(pa_stream_cork);
219 LOAD_FUNC(pa_path_get_filename);
220 LOAD_FUNC(pa_get_binary_name);
221 LOAD_FUNC(pa_threaded_mainloop_free);
222 LOAD_FUNC(pa_context_errno);
223 LOAD_FUNC(pa_xmalloc);
224 LOAD_FUNC(pa_stream_unref);
225 LOAD_FUNC(pa_threaded_mainloop_accept);
226 LOAD_FUNC(pa_stream_set_write_callback);
227 LOAD_FUNC(pa_threaded_mainloop_new);
228 LOAD_FUNC(pa_context_connect);
229 LOAD_FUNC(pa_stream_set_buffer_attr);
230 LOAD_FUNC(pa_stream_get_buffer_attr);
231 LOAD_FUNC(pa_stream_get_sample_spec);
232 LOAD_FUNC(pa_stream_set_read_callback);
233 LOAD_FUNC(pa_stream_set_state_callback);
234 LOAD_FUNC(pa_stream_new);
235 LOAD_FUNC(pa_stream_disconnect);
236 LOAD_FUNC(pa_threaded_mainloop_lock);
237 LOAD_FUNC(pa_channel_map_init_auto);
238 LOAD_FUNC(pa_channel_map_parse);
239 LOAD_FUNC(pa_channel_map_snprint);
240 LOAD_FUNC(pa_channel_map_superset);
241 LOAD_FUNC(pa_context_get_server_info);
242 LOAD_FUNC(pa_context_get_sink_info_by_name);
243 LOAD_FUNC(pa_operation_get_state);
244 LOAD_FUNC(pa_operation_unref);
245 #if PA_CHECK_VERSION(0,9,15)
246 LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback);
247 #endif
248 #if PA_CHECK_VERSION(0,9,16)
249 LOAD_OPTIONAL_FUNC(pa_stream_begin_write);
250 #endif
252 #undef LOAD_OPTIONAL_FUNC
253 #undef LOAD_FUNC
255 ++load_count;
257 return pa_handle;
258 } //}}}
260 void pulse_unload(void) //{{{
262 if(load_count == 0 || --load_count > 0)
263 return;
265 #ifdef _WIN32
266 FreeLibrary(pa_handle);
267 #elif defined (HAVE_DLFCN_H)
268 dlclose(pa_handle);
269 #endif
270 pa_handle = NULL;
271 } //}}}
274 // PulseAudio Event Callbacks //{{{
275 static void context_state_callback(pa_context *context, void *pdata) //{{{
277 ALCdevice *Device = pdata;
278 pulse_data *data = Device->ExtraData;
279 pa_context_state_t state;
281 state = ppa_context_get_state(context);
282 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
284 if(ppa_threaded_mainloop_in_thread(data->loop))
285 ppa_threaded_mainloop_signal(data->loop, 0);
287 }//}}}
289 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
291 ALCdevice *Device = pdata;
292 pulse_data *data = Device->ExtraData;
293 pa_stream_state_t state;
295 state = ppa_stream_get_state(stream);
296 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
298 if(ppa_threaded_mainloop_in_thread(data->loop))
299 ppa_threaded_mainloop_signal(data->loop, 0);
301 }//}}}
303 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
305 ALCdevice *Device = pdata;
306 pulse_data *data = Device->ExtraData;
308 SuspendContext(NULL);
310 data->attr = *(ppa_stream_get_buffer_attr(stream));
311 if(data->attr.tlength < data->attr.minreq*2)
312 AL_PRINT("new tlength (%d) is smaller than two periods (%d x 2)!\n",
313 data->attr.tlength, data->attr.minreq);
314 Device->UpdateSize = data->attr.minreq / data->frame_size;
315 Device->NumUpdates = data->attr.tlength/data->attr.minreq;
317 ProcessContext(NULL);
318 }//}}}
320 static void context_state_callback2(pa_context *context, void *pdata) //{{{
322 ALCdevice *Device = pdata;
324 if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
326 AL_PRINT("Received context failure!\n");
327 aluHandleDisconnect(Device);
329 }//}}}
331 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
333 ALCdevice *Device = pdata;
335 if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
337 AL_PRINT("Received stream failure!\n");
338 aluHandleDisconnect(Device);
340 }//}}}
342 static void stream_success_callback(pa_stream *stream, int success, void *pdata) //{{{
344 ALCdevice *Device = pdata;
345 pulse_data *data = Device->ExtraData;
346 (void)stream;
347 (void)success;
349 if(ppa_threaded_mainloop_in_thread(data->loop))
350 ppa_threaded_mainloop_signal(data->loop, 0);
351 }//}}}
353 static void server_info_callback(pa_context *context, const pa_server_info *info, void *pdata) //{{{
355 struct {
356 pa_threaded_mainloop *loop;
357 char *name;
358 } *data = pdata;
359 (void)context;
361 data->name = strdup(info->default_sink_name);
362 ppa_threaded_mainloop_signal(data->loop, 0);
363 }//}}}
365 static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{
367 ALCdevice *device = pdata;
368 pulse_data *data = device->ExtraData;
369 char chanmap_str[256] = "";
370 const struct {
371 const char *str;
372 ALenum format;
373 } chanmaps[] = {
374 { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
375 AL_FORMAT_71CHN32 },
376 { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
377 AL_FORMAT_61CHN32 },
378 { "front-left,front-right,front-center,lfe,rear-left,rear-right",
379 AL_FORMAT_51CHN32 },
380 { "front-left,front-right,rear-left,rear-right", AL_FORMAT_QUAD32 },
381 { "front-left,front-right", AL_FORMAT_STEREO_FLOAT32 },
382 { "mono", AL_FORMAT_MONO_FLOAT32 },
383 { NULL, 0 }
385 int i;
386 (void)context;
388 if(eol)
390 ppa_threaded_mainloop_signal(data->loop, 0);
391 return;
394 for(i = 0;chanmaps[i].str;i++)
396 pa_channel_map map;
397 if(ppa_channel_map_parse(&map, chanmaps[i].str) &&
398 ppa_channel_map_superset(&info->channel_map, &map))
400 device->Format = chanmaps[i].format;
401 return;
405 ppa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
406 AL_PRINT("Failed to find format for channel map:\n %s\n", chanmap_str);
407 }//}}}
408 //}}}
410 // PulseAudio I/O Callbacks //{{{
411 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
413 ALCdevice *Device = pdata;
414 pulse_data *data = Device->ExtraData;
416 len -= len%data->attr.minreq;
417 if(len > 0)
419 void *buf;
420 pa_free_cb_t free_func = NULL;
422 #if PA_CHECK_VERSION(0,9,16)
423 if(!ppa_stream_begin_write ||
424 ppa_stream_begin_write(stream, &buf, &len) < 0)
425 #endif
427 buf = ppa_xmalloc(len);
428 free_func = ppa_xfree;
431 aluMixData(Device, buf, len/data->frame_size);
432 ppa_stream_write(stream, buf, len, free_func, 0, PA_SEEK_RELATIVE);
434 } //}}}
435 //}}}
437 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
439 pulse_data *data = ppa_xmalloc(sizeof(pulse_data));
440 pa_context_state_t state;
442 memset(data, 0, sizeof(*data));
444 if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
445 data->context_name = ppa_path_get_filename(data->path_name);
446 else
447 data->context_name = "OpenAL Soft";
449 if(!(data->loop = ppa_threaded_mainloop_new()))
451 AL_PRINT("pa_threaded_mainloop_new() failed!\n");
452 goto out;
455 if(ppa_threaded_mainloop_start(data->loop) < 0)
457 AL_PRINT("pa_threaded_mainloop_start() failed\n");
458 goto out;
461 ppa_threaded_mainloop_lock(data->loop);
462 device->ExtraData = data;
464 data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
465 if(!data->context)
467 AL_PRINT("pa_context_new() failed\n");
469 ppa_threaded_mainloop_unlock(data->loop);
470 goto out;
473 ppa_context_set_state_callback(data->context, context_state_callback, device);
475 if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
477 AL_PRINT("Context did not connect: %s\n",
478 ppa_strerror(ppa_context_errno(data->context)));
480 ppa_context_unref(data->context);
481 data->context = NULL;
483 ppa_threaded_mainloop_unlock(data->loop);
484 goto out;
487 while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
489 if(!PA_CONTEXT_IS_GOOD(state))
491 AL_PRINT("Context did not get ready: %s\n",
492 ppa_strerror(ppa_context_errno(data->context)));
494 ppa_context_unref(data->context);
495 data->context = NULL;
497 ppa_threaded_mainloop_unlock(data->loop);
498 goto out;
501 ppa_threaded_mainloop_wait(data->loop);
503 ppa_context_set_state_callback(data->context, context_state_callback2, device);
505 device->szDeviceName = strdup(device_name);
507 ppa_threaded_mainloop_unlock(data->loop);
508 return ALC_TRUE;
510 out:
511 if(data->loop)
513 ppa_threaded_mainloop_stop(data->loop);
514 ppa_threaded_mainloop_free(data->loop);
517 device->ExtraData = NULL;
518 ppa_xfree(data);
519 return ALC_FALSE;
520 } //}}}
522 static void pulse_close(ALCdevice *device) //{{{
524 pulse_data *data = device->ExtraData;
526 ppa_threaded_mainloop_lock(data->loop);
528 if(data->stream)
530 ppa_stream_disconnect(data->stream);
531 ppa_stream_unref(data->stream);
534 ppa_context_disconnect(data->context);
535 ppa_context_unref(data->context);
537 ppa_threaded_mainloop_unlock(data->loop);
539 ppa_threaded_mainloop_stop(data->loop);
540 ppa_threaded_mainloop_free(data->loop);
542 DestroyRingBuffer(data->ring);
544 device->ExtraData = NULL;
545 ppa_xfree(data);
546 } //}}}
547 //}}}
549 // OpenAL {{{
550 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
552 if(!device_name)
553 device_name = pulse_device;
554 else if(strcmp(device_name, pulse_device) != 0)
555 return ALC_FALSE;
557 if(!pulse_load())
558 return ALC_FALSE;
560 if(pulse_open(device, device_name) != ALC_FALSE)
561 return ALC_TRUE;
563 pulse_unload();
564 return ALC_FALSE;
565 } //}}}
567 static void pulse_close_playback(ALCdevice *device) //{{{
569 pulse_close(device);
570 pulse_unload();
571 } //}}}
573 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
575 pulse_data *data = device->ExtraData;
576 pa_stream_flags_t flags = 0;
577 pa_stream_state_t state;
578 pa_channel_map chanmap;
580 ppa_threaded_mainloop_lock(data->loop);
582 if(*(GetConfigValue(NULL, "format", "")) == '\0')
584 pa_operation *o;
585 struct {
586 pa_threaded_mainloop *loop;
587 char *name;
588 } server_data;
589 server_data.loop = data->loop;
590 server_data.name = NULL;
592 o = ppa_context_get_server_info(data->context, server_info_callback, &server_data);
593 while(ppa_operation_get_state(o) == PA_OPERATION_RUNNING)
594 ppa_threaded_mainloop_wait(data->loop);
595 ppa_operation_unref(o);
596 if(server_data.name)
598 o = ppa_context_get_sink_info_by_name(data->context, server_data.name, sink_info_callback, device);
599 while(ppa_operation_get_state(o) == PA_OPERATION_RUNNING)
600 ppa_threaded_mainloop_wait(data->loop);
601 ppa_operation_unref(o);
602 free(server_data.name);
605 if(*(GetConfigValue(NULL, "frequency", "")) == '\0')
606 flags |= PA_STREAM_FIX_RATE;
608 data->frame_size = aluBytesFromFormat(device->Format) *
609 aluChannelsFromFormat(device->Format);
610 data->attr.minreq = data->frame_size * device->UpdateSize;
611 data->attr.prebuf = -1;
612 data->attr.maxlength = -1;
613 data->attr.fragsize = -1;
614 data->attr.tlength = data->attr.minreq * device->NumUpdates;
615 data->stream_name = "Playback Stream";
617 switch(aluBytesFromFormat(device->Format))
619 case 1:
620 data->spec.format = PA_SAMPLE_U8;
621 break;
622 case 2:
623 data->spec.format = PA_SAMPLE_S16NE;
624 break;
625 case 4:
626 data->spec.format = PA_SAMPLE_FLOAT32NE;
627 break;
628 default:
629 AL_PRINT("Unknown format: 0x%x\n", device->Format);
630 ppa_threaded_mainloop_unlock(data->loop);
631 return ALC_FALSE;
633 data->spec.rate = device->Frequency;
634 data->spec.channels = aluChannelsFromFormat(device->Format);
636 if(ppa_sample_spec_valid(&data->spec) == 0)
638 AL_PRINT("Invalid sample format\n");
639 ppa_threaded_mainloop_unlock(data->loop);
640 return ALC_FALSE;
643 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
645 AL_PRINT("Couldn't build map for channel count (%d)!\n", data->spec.channels);
646 ppa_threaded_mainloop_unlock(data->loop);
647 return ALC_FALSE;
649 SetDefaultWFXChannelOrder(device);
651 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
652 if(!data->stream)
654 AL_PRINT("pa_stream_new() failed: %s\n",
655 ppa_strerror(ppa_context_errno(data->context)));
657 ppa_threaded_mainloop_unlock(data->loop);
658 return ALC_FALSE;
661 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
663 if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, flags, NULL, NULL) < 0)
665 AL_PRINT("Stream did not connect: %s\n",
666 ppa_strerror(ppa_context_errno(data->context)));
668 ppa_stream_unref(data->stream);
669 data->stream = NULL;
671 ppa_threaded_mainloop_unlock(data->loop);
672 return ALC_FALSE;
675 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
677 if(!PA_STREAM_IS_GOOD(state))
679 AL_PRINT("Stream did not get ready: %s\n",
680 ppa_strerror(ppa_context_errno(data->context)));
682 ppa_stream_unref(data->stream);
683 data->stream = NULL;
685 ppa_threaded_mainloop_unlock(data->loop);
686 return ALC_FALSE;
689 ppa_threaded_mainloop_wait(data->loop);
691 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
693 data->spec = *(ppa_stream_get_sample_spec(data->stream));
694 if(device->Frequency != data->spec.rate)
696 pa_operation *o;
698 /* Server updated our playback rate, so modify the buffer attribs
699 * accordingly. */
700 data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) *
701 data->spec.rate / device->Frequency * data->frame_size;
702 data->attr.tlength = data->attr.minreq * device->NumUpdates;
704 o = ppa_stream_set_buffer_attr(data->stream, &data->attr,
705 stream_success_callback, device);
706 while(ppa_operation_get_state(o) == PA_OPERATION_RUNNING)
707 ppa_threaded_mainloop_wait(data->loop);
708 ppa_operation_unref(o);
710 device->Frequency = data->spec.rate;
713 stream_buffer_attr_callback(data->stream, device);
714 #if PA_CHECK_VERSION(0,9,15)
715 if(ppa_stream_set_buffer_attr_callback)
716 ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
717 #endif
719 stream_write_callback(data->stream, data->attr.tlength, device);
720 ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
722 ppa_threaded_mainloop_unlock(data->loop);
723 return ALC_TRUE;
724 } //}}}
726 static void pulse_stop_playback(ALCdevice *device) //{{{
728 pulse_data *data = device->ExtraData;
730 if(!data->stream)
731 return;
733 ppa_threaded_mainloop_lock(data->loop);
735 ppa_stream_disconnect(data->stream);
736 ppa_stream_unref(data->stream);
737 data->stream = NULL;
739 ppa_threaded_mainloop_unlock(data->loop);
740 } //}}}
743 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
745 pulse_data *data;
746 pa_stream_state_t state;
747 pa_channel_map chanmap;
749 if(!device_name)
750 device_name = pulse_capture_device;
751 else if(strcmp(device_name, pulse_capture_device) != 0)
752 return ALC_FALSE;
754 if(!pulse_load())
755 return ALC_FALSE;
757 if(pulse_open(device, device_name) == ALC_FALSE)
759 pulse_unload();
760 return ALC_FALSE;
763 data = device->ExtraData;
764 ppa_threaded_mainloop_lock(data->loop);
766 data->samples = device->UpdateSize * device->NumUpdates;
767 data->frame_size = aluBytesFromFormat(device->Format) *
768 aluChannelsFromFormat(device->Format);
770 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
772 ppa_threaded_mainloop_unlock(data->loop);
773 goto fail;
776 data->attr.minreq = -1;
777 data->attr.prebuf = -1;
778 data->attr.maxlength = data->frame_size * data->samples;
779 data->attr.tlength = -1;
780 data->attr.fragsize = min(data->frame_size * data->samples,
781 10 * device->Frequency / 1000);
782 data->stream_name = "Capture Stream";
784 data->spec.rate = device->Frequency;
785 data->spec.channels = aluChannelsFromFormat(device->Format);
787 switch(aluBytesFromFormat(device->Format))
789 case 1:
790 data->spec.format = PA_SAMPLE_U8;
791 break;
792 case 2:
793 data->spec.format = PA_SAMPLE_S16NE;
794 break;
795 case 4:
796 data->spec.format = PA_SAMPLE_FLOAT32NE;
797 break;
798 default:
799 AL_PRINT("Unknown format: 0x%x\n", device->Format);
800 ppa_threaded_mainloop_unlock(data->loop);
801 goto fail;
804 if(ppa_sample_spec_valid(&data->spec) == 0)
806 AL_PRINT("Invalid sample format\n");
807 ppa_threaded_mainloop_unlock(data->loop);
808 goto fail;
811 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
813 AL_PRINT("Couldn't build map for channel count (%d)!\n", data->spec.channels);
814 ppa_threaded_mainloop_unlock(data->loop);
815 goto fail;
818 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
819 if(!data->stream)
821 AL_PRINT("pa_stream_new() failed: %s\n",
822 ppa_strerror(ppa_context_errno(data->context)));
824 ppa_threaded_mainloop_unlock(data->loop);
825 goto fail;
828 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
830 if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_START_CORKED) < 0)
832 AL_PRINT("Stream did not connect: %s\n",
833 ppa_strerror(ppa_context_errno(data->context)));
835 ppa_stream_unref(data->stream);
836 data->stream = NULL;
838 ppa_threaded_mainloop_unlock(data->loop);
839 goto fail;
842 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
844 if(!PA_STREAM_IS_GOOD(state))
846 AL_PRINT("Stream did not get ready: %s\n",
847 ppa_strerror(ppa_context_errno(data->context)));
849 ppa_stream_unref(data->stream);
850 data->stream = NULL;
852 ppa_threaded_mainloop_unlock(data->loop);
853 goto fail;
856 ppa_threaded_mainloop_wait(data->loop);
858 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
860 ppa_threaded_mainloop_unlock(data->loop);
861 return ALC_TRUE;
863 fail:
864 pulse_close(device);
865 pulse_unload();
866 return ALC_FALSE;
867 } //}}}
869 static void pulse_close_capture(ALCdevice *device) //{{{
871 pulse_close(device);
872 pulse_unload();
873 } //}}}
875 static void pulse_start_capture(ALCdevice *device) //{{{
877 pulse_data *data = device->ExtraData;
878 pa_operation *o;
880 ppa_threaded_mainloop_lock(data->loop);
881 o = ppa_stream_cork(data->stream, 0, stream_success_callback, device);
882 while(ppa_operation_get_state(o) == PA_OPERATION_RUNNING)
883 ppa_threaded_mainloop_wait(data->loop);
884 ppa_operation_unref(o);
885 ppa_threaded_mainloop_unlock(data->loop);
886 } //}}}
888 static void pulse_stop_capture(ALCdevice *device) //{{{
890 pulse_data *data = device->ExtraData;
891 pa_operation *o;
893 ppa_threaded_mainloop_lock(data->loop);
894 o = ppa_stream_cork(data->stream, 1, stream_success_callback, device);
895 while(ppa_operation_get_state(o) == PA_OPERATION_RUNNING)
896 ppa_threaded_mainloop_wait(data->loop);
897 ppa_operation_unref(o);
898 ppa_threaded_mainloop_unlock(data->loop);
899 } //}}}
901 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
903 pulse_data *data = device->ExtraData;
904 ALCuint available = RingBufferSize(data->ring);
905 const void *buf;
906 size_t length;
908 available *= data->frame_size;
909 samples *= data->frame_size;
911 ppa_threaded_mainloop_lock(data->loop);
912 if(available+ppa_stream_readable_size(data->stream) < samples)
914 ppa_threaded_mainloop_unlock(data->loop);
915 alcSetError(ALC_INVALID_VALUE);
916 return;
919 available = min(available, samples);
920 if(available > 0)
922 ReadRingBuffer(data->ring, buffer, available/data->frame_size);
923 buffer = (ALubyte*)buffer + available;
924 samples -= available;
927 /* Capture is done in fragment-sized chunks, so we loop until we get all
928 * that's requested */
929 while(samples > 0)
931 if(ppa_stream_peek(data->stream, &buf, &length) < 0)
933 AL_PRINT("pa_stream_peek() failed: %s\n",
934 ppa_strerror(ppa_context_errno(data->context)));
935 break;
937 available = min(length, samples);
939 memcpy(buffer, buf, available);
940 buffer = (ALubyte*)buffer + available;
941 buf = (const ALubyte*)buf + available;
942 samples -= available;
943 length -= available;
945 /* Any unread data in the fragment will be lost, so save it */
946 length /= data->frame_size;
947 if(length > 0)
949 if(length > data->samples)
950 length = data->samples;
951 WriteRingBuffer(data->ring, buf, length);
954 ppa_stream_drop(data->stream);
956 ppa_threaded_mainloop_unlock(data->loop);
957 } //}}}
959 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
961 pulse_data *data = device->ExtraData;
962 ALCuint ret;
964 ppa_threaded_mainloop_lock(data->loop);
965 ret = RingBufferSize(data->ring);
966 ret += ppa_stream_readable_size(data->stream)/data->frame_size;
967 ppa_threaded_mainloop_unlock(data->loop);
969 return ret;
970 } //}}}
972 BackendFuncs pulse_funcs = { //{{{
973 pulse_open_playback,
974 pulse_close_playback,
975 pulse_reset_playback,
976 pulse_stop_playback,
977 pulse_open_capture,
978 pulse_close_capture,
979 pulse_start_capture,
980 pulse_stop_capture,
981 pulse_capture_samples,
982 pulse_available_samples
983 }; //}}}
985 void alc_pulse_init(BackendFuncs *func_list) //{{{
987 *func_list = pulse_funcs;
988 } //}}}
990 void alc_pulse_deinit(void) //{{{
992 } //}}}
994 void alc_pulse_probe(int type) //{{{
996 if(!pulse_load()) return;
998 if(type == DEVICE_PROBE)
999 AppendDeviceList(pulse_device);
1000 else if(type == ALL_DEVICE_PROBE)
1001 AppendAllDeviceList(pulse_device);
1002 else if(type == CAPTURE_DEVICE_PROBE)
1003 AppendCaptureDeviceList(pulse_capture_device);
1005 pulse_unload();
1006 } //}}}
1007 //}}}