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
UnloadARTSLibrary()90 UnloadARTSLibrary()
91 {
92 if (arts_handle != NULL) {
93 SDL_UnloadObject(arts_handle);
94 arts_handle = NULL;
95 }
96 }
97
98 static int
LoadARTSLibrary(void)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
UnloadARTSLibrary()125 UnloadARTSLibrary()
126 {
127 return;
128 }
129
130 static int
LoadARTSLibrary(void)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
ARTS_WaitDevice(_THIS)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
ARTS_PlayDevice(_THIS)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 *
ARTS_GetDeviceBuf(_THIS)189 ARTS_GetDeviceBuf(_THIS)
190 {
191 return (this->hidden->mixbuf);
192 }
193
194
195 static void
ARTS_CloseDevice(_THIS)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
ARTS_Suspend(void)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
ARTS_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)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
ARTS_Deinitialize(void)317 ARTS_Deinitialize(void)
318 {
319 UnloadARTSLibrary();
320 }
321
322
323 static int
ARTS_Init(SDL_AudioDriverImpl * impl)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: */
366