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