SDL-1.2.14
[sdl_omap.git] / src / audio / pulse / SDL_pulseaudio.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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     Stéphan Kochen
20     stephan@kochen.nl
21
22     Based on parts of the ALSA and ESounD output drivers.
23 */
24 #include "SDL_config.h"
25
26 /* Allow access to an PulseAudio network stream mixing buffer */
27
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <pulse/pulseaudio.h>
33 #include <pulse/simple.h>
34
35 #include "SDL_timer.h"
36 #include "SDL_audio.h"
37 #include "../SDL_audiomem.h"
38 #include "../SDL_audio_c.h"
39 #include "../SDL_audiodev_c.h"
40 #include "SDL_pulseaudio.h"
41
42 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
43 #include "SDL_name.h"
44 #include "SDL_loadso.h"
45 #else
46 #define SDL_NAME(X)     X
47 #endif
48
49 /* The tag name used by the driver */
50 #define PULSE_DRIVER_NAME       "pulse"
51
52 /* Audio driver functions */
53 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec);
54 static void PULSE_WaitAudio(_THIS);
55 static void PULSE_PlayAudio(_THIS);
56 static Uint8 *PULSE_GetAudioBuf(_THIS);
57 static void PULSE_CloseAudio(_THIS);
58 static void PULSE_WaitDone(_THIS);
59
60 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
61
62 static const char *pulse_library = SDL_AUDIO_DRIVER_PULSE_DYNAMIC;
63 static void *pulse_handle = NULL;
64 static int pulse_loaded = 0;
65
66 static pa_simple* (*SDL_NAME(pa_simple_new))(
67         const char *server,
68         const char *name,
69         pa_stream_direction_t dir,
70         const char *dev,
71         const char *stream_name,
72         const pa_sample_spec *ss,
73         const pa_channel_map *map,
74         const pa_buffer_attr *attr,
75         int *error
76 );
77 static void (*SDL_NAME(pa_simple_free))(pa_simple *s);
78
79 static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))(
80         pa_channel_map *m,
81         unsigned channels,
82         pa_channel_map_def_t def
83 );
84
85 pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void);
86 pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m);
87 int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval);
88 void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m);
89
90 pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o);
91 void (*SDL_NAME(pa_operation_cancel))(pa_operation *o);
92 void (*SDL_NAME(pa_operation_unref))(pa_operation *o);
93
94 pa_context * (*SDL_NAME(pa_context_new))(
95         pa_mainloop_api *m, const char *name);
96 int (*SDL_NAME(pa_context_connect))(
97         pa_context *c, const char *server,
98         pa_context_flags_t flags, const pa_spawn_api *api);
99 pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c);
100 void (*SDL_NAME(pa_context_disconnect))(pa_context *c);
101 void (*SDL_NAME(pa_context_unref))(pa_context *c);
102
103 pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c,
104         const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
105 int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev,
106         const pa_buffer_attr *attr, pa_stream_flags_t flags,
107         pa_cvolume *volume, pa_stream *sync_stream);
108 pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s);
109 size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s);
110 int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes,
111         pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
112 pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s,
113         pa_stream_success_cb_t cb, void *userdata);
114 int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s);
115 void (*SDL_NAME(pa_stream_unref))(pa_stream *s);
116
117 static struct {
118         const char *name;
119         void **func;
120 } pulse_functions[] = {
121         { "pa_simple_new",
122                 (void **)&SDL_NAME(pa_simple_new)               },
123         { "pa_simple_free",
124                 (void **)&SDL_NAME(pa_simple_free)              },
125         { "pa_channel_map_init_auto",
126                 (void **)&SDL_NAME(pa_channel_map_init_auto)    },
127         { "pa_mainloop_new",
128                 (void **)&SDL_NAME(pa_mainloop_new)             },
129         { "pa_mainloop_get_api",
130                 (void **)&SDL_NAME(pa_mainloop_get_api)         },
131         { "pa_mainloop_iterate",
132                 (void **)&SDL_NAME(pa_mainloop_iterate)         },
133         { "pa_mainloop_free",
134                 (void **)&SDL_NAME(pa_mainloop_free)            },
135         { "pa_operation_get_state",
136                 (void **)&SDL_NAME(pa_operation_get_state)      },
137         { "pa_operation_cancel",
138                 (void **)&SDL_NAME(pa_operation_cancel)         },
139         { "pa_operation_unref",
140                 (void **)&SDL_NAME(pa_operation_unref)          },
141         { "pa_context_new",
142                 (void **)&SDL_NAME(pa_context_new)              },
143         { "pa_context_connect",
144                 (void **)&SDL_NAME(pa_context_connect)          },
145         { "pa_context_get_state",
146                 (void **)&SDL_NAME(pa_context_get_state)        },
147         { "pa_context_disconnect",
148                 (void **)&SDL_NAME(pa_context_disconnect)       },
149         { "pa_context_unref",
150                 (void **)&SDL_NAME(pa_context_unref)            },
151         { "pa_stream_new",
152                 (void **)&SDL_NAME(pa_stream_new)               },
153         { "pa_stream_connect_playback",
154                 (void **)&SDL_NAME(pa_stream_connect_playback)  },
155         { "pa_stream_get_state",
156                 (void **)&SDL_NAME(pa_stream_get_state)         },
157         { "pa_stream_writable_size",
158                 (void **)&SDL_NAME(pa_stream_writable_size)     },
159         { "pa_stream_write",
160                 (void **)&SDL_NAME(pa_stream_write)             },
161         { "pa_stream_drain",
162                 (void **)&SDL_NAME(pa_stream_drain)             },
163         { "pa_stream_disconnect",
164                 (void **)&SDL_NAME(pa_stream_disconnect)        },
165         { "pa_stream_unref",
166                 (void **)&SDL_NAME(pa_stream_unref)             },
167 };
168
169 static void UnloadPulseLibrary()
170 {
171         if ( pulse_loaded ) {
172                 SDL_UnloadObject(pulse_handle);
173                 pulse_handle = NULL;
174                 pulse_loaded = 0;
175         }
176 }
177
178 static int LoadPulseLibrary(void)
179 {
180         int i, retval = -1;
181
182         pulse_handle = SDL_LoadObject(pulse_library);
183         if ( pulse_handle ) {
184                 pulse_loaded = 1;
185                 retval = 0;
186                 for ( i=0; i<SDL_arraysize(pulse_functions); ++i ) {
187                         *pulse_functions[i].func = SDL_LoadFunction(pulse_handle, pulse_functions[i].name);
188                         if ( !*pulse_functions[i].func ) {
189                                 retval = -1;
190                                 UnloadPulseLibrary();
191                                 break;
192                         }
193                 }
194         }
195         return retval;
196 }
197
198 #else
199
200 static void UnloadPulseLibrary()
201 {
202         return;
203 }
204
205 static int LoadPulseLibrary(void)
206 {
207         return 0;
208 }
209
210 #endif /* SDL_AUDIO_DRIVER_PULSE_DYNAMIC */
211
212 /* Audio driver bootstrap functions */
213
214 static int Audio_Available(void)
215 {
216         pa_sample_spec paspec;
217         pa_simple *connection;
218         int available;
219
220         available = 0;
221         if ( LoadPulseLibrary() < 0 ) {
222                 return available;
223         }
224
225         /* Connect with a dummy format. */
226         paspec.format = PA_SAMPLE_U8;
227         paspec.rate = 11025;
228         paspec.channels = 1;
229         connection = SDL_NAME(pa_simple_new)(
230                 NULL,                        /* server */
231                 "Test stream",               /* application name */
232                 PA_STREAM_PLAYBACK,          /* playback mode */
233                 NULL,                        /* device on the server */
234                 "Simple DirectMedia Layer",  /* stream description */
235                 &paspec,                     /* sample format spec */
236                 NULL,                        /* channel map */
237                 NULL,                        /* buffering attributes */
238                 NULL                         /* error code */
239         );
240         if ( connection != NULL ) {
241                 available = 1;
242                 SDL_NAME(pa_simple_free)(connection);
243         }
244
245         UnloadPulseLibrary();
246         return(available);
247 }
248
249 static void Audio_DeleteDevice(SDL_AudioDevice *device)
250 {
251         SDL_free(device->hidden);
252         SDL_free(device);
253         UnloadPulseLibrary();
254 }
255
256 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
257 {
258         SDL_AudioDevice *this;
259
260         /* Initialize all variables that we clean on shutdown */
261         LoadPulseLibrary();
262         this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
263         if ( this ) {
264                 SDL_memset(this, 0, (sizeof *this));
265                 this->hidden = (struct SDL_PrivateAudioData *)
266                                 SDL_malloc((sizeof *this->hidden));
267         }
268         if ( (this == NULL) || (this->hidden == NULL) ) {
269                 SDL_OutOfMemory();
270                 if ( this ) {
271                         SDL_free(this);
272                 }
273                 return(0);
274         }
275         SDL_memset(this->hidden, 0, (sizeof *this->hidden));
276
277         /* Set the function pointers */
278         this->OpenAudio = PULSE_OpenAudio;
279         this->WaitAudio = PULSE_WaitAudio;
280         this->PlayAudio = PULSE_PlayAudio;
281         this->GetAudioBuf = PULSE_GetAudioBuf;
282         this->CloseAudio = PULSE_CloseAudio;
283         this->WaitDone = PULSE_WaitDone;
284
285         this->free = Audio_DeleteDevice;
286
287         return this;
288 }
289
290 AudioBootStrap PULSE_bootstrap = {
291         PULSE_DRIVER_NAME, "PulseAudio",
292         Audio_Available, Audio_CreateDevice
293 };
294
295 /* This function waits until it is possible to write a full sound buffer */
296 static void PULSE_WaitAudio(_THIS)
297 {
298         int size;
299         while(1) {
300                 if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
301                     SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
302                     SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
303                         this->enabled = 0;
304                         return;
305                 }
306                 size = SDL_NAME(pa_stream_writable_size)(stream);
307                 if (size >= mixlen)
308                         return;
309         }
310 }
311
312 static void PULSE_PlayAudio(_THIS)
313 {
314         /* Write the audio data */
315         if (SDL_NAME(pa_stream_write)(stream, mixbuf, mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
316                 this->enabled = 0;
317 }
318
319 static Uint8 *PULSE_GetAudioBuf(_THIS)
320 {
321         return(mixbuf);
322 }
323
324 static void PULSE_CloseAudio(_THIS)
325 {
326         if ( mixbuf != NULL ) {
327                 SDL_FreeAudioMem(mixbuf);
328                 mixbuf = NULL;
329         }
330         if ( stream != NULL ) {
331                 SDL_NAME(pa_stream_disconnect)(stream);
332                 SDL_NAME(pa_stream_unref)(stream);
333                 stream = NULL;
334         }
335         if (context != NULL) {
336                 SDL_NAME(pa_context_disconnect)(context);
337                 SDL_NAME(pa_context_unref)(context);
338                 context = NULL;
339         }
340         if (mainloop != NULL) {
341                 SDL_NAME(pa_mainloop_free)(mainloop);
342                 mainloop = NULL;
343         }
344 }
345
346 /* Try to get the name of the program */
347 static char *get_progname(void)
348 {
349 #ifdef __LINUX__
350         char *progname = NULL;
351         FILE *fp;
352         static char temp[BUFSIZ];
353
354         SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
355         fp = fopen(temp, "r");
356         if ( fp != NULL ) {
357                 if ( fgets(temp, sizeof(temp)-1, fp) ) {
358                         progname = SDL_strrchr(temp, '/');
359                         if ( progname == NULL ) {
360                                 progname = temp;
361                         } else {
362                                 progname = progname+1;
363                         }
364                 }
365                 fclose(fp);
366         }
367         return(progname);
368 #elif defined(__NetBSD__)
369         return getprogname();
370 #else
371         return("unknown");
372 #endif
373 }
374
375 static void stream_drain_complete(pa_stream *s, int success, void *userdata) {
376 }
377
378 static void PULSE_WaitDone(_THIS)
379 {
380         pa_operation *o;
381
382         o = SDL_NAME(pa_stream_drain)(stream, stream_drain_complete, NULL);
383         if (!o)
384                 return;
385
386         while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) {
387                 if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
388                     SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
389                     SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
390                         SDL_NAME(pa_operation_cancel)(o);
391                         break;
392                 }
393         }
394         SDL_NAME(pa_operation_unref)(o);
395 }
396
397 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec)
398 {
399         int             state;
400         Uint16          test_format;
401         pa_sample_spec  paspec;
402         pa_buffer_attr  paattr;
403         pa_channel_map  pacmap;
404         pa_stream_flags_t flags = 0;
405
406         paspec.format = PA_SAMPLE_INVALID;
407         for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) {
408                 switch ( test_format ) {
409                         case AUDIO_U8:
410                                 paspec.format = PA_SAMPLE_U8;
411                                 break;
412                         case AUDIO_S16LSB:
413                                 paspec.format = PA_SAMPLE_S16LE;
414                                 break;
415                         case AUDIO_S16MSB:
416                                 paspec.format = PA_SAMPLE_S16BE;
417                                 break;
418                 }
419                 if ( paspec.format != PA_SAMPLE_INVALID )
420                         break;
421         }
422         if (paspec.format == PA_SAMPLE_INVALID ) {
423                 SDL_SetError("Couldn't find any suitable audio formats");
424                 return(-1);
425         }
426         spec->format = test_format;
427
428         paspec.channels = spec->channels;
429         paspec.rate = spec->freq;
430
431         /* Calculate the final parameters for this audio specification */
432 #ifdef PA_STREAM_ADJUST_LATENCY
433         spec->samples /= 2; /* Mix in smaller chunck to avoid underruns */
434 #endif
435         SDL_CalculateAudioSpec(spec);
436
437         /* Allocate mixing buffer */
438         mixlen = spec->size;
439         mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
440         if ( mixbuf == NULL ) {
441                 return(-1);
442         }
443         SDL_memset(mixbuf, spec->silence, spec->size);
444
445         /* Reduced prebuffering compared to the defaults. */
446 #ifdef PA_STREAM_ADJUST_LATENCY
447         paattr.tlength = mixlen * 4; /* 2x original requested bufsize */
448         paattr.prebuf = -1;
449         paattr.maxlength = -1;
450         paattr.minreq = mixlen; /* -1 can lead to pa_stream_writable_size()
451                                    >= mixlen never becoming true */
452         flags = PA_STREAM_ADJUST_LATENCY;
453 #else
454         paattr.tlength = mixlen*2;
455         paattr.prebuf = mixlen*2;
456         paattr.maxlength = mixlen*2;
457         paattr.minreq = mixlen;
458 #endif
459
460         /* The SDL ALSA output hints us that we use Windows' channel mapping */
461         /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
462         SDL_NAME(pa_channel_map_init_auto)(
463                 &pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX);
464
465         /* Set up a new main loop */
466         if (!(mainloop = SDL_NAME(pa_mainloop_new)())) {
467                 PULSE_CloseAudio(this);
468                 SDL_SetError("pa_mainloop_new() failed");
469                 return(-1);
470         }
471
472         mainloop_api = SDL_NAME(pa_mainloop_get_api)(mainloop);
473         if (!(context = SDL_NAME(pa_context_new)(mainloop_api, get_progname()))) {
474                 PULSE_CloseAudio(this);
475                 SDL_SetError("pa_context_new() failed");
476                 return(-1);
477         }
478
479         /* Connect to the PulseAudio server */
480         if (SDL_NAME(pa_context_connect)(context, NULL, 0, NULL) < 0) {
481                 PULSE_CloseAudio(this);
482                 SDL_SetError("Could not setup connection to PulseAudio");
483                 return(-1);
484         }
485
486         do {
487                 if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
488                         PULSE_CloseAudio(this);
489                         SDL_SetError("pa_mainloop_iterate() failed");
490                         return(-1);
491                 }
492                 state = SDL_NAME(pa_context_get_state)(context);
493                 if (!PA_CONTEXT_IS_GOOD(state)) {
494                         PULSE_CloseAudio(this);
495                         SDL_SetError("Could not connect to PulseAudio");
496                         return(-1);
497                 }
498         } while (state != PA_CONTEXT_READY);
499
500         stream = SDL_NAME(pa_stream_new)(
501                 context,
502                 "Simple DirectMedia Layer",  /* stream description */
503                 &paspec,                     /* sample format spec */
504                 &pacmap                      /* channel map */
505         );
506         if ( stream == NULL ) {
507                 PULSE_CloseAudio(this);
508                 SDL_SetError("Could not setup PulseAudio stream");
509                 return(-1);
510         }
511
512         if (SDL_NAME(pa_stream_connect_playback)(stream, NULL, &paattr, flags,
513                         NULL, NULL) < 0) {
514                 PULSE_CloseAudio(this);
515                 SDL_SetError("Could not connect PulseAudio stream");
516                 return(-1);
517         }
518
519         do {
520                 if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
521                         PULSE_CloseAudio(this);
522                         SDL_SetError("pa_mainloop_iterate() failed");
523                         return(-1);
524                 }
525                 state = SDL_NAME(pa_stream_get_state)(stream);
526                 if (!PA_STREAM_IS_GOOD(state)) {
527                         PULSE_CloseAudio(this);
528                         SDL_SetError("Could not create to PulseAudio stream");
529                         return(-1);
530                 }
531         } while (state != PA_STREAM_READY);
532
533         return(0);
534 }