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