src/audio/pulseaudio/SDL_pulseaudio.c
author Sam Lantinga
Thu, 16 May 2013 00:43:22 -0700
changeset 7180 3733e68edbc3
parent 7038 7f22b9ba218f
child 7191 75360622e65f
permissions -rw-r--r--
Fixed bug 1846 - _allmul implementation in SDL_stdlib.c doesn't clean up the stack

Colin Barrett

I see this manifest itself (VS2012 x86) as:

"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."

in the first call to SDL_GetTicks in my application. The disassembly at the problem line is:

hires_now.QuadPart *= 1000;
00AD0792 push 0
00AD0794 push 3E8h
00AD0799 mov eax,dword ptr [ebp-10h]
00AD079C push eax
00AD079D mov ecx,dword ptr [hires_now]
00AD07A0 push ecx
00AD07A1 call _allmul (0AE7D40h)
00AD07A6 mov dword ptr [hires_now],eax
00AD07A9 mov dword ptr [ebp-10h],edx

Apparently _allmul should be popping the stack but isn't (other similar functions in SDL_stdlib.c - _alldiv and whatnot - DO pop the stack).

A 'ret 10h' at the end of _allmul appears to do the trick
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@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@2274
   363
        default:
slouken@2274
   364
            paspec.format = PA_SAMPLE_INVALID;
slouken@2274
   365
            break;
icculus@2271
   366
        }
icculus@2271
   367
        if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   368
            test_format = SDL_NextAudioFormat();
icculus@2271
   369
        }
icculus@2271
   370
    }
icculus@2271
   371
    if (paspec.format == PA_SAMPLE_INVALID) {
icculus@2271
   372
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   373
        return SDL_SetError("Couldn't find any hardware audio formats");
icculus@2271
   374
    }
icculus@2271
   375
    this->spec.format = test_format;
icculus@2271
   376
icculus@2271
   377
    /* Calculate the final parameters for this audio specification */
icculus@3672
   378
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3672
   379
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
icculus@3672
   380
#endif
icculus@2271
   381
    SDL_CalculateAudioSpec(&this->spec);
icculus@2271
   382
icculus@2271
   383
    /* Allocate mixing buffer */
icculus@3674
   384
    h->mixlen = this->spec.size;
icculus@3674
   385
    h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
icculus@3674
   386
    if (h->mixbuf == NULL) {
icculus@2271
   387
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   388
        return SDL_OutOfMemory();
icculus@2271
   389
    }
icculus@3674
   390
    SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
icculus@2271
   391
icculus@2271
   392
    paspec.channels = this->spec.channels;
icculus@2271
   393
    paspec.rate = this->spec.freq;
icculus@2271
   394
icculus@2271
   395
    /* Reduced prebuffering compared to the defaults. */
icculus@3672
   396
#ifdef PA_STREAM_ADJUST_LATENCY
icculus@3674
   397
    /* 2x original requested bufsize */
icculus@3674
   398
    paattr.tlength = h->mixlen * 4;
icculus@3672
   399
    paattr.prebuf = -1;
icculus@3672
   400
    paattr.maxlength = -1;
icculus@3674
   401
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
icculus@3674
   402
    paattr.minreq = h->mixlen;
icculus@3672
   403
    flags = PA_STREAM_ADJUST_LATENCY;
icculus@3672
   404
#else
icculus@3674
   405
    paattr.tlength = h->mixlen*2;
icculus@3674
   406
    paattr.prebuf = h->mixlen*2;
icculus@3674
   407
    paattr.maxlength = h->mixlen*2;
icculus@3674
   408
    paattr.minreq = h->mixlen;
icculus@3672
   409
#endif
icculus@2271
   410
icculus@2271
   411
    /* The SDL ALSA output hints us that we use Windows' channel mapping */
icculus@2271
   412
    /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
icculus@3673
   413
    PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
slouken@2274
   414
                                        PA_CHANNEL_MAP_WAVEEX);
icculus@2271
   415
icculus@3672
   416
    /* Set up a new main loop */
icculus@3674
   417
    if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
icculus@3672
   418
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   419
        return SDL_SetError("pa_mainloop_new() failed");
icculus@3672
   420
    }
icculus@3672
   421
icculus@3674
   422
    h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
slouken@5080
   423
    h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, NULL);
icculus@3674
   424
    if (!h->context) {
icculus@3672
   425
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   426
        return SDL_SetError("pa_context_new() failed");
icculus@3672
   427
    }
icculus@3672
   428
icculus@2271
   429
    /* Connect to the PulseAudio server */
icculus@3674
   430
    if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
icculus@3672
   431
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   432
        return SDL_SetError("Could not setup connection to PulseAudio");
icculus@3672
   433
    }
icculus@3672
   434
icculus@3672
   435
    do {
icculus@3674
   436
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   437
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   438
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   439
        }
icculus@3674
   440
        state = PULSEAUDIO_pa_context_get_state(h->context);
icculus@3672
   441
        if (!PA_CONTEXT_IS_GOOD(state)) {
icculus@3672
   442
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   443
            return SDL_SetError("Could not connect to PulseAudio");
icculus@3672
   444
        }
icculus@3672
   445
    } while (state != PA_CONTEXT_READY);
icculus@3672
   446
icculus@3674
   447
    h->stream = PULSEAUDIO_pa_stream_new(
icculus@3674
   448
        h->context,
icculus@3672
   449
        "Simple DirectMedia Layer", /* stream description */
icculus@3672
   450
        &paspec,    /* sample format spec */
icculus@3672
   451
        &pacmap     /* channel map */
slouken@2274
   452
        );
icculus@2272
   453
icculus@3674
   454
    if (h->stream == NULL) {
icculus@2271
   455
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   456
        return SDL_SetError("Could not set up PulseAudio stream");
icculus@3672
   457
    }
icculus@3672
   458
icculus@3674
   459
    if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
icculus@3672
   460
            NULL, NULL) < 0) {
icculus@3672
   461
        PULSEAUDIO_CloseDevice(this);
icculus@7038
   462
        return SDL_SetError("Could not connect PulseAudio stream");
icculus@2271
   463
    }
icculus@2271
   464
icculus@3672
   465
    do {
icculus@3674
   466
        if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
icculus@3672
   467
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   468
            return SDL_SetError("pa_mainloop_iterate() failed");
icculus@3672
   469
        }
icculus@3674
   470
        state = PULSEAUDIO_pa_stream_get_state(h->stream);
icculus@3672
   471
        if (!PA_STREAM_IS_GOOD(state)) {
icculus@3672
   472
            PULSEAUDIO_CloseDevice(this);
icculus@7038
   473
            return SDL_SetError("Could not create to PulseAudio stream");
icculus@3672
   474
        }
icculus@3672
   475
    } while (state != PA_STREAM_READY);
icculus@2271
   476
icculus@2271
   477
    /* We're ready to rock and roll. :-) */
icculus@7038
   478
    return 0;
icculus@2271
   479
}
icculus@2271
   480
icculus@2271
   481
icculus@2271
   482
static void
icculus@2271
   483
PULSEAUDIO_Deinitialize(void)
icculus@2271
   484
{
icculus@3673
   485
    UnloadPulseAudioLibrary();
icculus@2271
   486
}
icculus@2271
   487
icculus@2271
   488
static int
icculus@2271
   489
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2271
   490
{
icculus@3673
   491
    if (LoadPulseAudioLibrary() < 0) {
icculus@2271
   492
        return 0;
icculus@2271
   493
    }
icculus@2271
   494
slouken@6933
   495
    if (!CheckPulseAudioAvailable()) {
slouken@6934
   496
        UnloadPulseAudioLibrary();
slouken@6933
   497
        return 0;
slouken@6933
   498
    }
slouken@6933
   499
icculus@2271
   500
    /* Set the function pointers */
icculus@2271
   501
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
icculus@2271
   502
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
icculus@2271
   503
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
icculus@2271
   504
    impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
icculus@2271
   505
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
icculus@2271
   506
    impl->WaitDone = PULSEAUDIO_WaitDone;
icculus@2271
   507
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
icculus@2271
   508
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2271
   509
icculus@3699
   510
    return 1;   /* this audio target is available. */
icculus@2271
   511
}
icculus@2271
   512
icculus@2271
   513
icculus@2271
   514
AudioBootStrap PULSEAUDIO_bootstrap = {
icculus@5594
   515
    "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
icculus@2271
   516
};
icculus@2271
   517
slouken@5481
   518
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
slouken@5481
   519
icculus@2271
   520
/* vi: set ts=4 sw=4 expandtab: */