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