Mercurial > audio-send
diff 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 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alc/backends/pulseaudio.c Tue Oct 25 13:02:31 2011 -0700 1.3 @@ -0,0 +1,1418 @@ 1.4 +/** 1.5 + * OpenAL cross platform audio library 1.6 + * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com> 1.7 + * Copyright (C) 2010 by Chris Robinson <chris.kcat@gmail.com> 1.8 + * This library is free software; you can redistribute it and/or 1.9 + * modify it under the terms of the GNU Library General Public 1.10 + * License as published by the Free Software Foundation; either 1.11 + * version 2 of the License, or (at your option) any later version. 1.12 + * 1.13 + * This library is distributed in the hope that it will be useful, 1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.16 + * Library General Public License for more details. 1.17 + * 1.18 + * You should have received a copy of the GNU Library General Public 1.19 + * License along with this library; if not, write to the 1.20 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 1.21 + * Boston, MA 02111-1307, USA. 1.22 + * Or go to http://www.gnu.org/copyleft/lgpl.html 1.23 + */ 1.24 + 1.25 +#include "config.h" 1.26 + 1.27 +#include "alMain.h" 1.28 + 1.29 +#include <pulse/pulseaudio.h> 1.30 + 1.31 +#if PA_API_VERSION == 11 1.32 +#define PA_STREAM_ADJUST_LATENCY 0x2000U 1.33 +#define PA_STREAM_EARLY_REQUESTS 0x4000U 1.34 +static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) 1.35 +{ 1.36 + return (x == PA_STREAM_CREATING || x == PA_STREAM_READY); 1.37 +} 1.38 +static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) 1.39 +{ 1.40 + return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || 1.41 + x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY); 1.42 +} 1.43 +#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD 1.44 +#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD 1.45 +#elif PA_API_VERSION != 12 1.46 +#error Invalid PulseAudio API version 1.47 +#endif 1.48 + 1.49 +#ifndef PA_CHECK_VERSION 1.50 +#define PA_CHECK_VERSION(major,minor,micro) \ 1.51 + ((PA_MAJOR > (major)) || \ 1.52 + (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ 1.53 + (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) 1.54 +#endif 1.55 + 1.56 +static void *pa_handle; 1.57 +#ifdef HAVE_DYNLOAD 1.58 +#define MAKE_FUNC(x) static typeof(x) * p##x 1.59 +MAKE_FUNC(pa_context_unref); 1.60 +MAKE_FUNC(pa_sample_spec_valid); 1.61 +MAKE_FUNC(pa_stream_drop); 1.62 +MAKE_FUNC(pa_strerror); 1.63 +MAKE_FUNC(pa_context_get_state); 1.64 +MAKE_FUNC(pa_stream_get_state); 1.65 +MAKE_FUNC(pa_threaded_mainloop_signal); 1.66 +MAKE_FUNC(pa_stream_peek); 1.67 +MAKE_FUNC(pa_threaded_mainloop_wait); 1.68 +MAKE_FUNC(pa_threaded_mainloop_unlock); 1.69 +MAKE_FUNC(pa_threaded_mainloop_in_thread); 1.70 +MAKE_FUNC(pa_context_new); 1.71 +MAKE_FUNC(pa_threaded_mainloop_stop); 1.72 +MAKE_FUNC(pa_context_disconnect); 1.73 +MAKE_FUNC(pa_threaded_mainloop_start); 1.74 +MAKE_FUNC(pa_threaded_mainloop_get_api); 1.75 +MAKE_FUNC(pa_context_set_state_callback); 1.76 +MAKE_FUNC(pa_stream_write); 1.77 +MAKE_FUNC(pa_xfree); 1.78 +MAKE_FUNC(pa_stream_connect_record); 1.79 +MAKE_FUNC(pa_stream_connect_playback); 1.80 +MAKE_FUNC(pa_stream_readable_size); 1.81 +MAKE_FUNC(pa_stream_writable_size); 1.82 +MAKE_FUNC(pa_stream_cork); 1.83 +MAKE_FUNC(pa_stream_is_suspended); 1.84 +MAKE_FUNC(pa_stream_get_device_name); 1.85 +MAKE_FUNC(pa_path_get_filename); 1.86 +MAKE_FUNC(pa_get_binary_name); 1.87 +MAKE_FUNC(pa_threaded_mainloop_free); 1.88 +MAKE_FUNC(pa_context_errno); 1.89 +MAKE_FUNC(pa_xmalloc); 1.90 +MAKE_FUNC(pa_stream_unref); 1.91 +MAKE_FUNC(pa_threaded_mainloop_accept); 1.92 +MAKE_FUNC(pa_stream_set_write_callback); 1.93 +MAKE_FUNC(pa_threaded_mainloop_new); 1.94 +MAKE_FUNC(pa_context_connect); 1.95 +MAKE_FUNC(pa_stream_set_buffer_attr); 1.96 +MAKE_FUNC(pa_stream_get_buffer_attr); 1.97 +MAKE_FUNC(pa_stream_get_sample_spec); 1.98 +MAKE_FUNC(pa_stream_get_time); 1.99 +MAKE_FUNC(pa_stream_set_read_callback); 1.100 +MAKE_FUNC(pa_stream_set_state_callback); 1.101 +MAKE_FUNC(pa_stream_set_moved_callback); 1.102 +MAKE_FUNC(pa_stream_set_underflow_callback); 1.103 +MAKE_FUNC(pa_stream_new); 1.104 +MAKE_FUNC(pa_stream_disconnect); 1.105 +MAKE_FUNC(pa_threaded_mainloop_lock); 1.106 +MAKE_FUNC(pa_channel_map_init_auto); 1.107 +MAKE_FUNC(pa_channel_map_parse); 1.108 +MAKE_FUNC(pa_channel_map_snprint); 1.109 +MAKE_FUNC(pa_channel_map_equal); 1.110 +MAKE_FUNC(pa_context_get_server_info); 1.111 +MAKE_FUNC(pa_context_get_sink_info_by_name); 1.112 +MAKE_FUNC(pa_context_get_sink_info_list); 1.113 +MAKE_FUNC(pa_context_get_source_info_list); 1.114 +MAKE_FUNC(pa_operation_get_state); 1.115 +MAKE_FUNC(pa_operation_unref); 1.116 +#if PA_CHECK_VERSION(0,9,15) 1.117 +MAKE_FUNC(pa_channel_map_superset); 1.118 +MAKE_FUNC(pa_stream_set_buffer_attr_callback); 1.119 +#endif 1.120 +#if PA_CHECK_VERSION(0,9,16) 1.121 +MAKE_FUNC(pa_stream_begin_write); 1.122 +#endif 1.123 +#undef MAKE_FUNC 1.124 + 1.125 +#define pa_context_unref ppa_context_unref 1.126 +#define pa_sample_spec_valid ppa_sample_spec_valid 1.127 +#define pa_stream_drop ppa_stream_drop 1.128 +#define pa_strerror ppa_strerror 1.129 +#define pa_context_get_state ppa_context_get_state 1.130 +#define pa_stream_get_state ppa_stream_get_state 1.131 +#define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal 1.132 +#define pa_stream_peek ppa_stream_peek 1.133 +#define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait 1.134 +#define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock 1.135 +#define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread 1.136 +#define pa_context_new ppa_context_new 1.137 +#define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop 1.138 +#define pa_context_disconnect ppa_context_disconnect 1.139 +#define pa_threaded_mainloop_start ppa_threaded_mainloop_start 1.140 +#define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api 1.141 +#define pa_context_set_state_callback ppa_context_set_state_callback 1.142 +#define pa_stream_write ppa_stream_write 1.143 +#define pa_xfree ppa_xfree 1.144 +#define pa_stream_connect_record ppa_stream_connect_record 1.145 +#define pa_stream_connect_playback ppa_stream_connect_playback 1.146 +#define pa_stream_readable_size ppa_stream_readable_size 1.147 +#define pa_stream_writable_size ppa_stream_writable_size 1.148 +#define pa_stream_cork ppa_stream_cork 1.149 +#define pa_stream_is_suspended ppa_stream_is_suspended 1.150 +#define pa_stream_get_device_name ppa_stream_get_device_name 1.151 +#define pa_path_get_filename ppa_path_get_filename 1.152 +#define pa_get_binary_name ppa_get_binary_name 1.153 +#define pa_threaded_mainloop_free ppa_threaded_mainloop_free 1.154 +#define pa_context_errno ppa_context_errno 1.155 +#define pa_xmalloc ppa_xmalloc 1.156 +#define pa_stream_unref ppa_stream_unref 1.157 +#define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept 1.158 +#define pa_stream_set_write_callback ppa_stream_set_write_callback 1.159 +#define pa_threaded_mainloop_new ppa_threaded_mainloop_new 1.160 +#define pa_context_connect ppa_context_connect 1.161 +#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr 1.162 +#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr 1.163 +#define pa_stream_get_sample_spec ppa_stream_get_sample_spec 1.164 +#define pa_stream_get_time ppa_stream_get_time 1.165 +#define pa_stream_set_read_callback ppa_stream_set_read_callback 1.166 +#define pa_stream_set_state_callback ppa_stream_set_state_callback 1.167 +#define pa_stream_set_moved_callback ppa_stream_set_moved_callback 1.168 +#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback 1.169 +#define pa_stream_new ppa_stream_new 1.170 +#define pa_stream_disconnect ppa_stream_disconnect 1.171 +#define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock 1.172 +#define pa_channel_map_init_auto ppa_channel_map_init_auto 1.173 +#define pa_channel_map_parse ppa_channel_map_parse 1.174 +#define pa_channel_map_snprint ppa_channel_map_snprint 1.175 +#define pa_channel_map_equal ppa_channel_map_equal 1.176 +#define pa_context_get_server_info ppa_context_get_server_info 1.177 +#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name 1.178 +#define pa_context_get_sink_info_list ppa_context_get_sink_info_list 1.179 +#define pa_context_get_source_info_list ppa_context_get_source_info_list 1.180 +#define pa_operation_get_state ppa_operation_get_state 1.181 +#define pa_operation_unref ppa_operation_unref 1.182 +#if PA_CHECK_VERSION(0,9,15) 1.183 +#define pa_channel_map_superset ppa_channel_map_superset 1.184 +#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback 1.185 +#endif 1.186 +#if PA_CHECK_VERSION(0,9,16) 1.187 +#define pa_stream_begin_write ppa_stream_begin_write 1.188 +#endif 1.189 + 1.190 +#endif 1.191 + 1.192 +#ifndef PATH_MAX 1.193 +#define PATH_MAX 4096 1.194 +#endif 1.195 + 1.196 +typedef struct { 1.197 + char *device_name; 1.198 + 1.199 + ALCuint samples; 1.200 + ALCuint frame_size; 1.201 + 1.202 + RingBuffer *ring; 1.203 + 1.204 + pa_buffer_attr attr; 1.205 + pa_sample_spec spec; 1.206 + 1.207 + pa_threaded_mainloop *loop; 1.208 + 1.209 + ALvoid *thread; 1.210 + volatile ALboolean killNow; 1.211 + 1.212 + pa_stream *stream; 1.213 + pa_context *context; 1.214 +} pulse_data; 1.215 + 1.216 +typedef struct { 1.217 + char *name; 1.218 + char *device_name; 1.219 +} DevMap; 1.220 + 1.221 + 1.222 +static const ALCchar pulse_device[] = "PulseAudio Default"; 1.223 +static DevMap *allDevNameMap; 1.224 +static ALuint numDevNames; 1.225 +static DevMap *allCaptureDevNameMap; 1.226 +static ALuint numCaptureDevNames; 1.227 +static pa_context_flags_t pulse_ctx_flags; 1.228 + 1.229 + 1.230 +static void context_state_callback(pa_context *context, void *pdata) 1.231 +{ 1.232 + pa_threaded_mainloop *loop = pdata; 1.233 + pa_context_state_t state; 1.234 + 1.235 + state = pa_context_get_state(context); 1.236 + if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state)) 1.237 + pa_threaded_mainloop_signal(loop, 0); 1.238 +} 1.239 + 1.240 +static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) 1.241 +{ 1.242 + const char *name = "OpenAL Soft"; 1.243 + char path_name[PATH_MAX]; 1.244 + pa_context_state_t state; 1.245 + pa_context *context; 1.246 + int err; 1.247 + 1.248 + if(pa_get_binary_name(path_name, sizeof(path_name))) 1.249 + name = pa_path_get_filename(path_name); 1.250 + 1.251 + context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); 1.252 + if(!context) 1.253 + { 1.254 + ERR("pa_context_new() failed\n"); 1.255 + return NULL; 1.256 + } 1.257 + 1.258 + pa_context_set_state_callback(context, context_state_callback, loop); 1.259 + 1.260 + if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0) 1.261 + { 1.262 + while((state=pa_context_get_state(context)) != PA_CONTEXT_READY) 1.263 + { 1.264 + if(!PA_CONTEXT_IS_GOOD(state)) 1.265 + { 1.266 + err = pa_context_errno(context); 1.267 + if(err > 0) err = -err; 1.268 + break; 1.269 + } 1.270 + 1.271 + pa_threaded_mainloop_wait(loop); 1.272 + } 1.273 + } 1.274 + pa_context_set_state_callback(context, NULL, NULL); 1.275 + 1.276 + if(err < 0) 1.277 + { 1.278 + if(!silent) 1.279 + ERR("Context did not connect: %s\n", pa_strerror(err)); 1.280 + pa_context_unref(context); 1.281 + return NULL; 1.282 + } 1.283 + 1.284 + return context; 1.285 +} 1.286 + 1.287 + 1.288 +static ALCboolean pulse_load(void) //{{{ 1.289 +{ 1.290 + ALCboolean ret = ALC_FALSE; 1.291 + if(!pa_handle) 1.292 + { 1.293 + pa_threaded_mainloop *loop; 1.294 + 1.295 +#ifdef HAVE_DYNLOAD 1.296 + 1.297 +#ifdef _WIN32 1.298 +#define PALIB "libpulse-0.dll" 1.299 +#elif defined(__APPLE__) && defined(__MACH__) 1.300 +#define PALIB "libpulse.0.dylib" 1.301 +#else 1.302 +#define PALIB "libpulse.so.0" 1.303 +#endif 1.304 + pa_handle = LoadLib(PALIB); 1.305 + if(!pa_handle) 1.306 + return ALC_FALSE; 1.307 + 1.308 +#define LOAD_FUNC(x) do { \ 1.309 + p##x = GetSymbol(pa_handle, #x); \ 1.310 + if(!(p##x)) { \ 1.311 + CloseLib(pa_handle); \ 1.312 + pa_handle = NULL; \ 1.313 + return ALC_FALSE; \ 1.314 + } \ 1.315 +} while(0) 1.316 + LOAD_FUNC(pa_context_unref); 1.317 + LOAD_FUNC(pa_sample_spec_valid); 1.318 + LOAD_FUNC(pa_stream_drop); 1.319 + LOAD_FUNC(pa_strerror); 1.320 + LOAD_FUNC(pa_context_get_state); 1.321 + LOAD_FUNC(pa_stream_get_state); 1.322 + LOAD_FUNC(pa_threaded_mainloop_signal); 1.323 + LOAD_FUNC(pa_stream_peek); 1.324 + LOAD_FUNC(pa_threaded_mainloop_wait); 1.325 + LOAD_FUNC(pa_threaded_mainloop_unlock); 1.326 + LOAD_FUNC(pa_threaded_mainloop_in_thread); 1.327 + LOAD_FUNC(pa_context_new); 1.328 + LOAD_FUNC(pa_threaded_mainloop_stop); 1.329 + LOAD_FUNC(pa_context_disconnect); 1.330 + LOAD_FUNC(pa_threaded_mainloop_start); 1.331 + LOAD_FUNC(pa_threaded_mainloop_get_api); 1.332 + LOAD_FUNC(pa_context_set_state_callback); 1.333 + LOAD_FUNC(pa_stream_write); 1.334 + LOAD_FUNC(pa_xfree); 1.335 + LOAD_FUNC(pa_stream_connect_record); 1.336 + LOAD_FUNC(pa_stream_connect_playback); 1.337 + LOAD_FUNC(pa_stream_readable_size); 1.338 + LOAD_FUNC(pa_stream_writable_size); 1.339 + LOAD_FUNC(pa_stream_cork); 1.340 + LOAD_FUNC(pa_stream_is_suspended); 1.341 + LOAD_FUNC(pa_stream_get_device_name); 1.342 + LOAD_FUNC(pa_path_get_filename); 1.343 + LOAD_FUNC(pa_get_binary_name); 1.344 + LOAD_FUNC(pa_threaded_mainloop_free); 1.345 + LOAD_FUNC(pa_context_errno); 1.346 + LOAD_FUNC(pa_xmalloc); 1.347 + LOAD_FUNC(pa_stream_unref); 1.348 + LOAD_FUNC(pa_threaded_mainloop_accept); 1.349 + LOAD_FUNC(pa_stream_set_write_callback); 1.350 + LOAD_FUNC(pa_threaded_mainloop_new); 1.351 + LOAD_FUNC(pa_context_connect); 1.352 + LOAD_FUNC(pa_stream_set_buffer_attr); 1.353 + LOAD_FUNC(pa_stream_get_buffer_attr); 1.354 + LOAD_FUNC(pa_stream_get_sample_spec); 1.355 + LOAD_FUNC(pa_stream_get_time); 1.356 + LOAD_FUNC(pa_stream_set_read_callback); 1.357 + LOAD_FUNC(pa_stream_set_state_callback); 1.358 + LOAD_FUNC(pa_stream_set_moved_callback); 1.359 + LOAD_FUNC(pa_stream_set_underflow_callback); 1.360 + LOAD_FUNC(pa_stream_new); 1.361 + LOAD_FUNC(pa_stream_disconnect); 1.362 + LOAD_FUNC(pa_threaded_mainloop_lock); 1.363 + LOAD_FUNC(pa_channel_map_init_auto); 1.364 + LOAD_FUNC(pa_channel_map_parse); 1.365 + LOAD_FUNC(pa_channel_map_snprint); 1.366 + LOAD_FUNC(pa_channel_map_equal); 1.367 + LOAD_FUNC(pa_context_get_server_info); 1.368 + LOAD_FUNC(pa_context_get_sink_info_by_name); 1.369 + LOAD_FUNC(pa_context_get_sink_info_list); 1.370 + LOAD_FUNC(pa_context_get_source_info_list); 1.371 + LOAD_FUNC(pa_operation_get_state); 1.372 + LOAD_FUNC(pa_operation_unref); 1.373 +#undef LOAD_FUNC 1.374 +#define LOAD_OPTIONAL_FUNC(x) do { \ 1.375 + p##x = GetSymbol(pa_handle, #x); \ 1.376 +} while(0) 1.377 +#if PA_CHECK_VERSION(0,9,15) 1.378 + LOAD_OPTIONAL_FUNC(pa_channel_map_superset); 1.379 + LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback); 1.380 +#endif 1.381 +#if PA_CHECK_VERSION(0,9,16) 1.382 + LOAD_OPTIONAL_FUNC(pa_stream_begin_write); 1.383 +#endif 1.384 +#undef LOAD_OPTIONAL_FUNC 1.385 + 1.386 +#else /* HAVE_DYNLOAD */ 1.387 + pa_handle = (void*)0xDEADBEEF; 1.388 +#endif 1.389 + 1.390 + if((loop=pa_threaded_mainloop_new()) && 1.391 + pa_threaded_mainloop_start(loop) >= 0) 1.392 + { 1.393 + pa_context *context; 1.394 + 1.395 + pa_threaded_mainloop_lock(loop); 1.396 + context = connect_context(loop, AL_TRUE); 1.397 + if(context) 1.398 + { 1.399 + ret = ALC_TRUE; 1.400 + 1.401 + pa_context_disconnect(context); 1.402 + pa_context_unref(context); 1.403 + } 1.404 + pa_threaded_mainloop_unlock(loop); 1.405 + pa_threaded_mainloop_stop(loop); 1.406 + } 1.407 + if(loop) 1.408 + pa_threaded_mainloop_free(loop); 1.409 + 1.410 + if(!ret) 1.411 + { 1.412 +#ifdef HAVE_DYNLOAD 1.413 + CloseLib(pa_handle); 1.414 +#endif 1.415 + pa_handle = NULL; 1.416 + } 1.417 + } 1.418 + return ret; 1.419 +} //}}} 1.420 + 1.421 +// PulseAudio Event Callbacks //{{{ 1.422 +static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ 1.423 +{ 1.424 + pa_threaded_mainloop *loop = pdata; 1.425 + pa_stream_state_t state; 1.426 + 1.427 + state = pa_stream_get_state(stream); 1.428 + if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state)) 1.429 + pa_threaded_mainloop_signal(loop, 0); 1.430 +}//}}} 1.431 + 1.432 +static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{ 1.433 +{ 1.434 + ALCdevice *Device = pdata; 1.435 + pulse_data *data = Device->ExtraData; 1.436 + (void)stream; 1.437 + 1.438 + pa_threaded_mainloop_signal(data->loop, 0); 1.439 +}//}}} 1.440 + 1.441 +static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{ 1.442 +{ 1.443 + ALCdevice *Device = pdata; 1.444 + pulse_data *data = Device->ExtraData; 1.445 + 1.446 + LockDevice(Device); 1.447 + 1.448 + data->attr = *(pa_stream_get_buffer_attr(stream)); 1.449 + Device->UpdateSize = data->attr.minreq / data->frame_size; 1.450 + Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize; 1.451 + if(Device->NumUpdates <= 1) 1.452 + { 1.453 + Device->NumUpdates = 1; 1.454 + ERR("PulseAudio returned minreq > tlength/2; expect break up\n"); 1.455 + } 1.456 + 1.457 + UnlockDevice(Device); 1.458 +}//}}} 1.459 + 1.460 +static void stream_device_callback(pa_stream *stream, void *pdata) //{{{ 1.461 +{ 1.462 + ALCdevice *Device = pdata; 1.463 + pulse_data *data = Device->ExtraData; 1.464 + 1.465 + free(data->device_name); 1.466 + data->device_name = strdup(pa_stream_get_device_name(stream)); 1.467 +}//}}} 1.468 + 1.469 +static void context_state_callback2(pa_context *context, void *pdata) //{{{ 1.470 +{ 1.471 + ALCdevice *Device = pdata; 1.472 + pulse_data *data = Device->ExtraData; 1.473 + 1.474 + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) 1.475 + { 1.476 + ERR("Received context failure!\n"); 1.477 + aluHandleDisconnect(Device); 1.478 + } 1.479 + pa_threaded_mainloop_signal(data->loop, 0); 1.480 +}//}}} 1.481 + 1.482 +static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{ 1.483 +{ 1.484 + ALCdevice *Device = pdata; 1.485 + pulse_data *data = Device->ExtraData; 1.486 + 1.487 + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) 1.488 + { 1.489 + ERR("Received stream failure!\n"); 1.490 + aluHandleDisconnect(Device); 1.491 + } 1.492 + pa_threaded_mainloop_signal(data->loop, 0); 1.493 +}//}}} 1.494 + 1.495 +static void stream_success_callback(pa_stream *stream, int success, void *pdata) //{{{ 1.496 +{ 1.497 + ALCdevice *Device = pdata; 1.498 + pulse_data *data = Device->ExtraData; 1.499 + (void)stream; 1.500 + (void)success; 1.501 + 1.502 + pa_threaded_mainloop_signal(data->loop, 0); 1.503 +}//}}} 1.504 + 1.505 +static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ 1.506 +{ 1.507 + ALCdevice *device = pdata; 1.508 + pulse_data *data = device->ExtraData; 1.509 + char chanmap_str[256] = ""; 1.510 + const struct { 1.511 + const char *str; 1.512 + enum DevFmtChannels chans; 1.513 + } chanmaps[] = { 1.514 + { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right", 1.515 + DevFmtX71 }, 1.516 + { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right", 1.517 + DevFmtX61 }, 1.518 + { "front-left,front-right,front-center,lfe,rear-left,rear-right", 1.519 + DevFmtX51 }, 1.520 + { "front-left,front-right,front-center,lfe,side-left,side-right", 1.521 + DevFmtX51Side }, 1.522 + { "front-left,front-right,rear-left,rear-right", DevFmtQuad }, 1.523 + { "front-left,front-right", DevFmtStereo }, 1.524 + { "mono", DevFmtMono }, 1.525 + { NULL, 0 } 1.526 + }; 1.527 + int i; 1.528 + (void)context; 1.529 + 1.530 + if(eol) 1.531 + { 1.532 + pa_threaded_mainloop_signal(data->loop, 0); 1.533 + return; 1.534 + } 1.535 + 1.536 + for(i = 0;chanmaps[i].str;i++) 1.537 + { 1.538 + pa_channel_map map; 1.539 + if(!pa_channel_map_parse(&map, chanmaps[i].str)) 1.540 + continue; 1.541 + 1.542 + if(pa_channel_map_equal(&info->channel_map, &map) 1.543 +#if PA_CHECK_VERSION(0,9,15) 1.544 + || (pa_channel_map_superset && 1.545 + pa_channel_map_superset(&info->channel_map, &map)) 1.546 +#endif 1.547 + ) 1.548 + { 1.549 + device->FmtChans = chanmaps[i].chans; 1.550 + return; 1.551 + } 1.552 + } 1.553 + 1.554 + pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); 1.555 + ERR("Failed to find format for channel map:\n %s\n", chanmap_str); 1.556 +}//}}} 1.557 + 1.558 +static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ 1.559 +{ 1.560 + pa_threaded_mainloop *loop = pdata; 1.561 + char str[1024]; 1.562 + void *temp; 1.563 + int count; 1.564 + ALuint i; 1.565 + 1.566 + (void)context; 1.567 + 1.568 + if(eol) 1.569 + { 1.570 + pa_threaded_mainloop_signal(loop, 0); 1.571 + return; 1.572 + } 1.573 + 1.574 + count = 0; 1.575 + do { 1.576 + if(count == 0) 1.577 + snprintf(str, sizeof(str), "%s", info->description); 1.578 + else 1.579 + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); 1.580 + count++; 1.581 + 1.582 + for(i = 0;i < numDevNames;i++) 1.583 + { 1.584 + if(strcmp(str, allDevNameMap[i].name) == 0) 1.585 + break; 1.586 + } 1.587 + } while(i != numDevNames); 1.588 + 1.589 + temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); 1.590 + if(temp) 1.591 + { 1.592 + allDevNameMap = temp; 1.593 + allDevNameMap[numDevNames].name = strdup(str); 1.594 + allDevNameMap[numDevNames].device_name = strdup(info->name); 1.595 + numDevNames++; 1.596 + } 1.597 +}//}}} 1.598 + 1.599 +static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{ 1.600 +{ 1.601 + pa_threaded_mainloop *loop = pdata; 1.602 + char str[1024]; 1.603 + void *temp; 1.604 + int count; 1.605 + ALuint i; 1.606 + 1.607 + (void)context; 1.608 + 1.609 + if(eol) 1.610 + { 1.611 + pa_threaded_mainloop_signal(loop, 0); 1.612 + return; 1.613 + } 1.614 + 1.615 + count = 0; 1.616 + do { 1.617 + if(count == 0) 1.618 + snprintf(str, sizeof(str), "%s", info->description); 1.619 + else 1.620 + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); 1.621 + count++; 1.622 + 1.623 + for(i = 0;i < numCaptureDevNames;i++) 1.624 + { 1.625 + if(strcmp(str, allCaptureDevNameMap[i].name) == 0) 1.626 + break; 1.627 + } 1.628 + } while(i != numCaptureDevNames); 1.629 + 1.630 + temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); 1.631 + if(temp) 1.632 + { 1.633 + allCaptureDevNameMap = temp; 1.634 + allCaptureDevNameMap[numCaptureDevNames].name = strdup(str); 1.635 + allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); 1.636 + numCaptureDevNames++; 1.637 + } 1.638 +}//}}} 1.639 +//}}} 1.640 + 1.641 +// PulseAudio I/O Callbacks //{{{ 1.642 +static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ 1.643 +{ 1.644 + ALCdevice *Device = pdata; 1.645 + pulse_data *data = Device->ExtraData; 1.646 + (void)stream; 1.647 + (void)len; 1.648 + 1.649 + pa_threaded_mainloop_signal(data->loop, 0); 1.650 +} //}}} 1.651 +//}}} 1.652 + 1.653 +static ALuint PulseProc(ALvoid *param) 1.654 +{ 1.655 + ALCdevice *Device = param; 1.656 + pulse_data *data = Device->ExtraData; 1.657 + ssize_t len; 1.658 + 1.659 + SetRTPriority(); 1.660 + 1.661 + pa_threaded_mainloop_lock(data->loop); 1.662 + do { 1.663 + len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); 1.664 + len -= len%(Device->UpdateSize*data->frame_size); 1.665 + if(len == 0) 1.666 + { 1.667 + pa_threaded_mainloop_wait(data->loop); 1.668 + continue; 1.669 + } 1.670 + 1.671 + while(len > 0) 1.672 + { 1.673 + size_t newlen = len; 1.674 + void *buf; 1.675 + pa_free_cb_t free_func = NULL; 1.676 + 1.677 +#if PA_CHECK_VERSION(0,9,16) 1.678 + if(!pa_stream_begin_write || 1.679 + pa_stream_begin_write(data->stream, &buf, &newlen) < 0) 1.680 +#endif 1.681 + { 1.682 + buf = pa_xmalloc(newlen); 1.683 + free_func = pa_xfree; 1.684 + } 1.685 + pa_threaded_mainloop_unlock(data->loop); 1.686 + 1.687 + aluMixData(Device, buf, newlen/data->frame_size); 1.688 + 1.689 + pa_threaded_mainloop_lock(data->loop); 1.690 + pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); 1.691 + len -= newlen; 1.692 + } 1.693 + } while(Device->Connected && !data->killNow); 1.694 + pa_threaded_mainloop_unlock(data->loop); 1.695 + 1.696 + return 0; 1.697 +} 1.698 + 1.699 +static pa_stream *connect_playback_stream(ALCdevice *device, 1.700 + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, 1.701 + pa_channel_map *chanmap) 1.702 +{ 1.703 + pulse_data *data = device->ExtraData; 1.704 + pa_stream_state_t state; 1.705 + pa_stream *stream; 1.706 + 1.707 + stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap); 1.708 + if(!stream) 1.709 + { 1.710 + ERR("pa_stream_new() failed: %s\n", 1.711 + pa_strerror(pa_context_errno(data->context))); 1.712 + return NULL; 1.713 + } 1.714 + 1.715 + pa_stream_set_state_callback(stream, stream_state_callback, data->loop); 1.716 + 1.717 + if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0) 1.718 + { 1.719 + ERR("Stream did not connect: %s\n", 1.720 + pa_strerror(pa_context_errno(data->context))); 1.721 + pa_stream_unref(stream); 1.722 + return NULL; 1.723 + } 1.724 + 1.725 + while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) 1.726 + { 1.727 + if(!PA_STREAM_IS_GOOD(state)) 1.728 + { 1.729 + ERR("Stream did not get ready: %s\n", 1.730 + pa_strerror(pa_context_errno(data->context))); 1.731 + pa_stream_unref(stream); 1.732 + return NULL; 1.733 + } 1.734 + 1.735 + pa_threaded_mainloop_wait(data->loop); 1.736 + } 1.737 + pa_stream_set_state_callback(stream, NULL, NULL); 1.738 + 1.739 + return stream; 1.740 +} 1.741 + 1.742 +static void probe_devices(ALboolean capture) 1.743 +{ 1.744 + pa_threaded_mainloop *loop; 1.745 + 1.746 + if(capture == AL_FALSE) 1.747 + allDevNameMap = malloc(sizeof(DevMap) * 1); 1.748 + else 1.749 + allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); 1.750 + 1.751 + if((loop=pa_threaded_mainloop_new()) && 1.752 + pa_threaded_mainloop_start(loop) >= 0) 1.753 + { 1.754 + pa_context *context; 1.755 + 1.756 + pa_threaded_mainloop_lock(loop); 1.757 + context = connect_context(loop, AL_FALSE); 1.758 + if(context) 1.759 + { 1.760 + pa_operation *o; 1.761 + 1.762 + if(capture == AL_FALSE) 1.763 + o = pa_context_get_sink_info_list(context, sink_device_callback, loop); 1.764 + else 1.765 + o = pa_context_get_source_info_list(context, source_device_callback, loop); 1.766 + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) 1.767 + pa_threaded_mainloop_wait(loop); 1.768 + pa_operation_unref(o); 1.769 + 1.770 + pa_context_disconnect(context); 1.771 + pa_context_unref(context); 1.772 + } 1.773 + pa_threaded_mainloop_unlock(loop); 1.774 + pa_threaded_mainloop_stop(loop); 1.775 + } 1.776 + if(loop) 1.777 + pa_threaded_mainloop_free(loop); 1.778 +} 1.779 + 1.780 + 1.781 +static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{ 1.782 +{ 1.783 + pulse_data *data = pa_xmalloc(sizeof(pulse_data)); 1.784 + memset(data, 0, sizeof(*data)); 1.785 + 1.786 + if(!(data->loop = pa_threaded_mainloop_new())) 1.787 + { 1.788 + ERR("pa_threaded_mainloop_new() failed!\n"); 1.789 + goto out; 1.790 + } 1.791 + if(pa_threaded_mainloop_start(data->loop) < 0) 1.792 + { 1.793 + ERR("pa_threaded_mainloop_start() failed\n"); 1.794 + goto out; 1.795 + } 1.796 + 1.797 + pa_threaded_mainloop_lock(data->loop); 1.798 + device->ExtraData = data; 1.799 + 1.800 + data->context = connect_context(data->loop, AL_FALSE); 1.801 + if(!data->context) 1.802 + { 1.803 + pa_threaded_mainloop_unlock(data->loop); 1.804 + goto out; 1.805 + } 1.806 + pa_context_set_state_callback(data->context, context_state_callback2, device); 1.807 + 1.808 + device->szDeviceName = strdup(device_name); 1.809 + 1.810 + pa_threaded_mainloop_unlock(data->loop); 1.811 + return ALC_TRUE; 1.812 + 1.813 +out: 1.814 + if(data->loop) 1.815 + { 1.816 + pa_threaded_mainloop_stop(data->loop); 1.817 + pa_threaded_mainloop_free(data->loop); 1.818 + } 1.819 + 1.820 + device->ExtraData = NULL; 1.821 + pa_xfree(data); 1.822 + return ALC_FALSE; 1.823 +} //}}} 1.824 + 1.825 +static void pulse_close(ALCdevice *device) //{{{ 1.826 +{ 1.827 + pulse_data *data = device->ExtraData; 1.828 + 1.829 + pa_threaded_mainloop_lock(data->loop); 1.830 + 1.831 + if(data->stream) 1.832 + { 1.833 + pa_stream_disconnect(data->stream); 1.834 + pa_stream_unref(data->stream); 1.835 + } 1.836 + 1.837 + pa_context_disconnect(data->context); 1.838 + pa_context_unref(data->context); 1.839 + 1.840 + pa_threaded_mainloop_unlock(data->loop); 1.841 + 1.842 + pa_threaded_mainloop_stop(data->loop); 1.843 + pa_threaded_mainloop_free(data->loop); 1.844 + 1.845 + DestroyRingBuffer(data->ring); 1.846 + free(data->device_name); 1.847 + 1.848 + device->ExtraData = NULL; 1.849 + pa_xfree(data); 1.850 +} //}}} 1.851 +//}}} 1.852 + 1.853 +// OpenAL {{{ 1.854 +static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{ 1.855 +{ 1.856 + char *pulse_name = NULL; 1.857 + pa_sample_spec spec; 1.858 + pulse_data *data; 1.859 + 1.860 + if(!allDevNameMap) 1.861 + probe_devices(AL_FALSE); 1.862 + 1.863 + if(!device_name) 1.864 + device_name = pulse_device; 1.865 + else if(strcmp(device_name, pulse_device) != 0) 1.866 + { 1.867 + ALuint i; 1.868 + 1.869 + for(i = 0;i < numDevNames;i++) 1.870 + { 1.871 + if(strcmp(device_name, allDevNameMap[i].name) == 0) 1.872 + { 1.873 + pulse_name = allDevNameMap[i].device_name; 1.874 + break; 1.875 + } 1.876 + } 1.877 + if(i == numDevNames) 1.878 + return ALC_FALSE; 1.879 + } 1.880 + 1.881 + if(pulse_open(device, device_name) == ALC_FALSE) 1.882 + return ALC_FALSE; 1.883 + 1.884 + data = device->ExtraData; 1.885 + 1.886 + pa_threaded_mainloop_lock(data->loop); 1.887 + 1.888 + spec.format = PA_SAMPLE_S16NE; 1.889 + spec.rate = 44100; 1.890 + spec.channels = 2; 1.891 + 1.892 + data->device_name = pulse_name; 1.893 + pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL); 1.894 + if(!stream) 1.895 + { 1.896 + pa_threaded_mainloop_unlock(data->loop); 1.897 + goto fail; 1.898 + } 1.899 + 1.900 + if(pa_stream_is_suspended(stream)) 1.901 + { 1.902 + ERR("Device is suspended\n"); 1.903 + pa_stream_disconnect(stream); 1.904 + pa_stream_unref(stream); 1.905 + pa_threaded_mainloop_unlock(data->loop); 1.906 + goto fail; 1.907 + } 1.908 + data->device_name = strdup(pa_stream_get_device_name(stream)); 1.909 + 1.910 + pa_stream_disconnect(stream); 1.911 + pa_stream_unref(stream); 1.912 + 1.913 + pa_threaded_mainloop_unlock(data->loop); 1.914 + 1.915 + return ALC_TRUE; 1.916 + 1.917 +fail: 1.918 + pulse_close(device); 1.919 + return ALC_FALSE; 1.920 +} //}}} 1.921 + 1.922 +static void pulse_close_playback(ALCdevice *device) //{{{ 1.923 +{ 1.924 + pulse_close(device); 1.925 +} //}}} 1.926 + 1.927 +static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ 1.928 +{ 1.929 + pulse_data *data = device->ExtraData; 1.930 + pa_stream_flags_t flags = 0; 1.931 + pa_channel_map chanmap; 1.932 + 1.933 + pa_threaded_mainloop_lock(data->loop); 1.934 + 1.935 + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) 1.936 + { 1.937 + pa_operation *o; 1.938 + o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); 1.939 + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) 1.940 + pa_threaded_mainloop_wait(data->loop); 1.941 + pa_operation_unref(o); 1.942 + } 1.943 + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) 1.944 + flags |= PA_STREAM_FIX_RATE; 1.945 + 1.946 + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); 1.947 + data->attr.prebuf = -1; 1.948 + data->attr.fragsize = -1; 1.949 + data->attr.minreq = device->UpdateSize * data->frame_size; 1.950 + data->attr.tlength = data->attr.minreq * device->NumUpdates; 1.951 + if(data->attr.tlength < data->attr.minreq*2) 1.952 + data->attr.tlength = data->attr.minreq*2; 1.953 + data->attr.maxlength = data->attr.tlength; 1.954 + flags |= PA_STREAM_EARLY_REQUESTS; 1.955 + flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; 1.956 + 1.957 + switch(device->FmtType) 1.958 + { 1.959 + case DevFmtByte: 1.960 + device->FmtType = DevFmtUByte; 1.961 + /* fall-through */ 1.962 + case DevFmtUByte: 1.963 + data->spec.format = PA_SAMPLE_U8; 1.964 + break; 1.965 + case DevFmtUShort: 1.966 + device->FmtType = DevFmtShort; 1.967 + /* fall-through */ 1.968 + case DevFmtShort: 1.969 + data->spec.format = PA_SAMPLE_S16NE; 1.970 + break; 1.971 + case DevFmtFloat: 1.972 + data->spec.format = PA_SAMPLE_FLOAT32NE; 1.973 + break; 1.974 + } 1.975 + data->spec.rate = device->Frequency; 1.976 + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); 1.977 + 1.978 + if(pa_sample_spec_valid(&data->spec) == 0) 1.979 + { 1.980 + ERR("Invalid sample format\n"); 1.981 + pa_threaded_mainloop_unlock(data->loop); 1.982 + return ALC_FALSE; 1.983 + } 1.984 + 1.985 + if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) 1.986 + { 1.987 + ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); 1.988 + pa_threaded_mainloop_unlock(data->loop); 1.989 + return ALC_FALSE; 1.990 + } 1.991 + SetDefaultWFXChannelOrder(device); 1.992 + 1.993 + data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); 1.994 + if(!data->stream) 1.995 + { 1.996 + pa_threaded_mainloop_unlock(data->loop); 1.997 + return ALC_FALSE; 1.998 + } 1.999 + 1.1000 + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); 1.1001 + 1.1002 + data->spec = *(pa_stream_get_sample_spec(data->stream)); 1.1003 + if(device->Frequency != data->spec.rate) 1.1004 + { 1.1005 + pa_operation *o; 1.1006 + 1.1007 + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) 1.1008 + ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); 1.1009 + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; 1.1010 + 1.1011 + /* Server updated our playback rate, so modify the buffer attribs 1.1012 + * accordingly. */ 1.1013 + data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * 1.1014 + data->spec.rate / device->Frequency * data->frame_size; 1.1015 + data->attr.tlength = data->attr.minreq * device->NumUpdates; 1.1016 + data->attr.maxlength = data->attr.tlength; 1.1017 + 1.1018 + o = pa_stream_set_buffer_attr(data->stream, &data->attr, 1.1019 + stream_success_callback, device); 1.1020 + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) 1.1021 + pa_threaded_mainloop_wait(data->loop); 1.1022 + pa_operation_unref(o); 1.1023 + 1.1024 + device->Frequency = data->spec.rate; 1.1025 + } 1.1026 + 1.1027 + stream_buffer_attr_callback(data->stream, device); 1.1028 +#if PA_CHECK_VERSION(0,9,15) 1.1029 + if(pa_stream_set_buffer_attr_callback) 1.1030 + pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); 1.1031 +#endif 1.1032 + pa_stream_set_moved_callback(data->stream, stream_device_callback, device); 1.1033 + pa_stream_set_write_callback(data->stream, stream_write_callback, device); 1.1034 + pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); 1.1035 + 1.1036 + data->thread = StartThread(PulseProc, device); 1.1037 + if(!data->thread) 1.1038 + { 1.1039 +#if PA_CHECK_VERSION(0,9,15) 1.1040 + if(pa_stream_set_buffer_attr_callback) 1.1041 + pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); 1.1042 +#endif 1.1043 + pa_stream_set_moved_callback(data->stream, NULL, NULL); 1.1044 + pa_stream_set_write_callback(data->stream, NULL, NULL); 1.1045 + pa_stream_set_underflow_callback(data->stream, NULL, NULL); 1.1046 + pa_stream_disconnect(data->stream); 1.1047 + pa_stream_unref(data->stream); 1.1048 + data->stream = NULL; 1.1049 + 1.1050 + pa_threaded_mainloop_unlock(data->loop); 1.1051 + return ALC_FALSE; 1.1052 + } 1.1053 + 1.1054 + pa_threaded_mainloop_unlock(data->loop); 1.1055 + return ALC_TRUE; 1.1056 +} //}}} 1.1057 + 1.1058 +static void pulse_stop_playback(ALCdevice *device) //{{{ 1.1059 +{ 1.1060 + pulse_data *data = device->ExtraData; 1.1061 + 1.1062 + if(!data->stream) 1.1063 + return; 1.1064 + 1.1065 + data->killNow = AL_TRUE; 1.1066 + if(data->thread) 1.1067 + { 1.1068 + pa_threaded_mainloop_signal(data->loop, 0); 1.1069 + StopThread(data->thread); 1.1070 + data->thread = NULL; 1.1071 + } 1.1072 + data->killNow = AL_FALSE; 1.1073 + 1.1074 + pa_threaded_mainloop_lock(data->loop); 1.1075 + 1.1076 +#if PA_CHECK_VERSION(0,9,15) 1.1077 + if(pa_stream_set_buffer_attr_callback) 1.1078 + pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); 1.1079 +#endif 1.1080 + pa_stream_set_moved_callback(data->stream, NULL, NULL); 1.1081 + pa_stream_set_write_callback(data->stream, NULL, NULL); 1.1082 + pa_stream_set_underflow_callback(data->stream, NULL, NULL); 1.1083 + pa_stream_disconnect(data->stream); 1.1084 + pa_stream_unref(data->stream); 1.1085 + data->stream = NULL; 1.1086 + 1.1087 + pa_threaded_mainloop_unlock(data->loop); 1.1088 +} //}}} 1.1089 + 1.1090 + 1.1091 +static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ 1.1092 +{ 1.1093 + char *pulse_name = NULL; 1.1094 + pulse_data *data; 1.1095 + pa_stream_flags_t flags = 0; 1.1096 + pa_stream_state_t state; 1.1097 + pa_channel_map chanmap; 1.1098 + 1.1099 + if(!allCaptureDevNameMap) 1.1100 + probe_devices(AL_TRUE); 1.1101 + 1.1102 + if(!device_name) 1.1103 + device_name = pulse_device; 1.1104 + else if(strcmp(device_name, pulse_device) != 0) 1.1105 + { 1.1106 + ALuint i; 1.1107 + 1.1108 + for(i = 0;i < numCaptureDevNames;i++) 1.1109 + { 1.1110 + if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) 1.1111 + { 1.1112 + pulse_name = allCaptureDevNameMap[i].device_name; 1.1113 + break; 1.1114 + } 1.1115 + } 1.1116 + if(i == numCaptureDevNames) 1.1117 + return ALC_FALSE; 1.1118 + } 1.1119 + 1.1120 + if(pulse_open(device, device_name) == ALC_FALSE) 1.1121 + return ALC_FALSE; 1.1122 + 1.1123 + data = device->ExtraData; 1.1124 + pa_threaded_mainloop_lock(data->loop); 1.1125 + 1.1126 + data->samples = device->UpdateSize * device->NumUpdates; 1.1127 + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); 1.1128 + if(data->samples < 100 * device->Frequency / 1000) 1.1129 + data->samples = 100 * device->Frequency / 1000; 1.1130 + 1.1131 + if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) 1.1132 + { 1.1133 + pa_threaded_mainloop_unlock(data->loop); 1.1134 + goto fail; 1.1135 + } 1.1136 + 1.1137 + data->attr.minreq = -1; 1.1138 + data->attr.prebuf = -1; 1.1139 + data->attr.maxlength = data->samples * data->frame_size; 1.1140 + data->attr.tlength = -1; 1.1141 + data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * 1.1142 + data->frame_size; 1.1143 + 1.1144 + data->spec.rate = device->Frequency; 1.1145 + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); 1.1146 + 1.1147 + switch(device->FmtType) 1.1148 + { 1.1149 + case DevFmtUByte: 1.1150 + data->spec.format = PA_SAMPLE_U8; 1.1151 + break; 1.1152 + case DevFmtShort: 1.1153 + data->spec.format = PA_SAMPLE_S16NE; 1.1154 + break; 1.1155 + case DevFmtFloat: 1.1156 + data->spec.format = PA_SAMPLE_FLOAT32NE; 1.1157 + break; 1.1158 + case DevFmtByte: 1.1159 + case DevFmtUShort: 1.1160 + ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); 1.1161 + pa_threaded_mainloop_unlock(data->loop); 1.1162 + goto fail; 1.1163 + } 1.1164 + 1.1165 + if(pa_sample_spec_valid(&data->spec) == 0) 1.1166 + { 1.1167 + ERR("Invalid sample format\n"); 1.1168 + pa_threaded_mainloop_unlock(data->loop); 1.1169 + goto fail; 1.1170 + } 1.1171 + 1.1172 + if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) 1.1173 + { 1.1174 + ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); 1.1175 + pa_threaded_mainloop_unlock(data->loop); 1.1176 + goto fail; 1.1177 + } 1.1178 + 1.1179 + data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); 1.1180 + if(!data->stream) 1.1181 + { 1.1182 + ERR("pa_stream_new() failed: %s\n", 1.1183 + pa_strerror(pa_context_errno(data->context))); 1.1184 + 1.1185 + pa_threaded_mainloop_unlock(data->loop); 1.1186 + goto fail; 1.1187 + } 1.1188 + 1.1189 + pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); 1.1190 + 1.1191 + flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; 1.1192 + if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) 1.1193 + { 1.1194 + ERR("Stream did not connect: %s\n", 1.1195 + pa_strerror(pa_context_errno(data->context))); 1.1196 + 1.1197 + pa_stream_unref(data->stream); 1.1198 + data->stream = NULL; 1.1199 + 1.1200 + pa_threaded_mainloop_unlock(data->loop); 1.1201 + goto fail; 1.1202 + } 1.1203 + 1.1204 + while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) 1.1205 + { 1.1206 + if(!PA_STREAM_IS_GOOD(state)) 1.1207 + { 1.1208 + ERR("Stream did not get ready: %s\n", 1.1209 + pa_strerror(pa_context_errno(data->context))); 1.1210 + 1.1211 + pa_stream_unref(data->stream); 1.1212 + data->stream = NULL; 1.1213 + 1.1214 + pa_threaded_mainloop_unlock(data->loop); 1.1215 + goto fail; 1.1216 + } 1.1217 + 1.1218 + pa_threaded_mainloop_wait(data->loop); 1.1219 + } 1.1220 + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); 1.1221 + 1.1222 + pa_threaded_mainloop_unlock(data->loop); 1.1223 + return ALC_TRUE; 1.1224 + 1.1225 +fail: 1.1226 + pulse_close(device); 1.1227 + return ALC_FALSE; 1.1228 +} //}}} 1.1229 + 1.1230 +static void pulse_close_capture(ALCdevice *device) //{{{ 1.1231 +{ 1.1232 + pulse_close(device); 1.1233 +} //}}} 1.1234 + 1.1235 +static void pulse_start_capture(ALCdevice *device) //{{{ 1.1236 +{ 1.1237 + pulse_data *data = device->ExtraData; 1.1238 + pa_operation *o; 1.1239 + 1.1240 + pa_threaded_mainloop_lock(data->loop); 1.1241 + o = pa_stream_cork(data->stream, 0, stream_success_callback, device); 1.1242 + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) 1.1243 + pa_threaded_mainloop_wait(data->loop); 1.1244 + pa_operation_unref(o); 1.1245 + pa_threaded_mainloop_unlock(data->loop); 1.1246 +} //}}} 1.1247 + 1.1248 +static void pulse_stop_capture(ALCdevice *device) //{{{ 1.1249 +{ 1.1250 + pulse_data *data = device->ExtraData; 1.1251 + pa_operation *o; 1.1252 + 1.1253 + pa_threaded_mainloop_lock(data->loop); 1.1254 + o = pa_stream_cork(data->stream, 1, stream_success_callback, device); 1.1255 + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) 1.1256 + pa_threaded_mainloop_wait(data->loop); 1.1257 + pa_operation_unref(o); 1.1258 + pa_threaded_mainloop_unlock(data->loop); 1.1259 +} //}}} 1.1260 + 1.1261 +static ALCuint pulse_available_samples(ALCdevice *device) //{{{ 1.1262 +{ 1.1263 + pulse_data *data = device->ExtraData; 1.1264 + size_t samples; 1.1265 + 1.1266 + pa_threaded_mainloop_lock(data->loop); 1.1267 + /* Capture is done in fragment-sized chunks, so we loop until we get all 1.1268 + * that's available */ 1.1269 + samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); 1.1270 + while(samples > 0) 1.1271 + { 1.1272 + const void *buf; 1.1273 + size_t length; 1.1274 + 1.1275 + if(pa_stream_peek(data->stream, &buf, &length) < 0) 1.1276 + { 1.1277 + ERR("pa_stream_peek() failed: %s\n", 1.1278 + pa_strerror(pa_context_errno(data->context))); 1.1279 + break; 1.1280 + } 1.1281 + 1.1282 + WriteRingBuffer(data->ring, buf, length/data->frame_size); 1.1283 + samples -= length; 1.1284 + 1.1285 + pa_stream_drop(data->stream); 1.1286 + } 1.1287 + pa_threaded_mainloop_unlock(data->loop); 1.1288 + 1.1289 + return RingBufferSize(data->ring); 1.1290 +} //}}} 1.1291 + 1.1292 +static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ 1.1293 +{ 1.1294 + pulse_data *data = device->ExtraData; 1.1295 + 1.1296 + if(pulse_available_samples(device) >= samples) 1.1297 + ReadRingBuffer(data->ring, buffer, samples); 1.1298 + else 1.1299 + alcSetError(device, ALC_INVALID_VALUE); 1.1300 +} //}}} 1.1301 + 1.1302 + 1.1303 +static const BackendFuncs pulse_funcs = { //{{{ 1.1304 + pulse_open_playback, 1.1305 + pulse_close_playback, 1.1306 + pulse_reset_playback, 1.1307 + pulse_stop_playback, 1.1308 + pulse_open_capture, 1.1309 + pulse_close_capture, 1.1310 + pulse_start_capture, 1.1311 + pulse_stop_capture, 1.1312 + pulse_capture_samples, 1.1313 + pulse_available_samples 1.1314 +}; //}}} 1.1315 + 1.1316 +ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{ 1.1317 +{ 1.1318 + if(!pulse_load()) 1.1319 + return ALC_FALSE; 1.1320 + 1.1321 + *func_list = pulse_funcs; 1.1322 + 1.1323 + pulse_ctx_flags = 0; 1.1324 + if(!GetConfigValueBool("pulse", "spawn-server", 0)) 1.1325 + pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; 1.1326 + 1.1327 + return ALC_TRUE; 1.1328 +} //}}} 1.1329 + 1.1330 +void alc_pulse_deinit(void) //{{{ 1.1331 +{ 1.1332 + ALuint i; 1.1333 + 1.1334 + for(i = 0;i < numDevNames;++i) 1.1335 + { 1.1336 + free(allDevNameMap[i].name); 1.1337 + free(allDevNameMap[i].device_name); 1.1338 + } 1.1339 + free(allDevNameMap); 1.1340 + allDevNameMap = NULL; 1.1341 + numDevNames = 0; 1.1342 + 1.1343 + for(i = 0;i < numCaptureDevNames;++i) 1.1344 + { 1.1345 + free(allCaptureDevNameMap[i].name); 1.1346 + free(allCaptureDevNameMap[i].device_name); 1.1347 + } 1.1348 + free(allCaptureDevNameMap); 1.1349 + allCaptureDevNameMap = NULL; 1.1350 + numCaptureDevNames = 0; 1.1351 + 1.1352 +#ifdef HAVE_DYNLOAD 1.1353 + if(pa_handle) 1.1354 + CloseLib(pa_handle); 1.1355 + pa_handle = NULL; 1.1356 +#endif 1.1357 +} //}}} 1.1358 + 1.1359 +void alc_pulse_probe(enum DevProbe type) //{{{ 1.1360 +{ 1.1361 + pa_threaded_mainloop *loop; 1.1362 + ALuint i; 1.1363 + 1.1364 + switch(type) 1.1365 + { 1.1366 + case DEVICE_PROBE: 1.1367 + if((loop=pa_threaded_mainloop_new()) && 1.1368 + pa_threaded_mainloop_start(loop) >= 0) 1.1369 + { 1.1370 + pa_context *context; 1.1371 + 1.1372 + pa_threaded_mainloop_lock(loop); 1.1373 + context = connect_context(loop, AL_FALSE); 1.1374 + if(context) 1.1375 + { 1.1376 + AppendDeviceList(pulse_device); 1.1377 + 1.1378 + pa_context_disconnect(context); 1.1379 + pa_context_unref(context); 1.1380 + } 1.1381 + pa_threaded_mainloop_unlock(loop); 1.1382 + pa_threaded_mainloop_stop(loop); 1.1383 + } 1.1384 + if(loop) 1.1385 + pa_threaded_mainloop_free(loop); 1.1386 + break; 1.1387 + 1.1388 + case ALL_DEVICE_PROBE: 1.1389 + for(i = 0;i < numDevNames;++i) 1.1390 + { 1.1391 + free(allDevNameMap[i].name); 1.1392 + free(allDevNameMap[i].device_name); 1.1393 + } 1.1394 + free(allDevNameMap); 1.1395 + allDevNameMap = NULL; 1.1396 + numDevNames = 0; 1.1397 + 1.1398 + probe_devices(AL_FALSE); 1.1399 + 1.1400 + for(i = 0;i < numDevNames;i++) 1.1401 + AppendAllDeviceList(allDevNameMap[i].name); 1.1402 + break; 1.1403 + 1.1404 + case CAPTURE_DEVICE_PROBE: 1.1405 + for(i = 0;i < numCaptureDevNames;++i) 1.1406 + { 1.1407 + free(allCaptureDevNameMap[i].name); 1.1408 + free(allCaptureDevNameMap[i].device_name); 1.1409 + } 1.1410 + free(allCaptureDevNameMap); 1.1411 + allCaptureDevNameMap = NULL; 1.1412 + numCaptureDevNames = 0; 1.1413 + 1.1414 + probe_devices(AL_TRUE); 1.1415 + 1.1416 + for(i = 0;i < numCaptureDevNames;i++) 1.1417 + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); 1.1418 + break; 1.1419 + } 1.1420 +} //}}} 1.1421 +//}}}