diff options
Diffstat (limited to 'apps/plugins/sdl/src/audio/arts')
-rw-r--r-- | apps/plugins/sdl/src/audio/arts/SDL_artsaudio.c | 362 | ||||
-rw-r--r-- | apps/plugins/sdl/src/audio/arts/SDL_artsaudio.h | 60 |
2 files changed, 422 insertions, 0 deletions
diff --git a/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.c b/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.c new file mode 100644 index 0000000000..373f8c1677 --- /dev/null +++ b/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | SDL - Simple DirectMedia Layer | ||
3 | Copyright (C) 1997-2012 Sam Lantinga | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 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 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | |||
19 | Sam Lantinga | ||
20 | slouken@libsdl.org | ||
21 | */ | ||
22 | #include "SDL_config.h" | ||
23 | |||
24 | /* Allow access to a raw mixing buffer */ | ||
25 | |||
26 | #ifdef HAVE_SIGNAL_H | ||
27 | #include <signal.h> | ||
28 | #endif | ||
29 | #include <unistd.h> | ||
30 | |||
31 | #include "SDL_timer.h" | ||
32 | #include "SDL_audio.h" | ||
33 | #include "../SDL_audiomem.h" | ||
34 | #include "../SDL_audio_c.h" | ||
35 | #include "../SDL_audiodev_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 | /* The tag name used by artsc audio */ | ||
46 | #define ARTS_DRIVER_NAME "arts" | ||
47 | |||
48 | /* Audio driver functions */ | ||
49 | static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec); | ||
50 | static void ARTS_WaitAudio(_THIS); | ||
51 | static void ARTS_PlayAudio(_THIS); | ||
52 | static Uint8 *ARTS_GetAudioBuf(_THIS); | ||
53 | static void ARTS_CloseAudio(_THIS); | ||
54 | |||
55 | #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC | ||
56 | |||
57 | static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC; | ||
58 | static void *arts_handle = NULL; | ||
59 | static int arts_loaded = 0; | ||
60 | |||
61 | static int (*SDL_NAME(arts_init))(void); | ||
62 | static void (*SDL_NAME(arts_free))(void); | ||
63 | static arts_stream_t (*SDL_NAME(arts_play_stream))(int rate, int bits, int channels, const char *name); | ||
64 | static int (*SDL_NAME(arts_stream_set))(arts_stream_t s, arts_parameter_t param, int value); | ||
65 | static int (*SDL_NAME(arts_stream_get))(arts_stream_t s, arts_parameter_t param); | ||
66 | static int (*SDL_NAME(arts_write))(arts_stream_t s, const void *buffer, int count); | ||
67 | static void (*SDL_NAME(arts_close_stream))(arts_stream_t s); | ||
68 | static int (*SDL_NAME(arts_suspend))(void); | ||
69 | static int (*SDL_NAME(arts_suspended))(void); | ||
70 | static const char *(*SDL_NAME(arts_error_text))(int errorcode); | ||
71 | |||
72 | static struct { | ||
73 | const char *name; | ||
74 | void **func; | ||
75 | } arts_functions[] = { | ||
76 | { "arts_init", (void **)&SDL_NAME(arts_init) }, | ||
77 | { "arts_free", (void **)&SDL_NAME(arts_free) }, | ||
78 | { "arts_play_stream", (void **)&SDL_NAME(arts_play_stream) }, | ||
79 | { "arts_stream_set", (void **)&SDL_NAME(arts_stream_set) }, | ||
80 | { "arts_stream_get", (void **)&SDL_NAME(arts_stream_get) }, | ||
81 | { "arts_write", (void **)&SDL_NAME(arts_write) }, | ||
82 | { "arts_close_stream", (void **)&SDL_NAME(arts_close_stream) }, | ||
83 | { "arts_suspend", (void **)&SDL_NAME(arts_suspend) }, | ||
84 | { "arts_suspended", (void **)&SDL_NAME(arts_suspended) }, | ||
85 | { "arts_error_text", (void **)&SDL_NAME(arts_error_text) }, | ||
86 | }; | ||
87 | |||
88 | static void UnloadARTSLibrary() | ||
89 | { | ||
90 | if ( arts_loaded ) { | ||
91 | SDL_UnloadObject(arts_handle); | ||
92 | arts_handle = NULL; | ||
93 | arts_loaded = 0; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static int LoadARTSLibrary(void) | ||
98 | { | ||
99 | int i, retval = -1; | ||
100 | |||
101 | arts_handle = SDL_LoadObject(arts_library); | ||
102 | if ( arts_handle ) { | ||
103 | arts_loaded = 1; | ||
104 | retval = 0; | ||
105 | for ( i=0; i<SDL_arraysize(arts_functions); ++i ) { | ||
106 | *arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name); | ||
107 | if ( !*arts_functions[i].func ) { | ||
108 | retval = -1; | ||
109 | UnloadARTSLibrary(); | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | return retval; | ||
115 | } | ||
116 | |||
117 | #else | ||
118 | |||
119 | static void UnloadARTSLibrary() | ||
120 | { | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | static int LoadARTSLibrary(void) | ||
125 | { | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | #endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */ | ||
130 | |||
131 | /* Audio driver bootstrap functions */ | ||
132 | |||
133 | static int ARTS_Suspend(void) | ||
134 | { | ||
135 | const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */ | ||
136 | while ( (!SDL_NAME(arts_suspended)()) && (SDL_GetTicks() < abortms) ) { | ||
137 | if ( SDL_NAME(arts_suspend)() ) { | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | return SDL_NAME(arts_suspended)(); | ||
143 | } | ||
144 | |||
145 | static int Audio_Available(void) | ||
146 | { | ||
147 | int available = 0; | ||
148 | |||
149 | if ( LoadARTSLibrary() < 0 ) { | ||
150 | return available; | ||
151 | } | ||
152 | if ( SDL_NAME(arts_init)() == 0 ) { | ||
153 | if ( ARTS_Suspend() ) { | ||
154 | /* Play a stream so aRts doesn't crash */ | ||
155 | arts_stream_t stream2; | ||
156 | stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL"); | ||
157 | SDL_NAME(arts_write)(stream2, "", 0); | ||
158 | SDL_NAME(arts_close_stream)(stream2); | ||
159 | available = 1; | ||
160 | } | ||
161 | SDL_NAME(arts_free)(); | ||
162 | } | ||
163 | UnloadARTSLibrary(); | ||
164 | |||
165 | return available; | ||
166 | } | ||
167 | |||
168 | static void Audio_DeleteDevice(SDL_AudioDevice *device) | ||
169 | { | ||
170 | SDL_free(device->hidden); | ||
171 | SDL_free(device); | ||
172 | UnloadARTSLibrary(); | ||
173 | } | ||
174 | |||
175 | static SDL_AudioDevice *Audio_CreateDevice(int devindex) | ||
176 | { | ||
177 | SDL_AudioDevice *this; | ||
178 | |||
179 | /* Initialize all variables that we clean on shutdown */ | ||
180 | LoadARTSLibrary(); | ||
181 | this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); | ||
182 | if ( this ) { | ||
183 | SDL_memset(this, 0, (sizeof *this)); | ||
184 | this->hidden = (struct SDL_PrivateAudioData *) | ||
185 | SDL_malloc((sizeof *this->hidden)); | ||
186 | } | ||
187 | if ( (this == NULL) || (this->hidden == NULL) ) { | ||
188 | SDL_OutOfMemory(); | ||
189 | if ( this ) { | ||
190 | SDL_free(this); | ||
191 | } | ||
192 | return(0); | ||
193 | } | ||
194 | SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | ||
195 | stream = 0; | ||
196 | |||
197 | /* Set the function pointers */ | ||
198 | this->OpenAudio = ARTS_OpenAudio; | ||
199 | this->WaitAudio = ARTS_WaitAudio; | ||
200 | this->PlayAudio = ARTS_PlayAudio; | ||
201 | this->GetAudioBuf = ARTS_GetAudioBuf; | ||
202 | this->CloseAudio = ARTS_CloseAudio; | ||
203 | |||
204 | this->free = Audio_DeleteDevice; | ||
205 | |||
206 | return this; | ||
207 | } | ||
208 | |||
209 | AudioBootStrap ARTS_bootstrap = { | ||
210 | ARTS_DRIVER_NAME, "Analog Realtime Synthesizer", | ||
211 | Audio_Available, Audio_CreateDevice | ||
212 | }; | ||
213 | |||
214 | /* This function waits until it is possible to write a full sound buffer */ | ||
215 | static void ARTS_WaitAudio(_THIS) | ||
216 | { | ||
217 | Sint32 ticks; | ||
218 | |||
219 | /* Check to see if the thread-parent process is still alive */ | ||
220 | { static int cnt = 0; | ||
221 | /* Note that this only works with thread implementations | ||
222 | that use a different process id for each thread. | ||
223 | */ | ||
224 | if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ | ||
225 | if ( kill(parent, 0) < 0 ) { | ||
226 | this->enabled = 0; | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | /* Use timer for general audio synchronization */ | ||
232 | ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; | ||
233 | if ( ticks > 0 ) { | ||
234 | SDL_Delay(ticks); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static void ARTS_PlayAudio(_THIS) | ||
239 | { | ||
240 | int written; | ||
241 | |||
242 | /* Write the audio data */ | ||
243 | written = SDL_NAME(arts_write)(stream, mixbuf, mixlen); | ||
244 | |||
245 | /* If timer synchronization is enabled, set the next write frame */ | ||
246 | if ( frame_ticks ) { | ||
247 | next_frame += frame_ticks; | ||
248 | } | ||
249 | |||
250 | /* If we couldn't write, assume fatal error for now */ | ||
251 | if ( written < 0 ) { | ||
252 | this->enabled = 0; | ||
253 | } | ||
254 | #ifdef DEBUG_AUDIO | ||
255 | fprintf(stderr, "Wrote %d bytes of audio data\n", written); | ||
256 | #endif | ||
257 | } | ||
258 | |||
259 | static Uint8 *ARTS_GetAudioBuf(_THIS) | ||
260 | { | ||
261 | return(mixbuf); | ||
262 | } | ||
263 | |||
264 | static void ARTS_CloseAudio(_THIS) | ||
265 | { | ||
266 | if ( mixbuf != NULL ) { | ||
267 | SDL_FreeAudioMem(mixbuf); | ||
268 | mixbuf = NULL; | ||
269 | } | ||
270 | if ( stream ) { | ||
271 | SDL_NAME(arts_close_stream)(stream); | ||
272 | stream = 0; | ||
273 | } | ||
274 | SDL_NAME(arts_free)(); | ||
275 | } | ||
276 | |||
277 | static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec) | ||
278 | { | ||
279 | int bits, frag_spec; | ||
280 | Uint16 test_format, format; | ||
281 | int error_code; | ||
282 | |||
283 | /* Reset the timer synchronization flag */ | ||
284 | frame_ticks = 0.0; | ||
285 | |||
286 | mixbuf = NULL; | ||
287 | |||
288 | /* Try for a closest match on audio format */ | ||
289 | format = 0; | ||
290 | bits = 0; | ||
291 | for ( test_format = SDL_FirstAudioFormat(spec->format); | ||
292 | ! format && test_format; ) { | ||
293 | #ifdef DEBUG_AUDIO | ||
294 | fprintf(stderr, "Trying format 0x%4.4x\n", test_format); | ||
295 | #endif | ||
296 | switch ( test_format ) { | ||
297 | case AUDIO_U8: | ||
298 | bits = 8; | ||
299 | format = 1; | ||
300 | break; | ||
301 | case AUDIO_S16LSB: | ||
302 | bits = 16; | ||
303 | format = 1; | ||
304 | break; | ||
305 | default: | ||
306 | format = 0; | ||
307 | break; | ||
308 | } | ||
309 | if ( ! format ) { | ||
310 | test_format = SDL_NextAudioFormat(); | ||
311 | } | ||
312 | } | ||
313 | if ( format == 0 ) { | ||
314 | SDL_SetError("Couldn't find any hardware audio formats"); | ||
315 | return(-1); | ||
316 | } | ||
317 | spec->format = test_format; | ||
318 | |||
319 | error_code = SDL_NAME(arts_init)(); | ||
320 | if ( error_code != 0 ) { | ||
321 | SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code)); | ||
322 | return(-1); | ||
323 | } | ||
324 | if ( ! ARTS_Suspend() ) { | ||
325 | SDL_SetError("ARTS can not open audio device"); | ||
326 | return(-1); | ||
327 | } | ||
328 | stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL"); | ||
329 | |||
330 | /* Calculate the final parameters for this audio specification */ | ||
331 | SDL_CalculateAudioSpec(spec); | ||
332 | |||
333 | /* Determine the power of two of the fragment size */ | ||
334 | for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); | ||
335 | if ( (0x01<<frag_spec) != spec->size ) { | ||
336 | SDL_SetError("Fragment size must be a power of two"); | ||
337 | return(-1); | ||
338 | } | ||
339 | frag_spec |= 0x00020000; /* two fragments, for low latency */ | ||
340 | |||
341 | #ifdef ARTS_P_PACKET_SETTINGS | ||
342 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec); | ||
343 | #else | ||
344 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff); | ||
345 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16); | ||
346 | #endif | ||
347 | spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE); | ||
348 | |||
349 | /* Allocate mixing buffer */ | ||
350 | mixlen = spec->size; | ||
351 | mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); | ||
352 | if ( mixbuf == NULL ) { | ||
353 | return(-1); | ||
354 | } | ||
355 | SDL_memset(mixbuf, spec->silence, spec->size); | ||
356 | |||
357 | /* Get the parent process id (we're the parent of the audio thread) */ | ||
358 | parent = getpid(); | ||
359 | |||
360 | /* We're ready to rock and roll. :-) */ | ||
361 | return(0); | ||
362 | } | ||
diff --git a/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.h b/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.h new file mode 100644 index 0000000000..de3b22822c --- /dev/null +++ b/apps/plugins/sdl/src/audio/arts/SDL_artsaudio.h | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | SDL - Simple DirectMedia Layer | ||
3 | Copyright (C) 1997-2012 Sam Lantinga | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 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 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | |||
19 | Sam Lantinga | ||
20 | slouken@libsdl.org | ||
21 | */ | ||
22 | #include "SDL_config.h" | ||
23 | |||
24 | #ifndef _SDL_artscaudio_h | ||
25 | #define _SDL_artscaudio_h | ||
26 | |||
27 | #include <artsc.h> | ||
28 | |||
29 | #include "../SDL_sysaudio.h" | ||
30 | |||
31 | /* Hidden "this" pointer for the video functions */ | ||
32 | #define _THIS SDL_AudioDevice *this | ||
33 | |||
34 | struct SDL_PrivateAudioData { | ||
35 | /* The stream descriptor for the audio device */ | ||
36 | arts_stream_t stream; | ||
37 | |||
38 | /* The parent process id, to detect when application quits */ | ||
39 | pid_t parent; | ||
40 | |||
41 | /* Raw mixing buffer */ | ||
42 | Uint8 *mixbuf; | ||
43 | int mixlen; | ||
44 | |||
45 | /* Support for audio timing using a timer, in addition to select() */ | ||
46 | float frame_ticks; | ||
47 | float next_frame; | ||
48 | }; | ||
49 | #define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ | ||
50 | |||
51 | /* Old variable names */ | ||
52 | #define stream (this->hidden->stream) | ||
53 | #define parent (this->hidden->parent) | ||
54 | #define mixbuf (this->hidden->mixbuf) | ||
55 | #define mixlen (this->hidden->mixlen) | ||
56 | #define frame_ticks (this->hidden->frame_ticks) | ||
57 | #define next_frame (this->hidden->next_frame) | ||
58 | |||
59 | #endif /* _SDL_artscaudio_h */ | ||
60 | |||