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 +//}}}