Mercurial > audio-send
view Alc/backends/pulseaudio.c @ 1:c41d773a85fb
moved org files, ignored html files
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:03:35 -0700 |
parents | f9476ff7637e |
children |
line wrap: on
line source
1 /**2 * OpenAL cross platform audio library3 * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>4 * Copyright (C) 2010 by Chris Robinson <chris.kcat@gmail.com>5 * This library is free software; you can redistribute it and/or6 * modify it under the terms of the GNU Library General Public7 * License as published by the Free Software Foundation; either8 * version 2 of the License, or (at your option) any later version.9 *10 * This library is distributed in the hope that it will be useful,11 * but WITHOUT ANY WARRANTY; without even the implied warranty of12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU13 * Library General Public License for more details.14 *15 * You should have received a copy of the GNU Library General Public16 * License along with this library; if not, write to the17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,18 * Boston, MA 02111-1307, USA.19 * Or go to http://www.gnu.org/copyleft/lgpl.html20 */22 #include "config.h"24 #include "alMain.h"26 #include <pulse/pulseaudio.h>28 #if PA_API_VERSION == 1129 #define PA_STREAM_ADJUST_LATENCY 0x2000U30 #define PA_STREAM_EARLY_REQUESTS 0x4000U31 static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)32 {33 return (x == PA_STREAM_CREATING || x == PA_STREAM_READY);34 }35 static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)36 {37 return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING ||38 x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY);39 }40 #define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD41 #define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD42 #elif PA_API_VERSION != 1243 #error Invalid PulseAudio API version44 #endif46 #ifndef PA_CHECK_VERSION47 #define PA_CHECK_VERSION(major,minor,micro) \48 ((PA_MAJOR > (major)) || \49 (PA_MAJOR == (major) && PA_MINOR > (minor)) || \50 (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))51 #endif53 static void *pa_handle;54 #ifdef HAVE_DYNLOAD55 #define MAKE_FUNC(x) static typeof(x) * p##x56 MAKE_FUNC(pa_context_unref);57 MAKE_FUNC(pa_sample_spec_valid);58 MAKE_FUNC(pa_stream_drop);59 MAKE_FUNC(pa_strerror);60 MAKE_FUNC(pa_context_get_state);61 MAKE_FUNC(pa_stream_get_state);62 MAKE_FUNC(pa_threaded_mainloop_signal);63 MAKE_FUNC(pa_stream_peek);64 MAKE_FUNC(pa_threaded_mainloop_wait);65 MAKE_FUNC(pa_threaded_mainloop_unlock);66 MAKE_FUNC(pa_threaded_mainloop_in_thread);67 MAKE_FUNC(pa_context_new);68 MAKE_FUNC(pa_threaded_mainloop_stop);69 MAKE_FUNC(pa_context_disconnect);70 MAKE_FUNC(pa_threaded_mainloop_start);71 MAKE_FUNC(pa_threaded_mainloop_get_api);72 MAKE_FUNC(pa_context_set_state_callback);73 MAKE_FUNC(pa_stream_write);74 MAKE_FUNC(pa_xfree);75 MAKE_FUNC(pa_stream_connect_record);76 MAKE_FUNC(pa_stream_connect_playback);77 MAKE_FUNC(pa_stream_readable_size);78 MAKE_FUNC(pa_stream_writable_size);79 MAKE_FUNC(pa_stream_cork);80 MAKE_FUNC(pa_stream_is_suspended);81 MAKE_FUNC(pa_stream_get_device_name);82 MAKE_FUNC(pa_path_get_filename);83 MAKE_FUNC(pa_get_binary_name);84 MAKE_FUNC(pa_threaded_mainloop_free);85 MAKE_FUNC(pa_context_errno);86 MAKE_FUNC(pa_xmalloc);87 MAKE_FUNC(pa_stream_unref);88 MAKE_FUNC(pa_threaded_mainloop_accept);89 MAKE_FUNC(pa_stream_set_write_callback);90 MAKE_FUNC(pa_threaded_mainloop_new);91 MAKE_FUNC(pa_context_connect);92 MAKE_FUNC(pa_stream_set_buffer_attr);93 MAKE_FUNC(pa_stream_get_buffer_attr);94 MAKE_FUNC(pa_stream_get_sample_spec);95 MAKE_FUNC(pa_stream_get_time);96 MAKE_FUNC(pa_stream_set_read_callback);97 MAKE_FUNC(pa_stream_set_state_callback);98 MAKE_FUNC(pa_stream_set_moved_callback);99 MAKE_FUNC(pa_stream_set_underflow_callback);100 MAKE_FUNC(pa_stream_new);101 MAKE_FUNC(pa_stream_disconnect);102 MAKE_FUNC(pa_threaded_mainloop_lock);103 MAKE_FUNC(pa_channel_map_init_auto);104 MAKE_FUNC(pa_channel_map_parse);105 MAKE_FUNC(pa_channel_map_snprint);106 MAKE_FUNC(pa_channel_map_equal);107 MAKE_FUNC(pa_context_get_server_info);108 MAKE_FUNC(pa_context_get_sink_info_by_name);109 MAKE_FUNC(pa_context_get_sink_info_list);110 MAKE_FUNC(pa_context_get_source_info_list);111 MAKE_FUNC(pa_operation_get_state);112 MAKE_FUNC(pa_operation_unref);113 #if PA_CHECK_VERSION(0,9,15)114 MAKE_FUNC(pa_channel_map_superset);115 MAKE_FUNC(pa_stream_set_buffer_attr_callback);116 #endif117 #if PA_CHECK_VERSION(0,9,16)118 MAKE_FUNC(pa_stream_begin_write);119 #endif120 #undef MAKE_FUNC122 #define pa_context_unref ppa_context_unref123 #define pa_sample_spec_valid ppa_sample_spec_valid124 #define pa_stream_drop ppa_stream_drop125 #define pa_strerror ppa_strerror126 #define pa_context_get_state ppa_context_get_state127 #define pa_stream_get_state ppa_stream_get_state128 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal129 #define pa_stream_peek ppa_stream_peek130 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait131 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock132 #define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread133 #define pa_context_new ppa_context_new134 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop135 #define pa_context_disconnect ppa_context_disconnect136 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start137 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api138 #define pa_context_set_state_callback ppa_context_set_state_callback139 #define pa_stream_write ppa_stream_write140 #define pa_xfree ppa_xfree141 #define pa_stream_connect_record ppa_stream_connect_record142 #define pa_stream_connect_playback ppa_stream_connect_playback143 #define pa_stream_readable_size ppa_stream_readable_size144 #define pa_stream_writable_size ppa_stream_writable_size145 #define pa_stream_cork ppa_stream_cork146 #define pa_stream_is_suspended ppa_stream_is_suspended147 #define pa_stream_get_device_name ppa_stream_get_device_name148 #define pa_path_get_filename ppa_path_get_filename149 #define pa_get_binary_name ppa_get_binary_name150 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free151 #define pa_context_errno ppa_context_errno152 #define pa_xmalloc ppa_xmalloc153 #define pa_stream_unref ppa_stream_unref154 #define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept155 #define pa_stream_set_write_callback ppa_stream_set_write_callback156 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new157 #define pa_context_connect ppa_context_connect158 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr159 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr160 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec161 #define pa_stream_get_time ppa_stream_get_time162 #define pa_stream_set_read_callback ppa_stream_set_read_callback163 #define pa_stream_set_state_callback ppa_stream_set_state_callback164 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback165 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback166 #define pa_stream_new ppa_stream_new167 #define pa_stream_disconnect ppa_stream_disconnect168 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock169 #define pa_channel_map_init_auto ppa_channel_map_init_auto170 #define pa_channel_map_parse ppa_channel_map_parse171 #define pa_channel_map_snprint ppa_channel_map_snprint172 #define pa_channel_map_equal ppa_channel_map_equal173 #define pa_context_get_server_info ppa_context_get_server_info174 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name175 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list176 #define pa_context_get_source_info_list ppa_context_get_source_info_list177 #define pa_operation_get_state ppa_operation_get_state178 #define pa_operation_unref ppa_operation_unref179 #if PA_CHECK_VERSION(0,9,15)180 #define pa_channel_map_superset ppa_channel_map_superset181 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback182 #endif183 #if PA_CHECK_VERSION(0,9,16)184 #define pa_stream_begin_write ppa_stream_begin_write185 #endif187 #endif189 #ifndef PATH_MAX190 #define PATH_MAX 4096191 #endif193 typedef struct {194 char *device_name;196 ALCuint samples;197 ALCuint frame_size;199 RingBuffer *ring;201 pa_buffer_attr attr;202 pa_sample_spec spec;204 pa_threaded_mainloop *loop;206 ALvoid *thread;207 volatile ALboolean killNow;209 pa_stream *stream;210 pa_context *context;211 } pulse_data;213 typedef struct {214 char *name;215 char *device_name;216 } DevMap;219 static const ALCchar pulse_device[] = "PulseAudio Default";220 static DevMap *allDevNameMap;221 static ALuint numDevNames;222 static DevMap *allCaptureDevNameMap;223 static ALuint numCaptureDevNames;224 static pa_context_flags_t pulse_ctx_flags;227 static void context_state_callback(pa_context *context, void *pdata)228 {229 pa_threaded_mainloop *loop = pdata;230 pa_context_state_t state;232 state = pa_context_get_state(context);233 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))234 pa_threaded_mainloop_signal(loop, 0);235 }237 static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)238 {239 const char *name = "OpenAL Soft";240 char path_name[PATH_MAX];241 pa_context_state_t state;242 pa_context *context;243 int err;245 if(pa_get_binary_name(path_name, sizeof(path_name)))246 name = pa_path_get_filename(path_name);248 context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);249 if(!context)250 {251 ERR("pa_context_new() failed\n");252 return NULL;253 }255 pa_context_set_state_callback(context, context_state_callback, loop);257 if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)258 {259 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)260 {261 if(!PA_CONTEXT_IS_GOOD(state))262 {263 err = pa_context_errno(context);264 if(err > 0) err = -err;265 break;266 }268 pa_threaded_mainloop_wait(loop);269 }270 }271 pa_context_set_state_callback(context, NULL, NULL);273 if(err < 0)274 {275 if(!silent)276 ERR("Context did not connect: %s\n", pa_strerror(err));277 pa_context_unref(context);278 return NULL;279 }281 return context;282 }285 static ALCboolean pulse_load(void) //{{{286 {287 ALCboolean ret = ALC_FALSE;288 if(!pa_handle)289 {290 pa_threaded_mainloop *loop;292 #ifdef HAVE_DYNLOAD294 #ifdef _WIN32295 #define PALIB "libpulse-0.dll"296 #elif defined(__APPLE__) && defined(__MACH__)297 #define PALIB "libpulse.0.dylib"298 #else299 #define PALIB "libpulse.so.0"300 #endif301 pa_handle = LoadLib(PALIB);302 if(!pa_handle)303 return ALC_FALSE;305 #define LOAD_FUNC(x) do { \306 p##x = GetSymbol(pa_handle, #x); \307 if(!(p##x)) { \308 CloseLib(pa_handle); \309 pa_handle = NULL; \310 return ALC_FALSE; \311 } \312 } while(0)313 LOAD_FUNC(pa_context_unref);314 LOAD_FUNC(pa_sample_spec_valid);315 LOAD_FUNC(pa_stream_drop);316 LOAD_FUNC(pa_strerror);317 LOAD_FUNC(pa_context_get_state);318 LOAD_FUNC(pa_stream_get_state);319 LOAD_FUNC(pa_threaded_mainloop_signal);320 LOAD_FUNC(pa_stream_peek);321 LOAD_FUNC(pa_threaded_mainloop_wait);322 LOAD_FUNC(pa_threaded_mainloop_unlock);323 LOAD_FUNC(pa_threaded_mainloop_in_thread);324 LOAD_FUNC(pa_context_new);325 LOAD_FUNC(pa_threaded_mainloop_stop);326 LOAD_FUNC(pa_context_disconnect);327 LOAD_FUNC(pa_threaded_mainloop_start);328 LOAD_FUNC(pa_threaded_mainloop_get_api);329 LOAD_FUNC(pa_context_set_state_callback);330 LOAD_FUNC(pa_stream_write);331 LOAD_FUNC(pa_xfree);332 LOAD_FUNC(pa_stream_connect_record);333 LOAD_FUNC(pa_stream_connect_playback);334 LOAD_FUNC(pa_stream_readable_size);335 LOAD_FUNC(pa_stream_writable_size);336 LOAD_FUNC(pa_stream_cork);337 LOAD_FUNC(pa_stream_is_suspended);338 LOAD_FUNC(pa_stream_get_device_name);339 LOAD_FUNC(pa_path_get_filename);340 LOAD_FUNC(pa_get_binary_name);341 LOAD_FUNC(pa_threaded_mainloop_free);342 LOAD_FUNC(pa_context_errno);343 LOAD_FUNC(pa_xmalloc);344 LOAD_FUNC(pa_stream_unref);345 LOAD_FUNC(pa_threaded_mainloop_accept);346 LOAD_FUNC(pa_stream_set_write_callback);347 LOAD_FUNC(pa_threaded_mainloop_new);348 LOAD_FUNC(pa_context_connect);349 LOAD_FUNC(pa_stream_set_buffer_attr);350 LOAD_FUNC(pa_stream_get_buffer_attr);351 LOAD_FUNC(pa_stream_get_sample_spec);352 LOAD_FUNC(pa_stream_get_time);353 LOAD_FUNC(pa_stream_set_read_callback);354 LOAD_FUNC(pa_stream_set_state_callback);355 LOAD_FUNC(pa_stream_set_moved_callback);356 LOAD_FUNC(pa_stream_set_underflow_callback);357 LOAD_FUNC(pa_stream_new);358 LOAD_FUNC(pa_stream_disconnect);359 LOAD_FUNC(pa_threaded_mainloop_lock);360 LOAD_FUNC(pa_channel_map_init_auto);361 LOAD_FUNC(pa_channel_map_parse);362 LOAD_FUNC(pa_channel_map_snprint);363 LOAD_FUNC(pa_channel_map_equal);364 LOAD_FUNC(pa_context_get_server_info);365 LOAD_FUNC(pa_context_get_sink_info_by_name);366 LOAD_FUNC(pa_context_get_sink_info_list);367 LOAD_FUNC(pa_context_get_source_info_list);368 LOAD_FUNC(pa_operation_get_state);369 LOAD_FUNC(pa_operation_unref);370 #undef LOAD_FUNC371 #define LOAD_OPTIONAL_FUNC(x) do { \372 p##x = GetSymbol(pa_handle, #x); \373 } while(0)374 #if PA_CHECK_VERSION(0,9,15)375 LOAD_OPTIONAL_FUNC(pa_channel_map_superset);376 LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback);377 #endif378 #if PA_CHECK_VERSION(0,9,16)379 LOAD_OPTIONAL_FUNC(pa_stream_begin_write);380 #endif381 #undef LOAD_OPTIONAL_FUNC383 #else /* HAVE_DYNLOAD */384 pa_handle = (void*)0xDEADBEEF;385 #endif387 if((loop=pa_threaded_mainloop_new()) &&388 pa_threaded_mainloop_start(loop) >= 0)389 {390 pa_context *context;392 pa_threaded_mainloop_lock(loop);393 context = connect_context(loop, AL_TRUE);394 if(context)395 {396 ret = ALC_TRUE;398 pa_context_disconnect(context);399 pa_context_unref(context);400 }401 pa_threaded_mainloop_unlock(loop);402 pa_threaded_mainloop_stop(loop);403 }404 if(loop)405 pa_threaded_mainloop_free(loop);407 if(!ret)408 {409 #ifdef HAVE_DYNLOAD410 CloseLib(pa_handle);411 #endif412 pa_handle = NULL;413 }414 }415 return ret;416 } //}}}418 // PulseAudio Event Callbacks //{{{419 static void stream_state_callback(pa_stream *stream, void *pdata) //{{{420 {421 pa_threaded_mainloop *loop = pdata;422 pa_stream_state_t state;424 state = pa_stream_get_state(stream);425 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))426 pa_threaded_mainloop_signal(loop, 0);427 }//}}}429 static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{430 {431 ALCdevice *Device = pdata;432 pulse_data *data = Device->ExtraData;433 (void)stream;435 pa_threaded_mainloop_signal(data->loop, 0);436 }//}}}438 static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{439 {440 ALCdevice *Device = pdata;441 pulse_data *data = Device->ExtraData;443 LockDevice(Device);445 data->attr = *(pa_stream_get_buffer_attr(stream));446 Device->UpdateSize = data->attr.minreq / data->frame_size;447 Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize;448 if(Device->NumUpdates <= 1)449 {450 Device->NumUpdates = 1;451 ERR("PulseAudio returned minreq > tlength/2; expect break up\n");452 }454 UnlockDevice(Device);455 }//}}}457 static void stream_device_callback(pa_stream *stream, void *pdata) //{{{458 {459 ALCdevice *Device = pdata;460 pulse_data *data = Device->ExtraData;462 free(data->device_name);463 data->device_name = strdup(pa_stream_get_device_name(stream));464 }//}}}466 static void context_state_callback2(pa_context *context, void *pdata) //{{{467 {468 ALCdevice *Device = pdata;469 pulse_data *data = Device->ExtraData;471 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)472 {473 ERR("Received context failure!\n");474 aluHandleDisconnect(Device);475 }476 pa_threaded_mainloop_signal(data->loop, 0);477 }//}}}479 static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{480 {481 ALCdevice *Device = pdata;482 pulse_data *data = Device->ExtraData;484 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)485 {486 ERR("Received stream failure!\n");487 aluHandleDisconnect(Device);488 }489 pa_threaded_mainloop_signal(data->loop, 0);490 }//}}}492 static void stream_success_callback(pa_stream *stream, int success, void *pdata) //{{{493 {494 ALCdevice *Device = pdata;495 pulse_data *data = Device->ExtraData;496 (void)stream;497 (void)success;499 pa_threaded_mainloop_signal(data->loop, 0);500 }//}}}502 static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{503 {504 ALCdevice *device = pdata;505 pulse_data *data = device->ExtraData;506 char chanmap_str[256] = "";507 const struct {508 const char *str;509 enum DevFmtChannels chans;510 } chanmaps[] = {511 { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",512 DevFmtX71 },513 { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",514 DevFmtX61 },515 { "front-left,front-right,front-center,lfe,rear-left,rear-right",516 DevFmtX51 },517 { "front-left,front-right,front-center,lfe,side-left,side-right",518 DevFmtX51Side },519 { "front-left,front-right,rear-left,rear-right", DevFmtQuad },520 { "front-left,front-right", DevFmtStereo },521 { "mono", DevFmtMono },522 { NULL, 0 }523 };524 int i;525 (void)context;527 if(eol)528 {529 pa_threaded_mainloop_signal(data->loop, 0);530 return;531 }533 for(i = 0;chanmaps[i].str;i++)534 {535 pa_channel_map map;536 if(!pa_channel_map_parse(&map, chanmaps[i].str))537 continue;539 if(pa_channel_map_equal(&info->channel_map, &map)540 #if PA_CHECK_VERSION(0,9,15)541 || (pa_channel_map_superset &&542 pa_channel_map_superset(&info->channel_map, &map))543 #endif544 )545 {546 device->FmtChans = chanmaps[i].chans;547 return;548 }549 }551 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);552 ERR("Failed to find format for channel map:\n %s\n", chanmap_str);553 }//}}}555 static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{556 {557 pa_threaded_mainloop *loop = pdata;558 char str[1024];559 void *temp;560 int count;561 ALuint i;563 (void)context;565 if(eol)566 {567 pa_threaded_mainloop_signal(loop, 0);568 return;569 }571 count = 0;572 do {573 if(count == 0)574 snprintf(str, sizeof(str), "%s", info->description);575 else576 snprintf(str, sizeof(str), "%s #%d", info->description, count+1);577 count++;579 for(i = 0;i < numDevNames;i++)580 {581 if(strcmp(str, allDevNameMap[i].name) == 0)582 break;583 }584 } while(i != numDevNames);586 temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap));587 if(temp)588 {589 allDevNameMap = temp;590 allDevNameMap[numDevNames].name = strdup(str);591 allDevNameMap[numDevNames].device_name = strdup(info->name);592 numDevNames++;593 }594 }//}}}596 static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{597 {598 pa_threaded_mainloop *loop = pdata;599 char str[1024];600 void *temp;601 int count;602 ALuint i;604 (void)context;606 if(eol)607 {608 pa_threaded_mainloop_signal(loop, 0);609 return;610 }612 count = 0;613 do {614 if(count == 0)615 snprintf(str, sizeof(str), "%s", info->description);616 else617 snprintf(str, sizeof(str), "%s #%d", info->description, count+1);618 count++;620 for(i = 0;i < numCaptureDevNames;i++)621 {622 if(strcmp(str, allCaptureDevNameMap[i].name) == 0)623 break;624 }625 } while(i != numCaptureDevNames);627 temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap));628 if(temp)629 {630 allCaptureDevNameMap = temp;631 allCaptureDevNameMap[numCaptureDevNames].name = strdup(str);632 allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name);633 numCaptureDevNames++;634 }635 }//}}}636 //}}}638 // PulseAudio I/O Callbacks //{{{639 static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{640 {641 ALCdevice *Device = pdata;642 pulse_data *data = Device->ExtraData;643 (void)stream;644 (void)len;646 pa_threaded_mainloop_signal(data->loop, 0);647 } //}}}648 //}}}650 static ALuint PulseProc(ALvoid *param)651 {652 ALCdevice *Device = param;653 pulse_data *data = Device->ExtraData;654 ssize_t len;656 SetRTPriority();658 pa_threaded_mainloop_lock(data->loop);659 do {660 len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0);661 len -= len%(Device->UpdateSize*data->frame_size);662 if(len == 0)663 {664 pa_threaded_mainloop_wait(data->loop);665 continue;666 }668 while(len > 0)669 {670 size_t newlen = len;671 void *buf;672 pa_free_cb_t free_func = NULL;674 #if PA_CHECK_VERSION(0,9,16)675 if(!pa_stream_begin_write ||676 pa_stream_begin_write(data->stream, &buf, &newlen) < 0)677 #endif678 {679 buf = pa_xmalloc(newlen);680 free_func = pa_xfree;681 }682 pa_threaded_mainloop_unlock(data->loop);684 aluMixData(Device, buf, newlen/data->frame_size);686 pa_threaded_mainloop_lock(data->loop);687 pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);688 len -= newlen;689 }690 } while(Device->Connected && !data->killNow);691 pa_threaded_mainloop_unlock(data->loop);693 return 0;694 }696 static pa_stream *connect_playback_stream(ALCdevice *device,697 pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,698 pa_channel_map *chanmap)699 {700 pulse_data *data = device->ExtraData;701 pa_stream_state_t state;702 pa_stream *stream;704 stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap);705 if(!stream)706 {707 ERR("pa_stream_new() failed: %s\n",708 pa_strerror(pa_context_errno(data->context)));709 return NULL;710 }712 pa_stream_set_state_callback(stream, stream_state_callback, data->loop);714 if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0)715 {716 ERR("Stream did not connect: %s\n",717 pa_strerror(pa_context_errno(data->context)));718 pa_stream_unref(stream);719 return NULL;720 }722 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)723 {724 if(!PA_STREAM_IS_GOOD(state))725 {726 ERR("Stream did not get ready: %s\n",727 pa_strerror(pa_context_errno(data->context)));728 pa_stream_unref(stream);729 return NULL;730 }732 pa_threaded_mainloop_wait(data->loop);733 }734 pa_stream_set_state_callback(stream, NULL, NULL);736 return stream;737 }739 static void probe_devices(ALboolean capture)740 {741 pa_threaded_mainloop *loop;743 if(capture == AL_FALSE)744 allDevNameMap = malloc(sizeof(DevMap) * 1);745 else746 allCaptureDevNameMap = malloc(sizeof(DevMap) * 1);748 if((loop=pa_threaded_mainloop_new()) &&749 pa_threaded_mainloop_start(loop) >= 0)750 {751 pa_context *context;753 pa_threaded_mainloop_lock(loop);754 context = connect_context(loop, AL_FALSE);755 if(context)756 {757 pa_operation *o;759 if(capture == AL_FALSE)760 o = pa_context_get_sink_info_list(context, sink_device_callback, loop);761 else762 o = pa_context_get_source_info_list(context, source_device_callback, loop);763 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)764 pa_threaded_mainloop_wait(loop);765 pa_operation_unref(o);767 pa_context_disconnect(context);768 pa_context_unref(context);769 }770 pa_threaded_mainloop_unlock(loop);771 pa_threaded_mainloop_stop(loop);772 }773 if(loop)774 pa_threaded_mainloop_free(loop);775 }778 static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{779 {780 pulse_data *data = pa_xmalloc(sizeof(pulse_data));781 memset(data, 0, sizeof(*data));783 if(!(data->loop = pa_threaded_mainloop_new()))784 {785 ERR("pa_threaded_mainloop_new() failed!\n");786 goto out;787 }788 if(pa_threaded_mainloop_start(data->loop) < 0)789 {790 ERR("pa_threaded_mainloop_start() failed\n");791 goto out;792 }794 pa_threaded_mainloop_lock(data->loop);795 device->ExtraData = data;797 data->context = connect_context(data->loop, AL_FALSE);798 if(!data->context)799 {800 pa_threaded_mainloop_unlock(data->loop);801 goto out;802 }803 pa_context_set_state_callback(data->context, context_state_callback2, device);805 device->szDeviceName = strdup(device_name);807 pa_threaded_mainloop_unlock(data->loop);808 return ALC_TRUE;810 out:811 if(data->loop)812 {813 pa_threaded_mainloop_stop(data->loop);814 pa_threaded_mainloop_free(data->loop);815 }817 device->ExtraData = NULL;818 pa_xfree(data);819 return ALC_FALSE;820 } //}}}822 static void pulse_close(ALCdevice *device) //{{{823 {824 pulse_data *data = device->ExtraData;826 pa_threaded_mainloop_lock(data->loop);828 if(data->stream)829 {830 pa_stream_disconnect(data->stream);831 pa_stream_unref(data->stream);832 }834 pa_context_disconnect(data->context);835 pa_context_unref(data->context);837 pa_threaded_mainloop_unlock(data->loop);839 pa_threaded_mainloop_stop(data->loop);840 pa_threaded_mainloop_free(data->loop);842 DestroyRingBuffer(data->ring);843 free(data->device_name);845 device->ExtraData = NULL;846 pa_xfree(data);847 } //}}}848 //}}}850 // OpenAL {{{851 static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{852 {853 char *pulse_name = NULL;854 pa_sample_spec spec;855 pulse_data *data;857 if(!allDevNameMap)858 probe_devices(AL_FALSE);860 if(!device_name)861 device_name = pulse_device;862 else if(strcmp(device_name, pulse_device) != 0)863 {864 ALuint i;866 for(i = 0;i < numDevNames;i++)867 {868 if(strcmp(device_name, allDevNameMap[i].name) == 0)869 {870 pulse_name = allDevNameMap[i].device_name;871 break;872 }873 }874 if(i == numDevNames)875 return ALC_FALSE;876 }878 if(pulse_open(device, device_name) == ALC_FALSE)879 return ALC_FALSE;881 data = device->ExtraData;883 pa_threaded_mainloop_lock(data->loop);885 spec.format = PA_SAMPLE_S16NE;886 spec.rate = 44100;887 spec.channels = 2;889 data->device_name = pulse_name;890 pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL);891 if(!stream)892 {893 pa_threaded_mainloop_unlock(data->loop);894 goto fail;895 }897 if(pa_stream_is_suspended(stream))898 {899 ERR("Device is suspended\n");900 pa_stream_disconnect(stream);901 pa_stream_unref(stream);902 pa_threaded_mainloop_unlock(data->loop);903 goto fail;904 }905 data->device_name = strdup(pa_stream_get_device_name(stream));907 pa_stream_disconnect(stream);908 pa_stream_unref(stream);910 pa_threaded_mainloop_unlock(data->loop);912 return ALC_TRUE;914 fail:915 pulse_close(device);916 return ALC_FALSE;917 } //}}}919 static void pulse_close_playback(ALCdevice *device) //{{{920 {921 pulse_close(device);922 } //}}}924 static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{925 {926 pulse_data *data = device->ExtraData;927 pa_stream_flags_t flags = 0;928 pa_channel_map chanmap;930 pa_threaded_mainloop_lock(data->loop);932 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))933 {934 pa_operation *o;935 o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);936 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)937 pa_threaded_mainloop_wait(data->loop);938 pa_operation_unref(o);939 }940 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))941 flags |= PA_STREAM_FIX_RATE;943 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);944 data->attr.prebuf = -1;945 data->attr.fragsize = -1;946 data->attr.minreq = device->UpdateSize * data->frame_size;947 data->attr.tlength = data->attr.minreq * device->NumUpdates;948 if(data->attr.tlength < data->attr.minreq*2)949 data->attr.tlength = data->attr.minreq*2;950 data->attr.maxlength = data->attr.tlength;951 flags |= PA_STREAM_EARLY_REQUESTS;952 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;954 switch(device->FmtType)955 {956 case DevFmtByte:957 device->FmtType = DevFmtUByte;958 /* fall-through */959 case DevFmtUByte:960 data->spec.format = PA_SAMPLE_U8;961 break;962 case DevFmtUShort:963 device->FmtType = DevFmtShort;964 /* fall-through */965 case DevFmtShort:966 data->spec.format = PA_SAMPLE_S16NE;967 break;968 case DevFmtFloat:969 data->spec.format = PA_SAMPLE_FLOAT32NE;970 break;971 }972 data->spec.rate = device->Frequency;973 data->spec.channels = ChannelsFromDevFmt(device->FmtChans);975 if(pa_sample_spec_valid(&data->spec) == 0)976 {977 ERR("Invalid sample format\n");978 pa_threaded_mainloop_unlock(data->loop);979 return ALC_FALSE;980 }982 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))983 {984 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);985 pa_threaded_mainloop_unlock(data->loop);986 return ALC_FALSE;987 }988 SetDefaultWFXChannelOrder(device);990 data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap);991 if(!data->stream)992 {993 pa_threaded_mainloop_unlock(data->loop);994 return ALC_FALSE;995 }997 pa_stream_set_state_callback(data->stream, stream_state_callback2, device);999 data->spec = *(pa_stream_get_sample_spec(data->stream));1000 if(device->Frequency != data->spec.rate)1001 {1002 pa_operation *o;1004 if((device->Flags&DEVICE_FREQUENCY_REQUEST))1005 ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate);1006 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;1008 /* Server updated our playback rate, so modify the buffer attribs1009 * accordingly. */1010 data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) *1011 data->spec.rate / device->Frequency * data->frame_size;1012 data->attr.tlength = data->attr.minreq * device->NumUpdates;1013 data->attr.maxlength = data->attr.tlength;1015 o = pa_stream_set_buffer_attr(data->stream, &data->attr,1016 stream_success_callback, device);1017 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)1018 pa_threaded_mainloop_wait(data->loop);1019 pa_operation_unref(o);1021 device->Frequency = data->spec.rate;1022 }1024 stream_buffer_attr_callback(data->stream, device);1025 #if PA_CHECK_VERSION(0,9,15)1026 if(pa_stream_set_buffer_attr_callback)1027 pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);1028 #endif1029 pa_stream_set_moved_callback(data->stream, stream_device_callback, device);1030 pa_stream_set_write_callback(data->stream, stream_write_callback, device);1031 pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device);1033 data->thread = StartThread(PulseProc, device);1034 if(!data->thread)1035 {1036 #if PA_CHECK_VERSION(0,9,15)1037 if(pa_stream_set_buffer_attr_callback)1038 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);1039 #endif1040 pa_stream_set_moved_callback(data->stream, NULL, NULL);1041 pa_stream_set_write_callback(data->stream, NULL, NULL);1042 pa_stream_set_underflow_callback(data->stream, NULL, NULL);1043 pa_stream_disconnect(data->stream);1044 pa_stream_unref(data->stream);1045 data->stream = NULL;1047 pa_threaded_mainloop_unlock(data->loop);1048 return ALC_FALSE;1049 }1051 pa_threaded_mainloop_unlock(data->loop);1052 return ALC_TRUE;1053 } //}}}1055 static void pulse_stop_playback(ALCdevice *device) //{{{1056 {1057 pulse_data *data = device->ExtraData;1059 if(!data->stream)1060 return;1062 data->killNow = AL_TRUE;1063 if(data->thread)1064 {1065 pa_threaded_mainloop_signal(data->loop, 0);1066 StopThread(data->thread);1067 data->thread = NULL;1068 }1069 data->killNow = AL_FALSE;1071 pa_threaded_mainloop_lock(data->loop);1073 #if PA_CHECK_VERSION(0,9,15)1074 if(pa_stream_set_buffer_attr_callback)1075 pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);1076 #endif1077 pa_stream_set_moved_callback(data->stream, NULL, NULL);1078 pa_stream_set_write_callback(data->stream, NULL, NULL);1079 pa_stream_set_underflow_callback(data->stream, NULL, NULL);1080 pa_stream_disconnect(data->stream);1081 pa_stream_unref(data->stream);1082 data->stream = NULL;1084 pa_threaded_mainloop_unlock(data->loop);1085 } //}}}1088 static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{1089 {1090 char *pulse_name = NULL;1091 pulse_data *data;1092 pa_stream_flags_t flags = 0;1093 pa_stream_state_t state;1094 pa_channel_map chanmap;1096 if(!allCaptureDevNameMap)1097 probe_devices(AL_TRUE);1099 if(!device_name)1100 device_name = pulse_device;1101 else if(strcmp(device_name, pulse_device) != 0)1102 {1103 ALuint i;1105 for(i = 0;i < numCaptureDevNames;i++)1106 {1107 if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)1108 {1109 pulse_name = allCaptureDevNameMap[i].device_name;1110 break;1111 }1112 }1113 if(i == numCaptureDevNames)1114 return ALC_FALSE;1115 }1117 if(pulse_open(device, device_name) == ALC_FALSE)1118 return ALC_FALSE;1120 data = device->ExtraData;1121 pa_threaded_mainloop_lock(data->loop);1123 data->samples = device->UpdateSize * device->NumUpdates;1124 data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);1125 if(data->samples < 100 * device->Frequency / 1000)1126 data->samples = 100 * device->Frequency / 1000;1128 if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))1129 {1130 pa_threaded_mainloop_unlock(data->loop);1131 goto fail;1132 }1134 data->attr.minreq = -1;1135 data->attr.prebuf = -1;1136 data->attr.maxlength = data->samples * data->frame_size;1137 data->attr.tlength = -1;1138 data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) *1139 data->frame_size;1141 data->spec.rate = device->Frequency;1142 data->spec.channels = ChannelsFromDevFmt(device->FmtChans);1144 switch(device->FmtType)1145 {1146 case DevFmtUByte:1147 data->spec.format = PA_SAMPLE_U8;1148 break;1149 case DevFmtShort:1150 data->spec.format = PA_SAMPLE_S16NE;1151 break;1152 case DevFmtFloat:1153 data->spec.format = PA_SAMPLE_FLOAT32NE;1154 break;1155 case DevFmtByte:1156 case DevFmtUShort:1157 ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType);1158 pa_threaded_mainloop_unlock(data->loop);1159 goto fail;1160 }1162 if(pa_sample_spec_valid(&data->spec) == 0)1163 {1164 ERR("Invalid sample format\n");1165 pa_threaded_mainloop_unlock(data->loop);1166 goto fail;1167 }1169 if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))1170 {1171 ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);1172 pa_threaded_mainloop_unlock(data->loop);1173 goto fail;1174 }1176 data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap);1177 if(!data->stream)1178 {1179 ERR("pa_stream_new() failed: %s\n",1180 pa_strerror(pa_context_errno(data->context)));1182 pa_threaded_mainloop_unlock(data->loop);1183 goto fail;1184 }1186 pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop);1188 flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;1189 if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0)1190 {1191 ERR("Stream did not connect: %s\n",1192 pa_strerror(pa_context_errno(data->context)));1194 pa_stream_unref(data->stream);1195 data->stream = NULL;1197 pa_threaded_mainloop_unlock(data->loop);1198 goto fail;1199 }1201 while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY)1202 {1203 if(!PA_STREAM_IS_GOOD(state))1204 {1205 ERR("Stream did not get ready: %s\n",1206 pa_strerror(pa_context_errno(data->context)));1208 pa_stream_unref(data->stream);1209 data->stream = NULL;1211 pa_threaded_mainloop_unlock(data->loop);1212 goto fail;1213 }1215 pa_threaded_mainloop_wait(data->loop);1216 }1217 pa_stream_set_state_callback(data->stream, stream_state_callback2, device);1219 pa_threaded_mainloop_unlock(data->loop);1220 return ALC_TRUE;1222 fail:1223 pulse_close(device);1224 return ALC_FALSE;1225 } //}}}1227 static void pulse_close_capture(ALCdevice *device) //{{{1228 {1229 pulse_close(device);1230 } //}}}1232 static void pulse_start_capture(ALCdevice *device) //{{{1233 {1234 pulse_data *data = device->ExtraData;1235 pa_operation *o;1237 pa_threaded_mainloop_lock(data->loop);1238 o = pa_stream_cork(data->stream, 0, stream_success_callback, device);1239 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)1240 pa_threaded_mainloop_wait(data->loop);1241 pa_operation_unref(o);1242 pa_threaded_mainloop_unlock(data->loop);1243 } //}}}1245 static void pulse_stop_capture(ALCdevice *device) //{{{1246 {1247 pulse_data *data = device->ExtraData;1248 pa_operation *o;1250 pa_threaded_mainloop_lock(data->loop);1251 o = pa_stream_cork(data->stream, 1, stream_success_callback, device);1252 while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)1253 pa_threaded_mainloop_wait(data->loop);1254 pa_operation_unref(o);1255 pa_threaded_mainloop_unlock(data->loop);1256 } //}}}1258 static ALCuint pulse_available_samples(ALCdevice *device) //{{{1259 {1260 pulse_data *data = device->ExtraData;1261 size_t samples;1263 pa_threaded_mainloop_lock(data->loop);1264 /* Capture is done in fragment-sized chunks, so we loop until we get all1265 * that's available */1266 samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0);1267 while(samples > 0)1268 {1269 const void *buf;1270 size_t length;1272 if(pa_stream_peek(data->stream, &buf, &length) < 0)1273 {1274 ERR("pa_stream_peek() failed: %s\n",1275 pa_strerror(pa_context_errno(data->context)));1276 break;1277 }1279 WriteRingBuffer(data->ring, buf, length/data->frame_size);1280 samples -= length;1282 pa_stream_drop(data->stream);1283 }1284 pa_threaded_mainloop_unlock(data->loop);1286 return RingBufferSize(data->ring);1287 } //}}}1289 static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{1290 {1291 pulse_data *data = device->ExtraData;1293 if(pulse_available_samples(device) >= samples)1294 ReadRingBuffer(data->ring, buffer, samples);1295 else1296 alcSetError(device, ALC_INVALID_VALUE);1297 } //}}}1300 static const BackendFuncs pulse_funcs = { //{{{1301 pulse_open_playback,1302 pulse_close_playback,1303 pulse_reset_playback,1304 pulse_stop_playback,1305 pulse_open_capture,1306 pulse_close_capture,1307 pulse_start_capture,1308 pulse_stop_capture,1309 pulse_capture_samples,1310 pulse_available_samples1311 }; //}}}1313 ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{1314 {1315 if(!pulse_load())1316 return ALC_FALSE;1318 *func_list = pulse_funcs;1320 pulse_ctx_flags = 0;1321 if(!GetConfigValueBool("pulse", "spawn-server", 0))1322 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;1324 return ALC_TRUE;1325 } //}}}1327 void alc_pulse_deinit(void) //{{{1328 {1329 ALuint i;1331 for(i = 0;i < numDevNames;++i)1332 {1333 free(allDevNameMap[i].name);1334 free(allDevNameMap[i].device_name);1335 }1336 free(allDevNameMap);1337 allDevNameMap = NULL;1338 numDevNames = 0;1340 for(i = 0;i < numCaptureDevNames;++i)1341 {1342 free(allCaptureDevNameMap[i].name);1343 free(allCaptureDevNameMap[i].device_name);1344 }1345 free(allCaptureDevNameMap);1346 allCaptureDevNameMap = NULL;1347 numCaptureDevNames = 0;1349 #ifdef HAVE_DYNLOAD1350 if(pa_handle)1351 CloseLib(pa_handle);1352 pa_handle = NULL;1353 #endif1354 } //}}}1356 void alc_pulse_probe(enum DevProbe type) //{{{1357 {1358 pa_threaded_mainloop *loop;1359 ALuint i;1361 switch(type)1362 {1363 case DEVICE_PROBE:1364 if((loop=pa_threaded_mainloop_new()) &&1365 pa_threaded_mainloop_start(loop) >= 0)1366 {1367 pa_context *context;1369 pa_threaded_mainloop_lock(loop);1370 context = connect_context(loop, AL_FALSE);1371 if(context)1372 {1373 AppendDeviceList(pulse_device);1375 pa_context_disconnect(context);1376 pa_context_unref(context);1377 }1378 pa_threaded_mainloop_unlock(loop);1379 pa_threaded_mainloop_stop(loop);1380 }1381 if(loop)1382 pa_threaded_mainloop_free(loop);1383 break;1385 case ALL_DEVICE_PROBE:1386 for(i = 0;i < numDevNames;++i)1387 {1388 free(allDevNameMap[i].name);1389 free(allDevNameMap[i].device_name);1390 }1391 free(allDevNameMap);1392 allDevNameMap = NULL;1393 numDevNames = 0;1395 probe_devices(AL_FALSE);1397 for(i = 0;i < numDevNames;i++)1398 AppendAllDeviceList(allDevNameMap[i].name);1399 break;1401 case CAPTURE_DEVICE_PROBE:1402 for(i = 0;i < numCaptureDevNames;++i)1403 {1404 free(allCaptureDevNameMap[i].name);1405 free(allCaptureDevNameMap[i].device_name);1406 }1407 free(allCaptureDevNameMap);1408 allCaptureDevNameMap = NULL;1409 numCaptureDevNames = 0;1411 probe_devices(AL_TRUE);1413 for(i = 0;i < numCaptureDevNames;i++)1414 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);1415 break;1416 }1417 } //}}}1418 //}}}