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