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