src/audio/pulseaudio/SDL_pulseaudio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 20 Oct 2013 21:56:15 -0700
changeset 7860 2b0bcdea3a79
parent 7719 31b5f9ff36ca
child 8093 b43765095a6f
permissions -rw-r--r--
Fixed bug 2129 - fix for bug 2121 breaks linking for mingw and throws multiple warnings

Andreas Ertelt

The problem in question is caused by changeset 7771 (http://hg.libsdl.org/SDL/rev/4434498bf4b9 / https://bugzilla.libsdl.org/show_bug.cgi?id=2121)

The redefinition of __inline__ (introduced by the addition of begin_code.h:128's "|| __STRICT_ANSI__") results in mingw's gcc throwing multiple

warning: always_inline function might not be inlinable [-Wattributes]

as well as a whole bunch of redefinitions of mingw internals which break linking of projects including the SDL2 headers.
icculus@2271
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 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
*/
slouken@5481
    28
#include "SDL_config.h"
icculus@2271
    29
slouken@5481
    30
#if SDL_AUDIO_DRIVER_PULSEAUDIO
icculus@2271
    31
icculus@2271
    32
/* Allow access to a raw mixing buffer */
icculus@2271
    33
icculus@2271
    34
#ifdef HAVE_SIGNAL_H
icculus@2271
    35
#include <signal.h>
icculus@2271
    36
#endif
icculus@2271
    37
#include <unistd.h>
icculus@2271
    38
#include <sys/types.h>
icculus@2271
    39
#include <errno.h>
icculus@3672
    40
#include <pulse/pulseaudio.h>
icculus@2271
    41
#include <pulse/simple.h>
icculus@2271
    42
icculus@2271
    43
#include "SDL_timer.h"
icculus@2271
    44
#include "SDL_audio.h"
icculus@2271
    45
#include "../SDL_audiomem.h"
icculus@2271
    46
#include "../SDL_audio_c.h"
icculus@2271
    47
#include "SDL_pulseaudio.h"
icculus@2271
    48
#include "SDL_loadso.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_simple *(*PULSEAUDIO_pa_simple_new) (const char *, const char *,
icculus@3673
    70
    pa_stream_direction_t, const char *, const char *, const pa_sample_spec *,
icculus@3673
    71
    const pa_channel_map *, const pa_buffer_attr *, int *);
icculus@3673
    72
static void (*PULSEAUDIO_pa_simple_free) (pa_simple *);
icculus@3673
    73
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) (
icculus@3673
    74
    pa_channel_map *, unsigned, pa_channel_map_def_t);
icculus@3673
    75
static const char * (*PULSEAUDIO_pa_strerror) (int);
icculus@3673
    76
static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void);
icculus@3673
    77
static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *);
icculus@3673
    78
static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *);
icculus@3673
    79
static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *);
icculus@3672
    80
icculus@3673
    81
static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
icculus@3673
    82
    pa_operation *);
icculus@3673
    83
static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
icculus@3673
    84
static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
icculus@3672
    85
icculus@3673
    86
static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
icculus@3673
    87
    const char *);
icculus@3673
    88
static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *,
icculus@3673
    89
    pa_context_flags_t, const pa_spawn_api *);
icculus@3673
    90
static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
icculus@3673
    91
static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *);
icculus@3673
    92
static void (*PULSEAUDIO_pa_context_unref) (pa_context *);
icculus@3672
    93
icculus@3673
    94
static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
icculus@3673
    95
    const pa_sample_spec *, const pa_channel_map *);
icculus@3673
    96
static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
icculus@3673
    97
    const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
icculus@3673
    98
static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
icculus@3673
    99
static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
icculus@3673
   100
static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
icculus@3673
   101
    pa_free_cb_t, int64_t, pa_seek_mode_t);
icculus@3673
   102
static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
icculus@3673
   103
    pa_stream_success_cb_t, void *);
icculus@3673
   104
static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
icculus@3673
   105
static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
icculus@2271
   106
icculus@3673
   107
static int load_pulseaudio_syms(void);
icculus@2271
   108
icculus@3673
   109
icculus@6046
   110
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
icculus@3673
   111
icculus@3673
   112
static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
icculus@3673
   113
static void *pulseaudio_handle = NULL;
icculus@3673
   114
icculus@3673
   115
static int
icculus@3673
   116
load_pulseaudio_sym(const char *fn, void **addr)
slouken@2274
   117
{
icculus@3673
   118
    *addr = SDL_LoadFunction(pulseaudio_handle, fn);
icculus@3673
   119
    if (*addr == NULL) {
icculus@3673
   120
        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
icculus@3673
   121
        return 0;
icculus@3673
   122
    }
slouken@2274
   123
icculus@3673
   124
    return 1;
icculus@3673
   125
}
icculus@3673
   126
icculus@3673
   127
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
icculus@3673
   128
#define SDL_PULSEAUDIO_SYM(x) \
icculus@3673
   129
    if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
icculus@2271
   130
icculus@2271
   131
static void
icculus@3673
   132
UnloadPulseAudioLibrary(void)
icculus@2271
   133
{
icculus@3673
   134
    if (pulseaudio_handle != NULL) {
slouken@7191
   135
        SDL_UnloadObject(pulseaudio_handle);
icculus@3673
   136
        pulseaudio_handle = NULL;
icculus@2271
   137
    }
icculus@2271
   138
}
icculus@2271
   139
icculus@2271
   140
static int
icculus@3673
   141
LoadPulseAudioLibrary(void)
icculus@2271
   142
{
icculus@3673
   143
    int retval = 0;
icculus@3673
   144
    if (pulseaudio_handle == NULL) {
icculus@3673
   145
        pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
icculus@3673
   146
        if (pulseaudio_handle == NULL) {
icculus@3673
   147
            retval = -1;
icculus@3673
   148
            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
icculus@3673
   149
        } else {
icculus@3673
   150
            retval = load_pulseaudio_syms();
icculus@3673
   151
            if (retval < 0) {
icculus@3673
   152
                UnloadPulseAudioLibrary();
icculus@2271
   153
            }
icculus@2271
   154
        }
icculus@2271
   155
    }
icculus@2271
   156
    return retval;
icculus@2271
   157
}
icculus@2271
   158
icculus@2271
   159
#else
icculus@2271
   160
icculus@3673
   161
#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
icculus@3673
   162
icculus@2271
   163
static void
icculus@3673
   164
UnloadPulseAudioLibrary(void)
icculus@2271
   165
{
icculus@2271
   166
}
icculus@2271
   167
icculus@2271
   168
static int
icculus@3673
   169
LoadPulseAudioLibrary(void)
icculus@2271
   170
{
icculus@3673
   171
    load_pulseaudio_syms();
icculus@2271
   172
    return 0;
icculus@2271
   173
}
icculus@2271
   174
icculus@2271
   175
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
icculus@2271
   176
icculus@3673
   177
icculus@3673
   178
static int
icculus@3673
   179
load_pulseaudio_syms(void)
icculus@3673
   180
{
icculus@7545
   181
    SDL_PULSEAUDIO_SYM(pa_get_library_version);
icculus@3673
   182
    SDL_PULSEAUDIO_SYM(pa_simple_new);
icculus@3673
   183
    SDL_PULSEAUDIO_SYM(pa_simple_free);
icculus@3673
   184
    SDL_PULSEAUDIO_SYM(pa_mainloop_new);
icculus@3673
   185
    SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
icculus@3673
   186
    SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
icculus@3673
   187
    SDL_PULSEAUDIO_SYM(pa_mainloop_free);
icculus@3673
   188
    SDL_PULSEAUDIO_SYM(pa_operation_get_state);
icculus@3673
   189
    SDL_PULSEAUDIO_SYM(pa_operation_cancel);
icculus@3673
   190
    SDL_PULSEAUDIO_SYM(pa_operation_unref);
icculus@3673
   191
    SDL_PULSEAUDIO_SYM(pa_context_new);
icculus@3673
   192
    SDL_PULSEAUDIO_SYM(pa_context_connect);
icculus@3673
   193
    SDL_PULSEAUDIO_SYM(pa_context_get_state);
icculus@3673
   194
    SDL_PULSEAUDIO_SYM(pa_context_disconnect);
icculus@3673
   195
    SDL_PULSEAUDIO_SYM(pa_context_unref);
icculus@3673
   196
    SDL_PULSEAUDIO_SYM(pa_stream_new);
icculus@3673
   197
    SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
icculus@3673
   198
    SDL_PULSEAUDIO_SYM(pa_stream_get_state);
icculus@3673
   199
    SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
icculus@3673
   200
    SDL_PULSEAUDIO_SYM(pa_stream_write);
icculus@3673
   201
    SDL_PULSEAUDIO_SYM(pa_stream_drain);
icculus@3673
   202
    SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
icculus@3673
   203
    SDL_PULSEAUDIO_SYM(pa_stream_unref);
icculus@3673
   204
    SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
icculus@3673
   205
    SDL_PULSEAUDIO_SYM(pa_strerror);
icculus@3673
   206
    return 0;
icculus@3673
   207
}
icculus@3673
   208
icculus@3673
   209
slouken@6933
   210
/* Check to see if we can connect to PulseAudio */
slouken@6933
   211
static SDL_bool
slouken@6933
   212
CheckPulseAudioAvailable()
slouken@6933
   213
{
slouken@6933
   214
    pa_simple *s;
slouken@6933
   215
    pa_sample_spec ss;
slouken@6933
   216
slouken@6933
   217
    ss.format = PA_SAMPLE_S16NE;
slouken@6933
   218
    ss.channels = 1;
slouken@6933
   219
    ss.rate = 22050;
slouken@6933
   220
slouken@6933
   221
    s = PULSEAUDIO_pa_simple_new(NULL, "SDL", PA_STREAM_PLAYBACK, NULL,
slouken@6933
   222
                                 "Test", &ss, NULL, NULL, NULL);
slouken@6933
   223
    if (s) {
slouken@6933
   224
        PULSEAUDIO_pa_simple_free(s);
slouken@6933
   225
        return SDL_TRUE;
slouken@6933
   226
    } else {
slouken@6933
   227
        return SDL_FALSE;
slouken@6933
   228
    }
slouken@6933
   229
}
slouken@6933
   230
icculus@2271
   231
/* This function waits until it is possible to write a full sound buffer */
icculus@2271
   232
static void
icculus@2271
   233
PULSEAUDIO_WaitDevice(_THIS)
icculus@2271
   234
{
icculus@3674
   235
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3674
   236
icculus@3672
   237
    while(1) {
icculus@3674
   238
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@3674
   239
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@3674
   240
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   241
            this->enabled = 0;
icculus@3672
   242
            return;
icculus@2271
   243
        }
icculus@3674
   244
        if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
icculus@3672
   245
            return;
icculus@3672
   246
        }
icculus@2271
   247
    }
icculus@2271
   248
}
icculus@2271
   249
icculus@2271
   250
static void
icculus@2271
   251
PULSEAUDIO_PlayDevice(_THIS)
icculus@2271
   252
{
icculus@2271
   253
    /* Write the audio data */
icculus@3674
   254
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3674
   255
    if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
icculus@3672
   256
                                   PA_SEEK_RELATIVE) < 0) {
icculus@2271
   257
        this->enabled = 0;
icculus@2271
   258
    }
icculus@2271
   259
}
icculus@2271
   260
icculus@2271
   261
static void
icculus@3672
   262
stream_drain_complete(pa_stream *s, int success, void *userdata)
icculus@3672
   263
{
icculus@3672
   264
    /* no-op for pa_stream_drain() to use for callback. */
icculus@3672
   265
}
icculus@3672
   266
icculus@3672
   267
static void
icculus@2271
   268
PULSEAUDIO_WaitDone(_THIS)
icculus@2271
   269
{
icculus@3674
   270
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3672
   271
    pa_operation *o;
icculus@3672
   272
icculus@3674
   273
    o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL);
icculus@3672
   274
    if (!o) {
icculus@3672
   275
        return;
icculus@3672
   276
    }
icculus@3672
   277
icculus@3673
   278
    while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
icculus@3674
   279
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@3674
   280
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@3674
   281
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3673
   282
            PULSEAUDIO_pa_operation_cancel(o);
icculus@3672
   283
            break;
icculus@3672
   284
        }
icculus@3672
   285
    }
icculus@3672
   286
icculus@3673
   287
    PULSEAUDIO_pa_operation_unref(o);
icculus@2271
   288
}
icculus@2271
   289
icculus@2271
   290
icculus@3672
   291
icculus@2271
   292
static Uint8 *
icculus@2271
   293
PULSEAUDIO_GetDeviceBuf(_THIS)
icculus@2271
   294
{
icculus@2271
   295
    return (this->hidden->mixbuf);
icculus@2271
   296
}
icculus@2271
   297
icculus@2271
   298
icculus@2271
   299
static void
icculus@2271
   300
PULSEAUDIO_CloseDevice(_THIS)
icculus@2271
   301
{
icculus@2271
   302
    if (this->hidden != NULL) {
slouken@7719
   303
        SDL_FreeAudioMem(this->hidden->mixbuf);
slouken@7719
   304
        this->hidden->mixbuf = NULL;
icculus@2271
   305
        if (this->hidden->stream) {
icculus@3673
   306
            PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
icculus@3673
   307
            PULSEAUDIO_pa_stream_unref(this->hidden->stream);
icculus@2271
   308
            this->hidden->stream = NULL;
icculus@2271
   309
        }
icculus@3672
   310
        if (this->hidden->context != NULL) {
icculus@3673
   311
            PULSEAUDIO_pa_context_disconnect(this->hidden->context);
icculus@3673
   312
            PULSEAUDIO_pa_context_unref(this->hidden->context);
icculus@3672
   313
            this->hidden->context = NULL;
icculus@3672
   314
        }
icculus@3672
   315
        if (this->hidden->mainloop != NULL) {
icculus@3673
   316
            PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop);
icculus@3672
   317
            this->hidden->mainloop = NULL;
icculus@3672
   318
        }
icculus@2271
   319
        SDL_free(this->hidden);
icculus@2271
   320
        this->hidden = NULL;
icculus@2271
   321
    }
icculus@2271
   322
}
icculus@2271
   323
icculus@2271
   324
slouken@7860
   325
static SDL_INLINE int
icculus@7545
   326
squashVersion(const int major, const int minor, const int patch)
icculus@7545
   327
{
icculus@7545
   328
    return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
icculus@7545
   329
}
icculus@7545
   330
icculus@7545
   331
/* Workaround for older pulse: pa_context_new() must have non-NULL appname */
icculus@7545
   332
static const char *
icculus@7545
   333
getAppName(void)
icculus@7545
   334
{
icculus@7545
   335
    const char *verstr = PULSEAUDIO_pa_get_library_version();
icculus@7545
   336
    if (verstr != NULL) {
icculus@7545
   337
        int maj, min, patch;
icculus@7545
   338
        if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
icculus@7545
   339
            if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
icculus@7545
   340
                return NULL;  /* 0.9.15+ handles NULL correctly. */
icculus@7545
   341
            }
icculus@7545
   342
        }
icculus@7545
   343
    }
icculus@7545
   344
    return "SDL Application";  /* oh well. */
icculus@7545
   345
}
icculus@7545
   346
icculus@2271
   347
static int
icculus@2271
   348
PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
icculus@2271
   349
{
icculus@3674
   350
    struct SDL_PrivateAudioData *h = NULL;
icculus@2272
   351
    Uint16 test_format = 0;
icculus@2272
   352
    pa_sample_spec paspec;
icculus@2272
   353
    pa_buffer_attr paattr;
icculus@2272
   354
    pa_channel_map pacmap;
icculus@3672
   355
    pa_stream_flags_t flags = 0;
icculus@3672
   356
    int state = 0;
icculus@2271
   357
icculus@2271
   358
    /* Initialize all variables that we clean on shutdown */
icculus@2271
   359
    this->hidden = (struct SDL_PrivateAudioData *)
icculus@2271
   360
        SDL_malloc((sizeof *this->hidden));
icculus@2271
   361
    if (this->hidden == NULL) {
icculus@7038
   362
        return SDL_OutOfMemory();
icculus@2271
   363
    }
icculus@2271
   364
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@3674
   365
    h = this->hidden;
icculus@2271
   366
icculus@2271
   367
    paspec.format = PA_SAMPLE_INVALID;
icculus@2271
   368
icculus@2271
   369
    /* Try for a closest match on audio format */
icculus@2271
   370
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2271
   371
         (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
icculus@2271
   372
#ifdef DEBUG_AUDIO
icculus@2271
   373
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
icculus@2271
   374
#endif
slouken@2274
   375
        switch (test_format) {
slouken@2274
   376
        case AUDIO_U8:
slouken@2274
   377
            paspec.format = PA_SAMPLE_U8;
slouken@2274
   378
            break;
slouken@2274
   379
        case AUDIO_S16LSB:
slouken@2274
   380
            paspec.format = PA_SAMPLE_S16LE;
slouken@2274
   381
            break;
slouken@2274
   382
        case AUDIO_S16MSB:
slouken@2274
   383
            paspec.format = PA_SAMPLE_S16BE;
slouken@2274
   384
            break;
slouken@7397
   385
        case AUDIO_S32LSB:
slouken@7397
   386
            paspec.format = PA_SAMPLE_S32LE;
slouken@7397
   387
            break;
slouken@7397
   388
        case AUDIO_S32MSB:
slouken@7397
   389
            paspec.format = PA_SAMPLE_S32BE;
slouken@7397
   390
            break;
slouken@7397
   391
        case AUDIO_F32LSB:
slouken@7398
   392
            paspec.format = PA_SAMPLE_FLOAT32LE;
slouken@7397
   393
            break;
slouken@7397
   394
        case AUDIO_F32MSB:
slouken@7398
   395
            paspec.format = PA_SAMPLE_FLOAT32BE;
slouken@7397
   396
            break;
slouken@2274
   397
        default:
slouken@2274
   398
            paspec.format = PA_SAMPLE_INVALID;
slouken@2274
   399
            break;
icculus@2271
   400
        }
icculus@2271
   401
        if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   402
            test_format = SDL_NextAudioFormat();
icculus@2271
   403
        }
icculus@2271
   404
    }
icculus@2271
   405
    if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   406
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   407
        return SDL_SetError("Couldn't find any hardware audio formats");
icculus@2271
   408
    }
icculus@2271
   409
    this->spec.format = test_format;
icculus@2271
   410
icculus@2271
   411
    /* Calculate the final parameters for this audio specification */
icculus@3672
   412
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3672
   413
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
icculus@3672
   414
#endif
icculus@2271
   415
    SDL_CalculateAudioSpec(&this->spec);
icculus@2271
   416
icculus@2271
   417
    /* Allocate mixing buffer */
icculus@3674
   418
    h->mixlen = this->spec.size;
icculus@3674
   419
    h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
icculus@3674
   420
    if (h->mixbuf == NULL) {
icculus@2271
   421
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   422
        return SDL_OutOfMemory();
icculus@2271
   423
    }
icculus@3674
   424
    SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
icculus@2271
   425
icculus@2271
   426
    paspec.channels = this->spec.channels;
icculus@2271
   427
    paspec.rate = this->spec.freq;
icculus@2271
   428
icculus@2271
   429
    /* Reduced prebuffering compared to the defaults. */
icculus@3672
   430
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3674
   431
    /* 2x original requested bufsize */
icculus@3674
   432
    paattr.tlength = h->mixlen * 4;
icculus@3672
   433
    paattr.prebuf = -1;
icculus@3672
   434
    paattr.maxlength = -1;
icculus@3674
   435
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
icculus@3674
   436
    paattr.minreq = h->mixlen;
icculus@3672
   437
    flags = PA_STREAM_ADJUST_LATENCY;
icculus@3672
   438
#else
icculus@3674
   439
    paattr.tlength = h->mixlen*2;
icculus@3674
   440
    paattr.prebuf = h->mixlen*2;
icculus@3674
   441
    paattr.maxlength = h->mixlen*2;
icculus@3674
   442
    paattr.minreq = h->mixlen;
icculus@3672
   443
#endif
icculus@2271
   444
icculus@2271
   445
    /* The SDL ALSA output hints us that we use Windows' channel mapping */
icculus@2271
   446
    /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
icculus@3673
   447
    PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
slouken@2274
   448
                                        PA_CHANNEL_MAP_WAVEEX);
icculus@2271
   449
icculus@3672
   450
    /* Set up a new main loop */
icculus@3674
   451
    if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
icculus@3672
   452
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   453
        return SDL_SetError("pa_mainloop_new() failed");
icculus@3672
   454
    }
icculus@3672
   455
icculus@3674
   456
    h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
icculus@7545
   457
    h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName());
icculus@3674
   458
    if (!h->context) {
icculus@3672
   459
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   460
        return SDL_SetError("pa_context_new() failed");
icculus@3672
   461
    }
icculus@3672
   462
icculus@2271
   463
    /* Connect to the PulseAudio server */
icculus@3674
   464
    if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
icculus@3672
   465
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   466
        return SDL_SetError("Could not setup connection to PulseAudio");
icculus@3672
   467
    }
icculus@3672
   468
icculus@3672
   469
    do {
icculus@3674
   470
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   471
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   472
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   473
        }
icculus@3674
   474
        state = PULSEAUDIO_pa_context_get_state(h->context);
icculus@3672
   475
        if (!PA_CONTEXT_IS_GOOD(state)) {
icculus@3672
   476
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   477
            return SDL_SetError("Could not connect to PulseAudio");
icculus@3672
   478
        }
icculus@3672
   479
    } while (state != PA_CONTEXT_READY);
icculus@3672
   480
icculus@3674
   481
    h->stream = PULSEAUDIO_pa_stream_new(
icculus@3674
   482
        h->context,
icculus@3672
   483
        "Simple DirectMedia Layer", /* stream description */
icculus@3672
   484
        &paspec,    /* sample format spec */
icculus@3672
   485
        &pacmap     /* channel map */
slouken@2274
   486
        );
icculus@2272
   487
icculus@3674
   488
    if (h->stream == NULL) {
icculus@2271
   489
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   490
        return SDL_SetError("Could not set up PulseAudio stream");
icculus@2271
   491
    }
icculus@2271
   492
icculus@3674
   493
    if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
icculus@3672
   494
            NULL, NULL) < 0) {
icculus@3672
   495
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   496
        return SDL_SetError("Could not connect PulseAudio stream");
icculus@3672
   497
    }
icculus@3672
   498
icculus@3672
   499
    do {
icculus@3674
   500
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   501
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   502
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   503
        }
icculus@3674
   504
        state = PULSEAUDIO_pa_stream_get_state(h->stream);
icculus@3672
   505
        if (!PA_STREAM_IS_GOOD(state)) {
icculus@3672
   506
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   507
            return SDL_SetError("Could not create to PulseAudio stream");
icculus@3672
   508
        }
icculus@3672
   509
    } while (state != PA_STREAM_READY);
icculus@2271
   510
icculus@2271
   511
    /* We're ready to rock and roll. :-) */
icculus@7038
   512
    return 0;
icculus@2271
   513
}
icculus@2271
   514
icculus@2271
   515
icculus@2271
   516
static void
icculus@2271
   517
PULSEAUDIO_Deinitialize(void)
icculus@2271
   518
{
icculus@3673
   519
    UnloadPulseAudioLibrary();
icculus@2271
   520
}
icculus@2271
   521
icculus@2271
   522
static int
icculus@2271
   523
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2271
   524
{
icculus@3673
   525
    if (LoadPulseAudioLibrary() < 0) {
icculus@2271
   526
        return 0;
icculus@2271
   527
    }
icculus@2271
   528
slouken@6933
   529
    if (!CheckPulseAudioAvailable()) {
slouken@6934
   530
        UnloadPulseAudioLibrary();
slouken@6933
   531
        return 0;
slouken@6933
   532
    }
slouken@6933
   533
icculus@2271
   534
    /* Set the function pointers */
icculus@2271
   535
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
icculus@2271
   536
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
icculus@2271
   537
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
icculus@2271
   538
    impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
icculus@2271
   539
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
icculus@2271
   540
    impl->WaitDone = PULSEAUDIO_WaitDone;
icculus@2271
   541
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
icculus@2271
   542
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2271
   543
icculus@3699
   544
    return 1;   /* this audio target is available. */
icculus@2271
   545
}
icculus@2271
   546
icculus@2271
   547
icculus@2271
   548
AudioBootStrap PULSEAUDIO_bootstrap = {
icculus@5594
   549
    "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
icculus@2271
   550
};
icculus@2271
   551
slouken@5481
   552
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
slouken@5481
   553
icculus@2271
   554
/* vi: set ts=4 sw=4 expandtab: */