Call the Update effect method for null effects
[openal-soft.git] / Alc / pulseaudio.c
blob722df4f4a108959663c29d66f16bc18b55aed6ef
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_xmalloc0);
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_get_buffer_attr);
81 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
82 MAKE_FUNC(pa_stream_set_read_callback);
83 MAKE_FUNC(pa_stream_set_state_callback);
84 MAKE_FUNC(pa_stream_new);
85 MAKE_FUNC(pa_stream_disconnect);
86 MAKE_FUNC(pa_threaded_mainloop_lock);
87 MAKE_FUNC(pa_channel_map_init_auto);
88 MAKE_FUNC(pa_channel_map_parse);
89 #undef MAKE_FUNC
91 #ifndef PATH_MAX
92 #define PATH_MAX 4096
93 #endif
95 typedef struct {
96 ALCuint samples;
97 ALCuint frame_size;
99 RingBuffer *ring;
101 pa_buffer_attr attr;
102 pa_sample_spec spec;
104 char path_name[PATH_MAX];
105 const char *context_name;
106 const char *stream_name;
108 pa_threaded_mainloop *loop;
110 pa_stream *stream;
111 pa_context *context;
112 } pulse_data;
114 static const ALCchar pulse_device[] = "PulseAudio Software";
115 static const ALCchar pulse_capture_device[] = "PulseAudio Capture";
116 static volatile ALuint load_count;
119 void pulse_load(void) //{{{
121 if(load_count == 0)
123 #ifdef _WIN32
124 pa_handle = LoadLibrary("libpulse-0.dll");
125 #define LOAD_FUNC(x) do { \
126 p##x = GetProcAddress(pa_handle, #x); \
127 if(!(p##x)) { \
128 AL_PRINT("Could not load %s from libpulse-0.dll\n", #x); \
129 FreeLibrary(pa_handle); \
130 pa_handle = NULL; \
131 return; \
133 } while(0)
135 #elif defined (HAVE_DLFCN_H)
137 const char *err;
138 #if defined(__APPLE__) && defined(__MACH__)
139 pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW);
140 #else
141 pa_handle = dlopen("libpulse.so.0", RTLD_NOW);
142 #endif
143 dlerror();
145 #define LOAD_FUNC(x) do { \
146 p##x = dlsym(pa_handle, #x); \
147 if((err=dlerror()) != NULL) { \
148 AL_PRINT("Could not load %s from libpulse: %s\n", #x, err); \
149 dlclose(pa_handle); \
150 pa_handle = NULL; \
151 return; \
153 } while(0)
155 #else
157 pa_handle = (void*)0xDEADBEEF;
158 #define LOAD_FUNC(x) p##x = (x)
160 #endif
161 if(!pa_handle)
162 return;
164 LOAD_FUNC(pa_context_unref);
165 LOAD_FUNC(pa_sample_spec_valid);
166 LOAD_FUNC(pa_stream_drop);
167 LOAD_FUNC(pa_strerror);
168 LOAD_FUNC(pa_context_get_state);
169 LOAD_FUNC(pa_stream_get_state);
170 LOAD_FUNC(pa_threaded_mainloop_signal);
171 LOAD_FUNC(pa_stream_peek);
172 LOAD_FUNC(pa_threaded_mainloop_wait);
173 LOAD_FUNC(pa_threaded_mainloop_unlock);
174 LOAD_FUNC(pa_threaded_mainloop_in_thread);
175 LOAD_FUNC(pa_context_new);
176 LOAD_FUNC(pa_threaded_mainloop_stop);
177 LOAD_FUNC(pa_context_disconnect);
178 LOAD_FUNC(pa_threaded_mainloop_start);
179 LOAD_FUNC(pa_threaded_mainloop_get_api);
180 LOAD_FUNC(pa_context_set_state_callback);
181 LOAD_FUNC(pa_stream_write);
182 LOAD_FUNC(pa_xfree);
183 LOAD_FUNC(pa_stream_connect_record);
184 LOAD_FUNC(pa_stream_connect_playback);
185 LOAD_FUNC(pa_path_get_filename);
186 LOAD_FUNC(pa_get_binary_name);
187 LOAD_FUNC(pa_threaded_mainloop_free);
188 LOAD_FUNC(pa_context_errno);
189 LOAD_FUNC(pa_xmalloc0);
190 LOAD_FUNC(pa_stream_unref);
191 LOAD_FUNC(pa_threaded_mainloop_accept);
192 LOAD_FUNC(pa_stream_set_write_callback);
193 LOAD_FUNC(pa_threaded_mainloop_new);
194 LOAD_FUNC(pa_context_connect);
195 LOAD_FUNC(pa_stream_get_buffer_attr);
196 LOAD_FUNC(pa_stream_set_buffer_attr_callback);
197 LOAD_FUNC(pa_stream_set_read_callback);
198 LOAD_FUNC(pa_stream_set_state_callback);
199 LOAD_FUNC(pa_stream_new);
200 LOAD_FUNC(pa_stream_disconnect);
201 LOAD_FUNC(pa_threaded_mainloop_lock);
202 LOAD_FUNC(pa_channel_map_init_auto);
203 LOAD_FUNC(pa_channel_map_parse);
205 #undef LOAD_FUNC
207 ++load_count;
208 } //}}}
210 void pulse_unload(void) //{{{
212 if(load_count == 0 || --load_count > 0)
213 return;
215 #ifdef _WIN32
216 FreeLibrary(pa_handle);
217 #elif defined (HAVE_DLFCN_H)
218 dlclose(pa_handle);
219 #endif
220 pa_handle = NULL;
221 } //}}}
224 // PulseAudio Event Callbacks //{{{
225 static void context_state_callback(pa_context *context, void *pdata) //{{{
227 ALCdevice *Device = pdata;
228 pulse_data *data = Device->ExtraData;
229 (void)context;
231 if(ppa_threaded_mainloop_in_thread(data->loop))
232 ppa_threaded_mainloop_signal(data->loop, 1);
233 }//}}}
235 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
237 ALCdevice *Device = pdata;
238 pulse_data *data = Device->ExtraData;
239 (void)stream;
241 if(ppa_threaded_mainloop_in_thread(data->loop))
242 ppa_threaded_mainloop_signal(data->loop, 1);
243 }//}}}
245 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
247 ALCdevice *Device = pdata;
248 pulse_data *data = Device->ExtraData;
250 SuspendContext(NULL);
252 data->attr = *(ppa_stream_get_buffer_attr(stream));
253 if((data->attr.tlength%data->attr.minreq) != 0)
254 AL_PRINT("new tlength (%d) is not a multiple of minreq (%d)!\n",
255 data->attr.tlength, data->attr.minreq);
256 Device->UpdateSize = data->attr.minreq;
257 Device->NumUpdates = data->attr.tlength/data->attr.minreq;
259 ProcessContext(NULL);
260 }//}}}
262 static void context_state_callback2(pa_context *context, void *pdata) //{{{
264 ALCdevice *Device = pdata;
266 if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
268 AL_PRINT("Received context failure!\n");
269 aluHandleDisconnect(Device);
271 }//}}}
273 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
275 ALCdevice *Device = pdata;
277 if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
279 AL_PRINT("Received stream failure!\n");
280 aluHandleDisconnect(Device);
282 }//}}}
284 //}}}
286 // PulseAudio I/O Callbacks //{{{
287 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
289 ALCdevice *Device = pdata;
290 pulse_data *data = Device->ExtraData;
292 len -= len%data->attr.minreq;
293 if(len > 0)
295 void *buf = ppa_xmalloc0(len);
296 aluMixData(Device, buf, len/data->frame_size);
297 ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE);
299 } //}}}
301 static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
303 ALCdevice *Device = pdata;
304 pulse_data *data = Device->ExtraData;
305 const void *buf;
307 if(ppa_stream_peek(stream, &buf, &length) < 0)
309 AL_PRINT("pa_stream_peek() failed: %s\n",
310 ppa_strerror(ppa_context_errno(data->context)));
311 return;
314 assert(buf);
315 assert(length);
317 length /= data->frame_size;
319 if(data->samples < length)
320 AL_PRINT("stream_read_callback: buffer overflow!\n");
322 WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
324 ppa_stream_drop(stream);
325 } //}}}
326 //}}}
328 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
330 pulse_data *data = ppa_xmalloc0(sizeof(pulse_data));
331 pa_context_state_t state;
333 if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
334 data->context_name = ppa_path_get_filename(data->path_name);
335 else
336 data->context_name = "OpenAL Soft";
338 if(!(data->loop = ppa_threaded_mainloop_new()))
340 AL_PRINT("pa_threaded_mainloop_new() failed!\n");
341 goto out;
344 if(ppa_threaded_mainloop_start(data->loop) < 0)
346 AL_PRINT("pa_threaded_mainloop_start() failed\n");
347 goto out;
350 ppa_threaded_mainloop_lock(data->loop);
351 device->ExtraData = data;
353 data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
354 if(!data->context)
356 AL_PRINT("pa_context_new() failed: %s\n",
357 ppa_strerror(ppa_context_errno(data->context)));
359 ppa_threaded_mainloop_unlock(data->loop);
360 goto out;
363 ppa_context_set_state_callback(data->context, context_state_callback, device);
365 if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
367 AL_PRINT("Context did not connect: %s\n",
368 ppa_strerror(ppa_context_errno(data->context)));
370 ppa_context_unref(data->context);
371 data->context = NULL;
373 ppa_threaded_mainloop_unlock(data->loop);
374 goto out;
377 while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
379 if(!PA_CONTEXT_IS_GOOD(state))
381 AL_PRINT("Context did not get ready: %s\n",
382 ppa_strerror(ppa_context_errno(data->context)));
384 ppa_context_unref(data->context);
385 data->context = NULL;
387 ppa_threaded_mainloop_unlock(data->loop);
388 goto out;
391 ppa_threaded_mainloop_wait(data->loop);
392 ppa_threaded_mainloop_accept(data->loop);
394 ppa_context_set_state_callback(data->context, context_state_callback2, device);
396 device->szDeviceName = strdup(device_name);
398 ppa_threaded_mainloop_unlock(data->loop);
399 return ALC_TRUE;
401 out:
402 if(data->loop)
404 ppa_threaded_mainloop_stop(data->loop);
405 ppa_threaded_mainloop_free(data->loop);
408 device->ExtraData = NULL;
409 ppa_xfree(data);
410 return ALC_FALSE;
411 } //}}}
413 static void pulse_close(ALCdevice *device) //{{{
415 pulse_data *data = device->ExtraData;
417 ppa_threaded_mainloop_lock(data->loop);
419 if(data->stream)
421 ppa_stream_disconnect(data->stream);
422 ppa_stream_unref(data->stream);
425 ppa_context_disconnect(data->context);
426 ppa_context_unref(data->context);
428 ppa_threaded_mainloop_unlock(data->loop);
430 ppa_threaded_mainloop_stop(data->loop);
431 ppa_threaded_mainloop_free(data->loop);
433 DestroyRingBuffer(data->ring);
435 device->ExtraData = NULL;
436 ppa_xfree(data);
437 } //}}}
438 //}}}
440 // OpenAL {{{
441 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
443 if(!device_name)
444 device_name = pulse_device;
445 else if(strcmp(device_name, pulse_device) != 0)
446 return ALC_FALSE;
448 pulse_load();
449 if(!pa_handle)
450 return ALC_FALSE;
452 if(pulse_open(device, device_name) != ALC_FALSE)
453 return ALC_TRUE;
455 pulse_unload();
456 return ALC_FALSE;
457 } //}}}
459 static void pulse_close_playback(ALCdevice *device) //{{{
461 pulse_close(device);
462 pulse_unload();
463 } //}}}
465 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
467 pulse_data *data = device->ExtraData;
468 pa_stream_state_t state;
469 pa_channel_map chanmap;
471 ppa_threaded_mainloop_lock(data->loop);
473 data->frame_size = aluBytesFromFormat(device->Format) *
474 aluChannelsFromFormat(device->Format);
475 data->attr.minreq = data->frame_size * device->UpdateSize;
476 data->attr.prebuf = -1;
477 data->attr.maxlength = -1;
478 data->attr.fragsize = -1;
479 data->attr.tlength = data->attr.minreq * device->NumUpdates;
480 data->stream_name = "Playback Stream";
482 switch(aluBytesFromFormat(device->Format))
484 case 1:
485 data->spec.format = PA_SAMPLE_U8;
486 break;
487 case 2:
488 data->spec.format = PA_SAMPLE_S16NE;
489 break;
490 case 4:
491 data->spec.format = PA_SAMPLE_FLOAT32NE;
492 break;
493 default:
494 AL_PRINT("Unknown format: 0x%x\n", device->Format);
495 ppa_threaded_mainloop_unlock(data->loop);
496 return ALC_FALSE;
498 data->spec.rate = device->Frequency;
499 data->spec.channels = aluChannelsFromFormat(device->Format);
501 if(ppa_sample_spec_valid(&data->spec) == 0)
503 AL_PRINT("Invalid sample format\n");
504 ppa_threaded_mainloop_unlock(data->loop);
505 return ALC_FALSE;
508 #ifdef _WIN32
509 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
511 AL_PRINT("Couldn't build map for channel count (%d)!", data->spec.channels);
512 ppa_threaded_mainloop_unlock(data->loop);
513 return ALC_FALSE;
515 #else
516 switch(data->spec.channels)
518 case 1:
519 ppa_channel_map_parse(&chanmap, "mono");
520 break;
521 case 2:
522 ppa_channel_map_parse(&chanmap, "front-left,front-right");
523 break;
524 case 4:
525 ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right");
526 break;
527 case 6:
528 ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right,front-center,lfe");
529 break;
530 case 7:
531 ppa_channel_map_parse(&chanmap, "front-left,front-right,front-center,lfe,rear-center,side-left,side-right");
532 break;
533 case 8:
534 ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right");
535 break;
536 default:
537 AL_PRINT("Got unhandled channel count (%d)!", data->spec.channels);
538 ppa_threaded_mainloop_unlock(data->loop);
539 return ALC_FALSE;
541 #endif
543 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
544 if(!data->stream)
546 AL_PRINT("pa_stream_new() failed: %s\n",
547 ppa_strerror(ppa_context_errno(data->context)));
549 ppa_threaded_mainloop_unlock(data->loop);
550 return ALC_FALSE;
553 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
554 ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
556 if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
558 AL_PRINT("Stream did not connect: %s\n",
559 ppa_strerror(ppa_context_errno(data->context)));
561 ppa_stream_unref(data->stream);
562 data->stream = NULL;
564 ppa_threaded_mainloop_unlock(data->loop);
565 return ALC_FALSE;
568 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
570 if(!PA_STREAM_IS_GOOD(state))
572 AL_PRINT("Stream did not get ready: %s\n",
573 ppa_strerror(ppa_context_errno(data->context)));
575 ppa_stream_unref(data->stream);
576 data->stream = NULL;
578 ppa_threaded_mainloop_unlock(data->loop);
579 return ALC_FALSE;
582 ppa_threaded_mainloop_wait(data->loop);
583 ppa_threaded_mainloop_accept(data->loop);
585 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
587 stream_buffer_attr_callback(data->stream, device);
588 ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
590 ppa_threaded_mainloop_unlock(data->loop);
591 return ALC_TRUE;
592 } //}}}
594 static void pulse_stop_playback(ALCdevice *device) //{{{
596 pulse_data *data = device->ExtraData;
598 if(!data->stream)
599 return;
601 ppa_threaded_mainloop_lock(data->loop);
603 ppa_stream_disconnect(data->stream);
604 ppa_stream_unref(data->stream);
605 data->stream = NULL;
607 ppa_threaded_mainloop_unlock(data->loop);
608 } //}}}
611 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
613 pulse_data *data;
614 pa_stream_state_t state;
616 if(!device_name)
617 device_name = pulse_capture_device;
618 else if(strcmp(device_name, pulse_capture_device) != 0)
619 return ALC_FALSE;
621 pulse_load();
622 if(!pa_handle)
623 return ALC_FALSE;
625 if(pulse_open(device, device_name) == ALC_FALSE)
627 pulse_unload();
628 return ALC_FALSE;
631 data = device->ExtraData;
632 ppa_threaded_mainloop_lock(data->loop);
634 data->samples = device->UpdateSize * device->NumUpdates;
635 data->frame_size = aluBytesFromFormat(device->Format) *
636 aluChannelsFromFormat(device->Format);
638 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
640 ppa_threaded_mainloop_unlock(data->loop);
641 pulse_close(device);
642 pulse_unload();
643 return ALC_FALSE;
646 data->attr.minreq = -1;
647 data->attr.prebuf = -1;
648 data->attr.maxlength = -1;
649 data->attr.tlength = -1;
650 data->attr.fragsize = data->frame_size * data->samples / 2;
651 data->stream_name = "Capture Stream";
653 data->spec.rate = device->Frequency;
654 data->spec.channels = aluChannelsFromFormat(device->Format);
656 switch(aluBytesFromFormat(device->Format))
658 case 1:
659 data->spec.format = PA_SAMPLE_U8;
660 break;
661 case 2:
662 data->spec.format = PA_SAMPLE_S16NE;
663 break;
664 case 4:
665 data->spec.format = PA_SAMPLE_FLOAT32NE;
666 break;
667 default:
668 AL_PRINT("Unknown format: 0x%x\n", device->Format);
669 ppa_threaded_mainloop_unlock(data->loop);
670 pulse_close(device);
671 pulse_unload();
672 return ALC_FALSE;
675 if(ppa_sample_spec_valid(&data->spec) == 0)
677 AL_PRINT("Invalid sample format\n");
678 ppa_threaded_mainloop_unlock(data->loop);
679 pulse_close(device);
680 pulse_unload();
681 return ALC_FALSE;
684 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
685 if(!data->stream)
687 AL_PRINT("pa_stream_new() failed: %s\n",
688 ppa_strerror(ppa_context_errno(data->context)));
690 ppa_threaded_mainloop_unlock(data->loop);
691 pulse_close(device);
692 pulse_unload();
693 return ALC_FALSE;
696 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
698 if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
700 AL_PRINT("Stream did not connect: %s\n",
701 ppa_strerror(ppa_context_errno(data->context)));
703 ppa_stream_unref(data->stream);
704 ppa_threaded_mainloop_unlock(data->loop);
706 data->stream = NULL;
707 pulse_close(device);
708 pulse_unload();
709 return ALC_FALSE;
712 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
714 if(!PA_STREAM_IS_GOOD(state))
716 AL_PRINT("Stream did not get ready: %s\n",
717 ppa_strerror(ppa_context_errno(data->context)));
719 ppa_stream_unref(data->stream);
720 ppa_threaded_mainloop_unlock(data->loop);
722 data->stream = NULL;
723 pulse_close(device);
724 pulse_unload();
725 return ALC_FALSE;
728 ppa_threaded_mainloop_wait(data->loop);
729 ppa_threaded_mainloop_accept(data->loop);
731 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
733 ppa_threaded_mainloop_unlock(data->loop);
734 return ALC_TRUE;
735 } //}}}
737 static void pulse_close_capture(ALCdevice *device) //{{{
739 pulse_close(device);
740 pulse_unload();
741 } //}}}
743 static void pulse_start_capture(ALCdevice *device) //{{{
745 pulse_data *data = device->ExtraData;
747 ppa_threaded_mainloop_lock(data->loop);
748 ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
749 ppa_threaded_mainloop_unlock(data->loop);
750 } //}}}
752 static void pulse_stop_capture(ALCdevice *device) //{{{
754 pulse_data *data = device->ExtraData;
756 ppa_threaded_mainloop_lock(data->loop);
757 ppa_stream_set_read_callback(data->stream, NULL, NULL);
758 ppa_threaded_mainloop_unlock(data->loop);
759 } //}}}
761 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
763 pulse_data *data = device->ExtraData;
764 ALCuint available = RingBufferSize(data->ring);
766 if(available < samples)
767 alcSetError(ALC_INVALID_VALUE);
768 else
769 ReadRingBuffer(data->ring, buffer, samples);
770 } //}}}
772 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
774 pulse_data *data = device->ExtraData;
775 return RingBufferSize(data->ring);
776 } //}}}
778 BackendFuncs pulse_funcs = { //{{{
779 pulse_open_playback,
780 pulse_close_playback,
781 pulse_reset_playback,
782 pulse_stop_playback,
783 pulse_open_capture,
784 pulse_close_capture,
785 pulse_start_capture,
786 pulse_stop_capture,
787 pulse_capture_samples,
788 pulse_available_samples
789 }; //}}}
791 void alc_pulse_init(BackendFuncs *func_list) //{{{
793 *func_list = pulse_funcs;
794 } //}}}
796 void alc_pulse_deinit(void) //{{{
798 } //}}}
800 void alc_pulse_probe(int type) //{{{
802 pulse_load();
803 if(!pa_handle) return;
805 if(type == DEVICE_PROBE)
806 AppendDeviceList(pulse_device);
807 else if(type == ALL_DEVICE_PROBE)
808 AppendAllDeviceList(pulse_device);
809 else if(type == CAPTURE_DEVICE_PROBE)
810 AppendCaptureDeviceList(pulse_capture_device);
812 pulse_unload();
813 } //}}}
814 //}}}