src/audio/pulseaudio/SDL_pulseaudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sat, 27 May 2017 23:30:07 +0200
changeset 11038 b2883845e32c
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Removed unused errno includes.
icculus@2271
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
icculus@2271
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
icculus@2271
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
icculus@2271
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@2271
    20
*/
icculus@2271
    21
icculus@2271
    22
/*
icculus@2271
    23
  The PulseAudio target for SDL 1.3 is based on the 1.3 arts target, with
icculus@2271
    24
   the appropriate parts replaced with the 1.2 PulseAudio target code. This
icculus@2271
    25
   was the cleanest way to move it to 1.3. The 1.2 target was written by
icculus@2271
    26
   St├ęphan Kochen: stephan .a.t. kochen.nl
icculus@2271
    27
*/
icculus@8093
    28
#include "../../SDL_internal.h"
icculus@9388
    29
#include "SDL_assert.h"
icculus@2271
    30
slouken@5481
    31
#if SDL_AUDIO_DRIVER_PULSEAUDIO
icculus@2271
    32
icculus@2271
    33
/* Allow access to a raw mixing buffer */
icculus@2271
    34
icculus@2271
    35
#ifdef HAVE_SIGNAL_H
icculus@2271
    36
#include <signal.h>
icculus@2271
    37
#endif
icculus@2271
    38
#include <unistd.h>
icculus@2271
    39
#include <sys/types.h>
icculus@3672
    40
#include <pulse/pulseaudio.h>
icculus@2271
    41
icculus@2271
    42
#include "SDL_timer.h"
icculus@2271
    43
#include "SDL_audio.h"
icculus@2271
    44
#include "../SDL_audio_c.h"
icculus@2271
    45
#include "SDL_pulseaudio.h"
icculus@2271
    46
#include "SDL_loadso.h"
icculus@10147
    47
#include "../../thread/SDL_systhread.h"
icculus@2271
    48
icculus@3672
    49
#if (PA_API_VERSION < 12)
icculus@3672
    50
/** Return non-zero if the passed state is one of the connected states */
slouken@7860
    51
static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
icculus@3672
    52
    return
icculus@3672
    53
        x == PA_CONTEXT_CONNECTING ||
icculus@3672
    54
        x == PA_CONTEXT_AUTHORIZING ||
icculus@3672
    55
        x == PA_CONTEXT_SETTING_NAME ||
icculus@3672
    56
        x == PA_CONTEXT_READY;
icculus@3672
    57
}
icculus@3672
    58
/** Return non-zero if the passed state is one of the connected states */
slouken@7860
    59
static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
icculus@3672
    60
    return
icculus@3672
    61
        x == PA_STREAM_CREATING ||
icculus@3672
    62
        x == PA_STREAM_READY;
icculus@3672
    63
}
icculus@3672
    64
#endif /* pulseaudio <= 0.9.10 */
icculus@3672
    65
icculus@2271
    66
icculus@7545
    67
static const char *(*PULSEAUDIO_pa_get_library_version) (void);
icculus@3673
    68
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) (
icculus@3673
    69
    pa_channel_map *, unsigned, pa_channel_map_def_t);
icculus@3673
    70
static const char * (*PULSEAUDIO_pa_strerror) (int);
icculus@3673
    71
static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void);
icculus@3673
    72
static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *);
icculus@3673
    73
static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *);
icculus@9400
    74
static int (*PULSEAUDIO_pa_mainloop_run) (pa_mainloop *, int *);
icculus@9400
    75
static void (*PULSEAUDIO_pa_mainloop_quit) (pa_mainloop *, int);
icculus@3673
    76
static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *);
icculus@3673
    77
icculus@3673
    78
static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
icculus@3673
    79
    pa_operation *);
icculus@3673
    80
static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
icculus@3673
    81
static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
icculus@3672
    82
icculus@3673
    83
static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
icculus@3673
    84
    const char *);
icculus@3673
    85
static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *,
icculus@3673
    86
    pa_context_flags_t, const pa_spawn_api *);
icculus@9400
    87
static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_list) (pa_context *, pa_sink_info_cb_t, void *);
icculus@9400
    88
static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_list) (pa_context *, pa_source_info_cb_t, void *);
icculus@9400
    89
static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_by_index) (pa_context *, uint32_t, pa_sink_info_cb_t, void *);
icculus@9400
    90
static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_by_index) (pa_context *, uint32_t, pa_source_info_cb_t, void *);
icculus@3673
    91
static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
icculus@9400
    92
static pa_operation * (*PULSEAUDIO_pa_context_subscribe) (pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *);
icculus@9400
    93
static void (*PULSEAUDIO_pa_context_set_subscribe_callback) (pa_context *, pa_context_subscribe_cb_t, void *);
icculus@3673
    94
static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *);
icculus@3673
    95
static void (*PULSEAUDIO_pa_context_unref) (pa_context *);
icculus@3672
    96
icculus@3673
    97
static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
icculus@3673
    98
    const pa_sample_spec *, const pa_channel_map *);
icculus@3673
    99
static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
icculus@3673
   100
    const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
icculus@10240
   101
static int (*PULSEAUDIO_pa_stream_connect_record) (pa_stream *, const char *,
icculus@10240
   102
    const pa_buffer_attr *, pa_stream_flags_t);
icculus@3673
   103
static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
icculus@3673
   104
static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
icculus@10240
   105
static size_t (*PULSEAUDIO_pa_stream_readable_size) (pa_stream *);
icculus@3673
   106
static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
icculus@3673
   107
    pa_free_cb_t, int64_t, pa_seek_mode_t);
icculus@3673
   108
static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
icculus@3673
   109
    pa_stream_success_cb_t, void *);
icculus@10240
   110
static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *);
icculus@10240
   111
static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *);
icculus@10240
   112
static pa_operation * (*PULSEAUDIO_pa_stream_flush)	(pa_stream *,
icculus@10240
   113
    pa_stream_success_cb_t, void *);
icculus@3673
   114
static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
icculus@3673
   115
static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
icculus@3673
   116
icculus@3673
   117
static int load_pulseaudio_syms(void);
icculus@2271
   118
icculus@2271
   119
icculus@6046
   120
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
icculus@3673
   121
icculus@3673
   122
static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
icculus@3673
   123
static void *pulseaudio_handle = NULL;
icculus@3673
   124
icculus@3673
   125
static int
icculus@3673
   126
load_pulseaudio_sym(const char *fn, void **addr)
slouken@2274
   127
{
icculus@3673
   128
    *addr = SDL_LoadFunction(pulseaudio_handle, fn);
icculus@3673
   129
    if (*addr == NULL) {
icculus@3673
   130
        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
icculus@3673
   131
        return 0;
icculus@3673
   132
    }
slouken@2274
   133
icculus@3673
   134
    return 1;
icculus@3673
   135
}
icculus@3673
   136
icculus@3673
   137
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
icculus@3673
   138
#define SDL_PULSEAUDIO_SYM(x) \
icculus@3673
   139
    if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
icculus@2271
   140
icculus@2271
   141
static void
icculus@3673
   142
UnloadPulseAudioLibrary(void)
icculus@2271
   143
{
icculus@3673
   144
    if (pulseaudio_handle != NULL) {
slouken@7191
   145
        SDL_UnloadObject(pulseaudio_handle);
icculus@3673
   146
        pulseaudio_handle = NULL;
icculus@2271
   147
    }
icculus@2271
   148
}
icculus@2271
   149
icculus@2271
   150
static int
icculus@3673
   151
LoadPulseAudioLibrary(void)
icculus@2271
   152
{
icculus@3673
   153
    int retval = 0;
icculus@3673
   154
    if (pulseaudio_handle == NULL) {
icculus@3673
   155
        pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
icculus@3673
   156
        if (pulseaudio_handle == NULL) {
icculus@3673
   157
            retval = -1;
icculus@3673
   158
            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
icculus@3673
   159
        } else {
icculus@3673
   160
            retval = load_pulseaudio_syms();
icculus@3673
   161
            if (retval < 0) {
icculus@3673
   162
                UnloadPulseAudioLibrary();
icculus@2271
   163
            }
icculus@2271
   164
        }
icculus@2271
   165
    }
icculus@2271
   166
    return retval;
icculus@2271
   167
}
icculus@2271
   168
icculus@2271
   169
#else
icculus@2271
   170
icculus@3673
   171
#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
icculus@3673
   172
icculus@2271
   173
static void
icculus@3673
   174
UnloadPulseAudioLibrary(void)
icculus@2271
   175
{
icculus@2271
   176
}
icculus@2271
   177
icculus@2271
   178
static int
icculus@3673
   179
LoadPulseAudioLibrary(void)
icculus@2271
   180
{
icculus@3673
   181
    load_pulseaudio_syms();
icculus@2271
   182
    return 0;
icculus@2271
   183
}
icculus@2271
   184
icculus@2271
   185
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
icculus@2271
   186
icculus@3673
   187
icculus@3673
   188
static int
icculus@3673
   189
load_pulseaudio_syms(void)
icculus@3673
   190
{
icculus@7545
   191
    SDL_PULSEAUDIO_SYM(pa_get_library_version);
icculus@3673
   192
    SDL_PULSEAUDIO_SYM(pa_mainloop_new);
icculus@3673
   193
    SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
icculus@3673
   194
    SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
icculus@9400
   195
    SDL_PULSEAUDIO_SYM(pa_mainloop_run);
icculus@9400
   196
    SDL_PULSEAUDIO_SYM(pa_mainloop_quit);
icculus@3673
   197
    SDL_PULSEAUDIO_SYM(pa_mainloop_free);
icculus@3673
   198
    SDL_PULSEAUDIO_SYM(pa_operation_get_state);
icculus@3673
   199
    SDL_PULSEAUDIO_SYM(pa_operation_cancel);
icculus@3673
   200
    SDL_PULSEAUDIO_SYM(pa_operation_unref);
icculus@3673
   201
    SDL_PULSEAUDIO_SYM(pa_context_new);
icculus@3673
   202
    SDL_PULSEAUDIO_SYM(pa_context_connect);
icculus@9388
   203
    SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
icculus@9395
   204
    SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list);
icculus@9400
   205
    SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index);
icculus@9400
   206
    SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index);
icculus@3673
   207
    SDL_PULSEAUDIO_SYM(pa_context_get_state);
icculus@9400
   208
    SDL_PULSEAUDIO_SYM(pa_context_subscribe);
icculus@9400
   209
    SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback);
icculus@3673
   210
    SDL_PULSEAUDIO_SYM(pa_context_disconnect);
icculus@3673
   211
    SDL_PULSEAUDIO_SYM(pa_context_unref);
icculus@3673
   212
    SDL_PULSEAUDIO_SYM(pa_stream_new);
icculus@3673
   213
    SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
icculus@10240
   214
    SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
icculus@3673
   215
    SDL_PULSEAUDIO_SYM(pa_stream_get_state);
icculus@3673
   216
    SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
icculus@10240
   217
    SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
icculus@3673
   218
    SDL_PULSEAUDIO_SYM(pa_stream_write);
icculus@3673
   219
    SDL_PULSEAUDIO_SYM(pa_stream_drain);
icculus@3673
   220
    SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
icculus@10240
   221
    SDL_PULSEAUDIO_SYM(pa_stream_peek);
icculus@10240
   222
    SDL_PULSEAUDIO_SYM(pa_stream_drop);
icculus@10240
   223
    SDL_PULSEAUDIO_SYM(pa_stream_flush);
icculus@3673
   224
    SDL_PULSEAUDIO_SYM(pa_stream_unref);
icculus@3673
   225
    SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
icculus@3673
   226
    SDL_PULSEAUDIO_SYM(pa_strerror);
icculus@3673
   227
    return 0;
icculus@3673
   228
}
icculus@3673
   229
icculus@9388
   230
static SDL_INLINE int
icculus@9388
   231
squashVersion(const int major, const int minor, const int patch)
slouken@6933
   232
{
icculus@9388
   233
    return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
icculus@9388
   234
}
slouken@6933
   235
icculus@9388
   236
/* Workaround for older pulse: pa_context_new() must have non-NULL appname */
icculus@9388
   237
static const char *
icculus@9388
   238
getAppName(void)
icculus@9388
   239
{
icculus@9388
   240
    const char *verstr = PULSEAUDIO_pa_get_library_version();
icculus@9388
   241
    if (verstr != NULL) {
icculus@9388
   242
        int maj, min, patch;
icculus@9388
   243
        if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
icculus@9388
   244
            if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
icculus@9388
   245
                return NULL;  /* 0.9.15+ handles NULL correctly. */
icculus@9388
   246
            }
icculus@9388
   247
        }
icculus@9388
   248
    }
icculus@9388
   249
    return "SDL Application";  /* oh well. */
icculus@9388
   250
}
slouken@6933
   251
icculus@9388
   252
static void
icculus@10240
   253
stream_operation_complete_no_op(pa_stream *s, int success, void *userdata)
icculus@10240
   254
{
icculus@10240
   255
    /* no-op for pa_stream_drain(), etc, to use for callback. */
icculus@10240
   256
}
icculus@10240
   257
icculus@10240
   258
static void
icculus@9400
   259
WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
icculus@9400
   260
{
icculus@9400
   261
    /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
icculus@9400
   262
    if (mainloop && o) {
icculus@9400
   263
        SDL_bool okay = SDL_TRUE;
icculus@9400
   264
        while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
icculus@9400
   265
            okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
icculus@9400
   266
        }
icculus@9400
   267
        PULSEAUDIO_pa_operation_unref(o);
icculus@9400
   268
    }
icculus@9400
   269
}
icculus@9400
   270
icculus@9400
   271
static void
icculus@9388
   272
DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
icculus@9388
   273
{
icculus@9388
   274
    if (context) {
icculus@9388
   275
        PULSEAUDIO_pa_context_disconnect(context);
icculus@9388
   276
        PULSEAUDIO_pa_context_unref(context);
icculus@9388
   277
    }
icculus@9388
   278
    if (mainloop != NULL) {
icculus@9388
   279
        PULSEAUDIO_pa_mainloop_free(mainloop);
slouken@6933
   280
    }
slouken@6933
   281
}
slouken@6933
   282
icculus@9388
   283
static int
icculus@9388
   284
ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
icculus@9388
   285
{
icculus@9388
   286
    pa_mainloop *mainloop = NULL;
icculus@9388
   287
    pa_context *context = NULL;
icculus@9388
   288
    pa_mainloop_api *mainloop_api = NULL;
icculus@9388
   289
    int state = 0;
icculus@9388
   290
icculus@9388
   291
    *_mainloop = NULL;
icculus@9388
   292
    *_context = NULL;
icculus@9388
   293
icculus@9388
   294
    /* Set up a new main loop */
icculus@9388
   295
    if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
icculus@9388
   296
        return SDL_SetError("pa_mainloop_new() failed");
icculus@9388
   297
    }
icculus@9388
   298
icculus@9388
   299
    *_mainloop = mainloop;
icculus@9388
   300
icculus@9388
   301
    mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
icculus@9388
   302
    SDL_assert(mainloop_api);  /* this never fails, right? */
icculus@9388
   303
icculus@9388
   304
    context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
icculus@9388
   305
    if (!context) {
icculus@9388
   306
        return SDL_SetError("pa_context_new() failed");
icculus@9388
   307
    }
icculus@9388
   308
    *_context = context;
icculus@9388
   309
icculus@9388
   310
    /* Connect to the PulseAudio server */
icculus@9388
   311
    if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
icculus@9388
   312
        return SDL_SetError("Could not setup connection to PulseAudio");
icculus@9388
   313
    }
icculus@9388
   314
icculus@9388
   315
    do {
icculus@9388
   316
        if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
icculus@9388
   317
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@9388
   318
        }
icculus@9388
   319
        state = PULSEAUDIO_pa_context_get_state(context);
icculus@9388
   320
        if (!PA_CONTEXT_IS_GOOD(state)) {
icculus@9388
   321
            return SDL_SetError("Could not connect to PulseAudio");
icculus@9388
   322
        }
icculus@9388
   323
    } while (state != PA_CONTEXT_READY);
icculus@9388
   324
icculus@9388
   325
    return 0;  /* connected and ready! */
icculus@9388
   326
}
icculus@9388
   327
icculus@9388
   328
static int
icculus@9388
   329
ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
icculus@9388
   330
{
icculus@9388
   331
    const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
icculus@9388
   332
    if (retval < 0) {
icculus@9388
   333
        DisconnectFromPulseServer(*_mainloop, *_context);
icculus@9388
   334
    }
icculus@9388
   335
    return retval;
icculus@9388
   336
}
icculus@9388
   337
icculus@9388
   338
icculus@2271
   339
/* This function waits until it is possible to write a full sound buffer */
icculus@2271
   340
static void
icculus@2271
   341
PULSEAUDIO_WaitDevice(_THIS)
icculus@2271
   342
{
icculus@3674
   343
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3674
   344
icculus@10238
   345
    while (SDL_AtomicGet(&this->enabled)) {
icculus@3674
   346
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@3674
   347
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@3674
   348
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@9394
   349
            SDL_OpenedAudioDeviceDisconnected(this);
icculus@3672
   350
            return;
icculus@2271
   351
        }
icculus@3674
   352
        if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
icculus@3672
   353
            return;
icculus@3672
   354
        }
icculus@2271
   355
    }
icculus@2271
   356
}
icculus@2271
   357
icculus@2271
   358
static void
icculus@2271
   359
PULSEAUDIO_PlayDevice(_THIS)
icculus@2271
   360
{
icculus@2271
   361
    /* Write the audio data */
icculus@3674
   362
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@10238
   363
    if (SDL_AtomicGet(&this->enabled)) {
icculus@9400
   364
        if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
icculus@9400
   365
            SDL_OpenedAudioDeviceDisconnected(this);
icculus@9400
   366
        }
icculus@2271
   367
    }
icculus@2271
   368
}
icculus@2271
   369
icculus@2271
   370
static Uint8 *
icculus@2271
   371
PULSEAUDIO_GetDeviceBuf(_THIS)
icculus@2271
   372
{
icculus@2271
   373
    return (this->hidden->mixbuf);
icculus@2271
   374
}
icculus@2271
   375
icculus@2271
   376
icculus@10240
   377
static int
icculus@10240
   378
PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
icculus@10240
   379
{
icculus@10240
   380
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@10240
   381
    const void *data = NULL;
icculus@10240
   382
    size_t nbytes = 0;
icculus@10240
   383
icculus@10240
   384
    while (SDL_AtomicGet(&this->enabled)) {
icculus@10240
   385
        if (h->capturebuf != NULL) {
icculus@10240
   386
            const int cpy = SDL_min(buflen, h->capturelen);
icculus@10240
   387
            SDL_memcpy(buffer, h->capturebuf, cpy);
icculus@10240
   388
            /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
icculus@10240
   389
            h->capturebuf += cpy;
icculus@10240
   390
            h->capturelen -= cpy;
icculus@10240
   391
            if (h->capturelen == 0) {
icculus@10240
   392
                h->capturebuf = NULL;
icculus@10240
   393
                PULSEAUDIO_pa_stream_drop(h->stream);  /* done with this fragment. */
icculus@10240
   394
            }
icculus@10240
   395
            return cpy;  /* new data, return it. */
icculus@10240
   396
        }
icculus@10240
   397
icculus@10240
   398
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@10240
   399
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@10240
   400
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@10240
   401
            SDL_OpenedAudioDeviceDisconnected(this);
icculus@10240
   402
            return -1;  /* uhoh, pulse failed! */
icculus@10240
   403
        }
icculus@10240
   404
icculus@10240
   405
        if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
icculus@10240
   406
            continue;  /* no data available yet. */
icculus@10240
   407
        }
icculus@10240
   408
icculus@10240
   409
        /* a new fragment is available! */
icculus@10240
   410
        PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
icculus@10240
   411
        SDL_assert(nbytes > 0);
icculus@10240
   412
        if (data == NULL) {  /* NULL==buffer had a hole. Ignore that. */
icculus@10240
   413
            PULSEAUDIO_pa_stream_drop(h->stream);  /* drop this fragment. */
icculus@10240
   414
        } else {
icculus@10240
   415
            /* store this fragment's data, start feeding it to SDL. */
icculus@10240
   416
            /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
icculus@10240
   417
            h->capturebuf = (const Uint8 *) data;
icculus@10240
   418
            h->capturelen = nbytes;
icculus@10240
   419
        }
icculus@10240
   420
    }
icculus@10240
   421
icculus@10240
   422
    return -1;  /* not enabled? */
icculus@10240
   423
}
icculus@10240
   424
icculus@10240
   425
static void
icculus@10240
   426
PULSEAUDIO_FlushCapture(_THIS)
icculus@10240
   427
{
icculus@10240
   428
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@10240
   429
icculus@10240
   430
    if (h->capturebuf != NULL) {
icculus@10240
   431
        PULSEAUDIO_pa_stream_drop(h->stream);
icculus@10240
   432
        h->capturebuf = NULL;
icculus@10240
   433
        h->capturelen = 0;
icculus@10240
   434
    }
icculus@10240
   435
icculus@10240
   436
    WaitForPulseOperation(h->mainloop, PULSEAUDIO_pa_stream_flush(h->stream, stream_operation_complete_no_op, NULL));
icculus@10240
   437
}
icculus@10240
   438
icculus@2271
   439
static void
icculus@2271
   440
PULSEAUDIO_CloseDevice(_THIS)
icculus@2271
   441
{
icculus@10255
   442
    if (this->hidden->stream) {
icculus@10240
   443
        if (this->hidden->capturebuf != NULL) {
icculus@10240
   444
            PULSEAUDIO_pa_stream_drop(this->hidden->stream);
icculus@10240
   445
        }
icculus@10255
   446
        PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
icculus@10255
   447
        PULSEAUDIO_pa_stream_unref(this->hidden->stream);
icculus@2271
   448
    }
icculus@10255
   449
icculus@10255
   450
    DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
icculus@10256
   451
    SDL_free(this->hidden->mixbuf);
icculus@10255
   452
    SDL_free(this->hidden->device_name);
icculus@10255
   453
    SDL_free(this->hidden);
icculus@2271
   454
}
icculus@2271
   455
icculus@9400
   456
static void
icculus@10240
   457
SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
icculus@10240
   458
{
icculus@10240
   459
    if (i) {
icculus@10240
   460
        char **devname = (char **) data;
icculus@10240
   461
        *devname = SDL_strdup(i->name);
icculus@10240
   462
    }
icculus@10240
   463
}
icculus@10240
   464
icculus@10240
   465
static void
icculus@10240
   466
SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
icculus@9400
   467
{
icculus@9400
   468
    if (i) {
icculus@9400
   469
        char **devname = (char **) data;
icculus@9400
   470
        *devname = SDL_strdup(i->name);
icculus@9400
   471
    }
icculus@9400
   472
}
icculus@9400
   473
icculus@9400
   474
static SDL_bool
icculus@10240
   475
FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
icculus@9400
   476
{
icculus@9400
   477
    const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
icculus@9400
   478
icculus@9400
   479
    if (handle == NULL) {  /* NULL == default device. */
icculus@9400
   480
        return SDL_TRUE;
icculus@9400
   481
    }
icculus@9400
   482
icculus@10240
   483
    if (iscapture) {
icculus@10240
   484
        WaitForPulseOperation(h->mainloop,
icculus@10240
   485
            PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
icculus@10240
   486
                SourceDeviceNameCallback, &h->device_name));
icculus@10240
   487
    } else {
icculus@10240
   488
        WaitForPulseOperation(h->mainloop,
icculus@10240
   489
            PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
icculus@10240
   490
                SinkDeviceNameCallback, &h->device_name));
icculus@10240
   491
    }
icculus@10240
   492
icculus@9400
   493
    return (h->device_name != NULL);
icculus@9400
   494
}
icculus@9400
   495
icculus@2271
   496
static int
icculus@9394
   497
PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
icculus@2271
   498
{
icculus@3674
   499
    struct SDL_PrivateAudioData *h = NULL;
icculus@2272
   500
    Uint16 test_format = 0;
icculus@2272
   501
    pa_sample_spec paspec;
icculus@2272
   502
    pa_buffer_attr paattr;
icculus@2272
   503
    pa_channel_map pacmap;
icculus@3672
   504
    pa_stream_flags_t flags = 0;
icculus@3672
   505
    int state = 0;
icculus@10240
   506
    int rc = 0;
icculus@2271
   507
icculus@2271
   508
    /* Initialize all variables that we clean on shutdown */
icculus@10257
   509
    h = this->hidden = (struct SDL_PrivateAudioData *)
icculus@2271
   510
        SDL_malloc((sizeof *this->hidden));
icculus@2271
   511
    if (this->hidden == NULL) {
icculus@7038
   512
        return SDL_OutOfMemory();
icculus@2271
   513
    }
icculus@10257
   514
    SDL_zerop(this->hidden);
icculus@2271
   515
icculus@2271
   516
    paspec.format = PA_SAMPLE_INVALID;
icculus@2271
   517
icculus@2271
   518
    /* Try for a closest match on audio format */
icculus@2271
   519
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2271
   520
         (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
icculus@2271
   521
#ifdef DEBUG_AUDIO
icculus@2271
   522
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
icculus@2271
   523
#endif
slouken@2274
   524
        switch (test_format) {
slouken@2274
   525
        case AUDIO_U8:
slouken@2274
   526
            paspec.format = PA_SAMPLE_U8;
slouken@2274
   527
            break;
slouken@2274
   528
        case AUDIO_S16LSB:
slouken@2274
   529
            paspec.format = PA_SAMPLE_S16LE;
slouken@2274
   530
            break;
slouken@2274
   531
        case AUDIO_S16MSB:
slouken@2274
   532
            paspec.format = PA_SAMPLE_S16BE;
slouken@2274
   533
            break;
slouken@7397
   534
        case AUDIO_S32LSB:
slouken@7397
   535
            paspec.format = PA_SAMPLE_S32LE;
slouken@7397
   536
            break;
slouken@7397
   537
        case AUDIO_S32MSB:
slouken@7397
   538
            paspec.format = PA_SAMPLE_S32BE;
slouken@7397
   539
            break;
slouken@7397
   540
        case AUDIO_F32LSB:
slouken@7398
   541
            paspec.format = PA_SAMPLE_FLOAT32LE;
slouken@7397
   542
            break;
slouken@7397
   543
        case AUDIO_F32MSB:
slouken@7398
   544
            paspec.format = PA_SAMPLE_FLOAT32BE;
slouken@7397
   545
            break;
slouken@2274
   546
        default:
slouken@2274
   547
            paspec.format = PA_SAMPLE_INVALID;
slouken@2274
   548
            break;
icculus@2271
   549
        }
icculus@2271
   550
        if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   551
            test_format = SDL_NextAudioFormat();
icculus@2271
   552
        }
icculus@2271
   553
    }
icculus@2271
   554
    if (paspec.format == PA_SAMPLE_INVALID) {
icculus@7038
   555
        return SDL_SetError("Couldn't find any hardware audio formats");
icculus@2271
   556
    }
icculus@2271
   557
    this->spec.format = test_format;
icculus@2271
   558
icculus@2271
   559
    /* Calculate the final parameters for this audio specification */
icculus@3672
   560
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3672
   561
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
icculus@3672
   562
#endif
icculus@2271
   563
    SDL_CalculateAudioSpec(&this->spec);
icculus@2271
   564
icculus@2271
   565
    /* Allocate mixing buffer */
icculus@10240
   566
    if (!iscapture) {
icculus@10240
   567
        h->mixlen = this->spec.size;
icculus@10256
   568
        h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
icculus@10240
   569
        if (h->mixbuf == NULL) {
icculus@10240
   570
            return SDL_OutOfMemory();
icculus@10240
   571
        }
icculus@10240
   572
        SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
icculus@2271
   573
    }
icculus@2271
   574
icculus@2271
   575
    paspec.channels = this->spec.channels;
icculus@2271
   576
    paspec.rate = this->spec.freq;
icculus@2271
   577
icculus@2271
   578
    /* Reduced prebuffering compared to the defaults. */
icculus@3672
   579
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3674
   580
    /* 2x original requested bufsize */
icculus@3674
   581
    paattr.tlength = h->mixlen * 4;
icculus@3672
   582
    paattr.prebuf = -1;
icculus@3672
   583
    paattr.maxlength = -1;
icculus@3674
   584
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
icculus@3674
   585
    paattr.minreq = h->mixlen;
icculus@3672
   586
    flags = PA_STREAM_ADJUST_LATENCY;
icculus@3672
   587
#else
icculus@3674
   588
    paattr.tlength = h->mixlen*2;
icculus@3674
   589
    paattr.prebuf = h->mixlen*2;
icculus@3674
   590
    paattr.maxlength = h->mixlen*2;
icculus@3674
   591
    paattr.minreq = h->mixlen;
icculus@3672
   592
#endif
icculus@2271
   593
icculus@9388
   594
    if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
icculus@9388
   595
        return SDL_SetError("Could not connect to PulseAudio server");
icculus@9388
   596
    }
icculus@9388
   597
icculus@10240
   598
    if (!FindDeviceName(h, iscapture, handle)) {
icculus@10240
   599
        return SDL_SetError("Requested PulseAudio sink/source missing?");
icculus@9400
   600
    }
icculus@9400
   601
icculus@2271
   602
    /* The SDL ALSA output hints us that we use Windows' channel mapping */
icculus@2271
   603
    /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
icculus@3673
   604
    PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
slouken@2274
   605
                                        PA_CHANNEL_MAP_WAVEEX);
icculus@2271
   606
icculus@3674
   607
    h->stream = PULSEAUDIO_pa_stream_new(
icculus@3674
   608
        h->context,
icculus@3672
   609
        "Simple DirectMedia Layer", /* stream description */
icculus@3672
   610
        &paspec,    /* sample format spec */
icculus@3672
   611
        &pacmap     /* channel map */
slouken@2274
   612
        );
icculus@2272
   613
icculus@3674
   614
    if (h->stream == NULL) {
icculus@7038
   615
        return SDL_SetError("Could not set up PulseAudio stream");
icculus@3672
   616
    }
icculus@3672
   617
icculus@9400
   618
    /* now that we have multi-device support, don't move a stream from
icculus@9400
   619
        a device that was unplugged to something else, unless we're default. */
icculus@9400
   620
    if (h->device_name != NULL) {
icculus@9400
   621
        flags |= PA_STREAM_DONT_MOVE;
icculus@9400
   622
    }
icculus@9400
   623
icculus@10240
   624
    if (iscapture) {
icculus@10240
   625
        rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
icculus@10240
   626
    } else {
icculus@10240
   627
        rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
icculus@10240
   628
    }
icculus@10240
   629
icculus@10240
   630
    if (rc < 0) {
icculus@7038
   631
        return SDL_SetError("Could not connect PulseAudio stream");
icculus@2271
   632
    }
icculus@2271
   633
icculus@3672
   634
    do {
icculus@3674
   635
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@7038
   636
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   637
        }
icculus@3674
   638
        state = PULSEAUDIO_pa_stream_get_state(h->stream);
icculus@3672
   639
        if (!PA_STREAM_IS_GOOD(state)) {
icculus@9400
   640
            return SDL_SetError("Could not connect PulseAudio stream");
icculus@3672
   641
        }
icculus@3672
   642
    } while (state != PA_STREAM_READY);
icculus@2271
   643
icculus@2271
   644
    /* We're ready to rock and roll. :-) */
icculus@7038
   645
    return 0;
icculus@2271
   646
}
icculus@2271
   647
icculus@9400
   648
static pa_mainloop *hotplug_mainloop = NULL;
icculus@9400
   649
static pa_context *hotplug_context = NULL;
icculus@9400
   650
static SDL_Thread *hotplug_thread = NULL;
icculus@9400
   651
icculus@9400
   652
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
icculus@9388
   653
icculus@9400
   654
/* This is called when PulseAudio adds an output ("sink") device. */
icculus@9400
   655
static void
icculus@9400
   656
SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
icculus@9400
   657
{
icculus@9388
   658
    if (i) {
icculus@9400
   659
        SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
icculus@9400
   660
    }
icculus@9400
   661
}
icculus@9400
   662
icculus@9400
   663
/* This is called when PulseAudio adds a capture ("source") device. */
icculus@9400
   664
static void
icculus@9400
   665
SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
icculus@9400
   666
{
icculus@9400
   667
    if (i) {
icculus@9400
   668
        /* Skip "monitor" sources. These are just output from other sinks. */
icculus@9400
   669
        if (i->monitor_of_sink == PA_INVALID_INDEX) {
icculus@9400
   670
            SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
icculus@9395
   671
        }
icculus@9388
   672
    }
icculus@9388
   673
}
icculus@9388
   674
icculus@9400
   675
/* This is called when PulseAudio has a device connected/removed/changed. */
icculus@9388
   676
static void
icculus@9400
   677
HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
icculus@9395
   678
{
icculus@9400
   679
    const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
icculus@9400
   680
    const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
icculus@9395
   681
icculus@9400
   682
    if (added || removed) {  /* we only care about add/remove events. */
icculus@9400
   683
        const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
icculus@9400
   684
        const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
icculus@9400
   685
icculus@9400
   686
        /* adds need sink details from the PulseAudio server. Another callback... */
icculus@9400
   687
        if (added && sink) {
icculus@9400
   688
            PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
icculus@9400
   689
        } else if (added && source) {
icculus@9400
   690
            PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
icculus@9400
   691
        } else if (removed && (sink || source)) {
icculus@9400
   692
            /* removes we can handle just with the device index. */
icculus@9400
   693
            SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
icculus@9395
   694
        }
icculus@9395
   695
    }
icculus@9395
   696
}
icculus@9395
   697
icculus@9400
   698
/* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
icculus@9400
   699
static int SDLCALL
icculus@9400
   700
HotplugThread(void *data)
icculus@9395
   701
{
icculus@9400
   702
    pa_operation *o;
icculus@9400
   703
    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
icculus@9400
   704
    PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
icculus@9400
   705
    o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
icculus@9400
   706
    PULSEAUDIO_pa_operation_unref(o);  /* don't wait for it, just do our thing. */
icculus@9400
   707
    PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
icculus@9400
   708
    return 0;
icculus@9395
   709
}
icculus@9395
   710
icculus@9395
   711
static void
icculus@9395
   712
PULSEAUDIO_DetectDevices()
icculus@9388
   713
{
icculus@9400
   714
    WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
icculus@9400
   715
    WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
icculus@9388
   716
icculus@9400
   717
    /* ok, we have a sane list, let's set up hotplug notifications now... */
icculus@10146
   718
    hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
icculus@9395
   719
}
icculus@9395
   720
icculus@9395
   721
static void
icculus@2271
   722
PULSEAUDIO_Deinitialize(void)
icculus@2271
   723
{
icculus@9400
   724
    if (hotplug_thread) {
icculus@9400
   725
        PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
icculus@9400
   726
        SDL_WaitThread(hotplug_thread, NULL);
icculus@9400
   727
        hotplug_thread = NULL;
icculus@9400
   728
    }
icculus@9400
   729
icculus@9400
   730
    DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
icculus@9400
   731
    hotplug_mainloop = NULL;
icculus@9400
   732
    hotplug_context = NULL;
icculus@9400
   733
icculus@3673
   734
    UnloadPulseAudioLibrary();
icculus@2271
   735
}
icculus@2271
   736
icculus@2271
   737
static int
icculus@2271
   738
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2271
   739
{
icculus@3673
   740
    if (LoadPulseAudioLibrary() < 0) {
icculus@2271
   741
        return 0;
icculus@2271
   742
    }
icculus@2271
   743
icculus@9400
   744
    if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
slouken@6934
   745
        UnloadPulseAudioLibrary();
slouken@6933
   746
        return 0;
slouken@6933
   747
    }
slouken@6933
   748
icculus@2271
   749
    /* Set the function pointers */
icculus@9388
   750
    impl->DetectDevices = PULSEAUDIO_DetectDevices;
icculus@2271
   751
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
icculus@2271
   752
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
icculus@2271
   753
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
icculus@2271
   754
    impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
icculus@2271
   755
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
icculus@2271
   756
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
icculus@10240
   757
    impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
icculus@10240
   758
    impl->FlushCapture = PULSEAUDIO_FlushCapture;
icculus@10240
   759
icculus@10240
   760
    impl->HasCaptureSupport = SDL_TRUE;
icculus@2271
   761
icculus@3699
   762
    return 1;   /* this audio target is available. */
icculus@2271
   763
}
icculus@2271
   764
icculus@2271
   765
AudioBootStrap PULSEAUDIO_bootstrap = {
icculus@5594
   766
    "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
icculus@2271
   767
};
icculus@2271
   768
slouken@5481
   769
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
slouken@5481
   770
icculus@2271
   771
/* vi: set ts=4 sw=4 expandtab: */