src/audio/pulseaudio/SDL_pulseaudio.c
author Sam Lantinga
Tue, 26 Feb 2013 16:31:52 -0800
changeset 6933 e56ed2eb139e
parent 6885 700f1b25f77f
child 6934 3ce238619067
permissions -rw-r--r--
Check to make sure we can connect to PulseAudio before we use it.
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 */
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
slouken@6933
   208
/* Check to see if we can connect to PulseAudio */
slouken@6933
   209
static SDL_bool
slouken@6933
   210
CheckPulseAudioAvailable()
slouken@6933
   211
{
slouken@6933
   212
    pa_simple *s;
slouken@6933
   213
    pa_sample_spec ss;
slouken@6933
   214
slouken@6933
   215
    ss.format = PA_SAMPLE_S16NE;
slouken@6933
   216
    ss.channels = 1;
slouken@6933
   217
    ss.rate = 22050;
slouken@6933
   218
slouken@6933
   219
    s = PULSEAUDIO_pa_simple_new(NULL, "SDL", PA_STREAM_PLAYBACK, NULL,
slouken@6933
   220
                                 "Test", &ss, NULL, NULL, NULL);
slouken@6933
   221
    if (s) {
slouken@6933
   222
        PULSEAUDIO_pa_simple_free(s);
slouken@6933
   223
        return SDL_TRUE;
slouken@6933
   224
    } else {
slouken@6933
   225
        return SDL_FALSE;
slouken@6933
   226
    }
slouken@6933
   227
}
slouken@6933
   228
icculus@2271
   229
/* This function waits until it is possible to write a full sound buffer */
icculus@2271
   230
static void
icculus@2271
   231
PULSEAUDIO_WaitDevice(_THIS)
icculus@2271
   232
{
icculus@3674
   233
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3674
   234
icculus@3672
   235
    while(1) {
icculus@3674
   236
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@3674
   237
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@3674
   238
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   239
            this->enabled = 0;
icculus@3672
   240
            return;
icculus@2271
   241
        }
icculus@3674
   242
        if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
icculus@3672
   243
            return;
icculus@3672
   244
        }
icculus@2271
   245
    }
icculus@2271
   246
}
icculus@2271
   247
icculus@2271
   248
static void
icculus@2271
   249
PULSEAUDIO_PlayDevice(_THIS)
icculus@2271
   250
{
icculus@2271
   251
    /* Write the audio data */
icculus@3674
   252
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3674
   253
    if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
icculus@3672
   254
                                   PA_SEEK_RELATIVE) < 0) {
icculus@2271
   255
        this->enabled = 0;
icculus@2271
   256
    }
icculus@2271
   257
}
icculus@2271
   258
icculus@2271
   259
static void
icculus@3672
   260
stream_drain_complete(pa_stream *s, int success, void *userdata)
icculus@3672
   261
{
icculus@3672
   262
    /* no-op for pa_stream_drain() to use for callback. */
icculus@3672
   263
}
icculus@3672
   264
icculus@3672
   265
static void
icculus@2271
   266
PULSEAUDIO_WaitDone(_THIS)
icculus@2271
   267
{
icculus@3674
   268
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@3672
   269
    pa_operation *o;
icculus@3672
   270
icculus@3674
   271
    o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL);
icculus@3672
   272
    if (!o) {
icculus@3672
   273
        return;
icculus@3672
   274
    }
icculus@3672
   275
icculus@3673
   276
    while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
icculus@3674
   277
        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
icculus@3674
   278
            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
icculus@3674
   279
            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3673
   280
            PULSEAUDIO_pa_operation_cancel(o);
icculus@3672
   281
            break;
icculus@3672
   282
        }
icculus@3672
   283
    }
icculus@3672
   284
icculus@3673
   285
    PULSEAUDIO_pa_operation_unref(o);
icculus@2271
   286
}
icculus@2271
   287
icculus@2271
   288
icculus@3672
   289
icculus@2271
   290
static Uint8 *
icculus@2271
   291
PULSEAUDIO_GetDeviceBuf(_THIS)
icculus@2271
   292
{
icculus@2271
   293
    return (this->hidden->mixbuf);
icculus@2271
   294
}
icculus@2271
   295
icculus@2271
   296
icculus@2271
   297
static void
icculus@2271
   298
PULSEAUDIO_CloseDevice(_THIS)
icculus@2271
   299
{
icculus@2271
   300
    if (this->hidden != NULL) {
icculus@2271
   301
        if (this->hidden->mixbuf != NULL) {
icculus@2271
   302
            SDL_FreeAudioMem(this->hidden->mixbuf);
icculus@2271
   303
            this->hidden->mixbuf = NULL;
icculus@2271
   304
        }
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
icculus@2271
   325
static int
icculus@2271
   326
PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
icculus@2271
   327
{
icculus@3674
   328
    struct SDL_PrivateAudioData *h = NULL;
icculus@2272
   329
    Uint16 test_format = 0;
icculus@2272
   330
    pa_sample_spec paspec;
icculus@2272
   331
    pa_buffer_attr paattr;
icculus@2272
   332
    pa_channel_map pacmap;
icculus@3672
   333
    pa_stream_flags_t flags = 0;
icculus@3672
   334
    int state = 0;
icculus@2271
   335
icculus@2271
   336
    /* Initialize all variables that we clean on shutdown */
icculus@2271
   337
    this->hidden = (struct SDL_PrivateAudioData *)
icculus@2271
   338
        SDL_malloc((sizeof *this->hidden));
icculus@2271
   339
    if (this->hidden == NULL) {
icculus@2271
   340
        SDL_OutOfMemory();
icculus@2271
   341
        return 0;
icculus@2271
   342
    }
icculus@2271
   343
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@3674
   344
    h = this->hidden;
icculus@2271
   345
icculus@2271
   346
    paspec.format = PA_SAMPLE_INVALID;
icculus@2271
   347
icculus@2271
   348
    /* Try for a closest match on audio format */
icculus@2271
   349
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2271
   350
         (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
icculus@2271
   351
#ifdef DEBUG_AUDIO
icculus@2271
   352
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
icculus@2271
   353
#endif
slouken@2274
   354
        switch (test_format) {
slouken@2274
   355
        case AUDIO_U8:
slouken@2274
   356
            paspec.format = PA_SAMPLE_U8;
slouken@2274
   357
            break;
slouken@2274
   358
        case AUDIO_S16LSB:
slouken@2274
   359
            paspec.format = PA_SAMPLE_S16LE;
slouken@2274
   360
            break;
slouken@2274
   361
        case AUDIO_S16MSB:
slouken@2274
   362
            paspec.format = PA_SAMPLE_S16BE;
slouken@2274
   363
            break;
slouken@2274
   364
        default:
slouken@2274
   365
            paspec.format = PA_SAMPLE_INVALID;
slouken@2274
   366
            break;
icculus@2271
   367
        }
icculus@2271
   368
        if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   369
            test_format = SDL_NextAudioFormat();
icculus@2271
   370
        }
icculus@2271
   371
    }
icculus@2271
   372
    if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   373
        PULSEAUDIO_CloseDevice(this);
icculus@2271
   374
        SDL_SetError("Couldn't find any hardware audio formats");
icculus@2271
   375
        return 0;
icculus@2271
   376
    }
icculus@2271
   377
    this->spec.format = test_format;
icculus@2271
   378
icculus@2271
   379
    /* Calculate the final parameters for this audio specification */
icculus@3672
   380
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3672
   381
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
icculus@3672
   382
#endif
icculus@2271
   383
    SDL_CalculateAudioSpec(&this->spec);
icculus@2271
   384
icculus@2271
   385
    /* Allocate mixing buffer */
icculus@3674
   386
    h->mixlen = this->spec.size;
icculus@3674
   387
    h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
icculus@3674
   388
    if (h->mixbuf == NULL) {
icculus@2271
   389
        PULSEAUDIO_CloseDevice(this);
icculus@2271
   390
        SDL_OutOfMemory();
icculus@2271
   391
        return 0;
icculus@2271
   392
    }
icculus@3674
   393
    SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
icculus@2271
   394
icculus@2271
   395
    paspec.channels = this->spec.channels;
icculus@2271
   396
    paspec.rate = this->spec.freq;
icculus@2271
   397
icculus@2271
   398
    /* Reduced prebuffering compared to the defaults. */
icculus@3672
   399
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3674
   400
    /* 2x original requested bufsize */
icculus@3674
   401
    paattr.tlength = h->mixlen * 4;
icculus@3672
   402
    paattr.prebuf = -1;
icculus@3672
   403
    paattr.maxlength = -1;
icculus@3674
   404
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
icculus@3674
   405
    paattr.minreq = h->mixlen;
icculus@3672
   406
    flags = PA_STREAM_ADJUST_LATENCY;
icculus@3672
   407
#else
icculus@3674
   408
    paattr.tlength = h->mixlen*2;
icculus@3674
   409
    paattr.prebuf = h->mixlen*2;
icculus@3674
   410
    paattr.maxlength = h->mixlen*2;
icculus@3674
   411
    paattr.minreq = h->mixlen;
icculus@3672
   412
#endif
icculus@2271
   413
icculus@2271
   414
    /* The SDL ALSA output hints us that we use Windows' channel mapping */
icculus@2271
   415
    /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
icculus@3673
   416
    PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
slouken@2274
   417
                                        PA_CHANNEL_MAP_WAVEEX);
icculus@2271
   418
icculus@3672
   419
    /* Set up a new main loop */
icculus@3674
   420
    if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
icculus@3672
   421
        PULSEAUDIO_CloseDevice(this);
icculus@3672
   422
        SDL_SetError("pa_mainloop_new() failed");
icculus@3672
   423
        return 0;
icculus@3672
   424
    }
icculus@3672
   425
icculus@3674
   426
    h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
slouken@5080
   427
    h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, NULL);
icculus@3674
   428
    if (!h->context) {
icculus@3672
   429
        PULSEAUDIO_CloseDevice(this);
icculus@3672
   430
        SDL_SetError("pa_context_new() failed");
icculus@3672
   431
        return 0;
icculus@3672
   432
    }
icculus@3672
   433
icculus@2271
   434
    /* Connect to the PulseAudio server */
icculus@3674
   435
    if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
icculus@3672
   436
        PULSEAUDIO_CloseDevice(this);
icculus@3672
   437
        SDL_SetError("Could not setup connection to PulseAudio");
icculus@3672
   438
        return 0;
icculus@3672
   439
    }
icculus@3672
   440
icculus@3672
   441
    do {
icculus@3674
   442
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   443
            PULSEAUDIO_CloseDevice(this);
icculus@3672
   444
            SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   445
            return 0;
icculus@3672
   446
        }
icculus@3674
   447
        state = PULSEAUDIO_pa_context_get_state(h->context);
icculus@3672
   448
        if (!PA_CONTEXT_IS_GOOD(state)) {
icculus@3672
   449
            PULSEAUDIO_CloseDevice(this);
icculus@3672
   450
            SDL_SetError("Could not connect to PulseAudio");
icculus@3672
   451
            return 0;
icculus@3672
   452
        }
icculus@3672
   453
    } while (state != PA_CONTEXT_READY);
icculus@3672
   454
icculus@3674
   455
    h->stream = PULSEAUDIO_pa_stream_new(
icculus@3674
   456
        h->context,
icculus@3672
   457
        "Simple DirectMedia Layer", /* stream description */
icculus@3672
   458
        &paspec,    /* sample format spec */
icculus@3672
   459
        &pacmap     /* channel map */
slouken@2274
   460
        );
icculus@2272
   461
icculus@3674
   462
    if (h->stream == NULL) {
icculus@2271
   463
        PULSEAUDIO_CloseDevice(this);
icculus@3672
   464
        SDL_SetError("Could not set up PulseAudio stream");
icculus@3672
   465
        return 0;
icculus@3672
   466
    }
icculus@3672
   467
icculus@3674
   468
    if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
icculus@3672
   469
            NULL, NULL) < 0) {
icculus@3672
   470
        PULSEAUDIO_CloseDevice(this);
icculus@3672
   471
        SDL_SetError("Could not connect PulseAudio stream");
icculus@2272
   472
        return 0;
icculus@2271
   473
    }
icculus@2271
   474
icculus@3672
   475
    do {
icculus@3674
   476
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   477
            PULSEAUDIO_CloseDevice(this);
icculus@3672
   478
            SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   479
            return 0;
icculus@3672
   480
        }
icculus@3674
   481
        state = PULSEAUDIO_pa_stream_get_state(h->stream);
icculus@3672
   482
        if (!PA_STREAM_IS_GOOD(state)) {
icculus@3672
   483
            PULSEAUDIO_CloseDevice(this);
icculus@3672
   484
            SDL_SetError("Could not create to PulseAudio stream");
icculus@3672
   485
            return 0;
icculus@3672
   486
        }
icculus@3672
   487
    } while (state != PA_STREAM_READY);
icculus@2271
   488
icculus@2271
   489
    /* We're ready to rock and roll. :-) */
icculus@2271
   490
    return 1;
icculus@2271
   491
}
icculus@2271
   492
icculus@2271
   493
icculus@2271
   494
static void
icculus@2271
   495
PULSEAUDIO_Deinitialize(void)
icculus@2271
   496
{
icculus@3673
   497
    UnloadPulseAudioLibrary();
icculus@2271
   498
}
icculus@2271
   499
icculus@2271
   500
static int
icculus@2271
   501
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2271
   502
{
icculus@3673
   503
    if (LoadPulseAudioLibrary() < 0) {
icculus@2271
   504
        return 0;
icculus@2271
   505
    }
icculus@2271
   506
slouken@6933
   507
    if (!CheckPulseAudioAvailable()) {
slouken@6933
   508
        return 0;
slouken@6933
   509
    }
slouken@6933
   510
icculus@2271
   511
    /* Set the function pointers */
icculus@2271
   512
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
icculus@2271
   513
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
icculus@2271
   514
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
icculus@2271
   515
    impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
icculus@2271
   516
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
icculus@2271
   517
    impl->WaitDone = PULSEAUDIO_WaitDone;
icculus@2271
   518
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
icculus@2271
   519
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2271
   520
icculus@3699
   521
    return 1;   /* this audio target is available. */
icculus@2271
   522
}
icculus@2271
   523
icculus@2271
   524
icculus@2271
   525
AudioBootStrap PULSEAUDIO_bootstrap = {
icculus@5594
   526
    "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
icculus@2271
   527
};
icculus@2271
   528
slouken@5481
   529
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
slouken@5481
   530
icculus@2271
   531
/* vi: set ts=4 sw=4 expandtab: */