src/audio/arts/SDL_artsaudio.c
author Ryan C. Gordon
Mon, 02 Jan 2012 15:07:26 -0500
branchSDL-1.2
changeset 6151 331f27f01cdb
parent 6137 4720145f848b
permissions -rw-r--r--
Use arts_suspend() in a loop to wait for arts to become ready.

Fixes Bugzilla #372.

Thanks to Patrice Mandin for the patch!
     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@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 }