src/audio/arts/SDL_artsaudio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 28 Aug 2002 23:25:09 +0000
changeset 474 583a07ab5444
parent 301 fb4c4c6a2773
child 769 b8d311d90021
permissions -rw-r--r--
Doh! Patch by Joel Ray Holveck
     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 	return available;
   149 }
   150 
   151 static void Audio_DeleteDevice(SDL_AudioDevice *device)
   152 {
   153 	free(device->hidden);
   154 	free(device);
   155 	UnloadARTSLibrary();
   156 }
   157 
   158 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   159 {
   160 	SDL_AudioDevice *this;
   161 
   162 	/* Initialize all variables that we clean on shutdown */
   163 	LoadARTSLibrary();
   164 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
   165 	if ( this ) {
   166 		memset(this, 0, (sizeof *this));
   167 		this->hidden = (struct SDL_PrivateAudioData *)
   168 				malloc((sizeof *this->hidden));
   169 	}
   170 	if ( (this == NULL) || (this->hidden == NULL) ) {
   171 		SDL_OutOfMemory();
   172 		if ( this ) {
   173 			free(this);
   174 		}
   175 		return(0);
   176 	}
   177 	memset(this->hidden, 0, (sizeof *this->hidden));
   178 	stream = 0;
   179 
   180 	/* Set the function pointers */
   181 	this->OpenAudio = ARTSC_OpenAudio;
   182 	this->WaitAudio = ARTSC_WaitAudio;
   183 	this->PlayAudio = ARTSC_PlayAudio;
   184 	this->GetAudioBuf = ARTSC_GetAudioBuf;
   185 	this->CloseAudio = ARTSC_CloseAudio;
   186 
   187 	this->free = Audio_DeleteDevice;
   188 
   189 	return this;
   190 }
   191 
   192 AudioBootStrap ARTSC_bootstrap = {
   193 	ARTSC_DRIVER_NAME, "Analog Realtime Synthesizer",
   194 	Audio_Available, Audio_CreateDevice
   195 };
   196 
   197 /* This function waits until it is possible to write a full sound buffer */
   198 static void ARTSC_WaitAudio(_THIS)
   199 {
   200 	Sint32 ticks;
   201 
   202 	/* Check to see if the thread-parent process is still alive */
   203 	{ static int cnt = 0;
   204 		/* Note that this only works with thread implementations 
   205 		   that use a different process id for each thread.
   206 		*/
   207 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   208 			if ( kill(parent, 0) < 0 ) {
   209 				this->enabled = 0;
   210 			}
   211 		}
   212 	}
   213 
   214 	/* Use timer for general audio synchronization */
   215 	ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
   216 	if ( ticks > 0 ) {
   217 		SDL_Delay(ticks);
   218 	}
   219 }
   220 
   221 static void ARTSC_PlayAudio(_THIS)
   222 {
   223 	int written;
   224 
   225 	/* Write the audio data */
   226 	written = SDL_NAME(arts_write)(stream, mixbuf, mixlen);
   227 	
   228 	/* If timer synchronization is enabled, set the next write frame */
   229 	if ( frame_ticks ) {
   230 		next_frame += frame_ticks;
   231 	}
   232 
   233 	/* If we couldn't write, assume fatal error for now */
   234 	if ( written < 0 ) {
   235 		this->enabled = 0;
   236 	}
   237 #ifdef DEBUG_AUDIO
   238 	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   239 #endif
   240 }
   241 
   242 static Uint8 *ARTSC_GetAudioBuf(_THIS)
   243 {
   244 	return(mixbuf);
   245 }
   246 
   247 static void ARTSC_CloseAudio(_THIS)
   248 {
   249 	if ( mixbuf != NULL ) {
   250 		SDL_FreeAudioMem(mixbuf);
   251 		mixbuf = NULL;
   252 	}
   253 	if ( stream ) {
   254 		SDL_NAME(arts_close_stream)(stream);
   255 		stream = 0;
   256 	}
   257 	SDL_NAME(arts_free)();
   258 }
   259 
   260 static int ARTSC_OpenAudio(_THIS, SDL_AudioSpec *spec)
   261 {
   262 	int bits, frag_spec;
   263 	Uint16 test_format, format;
   264 
   265 	/* Reset the timer synchronization flag */
   266 	frame_ticks = 0.0;
   267 
   268 	mixbuf = NULL;
   269 
   270 	/* Try for a closest match on audio format */
   271 	format = 0;
   272 	bits = 0;
   273 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   274 						! format && test_format; ) {
   275 #ifdef DEBUG_AUDIO
   276 		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   277 #endif
   278 		switch ( test_format ) {
   279 			case AUDIO_U8:
   280 				bits = 8;
   281 				format = 1;
   282 				break;
   283 			case AUDIO_S16LSB:
   284 				bits = 16;
   285 				format = 1;
   286 				break;
   287 			default:
   288 				format = 0;
   289 				break;
   290 		}
   291 		if ( ! format ) {
   292 			test_format = SDL_NextAudioFormat();
   293 		}
   294 	}
   295 	if ( format == 0 ) {
   296 		SDL_SetError("Couldn't find any hardware audio formats");
   297 		return(-1);
   298 	}
   299 	spec->format = test_format;
   300 
   301 	if ( SDL_NAME(arts_init)() != 0 ) {
   302 		SDL_SetError("Unable to initialize ARTS");
   303 		return(-1);
   304 	}
   305 	stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL");
   306 
   307 	/* Calculate the final parameters for this audio specification */
   308 	SDL_CalculateAudioSpec(spec);
   309 
   310 	/* Determine the power of two of the fragment size */
   311 	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
   312 	if ( (0x01<<frag_spec) != spec->size ) {
   313 		SDL_SetError("Fragment size must be a power of two");
   314 		return(-1);
   315 	}
   316 	frag_spec |= 0x00020000;	/* two fragments, for low latency */
   317 
   318 #ifdef ARTS_P_PACKET_SETTINGS
   319 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
   320 #else
   321 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff);
   322 	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16);
   323 #endif
   324 	spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE);
   325 
   326 	/* Allocate mixing buffer */
   327 	mixlen = spec->size;
   328 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   329 	if ( mixbuf == NULL ) {
   330 		return(-1);
   331 	}
   332 	memset(mixbuf, spec->silence, spec->size);
   333 
   334 	/* Get the parent process id (we're the parent of the audio thread) */
   335 	parent = getpid();
   336 
   337 	/* We're ready to rock and roll. :-) */
   338 	return(0);
   339 }