src/audio/pulseaudio/SDL_pulseaudio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 10 Jul 2013 22:01:24 -0700
changeset 7397 08e4eb5cea46
parent 7191 75360622e65f
child 7398 31ec14665b79
permissions -rw-r--r--
Fixed bug 1949 - Pulseaudio 32 bit audio formats support

Matt Scheirer

Pulse has supported (since version 0.8, at least) 32 bit audio formats that are now becoming available in SDL2. This patch adds those format conversions to the switch clause in the pulseaudio backend.
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@3672
    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@2271
   105
icculus@3673
   106
static int load_pulseaudio_syms(void);
icculus@2271
   107
icculus@3673
   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) {
slouken@7191
   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@7038
   340
        return SDL_OutOfMemory();
icculus@2271
   341
    }
icculus@2271
   342
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@3674
   343
    h = this->hidden;
icculus@2271
   344
icculus@2271
   345
    paspec.format = PA_SAMPLE_INVALID;
icculus@2271
   346
icculus@2271
   347
    /* Try for a closest match on audio format */
icculus@2271
   348
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2271
   349
         (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
icculus@2271
   350
#ifdef DEBUG_AUDIO
icculus@2271
   351
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
icculus@2271
   352
#endif
slouken@2274
   353
        switch (test_format) {
slouken@2274
   354
        case AUDIO_U8:
slouken@2274
   355
            paspec.format = PA_SAMPLE_U8;
slouken@2274
   356
            break;
slouken@2274
   357
        case AUDIO_S16LSB:
slouken@2274
   358
            paspec.format = PA_SAMPLE_S16LE;
slouken@2274
   359
            break;
slouken@2274
   360
        case AUDIO_S16MSB:
slouken@2274
   361
            paspec.format = PA_SAMPLE_S16BE;
slouken@2274
   362
            break;
slouken@7397
   363
        case AUDIO_S32LSB:
slouken@7397
   364
            paspec.format = PA_SAMPLE_S32LE;
slouken@7397
   365
            break;
slouken@7397
   366
        case AUDIO_S32MSB:
slouken@7397
   367
            paspec.format = PA_SAMPLE_S32BE;
slouken@7397
   368
            break;
slouken@7397
   369
        case AUDIO_F32LSB:
slouken@7397
   370
            paspec.format = PA_SAMPLE_FLOAT32LE
slouken@7397
   371
            break;
slouken@7397
   372
        case AUDIO_F32MSB:
slouken@7397
   373
            paspec.format = PA_SAMPLE_FLOAT32BE
slouken@7397
   374
            break;
slouken@2274
   375
        default:
slouken@2274
   376
            paspec.format = PA_SAMPLE_INVALID;
slouken@2274
   377
            break;
icculus@2271
   378
        }
icculus@2271
   379
        if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   380
            test_format = SDL_NextAudioFormat();
icculus@2271
   381
        }
icculus@2271
   382
    }
icculus@2271
   383
    if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   384
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   385
        return SDL_SetError("Couldn't find any hardware audio formats");
icculus@2271
   386
    }
icculus@2271
   387
    this->spec.format = test_format;
icculus@2271
   388
icculus@2271
   389
    /* Calculate the final parameters for this audio specification */
icculus@3672
   390
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3672
   391
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
icculus@3672
   392
#endif
icculus@2271
   393
    SDL_CalculateAudioSpec(&this->spec);
icculus@2271
   394
icculus@2271
   395
    /* Allocate mixing buffer */
icculus@3674
   396
    h->mixlen = this->spec.size;
icculus@3674
   397
    h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
icculus@3674
   398
    if (h->mixbuf == NULL) {
icculus@2271
   399
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   400
        return SDL_OutOfMemory();
icculus@2271
   401
    }
icculus@3674
   402
    SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
icculus@2271
   403
icculus@2271
   404
    paspec.channels = this->spec.channels;
icculus@2271
   405
    paspec.rate = this->spec.freq;
icculus@2271
   406
icculus@2271
   407
    /* Reduced prebuffering compared to the defaults. */
icculus@3672
   408
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3674
   409
    /* 2x original requested bufsize */
icculus@3674
   410
    paattr.tlength = h->mixlen * 4;
icculus@3672
   411
    paattr.prebuf = -1;
icculus@3672
   412
    paattr.maxlength = -1;
icculus@3674
   413
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
icculus@3674
   414
    paattr.minreq = h->mixlen;
icculus@3672
   415
    flags = PA_STREAM_ADJUST_LATENCY;
icculus@3672
   416
#else
icculus@3674
   417
    paattr.tlength = h->mixlen*2;
icculus@3674
   418
    paattr.prebuf = h->mixlen*2;
icculus@3674
   419
    paattr.maxlength = h->mixlen*2;
icculus@3674
   420
    paattr.minreq = h->mixlen;
icculus@3672
   421
#endif
icculus@2271
   422
icculus@2271
   423
    /* The SDL ALSA output hints us that we use Windows' channel mapping */
icculus@2271
   424
    /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
icculus@3673
   425
    PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
slouken@2274
   426
                                        PA_CHANNEL_MAP_WAVEEX);
icculus@2271
   427
icculus@3672
   428
    /* Set up a new main loop */
icculus@3674
   429
    if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
icculus@3672
   430
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   431
        return SDL_SetError("pa_mainloop_new() failed");
icculus@3672
   432
    }
icculus@3672
   433
icculus@3674
   434
    h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
slouken@5080
   435
    h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, NULL);
icculus@3674
   436
    if (!h->context) {
icculus@3672
   437
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   438
        return SDL_SetError("pa_context_new() failed");
icculus@3672
   439
    }
icculus@3672
   440
icculus@2271
   441
    /* Connect to the PulseAudio server */
icculus@3674
   442
    if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
icculus@3672
   443
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   444
        return SDL_SetError("Could not setup connection to PulseAudio");
icculus@3672
   445
    }
icculus@3672
   446
icculus@3672
   447
    do {
icculus@3674
   448
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   449
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   450
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   451
        }
icculus@3674
   452
        state = PULSEAUDIO_pa_context_get_state(h->context);
icculus@3672
   453
        if (!PA_CONTEXT_IS_GOOD(state)) {
icculus@3672
   454
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   455
            return SDL_SetError("Could not connect to PulseAudio");
icculus@3672
   456
        }
icculus@3672
   457
    } while (state != PA_CONTEXT_READY);
icculus@3672
   458
icculus@3674
   459
    h->stream = PULSEAUDIO_pa_stream_new(
icculus@3674
   460
        h->context,
icculus@3672
   461
        "Simple DirectMedia Layer", /* stream description */
icculus@3672
   462
        &paspec,    /* sample format spec */
icculus@3672
   463
        &pacmap     /* channel map */
slouken@2274
   464
        );
icculus@2272
   465
icculus@3674
   466
    if (h->stream == NULL) {
icculus@2271
   467
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   468
        return SDL_SetError("Could not set up PulseAudio stream");
icculus@2271
   469
    }
icculus@2271
   470
icculus@3674
   471
    if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
icculus@3672
   472
            NULL, NULL) < 0) {
icculus@3672
   473
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   474
        return SDL_SetError("Could not connect PulseAudio stream");
icculus@3672
   475
    }
icculus@3672
   476
icculus@3672
   477
    do {
icculus@3674
   478
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   479
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   480
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   481
        }
icculus@3674
   482
        state = PULSEAUDIO_pa_stream_get_state(h->stream);
icculus@3672
   483
        if (!PA_STREAM_IS_GOOD(state)) {
icculus@3672
   484
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   485
            return SDL_SetError("Could not create to PulseAudio stream");
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@7038
   490
    return 0;
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@6934
   508
        UnloadPulseAudioLibrary();
slouken@6933
   509
        return 0;
slouken@6933
   510
    }
slouken@6933
   511
icculus@2271
   512
    /* Set the function pointers */
icculus@2271
   513
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
icculus@2271
   514
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
icculus@2271
   515
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
icculus@2271
   516
    impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
icculus@2271
   517
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
icculus@2271
   518
    impl->WaitDone = PULSEAUDIO_WaitDone;
icculus@2271
   519
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
icculus@2271
   520
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2271
   521
icculus@3699
   522
    return 1;   /* this audio target is available. */
icculus@2271
   523
}
icculus@2271
   524
icculus@2271
   525
icculus@2271
   526
AudioBootStrap PULSEAUDIO_bootstrap = {
icculus@5594
   527
    "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
icculus@2271
   528
};
icculus@2271
   529
slouken@5481
   530
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
slouken@5481
   531
icculus@2271
   532
/* vi: set ts=4 sw=4 expandtab: */