Don't wait to accept stream connections
[openal-soft.git] / Alc / pulseaudio.c
blob5bbdb6b6478c69ff826a3d3bffc21153e13ef0e5
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, 0);
237 }//}}}
239 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
241 ALCdevice *Device = pdata;
242 pulse_data *data = Device->ExtraData;
243 pa_stream_state_t state;
245 state = ppa_stream_get_state(stream);
246 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
248 if(ppa_threaded_mainloop_in_thread(data->loop))
249 ppa_threaded_mainloop_signal(data->loop, 0);
251 }//}}}
253 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
255 ALCdevice *Device = pdata;
256 pulse_data *data = Device->ExtraData;
258 SuspendContext(NULL);
260 data->attr = *(ppa_stream_get_buffer_attr(stream));
261 if(data->attr.tlength < data->attr.minreq*2)
262 AL_PRINT("new tlength (%d) is smaller than two periods (%d x 2)!\n",
263 data->attr.tlength, data->attr.minreq);
264 Device->UpdateSize = data->attr.minreq / data->frame_size;
265 Device->NumUpdates = data->attr.tlength/data->attr.minreq;
267 ProcessContext(NULL);
268 }//}}}
270 static void context_state_callback2(pa_context *context, void *pdata) //{{{
272 ALCdevice *Device = pdata;
274 if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
276 AL_PRINT("Received context failure!\n");
277 aluHandleDisconnect(Device);
279 }//}}}
281 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
283 ALCdevice *Device = pdata;
285 if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
287 AL_PRINT("Received stream failure!\n");
288 aluHandleDisconnect(Device);
290 }//}}}
292 //}}}
294 // PulseAudio I/O Callbacks //{{{
295 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
297 ALCdevice *Device = pdata;
298 pulse_data *data = Device->ExtraData;
300 len -= len%data->attr.minreq;
301 if(len > 0)
303 void *buf = ppa_xmalloc0(len);
304 aluMixData(Device, buf, len/data->frame_size);
305 ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE);
307 } //}}}
309 static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
311 ALCdevice *Device = pdata;
312 pulse_data *data = Device->ExtraData;
313 const void *buf;
315 if(ppa_stream_peek(stream, &buf, &length) < 0)
317 AL_PRINT("pa_stream_peek() failed: %s\n",
318 ppa_strerror(ppa_context_errno(data->context)));
319 return;
322 assert(buf);
323 assert(length);
325 length /= data->frame_size;
327 if(data->samples < length)
328 AL_PRINT("stream_read_callback: buffer overflow!\n");
330 WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
332 ppa_stream_drop(stream);
333 } //}}}
334 //}}}
336 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
338 pulse_data *data = ppa_xmalloc0(sizeof(pulse_data));
339 pa_context_state_t state;
341 if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
342 data->context_name = ppa_path_get_filename(data->path_name);
343 else
344 data->context_name = "OpenAL Soft";
346 if(!(data->loop = ppa_threaded_mainloop_new()))
348 AL_PRINT("pa_threaded_mainloop_new() failed!\n");
349 goto out;
352 if(ppa_threaded_mainloop_start(data->loop) < 0)
354 AL_PRINT("pa_threaded_mainloop_start() failed\n");
355 goto out;
358 ppa_threaded_mainloop_lock(data->loop);
359 device->ExtraData = data;
361 data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
362 if(!data->context)
364 AL_PRINT("pa_context_new() failed: %s\n",
365 ppa_strerror(ppa_context_errno(data->context)));
367 ppa_threaded_mainloop_unlock(data->loop);
368 goto out;
371 ppa_context_set_state_callback(data->context, context_state_callback, device);
373 if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
375 AL_PRINT("Context did not connect: %s\n",
376 ppa_strerror(ppa_context_errno(data->context)));
378 ppa_context_unref(data->context);
379 data->context = NULL;
381 ppa_threaded_mainloop_unlock(data->loop);
382 goto out;
385 while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
387 if(!PA_CONTEXT_IS_GOOD(state))
389 AL_PRINT("Context did not get ready: %s\n",
390 ppa_strerror(ppa_context_errno(data->context)));
392 ppa_context_unref(data->context);
393 data->context = NULL;
395 ppa_threaded_mainloop_unlock(data->loop);
396 goto out;
399 ppa_threaded_mainloop_wait(data->loop);
401 ppa_context_set_state_callback(data->context, context_state_callback2, device);
403 device->szDeviceName = strdup(device_name);
405 ppa_threaded_mainloop_unlock(data->loop);
406 return ALC_TRUE;
408 out:
409 if(data->loop)
411 ppa_threaded_mainloop_stop(data->loop);
412 ppa_threaded_mainloop_free(data->loop);
415 device->ExtraData = NULL;
416 ppa_xfree(data);
417 return ALC_FALSE;
418 } //}}}
420 static void pulse_close(ALCdevice *device) //{{{
422 pulse_data *data = device->ExtraData;
424 ppa_threaded_mainloop_lock(data->loop);
426 if(data->stream)
428 ppa_stream_disconnect(data->stream);
429 ppa_stream_unref(data->stream);
432 ppa_context_disconnect(data->context);
433 ppa_context_unref(data->context);
435 ppa_threaded_mainloop_unlock(data->loop);
437 ppa_threaded_mainloop_stop(data->loop);
438 ppa_threaded_mainloop_free(data->loop);
440 DestroyRingBuffer(data->ring);
442 device->ExtraData = NULL;
443 ppa_xfree(data);
444 } //}}}
445 //}}}
447 // OpenAL {{{
448 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
450 if(!device_name)
451 device_name = pulse_device;
452 else if(strcmp(device_name, pulse_device) != 0)
453 return ALC_FALSE;
455 pulse_load();
456 if(!pa_handle)
457 return ALC_FALSE;
459 if(pulse_open(device, device_name) != ALC_FALSE)
460 return ALC_TRUE;
462 pulse_unload();
463 return ALC_FALSE;
464 } //}}}
466 static void pulse_close_playback(ALCdevice *device) //{{{
468 pulse_close(device);
469 pulse_unload();
470 } //}}}
472 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
474 pulse_data *data = device->ExtraData;
475 pa_stream_state_t state;
476 pa_channel_map chanmap;
478 ppa_threaded_mainloop_lock(data->loop);
480 data->frame_size = aluBytesFromFormat(device->Format) *
481 aluChannelsFromFormat(device->Format);
482 data->attr.minreq = data->frame_size * device->UpdateSize;
483 data->attr.prebuf = -1;
484 data->attr.maxlength = -1;
485 data->attr.fragsize = -1;
486 data->attr.tlength = data->attr.minreq * device->NumUpdates;
487 data->stream_name = "Playback Stream";
489 switch(aluBytesFromFormat(device->Format))
491 case 1:
492 data->spec.format = PA_SAMPLE_U8;
493 break;
494 case 2:
495 data->spec.format = PA_SAMPLE_S16NE;
496 break;
497 case 4:
498 data->spec.format = PA_SAMPLE_FLOAT32NE;
499 break;
500 default:
501 AL_PRINT("Unknown format: 0x%x\n", device->Format);
502 ppa_threaded_mainloop_unlock(data->loop);
503 return ALC_FALSE;
505 data->spec.rate = device->Frequency;
506 data->spec.channels = aluChannelsFromFormat(device->Format);
508 if(ppa_sample_spec_valid(&data->spec) == 0)
510 AL_PRINT("Invalid sample format\n");
511 ppa_threaded_mainloop_unlock(data->loop);
512 return ALC_FALSE;
515 if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
517 AL_PRINT("Couldn't build map for channel count (%d)!", data->spec.channels);
518 ppa_threaded_mainloop_unlock(data->loop);
519 return ALC_FALSE;
521 SetDefaultWFXChannelOrder(device);
523 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
524 if(!data->stream)
526 AL_PRINT("pa_stream_new() failed: %s\n",
527 ppa_strerror(ppa_context_errno(data->context)));
529 ppa_threaded_mainloop_unlock(data->loop);
530 return ALC_FALSE;
533 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
535 if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
537 AL_PRINT("Stream did not connect: %s\n",
538 ppa_strerror(ppa_context_errno(data->context)));
540 ppa_stream_unref(data->stream);
541 data->stream = NULL;
543 ppa_threaded_mainloop_unlock(data->loop);
544 return ALC_FALSE;
547 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
549 if(!PA_STREAM_IS_GOOD(state))
551 AL_PRINT("Stream did not get ready: %s\n",
552 ppa_strerror(ppa_context_errno(data->context)));
554 ppa_stream_unref(data->stream);
555 data->stream = NULL;
557 ppa_threaded_mainloop_unlock(data->loop);
558 return ALC_FALSE;
561 ppa_threaded_mainloop_wait(data->loop);
563 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
565 stream_buffer_attr_callback(data->stream, device);
566 ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
568 stream_write_callback(data->stream, data->attr.tlength, device);
569 ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
571 ppa_threaded_mainloop_unlock(data->loop);
572 return ALC_TRUE;
573 } //}}}
575 static void pulse_stop_playback(ALCdevice *device) //{{{
577 pulse_data *data = device->ExtraData;
579 if(!data->stream)
580 return;
582 ppa_threaded_mainloop_lock(data->loop);
584 ppa_stream_disconnect(data->stream);
585 ppa_stream_unref(data->stream);
586 data->stream = NULL;
588 ppa_threaded_mainloop_unlock(data->loop);
589 } //}}}
592 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
594 pulse_data *data;
595 pa_stream_state_t state;
597 if(!device_name)
598 device_name = pulse_capture_device;
599 else if(strcmp(device_name, pulse_capture_device) != 0)
600 return ALC_FALSE;
602 pulse_load();
603 if(!pa_handle)
604 return ALC_FALSE;
606 if(pulse_open(device, device_name) == ALC_FALSE)
608 pulse_unload();
609 return ALC_FALSE;
612 data = device->ExtraData;
613 ppa_threaded_mainloop_lock(data->loop);
615 data->samples = device->UpdateSize * device->NumUpdates;
616 data->frame_size = aluBytesFromFormat(device->Format) *
617 aluChannelsFromFormat(device->Format);
619 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
621 ppa_threaded_mainloop_unlock(data->loop);
622 pulse_close(device);
623 pulse_unload();
624 return ALC_FALSE;
627 data->attr.minreq = -1;
628 data->attr.prebuf = -1;
629 data->attr.maxlength = -1;
630 data->attr.tlength = -1;
631 data->attr.fragsize = data->frame_size * data->samples / 2;
632 data->stream_name = "Capture Stream";
634 data->spec.rate = device->Frequency;
635 data->spec.channels = aluChannelsFromFormat(device->Format);
637 switch(aluBytesFromFormat(device->Format))
639 case 1:
640 data->spec.format = PA_SAMPLE_U8;
641 break;
642 case 2:
643 data->spec.format = PA_SAMPLE_S16NE;
644 break;
645 case 4:
646 data->spec.format = PA_SAMPLE_FLOAT32NE;
647 break;
648 default:
649 AL_PRINT("Unknown format: 0x%x\n", device->Format);
650 ppa_threaded_mainloop_unlock(data->loop);
651 pulse_close(device);
652 pulse_unload();
653 return ALC_FALSE;
656 if(ppa_sample_spec_valid(&data->spec) == 0)
658 AL_PRINT("Invalid sample format\n");
659 ppa_threaded_mainloop_unlock(data->loop);
660 pulse_close(device);
661 pulse_unload();
662 return ALC_FALSE;
665 data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
666 if(!data->stream)
668 AL_PRINT("pa_stream_new() failed: %s\n",
669 ppa_strerror(ppa_context_errno(data->context)));
671 ppa_threaded_mainloop_unlock(data->loop);
672 pulse_close(device);
673 pulse_unload();
674 return ALC_FALSE;
677 ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
679 if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
681 AL_PRINT("Stream did not connect: %s\n",
682 ppa_strerror(ppa_context_errno(data->context)));
684 ppa_stream_unref(data->stream);
685 ppa_threaded_mainloop_unlock(data->loop);
687 data->stream = NULL;
688 pulse_close(device);
689 pulse_unload();
690 return ALC_FALSE;
693 while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
695 if(!PA_STREAM_IS_GOOD(state))
697 AL_PRINT("Stream did not get ready: %s\n",
698 ppa_strerror(ppa_context_errno(data->context)));
700 ppa_stream_unref(data->stream);
701 data->stream = NULL;
703 ppa_threaded_mainloop_unlock(data->loop);
705 pulse_close(device);
706 pulse_unload();
707 return ALC_FALSE;
710 ppa_threaded_mainloop_wait(data->loop);
712 ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
714 ppa_threaded_mainloop_unlock(data->loop);
715 return ALC_TRUE;
716 } //}}}
718 static void pulse_close_capture(ALCdevice *device) //{{{
720 pulse_close(device);
721 pulse_unload();
722 } //}}}
724 static void pulse_start_capture(ALCdevice *device) //{{{
726 pulse_data *data = device->ExtraData;
728 ppa_threaded_mainloop_lock(data->loop);
729 ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
730 ppa_threaded_mainloop_unlock(data->loop);
731 } //}}}
733 static void pulse_stop_capture(ALCdevice *device) //{{{
735 pulse_data *data = device->ExtraData;
737 ppa_threaded_mainloop_lock(data->loop);
738 ppa_stream_set_read_callback(data->stream, NULL, NULL);
739 ppa_threaded_mainloop_unlock(data->loop);
740 } //}}}
742 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
744 pulse_data *data = device->ExtraData;
745 ALCuint available = RingBufferSize(data->ring);
747 if(available < samples)
748 alcSetError(ALC_INVALID_VALUE);
749 else
750 ReadRingBuffer(data->ring, buffer, samples);
751 } //}}}
753 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
755 pulse_data *data = device->ExtraData;
756 return RingBufferSize(data->ring);
757 } //}}}
759 BackendFuncs pulse_funcs = { //{{{
760 pulse_open_playback,
761 pulse_close_playback,
762 pulse_reset_playback,
763 pulse_stop_playback,
764 pulse_open_capture,
765 pulse_close_capture,
766 pulse_start_capture,
767 pulse_stop_capture,
768 pulse_capture_samples,
769 pulse_available_samples
770 }; //}}}
772 void alc_pulse_init(BackendFuncs *func_list) //{{{
774 *func_list = pulse_funcs;
775 } //}}}
777 void alc_pulse_deinit(void) //{{{
779 } //}}}
781 void alc_pulse_probe(int type) //{{{
783 pulse_load();
784 if(!pa_handle) return;
786 if(type == DEVICE_PROBE)
787 AppendDeviceList(pulse_device);
788 else if(type == ALL_DEVICE_PROBE)
789 AppendAllDeviceList(pulse_device);
790 else if(type == CAPTURE_DEVICE_PROBE)
791 AppendCaptureDeviceList(pulse_capture_device);
793 pulse_unload();
794 } //}}}
795 //}}}