symbian-qemu-0.9.1-12/libsdl-trunk/src/audio/pulse/SDL_pulseaudio.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2     SDL - Simple DirectMedia Layer
       
     3     Copyright (C) 1997-2007 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/simple.h>
       
    33 
       
    34 #include "SDL_timer.h"
       
    35 #include "SDL_audio.h"
       
    36 #include "../SDL_audiomem.h"
       
    37 #include "../SDL_audio_c.h"
       
    38 #include "../SDL_audiodev_c.h"
       
    39 #include "SDL_pulseaudio.h"
       
    40 
       
    41 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
       
    42 #include "SDL_name.h"
       
    43 #include "SDL_loadso.h"
       
    44 #else
       
    45 #define SDL_NAME(X)	X
       
    46 #endif
       
    47 
       
    48 /* The tag name used by the driver */
       
    49 #define PULSE_DRIVER_NAME	"pulse"
       
    50 
       
    51 /* Audio driver functions */
       
    52 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec);
       
    53 static void PULSE_WaitAudio(_THIS);
       
    54 static void PULSE_PlayAudio(_THIS);
       
    55 static Uint8 *PULSE_GetAudioBuf(_THIS);
       
    56 static void PULSE_CloseAudio(_THIS);
       
    57 
       
    58 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
       
    59 
       
    60 static const char *pulse_library = SDL_AUDIO_DRIVER_PULSE_DYNAMIC;
       
    61 static void *pulse_handle = NULL;
       
    62 static int pulse_loaded = 0;
       
    63 
       
    64 static pa_simple* (*SDL_NAME(pa_simple_new))(
       
    65 	const char *server,
       
    66 	const char *name,
       
    67 	pa_stream_direction_t dir,
       
    68 	const char *dev,
       
    69 	const char *stream_name,
       
    70 	const pa_sample_spec *ss,
       
    71 	const pa_channel_map *map,
       
    72 	const pa_buffer_attr *attr,
       
    73 	int *error
       
    74 );
       
    75 static void (*SDL_NAME(pa_simple_free))(pa_simple *s);
       
    76 static int (*SDL_NAME(pa_simple_drain))(pa_simple *s, int *error);
       
    77 static int (*SDL_NAME(pa_simple_write))(
       
    78 	pa_simple *s,
       
    79 	const void *data,
       
    80 	size_t length,
       
    81 	int *error 
       
    82 );
       
    83 static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))(
       
    84 	pa_channel_map *m,
       
    85 	unsigned channels,
       
    86 	pa_channel_map_def_t def
       
    87 );
       
    88 	
       
    89 
       
    90 static struct {
       
    91 	const char *name;
       
    92 	void **func;
       
    93 } pulse_functions[] = {
       
    94 	{ "pa_simple_new",
       
    95 		(void **)&SDL_NAME(pa_simple_new)		},
       
    96 	{ "pa_simple_free",
       
    97 		(void **)&SDL_NAME(pa_simple_free)		},
       
    98 	{ "pa_simple_drain",
       
    99 		(void **)&SDL_NAME(pa_simple_drain)		},
       
   100 	{ "pa_simple_write",
       
   101 		(void **)&SDL_NAME(pa_simple_write)		},
       
   102 	{ "pa_channel_map_init_auto",
       
   103 		(void **)&SDL_NAME(pa_channel_map_init_auto)	},
       
   104 };
       
   105 
       
   106 static void UnloadPulseLibrary()
       
   107 {
       
   108 	if ( pulse_loaded ) {
       
   109 		SDL_UnloadObject(pulse_handle);
       
   110 		pulse_handle = NULL;
       
   111 		pulse_loaded = 0;
       
   112 	}
       
   113 }
       
   114 
       
   115 static int LoadPulseLibrary(void)
       
   116 {
       
   117 	int i, retval = -1;
       
   118 
       
   119 	pulse_handle = SDL_LoadObject(pulse_library);
       
   120 	if ( pulse_handle ) {
       
   121 		pulse_loaded = 1;
       
   122 		retval = 0;
       
   123 		for ( i=0; i<SDL_arraysize(pulse_functions); ++i ) {
       
   124 			*pulse_functions[i].func = SDL_LoadFunction(pulse_handle, pulse_functions[i].name);
       
   125 			if ( !*pulse_functions[i].func ) {
       
   126 				retval = -1;
       
   127 				UnloadPulseLibrary();
       
   128 				break;
       
   129 			}
       
   130 		}
       
   131 	}
       
   132 	return retval;
       
   133 }
       
   134 
       
   135 #else
       
   136 
       
   137 static void UnloadPulseLibrary()
       
   138 {
       
   139 	return;
       
   140 }
       
   141 
       
   142 static int LoadPulseLibrary(void)
       
   143 {
       
   144 	return 0;
       
   145 }
       
   146 
       
   147 #endif /* SDL_AUDIO_DRIVER_PULSE_DYNAMIC */
       
   148 
       
   149 /* Audio driver bootstrap functions */
       
   150 
       
   151 static int Audio_Available(void)
       
   152 {
       
   153 	pa_sample_spec paspec;
       
   154 	pa_simple *connection;
       
   155 	int available;
       
   156 
       
   157 	available = 0;
       
   158 	if ( LoadPulseLibrary() < 0 ) {
       
   159 		return available;
       
   160 	}
       
   161 	
       
   162 	/* Connect with a dummy format. */
       
   163 	paspec.format = PA_SAMPLE_U8;
       
   164 	paspec.rate = 11025;
       
   165 	paspec.channels = 1;
       
   166 	connection = SDL_NAME(pa_simple_new)(
       
   167 		SDL_getenv("PASERVER"),      /* server */
       
   168 		"Test stream",               /* application name */
       
   169 		PA_STREAM_PLAYBACK,          /* playback mode */
       
   170 		SDL_getenv("PADEVICE"),      /* device on the server */
       
   171 		"Simple DirectMedia Layer",  /* stream description */
       
   172 		&paspec,                     /* sample format spec */
       
   173 		NULL,                        /* channel map */
       
   174 		NULL,                        /* buffering attributes */
       
   175 		NULL                         /* error code */
       
   176 	);
       
   177 	if ( connection != NULL ) {
       
   178 		available = 1;
       
   179 		SDL_NAME(pa_simple_free)(connection);
       
   180 	}
       
   181 	
       
   182 	UnloadPulseLibrary();
       
   183 	return(available);
       
   184 }
       
   185 
       
   186 static void Audio_DeleteDevice(SDL_AudioDevice *device)
       
   187 {
       
   188 	SDL_free(device->hidden);
       
   189 	SDL_free(device);
       
   190 	UnloadPulseLibrary();
       
   191 }
       
   192 
       
   193 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
       
   194 {
       
   195 	SDL_AudioDevice *this;
       
   196 
       
   197 	/* Initialize all variables that we clean on shutdown */
       
   198 	LoadPulseLibrary();
       
   199 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
       
   200 	if ( this ) {
       
   201 		SDL_memset(this, 0, (sizeof *this));
       
   202 		this->hidden = (struct SDL_PrivateAudioData *)
       
   203 				SDL_malloc((sizeof *this->hidden));
       
   204 	}
       
   205 	if ( (this == NULL) || (this->hidden == NULL) ) {
       
   206 		SDL_OutOfMemory();
       
   207 		if ( this ) {
       
   208 			SDL_free(this);
       
   209 		}
       
   210 		return(0);
       
   211 	}
       
   212 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
       
   213 
       
   214 	/* Set the function pointers */
       
   215 	this->OpenAudio = PULSE_OpenAudio;
       
   216 	this->WaitAudio = PULSE_WaitAudio;
       
   217 	this->PlayAudio = PULSE_PlayAudio;
       
   218 	this->GetAudioBuf = PULSE_GetAudioBuf;
       
   219 	this->CloseAudio = PULSE_CloseAudio;
       
   220 
       
   221 	this->free = Audio_DeleteDevice;
       
   222 
       
   223 	return this;
       
   224 }
       
   225 
       
   226 AudioBootStrap PULSE_bootstrap = {
       
   227 	PULSE_DRIVER_NAME, "PulseAudio",
       
   228 	Audio_Available, Audio_CreateDevice
       
   229 };
       
   230 
       
   231 /* This function waits until it is possible to write a full sound buffer */
       
   232 static void PULSE_WaitAudio(_THIS)
       
   233 {
       
   234 	/* Check to see if the thread-parent process is still alive */
       
   235 	{ static int cnt = 0;
       
   236 		/* Note that this only works with thread implementations 
       
   237 		   that use a different process id for each thread.
       
   238 		*/
       
   239 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
       
   240 			if ( kill(parent, 0) < 0 ) {
       
   241 				this->enabled = 0;
       
   242 			}
       
   243 		}
       
   244 	}
       
   245 }
       
   246 
       
   247 static void PULSE_PlayAudio(_THIS)
       
   248 {
       
   249 	/* Write the audio data */
       
   250 	if ( SDL_NAME(pa_simple_write)(stream, mixbuf, mixlen, NULL) != 0 )
       
   251 	{
       
   252 		this->enabled = 0;
       
   253 	}
       
   254 }
       
   255 
       
   256 static Uint8 *PULSE_GetAudioBuf(_THIS)
       
   257 {
       
   258 	return(mixbuf);
       
   259 }
       
   260 
       
   261 static void PULSE_CloseAudio(_THIS)
       
   262 {
       
   263 	if ( mixbuf != NULL ) {
       
   264 		SDL_FreeAudioMem(mixbuf);
       
   265 		mixbuf = NULL;
       
   266 	}
       
   267 	if ( stream != NULL ) {
       
   268 		SDL_NAME(pa_simple_drain)(stream, NULL);
       
   269 		SDL_NAME(pa_simple_free)(stream);
       
   270 		stream = NULL;
       
   271 	}
       
   272 }
       
   273 
       
   274 /* Try to get the name of the program */
       
   275 static char *get_progname(void)
       
   276 {
       
   277 	char *progname = NULL;
       
   278 #ifdef __LINUX__
       
   279 	FILE *fp;
       
   280 	static char temp[BUFSIZ];
       
   281 
       
   282 	SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
       
   283 	fp = fopen(temp, "r");
       
   284 	if ( fp != NULL ) {
       
   285 		if ( fgets(temp, sizeof(temp)-1, fp) ) {
       
   286 			progname = SDL_strrchr(temp, '/');
       
   287 			if ( progname == NULL ) {
       
   288 				progname = temp;
       
   289 			} else {
       
   290 				progname = progname+1;
       
   291 			}
       
   292 		}
       
   293 		fclose(fp);
       
   294 	}
       
   295 #endif
       
   296 	return(progname);
       
   297 }
       
   298 
       
   299 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec)
       
   300 {
       
   301 	Uint16          test_format;
       
   302 	pa_sample_spec  paspec;
       
   303 	pa_buffer_attr  paattr;
       
   304 	pa_channel_map  pacmap;
       
   305 	
       
   306 	paspec.format = PA_SAMPLE_INVALID;
       
   307 	for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) {
       
   308 		switch ( test_format ) {
       
   309 			case AUDIO_U8:
       
   310 				paspec.format = PA_SAMPLE_U8;
       
   311 				break;
       
   312 			case AUDIO_S16LSB:
       
   313 				paspec.format = PA_SAMPLE_S16LE;
       
   314 				break;
       
   315 			case AUDIO_S16MSB:
       
   316 				paspec.format = PA_SAMPLE_S16BE;
       
   317 				break;
       
   318 		}
       
   319 		if ( paspec.format != PA_SAMPLE_INVALID )
       
   320 			break;
       
   321 	}
       
   322 	if (paspec.format == PA_SAMPLE_INVALID ) {
       
   323 		SDL_SetError("Couldn't find any suitable audio formats");
       
   324 		return(-1);
       
   325 	}
       
   326 	spec->format = test_format;
       
   327 	
       
   328 	paspec.channels = spec->channels;
       
   329 	paspec.rate = spec->freq;
       
   330 
       
   331 	/* Calculate the final parameters for this audio specification */
       
   332 	SDL_CalculateAudioSpec(spec);
       
   333 
       
   334 	/* Allocate mixing buffer */
       
   335 	mixlen = spec->size;
       
   336 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
       
   337 	if ( mixbuf == NULL ) {
       
   338 		return(-1);
       
   339 	}
       
   340 	SDL_memset(mixbuf, spec->silence, spec->size);
       
   341 	
       
   342 	/* Reduced prebuffering compared to the defaults. */
       
   343 	paattr.tlength = mixlen;
       
   344 	paattr.minreq = mixlen;
       
   345 	paattr.fragsize = mixlen;
       
   346 	paattr.prebuf = mixlen;
       
   347 	paattr.maxlength = mixlen * 4;
       
   348 	
       
   349 	/* The SDL ALSA output hints us that we use Windows' channel mapping */
       
   350 	/* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
       
   351 	SDL_NAME(pa_channel_map_init_auto)(
       
   352 		&pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX);
       
   353 	
       
   354 	/* Connect to the PulseAudio server */
       
   355 	stream = SDL_NAME(pa_simple_new)(
       
   356 		SDL_getenv("PASERVER"),      /* server */
       
   357 		get_progname(),              /* application name */
       
   358 		PA_STREAM_PLAYBACK,          /* playback mode */
       
   359 		SDL_getenv("PADEVICE"),      /* device on the server */
       
   360 		"Simple DirectMedia Layer",  /* stream description */
       
   361 		&paspec,                     /* sample format spec */
       
   362 		&pacmap,                     /* channel map */
       
   363 		&paattr,                     /* buffering attributes */
       
   364 		NULL                         /* error code */
       
   365 	);
       
   366 	if ( stream == NULL ) {
       
   367 		PULSE_CloseAudio(this);
       
   368 		SDL_SetError("Could not connect to PulseAudio");
       
   369 		return(-1);
       
   370 	}
       
   371 
       
   372 	/* Get the parent process id (we're the parent of the audio thread) */
       
   373 	parent = getpid();
       
   374 	
       
   375 	return(0);
       
   376 }
       
   377