Home | History | Annotate | Download | only in arts
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 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 a raw mixing buffer */
     25 
     26 #ifdef HAVE_SIGNAL_H
     27 #include <signal.h>
     28 #endif
     29 #include <unistd.h>
     30 
     31 #include "SDL_timer.h"
     32 #include "SDL_audio.h"
     33 #include "../SDL_audiomem.h"
     34 #include "../SDL_audio_c.h"
     35 #include "../SDL_audiodev_c.h"
     36 #include "SDL_artsaudio.h"
     37 
     38 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
     39 #include "SDL_name.h"
     40 #include "SDL_loadso.h"
     41 #else
     42 #define SDL_NAME(X)	X
     43 #endif
     44 
     45 /* The tag name used by artsc audio */
     46 #define ARTS_DRIVER_NAME         "arts"
     47 
     48 /* Audio driver functions */
     49 static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec);
     50 static void ARTS_WaitAudio(_THIS);
     51 static void ARTS_PlayAudio(_THIS);
     52 static Uint8 *ARTS_GetAudioBuf(_THIS);
     53 static void ARTS_CloseAudio(_THIS);
     54 
     55 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
     56 
     57 static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
     58 static void *arts_handle = NULL;
     59 static int arts_loaded = 0;
     60 
     61 static int (*SDL_NAME(arts_init))(void);
     62 static void (*SDL_NAME(arts_free))(void);
     63 static arts_stream_t (*SDL_NAME(arts_play_stream))(int rate, int bits, int channels, const char *name);
     64 static int (*SDL_NAME(arts_stream_set))(arts_stream_t s, arts_parameter_t param, int value);
     65 static int (*SDL_NAME(arts_stream_get))(arts_stream_t s, arts_parameter_t param);
     66 static int (*SDL_NAME(arts_write))(arts_stream_t s, const void *buffer, int count);
     67 static void (*SDL_NAME(arts_close_stream))(arts_stream_t s);
     68 static int (*SDL_NAME(arts_suspended))(void);
     69 static const char *(*SDL_NAME(arts_error_text))(int errorcode);
     70 
     71 static struct {
     72 	const char *name;
     73 	void **func;
     74 } arts_functions[] = {
     75 	{ "arts_init",		(void **)&SDL_NAME(arts_init)		},
     76 	{ "arts_free",		(void **)&SDL_NAME(arts_free)		},
     77 	{ "arts_play_stream",	(void **)&SDL_NAME(arts_play_stream)	},
     78 	{ "arts_stream_set",	(void **)&SDL_NAME(arts_stream_set)	},
     79 	{ "arts_stream_get",	(void **)&SDL_NAME(arts_stream_get)	},
     80 	{ "arts_write",		(void **)&SDL_NAME(arts_write)		},
     81 	{ "arts_close_stream",	(void **)&SDL_NAME(arts_close_stream)	},
     82 	{ "arts_suspended",	(void **)&SDL_NAME(arts_suspended)	},
     83 	{ "arts_error_text",	(void **)&SDL_NAME(arts_error_text)	},
     84 };
     85 
     86 static void UnloadARTSLibrary()
     87 {
     88 	if ( arts_loaded ) {
     89 		SDL_UnloadObject(arts_handle);
     90 		arts_handle = NULL;
     91 		arts_loaded = 0;
     92 	}
     93 }
     94 
     95 static int LoadARTSLibrary(void)
     96 {
     97 	int i, retval = -1;
     98 
     99 	arts_handle = SDL_LoadObject(arts_library);
    100 	if ( arts_handle ) {
    101 		arts_loaded = 1;
    102 		retval = 0;
    103 		for ( i=0; i<SDL_arraysize(arts_functions); ++i ) {
    104 			*arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name);
    105 			if ( !*arts_functions[i].func ) {
    106 				retval = -1;
    107 				UnloadARTSLibrary();
    108 				break;
    109 			}
    110 		}
    111 	}
    112 	return retval;
    113 }
    114 
    115 #else
    116 
    117 static void UnloadARTSLibrary()
    118 {
    119 	return;
    120 }
    121 
    122 static int LoadARTSLibrary(void)
    123 {
    124 	return 0;
    125 }
    126 
    127 #endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
    128 
    129 /* Audio driver bootstrap functions */
    130 
    131 static int Audio_Available(void)
    132 {
    133 	int available = 0;
    134 
    135 	if ( LoadARTSLibrary() < 0 ) {
    136 		return available;
    137 	}
    138 	if ( SDL_NAME(arts_init)() == 0 ) {
    139 		if ( SDL_NAME(arts_suspended)() ) {
    140 			/* Play a stream so aRts doesn't crash */
    141 			arts_stream_t stream2;
    142 			stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL");
    143 			SDL_NAME(arts_write)(stream2, "", 0);
    144 			SDL_NAME(arts_close_stream)(stream2);
    145 			available = 1;
    146 		}
    147 		SDL_NAME(arts_free)();
    148 	}
    149 	UnloadARTSLibrary();
    150 
    151 	return available;
    152 }
    153 
    154 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    155 {
    156 	SDL_free(device->hidden);
    157 	SDL_free(device);
    158 	UnloadARTSLibrary();
    159 }
    160 
    161 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    162 {
    163 	SDL_AudioDevice *this;
    164 
    165 	/* Initialize all variables that we clean on shutdown */
    166 	LoadARTSLibrary();
    167 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
    168 	if ( this ) {
    169 		SDL_memset(this, 0, (sizeof *this));
    170 		this->hidden = (struct SDL_PrivateAudioData *)
    171 				SDL_malloc((sizeof *this->hidden));
    172 	}
    173 	if ( (this == NULL) || (this->hidden == NULL) ) {
    174 		SDL_OutOfMemory();
    175 		if ( this ) {
    176 			SDL_free(this);
    177 		}
    178 		return(0);
    179 	}
    180 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    181 	stream = 0;
    182 
    183 	/* Set the function pointers */
    184 	this->OpenAudio = ARTS_OpenAudio;
    185 	this->WaitAudio = ARTS_WaitAudio;
    186 	this->PlayAudio = ARTS_PlayAudio;
    187 	this->GetAudioBuf = ARTS_GetAudioBuf;
    188 	this->CloseAudio = ARTS_CloseAudio;
    189 
    190 	this->free = Audio_DeleteDevice;
    191 
    192 	return this;
    193 }
    194 
    195 AudioBootStrap ARTS_bootstrap = {
    196 	ARTS_DRIVER_NAME, "Analog Realtime Synthesizer",
    197 	Audio_Available, Audio_CreateDevice
    198 };
    199 
    200 /* This function waits until it is possible to write a full sound buffer */
    201 static void ARTS_WaitAudio(_THIS)
    202 {
    203 	Sint32 ticks;
    204 
    205 	/* Check to see if the thread-parent process is still alive */
    206 	{ static int cnt = 0;
    207 		/* Note that this only works with thread implementations
    208 		   that use a different process id for each thread.
    209 		*/
    210 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
    211 			if ( kill(parent, 0) < 0 ) {
    212 				this->enabled = 0;
    213 			}
    214 		}
    215 	}
    216 
    217 	/* Use timer for general audio synchronization */
    218 	ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
    219 	if ( ticks > 0 ) {
    220 		SDL_Delay(ticks);
    221 	}
    222 }
    223 
    224 static void ARTS_PlayAudio(_THIS)
    225 {
    226 	int written;
    227 
    228 	/* Write the audio data */
    229 	written = SDL_NAME(arts_write)(stream, mixbuf, mixlen);
    230 
    231 	/* If timer synchronization is enabled, set the next write frame */
    232 	if ( frame_ticks ) {
    233 		next_frame += frame_ticks;
    234 	}
    235 
    236 	/* If we couldn't write, assume fatal error for now */
    237 	if ( written < 0 ) {
    238 		this->enabled = 0;
    239 	}
    240 #ifdef DEBUG_AUDIO
    241 	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    242 #endif
    243 }
    244 
    245 static Uint8 *ARTS_GetAudioBuf(_THIS)
    246 {
    247 	return(mixbuf);
    248 }
    249 
    250 static void ARTS_CloseAudio(_THIS)
    251 {
    252 	if ( mixbuf != NULL ) {
    253 		SDL_FreeAudioMem(mixbuf);
    254 		mixbuf = NULL;
    255 	}
    256 	if ( stream ) {
    257 		SDL_NAME(arts_close_stream)(stream);
    258 		stream = 0;
    259 	}
    260 	SDL_NAME(arts_free)();
    261 }
    262 
    263 static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec)
    264 {
    265 	int bits, frag_spec;
    266 	Uint16 test_format, format;
    267 	int error_code;
    268 
    269 	/* Reset the timer synchronization flag */
    270 	frame_ticks = 0.0;
    271 
    272 	mixbuf = NULL;
    273 
    274 	/* Try for a closest match on audio format */
    275 	format = 0;
    276 	bits = 0;
    277 	for ( test_format = SDL_FirstAudioFormat(spec->format);
    278 						! format && test_format; ) {
    279 #ifdef DEBUG_AUDIO
    280 		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
    281 #endif
    282 		switch ( test_format ) {
    283 			case AUDIO_U8:
    284 				bits = 8;
    285 				format = 1;
    286 				break;
    287 			case AUDIO_S16LSB:
    288 				bits = 16;
    289 				format = 1;
    290 				break;
    291 			default:
    292 				format = 0;
    293 				break;
    294 		}
    295 		if ( ! format ) {
    296 			test_format = SDL_NextAudioFormat();
    297 		}
    298 	}
    299 	if ( format == 0 ) {
    300 		SDL_SetError("Couldn't find any hardware audio formats");
    301 		return(-1);
    302 	}
    303 	spec->format = test_format;
    304 
    305 	error_code = SDL_NAME(arts_init)();
    306 	if ( error_code != 0 ) {
    307 		SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code));
    308 		return(-1);
    309 	}
    310 	if ( ! SDL_NAME(arts_suspended)() ) {
    311 		SDL_SetError("ARTS can not open audio device");
    312 		return(-1);
    313 	}
    314 	stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL");
    315 
    316 	/* Calculate the final parameters for this audio specification */
    317 	SDL_CalculateAudioSpec(spec);
    318 
    319 	/* Determine the power of two of the fragment size */
    320 	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
    321 	if ( (0x01<<frag_spec) != spec->size ) {
    322 		SDL_SetError("Fragment size must be a power of two");
    323 		return(-1);
    324 	}
    325 	frag_spec |= 0x00020000;	/* two fragments, for low latency */
    326 
    327 #ifdef ARTS_P_PACKET_SETTINGS
    328 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
    329 #else
    330 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff);
    331 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16);
    332 #endif
    333 	spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE);
    334 
    335 	/* Allocate mixing buffer */
    336 	mixlen = spec->size;
    337 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
    338 	if ( mixbuf == NULL ) {
    339 		return(-1);
    340 	}
    341 	SDL_memset(mixbuf, spec->silence, spec->size);
    342 
    343 	/* Get the parent process id (we're the parent of the audio thread) */
    344 	parent = getpid();
    345 
    346 	/* We're ready to rock and roll. :-) */
    347 	return(0);
    348 }
    349