Use a channel-map to specify the output device channel order
[openal-soft/android/lowlatency.git] / Alc / pulseaudio.c
blob3b83d127a6cfb9c3ced416a00e33113ce227c1b8
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 pa_context_state_t state;
231 state = ppa_context_get_state(context);
232 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
234 if(ppa_threaded_mainloop_in_thread(data->loop))
235 ppa_threaded_mainloop_signal(data->loop, 1);
236 else
237 ppa_threaded_mainloop_signal(data->loop, 0);
239 }//}}}
241 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
243 ALCdevice *Device = pdata;
244 pulse_data *data = Device->ExtraData;
245 pa_stream_state_t state;
247 state = ppa_stream_get_state(stream);
248 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
250 if(ppa_threaded_mainloop_in_thread(data->loop))
251 ppa_threaded_mainloop_signal(data->loop, 1);
252 else
253 ppa_threaded_mainloop_signal(data->loop, 0);
255 }//}}}
257 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
259 ALCdevice *Device = pdata;
260 pulse_data *data = Device->ExtraData;
262 SuspendContext(NULL);
264 data->attr = *(ppa_stream_get_buffer_attr(stream));
265 if(data->attr.tlength < data->attr.minreq*2)
266 AL_PRINT("new tlength (%d) is smaller than two periods (%d x 2)!\n",
267 data->attr.tlength, data->attr.minreq);
268 Device->UpdateSize = data->attr.minreq / data->frame_size;
269 Device->NumUpdates = data->attr.tlength/data->attr.minreq;
271 ProcessContext(NULL);
272 }//}}}
274 static void context_state_callback2(pa_context *context, void *pdata) //{{{
276 ALCdevice *Device = pdata;
278 if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
280 AL_PRINT("Received context failure!\n");
281 aluHandleDisconnect(Device);
283 }//}}}
285 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
287 ALCdevice *Device = pdata;
289 if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
291 AL_PRINT("Received stream failure!\n");
292 aluHandleDisconnect(Device);
294 }//}}}
296 //}}}
298 // PulseAudio I/O Callbacks //{{{
299 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
301 ALCdevice *Device = pdata;
302 pulse_data *data = Device->ExtraData;
304 len -= len%data->attr.minreq;
305 if(len > 0)
307 void *buf = ppa_xmalloc0(len);
308 aluMixData(Device, buf, len/data->frame_size);
309 ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE);
311 } //}}}
313 static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
315 ALCdevice *Device = pdata;
316 pulse_data *data = Device->ExtraData;
317 const void *buf;
319 if(ppa_stream_peek(stream, &buf, &length) < 0)
321 AL_PRINT("pa_stream_peek() failed: %s\n",
322 ppa_strerror(ppa_context_errno(data->context)));
323 return;
326 assert(buf);
327 assert(length);
329 length /= data->frame_size;
331 if(data->samples < length)
332 AL_PRINT("stream_read_callback: buffer overflow!\n");
334 WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
336 ppa_stream_drop(stream);
337 } //}}}
338 //}}}
340 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
342 pulse_data *data = ppa_xmalloc0(sizeof(pulse_data));
343 pa_context_state_t state;
345 if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
346 data->context_name = ppa_path_get_filename(data->path_name);
347 else
348 data->context_name = "OpenAL Soft";
350 if(!(data->loop = ppa_threaded_mainloop_new()))
352 AL_PRINT("pa_threaded_mainloop_new() failed!\n");
353 goto out;
356 if(ppa_threaded_mainloop_start(data->loop) < 0)
358 AL_PRINT("pa_threaded_mainloop_start() failed\n");
359 goto out;
362 ppa_threaded_mainloop_lock(data->loop);
363 device->ExtraData = data;
365 data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
366 if(!data->context)
368 AL_PRINT("pa_context_new() failed: %s\n",
369 ppa_strerror(ppa_context_errno(data->context)));
371 ppa_threaded_mainloop_unlock(data->loop);
372 goto out;
375 ppa_context_set_state_callback(data->context, context_state_callback, device);
377 if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
379 AL_PRINT("Context did not connect: %s\n",
380 ppa_strerror(ppa_context_errno(data->context)));
382 ppa_context_unref(data->context);
383 data->context = NULL;
385 ppa_threaded_mainloop_unlock(data->loop);
386 goto out;
389 while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
391 if(!PA_CONTEXT_IS_GOOD(state))
393 AL_PRINT("Context did not get ready: %s\n",
394 ppa_strerror(ppa_context_errno(data->context)));
396 ppa_context_unref(data->context);
397 data->context = NULL;
399 ppa_threaded_mainloop_accept(data->loop);
400 ppa_threaded_mainloop_unlock(data->loop);
401 goto out;
404 ppa_threaded_mainloop_wait(data->loop);
406 ppa_threaded_mainloop_accept(data->loop);
407 ppa_context_set_state_callback(data->context, context_state_callback2, device);
409 device->szDeviceName = strdup(device_name);
411 ppa_threaded_mainloop_unlock(data->loop);
412 return ALC_TRUE;
414 out:
415 if(data->loop)
417 ppa_threaded_mainloop_stop(data->loop);
418 ppa_threaded_mainloop_free(data->loop);
421 device->ExtraData = NULL;
422 ppa_xfree(data);
423 return ALC_FALSE;
424 } //}}}
426 static void pulse_close(ALCdevice *device) //{{{
428 pulse_data *data = device->ExtraData;
430 ppa_threaded_mainloop_lock(data->loop);
432 if(data->stream)
434 ppa_stream_disconnect(data->stream);
435 ppa_stream_unref(data->stream);
438 ppa_context_disconnect(data->context);
439 ppa_context_unref(data->context);
441 ppa_threaded_mainloop_unlock(data->loop);
443 ppa_threaded_mainloop_stop(data->loop);
444 ppa_threaded_mainloop_free(data->loop);
446 DestroyRingBuffer(data->ring);
448 device->ExtraData = NULL;
449 ppa_xfree(data);
450 } //}}}
451 //}}}
453 // OpenAL {{{
454 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
456 if(!device_name)
457 device_name = pulse_device;
458 else if(strcmp(device_name, pulse_device) != 0)
459 return ALC_FALSE;
461 pulse_load();
462 if(!pa_handle)
463 return ALC_FALSE;
465 if(pulse_open(device, device_name) != ALC_FALSE)
466 return ALC_TRUE;
468 pulse_unload();
469 return ALC_FALSE;
470 } //}}}
472 static void pulse_close_playback(ALCdevice *device) //{{{
474 pulse_close(device);
475 pulse_unload();
476 } //}}}
478 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
480 pulse_data *data = device->ExtraData;
481 pa_stream_state_t state;
482 pa_channel_map chanmap;
484 ppa_threaded_mainloop_lock(data->loop);
486 data->frame_size = aluBytesFromFormat(device->Format) *
487 aluChannelsFromFormat(device->Format);
488 data->attr.minreq = data->frame_size * device->UpdateSize;
489 data->attr.prebuf = -1;
490 data->attr.maxlength = -1;
491 data->attr.fragsize = -1;
492 data->attr.tlength = data->attr.minreq * device->NumUpdates;
493 data->stream_name = "Playback Stream";
495 switch(aluBytesFromFormat(device->Format))
497 case 1:
498 data->spec.format = PA_SAMPLE_U8;
499 break;
500 case 2:
501 data->spec.format = PA_SAMPLE_S16NE;
502 break;
503 case 4:
504 data->spec.format = PA_SAMPLE_FLOAT32NE;
505 break;
506 default:
507 AL_PRINT("Unknown format: 0x%x\n", device->Format);
508 ppa_threaded_mainloop_unlock(data->loop);
509 return ALC_FALSE;
511 data->spec.rate = device->Frequency;
512 data->spec.channels = aluChannelsFromFormat(device->Format);
514 if(ppa_sample_spec_valid(&data->spec) == 0)
516 AL_PRINT("Invalid sample format\n");
517 ppa_threaded_mainloop_unlock(data->loop);
518 return ALC_FALSE;
521 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
523 AL_PRINT("Couldn't build map for channel count (%d)!", data->spec.channels);
524 ppa_threaded_mainloop_unlock(data->loop);
525 return ALC_FALSE;
527 SetDefaultWFXChannelOrder(device);
529 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
530 if(!data->stream)
532 AL_PRINT("pa_stream_new() failed: %s\n",
533 ppa_strerror(ppa_context_errno(data->context)));
535 ppa_threaded_mainloop_unlock(data->loop);
536 return ALC_FALSE;
539 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
540 ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
542 if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
544 AL_PRINT("Stream did not connect: %s\n",
545 ppa_strerror(ppa_context_errno(data->context)));
547 ppa_stream_unref(data->stream);
548 data->stream = NULL;
550 ppa_threaded_mainloop_unlock(data->loop);
551 return ALC_FALSE;
554 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
556 if(!PA_STREAM_IS_GOOD(state))
558 AL_PRINT("Stream did not get ready: %s\n",
559 ppa_strerror(ppa_context_errno(data->context)));
561 ppa_stream_unref(data->stream);
562 data->stream = NULL;
564 ppa_threaded_mainloop_accept(data->loop);
565 ppa_threaded_mainloop_unlock(data->loop);
566 return ALC_FALSE;
569 ppa_threaded_mainloop_wait(data->loop);
571 ppa_threaded_mainloop_accept(data->loop);
572 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
574 stream_buffer_attr_callback(data->stream, device);
575 ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
577 ppa_threaded_mainloop_unlock(data->loop);
578 return ALC_TRUE;
579 } //}}}
581 static void pulse_stop_playback(ALCdevice *device) //{{{
583 pulse_data *data = device->ExtraData;
585 if(!data->stream)
586 return;
588 ppa_threaded_mainloop_lock(data->loop);
590 ppa_stream_disconnect(data->stream);
591 ppa_stream_unref(data->stream);
592 data->stream = NULL;
594 ppa_threaded_mainloop_unlock(data->loop);
595 } //}}}
598 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
600 pulse_data *data;
601 pa_stream_state_t state;
603 if(!device_name)
604 device_name = pulse_capture_device;
605 else if(strcmp(device_name, pulse_capture_device) != 0)
606 return ALC_FALSE;
608 pulse_load();
609 if(!pa_handle)
610 return ALC_FALSE;
612 if(pulse_open(device, device_name) == ALC_FALSE)
614 pulse_unload();
615 return ALC_FALSE;
618 data = device->ExtraData;
619 ppa_threaded_mainloop_lock(data->loop);
621 data->samples = device->UpdateSize * device->NumUpdates;
622 data->frame_size = aluBytesFromFormat(device->Format) *
623 aluChannelsFromFormat(device->Format);
625 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
627 ppa_threaded_mainloop_unlock(data->loop);
628 pulse_close(device);
629 pulse_unload();
630 return ALC_FALSE;
633 data->attr.minreq = -1;
634 data->attr.prebuf = -1;
635 data->attr.maxlength = -1;
636 data->attr.tlength = -1;
637 data->attr.fragsize = data->frame_size * data->samples / 2;
638 data->stream_name = "Capture Stream";
640 data->spec.rate = device->Frequency;
641 data->spec.channels = aluChannelsFromFormat(device->Format);
643 switch(aluBytesFromFormat(device->Format))
645 case 1:
646 data->spec.format = PA_SAMPLE_U8;
647 break;
648 case 2:
649 data->spec.format = PA_SAMPLE_S16NE;
650 break;
651 case 4:
652 data->spec.format = PA_SAMPLE_FLOAT32NE;
653 break;
654 default:
655 AL_PRINT("Unknown format: 0x%x\n", device->Format);
656 ppa_threaded_mainloop_unlock(data->loop);
657 pulse_close(device);
658 pulse_unload();
659 return ALC_FALSE;
662 if(ppa_sample_spec_valid(&data->spec) == 0)
664 AL_PRINT("Invalid sample format\n");
665 ppa_threaded_mainloop_unlock(data->loop);
666 pulse_close(device);
667 pulse_unload();
668 return ALC_FALSE;
671 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
672 if(!data->stream)
674 AL_PRINT("pa_stream_new() failed: %s\n",
675 ppa_strerror(ppa_context_errno(data->context)));
677 ppa_threaded_mainloop_unlock(data->loop);
678 pulse_close(device);
679 pulse_unload();
680 return ALC_FALSE;
683 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
685 if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
687 AL_PRINT("Stream did not connect: %s\n",
688 ppa_strerror(ppa_context_errno(data->context)));
690 ppa_stream_unref(data->stream);
691 ppa_threaded_mainloop_unlock(data->loop);
693 data->stream = NULL;
694 pulse_close(device);
695 pulse_unload();
696 return ALC_FALSE;
699 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
701 if(!PA_STREAM_IS_GOOD(state))
703 AL_PRINT("Stream did not get ready: %s\n",
704 ppa_strerror(ppa_context_errno(data->context)));
706 ppa_stream_unref(data->stream);
707 data->stream = NULL;
709 ppa_threaded_mainloop_accept(data->loop);
710 ppa_threaded_mainloop_unlock(data->loop);
712 pulse_close(device);
713 pulse_unload();
714 return ALC_FALSE;
717 ppa_threaded_mainloop_wait(data->loop);
719 ppa_threaded_mainloop_accept(data->loop);
720 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
722 ppa_threaded_mainloop_unlock(data->loop);
723 return ALC_TRUE;
724 } //}}}
726 static void pulse_close_capture(ALCdevice *device) //{{{
728 pulse_close(device);
729 pulse_unload();
730 } //}}}
732 static void pulse_start_capture(ALCdevice *device) //{{{
734 pulse_data *data = device->ExtraData;
736 ppa_threaded_mainloop_lock(data->loop);
737 ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
738 ppa_threaded_mainloop_unlock(data->loop);
739 } //}}}
741 static void pulse_stop_capture(ALCdevice *device) //{{{
743 pulse_data *data = device->ExtraData;
745 ppa_threaded_mainloop_lock(data->loop);
746 ppa_stream_set_read_callback(data->stream, NULL, NULL);
747 ppa_threaded_mainloop_unlock(data->loop);
748 } //}}}
750 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
752 pulse_data *data = device->ExtraData;
753 ALCuint available = RingBufferSize(data->ring);
755 if(available < samples)
756 alcSetError(ALC_INVALID_VALUE);
757 else
758 ReadRingBuffer(data->ring, buffer, samples);
759 } //}}}
761 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
763 pulse_data *data = device->ExtraData;
764 return RingBufferSize(data->ring);
765 } //}}}
767 BackendFuncs pulse_funcs = { //{{{
768 pulse_open_playback,
769 pulse_close_playback,
770 pulse_reset_playback,
771 pulse_stop_playback,
772 pulse_open_capture,
773 pulse_close_capture,
774 pulse_start_capture,
775 pulse_stop_capture,
776 pulse_capture_samples,
777 pulse_available_samples
778 }; //}}}
780 void alc_pulse_init(BackendFuncs *func_list) //{{{
782 *func_list = pulse_funcs;
783 } //}}}
785 void alc_pulse_deinit(void) //{{{
787 } //}}}
789 void alc_pulse_probe(int type) //{{{
791 pulse_load();
792 if(!pa_handle) return;
794 if(type == DEVICE_PROBE)
795 AppendDeviceList(pulse_device);
796 else if(type == ALL_DEVICE_PROBE)
797 AppendAllDeviceList(pulse_device);
798 else if(type == CAPTURE_DEVICE_PROBE)
799 AppendCaptureDeviceList(pulse_capture_device);
801 pulse_unload();
802 } //}}}
803 //}}}