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