e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 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_suspended))(void); |
69 | static const char *(*SDL_NAME(arts_error_text))(int errorcode); |
70 | |
71 | static struct { |
72 | const char *name; |
73 | void **func; |
74 | } arts_functions[] = { |
75 | { "arts_init", (void **)&SDL_NAME(arts_init) }, |
76 | { "arts_free", (void **)&SDL_NAME(arts_free) }, |
77 | { "arts_play_stream", (void **)&SDL_NAME(arts_play_stream) }, |
78 | { "arts_stream_set", (void **)&SDL_NAME(arts_stream_set) }, |
79 | { "arts_stream_get", (void **)&SDL_NAME(arts_stream_get) }, |
80 | { "arts_write", (void **)&SDL_NAME(arts_write) }, |
81 | { "arts_close_stream", (void **)&SDL_NAME(arts_close_stream) }, |
82 | { "arts_suspended", (void **)&SDL_NAME(arts_suspended) }, |
83 | { "arts_error_text", (void **)&SDL_NAME(arts_error_text) }, |
84 | }; |
85 | |
86 | static void UnloadARTSLibrary() |
87 | { |
88 | if ( arts_loaded ) { |
89 | SDL_UnloadObject(arts_handle); |
90 | arts_handle = NULL; |
91 | arts_loaded = 0; |
92 | } |
93 | } |
94 | |
95 | static int LoadARTSLibrary(void) |
96 | { |
97 | int i, retval = -1; |
98 | |
99 | arts_handle = SDL_LoadObject(arts_library); |
100 | if ( arts_handle ) { |
101 | arts_loaded = 1; |
102 | retval = 0; |
103 | for ( i=0; i<SDL_arraysize(arts_functions); ++i ) { |
104 | *arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name); |
105 | if ( !*arts_functions[i].func ) { |
106 | retval = -1; |
107 | UnloadARTSLibrary(); |
108 | break; |
109 | } |
110 | } |
111 | } |
112 | return retval; |
113 | } |
114 | |
115 | #else |
116 | |
117 | static void UnloadARTSLibrary() |
118 | { |
119 | return; |
120 | } |
121 | |
122 | static int LoadARTSLibrary(void) |
123 | { |
124 | return 0; |
125 | } |
126 | |
127 | #endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */ |
128 | |
129 | /* Audio driver bootstrap functions */ |
130 | |
131 | static int Audio_Available(void) |
132 | { |
133 | int available = 0; |
134 | |
135 | if ( LoadARTSLibrary() < 0 ) { |
136 | return available; |
137 | } |
138 | if ( SDL_NAME(arts_init)() == 0 ) { |
139 | if ( SDL_NAME(arts_suspended)() ) { |
140 | /* Play a stream so aRts doesn't crash */ |
141 | arts_stream_t stream2; |
142 | stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL"); |
143 | SDL_NAME(arts_write)(stream2, "", 0); |
144 | SDL_NAME(arts_close_stream)(stream2); |
145 | available = 1; |
146 | } |
147 | SDL_NAME(arts_free)(); |
148 | } |
149 | UnloadARTSLibrary(); |
150 | |
151 | return available; |
152 | } |
153 | |
154 | static void Audio_DeleteDevice(SDL_AudioDevice *device) |
155 | { |
156 | SDL_free(device->hidden); |
157 | SDL_free(device); |
158 | UnloadARTSLibrary(); |
159 | } |
160 | |
161 | static SDL_AudioDevice *Audio_CreateDevice(int devindex) |
162 | { |
163 | SDL_AudioDevice *this; |
164 | |
165 | /* Initialize all variables that we clean on shutdown */ |
166 | LoadARTSLibrary(); |
167 | this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); |
168 | if ( this ) { |
169 | SDL_memset(this, 0, (sizeof *this)); |
170 | this->hidden = (struct SDL_PrivateAudioData *) |
171 | SDL_malloc((sizeof *this->hidden)); |
172 | } |
173 | if ( (this == NULL) || (this->hidden == NULL) ) { |
174 | SDL_OutOfMemory(); |
175 | if ( this ) { |
176 | SDL_free(this); |
177 | } |
178 | return(0); |
179 | } |
180 | SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
181 | stream = 0; |
182 | |
183 | /* Set the function pointers */ |
184 | this->OpenAudio = ARTS_OpenAudio; |
185 | this->WaitAudio = ARTS_WaitAudio; |
186 | this->PlayAudio = ARTS_PlayAudio; |
187 | this->GetAudioBuf = ARTS_GetAudioBuf; |
188 | this->CloseAudio = ARTS_CloseAudio; |
189 | |
190 | this->free = Audio_DeleteDevice; |
191 | |
192 | return this; |
193 | } |
194 | |
195 | AudioBootStrap ARTS_bootstrap = { |
196 | ARTS_DRIVER_NAME, "Analog Realtime Synthesizer", |
197 | Audio_Available, Audio_CreateDevice |
198 | }; |
199 | |
200 | /* This function waits until it is possible to write a full sound buffer */ |
201 | static void ARTS_WaitAudio(_THIS) |
202 | { |
203 | Sint32 ticks; |
204 | |
205 | /* Check to see if the thread-parent process is still alive */ |
206 | { static int cnt = 0; |
207 | /* Note that this only works with thread implementations |
208 | that use a different process id for each thread. |
209 | */ |
210 | if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ |
211 | if ( kill(parent, 0) < 0 ) { |
212 | this->enabled = 0; |
213 | } |
214 | } |
215 | } |
216 | |
217 | /* Use timer for general audio synchronization */ |
218 | ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; |
219 | if ( ticks > 0 ) { |
220 | SDL_Delay(ticks); |
221 | } |
222 | } |
223 | |
224 | static void ARTS_PlayAudio(_THIS) |
225 | { |
226 | int written; |
227 | |
228 | /* Write the audio data */ |
229 | written = SDL_NAME(arts_write)(stream, mixbuf, mixlen); |
230 | |
231 | /* If timer synchronization is enabled, set the next write frame */ |
232 | if ( frame_ticks ) { |
233 | next_frame += frame_ticks; |
234 | } |
235 | |
236 | /* If we couldn't write, assume fatal error for now */ |
237 | if ( written < 0 ) { |
238 | this->enabled = 0; |
239 | } |
240 | #ifdef DEBUG_AUDIO |
241 | fprintf(stderr, "Wrote %d bytes of audio data\n", written); |
242 | #endif |
243 | } |
244 | |
245 | static Uint8 *ARTS_GetAudioBuf(_THIS) |
246 | { |
247 | return(mixbuf); |
248 | } |
249 | |
250 | static void ARTS_CloseAudio(_THIS) |
251 | { |
252 | if ( mixbuf != NULL ) { |
253 | SDL_FreeAudioMem(mixbuf); |
254 | mixbuf = NULL; |
255 | } |
256 | if ( stream ) { |
257 | SDL_NAME(arts_close_stream)(stream); |
258 | stream = 0; |
259 | } |
260 | SDL_NAME(arts_free)(); |
261 | } |
262 | |
263 | static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec) |
264 | { |
265 | int bits, frag_spec; |
266 | Uint16 test_format, format; |
267 | int error_code; |
268 | |
269 | /* Reset the timer synchronization flag */ |
270 | frame_ticks = 0.0; |
271 | |
272 | mixbuf = NULL; |
273 | |
274 | /* Try for a closest match on audio format */ |
275 | format = 0; |
276 | bits = 0; |
277 | for ( test_format = SDL_FirstAudioFormat(spec->format); |
278 | ! format && test_format; ) { |
279 | #ifdef DEBUG_AUDIO |
280 | fprintf(stderr, "Trying format 0x%4.4x\n", test_format); |
281 | #endif |
282 | switch ( test_format ) { |
283 | case AUDIO_U8: |
284 | bits = 8; |
285 | format = 1; |
286 | break; |
287 | case AUDIO_S16LSB: |
288 | bits = 16; |
289 | format = 1; |
290 | break; |
291 | default: |
292 | format = 0; |
293 | break; |
294 | } |
295 | if ( ! format ) { |
296 | test_format = SDL_NextAudioFormat(); |
297 | } |
298 | } |
299 | if ( format == 0 ) { |
300 | SDL_SetError("Couldn't find any hardware audio formats"); |
301 | return(-1); |
302 | } |
303 | spec->format = test_format; |
304 | |
305 | error_code = SDL_NAME(arts_init)(); |
306 | if ( error_code != 0 ) { |
307 | SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code)); |
308 | return(-1); |
309 | } |
310 | if ( ! SDL_NAME(arts_suspended)() ) { |
311 | SDL_SetError("ARTS can not open audio device"); |
312 | return(-1); |
313 | } |
314 | stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL"); |
315 | |
316 | /* Calculate the final parameters for this audio specification */ |
317 | SDL_CalculateAudioSpec(spec); |
318 | |
319 | /* Determine the power of two of the fragment size */ |
320 | for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); |
321 | if ( (0x01<<frag_spec) != spec->size ) { |
322 | SDL_SetError("Fragment size must be a power of two"); |
323 | return(-1); |
324 | } |
325 | frag_spec |= 0x00020000; /* two fragments, for low latency */ |
326 | |
327 | #ifdef ARTS_P_PACKET_SETTINGS |
328 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec); |
329 | #else |
330 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff); |
331 | SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16); |
332 | #endif |
333 | spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE); |
334 | |
335 | /* Allocate mixing buffer */ |
336 | mixlen = spec->size; |
337 | mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); |
338 | if ( mixbuf == NULL ) { |
339 | return(-1); |
340 | } |
341 | SDL_memset(mixbuf, spec->silence, spec->size); |
342 | |
343 | /* Get the parent process id (we're the parent of the audio thread) */ |
344 | parent = getpid(); |
345 | |
346 | /* We're ready to rock and roll. :-) */ |
347 | return(0); |
348 | } |