src/audio/pulse/SDL_pulseaudio.c
branchSDL-1.2
changeset 4216 5b99971a27b4
parent 4211 3ce5bfddbaf6
child 4344 14f95e514408
     1.1 --- a/src/audio/pulse/SDL_pulseaudio.c	Mon Sep 21 09:18:42 2009 +0000
     1.2 +++ b/src/audio/pulse/SDL_pulseaudio.c	Mon Sep 21 09:27:08 2009 +0000
     1.3 @@ -1,4 +1,3 @@
     1.4 -/* -*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*- */
     1.5  /*
     1.6      SDL - Simple DirectMedia Layer
     1.7      Copyright (C) 1997-2009 Sam Lantinga
     1.8 @@ -30,6 +29,7 @@
     1.9  #include <unistd.h>
    1.10  #include <signal.h>
    1.11  #include <errno.h>
    1.12 +#include <pulse/pulseaudio.h>
    1.13  #include <pulse/simple.h>
    1.14  
    1.15  #include "SDL_timer.h"
    1.16 @@ -55,6 +55,7 @@
    1.17  static void PULSE_PlayAudio(_THIS);
    1.18  static Uint8 *PULSE_GetAudioBuf(_THIS);
    1.19  static void PULSE_CloseAudio(_THIS);
    1.20 +static void PULSE_WaitDone(_THIS);
    1.21  
    1.22  #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
    1.23  
    1.24 @@ -74,19 +75,44 @@
    1.25  	int *error
    1.26  );
    1.27  static void (*SDL_NAME(pa_simple_free))(pa_simple *s);
    1.28 -static int (*SDL_NAME(pa_simple_drain))(pa_simple *s, int *error);
    1.29 -static int (*SDL_NAME(pa_simple_write))(
    1.30 -	pa_simple *s,
    1.31 -	const void *data,
    1.32 -	size_t length,
    1.33 -	int *error
    1.34 -);
    1.35 +
    1.36  static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))(
    1.37  	pa_channel_map *m,
    1.38  	unsigned channels,
    1.39  	pa_channel_map_def_t def
    1.40  );
    1.41  
    1.42 +pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void);
    1.43 +pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m);
    1.44 +int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval);
    1.45 +void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m);
    1.46 +
    1.47 +pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o);
    1.48 +void (*SDL_NAME(pa_operation_cancel))(pa_operation *o);
    1.49 +void (*SDL_NAME(pa_operation_unref))(pa_operation *o);
    1.50 +
    1.51 +pa_context * (*SDL_NAME(pa_context_new))(
    1.52 +	pa_mainloop_api *m, const char *name);
    1.53 +int (*SDL_NAME(pa_context_connect))(
    1.54 +	pa_context *c, const char *server,
    1.55 +	pa_context_flags_t flags, const pa_spawn_api *api);
    1.56 +pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c);
    1.57 +void (*SDL_NAME(pa_context_disconnect))(pa_context *c);
    1.58 +void (*SDL_NAME(pa_context_unref))(pa_context *c);
    1.59 +
    1.60 +pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c,
    1.61 +	const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
    1.62 +int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev,
    1.63 +	const pa_buffer_attr *attr, pa_stream_flags_t flags,
    1.64 +	pa_cvolume *volume, pa_stream *sync_stream);
    1.65 +pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s);
    1.66 +size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s);
    1.67 +int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes,
    1.68 +	pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
    1.69 +pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s,
    1.70 +	pa_stream_success_cb_t cb, void *userdata);
    1.71 +int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s);
    1.72 +void (*SDL_NAME(pa_stream_unref))(pa_stream *s);
    1.73  
    1.74  static struct {
    1.75  	const char *name;
    1.76 @@ -96,12 +122,48 @@
    1.77  		(void **)&SDL_NAME(pa_simple_new)		},
    1.78  	{ "pa_simple_free",
    1.79  		(void **)&SDL_NAME(pa_simple_free)		},
    1.80 -	{ "pa_simple_drain",
    1.81 -		(void **)&SDL_NAME(pa_simple_drain)		},
    1.82 -	{ "pa_simple_write",
    1.83 -		(void **)&SDL_NAME(pa_simple_write)		},
    1.84  	{ "pa_channel_map_init_auto",
    1.85  		(void **)&SDL_NAME(pa_channel_map_init_auto)	},
    1.86 +	{ "pa_mainloop_new",
    1.87 +		(void **)&SDL_NAME(pa_mainloop_new)		},
    1.88 +	{ "pa_mainloop_get_api",
    1.89 +		(void **)&SDL_NAME(pa_mainloop_get_api)		},
    1.90 +	{ "pa_mainloop_iterate",
    1.91 +		(void **)&SDL_NAME(pa_mainloop_iterate)		},
    1.92 +	{ "pa_mainloop_free",
    1.93 +		(void **)&SDL_NAME(pa_mainloop_free)		},
    1.94 +	{ "pa_operation_get_state",
    1.95 +		(void **)&SDL_NAME(pa_operation_get_state)	},
    1.96 +	{ "pa_operation_cancel",
    1.97 +		(void **)&SDL_NAME(pa_operation_cancel)		},
    1.98 +	{ "pa_operation_unref",
    1.99 +		(void **)&SDL_NAME(pa_operation_unref)		},
   1.100 +	{ "pa_context_new",
   1.101 +		(void **)&SDL_NAME(pa_context_new)		},
   1.102 +	{ "pa_context_connect",
   1.103 +		(void **)&SDL_NAME(pa_context_connect)		},
   1.104 +	{ "pa_context_get_state",
   1.105 +		(void **)&SDL_NAME(pa_context_get_state)	},
   1.106 +	{ "pa_context_disconnect",
   1.107 +		(void **)&SDL_NAME(pa_context_disconnect)	},
   1.108 +	{ "pa_context_unref",
   1.109 +		(void **)&SDL_NAME(pa_context_unref)		},
   1.110 +	{ "pa_stream_new",
   1.111 +		(void **)&SDL_NAME(pa_stream_new)		},
   1.112 +	{ "pa_stream_connect_playback",
   1.113 +		(void **)&SDL_NAME(pa_stream_connect_playback)	},
   1.114 +	{ "pa_stream_get_state",
   1.115 +		(void **)&SDL_NAME(pa_stream_get_state)		},
   1.116 +	{ "pa_stream_writable_size",
   1.117 +		(void **)&SDL_NAME(pa_stream_writable_size)	},
   1.118 +	{ "pa_stream_write",
   1.119 +		(void **)&SDL_NAME(pa_stream_write)		},
   1.120 +	{ "pa_stream_drain",
   1.121 +		(void **)&SDL_NAME(pa_stream_drain)		},
   1.122 +	{ "pa_stream_disconnect",
   1.123 +		(void **)&SDL_NAME(pa_stream_disconnect)	},
   1.124 +	{ "pa_stream_unref",
   1.125 +		(void **)&SDL_NAME(pa_stream_unref)		},
   1.126  };
   1.127  
   1.128  static void UnloadPulseLibrary()
   1.129 @@ -218,6 +280,7 @@
   1.130  	this->PlayAudio = PULSE_PlayAudio;
   1.131  	this->GetAudioBuf = PULSE_GetAudioBuf;
   1.132  	this->CloseAudio = PULSE_CloseAudio;
   1.133 +	this->WaitDone = PULSE_WaitDone;
   1.134  
   1.135  	this->free = Audio_DeleteDevice;
   1.136  
   1.137 @@ -232,26 +295,25 @@
   1.138  /* This function waits until it is possible to write a full sound buffer */
   1.139  static void PULSE_WaitAudio(_THIS)
   1.140  {
   1.141 -	/* Check to see if the thread-parent process is still alive */
   1.142 -	{ static int cnt = 0;
   1.143 -		/* Note that this only works with thread implementations
   1.144 -		   that use a different process id for each thread.
   1.145 -		*/
   1.146 -		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   1.147 -			if ( kill(parent, 0) < 0 ) {
   1.148 -				this->enabled = 0;
   1.149 -			}
   1.150 +	int size;
   1.151 +	while(1) {
   1.152 +		if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
   1.153 +		    SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
   1.154 +		    SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
   1.155 +			this->enabled = 0;
   1.156 +			return;
   1.157  		}
   1.158 +		size = SDL_NAME(pa_stream_writable_size)(stream);
   1.159 +		if (size >= mixlen)
   1.160 +			return;
   1.161  	}
   1.162  }
   1.163  
   1.164  static void PULSE_PlayAudio(_THIS)
   1.165  {
   1.166  	/* Write the audio data */
   1.167 -	if ( SDL_NAME(pa_simple_write)(stream, mixbuf, mixlen, NULL) != 0 )
   1.168 -	{
   1.169 +	if (SDL_NAME(pa_stream_write)(stream, mixbuf, mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
   1.170  		this->enabled = 0;
   1.171 -	}
   1.172  }
   1.173  
   1.174  static Uint8 *PULSE_GetAudioBuf(_THIS)
   1.175 @@ -266,10 +328,19 @@
   1.176  		mixbuf = NULL;
   1.177  	}
   1.178  	if ( stream != NULL ) {
   1.179 -		SDL_NAME(pa_simple_drain)(stream, NULL);
   1.180 -		SDL_NAME(pa_simple_free)(stream);
   1.181 +		SDL_NAME(pa_stream_disconnect)(stream);
   1.182 +		SDL_NAME(pa_stream_unref)(stream);
   1.183  		stream = NULL;
   1.184  	}
   1.185 +	if (context != NULL) {
   1.186 +		SDL_NAME(pa_context_disconnect)(context);
   1.187 +		SDL_NAME(pa_context_unref)(context);
   1.188 +		context = NULL;
   1.189 +	}
   1.190 +	if (mainloop != NULL) {
   1.191 +		SDL_NAME(pa_mainloop_free)(mainloop);
   1.192 +		mainloop = NULL;
   1.193 +	}
   1.194  }
   1.195  
   1.196  /* Try to get the name of the program */
   1.197 @@ -297,12 +368,36 @@
   1.198  	return(progname);
   1.199  }
   1.200  
   1.201 +static void stream_drain_complete(pa_stream *s, int success, void *userdata) {
   1.202 +}
   1.203 +
   1.204 +static void PULSE_WaitDone(_THIS)
   1.205 +{
   1.206 +	pa_operation *o;
   1.207 +
   1.208 +	o = SDL_NAME(pa_stream_drain)(stream, stream_drain_complete, NULL);
   1.209 +	if (!o)
   1.210 +		return;
   1.211 +
   1.212 +	while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) {
   1.213 +		if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
   1.214 +		    SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
   1.215 +		    SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
   1.216 +			SDL_NAME(pa_operation_cancel)(o);
   1.217 +			break;
   1.218 +		}
   1.219 +	}
   1.220 +	SDL_NAME(pa_operation_unref)(o);
   1.221 +}
   1.222 +
   1.223  static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.224  {
   1.225 +	int             state;
   1.226  	Uint16          test_format;
   1.227  	pa_sample_spec  paspec;
   1.228  	pa_buffer_attr  paattr;
   1.229  	pa_channel_map  pacmap;
   1.230 +	pa_stream_flags_t flags = 0;
   1.231  
   1.232  	paspec.format = PA_SAMPLE_INVALID;
   1.233  	for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) {
   1.234 @@ -330,6 +425,9 @@
   1.235  	paspec.rate = spec->freq;
   1.236  
   1.237  	/* Calculate the final parameters for this audio specification */
   1.238 +#ifdef PA_STREAM_ADJUST_LATENCY
   1.239 +	spec->samples /= 2; /* Mix in smaller chunck to avoid underruns */
   1.240 +#endif
   1.241  	SDL_CalculateAudioSpec(spec);
   1.242  
   1.243  	/* Allocate mixing buffer */
   1.244 @@ -341,36 +439,92 @@
   1.245  	SDL_memset(mixbuf, spec->silence, spec->size);
   1.246  
   1.247  	/* Reduced prebuffering compared to the defaults. */
   1.248 +#ifdef PA_STREAM_ADJUST_LATENCY
   1.249 +	paattr.tlength = mixlen * 4; /* 2x original requested bufsize */
   1.250 +	paattr.prebuf = -1;
   1.251 +	paattr.maxlength = -1;
   1.252 +	paattr.minreq = mixlen; /* -1 can lead to pa_stream_writable_size()
   1.253 +				   >= mixlen never becoming true */
   1.254 +	flags = PA_STREAM_ADJUST_LATENCY;
   1.255 +#else
   1.256  	paattr.tlength = mixlen*2;
   1.257 -	paattr.minreq = mixlen;
   1.258  	paattr.prebuf = mixlen*2;
   1.259  	paattr.maxlength = mixlen*2;
   1.260 +	paattr.minreq = mixlen;
   1.261 +#endif
   1.262  
   1.263  	/* The SDL ALSA output hints us that we use Windows' channel mapping */
   1.264  	/* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
   1.265  	SDL_NAME(pa_channel_map_init_auto)(
   1.266  		&pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX);
   1.267  
   1.268 +	/* Set up a new main loop */
   1.269 +	if (!(mainloop = SDL_NAME(pa_mainloop_new)())) {
   1.270 +		PULSE_CloseAudio(this);
   1.271 +		SDL_SetError("pa_mainloop_new() failed");
   1.272 +		return(-1);
   1.273 +	}
   1.274 +
   1.275 +	mainloop_api = SDL_NAME(pa_mainloop_get_api)(mainloop);
   1.276 +	if (!(context = SDL_NAME(pa_context_new)(mainloop_api, get_progname()))) {
   1.277 +		PULSE_CloseAudio(this);
   1.278 +		SDL_SetError("pa_context_new() failed");
   1.279 +		return(-1);
   1.280 +	}
   1.281 +
   1.282  	/* Connect to the PulseAudio server */
   1.283 -	stream = SDL_NAME(pa_simple_new)(
   1.284 -		NULL,                        /* server */
   1.285 -		get_progname(),              /* application name */
   1.286 -		PA_STREAM_PLAYBACK,          /* playback mode */
   1.287 -		NULL,                        /* device on the server */
   1.288 +	if (SDL_NAME(pa_context_connect)(context, NULL, 0, NULL) < 0) {
   1.289 +		PULSE_CloseAudio(this);
   1.290 +	        SDL_SetError("Could not setup connection to PulseAudio");
   1.291 +		return(-1);
   1.292 +	}
   1.293 +
   1.294 +	do {
   1.295 +		if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
   1.296 +			PULSE_CloseAudio(this);
   1.297 +			SDL_SetError("pa_mainloop_iterate() failed");
   1.298 +			return(-1);
   1.299 +		}
   1.300 +		state = SDL_NAME(pa_context_get_state)(context);
   1.301 +		if (!PA_CONTEXT_IS_GOOD(state)) {
   1.302 +			PULSE_CloseAudio(this);
   1.303 +			SDL_SetError("Could not connect to PulseAudio");
   1.304 +			return(-1);
   1.305 +		}
   1.306 +	} while (state != PA_CONTEXT_READY);
   1.307 +
   1.308 +	stream = SDL_NAME(pa_stream_new)(
   1.309 +		context,
   1.310  		"Simple DirectMedia Layer",  /* stream description */
   1.311  		&paspec,                     /* sample format spec */
   1.312 -		&pacmap,                     /* channel map */
   1.313 -		&paattr,                     /* buffering attributes */
   1.314 -		NULL                         /* error code */
   1.315 +		&pacmap                      /* channel map */
   1.316  	);
   1.317  	if ( stream == NULL ) {
   1.318  		PULSE_CloseAudio(this);
   1.319 -		SDL_SetError("Could not connect to PulseAudio");
   1.320 +		SDL_SetError("Could not setup PulseAudio stream");
   1.321  		return(-1);
   1.322  	}
   1.323  
   1.324 -	/* Get the parent process id (we're the parent of the audio thread) */
   1.325 -	parent = getpid();
   1.326 +	if (SDL_NAME(pa_stream_connect_playback)(stream, NULL, &paattr, flags,
   1.327 +			NULL, NULL) < 0) {
   1.328 +		PULSE_CloseAudio(this);
   1.329 +		SDL_SetError("Could not connect PulseAudio stream");
   1.330 +		return(-1);
   1.331 +	}
   1.332 +
   1.333 +	do {
   1.334 +		if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
   1.335 +			PULSE_CloseAudio(this);
   1.336 +			SDL_SetError("pa_mainloop_iterate() failed");
   1.337 +			return(-1);
   1.338 +		}
   1.339 +		state = SDL_NAME(pa_stream_get_state)(stream);
   1.340 +		if (!PA_STREAM_IS_GOOD(state)) {
   1.341 +			PULSE_CloseAudio(this);
   1.342 +			SDL_SetError("Could not create to PulseAudio stream");
   1.343 +			return(-1);
   1.344 +		}
   1.345 +	} while (state != PA_STREAM_READY);
   1.346  
   1.347  	return(0);
   1.348  }