Mercurial > sdl-ios-xcode
annotate src/audio/dma/SDL_dmaaudio.c @ 1118:65f4b2dd46b7
Date: Wed, 17 Aug 2005 11:23:40 -0400
From: Matt L <prometheus.uw@gmail.com>
To: "Ryan C. Gordon" <icculus@clutteredmind.org>
Subject: SDL Patch
Hio,
I saw your last call on the mailing list. Here's a patch which I
submitted about two weeks ago which hasn't made it in.
In the current sdl.m4, there's a bug where if your configure.ac, you
have AC_LANG(C++) specified, it won't properly compile and link the SDL
test program when you run the configure script. This is because only the
default CFLAGS is overriden in sdl.m4, and in the patch below, I've
fixed it so it overrides CXXFLAGS as well, allowing it to work with g++.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 18 Aug 2005 06:06:02 +0000 |
parents | eec28a5278be |
children | c9b51268668f |
rev | line source |
---|---|
0 | 1 /* |
2 SDL - Simple DirectMedia Layer | |
769
b8d311d90021
Updated copyright information for 2004 (Happy New Year!)
Sam Lantinga <slouken@libsdl.org>
parents:
354
diff
changeset
|
3 Copyright (C) 1997-2004 Sam Lantinga |
0 | 4 |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 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 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
252
e8157fcb3114
Updated the source with the correct e-mail address
Sam Lantinga <slouken@libsdl.org>
parents:
94
diff
changeset
|
20 slouken@libsdl.org |
0 | 21 */ |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* Allow access to a raw mixing buffer */ | |
29 | |
30 #include <stdlib.h> | |
31 #include <stdio.h> | |
32 #include <string.h> | |
33 #include <errno.h> | |
34 #include <unistd.h> | |
35 #include <fcntl.h> | |
36 #include <signal.h> | |
37 #include <sys/types.h> | |
38 #include <sys/time.h> | |
39 #include <sys/ioctl.h> | |
40 #include <sys/stat.h> | |
41 #include <sys/mman.h> | |
94
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
42 #ifdef OSS_USE_SOUNDCARD_H |
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
43 /* This is installed on some systems */ |
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
44 #include <soundcard.h> |
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
45 #else |
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
46 /* This is recommended by OSS */ |
0 | 47 #include <sys/soundcard.h> |
94
ae6e6b73333f
Cleaned up the OpenBSD port, thanks to Peter Valchev
Sam Lantinga <slouken@lokigames.com>
parents:
92
diff
changeset
|
48 #endif |
0 | 49 |
50 #ifndef MAP_FAILED | |
51 #define MAP_FAILED ((Uint8 *)-1) | |
52 #endif | |
53 | |
54 #include "SDL_audio.h" | |
55 #include "SDL_error.h" | |
56 #include "SDL_audiomem.h" | |
57 #include "SDL_audio_c.h" | |
58 #include "SDL_timer.h" | |
59 #include "SDL_audiodev_c.h" | |
60 #include "SDL_dmaaudio.h" | |
61 | |
62 /* The tag name used by DMA audio */ | |
63 #define DMA_DRIVER_NAME "dma" | |
64 | |
65 /* Open the audio device for playback, and don't block if busy */ | |
66 #define OPEN_FLAGS (O_RDWR|O_NONBLOCK) | |
67 | |
68 /* Audio driver functions */ | |
69 static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec); | |
70 static void DMA_WaitAudio(_THIS); | |
71 static void DMA_PlayAudio(_THIS); | |
72 static Uint8 *DMA_GetAudioBuf(_THIS); | |
73 static void DMA_CloseAudio(_THIS); | |
74 | |
75 /* Audio driver bootstrap functions */ | |
76 | |
77 static int Audio_Available(void) | |
78 { | |
79 int available; | |
80 int fd; | |
81 | |
82 available = 0; | |
83 | |
84 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); | |
85 if ( fd >= 0 ) { | |
86 int caps; | |
87 struct audio_buf_info info; | |
88 | |
89 if ( (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) && | |
90 (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && | |
91 (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0) ) { | |
92 available = 1; | |
93 } | |
94 close(fd); | |
95 } | |
96 return(available); | |
97 } | |
98 | |
99 static void Audio_DeleteDevice(SDL_AudioDevice *device) | |
100 { | |
101 free(device->hidden); | |
102 free(device); | |
103 } | |
104 | |
105 static SDL_AudioDevice *Audio_CreateDevice(int devindex) | |
106 { | |
107 SDL_AudioDevice *this; | |
108 | |
109 /* Initialize all variables that we clean on shutdown */ | |
110 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
111 if ( this ) { | |
112 memset(this, 0, (sizeof *this)); | |
113 this->hidden = (struct SDL_PrivateAudioData *) | |
114 malloc((sizeof *this->hidden)); | |
115 } | |
116 if ( (this == NULL) || (this->hidden == NULL) ) { | |
117 SDL_OutOfMemory(); | |
118 if ( this ) { | |
119 free(this); | |
120 } | |
121 return(0); | |
122 } | |
123 memset(this->hidden, 0, (sizeof *this->hidden)); | |
124 audio_fd = -1; | |
125 | |
126 /* Set the function pointers */ | |
127 this->OpenAudio = DMA_OpenAudio; | |
128 this->WaitAudio = DMA_WaitAudio; | |
129 this->PlayAudio = DMA_PlayAudio; | |
130 this->GetAudioBuf = DMA_GetAudioBuf; | |
131 this->CloseAudio = DMA_CloseAudio; | |
132 | |
133 this->free = Audio_DeleteDevice; | |
134 | |
135 return this; | |
136 } | |
137 | |
138 AudioBootStrap DMA_bootstrap = { | |
139 DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", | |
140 Audio_Available, Audio_CreateDevice | |
141 }; | |
142 | |
143 /* This function waits until it is possible to write a full sound buffer */ | |
144 static void DMA_WaitAudio(_THIS) | |
145 { | |
146 fd_set fdset; | |
147 | |
148 /* Check to see if the thread-parent process is still alive */ | |
149 { static int cnt = 0; | |
150 /* Note that this only works with thread implementations | |
151 that use a different process id for each thread. | |
152 */ | |
153 if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ | |
154 if ( kill(parent, 0) < 0 ) { | |
155 this->enabled = 0; | |
156 } | |
157 } | |
158 } | |
159 | |
160 /* See if we need to use timed audio synchronization */ | |
161 if ( frame_ticks ) { | |
162 /* Use timer for general audio synchronization */ | |
163 Sint32 ticks; | |
164 | |
165 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; | |
166 if ( ticks > 0 ) { | |
167 SDL_Delay(ticks); | |
168 } | |
169 } else { | |
170 /* Use select() for audio synchronization */ | |
171 struct timeval timeout; | |
172 FD_ZERO(&fdset); | |
173 FD_SET(audio_fd, &fdset); | |
174 timeout.tv_sec = 10; | |
175 timeout.tv_usec = 0; | |
176 #ifdef DEBUG_AUDIO | |
177 fprintf(stderr, "Waiting for audio to get ready\n"); | |
178 #endif | |
179 if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { | |
180 const char *message = | |
181 #ifdef AUDIO_OSPACE_HACK | |
182 "Audio timeout - buggy audio driver? (trying ospace)"; | |
183 #else | |
184 "Audio timeout - buggy audio driver? (disabled)"; | |
185 #endif | |
186 /* In general we should never print to the screen, | |
187 but in this case we have no other way of letting | |
188 the user know what happened. | |
189 */ | |
190 fprintf(stderr, "SDL: %s\n", message); | |
191 #ifdef AUDIO_OSPACE_HACK | |
192 /* We may be able to use GET_OSPACE trick */ | |
193 frame_ticks = (float)(this->spec->samples*1000) / | |
194 this->spec->freq; | |
195 next_frame = SDL_GetTicks()+frame_ticks; | |
196 #else | |
197 this->enabled = 0; | |
198 /* Don't try to close - may hang */ | |
199 audio_fd = -1; | |
200 #ifdef DEBUG_AUDIO | |
201 fprintf(stderr, "Done disabling audio\n"); | |
202 #endif | |
203 #endif /* AUDIO_OSPACE_HACK */ | |
204 } | |
205 #ifdef DEBUG_AUDIO | |
206 fprintf(stderr, "Ready!\n"); | |
207 #endif | |
208 } | |
209 } | |
210 | |
211 static void DMA_PlayAudio(_THIS) | |
212 { | |
213 /* If timer synchronization is enabled, set the next write frame */ | |
214 if ( frame_ticks ) { | |
215 next_frame += frame_ticks; | |
216 } | |
217 return; | |
218 } | |
219 | |
220 static Uint8 *DMA_GetAudioBuf(_THIS) | |
221 { | |
222 count_info info; | |
223 int playing; | |
224 int filling; | |
225 | |
226 /* Get number of blocks, looping if we're not using select() */ | |
227 do { | |
228 if ( ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) < 0 ) { | |
229 /* Uh oh... */ | |
230 this->enabled = 0; | |
231 return(NULL); | |
232 } | |
233 } while ( frame_ticks && (info.blocks < 1) ); | |
234 #ifdef DEBUG_AUDIO | |
235 if ( info.blocks > 1 ) { | |
236 printf("Warning: audio underflow (%d frags)\n", info.blocks-1); | |
237 } | |
238 #endif | |
239 playing = info.ptr / this->spec.size; | |
240 filling = (playing + 1)%num_buffers; | |
241 return (dma_buf + (filling * this->spec.size)); | |
242 } | |
243 | |
244 static void DMA_CloseAudio(_THIS) | |
245 { | |
246 if ( dma_buf != NULL ) { | |
247 munmap(dma_buf, dma_len); | |
248 dma_buf = NULL; | |
249 } | |
250 if ( audio_fd >= 0 ) { | |
251 close(audio_fd); | |
252 audio_fd = -1; | |
253 } | |
254 } | |
255 | |
256 static int DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo, | |
257 SDL_AudioSpec *spec) | |
258 { | |
259 int frag_spec; | |
260 int value; | |
261 | |
262 /* Close and then reopen the audio device */ | |
263 close(audio_fd); | |
264 audio_fd = open(audiodev, O_RDWR, 0); | |
265 if ( audio_fd < 0 ) { | |
266 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | |
267 return(-1); | |
268 } | |
269 | |
270 /* Calculate the final parameters for this audio specification */ | |
271 SDL_CalculateAudioSpec(spec); | |
272 | |
273 /* Determine the power of two of the fragment size */ | |
274 for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); | |
275 if ( (0x01<<frag_spec) != spec->size ) { | |
276 SDL_SetError("Fragment size must be a power of two"); | |
277 return(-1); | |
278 } | |
279 | |
280 /* Set the audio buffering parameters */ | |
281 if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) { | |
282 SDL_SetError("Couldn't set audio fragment spec"); | |
283 return(-1); | |
284 } | |
285 | |
286 /* Set the audio format */ | |
287 value = format; | |
288 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || | |
289 (value != format) ) { | |
290 SDL_SetError("Couldn't set audio format"); | |
291 return(-1); | |
292 } | |
293 | |
294 /* Set mono or stereo audio */ | |
295 value = (spec->channels > 1); | |
296 if ( (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || | |
297 (value != stereo) ) { | |
298 SDL_SetError("Couldn't set audio channels"); | |
299 return(-1); | |
300 } | |
301 | |
302 /* Set the DSP frequency */ | |
303 value = spec->freq; | |
960
eec28a5278be
Date: Sat, 9 Oct 2004 02:46:18 +0300 (EEST)
Sam Lantinga <slouken@libsdl.org>
parents:
769
diff
changeset
|
304 if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) { |
0 | 305 SDL_SetError("Couldn't set audio frequency"); |
306 return(-1); | |
307 } | |
308 spec->freq = value; | |
309 | |
310 /* We successfully re-opened the audio */ | |
311 return(0); | |
312 } | |
313 | |
314 static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
315 { | |
316 char audiodev[1024]; | |
317 int format; | |
318 int stereo; | |
319 int value; | |
320 Uint16 test_format; | |
321 struct audio_buf_info info; | |
322 | |
323 /* Reset the timer synchronization flag */ | |
324 frame_ticks = 0.0; | |
325 | |
326 /* Open the audio device */ | |
327 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); | |
328 if ( audio_fd < 0 ) { | |
329 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | |
330 return(-1); | |
331 } | |
332 dma_buf = NULL; | |
333 ioctl(audio_fd, SNDCTL_DSP_RESET, 0); | |
334 | |
335 /* Get a list of supported hardware formats */ | |
336 if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) { | |
337 SDL_SetError("Couldn't get audio format list"); | |
338 return(-1); | |
339 } | |
340 | |
341 /* Try for a closest match on audio format */ | |
342 format = 0; | |
343 for ( test_format = SDL_FirstAudioFormat(spec->format); | |
344 ! format && test_format; ) { | |
345 #ifdef DEBUG_AUDIO | |
346 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); | |
347 #endif | |
348 switch ( test_format ) { | |
349 case AUDIO_U8: | |
350 if ( value & AFMT_U8 ) { | |
351 format = AFMT_U8; | |
352 } | |
353 break; | |
354 case AUDIO_S8: | |
355 if ( value & AFMT_S8 ) { | |
356 format = AFMT_S8; | |
357 } | |
358 break; | |
359 case AUDIO_S16LSB: | |
360 if ( value & AFMT_S16_LE ) { | |
361 format = AFMT_S16_LE; | |
362 } | |
363 break; | |
364 case AUDIO_S16MSB: | |
365 if ( value & AFMT_S16_BE ) { | |
366 format = AFMT_S16_BE; | |
367 } | |
368 break; | |
369 case AUDIO_U16LSB: | |
370 if ( value & AFMT_U16_LE ) { | |
371 format = AFMT_U16_LE; | |
372 } | |
373 break; | |
374 case AUDIO_U16MSB: | |
375 if ( value & AFMT_U16_BE ) { | |
376 format = AFMT_U16_BE; | |
377 } | |
378 break; | |
379 default: | |
354
30935e76acb5
Updated ALSA audio support for ALSA 0.9
Sam Lantinga <slouken@libsdl.org>
parents:
297
diff
changeset
|
380 format = 0; |
0 | 381 break; |
382 } | |
383 if ( ! format ) { | |
384 test_format = SDL_NextAudioFormat(); | |
385 } | |
386 } | |
387 if ( format == 0 ) { | |
388 SDL_SetError("Couldn't find any hardware audio formats"); | |
389 return(-1); | |
390 } | |
391 spec->format = test_format; | |
392 | |
393 /* Set the audio format */ | |
394 value = format; | |
395 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || | |
396 (value != format) ) { | |
397 SDL_SetError("Couldn't set audio format"); | |
398 return(-1); | |
399 } | |
400 | |
401 /* Set mono or stereo audio (currently only two channels supported) */ | |
402 stereo = (spec->channels > 1); | |
403 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); | |
404 if ( stereo ) { | |
405 spec->channels = 2; | |
406 } else { | |
407 spec->channels = 1; | |
408 } | |
409 | |
410 /* Because some drivers don't allow setting the buffer size | |
411 after setting the format, we must re-open the audio device | |
412 once we know what format and channels are supported | |
413 */ | |
414 if ( DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0 ) { | |
415 /* Error is set by DMA_ReopenAudio() */ | |
416 return(-1); | |
417 } | |
418 | |
419 /* Memory map the audio buffer */ | |
420 if ( ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0 ) { | |
421 SDL_SetError("Couldn't get OSPACE parameters"); | |
422 return(-1); | |
423 } | |
424 spec->size = info.fragsize; | |
425 spec->samples = spec->size / ((spec->format & 0xFF) / 8); | |
426 spec->samples /= spec->channels; | |
427 num_buffers = info.fragstotal; | |
428 dma_len = num_buffers*spec->size; | |
429 dma_buf = (Uint8 *)mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, | |
430 audio_fd, 0); | |
431 if ( dma_buf == MAP_FAILED ) { | |
432 SDL_SetError("DMA memory map failed"); | |
433 dma_buf = NULL; | |
434 return(-1); | |
435 } | |
436 memset(dma_buf, spec->silence, dma_len); | |
437 | |
438 /* Check to see if we need to use select() workaround */ | |
439 { char *workaround; | |
440 workaround = getenv("SDL_DSP_NOSELECT"); | |
441 if ( workaround ) { | |
442 frame_ticks = (float)(spec->samples*1000)/spec->freq; | |
443 next_frame = SDL_GetTicks()+frame_ticks; | |
444 } | |
445 } | |
446 | |
447 /* Trigger audio playback */ | |
448 value = 0; | |
449 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); | |
450 value = PCM_ENABLE_OUTPUT; | |
451 if ( ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0 ) { | |
452 SDL_SetError("Couldn't trigger audio output"); | |
453 return(-1); | |
454 } | |
455 | |
456 /* Get the parent process id (we're the parent of the audio thread) */ | |
457 parent = getpid(); | |
458 | |
459 /* We're ready to rock and roll. :-) */ | |
460 return(0); | |
461 } |