Use the Pulse server-specified playback rate by default
[openal-soft.git] / Alc / pulseaudio.c
bloba40cfee7ac133d7f41fa065283d3e01abf38404b
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 static void *pa_handle;
48 #define MAKE_FUNC(x) static typeof(x) * p##x
49 MAKE_FUNC(pa_context_unref);
50 MAKE_FUNC(pa_sample_spec_valid);
51 MAKE_FUNC(pa_stream_drop);
52 MAKE_FUNC(pa_strerror);
53 MAKE_FUNC(pa_context_get_state);
54 MAKE_FUNC(pa_stream_get_state);
55 MAKE_FUNC(pa_threaded_mainloop_signal);
56 MAKE_FUNC(pa_stream_peek);
57 MAKE_FUNC(pa_threaded_mainloop_wait);
58 MAKE_FUNC(pa_threaded_mainloop_unlock);
59 MAKE_FUNC(pa_threaded_mainloop_in_thread);
60 MAKE_FUNC(pa_context_new);
61 MAKE_FUNC(pa_threaded_mainloop_stop);
62 MAKE_FUNC(pa_context_disconnect);
63 MAKE_FUNC(pa_threaded_mainloop_start);
64 MAKE_FUNC(pa_threaded_mainloop_get_api);
65 MAKE_FUNC(pa_context_set_state_callback);
66 MAKE_FUNC(pa_stream_write);
67 MAKE_FUNC(pa_xfree);
68 MAKE_FUNC(pa_stream_connect_record);
69 MAKE_FUNC(pa_stream_connect_playback);
70 MAKE_FUNC(pa_path_get_filename);
71 MAKE_FUNC(pa_get_binary_name);
72 MAKE_FUNC(pa_threaded_mainloop_free);
73 MAKE_FUNC(pa_context_errno);
74 MAKE_FUNC(pa_xmalloc);
75 MAKE_FUNC(pa_stream_unref);
76 MAKE_FUNC(pa_threaded_mainloop_accept);
77 MAKE_FUNC(pa_stream_set_write_callback);
78 MAKE_FUNC(pa_threaded_mainloop_new);
79 MAKE_FUNC(pa_context_connect);
80 MAKE_FUNC(pa_stream_set_buffer_attr);
81 MAKE_FUNC(pa_stream_get_buffer_attr);
82 MAKE_FUNC(pa_stream_get_sample_spec);
83 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
84 MAKE_FUNC(pa_stream_set_read_callback);
85 MAKE_FUNC(pa_stream_set_state_callback);
86 MAKE_FUNC(pa_stream_new);
87 MAKE_FUNC(pa_stream_disconnect);
88 MAKE_FUNC(pa_threaded_mainloop_lock);
89 MAKE_FUNC(pa_channel_map_init_auto);
90 MAKE_FUNC(pa_channel_map_parse);
91 MAKE_FUNC(pa_operation_unref);
92 #undef MAKE_FUNC
94 #ifndef PATH_MAX
95 #define PATH_MAX 4096
96 #endif
98 typedef struct {
99 ALCuint samples;
100 ALCuint frame_size;
102 RingBuffer *ring;
104 pa_buffer_attr attr;
105 pa_sample_spec spec;
107 char path_name[PATH_MAX];
108 const char *context_name;
109 const char *stream_name;
111 pa_threaded_mainloop *loop;
113 pa_stream *stream;
114 pa_context *context;
115 } pulse_data;
117 static const ALCchar pulse_device[] = "PulseAudio Software";
118 static const ALCchar pulse_capture_device[] = "PulseAudio Capture";
119 static volatile ALuint load_count;
122 void pulse_load(void) //{{{
124 if(load_count == 0)
126 #ifdef _WIN32
127 pa_handle = LoadLibrary("libpulse-0.dll");
128 #define LOAD_FUNC(x) do { \
129 p##x = GetProcAddress(pa_handle, #x); \
130 if(!(p##x)) { \
131 AL_PRINT("Could not load %s from libpulse-0.dll\n", #x); \
132 FreeLibrary(pa_handle); \
133 pa_handle = NULL; \
134 return; \
136 } while(0)
138 #elif defined (HAVE_DLFCN_H)
140 const char *err;
141 #if defined(__APPLE__) && defined(__MACH__)
142 pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW);
143 #else
144 pa_handle = dlopen("libpulse.so.0", RTLD_NOW);
145 #endif
146 dlerror();
148 #define LOAD_FUNC(x) do { \
149 p##x = dlsym(pa_handle, #x); \
150 if((err=dlerror()) != NULL) { \
151 AL_PRINT("Could not load %s from libpulse: %s\n", #x, err); \
152 dlclose(pa_handle); \
153 pa_handle = NULL; \
154 return; \
156 } while(0)
158 #else
160 pa_handle = (void*)0xDEADBEEF;
161 #define LOAD_FUNC(x) p##x = (x)
163 #endif
164 if(!pa_handle)
165 return;
167 LOAD_FUNC(pa_context_unref);
168 LOAD_FUNC(pa_sample_spec_valid);
169 LOAD_FUNC(pa_stream_drop);
170 LOAD_FUNC(pa_strerror);
171 LOAD_FUNC(pa_context_get_state);
172 LOAD_FUNC(pa_stream_get_state);
173 LOAD_FUNC(pa_threaded_mainloop_signal);
174 LOAD_FUNC(pa_stream_peek);
175 LOAD_FUNC(pa_threaded_mainloop_wait);
176 LOAD_FUNC(pa_threaded_mainloop_unlock);
177 LOAD_FUNC(pa_threaded_mainloop_in_thread);
178 LOAD_FUNC(pa_context_new);
179 LOAD_FUNC(pa_threaded_mainloop_stop);
180 LOAD_FUNC(pa_context_disconnect);
181 LOAD_FUNC(pa_threaded_mainloop_start);
182 LOAD_FUNC(pa_threaded_mainloop_get_api);
183 LOAD_FUNC(pa_context_set_state_callback);
184 LOAD_FUNC(pa_stream_write);
185 LOAD_FUNC(pa_xfree);
186 LOAD_FUNC(pa_stream_connect_record);
187 LOAD_FUNC(pa_stream_connect_playback);
188 LOAD_FUNC(pa_path_get_filename);
189 LOAD_FUNC(pa_get_binary_name);
190 LOAD_FUNC(pa_threaded_mainloop_free);
191 LOAD_FUNC(pa_context_errno);
192 LOAD_FUNC(pa_xmalloc);
193 LOAD_FUNC(pa_stream_unref);
194 LOAD_FUNC(pa_threaded_mainloop_accept);
195 LOAD_FUNC(pa_stream_set_write_callback);
196 LOAD_FUNC(pa_threaded_mainloop_new);
197 LOAD_FUNC(pa_context_connect);
198 LOAD_FUNC(pa_stream_set_buffer_attr);
199 LOAD_FUNC(pa_stream_get_buffer_attr);
200 LOAD_FUNC(pa_stream_get_sample_spec);
201 LOAD_FUNC(pa_stream_set_buffer_attr_callback);
202 LOAD_FUNC(pa_stream_set_read_callback);
203 LOAD_FUNC(pa_stream_set_state_callback);
204 LOAD_FUNC(pa_stream_new);
205 LOAD_FUNC(pa_stream_disconnect);
206 LOAD_FUNC(pa_threaded_mainloop_lock);
207 LOAD_FUNC(pa_channel_map_init_auto);
208 LOAD_FUNC(pa_channel_map_parse);
209 LOAD_FUNC(pa_operation_unref);
211 #undef LOAD_FUNC
213 ++load_count;
214 } //}}}
216 void pulse_unload(void) //{{{
218 if(load_count == 0 || --load_count > 0)
219 return;
221 #ifdef _WIN32
222 FreeLibrary(pa_handle);
223 #elif defined (HAVE_DLFCN_H)
224 dlclose(pa_handle);
225 #endif
226 pa_handle = NULL;
227 } //}}}
230 // PulseAudio Event Callbacks //{{{
231 static void context_state_callback(pa_context *context, void *pdata) //{{{
233 ALCdevice *Device = pdata;
234 pulse_data *data = Device->ExtraData;
235 pa_context_state_t state;
237 state = ppa_context_get_state(context);
238 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
240 if(ppa_threaded_mainloop_in_thread(data->loop))
241 ppa_threaded_mainloop_signal(data->loop, 0);
243 }//}}}
245 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
247 ALCdevice *Device = pdata;
248 pulse_data *data = Device->ExtraData;
249 pa_stream_state_t state;
251 state = ppa_stream_get_state(stream);
252 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
254 if(ppa_threaded_mainloop_in_thread(data->loop))
255 ppa_threaded_mainloop_signal(data->loop, 0);
257 }//}}}
259 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
261 ALCdevice *Device = pdata;
262 pulse_data *data = Device->ExtraData;
264 SuspendContext(NULL);
266 data->attr = *(ppa_stream_get_buffer_attr(stream));
267 if(data->attr.tlength < data->attr.minreq*2)
268 AL_PRINT("new tlength (%d) is smaller than two periods (%d x 2)!\n",
269 data->attr.tlength, data->attr.minreq);
270 Device->UpdateSize = data->attr.minreq / data->frame_size;
271 Device->NumUpdates = data->attr.tlength/data->attr.minreq;
273 ProcessContext(NULL);
274 }//}}}
276 static void context_state_callback2(pa_context *context, void *pdata) //{{{
278 ALCdevice *Device = pdata;
280 if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
282 AL_PRINT("Received context failure!\n");
283 aluHandleDisconnect(Device);
285 }//}}}
287 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
289 ALCdevice *Device = pdata;
291 if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
293 AL_PRINT("Received stream failure!\n");
294 aluHandleDisconnect(Device);
296 }//}}}
298 //}}}
300 // PulseAudio I/O Callbacks //{{{
301 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
303 ALCdevice *Device = pdata;
304 pulse_data *data = Device->ExtraData;
306 len -= len%data->attr.minreq;
307 if(len > 0)
309 void *buf = ppa_xmalloc(len);
310 aluMixData(Device, buf, len/data->frame_size);
311 ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE);
313 } //}}}
315 static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
317 ALCdevice *Device = pdata;
318 pulse_data *data = Device->ExtraData;
319 const void *buf;
321 if(ppa_stream_peek(stream, &buf, &length) < 0)
323 AL_PRINT("pa_stream_peek() failed: %s\n",
324 ppa_strerror(ppa_context_errno(data->context)));
325 return;
328 assert(buf);
329 assert(length);
331 length /= data->frame_size;
333 if(data->samples < length)
334 AL_PRINT("stream_read_callback: buffer overflow!\n");
336 WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
338 ppa_stream_drop(stream);
339 } //}}}
340 //}}}
342 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
344 pulse_data *data = ppa_xmalloc(sizeof(pulse_data));
345 pa_context_state_t state;
347 memset(data, 0, sizeof(*data));
349 if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
350 data->context_name = ppa_path_get_filename(data->path_name);
351 else
352 data->context_name = "OpenAL Soft";
354 if(!(data->loop = ppa_threaded_mainloop_new()))
356 AL_PRINT("pa_threaded_mainloop_new() failed!\n");
357 goto out;
360 if(ppa_threaded_mainloop_start(data->loop) < 0)
362 AL_PRINT("pa_threaded_mainloop_start() failed\n");
363 goto out;
366 ppa_threaded_mainloop_lock(data->loop);
367 device->ExtraData = data;
369 data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
370 if(!data->context)
372 AL_PRINT("pa_context_new() failed: %s\n",
373 ppa_strerror(ppa_context_errno(data->context)));
375 ppa_threaded_mainloop_unlock(data->loop);
376 goto out;
379 ppa_context_set_state_callback(data->context, context_state_callback, device);
381 if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
383 AL_PRINT("Context did not connect: %s\n",
384 ppa_strerror(ppa_context_errno(data->context)));
386 ppa_context_unref(data->context);
387 data->context = NULL;
389 ppa_threaded_mainloop_unlock(data->loop);
390 goto out;
393 while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
395 if(!PA_CONTEXT_IS_GOOD(state))
397 AL_PRINT("Context did not get ready: %s\n",
398 ppa_strerror(ppa_context_errno(data->context)));
400 ppa_context_unref(data->context);
401 data->context = NULL;
403 ppa_threaded_mainloop_unlock(data->loop);
404 goto out;
407 ppa_threaded_mainloop_wait(data->loop);
409 ppa_context_set_state_callback(data->context, context_state_callback2, device);
411 device->szDeviceName = strdup(device_name);
413 ppa_threaded_mainloop_unlock(data->loop);
414 return ALC_TRUE;
416 out:
417 if(data->loop)
419 ppa_threaded_mainloop_stop(data->loop);
420 ppa_threaded_mainloop_free(data->loop);
423 device->ExtraData = NULL;
424 ppa_xfree(data);
425 return ALC_FALSE;
426 } //}}}
428 static void pulse_close(ALCdevice *device) //{{{
430 pulse_data *data = device->ExtraData;
432 ppa_threaded_mainloop_lock(data->loop);
434 if(data->stream)
436 ppa_stream_disconnect(data->stream);
437 ppa_stream_unref(data->stream);
440 ppa_context_disconnect(data->context);
441 ppa_context_unref(data->context);
443 ppa_threaded_mainloop_unlock(data->loop);
445 ppa_threaded_mainloop_stop(data->loop);
446 ppa_threaded_mainloop_free(data->loop);
448 DestroyRingBuffer(data->ring);
450 device->ExtraData = NULL;
451 ppa_xfree(data);
452 } //}}}
453 //}}}
455 // OpenAL {{{
456 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
458 if(!device_name)
459 device_name = pulse_device;
460 else if(strcmp(device_name, pulse_device) != 0)
461 return ALC_FALSE;
463 pulse_load();
464 if(!pa_handle)
465 return ALC_FALSE;
467 if(pulse_open(device, device_name) != ALC_FALSE)
468 return ALC_TRUE;
470 pulse_unload();
471 return ALC_FALSE;
472 } //}}}
474 static void pulse_close_playback(ALCdevice *device) //{{{
476 pulse_close(device);
477 pulse_unload();
478 } //}}}
480 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
482 pulse_data *data = device->ExtraData;
483 pa_stream_flags_t flags = 0;
484 pa_stream_state_t state;
485 pa_channel_map chanmap;
487 ppa_threaded_mainloop_lock(data->loop);
489 data->frame_size = aluBytesFromFormat(device->Format) *
490 aluChannelsFromFormat(device->Format);
491 data->attr.minreq = data->frame_size * device->UpdateSize;
492 data->attr.prebuf = -1;
493 data->attr.maxlength = -1;
494 data->attr.fragsize = -1;
495 data->attr.tlength = data->attr.minreq * device->NumUpdates;
496 data->stream_name = "Playback Stream";
498 switch(aluBytesFromFormat(device->Format))
500 case 1:
501 data->spec.format = PA_SAMPLE_U8;
502 break;
503 case 2:
504 data->spec.format = PA_SAMPLE_S16NE;
505 break;
506 case 4:
507 data->spec.format = PA_SAMPLE_FLOAT32NE;
508 break;
509 default:
510 AL_PRINT("Unknown format: 0x%x\n", device->Format);
511 ppa_threaded_mainloop_unlock(data->loop);
512 return ALC_FALSE;
514 data->spec.rate = device->Frequency;
515 data->spec.channels = aluChannelsFromFormat(device->Format);
517 if(ppa_sample_spec_valid(&data->spec) == 0)
519 AL_PRINT("Invalid sample format\n");
520 ppa_threaded_mainloop_unlock(data->loop);
521 return ALC_FALSE;
524 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
526 AL_PRINT("Couldn't build map for channel count (%d)!", data->spec.channels);
527 ppa_threaded_mainloop_unlock(data->loop);
528 return ALC_FALSE;
530 SetDefaultWFXChannelOrder(device);
532 if(*(GetConfigValue(NULL, "frequency", "")) == '\0')
533 flags |= PA_STREAM_FIX_RATE;
535 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
536 if(!data->stream)
538 AL_PRINT("pa_stream_new() failed: %s\n",
539 ppa_strerror(ppa_context_errno(data->context)));
541 ppa_threaded_mainloop_unlock(data->loop);
542 return ALC_FALSE;
545 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
547 if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, flags, NULL, NULL) < 0)
549 AL_PRINT("Stream did not connect: %s\n",
550 ppa_strerror(ppa_context_errno(data->context)));
552 ppa_stream_unref(data->stream);
553 data->stream = NULL;
555 ppa_threaded_mainloop_unlock(data->loop);
556 return ALC_FALSE;
559 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
561 if(!PA_STREAM_IS_GOOD(state))
563 AL_PRINT("Stream did not get ready: %s\n",
564 ppa_strerror(ppa_context_errno(data->context)));
566 ppa_stream_unref(data->stream);
567 data->stream = NULL;
569 ppa_threaded_mainloop_unlock(data->loop);
570 return ALC_FALSE;
573 ppa_threaded_mainloop_wait(data->loop);
575 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
577 data->spec = *(ppa_stream_get_sample_spec(data->stream));
578 if(device->Frequency != data->spec.rate)
580 pa_operation *o;
582 /* Server updated our playback rate, so modify the buffer attribs
583 * accordingly. */
584 data->attr.minreq = (data->attr.minreq/data->frame_size) *
585 data->spec.rate / device->Frequency * data->frame_size;
586 data->attr.tlength = data->attr.minreq * device->NumUpdates;
588 o = ppa_stream_set_buffer_attr(data->stream, &data->attr, NULL, NULL);
589 ppa_operation_unref(o);
591 device->Frequency = data->spec.rate;
594 stream_buffer_attr_callback(data->stream, device);
595 ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
597 stream_write_callback(data->stream, data->attr.tlength, device);
598 ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
600 ppa_threaded_mainloop_unlock(data->loop);
601 return ALC_TRUE;
602 } //}}}
604 static void pulse_stop_playback(ALCdevice *device) //{{{
606 pulse_data *data = device->ExtraData;
608 if(!data->stream)
609 return;
611 ppa_threaded_mainloop_lock(data->loop);
613 ppa_stream_disconnect(data->stream);
614 ppa_stream_unref(data->stream);
615 data->stream = NULL;
617 ppa_threaded_mainloop_unlock(data->loop);
618 } //}}}
621 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
623 pulse_data *data;
624 pa_stream_state_t state;
626 if(!device_name)
627 device_name = pulse_capture_device;
628 else if(strcmp(device_name, pulse_capture_device) != 0)
629 return ALC_FALSE;
631 pulse_load();
632 if(!pa_handle)
633 return ALC_FALSE;
635 if(pulse_open(device, device_name) == ALC_FALSE)
637 pulse_unload();
638 return ALC_FALSE;
641 data = device->ExtraData;
642 ppa_threaded_mainloop_lock(data->loop);
644 data->samples = device->UpdateSize * device->NumUpdates;
645 data->frame_size = aluBytesFromFormat(device->Format) *
646 aluChannelsFromFormat(device->Format);
648 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
650 ppa_threaded_mainloop_unlock(data->loop);
651 pulse_close(device);
652 pulse_unload();
653 return ALC_FALSE;
656 data->attr.minreq = -1;
657 data->attr.prebuf = -1;
658 data->attr.maxlength = -1;
659 data->attr.tlength = -1;
660 data->attr.fragsize = min(data->frame_size * data->samples / 2,
661 25 * device->Frequency / 1000);
662 data->stream_name = "Capture Stream";
664 data->spec.rate = device->Frequency;
665 data->spec.channels = aluChannelsFromFormat(device->Format);
667 switch(aluBytesFromFormat(device->Format))
669 case 1:
670 data->spec.format = PA_SAMPLE_U8;
671 break;
672 case 2:
673 data->spec.format = PA_SAMPLE_S16NE;
674 break;
675 case 4:
676 data->spec.format = PA_SAMPLE_FLOAT32NE;
677 break;
678 default:
679 AL_PRINT("Unknown format: 0x%x\n", device->Format);
680 ppa_threaded_mainloop_unlock(data->loop);
681 pulse_close(device);
682 pulse_unload();
683 return ALC_FALSE;
686 if(ppa_sample_spec_valid(&data->spec) == 0)
688 AL_PRINT("Invalid sample format\n");
689 ppa_threaded_mainloop_unlock(data->loop);
690 pulse_close(device);
691 pulse_unload();
692 return ALC_FALSE;
695 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
696 if(!data->stream)
698 AL_PRINT("pa_stream_new() failed: %s\n",
699 ppa_strerror(ppa_context_errno(data->context)));
701 ppa_threaded_mainloop_unlock(data->loop);
702 pulse_close(device);
703 pulse_unload();
704 return ALC_FALSE;
707 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
709 if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
711 AL_PRINT("Stream did not connect: %s\n",
712 ppa_strerror(ppa_context_errno(data->context)));
714 ppa_stream_unref(data->stream);
715 ppa_threaded_mainloop_unlock(data->loop);
717 data->stream = NULL;
718 pulse_close(device);
719 pulse_unload();
720 return ALC_FALSE;
723 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
725 if(!PA_STREAM_IS_GOOD(state))
727 AL_PRINT("Stream did not get ready: %s\n",
728 ppa_strerror(ppa_context_errno(data->context)));
730 ppa_stream_unref(data->stream);
731 data->stream = NULL;
733 ppa_threaded_mainloop_unlock(data->loop);
735 pulse_close(device);
736 pulse_unload();
737 return ALC_FALSE;
740 ppa_threaded_mainloop_wait(data->loop);
742 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
744 ppa_threaded_mainloop_unlock(data->loop);
745 return ALC_TRUE;
746 } //}}}
748 static void pulse_close_capture(ALCdevice *device) //{{{
750 pulse_close(device);
751 pulse_unload();
752 } //}}}
754 static void pulse_start_capture(ALCdevice *device) //{{{
756 pulse_data *data = device->ExtraData;
758 ppa_threaded_mainloop_lock(data->loop);
759 ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
760 ppa_threaded_mainloop_unlock(data->loop);
761 } //}}}
763 static void pulse_stop_capture(ALCdevice *device) //{{{
765 pulse_data *data = device->ExtraData;
767 ppa_threaded_mainloop_lock(data->loop);
768 ppa_stream_set_read_callback(data->stream, NULL, NULL);
769 ppa_threaded_mainloop_unlock(data->loop);
770 } //}}}
772 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
774 pulse_data *data = device->ExtraData;
775 ALCuint available = RingBufferSize(data->ring);
777 if(available < samples)
778 alcSetError(ALC_INVALID_VALUE);
779 else
780 ReadRingBuffer(data->ring, buffer, samples);
781 } //}}}
783 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
785 pulse_data *data = device->ExtraData;
786 return RingBufferSize(data->ring);
787 } //}}}
789 BackendFuncs pulse_funcs = { //{{{
790 pulse_open_playback,
791 pulse_close_playback,
792 pulse_reset_playback,
793 pulse_stop_playback,
794 pulse_open_capture,
795 pulse_close_capture,
796 pulse_start_capture,
797 pulse_stop_capture,
798 pulse_capture_samples,
799 pulse_available_samples
800 }; //}}}
802 void alc_pulse_init(BackendFuncs *func_list) //{{{
804 *func_list = pulse_funcs;
805 } //}}}
807 void alc_pulse_deinit(void) //{{{
809 } //}}}
811 void alc_pulse_probe(int type) //{{{
813 pulse_load();
814 if(!pa_handle) return;
816 if(type == DEVICE_PROBE)
817 AppendDeviceList(pulse_device);
818 else if(type == ALL_DEVICE_PROBE)
819 AppendAllDeviceList(pulse_device);
820 else if(type == CAPTURE_DEVICE_PROBE)
821 AppendCaptureDeviceList(pulse_capture_device);
823 pulse_unload();
824 } //}}}
825 //}}}