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
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 /*
    23   The PulseAudio target for SDL 1.3 is based on the 1.3 arts target, with
    24    the appropriate parts replaced with the 1.2 PulseAudio target code. This
    25    was the cleanest way to move it to 1.3. The 1.2 target was written by
    26    St├ęphan Kochen: stephan .a.t. kochen.nl
    27 */
    28 #include "SDL_config.h"
    29 
    30 #if SDL_AUDIO_DRIVER_PULSEAUDIO
    31 
    32 /* Allow access to a raw mixing buffer */
    33 
    34 #ifdef HAVE_SIGNAL_H
    35 #include <signal.h>
    36 #endif
    37 #include <unistd.h>
    38 #include <sys/types.h>
    39 #include <errno.h>
    40 #include <pulse/pulseaudio.h>
    41 #include <pulse/simple.h>
    42 
    43 #include "SDL_timer.h"
    44 #include "SDL_audio.h"
    45 #include "../SDL_audiomem.h"
    46 #include "../SDL_audio_c.h"
    47 #include "SDL_pulseaudio.h"
    48 #include "SDL_loadso.h"
    49 
    50 #if (PA_API_VERSION < 12)
    51 /** Return non-zero if the passed state is one of the connected states */
    52 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
    53     return
    54         x == PA_CONTEXT_CONNECTING ||
    55         x == PA_CONTEXT_AUTHORIZING ||
    56         x == PA_CONTEXT_SETTING_NAME ||
    57         x == PA_CONTEXT_READY;
    58 }
    59 /** Return non-zero if the passed state is one of the connected states */
    60 static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
    61     return
    62         x == PA_STREAM_CREATING ||
    63         x == PA_STREAM_READY;
    64 }
    65 #endif /* pulseaudio <= 0.9.10 */
    66 
    67 
    68 static pa_simple *(*PULSEAUDIO_pa_simple_new) (const char *, const char *,
    69     pa_stream_direction_t, const char *, const char *, const pa_sample_spec *,
    70     const pa_channel_map *, const pa_buffer_attr *, int *);
    71 static void (*PULSEAUDIO_pa_simple_free) (pa_simple *);
    72 static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) (
    73     pa_channel_map *, unsigned, pa_channel_map_def_t);
    74 static const char * (*PULSEAUDIO_pa_strerror) (int);
    75 static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void);
    76 static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *);
    77 static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *);
    78 static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *);
    79 
    80 static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
    81     pa_operation *);
    82 static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
    83 static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
    84 
    85 static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
    86     const char *);
    87 static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *,
    88     pa_context_flags_t, const pa_spawn_api *);
    89 static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
    90 static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *);
    91 static void (*PULSEAUDIO_pa_context_unref) (pa_context *);
    92 
    93 static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
    94     const pa_sample_spec *, const pa_channel_map *);
    95 static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
    96     const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
    97 static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
    98 static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
    99 static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
   100     pa_free_cb_t, int64_t, pa_seek_mode_t);
   101 static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
   102     pa_stream_success_cb_t, void *);
   103 static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
   104 static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
   105 
   106 static int load_pulseaudio_syms(void);
   107 
   108 
   109 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
   110 
   111 static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
   112 static void *pulseaudio_handle = NULL;
   113 
   114 static int
   115 load_pulseaudio_sym(const char *fn, void **addr)
   116 {
   117     *addr = SDL_LoadFunction(pulseaudio_handle, fn);
   118     if (*addr == NULL) {
   119         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
   120         return 0;
   121     }
   122 
   123     return 1;
   124 }
   125 
   126 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
   127 #define SDL_PULSEAUDIO_SYM(x) \
   128     if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
   129 
   130 static void
   131 UnloadPulseAudioLibrary(void)
   132 {
   133     if (pulseaudio_handle != NULL) {
   134 		SDL_UnloadObject(pulseaudio_handle);
   135         pulseaudio_handle = NULL;
   136     }
   137 }
   138 
   139 static int
   140 LoadPulseAudioLibrary(void)
   141 {
   142     int retval = 0;
   143     if (pulseaudio_handle == NULL) {
   144         pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
   145         if (pulseaudio_handle == NULL) {
   146             retval = -1;
   147             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   148         } else {
   149             retval = load_pulseaudio_syms();
   150             if (retval < 0) {
   151                 UnloadPulseAudioLibrary();
   152             }
   153         }
   154     }
   155     return retval;
   156 }
   157 
   158 #else
   159 
   160 #define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
   161 
   162 static void
   163 UnloadPulseAudioLibrary(void)
   164 {
   165 }
   166 
   167 static int
   168 LoadPulseAudioLibrary(void)
   169 {
   170     load_pulseaudio_syms();
   171     return 0;
   172 }
   173 
   174 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
   175 
   176 
   177 static int
   178 load_pulseaudio_syms(void)
   179 {
   180     SDL_PULSEAUDIO_SYM(pa_simple_new);
   181     SDL_PULSEAUDIO_SYM(pa_simple_free);
   182     SDL_PULSEAUDIO_SYM(pa_mainloop_new);
   183     SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
   184     SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
   185     SDL_PULSEAUDIO_SYM(pa_mainloop_free);
   186     SDL_PULSEAUDIO_SYM(pa_operation_get_state);
   187     SDL_PULSEAUDIO_SYM(pa_operation_cancel);
   188     SDL_PULSEAUDIO_SYM(pa_operation_unref);
   189     SDL_PULSEAUDIO_SYM(pa_context_new);
   190     SDL_PULSEAUDIO_SYM(pa_context_connect);
   191     SDL_PULSEAUDIO_SYM(pa_context_get_state);
   192     SDL_PULSEAUDIO_SYM(pa_context_disconnect);
   193     SDL_PULSEAUDIO_SYM(pa_context_unref);
   194     SDL_PULSEAUDIO_SYM(pa_stream_new);
   195     SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
   196     SDL_PULSEAUDIO_SYM(pa_stream_get_state);
   197     SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
   198     SDL_PULSEAUDIO_SYM(pa_stream_write);
   199     SDL_PULSEAUDIO_SYM(pa_stream_drain);
   200     SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
   201     SDL_PULSEAUDIO_SYM(pa_stream_unref);
   202     SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
   203     SDL_PULSEAUDIO_SYM(pa_strerror);
   204     return 0;
   205 }
   206 
   207 
   208 /* Check to see if we can connect to PulseAudio */
   209 static SDL_bool
   210 CheckPulseAudioAvailable()
   211 {
   212     pa_simple *s;
   213     pa_sample_spec ss;
   214 
   215     ss.format = PA_SAMPLE_S16NE;
   216     ss.channels = 1;
   217     ss.rate = 22050;
   218 
   219     s = PULSEAUDIO_pa_simple_new(NULL, "SDL", PA_STREAM_PLAYBACK, NULL,
   220                                  "Test", &ss, NULL, NULL, NULL);
   221     if (s) {
   222         PULSEAUDIO_pa_simple_free(s);
   223         return SDL_TRUE;
   224     } else {
   225         return SDL_FALSE;
   226     }
   227 }
   228 
   229 /* This function waits until it is possible to write a full sound buffer */
   230 static void
   231 PULSEAUDIO_WaitDevice(_THIS)
   232 {
   233     struct SDL_PrivateAudioData *h = this->hidden;
   234 
   235     while(1) {
   236         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   237             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   238             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   239             this->enabled = 0;
   240             return;
   241         }
   242         if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
   243             return;
   244         }
   245     }
   246 }
   247 
   248 static void
   249 PULSEAUDIO_PlayDevice(_THIS)
   250 {
   251     /* Write the audio data */
   252     struct SDL_PrivateAudioData *h = this->hidden;
   253     if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
   254                                    PA_SEEK_RELATIVE) < 0) {
   255         this->enabled = 0;
   256     }
   257 }
   258 
   259 static void
   260 stream_drain_complete(pa_stream *s, int success, void *userdata)
   261 {
   262     /* no-op for pa_stream_drain() to use for callback. */
   263 }
   264 
   265 static void
   266 PULSEAUDIO_WaitDone(_THIS)
   267 {
   268     struct SDL_PrivateAudioData *h = this->hidden;
   269     pa_operation *o;
   270 
   271     o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL);
   272     if (!o) {
   273         return;
   274     }
   275 
   276     while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
   277         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   278             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   279             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   280             PULSEAUDIO_pa_operation_cancel(o);
   281             break;
   282         }
   283     }
   284 
   285     PULSEAUDIO_pa_operation_unref(o);
   286 }
   287 
   288 
   289 
   290 static Uint8 *
   291 PULSEAUDIO_GetDeviceBuf(_THIS)
   292 {
   293     return (this->hidden->mixbuf);
   294 }
   295 
   296 
   297 static void
   298 PULSEAUDIO_CloseDevice(_THIS)
   299 {
   300     if (this->hidden != NULL) {
   301         if (this->hidden->mixbuf != NULL) {
   302             SDL_FreeAudioMem(this->hidden->mixbuf);
   303             this->hidden->mixbuf = NULL;
   304         }
   305         if (this->hidden->stream) {
   306             PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
   307             PULSEAUDIO_pa_stream_unref(this->hidden->stream);
   308             this->hidden->stream = NULL;
   309         }
   310         if (this->hidden->context != NULL) {
   311             PULSEAUDIO_pa_context_disconnect(this->hidden->context);
   312             PULSEAUDIO_pa_context_unref(this->hidden->context);
   313             this->hidden->context = NULL;
   314         }
   315         if (this->hidden->mainloop != NULL) {
   316             PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop);
   317             this->hidden->mainloop = NULL;
   318         }
   319         SDL_free(this->hidden);
   320         this->hidden = NULL;
   321     }
   322 }
   323 
   324 
   325 static int
   326 PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
   327 {
   328     struct SDL_PrivateAudioData *h = NULL;
   329     Uint16 test_format = 0;
   330     pa_sample_spec paspec;
   331     pa_buffer_attr paattr;
   332     pa_channel_map pacmap;
   333     pa_stream_flags_t flags = 0;
   334     int state = 0;
   335 
   336     /* Initialize all variables that we clean on shutdown */
   337     this->hidden = (struct SDL_PrivateAudioData *)
   338         SDL_malloc((sizeof *this->hidden));
   339     if (this->hidden == NULL) {
   340         return SDL_OutOfMemory();
   341     }
   342     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   343     h = this->hidden;
   344 
   345     paspec.format = PA_SAMPLE_INVALID;
   346 
   347     /* Try for a closest match on audio format */
   348     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   349          (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
   350 #ifdef DEBUG_AUDIO
   351         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   352 #endif
   353         switch (test_format) {
   354         case AUDIO_U8:
   355             paspec.format = PA_SAMPLE_U8;
   356             break;
   357         case AUDIO_S16LSB:
   358             paspec.format = PA_SAMPLE_S16LE;
   359             break;
   360         case AUDIO_S16MSB:
   361             paspec.format = PA_SAMPLE_S16BE;
   362             break;
   363         default:
   364             paspec.format = PA_SAMPLE_INVALID;
   365             break;
   366         }
   367         if (paspec.format == PA_SAMPLE_INVALID) {
   368             test_format = SDL_NextAudioFormat();
   369         }
   370     }
   371     if (paspec.format == PA_SAMPLE_INVALID) {
   372         PULSEAUDIO_CloseDevice(this);
   373         return SDL_SetError("Couldn't find any hardware audio formats");
   374     }
   375     this->spec.format = test_format;
   376 
   377     /* Calculate the final parameters for this audio specification */
   378 #ifdef PA_STREAM_ADJUST_LATENCY
   379     this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
   380 #endif
   381     SDL_CalculateAudioSpec(&this->spec);
   382 
   383     /* Allocate mixing buffer */
   384     h->mixlen = this->spec.size;
   385     h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
   386     if (h->mixbuf == NULL) {
   387         PULSEAUDIO_CloseDevice(this);
   388         return SDL_OutOfMemory();
   389     }
   390     SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
   391 
   392     paspec.channels = this->spec.channels;
   393     paspec.rate = this->spec.freq;
   394 
   395     /* Reduced prebuffering compared to the defaults. */
   396 #ifdef PA_STREAM_ADJUST_LATENCY
   397     /* 2x original requested bufsize */
   398     paattr.tlength = h->mixlen * 4;
   399     paattr.prebuf = -1;
   400     paattr.maxlength = -1;
   401     /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
   402     paattr.minreq = h->mixlen;
   403     flags = PA_STREAM_ADJUST_LATENCY;
   404 #else
   405     paattr.tlength = h->mixlen*2;
   406     paattr.prebuf = h->mixlen*2;
   407     paattr.maxlength = h->mixlen*2;
   408     paattr.minreq = h->mixlen;
   409 #endif
   410 
   411     /* The SDL ALSA output hints us that we use Windows' channel mapping */
   412     /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
   413     PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
   414                                         PA_CHANNEL_MAP_WAVEEX);
   415 
   416     /* Set up a new main loop */
   417     if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
   418         PULSEAUDIO_CloseDevice(this);
   419         return SDL_SetError("pa_mainloop_new() failed");
   420     }
   421 
   422     h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
   423     h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, NULL);
   424     if (!h->context) {
   425         PULSEAUDIO_CloseDevice(this);
   426         return SDL_SetError("pa_context_new() failed");
   427     }
   428 
   429     /* Connect to the PulseAudio server */
   430     if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
   431         PULSEAUDIO_CloseDevice(this);
   432         return SDL_SetError("Could not setup connection to PulseAudio");
   433     }
   434 
   435     do {
   436         if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   437             PULSEAUDIO_CloseDevice(this);
   438             return SDL_SetError("pa_mainloop_iterate() failed");
   439         }
   440         state = PULSEAUDIO_pa_context_get_state(h->context);
   441         if (!PA_CONTEXT_IS_GOOD(state)) {
   442             PULSEAUDIO_CloseDevice(this);
   443             return SDL_SetError("Could not connect to PulseAudio");
   444         }
   445     } while (state != PA_CONTEXT_READY);
   446 
   447     h->stream = PULSEAUDIO_pa_stream_new(
   448         h->context,
   449         "Simple DirectMedia Layer", /* stream description */
   450         &paspec,    /* sample format spec */
   451         &pacmap     /* channel map */
   452         );
   453 
   454     if (h->stream == NULL) {
   455         PULSEAUDIO_CloseDevice(this);
   456         return SDL_SetError("Could not set up PulseAudio stream");
   457     }
   458 
   459     if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
   460             NULL, NULL) < 0) {
   461         PULSEAUDIO_CloseDevice(this);
   462         return SDL_SetError("Could not connect PulseAudio stream");
   463     }
   464 
   465     do {
   466         if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   467             PULSEAUDIO_CloseDevice(this);
   468             return SDL_SetError("pa_mainloop_iterate() failed");
   469         }
   470         state = PULSEAUDIO_pa_stream_get_state(h->stream);
   471         if (!PA_STREAM_IS_GOOD(state)) {
   472             PULSEAUDIO_CloseDevice(this);
   473             return SDL_SetError("Could not create to PulseAudio stream");
   474         }
   475     } while (state != PA_STREAM_READY);
   476 
   477     /* We're ready to rock and roll. :-) */
   478     return 0;
   479 }
   480 
   481 
   482 static void
   483 PULSEAUDIO_Deinitialize(void)
   484 {
   485     UnloadPulseAudioLibrary();
   486 }
   487 
   488 static int
   489 PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
   490 {
   491     if (LoadPulseAudioLibrary() < 0) {
   492         return 0;
   493     }
   494 
   495     if (!CheckPulseAudioAvailable()) {
   496         UnloadPulseAudioLibrary();
   497         return 0;
   498     }
   499 
   500     /* Set the function pointers */
   501     impl->OpenDevice = PULSEAUDIO_OpenDevice;
   502     impl->PlayDevice = PULSEAUDIO_PlayDevice;
   503     impl->WaitDevice = PULSEAUDIO_WaitDevice;
   504     impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
   505     impl->CloseDevice = PULSEAUDIO_CloseDevice;
   506     impl->WaitDone = PULSEAUDIO_WaitDone;
   507     impl->Deinitialize = PULSEAUDIO_Deinitialize;
   508     impl->OnlyHasDefaultOutputDevice = 1;
   509 
   510     return 1;   /* this audio target is available. */
   511 }
   512 
   513 
   514 AudioBootStrap PULSEAUDIO_bootstrap = {
   515     "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
   516 };
   517 
   518 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
   519 
   520 /* vi: set ts=4 sw=4 expandtab: */