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