SDL_artsaudio.c (11094B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_AUDIO_DRIVER_ARTS 24 25/* Allow access to a raw mixing buffer */ 26 27#ifdef HAVE_SIGNAL_H 28#include <signal.h> 29#endif 30#include <unistd.h> 31#include <errno.h> 32 33#include "SDL_timer.h" 34#include "SDL_audio.h" 35#include "../SDL_audiomem.h" 36#include "../SDL_audio_c.h" 37#include "SDL_artsaudio.h" 38 39#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC 40#include "SDL_name.h" 41#include "SDL_loadso.h" 42#else 43#define SDL_NAME(X) X 44#endif 45 46#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC 47 48static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC; 49static void *arts_handle = NULL; 50 51/* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */ 52static int (*SDL_NAME(arts_init)) (void); 53static void (*SDL_NAME(arts_free)) (void); 54static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits, 55 int channels, 56 const char *name); 57static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s, 58 arts_parameter_t param, int value); 59static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s, 60 arts_parameter_t param); 61static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer, 62 int count); 63static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s); 64static int (*SDL_NAME(arts_suspend))(void); 65static int (*SDL_NAME(arts_suspended)) (void); 66static const char *(*SDL_NAME(arts_error_text)) (int errorcode); 67 68#define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) } 69static struct 70{ 71 const char *name; 72 void **func; 73} arts_functions[] = { 74/* *INDENT-OFF* */ 75 SDL_ARTS_SYM(arts_init), 76 SDL_ARTS_SYM(arts_free), 77 SDL_ARTS_SYM(arts_play_stream), 78 SDL_ARTS_SYM(arts_stream_set), 79 SDL_ARTS_SYM(arts_stream_get), 80 SDL_ARTS_SYM(arts_write), 81 SDL_ARTS_SYM(arts_close_stream), 82 SDL_ARTS_SYM(arts_suspend), 83 SDL_ARTS_SYM(arts_suspended), 84 SDL_ARTS_SYM(arts_error_text), 85/* *INDENT-ON* */ 86}; 87 88#undef SDL_ARTS_SYM 89 90static void 91UnloadARTSLibrary() 92{ 93 if (arts_handle != NULL) { 94 SDL_UnloadObject(arts_handle); 95 arts_handle = NULL; 96 } 97} 98 99static int 100LoadARTSLibrary(void) 101{ 102 int i, retval = -1; 103 104 if (arts_handle == NULL) { 105 arts_handle = SDL_LoadObject(arts_library); 106 if (arts_handle != NULL) { 107 retval = 0; 108 for (i = 0; i < SDL_arraysize(arts_functions); ++i) { 109 *arts_functions[i].func = 110 SDL_LoadFunction(arts_handle, arts_functions[i].name); 111 if (!*arts_functions[i].func) { 112 retval = -1; 113 UnloadARTSLibrary(); 114 break; 115 } 116 } 117 } 118 } 119 120 return retval; 121} 122 123#else 124 125static void 126UnloadARTSLibrary() 127{ 128 return; 129} 130 131static int 132LoadARTSLibrary(void) 133{ 134 return 0; 135} 136 137#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */ 138 139/* This function waits until it is possible to write a full sound buffer */ 140static void 141ARTS_WaitDevice(_THIS) 142{ 143 Sint32 ticks; 144 145 /* Check to see if the thread-parent process is still alive */ 146 { 147 static int cnt = 0; 148 /* Note that this only works with thread implementations 149 that use a different process id for each thread. 150 */ 151 /* Check every 10 loops */ 152 if (this->hidden->parent && (((++cnt) % 10) == 0)) { 153 if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) { 154 this->enabled = 0; 155 } 156 } 157 } 158 159 /* Use timer for general audio synchronization */ 160 ticks = 161 ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS; 162 if (ticks > 0) { 163 SDL_Delay(ticks); 164 } 165} 166 167static void 168ARTS_PlayDevice(_THIS) 169{ 170 /* Write the audio data */ 171 int written = SDL_NAME(arts_write) (this->hidden->stream, 172 this->hidden->mixbuf, 173 this->hidden->mixlen); 174 175 /* If timer synchronization is enabled, set the next write frame */ 176 if (this->hidden->frame_ticks) { 177 this->hidden->next_frame += this->hidden->frame_ticks; 178 } 179 180 /* If we couldn't write, assume fatal error for now */ 181 if (written < 0) { 182 this->enabled = 0; 183 } 184#ifdef DEBUG_AUDIO 185 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 186#endif 187} 188 189static void 190ARTS_WaitDone(_THIS) 191{ 192 /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */ 193} 194 195 196static Uint8 * 197ARTS_GetDeviceBuf(_THIS) 198{ 199 return (this->hidden->mixbuf); 200} 201 202 203static void 204ARTS_CloseDevice(_THIS) 205{ 206 if (this->hidden != NULL) { 207 SDL_FreeAudioMem(this->hidden->mixbuf); 208 this->hidden->mixbuf = NULL; 209 if (this->hidden->stream) { 210 SDL_NAME(arts_close_stream) (this->hidden->stream); 211 this->hidden->stream = 0; 212 } 213 SDL_NAME(arts_free) (); 214 SDL_free(this->hidden); 215 this->hidden = NULL; 216 } 217} 218 219static int 220ARTS_Suspend(void) 221{ 222 const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */ 223 while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) { 224 if ( SDL_NAME(arts_suspend)() ) { 225 break; 226 } 227 } 228 return SDL_NAME(arts_suspended)(); 229} 230 231static int 232ARTS_OpenDevice(_THIS, const char *devname, int iscapture) 233{ 234 int rc = 0; 235 int bits = 0, frag_spec = 0; 236 SDL_AudioFormat test_format = 0, format = 0; 237 238 /* Initialize all variables that we clean on shutdown */ 239 this->hidden = (struct SDL_PrivateAudioData *) 240 SDL_malloc((sizeof *this->hidden)); 241 if (this->hidden == NULL) { 242 return SDL_OutOfMemory(); 243 } 244 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 245 246 /* Try for a closest match on audio format */ 247 for (test_format = SDL_FirstAudioFormat(this->spec.format); 248 !format && test_format;) { 249#ifdef DEBUG_AUDIO 250 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 251#endif 252 switch (test_format) { 253 case AUDIO_U8: 254 bits = 8; 255 format = 1; 256 break; 257 case AUDIO_S16LSB: 258 bits = 16; 259 format = 1; 260 break; 261 default: 262 format = 0; 263 break; 264 } 265 if (!format) { 266 test_format = SDL_NextAudioFormat(); 267 } 268 } 269 if (format == 0) { 270 ARTS_CloseDevice(this); 271 return SDL_SetError("Couldn't find any hardware audio formats"); 272 } 273 this->spec.format = test_format; 274 275 if ((rc = SDL_NAME(arts_init) ()) != 0) { 276 ARTS_CloseDevice(this); 277 return SDL_SetError("Unable to initialize ARTS: %s", 278 SDL_NAME(arts_error_text) (rc)); 279 } 280 281 if (!ARTS_Suspend()) { 282 ARTS_CloseDevice(this); 283 return SDL_SetError("ARTS can not open audio device"); 284 } 285 286 this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq, 287 bits, 288 this->spec.channels, 289 "SDL"); 290 291 /* Play nothing so we have at least one write (server bug workaround). */ 292 SDL_NAME(arts_write) (this->hidden->stream, "", 0); 293 294 /* Calculate the final parameters for this audio specification */ 295 SDL_CalculateAudioSpec(&this->spec); 296 297 /* Determine the power of two of the fragment size */ 298 for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec); 299 if ((0x01 << frag_spec) != this->spec.size) { 300 ARTS_CloseDevice(this); 301 return SDL_SetError("Fragment size must be a power of two"); 302 } 303 frag_spec |= 0x00020000; /* two fragments, for low latency */ 304 305#ifdef ARTS_P_PACKET_SETTINGS 306 SDL_NAME(arts_stream_set) (this->hidden->stream, 307 ARTS_P_PACKET_SETTINGS, frag_spec); 308#else 309 SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE, 310 frag_spec & 0xffff); 311 SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT, 312 frag_spec >> 16); 313#endif 314 this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream, 315 ARTS_P_PACKET_SIZE); 316 317 /* Allocate mixing buffer */ 318 this->hidden->mixlen = this->spec.size; 319 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 320 if (this->hidden->mixbuf == NULL) { 321 ARTS_CloseDevice(this); 322 return SDL_OutOfMemory(); 323 } 324 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 325 326 /* Get the parent process id (we're the parent of the audio thread) */ 327 this->hidden->parent = getpid(); 328 329 /* We're ready to rock and roll. :-) */ 330 return 0; 331} 332 333 334static void 335ARTS_Deinitialize(void) 336{ 337 UnloadARTSLibrary(); 338} 339 340 341static int 342ARTS_Init(SDL_AudioDriverImpl * impl) 343{ 344 if (LoadARTSLibrary() < 0) { 345 return 0; 346 } else { 347 if (SDL_NAME(arts_init) () != 0) { 348 UnloadARTSLibrary(); 349 SDL_SetError("ARTS: arts_init failed (no audio server?)"); 350 return 0; 351 } 352 353 /* Play a stream so aRts doesn't crash */ 354 if (ARTS_Suspend()) { 355 arts_stream_t stream; 356 stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL"); 357 SDL_NAME(arts_write) (stream, "", 0); 358 SDL_NAME(arts_close_stream) (stream); 359 } 360 361 SDL_NAME(arts_free) (); 362 } 363 364 /* Set the function pointers */ 365 impl->OpenDevice = ARTS_OpenDevice; 366 impl->PlayDevice = ARTS_PlayDevice; 367 impl->WaitDevice = ARTS_WaitDevice; 368 impl->GetDeviceBuf = ARTS_GetDeviceBuf; 369 impl->CloseDevice = ARTS_CloseDevice; 370 impl->WaitDone = ARTS_WaitDone; 371 impl->Deinitialize = ARTS_Deinitialize; 372 impl->OnlyHasDefaultOutputDevice = 1; 373 374 return 1; /* this audio target is available. */ 375} 376 377 378AudioBootStrap ARTS_bootstrap = { 379 "arts", "Analog RealTime Synthesizer", ARTS_Init, 0 380}; 381 382#endif /* SDL_AUDIO_DRIVER_ARTS */ 383 384/* vi: set ts=4 sw=4 expandtab: */