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