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