zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
pulseaudio.c
Go to the documentation of this file.
1 
22 #include "config.h"
23 
24 #include <string.h>
25 
26 #include "alMain.h"
27 #include "alu.h"
28 
29 #include <pulse/pulseaudio.h>
30 
31 #if PA_API_VERSION == 12
32 
33 #ifndef PA_CHECK_VERSION
34 #define PA_CHECK_VERSION(major,minor,micro) \
35  ((PA_MAJOR > (major)) || \
36  (PA_MAJOR == (major) && PA_MINOR > (minor)) || \
37  (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
38 #endif
39 
40 #ifdef HAVE_DYNLOAD
41 static void *pa_handle;
42 #define MAKE_FUNC(x) static typeof(x) * p##x
43 MAKE_FUNC(pa_context_unref);
44 MAKE_FUNC(pa_sample_spec_valid);
45 MAKE_FUNC(pa_frame_size);
46 MAKE_FUNC(pa_stream_drop);
47 MAKE_FUNC(pa_strerror);
48 MAKE_FUNC(pa_context_get_state);
49 MAKE_FUNC(pa_stream_get_state);
50 MAKE_FUNC(pa_threaded_mainloop_signal);
51 MAKE_FUNC(pa_stream_peek);
52 MAKE_FUNC(pa_threaded_mainloop_wait);
53 MAKE_FUNC(pa_threaded_mainloop_unlock);
54 MAKE_FUNC(pa_threaded_mainloop_in_thread);
55 MAKE_FUNC(pa_context_new);
56 MAKE_FUNC(pa_threaded_mainloop_stop);
57 MAKE_FUNC(pa_context_disconnect);
58 MAKE_FUNC(pa_threaded_mainloop_start);
59 MAKE_FUNC(pa_threaded_mainloop_get_api);
60 MAKE_FUNC(pa_context_set_state_callback);
61 MAKE_FUNC(pa_stream_write);
62 MAKE_FUNC(pa_xfree);
63 MAKE_FUNC(pa_stream_connect_record);
64 MAKE_FUNC(pa_stream_connect_playback);
65 MAKE_FUNC(pa_stream_readable_size);
66 MAKE_FUNC(pa_stream_writable_size);
67 MAKE_FUNC(pa_stream_is_corked);
68 MAKE_FUNC(pa_stream_cork);
69 MAKE_FUNC(pa_stream_is_suspended);
70 MAKE_FUNC(pa_stream_get_device_name);
71 MAKE_FUNC(pa_stream_get_latency);
72 MAKE_FUNC(pa_path_get_filename);
73 MAKE_FUNC(pa_get_binary_name);
74 MAKE_FUNC(pa_threaded_mainloop_free);
75 MAKE_FUNC(pa_context_errno);
76 MAKE_FUNC(pa_xmalloc);
77 MAKE_FUNC(pa_stream_unref);
78 MAKE_FUNC(pa_threaded_mainloop_accept);
79 MAKE_FUNC(pa_stream_set_write_callback);
80 MAKE_FUNC(pa_threaded_mainloop_new);
81 MAKE_FUNC(pa_context_connect);
82 MAKE_FUNC(pa_stream_set_buffer_attr);
83 MAKE_FUNC(pa_stream_get_buffer_attr);
84 MAKE_FUNC(pa_stream_get_sample_spec);
85 MAKE_FUNC(pa_stream_get_time);
86 MAKE_FUNC(pa_stream_set_read_callback);
87 MAKE_FUNC(pa_stream_set_state_callback);
88 MAKE_FUNC(pa_stream_set_moved_callback);
89 MAKE_FUNC(pa_stream_set_underflow_callback);
90 MAKE_FUNC(pa_stream_new_with_proplist);
91 MAKE_FUNC(pa_stream_disconnect);
92 MAKE_FUNC(pa_threaded_mainloop_lock);
93 MAKE_FUNC(pa_channel_map_init_auto);
94 MAKE_FUNC(pa_channel_map_parse);
95 MAKE_FUNC(pa_channel_map_snprint);
96 MAKE_FUNC(pa_channel_map_equal);
97 MAKE_FUNC(pa_context_get_server_info);
98 MAKE_FUNC(pa_context_get_sink_info_by_name);
99 MAKE_FUNC(pa_context_get_sink_info_list);
100 MAKE_FUNC(pa_context_get_source_info_by_name);
101 MAKE_FUNC(pa_context_get_source_info_list);
102 MAKE_FUNC(pa_operation_get_state);
103 MAKE_FUNC(pa_operation_unref);
104 MAKE_FUNC(pa_proplist_new);
105 MAKE_FUNC(pa_proplist_free);
106 MAKE_FUNC(pa_proplist_set);
107 #if PA_CHECK_VERSION(0,9,15)
108 MAKE_FUNC(pa_channel_map_superset);
109 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
110 #endif
111 #if PA_CHECK_VERSION(0,9,16)
112 MAKE_FUNC(pa_stream_begin_write);
113 #endif
114 #undef MAKE_FUNC
115 
116 #define pa_context_unref ppa_context_unref
117 #define pa_sample_spec_valid ppa_sample_spec_valid
118 #define pa_frame_size ppa_frame_size
119 #define pa_stream_drop ppa_stream_drop
120 #define pa_strerror ppa_strerror
121 #define pa_context_get_state ppa_context_get_state
122 #define pa_stream_get_state ppa_stream_get_state
123 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal
124 #define pa_stream_peek ppa_stream_peek
125 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait
126 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock
127 #define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread
128 #define pa_context_new ppa_context_new
129 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop
130 #define pa_context_disconnect ppa_context_disconnect
131 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start
132 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api
133 #define pa_context_set_state_callback ppa_context_set_state_callback
134 #define pa_stream_write ppa_stream_write
135 #define pa_xfree ppa_xfree
136 #define pa_stream_connect_record ppa_stream_connect_record
137 #define pa_stream_connect_playback ppa_stream_connect_playback
138 #define pa_stream_readable_size ppa_stream_readable_size
139 #define pa_stream_writable_size ppa_stream_writable_size
140 #define pa_stream_is_corked ppa_stream_is_corked
141 #define pa_stream_cork ppa_stream_cork
142 #define pa_stream_is_suspended ppa_stream_is_suspended
143 #define pa_stream_get_device_name ppa_stream_get_device_name
144 #define pa_stream_get_latency ppa_stream_get_latency
145 #define pa_path_get_filename ppa_path_get_filename
146 #define pa_get_binary_name ppa_get_binary_name
147 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free
148 #define pa_context_errno ppa_context_errno
149 #define pa_xmalloc ppa_xmalloc
150 #define pa_stream_unref ppa_stream_unref
151 #define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept
152 #define pa_stream_set_write_callback ppa_stream_set_write_callback
153 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new
154 #define pa_context_connect ppa_context_connect
155 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
156 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
157 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
158 #define pa_stream_get_time ppa_stream_get_time
159 #define pa_stream_set_read_callback ppa_stream_set_read_callback
160 #define pa_stream_set_state_callback ppa_stream_set_state_callback
161 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
162 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
163 #define pa_stream_new_with_proplist ppa_stream_new_with_proplist
164 #define pa_stream_disconnect ppa_stream_disconnect
165 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock
166 #define pa_channel_map_init_auto ppa_channel_map_init_auto
167 #define pa_channel_map_parse ppa_channel_map_parse
168 #define pa_channel_map_snprint ppa_channel_map_snprint
169 #define pa_channel_map_equal ppa_channel_map_equal
170 #define pa_context_get_server_info ppa_context_get_server_info
171 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
172 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
173 #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
174 #define pa_context_get_source_info_list ppa_context_get_source_info_list
175 #define pa_operation_get_state ppa_operation_get_state
176 #define pa_operation_unref ppa_operation_unref
177 #define pa_proplist_new ppa_proplist_new
178 #define pa_proplist_free ppa_proplist_free
179 #define pa_proplist_set ppa_proplist_set
180 #if PA_CHECK_VERSION(0,9,15)
181 #define pa_channel_map_superset ppa_channel_map_superset
182 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
183 #endif
184 #if PA_CHECK_VERSION(0,9,16)
185 #define pa_stream_begin_write ppa_stream_begin_write
186 #endif
187 
188 #endif
189 
190 #ifndef PATH_MAX
191 #define PATH_MAX 4096
192 #endif
193 
194 typedef struct {
195  char *device_name;
196 
197  const void *cap_store;
198  size_t cap_len;
199  size_t cap_remain;
200 
201  ALCuint last_readable;
202 
203  pa_buffer_attr attr;
204  pa_sample_spec spec;
205 
206  pa_threaded_mainloop *loop;
207 
208  ALvoid *thread;
209  volatile ALboolean killNow;
210 
211  pa_stream *stream;
212  pa_context *context;
213 } pulse_data;
214 
215 typedef struct {
216  char *name;
217  char *device_name;
218 } DevMap;
219 
220 
221 static DevMap *allDevNameMap;
222 static ALuint numDevNames;
223 static DevMap *allCaptureDevNameMap;
225 static pa_context_flags_t pulse_ctx_flags;
226 static pa_proplist *prop_filter;
227 
228 
229 static ALCboolean pulse_load(void)
230 {
232 #ifdef HAVE_DYNLOAD
233  if(!pa_handle)
234  {
235 #ifdef _WIN32
236 #define PALIB "libpulse-0.dll"
237 #elif defined(__APPLE__) && defined(__MACH__)
238 #define PALIB "libpulse.0.dylib"
239 #else
240 #define PALIB "libpulse.so.0"
241 #endif
242  pa_handle = LoadLib(PALIB);
243  if(!pa_handle)
244  return ALC_FALSE;
245 
246 #define LOAD_FUNC(x) do { \
247  p##x = GetSymbol(pa_handle, #x); \
248  if(!(p##x)) { \
249  ret = ALC_FALSE; \
250  } \
251 } while(0)
252  LOAD_FUNC(pa_context_unref);
253  LOAD_FUNC(pa_sample_spec_valid);
254  LOAD_FUNC(pa_stream_drop);
255  LOAD_FUNC(pa_frame_size);
256  LOAD_FUNC(pa_strerror);
257  LOAD_FUNC(pa_context_get_state);
258  LOAD_FUNC(pa_stream_get_state);
259  LOAD_FUNC(pa_threaded_mainloop_signal);
260  LOAD_FUNC(pa_stream_peek);
261  LOAD_FUNC(pa_threaded_mainloop_wait);
262  LOAD_FUNC(pa_threaded_mainloop_unlock);
263  LOAD_FUNC(pa_threaded_mainloop_in_thread);
264  LOAD_FUNC(pa_context_new);
265  LOAD_FUNC(pa_threaded_mainloop_stop);
266  LOAD_FUNC(pa_context_disconnect);
267  LOAD_FUNC(pa_threaded_mainloop_start);
268  LOAD_FUNC(pa_threaded_mainloop_get_api);
269  LOAD_FUNC(pa_context_set_state_callback);
270  LOAD_FUNC(pa_stream_write);
271  LOAD_FUNC(pa_xfree);
272  LOAD_FUNC(pa_stream_connect_record);
273  LOAD_FUNC(pa_stream_connect_playback);
274  LOAD_FUNC(pa_stream_readable_size);
275  LOAD_FUNC(pa_stream_writable_size);
276  LOAD_FUNC(pa_stream_is_corked);
277  LOAD_FUNC(pa_stream_cork);
278  LOAD_FUNC(pa_stream_is_suspended);
279  LOAD_FUNC(pa_stream_get_device_name);
280  LOAD_FUNC(pa_stream_get_latency);
281  LOAD_FUNC(pa_path_get_filename);
282  LOAD_FUNC(pa_get_binary_name);
283  LOAD_FUNC(pa_threaded_mainloop_free);
284  LOAD_FUNC(pa_context_errno);
285  LOAD_FUNC(pa_xmalloc);
286  LOAD_FUNC(pa_stream_unref);
287  LOAD_FUNC(pa_threaded_mainloop_accept);
288  LOAD_FUNC(pa_stream_set_write_callback);
289  LOAD_FUNC(pa_threaded_mainloop_new);
290  LOAD_FUNC(pa_context_connect);
291  LOAD_FUNC(pa_stream_set_buffer_attr);
292  LOAD_FUNC(pa_stream_get_buffer_attr);
293  LOAD_FUNC(pa_stream_get_sample_spec);
294  LOAD_FUNC(pa_stream_get_time);
295  LOAD_FUNC(pa_stream_set_read_callback);
296  LOAD_FUNC(pa_stream_set_state_callback);
297  LOAD_FUNC(pa_stream_set_moved_callback);
298  LOAD_FUNC(pa_stream_set_underflow_callback);
299  LOAD_FUNC(pa_stream_new_with_proplist);
300  LOAD_FUNC(pa_stream_disconnect);
301  LOAD_FUNC(pa_threaded_mainloop_lock);
302  LOAD_FUNC(pa_channel_map_init_auto);
303  LOAD_FUNC(pa_channel_map_parse);
304  LOAD_FUNC(pa_channel_map_snprint);
305  LOAD_FUNC(pa_channel_map_equal);
306  LOAD_FUNC(pa_context_get_server_info);
307  LOAD_FUNC(pa_context_get_sink_info_by_name);
308  LOAD_FUNC(pa_context_get_sink_info_list);
309  LOAD_FUNC(pa_context_get_source_info_by_name);
310  LOAD_FUNC(pa_context_get_source_info_list);
311  LOAD_FUNC(pa_operation_get_state);
312  LOAD_FUNC(pa_operation_unref);
313  LOAD_FUNC(pa_proplist_new);
314  LOAD_FUNC(pa_proplist_free);
315  LOAD_FUNC(pa_proplist_set);
316 #undef LOAD_FUNC
317 #define LOAD_OPTIONAL_FUNC(x) do { \
318  p##x = GetSymbol(pa_handle, #x); \
319 } while(0)
320 #if PA_CHECK_VERSION(0,9,15)
321  LOAD_OPTIONAL_FUNC(pa_channel_map_superset);
322  LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback);
323 #endif
324 #if PA_CHECK_VERSION(0,9,16)
325  LOAD_OPTIONAL_FUNC(pa_stream_begin_write);
326 #endif
327 #undef LOAD_OPTIONAL_FUNC
328 
329  if(ret == ALC_FALSE)
330  {
331  CloseLib(pa_handle);
332  pa_handle = NULL;
333  }
334  }
335 #endif /* HAVE_DYNLOAD */
336  return ret;
337 }
338 
339 /* PulseAudio Event Callbacks */
340 static void context_state_callback(pa_context *context, void *pdata)
341 {
342  pa_threaded_mainloop *loop = pdata;
343  pa_context_state_t state;
344 
345  state = pa_context_get_state(context);
346  if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
347  pa_threaded_mainloop_signal(loop, 0);
348 }
349 
350 static void stream_state_callback(pa_stream *stream, void *pdata)
351 {
352  pa_threaded_mainloop *loop = pdata;
353  pa_stream_state_t state;
354 
355  state = pa_stream_get_state(stream);
356  if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
357  pa_threaded_mainloop_signal(loop, 0);
358 }
359 
360 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata)
361 {
362  ALCdevice *device = pdata;
363  pulse_data *data = device->ExtraData;
364 
365  data->attr = *pa_stream_get_buffer_attr(stream);
366  TRACE("minreq=%d, tlength=%d, prebuf=%d\n", data->attr.minreq, data->attr.tlength, data->attr.prebuf);
367 }
368 
369 static void context_state_callback2(pa_context *context, void *pdata)
370 {
371  ALCdevice *Device = pdata;
372  pulse_data *data = Device->ExtraData;
373 
374  if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
375  {
376  ERR("Received context failure!\n");
377  aluHandleDisconnect(Device);
378  }
379  pa_threaded_mainloop_signal(data->loop, 0);
380 }
381 
382 static void stream_state_callback2(pa_stream *stream, void *pdata)
383 {
384  ALCdevice *Device = pdata;
385  pulse_data *data = Device->ExtraData;
386 
387  if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
388  {
389  ERR("Received stream failure!\n");
390  aluHandleDisconnect(Device);
391  }
392  pa_threaded_mainloop_signal(data->loop, 0);
393 }
394 
395 static void stream_success_callback(pa_stream *stream, int success, void *pdata)
396 {
397  ALCdevice *Device = pdata;
398  pulse_data *data = Device->ExtraData;
399  (void)stream;
400  (void)success;
401 
402  pa_threaded_mainloop_signal(data->loop, 0);
403 }
404 
405 static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
406 {
407  ALCdevice *device = pdata;
408  pulse_data *data = device->ExtraData;
409  char chanmap_str[256] = "";
410  const struct {
411  const char *str;
412  enum DevFmtChannels chans;
413  } chanmaps[] = {
414  { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
415  DevFmtX71 },
416  { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
417  DevFmtX61 },
418  { "front-left,front-right,front-center,lfe,rear-left,rear-right",
419  DevFmtX51 },
420  { "front-left,front-right,front-center,lfe,side-left,side-right",
421  DevFmtX51Side },
422  { "front-left,front-right,rear-left,rear-right", DevFmtQuad },
423  { "front-left,front-right", DevFmtStereo },
424  { "mono", DevFmtMono },
425  { NULL, 0 }
426  };
427  int i;
428  (void)context;
429 
430  if(eol)
431  {
432  pa_threaded_mainloop_signal(data->loop, 0);
433  return;
434  }
435 
436  for(i = 0;chanmaps[i].str;i++)
437  {
438  pa_channel_map map;
439  if(!pa_channel_map_parse(&map, chanmaps[i].str))
440  continue;
441 
442  if(pa_channel_map_equal(&info->channel_map, &map)
443 #if PA_CHECK_VERSION(0,9,15)
444  || (pa_channel_map_superset &&
445  pa_channel_map_superset(&info->channel_map, &map))
446 #endif
447  )
448  {
449  device->FmtChans = chanmaps[i].chans;
450  return;
451  }
452  }
453 
454  pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
455  ERR("Failed to find format for channel map:\n %s\n", chanmap_str);
456 }
457 
458 static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
459 {
460  pa_threaded_mainloop *loop = pdata;
461  void *temp;
462  ALuint i;
463 
464  (void)context;
465 
466  if(eol)
467  {
468  pa_threaded_mainloop_signal(loop, 0);
469  return;
470  }
471 
472  for(i = 0;i < numDevNames;i++)
473  {
474  if(strcmp(info->name, allDevNameMap[i].device_name) == 0)
475  return;
476  }
477 
478  TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
479 
480  temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap));
481  if(temp)
482  {
483  allDevNameMap = temp;
484  allDevNameMap[numDevNames].name = strdup(info->description);
485  allDevNameMap[numDevNames].device_name = strdup(info->name);
486  numDevNames++;
487  }
488 }
489 
490 static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata)
491 {
492  pa_threaded_mainloop *loop = pdata;
493  void *temp;
494  ALuint i;
495 
496  (void)context;
497 
498  if(eol)
499  {
500  pa_threaded_mainloop_signal(loop, 0);
501  return;
502  }
503 
504  for(i = 0;i < numCaptureDevNames;i++)
505  {
506  if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0)
507  return;
508  }
509 
510  TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
511 
512  temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap));
513  if(temp)
514  {
515  allCaptureDevNameMap = temp;
516  allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description);
517  allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name);
518  numCaptureDevNames++;
519  }
520 }
521 
522 static void sink_name_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
523 {
524  ALCdevice *device = pdata;
525  pulse_data *data = device->ExtraData;
526  (void)context;
527 
528  if(eol)
529  {
530  pa_threaded_mainloop_signal(data->loop, 0);
531  return;
532  }
533 
534  free(device->DeviceName);
535  device->DeviceName = strdup(info->description);
536 }
537 
538 static void source_name_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata)
539 {
540  ALCdevice *device = pdata;
541  pulse_data *data = device->ExtraData;
542  (void)context;
543 
544  if(eol)
545  {
546  pa_threaded_mainloop_signal(data->loop, 0);
547  return;
548  }
549 
550  free(device->DeviceName);
551  device->DeviceName = strdup(info->description);
552 }
553 
554 
555 static void stream_moved_callback(pa_stream *stream, void *pdata)
556 {
557  ALCdevice *device = pdata;
558  pulse_data *data = device->ExtraData;
559  (void)stream;
560 
561  free(data->device_name);
562  data->device_name = strdup(pa_stream_get_device_name(data->stream));
563 
564  TRACE("Stream moved to %s\n", data->device_name);
565 }
566 
567 
568 static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
569 {
570  const char *name = "OpenAL Soft";
571  char path_name[PATH_MAX];
572  pa_context_state_t state;
573  pa_context *context;
574  int err;
575 
576  if(pa_get_binary_name(path_name, sizeof(path_name)))
577  name = pa_path_get_filename(path_name);
578 
579  context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
580  if(!context)
581  {
582  ERR("pa_context_new() failed\n");
583  return NULL;
584  }
585 
586  pa_context_set_state_callback(context, context_state_callback, loop);
587 
588  if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)
589  {
590  while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
591  {
592  if(!PA_CONTEXT_IS_GOOD(state))
593  {
594  err = pa_context_errno(context);
595  if(err > 0) err = -err;
596  break;
597  }
598 
599  pa_threaded_mainloop_wait(loop);
600  }
601  }
602  pa_context_set_state_callback(context, NULL, NULL);
603 
604  if(err < 0)
605  {
606  if(!silent)
607  ERR("Context did not connect: %s\n", pa_strerror(err));
608  pa_context_unref(context);
609  return NULL;
610  }
611 
612  return context;
613 }
614 
615 static pa_stream *connect_playback_stream(const char *device_name,
616  pa_threaded_mainloop *loop, pa_context *context,
617  pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
618  pa_channel_map *chanmap)
619 {
620  pa_stream_state_t state;
621  pa_stream *stream;
622 
623  stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
624  if(!stream)
625  {
626  ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
627  return NULL;
628  }
629 
630  pa_stream_set_state_callback(stream, stream_state_callback, loop);
631 
632  if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0)
633  {
634  ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
635  pa_stream_unref(stream);
636  return NULL;
637  }
638 
639  while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
640  {
641  if(!PA_STREAM_IS_GOOD(state))
642  {
643  ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
644  pa_stream_unref(stream);
645  return NULL;
646  }
647 
648  pa_threaded_mainloop_wait(loop);
649  }
650  pa_stream_set_state_callback(stream, NULL, NULL);
651 
652  return stream;
653 }
654 
655 static pa_stream *connect_record_stream(const char *device_name,
656  pa_threaded_mainloop *loop, pa_context *context,
657  pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
658  pa_channel_map *chanmap)
659 {
660  pa_stream_state_t state;
661  pa_stream *stream;
662 
663  stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter);
664  if(!stream)
665  {
666  ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
667  return NULL;
668  }
669 
670  pa_stream_set_state_callback(stream, stream_state_callback, loop);
671 
672  if(pa_stream_connect_record(stream, device_name, attr, flags) < 0)
673  {
674  ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
675  pa_stream_unref(stream);
676  return NULL;
677  }
678 
679  while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
680  {
681  if(!PA_STREAM_IS_GOOD(state))
682  {
683  ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
684  pa_stream_unref(stream);
685  return NULL;
686  }
687 
688  pa_threaded_mainloop_wait(loop);
689  }
690  pa_stream_set_state_callback(stream, NULL, NULL);
691 
692  return stream;
693 }
694 
695 
696 static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
697 {
698  if(op)
699  {
700  while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
701  pa_threaded_mainloop_wait(loop);
702  pa_operation_unref(op);
703  }
704 }
705 
706 
707 static void probe_devices(ALboolean capture)
708 {
709  pa_threaded_mainloop *loop;
710 
711  if(capture == AL_FALSE)
712  allDevNameMap = malloc(sizeof(DevMap) * 1);
713  else
714  allCaptureDevNameMap = malloc(sizeof(DevMap) * 1);
715 
716  if((loop=pa_threaded_mainloop_new()) &&
717  pa_threaded_mainloop_start(loop) >= 0)
718  {
719  pa_context *context;
720 
721  pa_threaded_mainloop_lock(loop);
722  context = connect_context(loop, AL_FALSE);
723  if(context)
724  {
725  pa_operation *o;
726 
727  if(capture == AL_FALSE)
728  {
729  pa_stream_flags_t flags;
730  pa_sample_spec spec;
731  pa_stream *stream;
732 
733  flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
734  PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
735 
736  spec.format = PA_SAMPLE_S16NE;
737  spec.rate = 44100;
738  spec.channels = 2;
739 
740  stream = connect_playback_stream(NULL, loop, context, flags,
741  NULL, &spec, NULL);
742  if(stream)
743  {
744  o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), sink_device_callback, loop);
745  wait_for_operation(o, loop);
746 
747  pa_stream_disconnect(stream);
748  pa_stream_unref(stream);
749  stream = NULL;
750  }
751 
752  o = pa_context_get_sink_info_list(context, sink_device_callback, loop);
753  }
754  else
755  {
756  pa_stream_flags_t flags;
757  pa_sample_spec spec;
758  pa_stream *stream;
759 
760  flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
761  PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
762 
763  spec.format = PA_SAMPLE_S16NE;
764  spec.rate = 44100;
765  spec.channels = 1;
766 
767  stream = connect_record_stream(NULL, loop, context, flags,
768  NULL, &spec, NULL);
769  if(stream)
770  {
771  o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), source_device_callback, loop);
772  wait_for_operation(o, loop);
773 
774  pa_stream_disconnect(stream);
775  pa_stream_unref(stream);
776  stream = NULL;
777  }
778 
779  o = pa_context_get_source_info_list(context, source_device_callback, loop);
780  }
781  wait_for_operation(o, loop);
782 
783  pa_context_disconnect(context);
784  pa_context_unref(context);
785  }
786  pa_threaded_mainloop_unlock(loop);
787  pa_threaded_mainloop_stop(loop);
788  }
789  if(loop)
790  pa_threaded_mainloop_free(loop);
791 }
792 
793 
794 static ALuint PulseProc(ALvoid *param)
795 {
796  ALCdevice *Device = param;
797  pulse_data *data = Device->ExtraData;
798  ALuint buffer_size;
799  ALint update_size;
800  size_t frame_size;
801  ssize_t len;
802 
803  SetRTPriority();
804 
805  pa_threaded_mainloop_lock(data->loop);
806  frame_size = pa_frame_size(&data->spec);
807  update_size = Device->UpdateSize * frame_size;
808 
809  /* Sanitize buffer metrics, in case we actually have less than what we
810  * asked for. */
811  buffer_size = minu(update_size*Device->NumUpdates, data->attr.tlength);
812  update_size = minu(update_size, buffer_size/2);
813  do {
814  len = pa_stream_writable_size(data->stream) - data->attr.tlength +
815  buffer_size;
816  if(len < update_size)
817  {
818  if(pa_stream_is_corked(data->stream) == 1)
819  {
820  pa_operation *o;
821  o = pa_stream_cork(data->stream, 0, NULL, NULL);
822  if(o) pa_operation_unref(o);
823  }
824  pa_threaded_mainloop_unlock(data->loop);
825  Sleep(1);
826  pa_threaded_mainloop_lock(data->loop);
827  continue;
828  }
829  len -= len%update_size;
830 
831  while(len > 0)
832  {
833  size_t newlen = len;
834  void *buf;
835  pa_free_cb_t free_func = NULL;
836 
837 #if PA_CHECK_VERSION(0,9,16)
838  if(!pa_stream_begin_write ||
839  pa_stream_begin_write(data->stream, &buf, &newlen) < 0)
840 #endif
841  {
842  buf = pa_xmalloc(newlen);
843  free_func = pa_xfree;
844  }
845 
846  aluMixData(Device, buf, newlen/frame_size);
847 
848  pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
849  len -= newlen;
850  }
851  } while(!data->killNow && Device->Connected);
852  pa_threaded_mainloop_unlock(data->loop);
853 
854  return 0;
855 }
856 
857 
858 static ALCboolean pulse_open(ALCdevice *device)
859 {
860  pulse_data *data = pa_xmalloc(sizeof(pulse_data));
861  memset(data, 0, sizeof(*data));
862 
863  if(!(data->loop = pa_threaded_mainloop_new()))
864  {
865  ERR("pa_threaded_mainloop_new() failed!\n");
866  goto out;
867  }
868  if(pa_threaded_mainloop_start(data->loop) < 0)
869  {
870  ERR("pa_threaded_mainloop_start() failed\n");
871  goto out;
872  }
873 
874  pa_threaded_mainloop_lock(data->loop);
875  device->ExtraData = data;
876 
877  data->context = connect_context(data->loop, AL_FALSE);
878  if(!data->context)
879  {
880  pa_threaded_mainloop_unlock(data->loop);
881  goto out;
882  }
883  pa_context_set_state_callback(data->context, context_state_callback2, device);
884 
885  pa_threaded_mainloop_unlock(data->loop);
886  return ALC_TRUE;
887 
888 out:
889  if(data->loop)
890  {
891  pa_threaded_mainloop_stop(data->loop);
892  pa_threaded_mainloop_free(data->loop);
893  }
894 
895  device->ExtraData = NULL;
896  pa_xfree(data);
897  return ALC_FALSE;
898 }
899 
900 static void pulse_close(ALCdevice *device)
901 {
902  pulse_data *data = device->ExtraData;
903 
904  pa_threaded_mainloop_lock(data->loop);
905 
906  if(data->stream)
907  {
908  pa_stream_set_moved_callback(data->stream, NULL, NULL);
909 #if PA_CHECK_VERSION(0,9,15)
910  if(pa_stream_set_buffer_attr_callback)
911  pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
912 #endif
913  pa_stream_disconnect(data->stream);
914  pa_stream_unref(data->stream);
915  }
916 
917  pa_context_disconnect(data->context);
918  pa_context_unref(data->context);
919 
920  pa_threaded_mainloop_unlock(data->loop);
921 
922  pa_threaded_mainloop_stop(data->loop);
923  pa_threaded_mainloop_free(data->loop);
924 
925  free(data->device_name);
926 
927  device->ExtraData = NULL;
928  pa_xfree(data);
929 }
930 
931 /* OpenAL */
932 static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name)
933 {
934  const char *pulse_name = NULL;
935  pa_stream_flags_t flags;
936  pa_sample_spec spec;
937  pulse_data *data;
938  pa_operation *o;
939 
940  if(device_name)
941  {
942  ALuint i;
943 
944  if(!allDevNameMap)
946 
947  for(i = 0;i < numDevNames;i++)
948  {
949  if(strcmp(device_name, allDevNameMap[i].name) == 0)
950  {
951  pulse_name = allDevNameMap[i].device_name;
952  break;
953  }
954  }
955  if(i == numDevNames)
956  return ALC_INVALID_VALUE;
957  }
958 
959  if(pulse_open(device) == ALC_FALSE)
960  return ALC_INVALID_VALUE;
961 
962  data = device->ExtraData;
963  pa_threaded_mainloop_lock(data->loop);
964 
965  flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
966  PA_STREAM_FIX_CHANNELS;
967  if(!GetConfigValueBool("pulse", "allow-moves", 0))
968  flags |= PA_STREAM_DONT_MOVE;
969 
970  spec.format = PA_SAMPLE_S16NE;
971  spec.rate = 44100;
972  spec.channels = 2;
973 
974  data->stream = connect_playback_stream(pulse_name, data->loop, data->context,
975  flags, NULL, &spec, NULL);
976  if(!data->stream)
977  {
978  pa_threaded_mainloop_unlock(data->loop);
979  pulse_close(device);
980  return ALC_INVALID_VALUE;
981  }
982 
983  data->device_name = strdup(pa_stream_get_device_name(data->stream));
984  o = pa_context_get_sink_info_by_name(data->context, data->device_name,
985  sink_name_callback, device);
986  wait_for_operation(o, data->loop);
987 
988  pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
989 
990  pa_threaded_mainloop_unlock(data->loop);
991 
992  return ALC_NO_ERROR;
993 }
994 
995 static void pulse_close_playback(ALCdevice *device)
996 {
997  pulse_close(device);
998 }
999 
1000 static ALCboolean pulse_reset_playback(ALCdevice *device)
1001 {
1002  pulse_data *data = device->ExtraData;
1003  pa_stream_flags_t flags = 0;
1004  pa_channel_map chanmap;
1005  ALuint len;
1006 
1007  pa_threaded_mainloop_lock(data->loop);
1008 
1009  if(data->stream)
1010  {
1011  pa_stream_set_moved_callback(data->stream, NULL, NULL);
1012 #if PA_CHECK_VERSION(0,9,15)
1013  if(pa_stream_set_buffer_attr_callback)
1014  pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
1015 #endif
1016  pa_stream_disconnect(data->stream);
1017  pa_stream_unref(data->stream);
1018  data->stream = NULL;
1019  }
1020 
1021  if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
1022  {
1023  pa_operation *o;
1024  o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
1025  wait_for_operation(o, data->loop);
1026  }
1027  if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
1028  flags |= PA_STREAM_FIX_RATE;
1029 
1030  flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
1031  flags |= PA_STREAM_ADJUST_LATENCY;
1032  flags |= PA_STREAM_START_CORKED;
1033  if(!GetConfigValueBool("pulse", "allow-moves", 0))
1034  flags |= PA_STREAM_DONT_MOVE;
1035 
1036  switch(device->FmtType)
1037  {
1038  case DevFmtByte:
1039  device->FmtType = DevFmtUByte;
1040  /* fall-through */
1041  case DevFmtUByte:
1042  data->spec.format = PA_SAMPLE_U8;
1043  break;
1044  case DevFmtUShort:
1045  device->FmtType = DevFmtShort;
1046  /* fall-through */
1047  case DevFmtShort:
1048  data->spec.format = PA_SAMPLE_S16NE;
1049  break;
1050  case DevFmtUInt:
1051  device->FmtType = DevFmtInt;
1052  /* fall-through */
1053  case DevFmtInt:
1054  data->spec.format = PA_SAMPLE_S32NE;
1055  break;
1056  case DevFmtFloat:
1057  data->spec.format = PA_SAMPLE_FLOAT32NE;
1058  break;
1059  }
1060  data->spec.rate = device->Frequency;
1061  data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
1062 
1063  if(pa_sample_spec_valid(&data->spec) == 0)
1064  {
1065  ERR("Invalid sample format\n");
1066  pa_threaded_mainloop_unlock(data->loop);
1067  return ALC_FALSE;
1068  }
1069 
1070  if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
1071  {
1072  ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
1073  pa_threaded_mainloop_unlock(data->loop);
1074  return ALC_FALSE;
1075  }
1076  SetDefaultWFXChannelOrder(device);
1077 
1078  data->attr.fragsize = -1;
1079  data->attr.prebuf = 0;
1080  data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
1081  data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);
1082  data->attr.maxlength = -1;
1083 
1084  data->stream = connect_playback_stream(data->device_name, data->loop,
1085  data->context, flags, &data->attr,
1086  &data->spec, &chanmap);
1087  if(!data->stream)
1088  {
1089  pa_threaded_mainloop_unlock(data->loop);
1090  return ALC_FALSE;
1091  }
1092  pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
1093 
1094  data->spec = *(pa_stream_get_sample_spec(data->stream));
1095  if(device->Frequency != data->spec.rate)
1096  {
1097  pa_operation *o;
1098 
1099  /* Server updated our playback rate, so modify the buffer attribs
1100  * accordingly. */
1101  device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
1102  data->spec.rate + 0.5);
1103  data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
1104  data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16);
1105  data->attr.maxlength = -1;
1106  data->attr.prebuf = 0;
1107 
1108  o = pa_stream_set_buffer_attr(data->stream, &data->attr,
1109  stream_success_callback, device);
1110  wait_for_operation(o, data->loop);
1111 
1112  device->Frequency = data->spec.rate;
1113  }
1114 
1115  pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
1116 #if PA_CHECK_VERSION(0,9,15)
1117  if(pa_stream_set_buffer_attr_callback)
1118  pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
1119 #endif
1120  stream_buffer_attr_callback(data->stream, device);
1121 
1122  len = data->attr.minreq / pa_frame_size(&data->spec);
1123  if((CPUCapFlags&CPU_CAP_SSE))
1124  len = (len+3)&~3;
1125  device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
1126  device->NumUpdates = clampu(device->NumUpdates, 2, 16);
1127  device->UpdateSize = len;
1128 
1129  pa_threaded_mainloop_unlock(data->loop);
1130  return ALC_TRUE;
1131 }
1132 
1133 static ALCboolean pulse_start_playback(ALCdevice *device)
1134 {
1135  pulse_data *data = device->ExtraData;
1136 
1137  data->thread = StartThread(PulseProc, device);
1138  if(!data->thread)
1139  return ALC_FALSE;
1140 
1141  return ALC_TRUE;
1142 }
1143 
1144 static void pulse_stop_playback(ALCdevice *device)
1145 {
1146  pulse_data *data = device->ExtraData;
1147  pa_operation *o;
1148 
1149  if(!data->stream)
1150  return;
1151 
1152  data->killNow = AL_TRUE;
1153  if(data->thread)
1154  {
1155  StopThread(data->thread);
1156  data->thread = NULL;
1157  }
1158  data->killNow = AL_FALSE;
1159 
1160  pa_threaded_mainloop_lock(data->loop);
1161 
1162  o = pa_stream_cork(data->stream, 1, stream_success_callback, device);
1163  wait_for_operation(o, data->loop);
1164 
1165  pa_threaded_mainloop_unlock(data->loop);
1166 }
1167 
1168 
1169 static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name)
1170 {
1171  const char *pulse_name = NULL;
1172  pa_stream_flags_t flags = 0;
1173  pa_channel_map chanmap;
1174  pulse_data *data;
1175  pa_operation *o;
1176  ALuint samples;
1177 
1178  if(device_name)
1179  {
1180  ALuint i;
1181 
1184 
1185  for(i = 0;i < numCaptureDevNames;i++)
1186  {
1187  if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)
1188  {
1189  pulse_name = allCaptureDevNameMap[i].device_name;
1190  break;
1191  }
1192  }
1193  if(i == numCaptureDevNames)
1194  return ALC_INVALID_VALUE;
1195  }
1196 
1197  if(pulse_open(device) == ALC_FALSE)
1198  return ALC_INVALID_VALUE;
1199 
1200  data = device->ExtraData;
1201  pa_threaded_mainloop_lock(data->loop);
1202 
1203  data->spec.rate = device->Frequency;
1204  data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
1205 
1206  switch(device->FmtType)
1207  {
1208  case DevFmtUByte:
1209  data->spec.format = PA_SAMPLE_U8;
1210  break;
1211  case DevFmtShort:
1212  data->spec.format = PA_SAMPLE_S16NE;
1213  break;
1214  case DevFmtInt:
1215  data->spec.format = PA_SAMPLE_S32NE;
1216  break;
1217  case DevFmtFloat:
1218  data->spec.format = PA_SAMPLE_FLOAT32NE;
1219  break;
1220  case DevFmtByte:
1221  case DevFmtUShort:
1222  case DevFmtUInt:
1223  ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
1224  pa_threaded_mainloop_unlock(data->loop);
1225  goto fail;
1226  }
1227 
1228  if(pa_sample_spec_valid(&data->spec) == 0)
1229  {
1230  ERR("Invalid sample format\n");
1231  pa_threaded_mainloop_unlock(data->loop);
1232  goto fail;
1233  }
1234 
1235  if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
1236  {
1237  ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
1238  pa_threaded_mainloop_unlock(data->loop);
1239  goto fail;
1240  }
1241 
1242  samples = device->UpdateSize * device->NumUpdates;
1243  samples = maxu(samples, 100 * device->Frequency / 1000);
1244 
1245  data->attr.minreq = -1;
1246  data->attr.prebuf = -1;
1247  data->attr.maxlength = samples * pa_frame_size(&data->spec);
1248  data->attr.tlength = -1;
1249  data->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
1250  pa_frame_size(&data->spec);
1251 
1252  flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
1253  if(!GetConfigValueBool("pulse", "allow-moves", 0))
1254  flags |= PA_STREAM_DONT_MOVE;
1255 
1256  data->stream = connect_record_stream(pulse_name, data->loop, data->context,
1257  flags, &data->attr, &data->spec,
1258  &chanmap);
1259  if(!data->stream)
1260  {
1261  pa_threaded_mainloop_unlock(data->loop);
1262  goto fail;
1263  }
1264  pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
1265 
1266  data->device_name = strdup(pa_stream_get_device_name(data->stream));
1267  o = pa_context_get_source_info_by_name(data->context, data->device_name,
1268  source_name_callback, device);
1269  wait_for_operation(o, data->loop);
1270 
1271  pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
1272 
1273  pa_threaded_mainloop_unlock(data->loop);
1274  return ALC_NO_ERROR;
1275 
1276 fail:
1277  pulse_close(device);
1278  return ALC_INVALID_VALUE;
1279 }
1280 
1281 static void pulse_close_capture(ALCdevice *device)
1282 {
1283  pulse_close(device);
1284 }
1285 
1286 static void pulse_start_capture(ALCdevice *device)
1287 {
1288  pulse_data *data = device->ExtraData;
1289  pa_operation *o;
1290 
1291  o = pa_stream_cork(data->stream, 0, stream_success_callback, device);
1292  wait_for_operation(o, data->loop);
1293 }
1294 
1295 static void pulse_stop_capture(ALCdevice *device)
1296 {
1297  pulse_data *data = device->ExtraData;
1298  pa_operation *o;
1299 
1300  o = pa_stream_cork(data->stream, 1, stream_success_callback, device);
1301  wait_for_operation(o, data->loop);
1302 }
1303 
1304 static ALCenum pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
1305 {
1306  pulse_data *data = device->ExtraData;
1307  ALCuint todo = samples * pa_frame_size(&data->spec);
1308 
1309  /* Capture is done in fragment-sized chunks, so we loop until we get all
1310  * that's available */
1311  data->last_readable -= todo;
1312  while(todo > 0)
1313  {
1314  size_t rem = todo;
1315 
1316  if(data->cap_len == 0)
1317  {
1318  pa_stream_state_t state;
1319 
1320  state = pa_stream_get_state(data->stream);
1321  if(!PA_STREAM_IS_GOOD(state))
1322  {
1323  aluHandleDisconnect(device);
1324  break;
1325  }
1326  if(pa_stream_peek(data->stream, &data->cap_store, &data->cap_len) < 0)
1327  {
1328  ERR("pa_stream_peek() failed: %s\n",
1329  pa_strerror(pa_context_errno(data->context)));
1330  aluHandleDisconnect(device);
1331  break;
1332  }
1333  data->cap_remain = data->cap_len;
1334  }
1335  if(rem > data->cap_remain)
1336  rem = data->cap_remain;
1337 
1338  memcpy(buffer, data->cap_store, rem);
1339 
1340  buffer = (ALbyte*)buffer + rem;
1341  todo -= rem;
1342 
1343  data->cap_store = (ALbyte*)data->cap_store + rem;
1344  data->cap_remain -= rem;
1345  if(data->cap_remain == 0)
1346  {
1347  pa_stream_drop(data->stream);
1348  data->cap_len = 0;
1349  }
1350  }
1351  if(todo > 0)
1352  memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
1353 
1354  return ALC_NO_ERROR;
1355 }
1356 
1357 static ALCuint pulse_available_samples(ALCdevice *device)
1358 {
1359  pulse_data *data = device->ExtraData;
1360  size_t readable = data->cap_remain;
1361 
1362  if(device->Connected)
1363  {
1364  ssize_t got = pa_stream_readable_size(data->stream);
1365  if(got < 0)
1366  {
1367  ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
1368  aluHandleDisconnect(device);
1369  }
1370  else if((size_t)got > data->cap_len)
1371  readable += got - data->cap_len;
1372  }
1373 
1374  if(data->last_readable < readable)
1375  data->last_readable = readable;
1376  return data->last_readable / pa_frame_size(&data->spec);
1377 }
1378 
1379 
1380 static void pulse_lock(ALCdevice *device)
1381 {
1382  pulse_data *data = device->ExtraData;
1383  pa_threaded_mainloop_lock(data->loop);
1384 }
1385 
1386 static void pulse_unlock(ALCdevice *device)
1387 {
1388  pulse_data *data = device->ExtraData;
1389  pa_threaded_mainloop_unlock(data->loop);
1390 }
1391 
1392 
1393 static ALint64 pulse_get_latency(ALCdevice *device)
1394 {
1395  pulse_data *data = device->ExtraData;
1396  pa_usec_t latency = 0;
1397  int neg;
1398 
1399  if(pa_stream_get_latency(data->stream, &latency, &neg) == 0)
1400  {
1401  if(neg)
1402  latency = 0;
1403  return (ALint64)minu64(latency, MAKEU64(0x7fffffff,0xffffffff)/1000) * 1000;
1404  }
1405  ERR("Failed to get stream latency!\n");
1406  return 0;
1407 }
1408 
1409 
1410 static const BackendFuncs pulse_funcs = {
1411  pulse_open_playback,
1412  pulse_close_playback,
1413  pulse_reset_playback,
1414  pulse_start_playback,
1415  pulse_stop_playback,
1416  pulse_open_capture,
1417  pulse_close_capture,
1418  pulse_start_capture,
1419  pulse_stop_capture,
1420  pulse_capture_samples,
1421  pulse_available_samples,
1422  pulse_lock,
1423  pulse_unlock,
1424  pulse_get_latency
1425 };
1426 
1428 {
1429  ALCboolean ret = ALC_FALSE;
1430 
1431  if(pulse_load())
1432  {
1433  pa_threaded_mainloop *loop;
1434 
1435  pulse_ctx_flags = 0;
1436  if(!GetConfigValueBool("pulse", "spawn-server", 1))
1437  pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
1438 
1439  if((loop=pa_threaded_mainloop_new()) &&
1440  pa_threaded_mainloop_start(loop) >= 0)
1441  {
1442  pa_context *context;
1443 
1444  pa_threaded_mainloop_lock(loop);
1445  context = connect_context(loop, AL_TRUE);
1446  if(context)
1447  {
1448  *func_list = pulse_funcs;
1449  ret = ALC_TRUE;
1450 
1451  /* Some libraries (Phonon, Qt) set some pulseaudio properties
1452  * through environment variables, which causes all streams in
1453  * the process to inherit them. This attempts to filter those
1454  * properties out by setting them to 0-length data. */
1455  prop_filter = pa_proplist_new();
1456  pa_proplist_set(prop_filter, PA_PROP_MEDIA_ROLE, NULL, 0);
1457  pa_proplist_set(prop_filter, "phonon.streamid", NULL, 0);
1458 
1459  pa_context_disconnect(context);
1460  pa_context_unref(context);
1461  }
1462  pa_threaded_mainloop_unlock(loop);
1463  pa_threaded_mainloop_stop(loop);
1464  }
1465  if(loop)
1466  pa_threaded_mainloop_free(loop);
1467  }
1468 
1469  return ret;
1470 }
1471 
1472 void alc_pulse_deinit(void)
1473 {
1474  ALuint i;
1475 
1476  for(i = 0;i < numDevNames;++i)
1477  {
1478  free(allDevNameMap[i].name);
1479  free(allDevNameMap[i].device_name);
1480  }
1482  allDevNameMap = NULL;
1483  numDevNames = 0;
1484 
1485  for(i = 0;i < numCaptureDevNames;++i)
1486  {
1487  free(allCaptureDevNameMap[i].name);
1488  free(allCaptureDevNameMap[i].device_name);
1489  }
1492  numCaptureDevNames = 0;
1493 
1494  if(prop_filter)
1495  pa_proplist_free(prop_filter);
1496  prop_filter = NULL;
1497 
1498  /* PulseAudio doesn't like being CloseLib'd sometimes */
1499 }
1500 
1501 void alc_pulse_probe(enum DevProbe type)
1502 {
1503  ALuint i;
1504 
1505  switch(type)
1506  {
1507  case ALL_DEVICE_PROBE:
1508  for(i = 0;i < numDevNames;++i)
1509  {
1510  free(allDevNameMap[i].name);
1511  free(allDevNameMap[i].device_name);
1512  }
1514  allDevNameMap = NULL;
1515  numDevNames = 0;
1516 
1518 
1519  for(i = 0;i < numDevNames;i++)
1521  break;
1522 
1523  case CAPTURE_DEVICE_PROBE:
1524  for(i = 0;i < numCaptureDevNames;++i)
1525  {
1526  free(allCaptureDevNameMap[i].name);
1527  free(allCaptureDevNameMap[i].device_name);
1528  }
1531  numCaptureDevNames = 0;
1532 
1534 
1535  for(i = 0;i < numCaptureDevNames;i++)
1537  break;
1538  }
1539 }
1540 
1541 #else
1542 
1543 #warning "Unsupported API version, backend will be unavailable!"
1544 
1546 { return ALC_FALSE; (void)func_list; }
1547 
1549 { }
1550 
1551 void alc_pulse_probe(enum DevProbe type)
1552 { (void)type; }
1553 
1554 #endif
ALCboolean Connected
Definition: alMain.h:564
ALCboolean alc_pulse_init(BackendFuncs *func_list)
Definition: pulseaudio.c:1545
void ALvoid
Definition: al.h:74
GLenum GLint param
Definition: gl2ext.h:1491
static __inline ALuint clampu(ALuint val, ALuint min, ALuint max)
Definition: alu.h:65
#define DEVICE_FREQUENCY_REQUEST
Definition: alMain.h:651
#define ALC_TRUE
Definition: alc.h:84
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
Definition: gl2ext.h:845
GLvoid **typedef void(GLAPIENTRY *PFNGLGETVERTEXATTRIBDVPROC)(GLuint
Definition: glew.h:1824
#define AL_TRUE
Definition: al.h:86
char * strdup(const char *inStr)
Definition: strdup.c:6
#define NULL
Definition: ftobjs.h:61
#define TRACE(...)
Definition: alMain.h:806
void alc_pulse_probe(enum DevProbe type)
Definition: pulseaudio.c:1551
GLuint GLuint stream
Definition: glew.h:6573
SDL_EventEntry * free
Definition: SDL_events.c:80
ALuint Frequency
Definition: alMain.h:569
int ALint
Definition: al.h:56
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
Definition: ALu.c:970
#define memset
Definition: SDL_malloc.c:633
#define AL_FALSE
Definition: al.h:83
EGLImageKHR EGLint * name
Definition: eglext.h:284
ALuint Flags
Definition: alMain.h:605
GLenum GLsizei len
Definition: glew.h:7035
void AppendCaptureDeviceList(const ALCchar *name)
unsigned int ALCuint
Definition: alc.h:60
ALvoid aluHandleDisconnect(ALCdevice *device)
Definition: ALu.c:1176
char ALCchar
Definition: alc.h:42
signed char ALbyte
Definition: al.h:44
ret
Definition: glew_str_glx.c:2
#define Sleep(x)
Definition: allatency.c:34
static DevMap * probe_devices(snd_pcm_stream_t stream, ALuint *count)
Definition: alsa.c:316
EGLContext EGLenum EGLClientBuffer buffer
Definition: eglext.h:87
void * ExtraData
Definition: alMain.h:630
#define ALC_FALSE
Definition: alc.h:81
double ALdouble
Definition: al.h:71
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
Definition: alcConfig.c:356
ALuint StopThread(ALvoid *thread)
Definition: alcThread.c:131
void SetDefaultWFXChannelOrder(ALCdevice *device)
Definition: ALc.c:1295
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
GLsizei samples
Definition: gl2ext.h:970
void alc_pulse_deinit(void)
Definition: pulseaudio.c:1548
#define LOAD_FUNC(f)
#define realloc
Definition: SDL_malloc.c:637
static DevMap * allDevNameMap
Definition: alsa.c:304
unsigned int ALuint
Definition: al.h:59
void SetRTPriority(void)
Definition: helpers.c:470
static __inline ALuint64 minu64(ALuint64 a, ALuint64 b)
Definition: alu.h:82
enum DevFmtChannels FmtChans
Definition: alMain.h:572
static __inline ALuint minu(ALuint a, ALuint b)
Definition: alu.h:61
static DevMap * allCaptureDevNameMap
Definition: alsa.c:306
#define DEVICE_CHANNELS_REQUEST
Definition: alMain.h:653
static SDL_Thread * thread
#define PATH_MAX
Definition: hrtf.c:34
#define ALC_NO_ERROR
Definition: alc.h:102
#define malloc
Definition: SDL_malloc.c:635
char ALCboolean
Definition: alc.h:39
#define MAKEU64(x, y)
Definition: alMain.h:48
static ALuint numDevNames
Definition: alsa.c:305
void ALCvoid
Definition: alc.h:75
GLenum GLuint GLsizei const GLchar * buf
Definition: glew.h:2539
#define ERR(...)
Definition: alMain.h:816
GLenum GLsizei GLsizei GLsizei GLsizei GLbitfield flags
Definition: glew.h:2767
#define memcpy
Definition: SDL_malloc.c:634
ALuint UpdateSize
Definition: alMain.h:570
ALuint NumUpdates
Definition: alMain.h:571
enum DevFmtType FmtType
Definition: alMain.h:573
ALCchar * DeviceName
Definition: alMain.h:575
char ALboolean
Definition: al.h:38
#define ALC_INVALID_VALUE
Definition: alc.h:114
#define str(s)
TParseContext * context
int i
Definition: pngrutil.c:1377
const GLubyte GLuint GLuint GLuint GLuint alpha GLboolean GLboolean GLboolean GLboolean alpha GLint GLint GLsizei GLsizei GLenum type GLenum GLint GLenum GLint GLint GLsizei GLsizei GLint border GLenum GLint GLint GLint GLint GLint GLsizei GLsizei height GLsizei GLsizei GLenum GLenum const GLvoid *pixels GLenum GLint GLint GLint GLint j2 GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble zFar GLenum GLenum GLint *params GLenum GLenum GLint *params GLenum GLenum GLint *params GLenum GLenum GLfloat *params GLenum GLint GLenum GLenum GLvoid *pixels GLenum GLint GLenum GLint *params GLenum GLenum GLint *params GLenum GLsizei const GLvoid *pointer GLenum GLenum const GLint *params GLenum GLfloat GLfloat GLint GLint const GLfloat *points GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat *points GLint GLfloat GLfloat GLint GLfloat GLfloat v2 GLenum GLenum const GLint *params GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble zFar GLenum map
Definition: SDL_glfuncs.h:268
int ALCenum
Definition: alc.h:66
ALuint CPUCapFlags
Definition: helpers.c:70
const ALCchar * DevFmtTypeString(enum DevFmtType type)
Definition: ALc.c:1136
static __inline ALuint maxu(ALuint a, ALuint b)
Definition: alu.h:63
DevFmtChannels
Definition: alMain.h:507
ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
Definition: ALc.c:1179
static ALuint numCaptureDevNames
Definition: alsa.c:307
void AppendAllDevicesList(const ALCchar *name)
ALvoid * StartThread(ALuint(*func)(ALvoid *), ALvoid *ptr)
Definition: alcThread.c:100
DevProbe
Definition: alMain.h:383