annotate Alc/backends/pulseaudio.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
rev   line source
rlm@0 1 /**
rlm@0 2 * OpenAL cross platform audio library
rlm@0 3 * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>
rlm@0 4 * Copyright (C) 2010 by Chris Robinson <chris.kcat@gmail.com>
rlm@0 5 * This library is free software; you can redistribute it and/or
rlm@0 6 * modify it under the terms of the GNU Library General Public
rlm@0 7 * License as published by the Free Software Foundation; either
rlm@0 8 * version 2 of the License, or (at your option) any later version.
rlm@0 9 *
rlm@0 10 * This library is distributed in the hope that it will be useful,
rlm@0 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@0 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rlm@0 13 * Library General Public License for more details.
rlm@0 14 *
rlm@0 15 * You should have received a copy of the GNU Library General Public
rlm@0 16 * License along with this library; if not, write to the
rlm@0 17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rlm@0 18 * Boston, MA 02111-1307, USA.
rlm@0 19 * Or go to http://www.gnu.org/copyleft/lgpl.html
rlm@0 20 */
rlm@0 21
rlm@0 22 #include "config.h"
rlm@0 23
rlm@0 24 #include "alMain.h"
rlm@0 25
rlm@0 26 #include <pulse/pulseaudio.h>
rlm@0 27
rlm@0 28 #if PA_API_VERSION == 11
rlm@0 29 #define PA_STREAM_ADJUST_LATENCY 0x2000U
rlm@0 30 #define PA_STREAM_EARLY_REQUESTS 0x4000U
rlm@0 31 static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
rlm@0 32 {
rlm@0 33 return (x == PA_STREAM_CREATING || x == PA_STREAM_READY);
rlm@0 34 }
rlm@0 35 static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
rlm@0 36 {
rlm@0 37 return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING ||
rlm@0 38 x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY);
rlm@0 39 }
rlm@0 40 #define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
rlm@0 41 #define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
rlm@0 42 #elif PA_API_VERSION != 12
rlm@0 43 #error Invalid PulseAudio API version
rlm@0 44 #endif
rlm@0 45
rlm@0 46 #ifndef PA_CHECK_VERSION
rlm@0 47 #define PA_CHECK_VERSION(major,minor,micro) \
rlm@0 48 ((PA_MAJOR > (major)) || \
rlm@0 49 (PA_MAJOR == (major) && PA_MINOR > (minor)) || \
rlm@0 50 (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
rlm@0 51 #endif
rlm@0 52
rlm@0 53 static void *pa_handle;
rlm@0 54 #ifdef HAVE_DYNLOAD
rlm@0 55 #define MAKE_FUNC(x) static typeof(x) * p##x
rlm@0 56 MAKE_FUNC(pa_context_unref);
rlm@0 57 MAKE_FUNC(pa_sample_spec_valid);
rlm@0 58 MAKE_FUNC(pa_stream_drop);
rlm@0 59 MAKE_FUNC(pa_strerror);
rlm@0 60 MAKE_FUNC(pa_context_get_state);
rlm@0 61 MAKE_FUNC(pa_stream_get_state);
rlm@0 62 MAKE_FUNC(pa_threaded_mainloop_signal);
rlm@0 63 MAKE_FUNC(pa_stream_peek);
rlm@0 64 MAKE_FUNC(pa_threaded_mainloop_wait);
rlm@0 65 MAKE_FUNC(pa_threaded_mainloop_unlock);
rlm@0 66 MAKE_FUNC(pa_threaded_mainloop_in_thread);
rlm@0 67 MAKE_FUNC(pa_context_new);
rlm@0 68 MAKE_FUNC(pa_threaded_mainloop_stop);
rlm@0 69 MAKE_FUNC(pa_context_disconnect);
rlm@0 70 MAKE_FUNC(pa_threaded_mainloop_start);
rlm@0 71 MAKE_FUNC(pa_threaded_mainloop_get_api);
rlm@0 72 MAKE_FUNC(pa_context_set_state_callback);
rlm@0 73 MAKE_FUNC(pa_stream_write);
rlm@0 74 MAKE_FUNC(pa_xfree);
rlm@0 75 MAKE_FUNC(pa_stream_connect_record);
rlm@0 76 MAKE_FUNC(pa_stream_connect_playback);
rlm@0 77 MAKE_FUNC(pa_stream_readable_size);
rlm@0 78 MAKE_FUNC(pa_stream_writable_size);
rlm@0 79 MAKE_FUNC(pa_stream_cork);
rlm@0 80 MAKE_FUNC(pa_stream_is_suspended);
rlm@0 81 MAKE_FUNC(pa_stream_get_device_name);
rlm@0 82 MAKE_FUNC(pa_path_get_filename);
rlm@0 83 MAKE_FUNC(pa_get_binary_name);
rlm@0 84 MAKE_FUNC(pa_threaded_mainloop_free);
rlm@0 85 MAKE_FUNC(pa_context_errno);
rlm@0 86 MAKE_FUNC(pa_xmalloc);
rlm@0 87 MAKE_FUNC(pa_stream_unref);
rlm@0 88 MAKE_FUNC(pa_threaded_mainloop_accept);
rlm@0 89 MAKE_FUNC(pa_stream_set_write_callback);
rlm@0 90 MAKE_FUNC(pa_threaded_mainloop_new);
rlm@0 91 MAKE_FUNC(pa_context_connect);
rlm@0 92 MAKE_FUNC(pa_stream_set_buffer_attr);
rlm@0 93 MAKE_FUNC(pa_stream_get_buffer_attr);
rlm@0 94 MAKE_FUNC(pa_stream_get_sample_spec);
rlm@0 95 MAKE_FUNC(pa_stream_get_time);
rlm@0 96 MAKE_FUNC(pa_stream_set_read_callback);
rlm@0 97 MAKE_FUNC(pa_stream_set_state_callback);
rlm@0 98 MAKE_FUNC(pa_stream_set_moved_callback);
rlm@0 99 MAKE_FUNC(pa_stream_set_underflow_callback);
rlm@0 100 MAKE_FUNC(pa_stream_new);
rlm@0 101 MAKE_FUNC(pa_stream_disconnect);
rlm@0 102 MAKE_FUNC(pa_threaded_mainloop_lock);
rlm@0 103 MAKE_FUNC(pa_channel_map_init_auto);
rlm@0 104 MAKE_FUNC(pa_channel_map_parse);
rlm@0 105 MAKE_FUNC(pa_channel_map_snprint);
rlm@0 106 MAKE_FUNC(pa_channel_map_equal);
rlm@0 107 MAKE_FUNC(pa_context_get_server_info);
rlm@0 108 MAKE_FUNC(pa_context_get_sink_info_by_name);
rlm@0 109 MAKE_FUNC(pa_context_get_sink_info_list);
rlm@0 110 MAKE_FUNC(pa_context_get_source_info_list);
rlm@0 111 MAKE_FUNC(pa_operation_get_state);
rlm@0 112 MAKE_FUNC(pa_operation_unref);
rlm@0 113 #if PA_CHECK_VERSION(0,9,15)
rlm@0 114 MAKE_FUNC(pa_channel_map_superset);
rlm@0 115 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
rlm@0 116 #endif
rlm@0 117 #if PA_CHECK_VERSION(0,9,16)
rlm@0 118 MAKE_FUNC(pa_stream_begin_write);
rlm@0 119 #endif
rlm@0 120 #undef MAKE_FUNC
rlm@0 121
rlm@0 122 #define pa_context_unref ppa_context_unref
rlm@0 123 #define pa_sample_spec_valid ppa_sample_spec_valid
rlm@0 124 #define pa_stream_drop ppa_stream_drop
rlm@0 125 #define pa_strerror ppa_strerror
rlm@0 126 #define pa_context_get_state ppa_context_get_state
rlm@0 127 #define pa_stream_get_state ppa_stream_get_state
rlm@0 128 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal
rlm@0 129 #define pa_stream_peek ppa_stream_peek
rlm@0 130 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait
rlm@0 131 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock
rlm@0 132 #define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread
rlm@0 133 #define pa_context_new ppa_context_new
rlm@0 134 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop
rlm@0 135 #define pa_context_disconnect ppa_context_disconnect
rlm@0 136 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start
rlm@0 137 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api
rlm@0 138 #define pa_context_set_state_callback ppa_context_set_state_callback
rlm@0 139 #define pa_stream_write ppa_stream_write
rlm@0 140 #define pa_xfree ppa_xfree
rlm@0 141 #define pa_stream_connect_record ppa_stream_connect_record
rlm@0 142 #define pa_stream_connect_playback ppa_stream_connect_playback
rlm@0 143 #define pa_stream_readable_size ppa_stream_readable_size
rlm@0 144 #define pa_stream_writable_size ppa_stream_writable_size
rlm@0 145 #define pa_stream_cork ppa_stream_cork
rlm@0 146 #define pa_stream_is_suspended ppa_stream_is_suspended
rlm@0 147 #define pa_stream_get_device_name ppa_stream_get_device_name
rlm@0 148 #define pa_path_get_filename ppa_path_get_filename
rlm@0 149 #define pa_get_binary_name ppa_get_binary_name
rlm@0 150 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free
rlm@0 151 #define pa_context_errno ppa_context_errno
rlm@0 152 #define pa_xmalloc ppa_xmalloc
rlm@0 153 #define pa_stream_unref ppa_stream_unref
rlm@0 154 #define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept
rlm@0 155 #define pa_stream_set_write_callback ppa_stream_set_write_callback
rlm@0 156 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new
rlm@0 157 #define pa_context_connect ppa_context_connect
rlm@0 158 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
rlm@0 159 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
rlm@0 160 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
rlm@0 161 #define pa_stream_get_time ppa_stream_get_time
rlm@0 162 #define pa_stream_set_read_callback ppa_stream_set_read_callback
rlm@0 163 #define pa_stream_set_state_callback ppa_stream_set_state_callback
rlm@0 164 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
rlm@0 165 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
rlm@0 166 #define pa_stream_new ppa_stream_new
rlm@0 167 #define pa_stream_disconnect ppa_stream_disconnect
rlm@0 168 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock
rlm@0 169 #define pa_channel_map_init_auto ppa_channel_map_init_auto
rlm@0 170 #define pa_channel_map_parse ppa_channel_map_parse
rlm@0 171 #define pa_channel_map_snprint ppa_channel_map_snprint
rlm@0 172 #define pa_channel_map_equal ppa_channel_map_equal
rlm@0 173 #define pa_context_get_server_info ppa_context_get_server_info
rlm@0 174 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
rlm@0 175 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
rlm@0 176 #define pa_context_get_source_info_list ppa_context_get_source_info_list
rlm@0 177 #define pa_operation_get_state ppa_operation_get_state
rlm@0 178 #define pa_operation_unref ppa_operation_unref
rlm@0 179 #if PA_CHECK_VERSION(0,9,15)
rlm@0 180 #define pa_channel_map_superset ppa_channel_map_superset
rlm@0 181 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
rlm@0 182 #endif
rlm@0 183 #if PA_CHECK_VERSION(0,9,16)
rlm@0 184 #define pa_stream_begin_write ppa_stream_begin_write
rlm@0 185 #endif
rlm@0 186
rlm@0 187 #endif
rlm@0 188
rlm@0 189 #ifndef PATH_MAX
rlm@0 190 #define PATH_MAX 4096
rlm@0 191 #endif
rlm@0 192
rlm@0 193 typedef struct {
rlm@0 194 char *device_name;
rlm@0 195
rlm@0 196 ALCuint samples;
rlm@0 197 ALCuint frame_size;
rlm@0 198
rlm@0 199 RingBuffer *ring;
rlm@0 200
rlm@0 201 pa_buffer_attr attr;
rlm@0 202 pa_sample_spec spec;
rlm@0 203
rlm@0 204 pa_threaded_mainloop *loop;
rlm@0 205
rlm@0 206 ALvoid *thread;
rlm@0 207 volatile ALboolean killNow;
rlm@0 208
rlm@0 209 pa_stream *stream;
rlm@0 210 pa_context *context;
rlm@0 211 } pulse_data;
rlm@0 212
rlm@0 213 typedef struct {
rlm@0 214 char *name;
rlm@0 215 char *device_name;
rlm@0 216 } DevMap;
rlm@0 217
rlm@0 218
rlm@0 219 static const ALCchar pulse_device[] = "PulseAudio Default";
rlm@0 220 static DevMap *allDevNameMap;
rlm@0 221 static ALuint numDevNames;
rlm@0 222 static DevMap *allCaptureDevNameMap;
rlm@0 223 static ALuint numCaptureDevNames;
rlm@0 224 static pa_context_flags_t pulse_ctx_flags;
rlm@0 225
rlm@0 226
rlm@0 227 static void context_state_callback(pa_context *context, void *pdata)
rlm@0 228 {
rlm@0 229 pa_threaded_mainloop *loop = pdata;
rlm@0 230 pa_context_state_t state;
rlm@0 231
rlm@0 232 state = pa_context_get_state(context);
rlm@0 233 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
rlm@0 234 pa_threaded_mainloop_signal(loop, 0);
rlm@0 235 }
rlm@0 236
rlm@0 237 static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
rlm@0 238 {
rlm@0 239 const char *name = "OpenAL Soft";
rlm@0 240 char path_name[PATH_MAX];
rlm@0 241 pa_context_state_t state;
rlm@0 242 pa_context *context;
rlm@0 243 int err;
rlm@0 244
rlm@0 245 if(pa_get_binary_name(path_name, sizeof(path_name)))
rlm@0 246 name = pa_path_get_filename(path_name);
rlm@0 247
rlm@0 248 context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
rlm@0 249 if(!context)
rlm@0 250 {
rlm@0 251 ERR("pa_context_new() failed\n");
rlm@0 252 return NULL;
rlm@0 253 }
rlm@0 254
rlm@0 255 pa_context_set_state_callback(context, context_state_callback, loop);
rlm@0 256
rlm@0 257 if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)
rlm@0 258 {
rlm@0 259 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
rlm@0 260 {
rlm@0 261 if(!PA_CONTEXT_IS_GOOD(state))
rlm@0 262 {
rlm@0 263 err = pa_context_errno(context);
rlm@0 264 if(err > 0) err = -err;
rlm@0 265 break;
rlm@0 266 }
rlm@0 267
rlm@0 268 pa_threaded_mainloop_wait(loop);
rlm@0 269 }
rlm@0 270 }
rlm@0 271 pa_context_set_state_callback(context, NULL, NULL);
rlm@0 272
rlm@0 273 if(err < 0)
rlm@0 274 {
rlm@0 275 if(!silent)
rlm@0 276 ERR("Context did not connect: %s\n", pa_strerror(err));
rlm@0 277 pa_context_unref(context);
rlm@0 278 return NULL;
rlm@0 279 }
rlm@0 280
rlm@0 281 return context;
rlm@0 282 }
rlm@0 283
rlm@0 284
rlm@0 285 static ALCboolean pulse_load(void) //{{{
rlm@0 286 {
rlm@0 287 ALCboolean ret = ALC_FALSE;
rlm@0 288 if(!pa_handle)
rlm@0 289 {
rlm@0 290 pa_threaded_mainloop *loop;
rlm@0 291
rlm@0 292 #ifdef HAVE_DYNLOAD
rlm@0 293
rlm@0 294 #ifdef _WIN32
rlm@0 295 #define PALIB "libpulse-0.dll"
rlm@0 296 #elif defined(__APPLE__) && defined(__MACH__)
rlm@0 297 #define PALIB "libpulse.0.dylib"
rlm@0 298 #else
rlm@0 299 #define PALIB "libpulse.so.0"
rlm@0 300 #endif
rlm@0 301 pa_handle = LoadLib(PALIB);
rlm@0 302 if(!pa_handle)
rlm@0 303 return ALC_FALSE;
rlm@0 304
rlm@0 305 #define LOAD_FUNC(x) do { \
rlm@0 306 p##x = GetSymbol(pa_handle, #x); \
rlm@0 307 if(!(p##x)) { \
rlm@0 308 CloseLib(pa_handle); \
rlm@0 309 pa_handle = NULL; \
rlm@0 310 return ALC_FALSE; \
rlm@0 311 } \
rlm@0 312 } while(0)
rlm@0 313 LOAD_FUNC(pa_context_unref);
rlm@0 314 LOAD_FUNC(pa_sample_spec_valid);
rlm@0 315 LOAD_FUNC(pa_stream_drop);
rlm@0 316 LOAD_FUNC(pa_strerror);
rlm@0 317 LOAD_FUNC(pa_context_get_state);
rlm@0 318 LOAD_FUNC(pa_stream_get_state);
rlm@0 319 LOAD_FUNC(pa_threaded_mainloop_signal);
rlm@0 320 LOAD_FUNC(pa_stream_peek);
rlm@0 321 LOAD_FUNC(pa_threaded_mainloop_wait);
rlm@0 322 LOAD_FUNC(pa_threaded_mainloop_unlock);
rlm@0 323 LOAD_FUNC(pa_threaded_mainloop_in_thread);
rlm@0 324 LOAD_FUNC(pa_context_new);
rlm@0 325 LOAD_FUNC(pa_threaded_mainloop_stop);
rlm@0 326 LOAD_FUNC(pa_context_disconnect);
rlm@0 327 LOAD_FUNC(pa_threaded_mainloop_start);
rlm@0 328 LOAD_FUNC(pa_threaded_mainloop_get_api);
rlm@0 329 LOAD_FUNC(pa_context_set_state_callback);
rlm@0 330 LOAD_FUNC(pa_stream_write);
rlm@0 331 LOAD_FUNC(pa_xfree);
rlm@0 332 LOAD_FUNC(pa_stream_connect_record);
rlm@0 333 LOAD_FUNC(pa_stream_connect_playback);
rlm@0 334 LOAD_FUNC(pa_stream_readable_size);
rlm@0 335 LOAD_FUNC(pa_stream_writable_size);
rlm@0 336 LOAD_FUNC(pa_stream_cork);
rlm@0 337 LOAD_FUNC(pa_stream_is_suspended);
rlm@0 338 LOAD_FUNC(pa_stream_get_device_name);
rlm@0 339 LOAD_FUNC(pa_path_get_filename);
rlm@0 340 LOAD_FUNC(pa_get_binary_name);
rlm@0 341 LOAD_FUNC(pa_threaded_mainloop_free);
rlm@0 342 LOAD_FUNC(pa_context_errno);
rlm@0 343 LOAD_FUNC(pa_xmalloc);
rlm@0 344 LOAD_FUNC(pa_stream_unref);
rlm@0 345 LOAD_FUNC(pa_threaded_mainloop_accept);
rlm@0 346 LOAD_FUNC(pa_stream_set_write_callback);
rlm@0 347 LOAD_FUNC(pa_threaded_mainloop_new);
rlm@0 348 LOAD_FUNC(pa_context_connect);
rlm@0 349 LOAD_FUNC(pa_stream_set_buffer_attr);
rlm@0 350 LOAD_FUNC(pa_stream_get_buffer_attr);
rlm@0 351 LOAD_FUNC(pa_stream_get_sample_spec);
rlm@0 352 LOAD_FUNC(pa_stream_get_time);
rlm@0 353 LOAD_FUNC(pa_stream_set_read_callback);
rlm@0 354 LOAD_FUNC(pa_stream_set_state_callback);
rlm@0 355 LOAD_FUNC(pa_stream_set_moved_callback);
rlm@0 356 LOAD_FUNC(pa_stream_set_underflow_callback);
rlm@0 357 LOAD_FUNC(pa_stream_new);
rlm@0 358 LOAD_FUNC(pa_stream_disconnect);
rlm@0 359 LOAD_FUNC(pa_threaded_mainloop_lock);
rlm@0 360 LOAD_FUNC(pa_channel_map_init_auto);
rlm@0 361 LOAD_FUNC(pa_channel_map_parse);
rlm@0 362 LOAD_FUNC(pa_channel_map_snprint);
rlm@0 363 LOAD_FUNC(pa_channel_map_equal);
rlm@0 364 LOAD_FUNC(pa_context_get_server_info);
rlm@0 365 LOAD_FUNC(pa_context_get_sink_info_by_name);
rlm@0 366 LOAD_FUNC(pa_context_get_sink_info_list);
rlm@0 367 LOAD_FUNC(pa_context_get_source_info_list);
rlm@0 368 LOAD_FUNC(pa_operation_get_state);
rlm@0 369 LOAD_FUNC(pa_operation_unref);
rlm@0 370 #undef LOAD_FUNC
rlm@0 371 #define LOAD_OPTIONAL_FUNC(x) do { \
rlm@0 372 p##x = GetSymbol(pa_handle, #x); \
rlm@0 373 } while(0)
rlm@0 374 #if PA_CHECK_VERSION(0,9,15)
rlm@0 375 LOAD_OPTIONAL_FUNC(pa_channel_map_superset);
rlm@0 376 LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback);
rlm@0 377 #endif
rlm@0 378 #if PA_CHECK_VERSION(0,9,16)
rlm@0 379 LOAD_OPTIONAL_FUNC(pa_stream_begin_write);
rlm@0 380 #endif
rlm@0 381 #undef LOAD_OPTIONAL_FUNC
rlm@0 382
rlm@0 383 #else /* HAVE_DYNLOAD */
rlm@0 384 pa_handle = (void*)0xDEADBEEF;
rlm@0 385 #endif
rlm@0 386
rlm@0 387 if((loop=pa_threaded_mainloop_new()) &&
rlm@0 388 pa_threaded_mainloop_start(loop) >= 0)
rlm@0 389 {
rlm@0 390 pa_context *context;
rlm@0 391
rlm@0 392 pa_threaded_mainloop_lock(loop);
rlm@0 393 context = connect_context(loop, AL_TRUE);
rlm@0 394 if(context)
rlm@0 395 {
rlm@0 396 ret = ALC_TRUE;
rlm@0 397
rlm@0 398 pa_context_disconnect(context);
rlm@0 399 pa_context_unref(context);
rlm@0 400 }
rlm@0 401 pa_threaded_mainloop_unlock(loop);
rlm@0 402 pa_threaded_mainloop_stop(loop);
rlm@0 403 }
rlm@0 404 if(loop)
rlm@0 405 pa_threaded_mainloop_free(loop);
rlm@0 406
rlm@0 407 if(!ret)
rlm@0 408 {
rlm@0 409 #ifdef HAVE_DYNLOAD
rlm@0 410 CloseLib(pa_handle);
rlm@0 411 #endif
rlm@0 412 pa_handle = NULL;
rlm@0 413 }
rlm@0 414 }
rlm@0 415 return ret;
rlm@0 416 } //}}}
rlm@0 417
rlm@0 418 // PulseAudio Event Callbacks //{{{
rlm@0 419 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
rlm@0 420 {
rlm@0 421 pa_threaded_mainloop *loop = pdata;
rlm@0 422 pa_stream_state_t state;
rlm@0 423
rlm@0 424 state = pa_stream_get_state(stream);
rlm@0 425 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
rlm@0 426 pa_threaded_mainloop_signal(loop, 0);
rlm@0 427 }//}}}
rlm@0 428
rlm@0 429 static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{
rlm@0 430 {
rlm@0 431 ALCdevice *Device = pdata;
rlm@0 432 pulse_data *data = Device->ExtraData;
rlm@0 433 (void)stream;
rlm@0 434
rlm@0 435 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 436 }//}}}
rlm@0 437
rlm@0 438 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
rlm@0 439 {
rlm@0 440 ALCdevice *Device = pdata;
rlm@0 441 pulse_data *data = Device->ExtraData;
rlm@0 442
rlm@0 443 LockDevice(Device);
rlm@0 444
rlm@0 445 data->attr = *(pa_stream_get_buffer_attr(stream));
rlm@0 446 Device->UpdateSize = data->attr.minreq / data->frame_size;
rlm@0 447 Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize;
rlm@0 448 if(Device->NumUpdates <= 1)
rlm@0 449 {
rlm@0 450 Device->NumUpdates = 1;
rlm@0 451 ERR("PulseAudio returned minreq > tlength/2; expect break up\n");
rlm@0 452 }
rlm@0 453
rlm@0 454 UnlockDevice(Device);
rlm@0 455 }//}}}
rlm@0 456
rlm@0 457 static void stream_device_callback(pa_stream *stream, void *pdata) //{{{
rlm@0 458 {
rlm@0 459 ALCdevice *Device = pdata;
rlm@0 460 pulse_data *data = Device->ExtraData;
rlm@0 461
rlm@0 462 free(data->device_name);
rlm@0 463 data->device_name = strdup(pa_stream_get_device_name(stream));
rlm@0 464 }//}}}
rlm@0 465
rlm@0 466 static void context_state_callback2(pa_context *context, void *pdata) //{{{
rlm@0 467 {
rlm@0 468 ALCdevice *Device = pdata;
rlm@0 469 pulse_data *data = Device->ExtraData;
rlm@0 470
rlm@0 471 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
rlm@0 472 {
rlm@0 473 ERR("Received context failure!\n");
rlm@0 474 aluHandleDisconnect(Device);
rlm@0 475 }
rlm@0 476 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 477 }//}}}
rlm@0 478
rlm@0 479 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
rlm@0 480 {
rlm@0 481 ALCdevice *Device = pdata;
rlm@0 482 pulse_data *data = Device->ExtraData;
rlm@0 483
rlm@0 484 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
rlm@0 485 {
rlm@0 486 ERR("Received stream failure!\n");
rlm@0 487 aluHandleDisconnect(Device);
rlm@0 488 }
rlm@0 489 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 490 }//}}}
rlm@0 491
rlm@0 492 static void stream_success_callback(pa_stream *stream, int success, void *pdata) //{{{
rlm@0 493 {
rlm@0 494 ALCdevice *Device = pdata;
rlm@0 495 pulse_data *data = Device->ExtraData;
rlm@0 496 (void)stream;
rlm@0 497 (void)success;
rlm@0 498
rlm@0 499 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 500 }//}}}
rlm@0 501
rlm@0 502 static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{
rlm@0 503 {
rlm@0 504 ALCdevice *device = pdata;
rlm@0 505 pulse_data *data = device->ExtraData;
rlm@0 506 char chanmap_str[256] = "";
rlm@0 507 const struct {
rlm@0 508 const char *str;
rlm@0 509 enum DevFmtChannels chans;
rlm@0 510 } chanmaps[] = {
rlm@0 511 { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
rlm@0 512 DevFmtX71 },
rlm@0 513 { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
rlm@0 514 DevFmtX61 },
rlm@0 515 { "front-left,front-right,front-center,lfe,rear-left,rear-right",
rlm@0 516 DevFmtX51 },
rlm@0 517 { "front-left,front-right,front-center,lfe,side-left,side-right",
rlm@0 518 DevFmtX51Side },
rlm@0 519 { "front-left,front-right,rear-left,rear-right", DevFmtQuad },
rlm@0 520 { "front-left,front-right", DevFmtStereo },
rlm@0 521 { "mono", DevFmtMono },
rlm@0 522 { NULL, 0 }
rlm@0 523 };
rlm@0 524 int i;
rlm@0 525 (void)context;
rlm@0 526
rlm@0 527 if(eol)
rlm@0 528 {
rlm@0 529 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 530 return;
rlm@0 531 }
rlm@0 532
rlm@0 533 for(i = 0;chanmaps[i].str;i++)
rlm@0 534 {
rlm@0 535 pa_channel_map map;
rlm@0 536 if(!pa_channel_map_parse(&map, chanmaps[i].str))
rlm@0 537 continue;
rlm@0 538
rlm@0 539 if(pa_channel_map_equal(&info->channel_map, &map)
rlm@0 540 #if PA_CHECK_VERSION(0,9,15)
rlm@0 541 || (pa_channel_map_superset &&
rlm@0 542 pa_channel_map_superset(&info->channel_map, &map))
rlm@0 543 #endif
rlm@0 544 )
rlm@0 545 {
rlm@0 546 device->FmtChans = chanmaps[i].chans;
rlm@0 547 return;
rlm@0 548 }
rlm@0 549 }
rlm@0 550
rlm@0 551 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
rlm@0 552 ERR("Failed to find format for channel map:\n %s\n", chanmap_str);
rlm@0 553 }//}}}
rlm@0 554
rlm@0 555 static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{
rlm@0 556 {
rlm@0 557 pa_threaded_mainloop *loop = pdata;
rlm@0 558 char str[1024];
rlm@0 559 void *temp;
rlm@0 560 int count;
rlm@0 561 ALuint i;
rlm@0 562
rlm@0 563 (void)context;
rlm@0 564
rlm@0 565 if(eol)
rlm@0 566 {
rlm@0 567 pa_threaded_mainloop_signal(loop, 0);
rlm@0 568 return;
rlm@0 569 }
rlm@0 570
rlm@0 571 count = 0;
rlm@0 572 do {
rlm@0 573 if(count == 0)
rlm@0 574 snprintf(str, sizeof(str), "%s", info->description);
rlm@0 575 else
rlm@0 576 snprintf(str, sizeof(str), "%s #%d", info->description, count+1);
rlm@0 577 count++;
rlm@0 578
rlm@0 579 for(i = 0;i < numDevNames;i++)
rlm@0 580 {
rlm@0 581 if(strcmp(str, allDevNameMap[i].name) == 0)
rlm@0 582 break;
rlm@0 583 }
rlm@0 584 } while(i != numDevNames);
rlm@0 585
rlm@0 586 temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap));
rlm@0 587 if(temp)
rlm@0 588 {
rlm@0 589 allDevNameMap = temp;
rlm@0 590 allDevNameMap[numDevNames].name = strdup(str);
rlm@0 591 allDevNameMap[numDevNames].device_name = strdup(info->name);
rlm@0 592 numDevNames++;
rlm@0 593 }
rlm@0 594 }//}}}
rlm@0 595
rlm@0 596 static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{
rlm@0 597 {
rlm@0 598 pa_threaded_mainloop *loop = pdata;
rlm@0 599 char str[1024];
rlm@0 600 void *temp;
rlm@0 601 int count;
rlm@0 602 ALuint i;
rlm@0 603
rlm@0 604 (void)context;
rlm@0 605
rlm@0 606 if(eol)
rlm@0 607 {
rlm@0 608 pa_threaded_mainloop_signal(loop, 0);
rlm@0 609 return;
rlm@0 610 }
rlm@0 611
rlm@0 612 count = 0;
rlm@0 613 do {
rlm@0 614 if(count == 0)
rlm@0 615 snprintf(str, sizeof(str), "%s", info->description);
rlm@0 616 else
rlm@0 617 snprintf(str, sizeof(str), "%s #%d", info->description, count+1);
rlm@0 618 count++;
rlm@0 619
rlm@0 620 for(i = 0;i < numCaptureDevNames;i++)
rlm@0 621 {
rlm@0 622 if(strcmp(str, allCaptureDevNameMap[i].name) == 0)
rlm@0 623 break;
rlm@0 624 }
rlm@0 625 } while(i != numCaptureDevNames);
rlm@0 626
rlm@0 627 temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap));
rlm@0 628 if(temp)
rlm@0 629 {
rlm@0 630 allCaptureDevNameMap = temp;
rlm@0 631 allCaptureDevNameMap[numCaptureDevNames].name = strdup(str);
rlm@0 632 allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name);
rlm@0 633 numCaptureDevNames++;
rlm@0 634 }
rlm@0 635 }//}}}
rlm@0 636 //}}}
rlm@0 637
rlm@0 638 // PulseAudio I/O Callbacks //{{{
rlm@0 639 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
rlm@0 640 {
rlm@0 641 ALCdevice *Device = pdata;
rlm@0 642 pulse_data *data = Device->ExtraData;
rlm@0 643 (void)stream;
rlm@0 644 (void)len;
rlm@0 645
rlm@0 646 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 647 } //}}}
rlm@0 648 //}}}
rlm@0 649
rlm@0 650 static ALuint PulseProc(ALvoid *param)
rlm@0 651 {
rlm@0 652 ALCdevice *Device = param;
rlm@0 653 pulse_data *data = Device->ExtraData;
rlm@0 654 ssize_t len;
rlm@0 655
rlm@0 656 SetRTPriority();
rlm@0 657
rlm@0 658 pa_threaded_mainloop_lock(data->loop);
rlm@0 659 do {
rlm@0 660 len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0);
rlm@0 661 len -= len%(Device->UpdateSize*data->frame_size);
rlm@0 662 if(len == 0)
rlm@0 663 {
rlm@0 664 pa_threaded_mainloop_wait(data->loop);
rlm@0 665 continue;
rlm@0 666 }
rlm@0 667
rlm@0 668 while(len > 0)
rlm@0 669 {
rlm@0 670 size_t newlen = len;
rlm@0 671 void *buf;
rlm@0 672 pa_free_cb_t free_func = NULL;
rlm@0 673
rlm@0 674 #if PA_CHECK_VERSION(0,9,16)
rlm@0 675 if(!pa_stream_begin_write ||
rlm@0 676 pa_stream_begin_write(data->stream, &buf, &newlen) < 0)
rlm@0 677 #endif
rlm@0 678 {
rlm@0 679 buf = pa_xmalloc(newlen);
rlm@0 680 free_func = pa_xfree;
rlm@0 681 }
rlm@0 682 pa_threaded_mainloop_unlock(data->loop);
rlm@0 683
rlm@0 684 aluMixData(Device, buf, newlen/data->frame_size);
rlm@0 685
rlm@0 686 pa_threaded_mainloop_lock(data->loop);
rlm@0 687 pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
rlm@0 688 len -= newlen;
rlm@0 689 }
rlm@0 690 } while(Device->Connected && !data->killNow);
rlm@0 691 pa_threaded_mainloop_unlock(data->loop);
rlm@0 692
rlm@0 693 return 0;
rlm@0 694 }
rlm@0 695
rlm@0 696 static pa_stream *connect_playback_stream(ALCdevice *device,
rlm@0 697 pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
rlm@0 698 pa_channel_map *chanmap)
rlm@0 699 {
rlm@0 700 pulse_data *data = device->ExtraData;
rlm@0 701 pa_stream_state_t state;
rlm@0 702 pa_stream *stream;
rlm@0 703
rlm@0 704 stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap);
rlm@0 705 if(!stream)
rlm@0 706 {
rlm@0 707 ERR("pa_stream_new() failed: %s\n",
rlm@0 708 pa_strerror(pa_context_errno(data->context)));
rlm@0 709 return NULL;
rlm@0 710 }
rlm@0 711
rlm@0 712 pa_stream_set_state_callback(stream, stream_state_callback, data->loop);
rlm@0 713
rlm@0 714 if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0)
rlm@0 715 {
rlm@0 716 ERR("Stream did not connect: %s\n",
rlm@0 717 pa_strerror(pa_context_errno(data->context)));
rlm@0 718 pa_stream_unref(stream);
rlm@0 719 return NULL;
rlm@0 720 }
rlm@0 721
rlm@0 722 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
rlm@0 723 {
rlm@0 724 if(!PA_STREAM_IS_GOOD(state))
rlm@0 725 {
rlm@0 726 ERR("Stream did not get ready: %s\n",
rlm@0 727 pa_strerror(pa_context_errno(data->context)));
rlm@0 728 pa_stream_unref(stream);
rlm@0 729 return NULL;
rlm@0 730 }
rlm@0 731
rlm@0 732 pa_threaded_mainloop_wait(data->loop);
rlm@0 733 }
rlm@0 734 pa_stream_set_state_callback(stream, NULL, NULL);
rlm@0 735
rlm@0 736 return stream;
rlm@0 737 }
rlm@0 738
rlm@0 739 static void probe_devices(ALboolean capture)
rlm@0 740 {
rlm@0 741 pa_threaded_mainloop *loop;
rlm@0 742
rlm@0 743 if(capture == AL_FALSE)
rlm@0 744 allDevNameMap = malloc(sizeof(DevMap) * 1);
rlm@0 745 else
rlm@0 746 allCaptureDevNameMap = malloc(sizeof(DevMap) * 1);
rlm@0 747
rlm@0 748 if((loop=pa_threaded_mainloop_new()) &&
rlm@0 749 pa_threaded_mainloop_start(loop) >= 0)
rlm@0 750 {
rlm@0 751 pa_context *context;
rlm@0 752
rlm@0 753 pa_threaded_mainloop_lock(loop);
rlm@0 754 context = connect_context(loop, AL_FALSE);
rlm@0 755 if(context)
rlm@0 756 {
rlm@0 757 pa_operation *o;
rlm@0 758
rlm@0 759 if(capture == AL_FALSE)
rlm@0 760 o = pa_context_get_sink_info_list(context, sink_device_callback, loop);
rlm@0 761 else
rlm@0 762 o = pa_context_get_source_info_list(context, source_device_callback, loop);
rlm@0 763 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
rlm@0 764 pa_threaded_mainloop_wait(loop);
rlm@0 765 pa_operation_unref(o);
rlm@0 766
rlm@0 767 pa_context_disconnect(context);
rlm@0 768 pa_context_unref(context);
rlm@0 769 }
rlm@0 770 pa_threaded_mainloop_unlock(loop);
rlm@0 771 pa_threaded_mainloop_stop(loop);
rlm@0 772 }
rlm@0 773 if(loop)
rlm@0 774 pa_threaded_mainloop_free(loop);
rlm@0 775 }
rlm@0 776
rlm@0 777
rlm@0 778 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
rlm@0 779 {
rlm@0 780 pulse_data *data = pa_xmalloc(sizeof(pulse_data));
rlm@0 781 memset(data, 0, sizeof(*data));
rlm@0 782
rlm@0 783 if(!(data->loop = pa_threaded_mainloop_new()))
rlm@0 784 {
rlm@0 785 ERR("pa_threaded_mainloop_new() failed!\n");
rlm@0 786 goto out;
rlm@0 787 }
rlm@0 788 if(pa_threaded_mainloop_start(data->loop) < 0)
rlm@0 789 {
rlm@0 790 ERR("pa_threaded_mainloop_start() failed\n");
rlm@0 791 goto out;
rlm@0 792 }
rlm@0 793
rlm@0 794 pa_threaded_mainloop_lock(data->loop);
rlm@0 795 device->ExtraData = data;
rlm@0 796
rlm@0 797 data->context = connect_context(data->loop, AL_FALSE);
rlm@0 798 if(!data->context)
rlm@0 799 {
rlm@0 800 pa_threaded_mainloop_unlock(data->loop);
rlm@0 801 goto out;
rlm@0 802 }
rlm@0 803 pa_context_set_state_callback(data->context, context_state_callback2, device);
rlm@0 804
rlm@0 805 device->szDeviceName = strdup(device_name);
rlm@0 806
rlm@0 807 pa_threaded_mainloop_unlock(data->loop);
rlm@0 808 return ALC_TRUE;
rlm@0 809
rlm@0 810 out:
rlm@0 811 if(data->loop)
rlm@0 812 {
rlm@0 813 pa_threaded_mainloop_stop(data->loop);
rlm@0 814 pa_threaded_mainloop_free(data->loop);
rlm@0 815 }
rlm@0 816
rlm@0 817 device->ExtraData = NULL;
rlm@0 818 pa_xfree(data);
rlm@0 819 return ALC_FALSE;
rlm@0 820 } //}}}
rlm@0 821
rlm@0 822 static void pulse_close(ALCdevice *device) //{{{
rlm@0 823 {
rlm@0 824 pulse_data *data = device->ExtraData;
rlm@0 825
rlm@0 826 pa_threaded_mainloop_lock(data->loop);
rlm@0 827
rlm@0 828 if(data->stream)
rlm@0 829 {
rlm@0 830 pa_stream_disconnect(data->stream);
rlm@0 831 pa_stream_unref(data->stream);
rlm@0 832 }
rlm@0 833
rlm@0 834 pa_context_disconnect(data->context);
rlm@0 835 pa_context_unref(data->context);
rlm@0 836
rlm@0 837 pa_threaded_mainloop_unlock(data->loop);
rlm@0 838
rlm@0 839 pa_threaded_mainloop_stop(data->loop);
rlm@0 840 pa_threaded_mainloop_free(data->loop);
rlm@0 841
rlm@0 842 DestroyRingBuffer(data->ring);
rlm@0 843 free(data->device_name);
rlm@0 844
rlm@0 845 device->ExtraData = NULL;
rlm@0 846 pa_xfree(data);
rlm@0 847 } //}}}
rlm@0 848 //}}}
rlm@0 849
rlm@0 850 // OpenAL {{{
rlm@0 851 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
rlm@0 852 {
rlm@0 853 char *pulse_name = NULL;
rlm@0 854 pa_sample_spec spec;
rlm@0 855 pulse_data *data;
rlm@0 856
rlm@0 857 if(!allDevNameMap)
rlm@0 858 probe_devices(AL_FALSE);
rlm@0 859
rlm@0 860 if(!device_name)
rlm@0 861 device_name = pulse_device;
rlm@0 862 else if(strcmp(device_name, pulse_device) != 0)
rlm@0 863 {
rlm@0 864 ALuint i;
rlm@0 865
rlm@0 866 for(i = 0;i < numDevNames;i++)
rlm@0 867 {
rlm@0 868 if(strcmp(device_name, allDevNameMap[i].name) == 0)
rlm@0 869 {
rlm@0 870 pulse_name = allDevNameMap[i].device_name;
rlm@0 871 break;
rlm@0 872 }
rlm@0 873 }
rlm@0 874 if(i == numDevNames)
rlm@0 875 return ALC_FALSE;
rlm@0 876 }
rlm@0 877
rlm@0 878 if(pulse_open(device, device_name) == ALC_FALSE)
rlm@0 879 return ALC_FALSE;
rlm@0 880
rlm@0 881 data = device->ExtraData;
rlm@0 882
rlm@0 883 pa_threaded_mainloop_lock(data->loop);
rlm@0 884
rlm@0 885 spec.format = PA_SAMPLE_S16NE;
rlm@0 886 spec.rate = 44100;
rlm@0 887 spec.channels = 2;
rlm@0 888
rlm@0 889 data->device_name = pulse_name;
rlm@0 890 pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL);
rlm@0 891 if(!stream)
rlm@0 892 {
rlm@0 893 pa_threaded_mainloop_unlock(data->loop);
rlm@0 894 goto fail;
rlm@0 895 }
rlm@0 896
rlm@0 897 if(pa_stream_is_suspended(stream))
rlm@0 898 {
rlm@0 899 ERR("Device is suspended\n");
rlm@0 900 pa_stream_disconnect(stream);
rlm@0 901 pa_stream_unref(stream);
rlm@0 902 pa_threaded_mainloop_unlock(data->loop);
rlm@0 903 goto fail;
rlm@0 904 }
rlm@0 905 data->device_name = strdup(pa_stream_get_device_name(stream));
rlm@0 906
rlm@0 907 pa_stream_disconnect(stream);
rlm@0 908 pa_stream_unref(stream);
rlm@0 909
rlm@0 910 pa_threaded_mainloop_unlock(data->loop);
rlm@0 911
rlm@0 912 return ALC_TRUE;
rlm@0 913
rlm@0 914 fail:
rlm@0 915 pulse_close(device);
rlm@0 916 return ALC_FALSE;
rlm@0 917 } //}}}
rlm@0 918
rlm@0 919 static void pulse_close_playback(ALCdevice *device) //{{{
rlm@0 920 {
rlm@0 921 pulse_close(device);
rlm@0 922 } //}}}
rlm@0 923
rlm@0 924 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
rlm@0 925 {
rlm@0 926 pulse_data *data = device->ExtraData;
rlm@0 927 pa_stream_flags_t flags = 0;
rlm@0 928 pa_channel_map chanmap;
rlm@0 929
rlm@0 930 pa_threaded_mainloop_lock(data->loop);
rlm@0 931
rlm@0 932 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 933 {
rlm@0 934 pa_operation *o;
rlm@0 935 o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
rlm@0 936 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
rlm@0 937 pa_threaded_mainloop_wait(data->loop);
rlm@0 938 pa_operation_unref(o);
rlm@0 939 }
rlm@0 940 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 941 flags |= PA_STREAM_FIX_RATE;
rlm@0 942
rlm@0 943 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
rlm@0 944 data->attr.prebuf = -1;
rlm@0 945 data->attr.fragsize = -1;
rlm@0 946 data->attr.minreq = device->UpdateSize * data->frame_size;
rlm@0 947 data->attr.tlength = data->attr.minreq * device->NumUpdates;
rlm@0 948 if(data->attr.tlength < data->attr.minreq*2)
rlm@0 949 data->attr.tlength = data->attr.minreq*2;
rlm@0 950 data->attr.maxlength = data->attr.tlength;
rlm@0 951 flags |= PA_STREAM_EARLY_REQUESTS;
rlm@0 952 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
rlm@0 953
rlm@0 954 switch(device->FmtType)
rlm@0 955 {
rlm@0 956 case DevFmtByte:
rlm@0 957 device->FmtType = DevFmtUByte;
rlm@0 958 /* fall-through */
rlm@0 959 case DevFmtUByte:
rlm@0 960 data->spec.format = PA_SAMPLE_U8;
rlm@0 961 break;
rlm@0 962 case DevFmtUShort:
rlm@0 963 device->FmtType = DevFmtShort;
rlm@0 964 /* fall-through */
rlm@0 965 case DevFmtShort:
rlm@0 966 data->spec.format = PA_SAMPLE_S16NE;
rlm@0 967 break;
rlm@0 968 case DevFmtFloat:
rlm@0 969 data->spec.format = PA_SAMPLE_FLOAT32NE;
rlm@0 970 break;
rlm@0 971 }
rlm@0 972 data->spec.rate = device->Frequency;
rlm@0 973 data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
rlm@0 974
rlm@0 975 if(pa_sample_spec_valid(&data->spec) == 0)
rlm@0 976 {
rlm@0 977 ERR("Invalid sample format\n");
rlm@0 978 pa_threaded_mainloop_unlock(data->loop);
rlm@0 979 return ALC_FALSE;
rlm@0 980 }
rlm@0 981
rlm@0 982 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
rlm@0 983 {
rlm@0 984 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
rlm@0 985 pa_threaded_mainloop_unlock(data->loop);
rlm@0 986 return ALC_FALSE;
rlm@0 987 }
rlm@0 988 SetDefaultWFXChannelOrder(device);
rlm@0 989
rlm@0 990 data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap);
rlm@0 991 if(!data->stream)
rlm@0 992 {
rlm@0 993 pa_threaded_mainloop_unlock(data->loop);
rlm@0 994 return ALC_FALSE;
rlm@0 995 }
rlm@0 996
rlm@0 997 pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
rlm@0 998
rlm@0 999 data->spec = *(pa_stream_get_sample_spec(data->stream));
rlm@0 1000 if(device->Frequency != data->spec.rate)
rlm@0 1001 {
rlm@0 1002 pa_operation *o;
rlm@0 1003
rlm@0 1004 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 1005 ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate);
rlm@0 1006 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 1007
rlm@0 1008 /* Server updated our playback rate, so modify the buffer attribs
rlm@0 1009 * accordingly. */
rlm@0 1010 data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) *
rlm@0 1011 data->spec.rate / device->Frequency * data->frame_size;
rlm@0 1012 data->attr.tlength = data->attr.minreq * device->NumUpdates;
rlm@0 1013 data->attr.maxlength = data->attr.tlength;
rlm@0 1014
rlm@0 1015 o = pa_stream_set_buffer_attr(data->stream, &data->attr,
rlm@0 1016 stream_success_callback, device);
rlm@0 1017 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
rlm@0 1018 pa_threaded_mainloop_wait(data->loop);
rlm@0 1019 pa_operation_unref(o);
rlm@0 1020
rlm@0 1021 device->Frequency = data->spec.rate;
rlm@0 1022 }
rlm@0 1023
rlm@0 1024 stream_buffer_attr_callback(data->stream, device);
rlm@0 1025 #if PA_CHECK_VERSION(0,9,15)
rlm@0 1026 if(pa_stream_set_buffer_attr_callback)
rlm@0 1027 pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
rlm@0 1028 #endif
rlm@0 1029 pa_stream_set_moved_callback(data->stream, stream_device_callback, device);
rlm@0 1030 pa_stream_set_write_callback(data->stream, stream_write_callback, device);
rlm@0 1031 pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device);
rlm@0 1032
rlm@0 1033 data->thread = StartThread(PulseProc, device);
rlm@0 1034 if(!data->thread)
rlm@0 1035 {
rlm@0 1036 #if PA_CHECK_VERSION(0,9,15)
rlm@0 1037 if(pa_stream_set_buffer_attr_callback)
rlm@0 1038 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
rlm@0 1039 #endif
rlm@0 1040 pa_stream_set_moved_callback(data->stream, NULL, NULL);
rlm@0 1041 pa_stream_set_write_callback(data->stream, NULL, NULL);
rlm@0 1042 pa_stream_set_underflow_callback(data->stream, NULL, NULL);
rlm@0 1043 pa_stream_disconnect(data->stream);
rlm@0 1044 pa_stream_unref(data->stream);
rlm@0 1045 data->stream = NULL;
rlm@0 1046
rlm@0 1047 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1048 return ALC_FALSE;
rlm@0 1049 }
rlm@0 1050
rlm@0 1051 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1052 return ALC_TRUE;
rlm@0 1053 } //}}}
rlm@0 1054
rlm@0 1055 static void pulse_stop_playback(ALCdevice *device) //{{{
rlm@0 1056 {
rlm@0 1057 pulse_data *data = device->ExtraData;
rlm@0 1058
rlm@0 1059 if(!data->stream)
rlm@0 1060 return;
rlm@0 1061
rlm@0 1062 data->killNow = AL_TRUE;
rlm@0 1063 if(data->thread)
rlm@0 1064 {
rlm@0 1065 pa_threaded_mainloop_signal(data->loop, 0);
rlm@0 1066 StopThread(data->thread);
rlm@0 1067 data->thread = NULL;
rlm@0 1068 }
rlm@0 1069 data->killNow = AL_FALSE;
rlm@0 1070
rlm@0 1071 pa_threaded_mainloop_lock(data->loop);
rlm@0 1072
rlm@0 1073 #if PA_CHECK_VERSION(0,9,15)
rlm@0 1074 if(pa_stream_set_buffer_attr_callback)
rlm@0 1075 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
rlm@0 1076 #endif
rlm@0 1077 pa_stream_set_moved_callback(data->stream, NULL, NULL);
rlm@0 1078 pa_stream_set_write_callback(data->stream, NULL, NULL);
rlm@0 1079 pa_stream_set_underflow_callback(data->stream, NULL, NULL);
rlm@0 1080 pa_stream_disconnect(data->stream);
rlm@0 1081 pa_stream_unref(data->stream);
rlm@0 1082 data->stream = NULL;
rlm@0 1083
rlm@0 1084 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1085 } //}}}
rlm@0 1086
rlm@0 1087
rlm@0 1088 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
rlm@0 1089 {
rlm@0 1090 char *pulse_name = NULL;
rlm@0 1091 pulse_data *data;
rlm@0 1092 pa_stream_flags_t flags = 0;
rlm@0 1093 pa_stream_state_t state;
rlm@0 1094 pa_channel_map chanmap;
rlm@0 1095
rlm@0 1096 if(!allCaptureDevNameMap)
rlm@0 1097 probe_devices(AL_TRUE);
rlm@0 1098
rlm@0 1099 if(!device_name)
rlm@0 1100 device_name = pulse_device;
rlm@0 1101 else if(strcmp(device_name, pulse_device) != 0)
rlm@0 1102 {
rlm@0 1103 ALuint i;
rlm@0 1104
rlm@0 1105 for(i = 0;i < numCaptureDevNames;i++)
rlm@0 1106 {
rlm@0 1107 if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)
rlm@0 1108 {
rlm@0 1109 pulse_name = allCaptureDevNameMap[i].device_name;
rlm@0 1110 break;
rlm@0 1111 }
rlm@0 1112 }
rlm@0 1113 if(i == numCaptureDevNames)
rlm@0 1114 return ALC_FALSE;
rlm@0 1115 }
rlm@0 1116
rlm@0 1117 if(pulse_open(device, device_name) == ALC_FALSE)
rlm@0 1118 return ALC_FALSE;
rlm@0 1119
rlm@0 1120 data = device->ExtraData;
rlm@0 1121 pa_threaded_mainloop_lock(data->loop);
rlm@0 1122
rlm@0 1123 data->samples = device->UpdateSize * device->NumUpdates;
rlm@0 1124 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
rlm@0 1125 if(data->samples < 100 * device->Frequency / 1000)
rlm@0 1126 data->samples = 100 * device->Frequency / 1000;
rlm@0 1127
rlm@0 1128 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
rlm@0 1129 {
rlm@0 1130 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1131 goto fail;
rlm@0 1132 }
rlm@0 1133
rlm@0 1134 data->attr.minreq = -1;
rlm@0 1135 data->attr.prebuf = -1;
rlm@0 1136 data->attr.maxlength = data->samples * data->frame_size;
rlm@0 1137 data->attr.tlength = -1;
rlm@0 1138 data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) *
rlm@0 1139 data->frame_size;
rlm@0 1140
rlm@0 1141 data->spec.rate = device->Frequency;
rlm@0 1142 data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
rlm@0 1143
rlm@0 1144 switch(device->FmtType)
rlm@0 1145 {
rlm@0 1146 case DevFmtUByte:
rlm@0 1147 data->spec.format = PA_SAMPLE_U8;
rlm@0 1148 break;
rlm@0 1149 case DevFmtShort:
rlm@0 1150 data->spec.format = PA_SAMPLE_S16NE;
rlm@0 1151 break;
rlm@0 1152 case DevFmtFloat:
rlm@0 1153 data->spec.format = PA_SAMPLE_FLOAT32NE;
rlm@0 1154 break;
rlm@0 1155 case DevFmtByte:
rlm@0 1156 case DevFmtUShort:
rlm@0 1157 ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType);
rlm@0 1158 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1159 goto fail;
rlm@0 1160 }
rlm@0 1161
rlm@0 1162 if(pa_sample_spec_valid(&data->spec) == 0)
rlm@0 1163 {
rlm@0 1164 ERR("Invalid sample format\n");
rlm@0 1165 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1166 goto fail;
rlm@0 1167 }
rlm@0 1168
rlm@0 1169 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
rlm@0 1170 {
rlm@0 1171 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
rlm@0 1172 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1173 goto fail;
rlm@0 1174 }
rlm@0 1175
rlm@0 1176 data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap);
rlm@0 1177 if(!data->stream)
rlm@0 1178 {
rlm@0 1179 ERR("pa_stream_new() failed: %s\n",
rlm@0 1180 pa_strerror(pa_context_errno(data->context)));
rlm@0 1181
rlm@0 1182 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1183 goto fail;
rlm@0 1184 }
rlm@0 1185
rlm@0 1186 pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop);
rlm@0 1187
rlm@0 1188 flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
rlm@0 1189 if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0)
rlm@0 1190 {
rlm@0 1191 ERR("Stream did not connect: %s\n",
rlm@0 1192 pa_strerror(pa_context_errno(data->context)));
rlm@0 1193
rlm@0 1194 pa_stream_unref(data->stream);
rlm@0 1195 data->stream = NULL;
rlm@0 1196
rlm@0 1197 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1198 goto fail;
rlm@0 1199 }
rlm@0 1200
rlm@0 1201 while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY)
rlm@0 1202 {
rlm@0 1203 if(!PA_STREAM_IS_GOOD(state))
rlm@0 1204 {
rlm@0 1205 ERR("Stream did not get ready: %s\n",
rlm@0 1206 pa_strerror(pa_context_errno(data->context)));
rlm@0 1207
rlm@0 1208 pa_stream_unref(data->stream);
rlm@0 1209 data->stream = NULL;
rlm@0 1210
rlm@0 1211 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1212 goto fail;
rlm@0 1213 }
rlm@0 1214
rlm@0 1215 pa_threaded_mainloop_wait(data->loop);
rlm@0 1216 }
rlm@0 1217 pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
rlm@0 1218
rlm@0 1219 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1220 return ALC_TRUE;
rlm@0 1221
rlm@0 1222 fail:
rlm@0 1223 pulse_close(device);
rlm@0 1224 return ALC_FALSE;
rlm@0 1225 } //}}}
rlm@0 1226
rlm@0 1227 static void pulse_close_capture(ALCdevice *device) //{{{
rlm@0 1228 {
rlm@0 1229 pulse_close(device);
rlm@0 1230 } //}}}
rlm@0 1231
rlm@0 1232 static void pulse_start_capture(ALCdevice *device) //{{{
rlm@0 1233 {
rlm@0 1234 pulse_data *data = device->ExtraData;
rlm@0 1235 pa_operation *o;
rlm@0 1236
rlm@0 1237 pa_threaded_mainloop_lock(data->loop);
rlm@0 1238 o = pa_stream_cork(data->stream, 0, stream_success_callback, device);
rlm@0 1239 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
rlm@0 1240 pa_threaded_mainloop_wait(data->loop);
rlm@0 1241 pa_operation_unref(o);
rlm@0 1242 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1243 } //}}}
rlm@0 1244
rlm@0 1245 static void pulse_stop_capture(ALCdevice *device) //{{{
rlm@0 1246 {
rlm@0 1247 pulse_data *data = device->ExtraData;
rlm@0 1248 pa_operation *o;
rlm@0 1249
rlm@0 1250 pa_threaded_mainloop_lock(data->loop);
rlm@0 1251 o = pa_stream_cork(data->stream, 1, stream_success_callback, device);
rlm@0 1252 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
rlm@0 1253 pa_threaded_mainloop_wait(data->loop);
rlm@0 1254 pa_operation_unref(o);
rlm@0 1255 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1256 } //}}}
rlm@0 1257
rlm@0 1258 static ALCuint pulse_available_samples(ALCdevice *device) //{{{
rlm@0 1259 {
rlm@0 1260 pulse_data *data = device->ExtraData;
rlm@0 1261 size_t samples;
rlm@0 1262
rlm@0 1263 pa_threaded_mainloop_lock(data->loop);
rlm@0 1264 /* Capture is done in fragment-sized chunks, so we loop until we get all
rlm@0 1265 * that's available */
rlm@0 1266 samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0);
rlm@0 1267 while(samples > 0)
rlm@0 1268 {
rlm@0 1269 const void *buf;
rlm@0 1270 size_t length;
rlm@0 1271
rlm@0 1272 if(pa_stream_peek(data->stream, &buf, &length) < 0)
rlm@0 1273 {
rlm@0 1274 ERR("pa_stream_peek() failed: %s\n",
rlm@0 1275 pa_strerror(pa_context_errno(data->context)));
rlm@0 1276 break;
rlm@0 1277 }
rlm@0 1278
rlm@0 1279 WriteRingBuffer(data->ring, buf, length/data->frame_size);
rlm@0 1280 samples -= length;
rlm@0 1281
rlm@0 1282 pa_stream_drop(data->stream);
rlm@0 1283 }
rlm@0 1284 pa_threaded_mainloop_unlock(data->loop);
rlm@0 1285
rlm@0 1286 return RingBufferSize(data->ring);
rlm@0 1287 } //}}}
rlm@0 1288
rlm@0 1289 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
rlm@0 1290 {
rlm@0 1291 pulse_data *data = device->ExtraData;
rlm@0 1292
rlm@0 1293 if(pulse_available_samples(device) >= samples)
rlm@0 1294 ReadRingBuffer(data->ring, buffer, samples);
rlm@0 1295 else
rlm@0 1296 alcSetError(device, ALC_INVALID_VALUE);
rlm@0 1297 } //}}}
rlm@0 1298
rlm@0 1299
rlm@0 1300 static const BackendFuncs pulse_funcs = { //{{{
rlm@0 1301 pulse_open_playback,
rlm@0 1302 pulse_close_playback,
rlm@0 1303 pulse_reset_playback,
rlm@0 1304 pulse_stop_playback,
rlm@0 1305 pulse_open_capture,
rlm@0 1306 pulse_close_capture,
rlm@0 1307 pulse_start_capture,
rlm@0 1308 pulse_stop_capture,
rlm@0 1309 pulse_capture_samples,
rlm@0 1310 pulse_available_samples
rlm@0 1311 }; //}}}
rlm@0 1312
rlm@0 1313 ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{
rlm@0 1314 {
rlm@0 1315 if(!pulse_load())
rlm@0 1316 return ALC_FALSE;
rlm@0 1317
rlm@0 1318 *func_list = pulse_funcs;
rlm@0 1319
rlm@0 1320 pulse_ctx_flags = 0;
rlm@0 1321 if(!GetConfigValueBool("pulse", "spawn-server", 0))
rlm@0 1322 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
rlm@0 1323
rlm@0 1324 return ALC_TRUE;
rlm@0 1325 } //}}}
rlm@0 1326
rlm@0 1327 void alc_pulse_deinit(void) //{{{
rlm@0 1328 {
rlm@0 1329 ALuint i;
rlm@0 1330
rlm@0 1331 for(i = 0;i < numDevNames;++i)
rlm@0 1332 {
rlm@0 1333 free(allDevNameMap[i].name);
rlm@0 1334 free(allDevNameMap[i].device_name);
rlm@0 1335 }
rlm@0 1336 free(allDevNameMap);
rlm@0 1337 allDevNameMap = NULL;
rlm@0 1338 numDevNames = 0;
rlm@0 1339
rlm@0 1340 for(i = 0;i < numCaptureDevNames;++i)
rlm@0 1341 {
rlm@0 1342 free(allCaptureDevNameMap[i].name);
rlm@0 1343 free(allCaptureDevNameMap[i].device_name);
rlm@0 1344 }
rlm@0 1345 free(allCaptureDevNameMap);
rlm@0 1346 allCaptureDevNameMap = NULL;
rlm@0 1347 numCaptureDevNames = 0;
rlm@0 1348
rlm@0 1349 #ifdef HAVE_DYNLOAD
rlm@0 1350 if(pa_handle)
rlm@0 1351 CloseLib(pa_handle);
rlm@0 1352 pa_handle = NULL;
rlm@0 1353 #endif
rlm@0 1354 } //}}}
rlm@0 1355
rlm@0 1356 void alc_pulse_probe(enum DevProbe type) //{{{
rlm@0 1357 {
rlm@0 1358 pa_threaded_mainloop *loop;
rlm@0 1359 ALuint i;
rlm@0 1360
rlm@0 1361 switch(type)
rlm@0 1362 {
rlm@0 1363 case DEVICE_PROBE:
rlm@0 1364 if((loop=pa_threaded_mainloop_new()) &&
rlm@0 1365 pa_threaded_mainloop_start(loop) >= 0)
rlm@0 1366 {
rlm@0 1367 pa_context *context;
rlm@0 1368
rlm@0 1369 pa_threaded_mainloop_lock(loop);
rlm@0 1370 context = connect_context(loop, AL_FALSE);
rlm@0 1371 if(context)
rlm@0 1372 {
rlm@0 1373 AppendDeviceList(pulse_device);
rlm@0 1374
rlm@0 1375 pa_context_disconnect(context);
rlm@0 1376 pa_context_unref(context);
rlm@0 1377 }
rlm@0 1378 pa_threaded_mainloop_unlock(loop);
rlm@0 1379 pa_threaded_mainloop_stop(loop);
rlm@0 1380 }
rlm@0 1381 if(loop)
rlm@0 1382 pa_threaded_mainloop_free(loop);
rlm@0 1383 break;
rlm@0 1384
rlm@0 1385 case ALL_DEVICE_PROBE:
rlm@0 1386 for(i = 0;i < numDevNames;++i)
rlm@0 1387 {
rlm@0 1388 free(allDevNameMap[i].name);
rlm@0 1389 free(allDevNameMap[i].device_name);
rlm@0 1390 }
rlm@0 1391 free(allDevNameMap);
rlm@0 1392 allDevNameMap = NULL;
rlm@0 1393 numDevNames = 0;
rlm@0 1394
rlm@0 1395 probe_devices(AL_FALSE);
rlm@0 1396
rlm@0 1397 for(i = 0;i < numDevNames;i++)
rlm@0 1398 AppendAllDeviceList(allDevNameMap[i].name);
rlm@0 1399 break;
rlm@0 1400
rlm@0 1401 case CAPTURE_DEVICE_PROBE:
rlm@0 1402 for(i = 0;i < numCaptureDevNames;++i)
rlm@0 1403 {
rlm@0 1404 free(allCaptureDevNameMap[i].name);
rlm@0 1405 free(allCaptureDevNameMap[i].device_name);
rlm@0 1406 }
rlm@0 1407 free(allCaptureDevNameMap);
rlm@0 1408 allCaptureDevNameMap = NULL;
rlm@0 1409 numCaptureDevNames = 0;
rlm@0 1410
rlm@0 1411 probe_devices(AL_TRUE);
rlm@0 1412
rlm@0 1413 for(i = 0;i < numCaptureDevNames;i++)
rlm@0 1414 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
rlm@0 1415 break;
rlm@0 1416 }
rlm@0 1417 } //}}}
rlm@0 1418 //}}}