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