src/audio/esd/SDL_esdaudio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 06 Mar 2002 05:20:11 +0000
changeset 294 d2d48e10f370
parent 252 e8157fcb3114
child 297 f6ffac90895c
permissions -rw-r--r--
Added a new header file: SDL_loadso.h
It contains the following functions:
SDL_LoadObject(), SDL_LoadFunction(), SDL_UnloadObject()
The UNIX esd and arts audio code use these to dynamically load
their respective audio libraries.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* Allow access to an ESD network stream mixing buffer */
    29 
    30 #ifdef ESD_SUPPORT
    31 
    32 #include <stdlib.h>
    33 #include <stdio.h>
    34 #include <string.h>
    35 #include <errno.h>
    36 #include <signal.h>
    37 #include <unistd.h>
    38 
    39 #include <esd.h>
    40 
    41 #include "SDL_audio.h"
    42 #include "SDL_error.h"
    43 #include "SDL_audiomem.h"
    44 #include "SDL_audio_c.h"
    45 #include "SDL_timer.h"
    46 #include "SDL_audiodev_c.h"
    47 #include "SDL_esdaudio.h"
    48 
    49 #ifdef ESD_DYNAMIC
    50 #include "SDL_name.h"
    51 #include "SDL_loadso.h"
    52 #else
    53 #define SDL_NAME(X)	X
    54 #endif
    55 
    56 /* The tag name used by ESD audio */
    57 #define ESD_DRIVER_NAME		"esd"
    58 
    59 /* Audio driver functions */
    60 static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec);
    61 static void ESD_WaitAudio(_THIS);
    62 static void ESD_PlayAudio(_THIS);
    63 static Uint8 *ESD_GetAudioBuf(_THIS);
    64 static void ESD_CloseAudio(_THIS);
    65 
    66 #ifdef ESD_DYNAMIC
    67 
    68 static const char *esd_library = ESD_DYNAMIC;
    69 static void *esd_handle = NULL;
    70 static int esd_loaded = 0;
    71 
    72 static int (*SDL_NAME(esd_open_sound))( const char *host );
    73 static int (*SDL_NAME(esd_close))( int esd );
    74 static int (*SDL_NAME(esd_play_stream))( esd_format_t format, int rate,
    75                                          const char *host, const char *name );
    76 static struct {
    77 	const char *name;
    78 	void **func;
    79 } esd_functions[] = {
    80 	{ "esd_open_sound",	(void **)&SDL_NAME(esd_open_sound)	},
    81 	{ "esd_close",		(void **)&SDL_NAME(esd_close)		},
    82 	{ "esd_play_stream",	(void **)&SDL_NAME(esd_play_stream)	},
    83 };
    84 
    85 static void UnloadESDLibrary()
    86 {
    87 	if ( esd_loaded ) {
    88 		SDL_UnloadObject(esd_handle);
    89 		esd_handle = NULL;
    90 		esd_loaded = 0;
    91 	}
    92 }
    93 
    94 static int LoadESDLibrary(void)
    95 {
    96 	int i, retval = -1;
    97 
    98 	esd_handle = SDL_LoadObject(esd_library);
    99 	if ( esd_handle ) {
   100 		esd_loaded = 1;
   101 		retval = 0;
   102 		for ( i=0; i<SDL_TABLESIZE(esd_functions); ++i ) {
   103 			*esd_functions[i].func = SDL_LoadFunction(esd_handle, esd_functions[i].name);
   104 			if ( ! esd_functions[i].func ) {
   105 				retval = -1;
   106 				UnloadESDLibrary();
   107 				break;
   108 			}
   109 		}
   110 	}
   111 	return retval;
   112 }
   113 
   114 #else
   115 
   116 static void UnloadESDLibrary()
   117 {
   118 	return;
   119 }
   120 
   121 static int LoadESDLibrary(void)
   122 {
   123 	return 0;
   124 }
   125 
   126 #endif /* ESD_DYNAMIC */
   127 
   128 /* Audio driver bootstrap functions */
   129 
   130 static int Audio_Available(void)
   131 {
   132 	int connection;
   133 	int available;
   134 
   135 	available = 0;
   136 	if ( LoadESDLibrary() < 0 ) {
   137 		return available;
   138 	}
   139 	connection = SDL_NAME(esd_open_sound)(NULL);
   140 	if ( connection >= 0 ) {
   141 		available = 1;
   142 		SDL_NAME(esd_close)(connection);
   143 	}
   144 	UnloadESDLibrary();
   145 	return(available);
   146 }
   147 
   148 static void Audio_DeleteDevice(SDL_AudioDevice *device)
   149 {
   150 	free(device->hidden);
   151 	free(device);
   152 	UnloadESDLibrary();
   153 }
   154 
   155 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   156 {
   157 	SDL_AudioDevice *this;
   158 
   159 	/* Initialize all variables that we clean on shutdown */
   160 	LoadESDLibrary();
   161 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
   162 	if ( this ) {
   163 		memset(this, 0, (sizeof *this));
   164 		this->hidden = (struct SDL_PrivateAudioData *)
   165 				malloc((sizeof *this->hidden));
   166 	}
   167 	if ( (this == NULL) || (this->hidden == NULL) ) {
   168 		SDL_OutOfMemory();
   169 		if ( this ) {
   170 			free(this);
   171 		}
   172 		return(0);
   173 	}
   174 	memset(this->hidden, 0, (sizeof *this->hidden));
   175 	audio_fd = -1;
   176 
   177 	/* Set the function pointers */
   178 	this->OpenAudio = ESD_OpenAudio;
   179 	this->WaitAudio = ESD_WaitAudio;
   180 	this->PlayAudio = ESD_PlayAudio;
   181 	this->GetAudioBuf = ESD_GetAudioBuf;
   182 	this->CloseAudio = ESD_CloseAudio;
   183 
   184 	this->free = Audio_DeleteDevice;
   185 
   186 	return this;
   187 }
   188 
   189 AudioBootStrap ESD_bootstrap = {
   190 	ESD_DRIVER_NAME, "Enlightened Sound Daemon",
   191 	Audio_Available, Audio_CreateDevice
   192 };
   193 
   194 /* This function waits until it is possible to write a full sound buffer */
   195 static void ESD_WaitAudio(_THIS)
   196 {
   197 	Sint32 ticks;
   198 
   199 	/* Check to see if the thread-parent process is still alive */
   200 	{ static int cnt = 0;
   201 		/* Note that this only works with thread implementations 
   202 		   that use a different process id for each thread.
   203 		*/
   204 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   205 			if ( kill(parent, 0) < 0 ) {
   206 				this->enabled = 0;
   207 			}
   208 		}
   209 	}
   210 
   211 	/* Use timer for general audio synchronization */
   212 	ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
   213 	if ( ticks > 0 ) {
   214 		SDL_Delay(ticks);
   215 	}
   216 }
   217 
   218 static void ESD_PlayAudio(_THIS)
   219 {
   220 	int written;
   221 
   222 	/* Write the audio data, checking for EAGAIN on broken audio drivers */
   223 	do {
   224 		written = write(audio_fd, mixbuf, mixlen);
   225 		if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
   226 			SDL_Delay(1);	/* Let a little CPU time go by */
   227 		}
   228 	} while ( (written < 0) && 
   229 	          ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
   230 
   231 	/* Set the next write frame */
   232 	next_frame += frame_ticks;
   233 
   234 	/* If we couldn't write, assume fatal error for now */
   235 	if ( written < 0 ) {
   236 		this->enabled = 0;
   237 	}
   238 }
   239 
   240 static Uint8 *ESD_GetAudioBuf(_THIS)
   241 {
   242 	return(mixbuf);
   243 }
   244 
   245 static void ESD_CloseAudio(_THIS)
   246 {
   247 	if ( mixbuf != NULL ) {
   248 		SDL_FreeAudioMem(mixbuf);
   249 		mixbuf = NULL;
   250 	}
   251 	if ( audio_fd >= 0 ) {
   252 		SDL_NAME(esd_close)(audio_fd);
   253 		audio_fd = -1;
   254 	}
   255 }
   256 
   257 /* Try to get the name of the program */
   258 static char *get_progname(void)
   259 {
   260 	char *progname = NULL;
   261 #ifdef linux
   262 	FILE *fp;
   263 	static char temp[BUFSIZ];
   264 
   265 	sprintf(temp, "/proc/%d/cmdline", getpid());
   266 	fp = fopen(temp, "r");
   267 	if ( fp != NULL ) {
   268 		if ( fgets(temp, sizeof(temp)-1, fp) ) {
   269 			progname = strrchr(temp, '/');
   270 			if ( progname == NULL ) {
   271 				progname = temp;
   272 			} else {
   273 				progname = progname+1;
   274 			}
   275 		}
   276 		fclose(fp);
   277 	}
   278 #endif
   279 	return(progname);
   280 }
   281 
   282 static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec)
   283 {
   284 	esd_format_t format;
   285 
   286 	/* Convert audio spec to the ESD audio format */
   287 	format = (ESD_STREAM | ESD_PLAY);
   288 	switch ( spec->format & 0xFF ) {
   289 		case 8:
   290 			format |= ESD_BITS8;
   291 			break;
   292 		case 16:
   293 			format |= ESD_BITS16;
   294 			break;
   295 		default:
   296 			SDL_SetError("Unsupported ESD audio format");
   297 			return(-1);
   298 	}
   299 	if ( spec->channels == 1 ) {
   300 		format |= ESD_MONO;
   301 	} else {
   302 		format |= ESD_STEREO;
   303 	}
   304 #if 0
   305 	spec->samples = ESD_BUF_SIZE;	/* Darn, no way to change this yet */
   306 #endif
   307 
   308 	/* Open a connection to the ESD audio server */
   309 	audio_fd = SDL_NAME(esd_play_stream)(format, spec->freq, NULL, get_progname());
   310 	if ( audio_fd < 0 ) {
   311 		SDL_SetError("Couldn't open ESD connection");
   312 		return(-1);
   313 	}
   314 
   315 	/* Calculate the final parameters for this audio specification */
   316 	SDL_CalculateAudioSpec(spec);
   317 	frame_ticks = (float)(spec->samples*1000)/spec->freq;
   318 	next_frame = SDL_GetTicks()+frame_ticks;
   319 
   320 	/* Allocate mixing buffer */
   321 	mixlen = spec->size;
   322 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   323 	if ( mixbuf == NULL ) {
   324 		return(-1);
   325 	}
   326 	memset(mixbuf, spec->silence, spec->size);
   327 
   328 	/* Get the parent process id (we're the parent of the audio thread) */
   329 	parent = getpid();
   330 
   331 	/* We're ready to rock and roll. :-) */
   332 	return(0);
   333 }
   334 
   335 #endif /* ESD_SUPPORT */