Mercurial > sdl-ios-xcode
comparison src/audio/dma/SDL_dmaaudio.c @ 2049:5f6550e5184f
Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 09:15:21 +0000 |
parents | 3b4ce57c6215 |
children | 866052b01ee5 |
comparison
equal
deleted
inserted
replaced
2048:6067c7f9a672 | 2049:5f6550e5184f |
---|---|
18 | 18 |
19 Sam Lantinga | 19 Sam Lantinga |
20 slouken@libsdl.org | 20 slouken@libsdl.org |
21 */ | 21 */ |
22 #include "SDL_config.h" | 22 #include "SDL_config.h" |
23 | |
24 /* !!! FIXME: merge this driver with "dsp". */ | |
23 | 25 |
24 /* Allow access to a raw mixing buffer */ | 26 /* Allow access to a raw mixing buffer */ |
25 | 27 |
26 #include <stdio.h> | 28 #include <stdio.h> |
27 #include <string.h> /* For strerror() */ | 29 #include <string.h> /* For strerror() */ |
55 | 57 |
56 /* The tag name used by DMA audio */ | 58 /* The tag name used by DMA audio */ |
57 #define DMA_DRIVER_NAME "dma" | 59 #define DMA_DRIVER_NAME "dma" |
58 | 60 |
59 /* Open the audio device for playback, and don't block if busy */ | 61 /* Open the audio device for playback, and don't block if busy */ |
60 #define OPEN_FLAGS (O_RDWR|O_NONBLOCK) | 62 #define OPEN_FLAGS_INPUT (O_RDWR|O_NONBLOCK) |
61 | 63 #define OPEN_FLAGS_OUTPUT (O_RDWR|O_NONBLOCK) |
62 /* Audio driver functions */ | 64 |
63 static int DMA_OpenAudio(_THIS, SDL_AudioSpec * spec); | 65 static char **outputDevices = NULL; |
64 static void DMA_WaitAudio(_THIS); | 66 static int outputDeviceCount = 0; |
65 static void DMA_PlayAudio(_THIS); | 67 static char **inputDevices = NULL; |
66 static Uint8 *DMA_GetAudioBuf(_THIS); | 68 static int inputDeviceCount = 0; |
67 static void DMA_CloseAudio(_THIS); | |
68 | |
69 /* Audio driver bootstrap functions */ | |
70 | 69 |
71 static int | 70 static int |
72 Audio_Available(void) | 71 test_for_mmap(int fd) |
73 { | 72 { |
74 int available; | 73 int caps = 0; |
75 int fd; | 74 struct audio_buf_info info; |
76 | 75 if ((ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) && |
77 available = 0; | 76 (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && |
78 | 77 (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0)) |
79 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); | 78 { |
80 if (fd >= 0) { | 79 size_t len = info.fragstotal * info.fragsize; |
81 int caps; | 80 Uint8 *buf = (Uint8 *) mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0); |
82 struct audio_buf_info info; | 81 if (buf != MAP_FAILED) { |
83 | 82 munmap(buf, len); |
84 if ((ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) && | 83 return 1; |
85 (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && | 84 } |
86 (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0)) { | 85 } |
87 available = 1; | 86 return 0; |
88 } | 87 } |
89 close(fd); | 88 |
90 } | 89 |
91 return (available); | 90 static inline void |
92 } | 91 free_device_list(char ***devs, int *count) |
92 { | |
93 SDL_FreeUnixAudioDevices(devs, count); | |
94 } | |
95 | |
96 static inline void | |
97 build_device_list(int iscapture, char ***devs, int *count) | |
98 { | |
99 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); | |
100 free_device_list(devs, count); | |
101 SDL_EnumUnixAudioDevices(flags, 0, test_for_mmap, devs, count); | |
102 } | |
103 | |
104 static inline void | |
105 build_device_lists(void) | |
106 { | |
107 build_device_list(0, &outputDevices, &outputDeviceCount); | |
108 build_device_list(1, &inputDevices, &inputDeviceCount); | |
109 } | |
110 | |
111 | |
112 static inline void | |
113 free_device_lists(void) | |
114 { | |
115 free_device_list(&outputDevices, &outputDeviceCount); | |
116 free_device_list(&inputDevices, &inputDeviceCount); | |
117 } | |
118 | |
119 | |
120 static void DMA_Deinitialize(void) | |
121 { | |
122 free_device_lists(); | |
123 } | |
124 | |
125 static int | |
126 DMA_DetectDevices(int iscapture) | |
127 { | |
128 if (iscapture) { | |
129 build_device_list(1, &inputDevices, &inputDeviceCount); | |
130 return inputDeviceCount; | |
131 } else { | |
132 build_device_list(0, &outputDevices, &outputDeviceCount); | |
133 return outputDeviceCount; | |
134 } | |
135 | |
136 return 0; /* shouldn't ever hit this. */ | |
137 } | |
138 | |
139 | |
140 static const char * | |
141 DMA_GetDeviceName(int index, int iscapture) | |
142 { | |
143 if ((iscapture) && (index < inputDeviceCount)) { | |
144 return inputDevices[index]; | |
145 } else if ((!iscapture) && (index < outputDeviceCount)) { | |
146 return outputDevices[index]; | |
147 } | |
148 | |
149 SDL_SetError("No such device"); | |
150 return NULL; | |
151 } | |
152 | |
153 | |
154 static int | |
155 DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo) | |
156 { | |
157 int frag_spec; | |
158 int value; | |
159 | |
160 /* Close and then reopen the audio device */ | |
161 close(audio_fd); | |
162 audio_fd = open(audiodev, O_RDWR, 0); | |
163 if (audio_fd < 0) { | |
164 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | |
165 return (-1); | |
166 } | |
167 | |
168 /* Calculate the final parameters for this audio specification */ | |
169 SDL_CalculateAudioSpec(&this->spec); | |
170 | |
171 /* Determine the power of two of the fragment size */ | |
172 for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec); | |
173 if ((0x01 << frag_spec) != this->spec.size) { | |
174 SDL_SetError("Fragment size must be a power of two"); | |
175 return (-1); | |
176 } | |
177 | |
178 /* Set the audio buffering parameters */ | |
179 if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { | |
180 SDL_SetError("Couldn't set audio fragment spec"); | |
181 return (-1); | |
182 } | |
183 | |
184 /* Set the audio format */ | |
185 value = format; | |
186 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { | |
187 SDL_SetError("Couldn't set audio format"); | |
188 return (-1); | |
189 } | |
190 | |
191 /* Set mono or stereo audio */ | |
192 value = (this->spec.channels > 1); | |
193 if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || | |
194 (value != stereo)) { | |
195 SDL_SetError("Couldn't set audio channels"); | |
196 return (-1); | |
197 } | |
198 | |
199 /* Set the DSP frequency */ | |
200 value = this->spec.freq; | |
201 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { | |
202 SDL_SetError("Couldn't set audio frequency"); | |
203 return (-1); | |
204 } | |
205 this->spec.freq = value; | |
206 | |
207 /* We successfully re-opened the audio */ | |
208 return (0); | |
209 } | |
210 | |
93 | 211 |
94 static void | 212 static void |
95 Audio_DeleteDevice(SDL_AudioDevice * device) | 213 DMA_CloseDevice(_THIS) |
96 { | 214 { |
97 SDL_free(device->hidden); | 215 if (this->hidden != NULL) { |
98 SDL_free(device); | 216 if (dma_buf != NULL) { |
99 } | 217 munmap(dma_buf, dma_len); |
100 | 218 dma_buf = NULL; |
101 static SDL_AudioDevice * | 219 } |
102 Audio_CreateDevice(int devindex) | 220 if (audio_fd >= 0) { |
103 { | 221 close(audio_fd); |
104 SDL_AudioDevice *this; | 222 audio_fd = -1; |
223 } | |
224 SDL_free(this->hidden); | |
225 this->hidden = NULL; | |
226 } | |
227 } | |
228 | |
229 | |
230 static int | |
231 DMA_OpenDevice(_THIS, const char *devname, int iscapture) | |
232 { | |
233 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); | |
234 int format; | |
235 int stereo; | |
236 int value; | |
237 SDL_AudioFormat test_format; | |
238 struct audio_buf_info info; | |
239 | |
240 /* We don't care what the devname is...we'll try to open anything. */ | |
241 /* ...but default to first name in the list... */ | |
242 if (devname == NULL) { | |
243 if ( ((iscapture) && (inputDeviceCount == 0)) || | |
244 ((!iscapture) && (outputDeviceCount == 0)) ) { | |
245 SDL_SetError("No such audio device"); | |
246 return 0; | |
247 } | |
248 devname = ((iscapture) ? inputDevices[0] : outputDevices[0]); | |
249 } | |
105 | 250 |
106 /* Initialize all variables that we clean on shutdown */ | 251 /* Initialize all variables that we clean on shutdown */ |
107 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | 252 this->hidden = (struct SDL_PrivateAudioData *) |
108 if (this) { | 253 SDL_malloc((sizeof *this->hidden)); |
109 SDL_memset(this, 0, (sizeof *this)); | 254 if (this->hidden == NULL) { |
110 this->hidden = (struct SDL_PrivateAudioData *) | |
111 SDL_malloc((sizeof *this->hidden)); | |
112 } | |
113 if ((this == NULL) || (this->hidden == NULL)) { | |
114 SDL_OutOfMemory(); | 255 SDL_OutOfMemory(); |
115 if (this) { | 256 return 0; |
116 SDL_free(this); | |
117 } | |
118 return (0); | |
119 } | 257 } |
120 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | 258 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
121 audio_fd = -1; | 259 |
122 | 260 /* Open the audio device */ |
123 /* Set the function pointers */ | 261 audio_fd = open(devname, flags, 0); |
124 this->OpenAudio = DMA_OpenAudio; | 262 if (audio_fd < 0) { |
125 this->WaitAudio = DMA_WaitAudio; | 263 DMA_CloseDevice(this); |
126 this->PlayAudio = DMA_PlayAudio; | 264 SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); |
127 this->GetAudioBuf = DMA_GetAudioBuf; | 265 return 0; |
128 this->CloseAudio = DMA_CloseAudio; | 266 } |
129 | 267 dma_buf = NULL; |
130 this->free = Audio_DeleteDevice; | 268 ioctl(audio_fd, SNDCTL_DSP_RESET, 0); |
131 | 269 |
132 return this; | 270 /* Get a list of supported hardware formats */ |
133 } | 271 if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { |
134 | 272 DMA_CloseDevice(this); |
135 AudioBootStrap DMA_bootstrap = { | 273 SDL_SetError("Couldn't get audio format list"); |
136 DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", | 274 return 0; |
137 Audio_Available, Audio_CreateDevice | 275 } |
138 }; | 276 |
277 /* Try for a closest match on audio format */ | |
278 format = 0; | |
279 for (test_format = SDL_FirstAudioFormat(this->spec.format); | |
280 !format && test_format;) { | |
281 #ifdef DEBUG_AUDIO | |
282 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); | |
283 #endif | |
284 switch (test_format) { | |
285 case AUDIO_U8: | |
286 if (value & AFMT_U8) { | |
287 format = AFMT_U8; | |
288 } | |
289 break; | |
290 case AUDIO_S8: | |
291 if (value & AFMT_S8) { | |
292 format = AFMT_S8; | |
293 } | |
294 break; | |
295 case AUDIO_S16LSB: | |
296 if (value & AFMT_S16_LE) { | |
297 format = AFMT_S16_LE; | |
298 } | |
299 break; | |
300 case AUDIO_S16MSB: | |
301 if (value & AFMT_S16_BE) { | |
302 format = AFMT_S16_BE; | |
303 } | |
304 break; | |
305 case AUDIO_U16LSB: | |
306 if (value & AFMT_U16_LE) { | |
307 format = AFMT_U16_LE; | |
308 } | |
309 break; | |
310 case AUDIO_U16MSB: | |
311 if (value & AFMT_U16_BE) { | |
312 format = AFMT_U16_BE; | |
313 } | |
314 break; | |
315 default: | |
316 format = 0; | |
317 break; | |
318 } | |
319 if (!format) { | |
320 test_format = SDL_NextAudioFormat(); | |
321 } | |
322 } | |
323 if (format == 0) { | |
324 DMA_CloseDevice(this); | |
325 SDL_SetError("Couldn't find any hardware audio formats"); | |
326 return 0; | |
327 } | |
328 this->spec.format = test_format; | |
329 | |
330 /* Set the audio format */ | |
331 value = format; | |
332 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { | |
333 DMA_CloseDevice(this); | |
334 SDL_SetError("Couldn't set audio format"); | |
335 return 0; | |
336 } | |
337 | |
338 /* Set mono or stereo audio (currently only two channels supported) */ | |
339 stereo = (this->spec.channels > 1); | |
340 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); | |
341 if (stereo) { | |
342 this->spec.channels = 2; | |
343 } else { | |
344 this->spec.channels = 1; | |
345 } | |
346 | |
347 /* Because some drivers don't allow setting the buffer size | |
348 after setting the format, we must re-open the audio device | |
349 once we know what format and channels are supported | |
350 */ | |
351 if (DMA_ReopenAudio(this, devname, format, stereo) < 0) { | |
352 DMA_CloseDevice(this); | |
353 /* Error is set by DMA_ReopenAudio() */ | |
354 return 0; | |
355 } | |
356 | |
357 /* Memory map the audio buffer */ | |
358 if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { | |
359 DMA_CloseDevice(this); | |
360 SDL_SetError("Couldn't get OSPACE parameters"); | |
361 return 0; | |
362 } | |
363 this->spec.size = info.fragsize; | |
364 this->spec.samples = this->spec.size / ((this->spec.format & 0xFF) / 8); | |
365 this->spec.samples /= this->spec.channels; | |
366 num_buffers = info.fragstotal; | |
367 dma_len = num_buffers * this->spec.size; | |
368 dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, | |
369 audio_fd, 0); | |
370 if (dma_buf == MAP_FAILED) { | |
371 DMA_CloseDevice(this); | |
372 SDL_SetError("DMA memory map failed"); | |
373 dma_buf = NULL; | |
374 return 0; | |
375 } | |
376 SDL_memset(dma_buf, this->spec.silence, dma_len); | |
377 | |
378 /* Check to see if we need to use select() workaround */ | |
379 { | |
380 char *workaround; | |
381 workaround = SDL_getenv("SDL_DSP_NOSELECT"); | |
382 if (workaround) { | |
383 frame_ticks = (float) (this->spec.samples*1000) / this->spec.freq; | |
384 next_frame = SDL_GetTicks() + frame_ticks; | |
385 } | |
386 } | |
387 | |
388 /* Trigger audio playback */ | |
389 value = 0; | |
390 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); | |
391 value = PCM_ENABLE_OUTPUT; | |
392 if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) { | |
393 DMA_CloseDevice(this); | |
394 SDL_SetError("Couldn't trigger audio output"); | |
395 return 0; | |
396 } | |
397 | |
398 /* Get the parent process id (we're the parent of the audio thread) */ | |
399 parent = getpid(); | |
400 | |
401 /* We're ready to rock and roll. :-) */ | |
402 return 1; | |
403 } | |
404 | |
139 | 405 |
140 /* This function waits until it is possible to write a full sound buffer */ | 406 /* This function waits until it is possible to write a full sound buffer */ |
141 static void | 407 static void |
142 DMA_WaitAudio(_THIS) | 408 DMA_WaitDevice(_THIS) |
143 { | 409 { |
144 fd_set fdset; | 410 fd_set fdset; |
145 | 411 |
146 /* Check to see if the thread-parent process is still alive */ | 412 /* Check to see if the thread-parent process is still alive */ |
147 { | 413 { |
187 the user know what happened. | 453 the user know what happened. |
188 */ | 454 */ |
189 fprintf(stderr, "SDL: %s\n", message); | 455 fprintf(stderr, "SDL: %s\n", message); |
190 #ifdef AUDIO_OSPACE_HACK | 456 #ifdef AUDIO_OSPACE_HACK |
191 /* We may be able to use GET_OSPACE trick */ | 457 /* We may be able to use GET_OSPACE trick */ |
192 frame_ticks = (float) (this->spec->samples * 1000) / | 458 frame_ticks = (float) (this->spec.samples * 1000) / |
193 this->spec->freq; | 459 this->spec.freq; |
194 next_frame = SDL_GetTicks() + frame_ticks; | 460 next_frame = SDL_GetTicks() + frame_ticks; |
195 #else | 461 #else |
196 this->enabled = 0; | 462 this->enabled = 0; |
197 /* Don't try to close - may hang */ | 463 /* Don't try to close - may hang */ |
198 audio_fd = -1; | 464 audio_fd = -1; |
206 #endif | 472 #endif |
207 } | 473 } |
208 } | 474 } |
209 | 475 |
210 static void | 476 static void |
211 DMA_PlayAudio(_THIS) | 477 DMA_PlayDevice(_THIS) |
212 { | 478 { |
213 /* If timer synchronization is enabled, set the next write frame */ | 479 /* If timer synchronization is enabled, set the next write frame */ |
214 if (frame_ticks) { | 480 if (frame_ticks) { |
215 next_frame += frame_ticks; | 481 next_frame += frame_ticks; |
216 } | 482 } |
217 return; | 483 return; |
218 } | 484 } |
219 | 485 |
220 static Uint8 * | 486 static Uint8 * |
221 DMA_GetAudioBuf(_THIS) | 487 DMA_GetDeviceBuf(_THIS) |
222 { | 488 { |
223 count_info info; | 489 count_info info; |
224 int playing; | 490 int playing; |
225 int filling; | 491 int filling; |
226 | 492 |
241 playing = info.ptr / this->spec.size; | 507 playing = info.ptr / this->spec.size; |
242 filling = (playing + 1) % num_buffers; | 508 filling = (playing + 1) % num_buffers; |
243 return (dma_buf + (filling * this->spec.size)); | 509 return (dma_buf + (filling * this->spec.size)); |
244 } | 510 } |
245 | 511 |
246 static void | |
247 DMA_CloseAudio(_THIS) | |
248 { | |
249 if (dma_buf != NULL) { | |
250 munmap(dma_buf, dma_len); | |
251 dma_buf = NULL; | |
252 } | |
253 if (audio_fd >= 0) { | |
254 close(audio_fd); | |
255 audio_fd = -1; | |
256 } | |
257 } | |
258 | 512 |
259 static int | 513 static int |
260 DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo, | 514 DMA_Init(SDL_AudioDriverImpl *impl) |
261 SDL_AudioSpec * spec) | 515 { |
262 { | 516 /* Set the function pointers */ |
263 int frag_spec; | 517 impl->DetectDevices = DMA_DetectDevices; |
264 int value; | 518 impl->GetDeviceName = DMA_GetDeviceName; |
265 | 519 impl->OpenDevice = DMA_OpenDevice; |
266 /* Close and then reopen the audio device */ | 520 impl->WaitDevice = DMA_WaitDevice; |
267 close(audio_fd); | 521 impl->PlayDevice = DMA_PlayDevice; |
268 audio_fd = open(audiodev, O_RDWR, 0); | 522 impl->GetDeviceBuf = DMA_GetDeviceBuf; |
269 if (audio_fd < 0) { | 523 impl->CloseDevice = DMA_CloseDevice; |
270 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | 524 impl->Deinitialize = DMA_Deinitialize; |
271 return (-1); | 525 |
272 } | 526 build_device_lists(); |
273 | 527 return 1; |
274 /* Calculate the final parameters for this audio specification */ | 528 } |
275 SDL_CalculateAudioSpec(spec); | 529 |
276 | 530 AudioBootStrap DMA_bootstrap = { |
277 /* Determine the power of two of the fragment size */ | 531 DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", DMA_Init, 0 |
278 for (frag_spec = 0; (0x01 << frag_spec) < spec->size; ++frag_spec); | 532 }; |
279 if ((0x01 << frag_spec) != spec->size) { | |
280 SDL_SetError("Fragment size must be a power of two"); | |
281 return (-1); | |
282 } | |
283 | |
284 /* Set the audio buffering parameters */ | |
285 if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { | |
286 SDL_SetError("Couldn't set audio fragment spec"); | |
287 return (-1); | |
288 } | |
289 | |
290 /* Set the audio format */ | |
291 value = format; | |
292 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { | |
293 SDL_SetError("Couldn't set audio format"); | |
294 return (-1); | |
295 } | |
296 | |
297 /* Set mono or stereo audio */ | |
298 value = (spec->channels > 1); | |
299 if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || | |
300 (value != stereo)) { | |
301 SDL_SetError("Couldn't set audio channels"); | |
302 return (-1); | |
303 } | |
304 | |
305 /* Set the DSP frequency */ | |
306 value = spec->freq; | |
307 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { | |
308 SDL_SetError("Couldn't set audio frequency"); | |
309 return (-1); | |
310 } | |
311 spec->freq = value; | |
312 | |
313 /* We successfully re-opened the audio */ | |
314 return (0); | |
315 } | |
316 | |
317 static int | |
318 DMA_OpenAudio(_THIS, SDL_AudioSpec * spec) | |
319 { | |
320 char audiodev[1024]; | |
321 int format; | |
322 int stereo; | |
323 int value; | |
324 SDL_AudioFormat test_format; | |
325 struct audio_buf_info info; | |
326 | |
327 /* Reset the timer synchronization flag */ | |
328 frame_ticks = 0.0; | |
329 | |
330 /* Open the audio device */ | |
331 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); | |
332 if (audio_fd < 0) { | |
333 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | |
334 return (-1); | |
335 } | |
336 dma_buf = NULL; | |
337 ioctl(audio_fd, SNDCTL_DSP_RESET, 0); | |
338 | |
339 /* Get a list of supported hardware formats */ | |
340 if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { | |
341 SDL_SetError("Couldn't get audio format list"); | |
342 return (-1); | |
343 } | |
344 | |
345 /* Try for a closest match on audio format */ | |
346 format = 0; | |
347 for (test_format = SDL_FirstAudioFormat(spec->format); | |
348 !format && test_format;) { | |
349 #ifdef DEBUG_AUDIO | |
350 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); | |
351 #endif | |
352 switch (test_format) { | |
353 case AUDIO_U8: | |
354 if (value & AFMT_U8) { | |
355 format = AFMT_U8; | |
356 } | |
357 break; | |
358 case AUDIO_S8: | |
359 if (value & AFMT_S8) { | |
360 format = AFMT_S8; | |
361 } | |
362 break; | |
363 case AUDIO_S16LSB: | |
364 if (value & AFMT_S16_LE) { | |
365 format = AFMT_S16_LE; | |
366 } | |
367 break; | |
368 case AUDIO_S16MSB: | |
369 if (value & AFMT_S16_BE) { | |
370 format = AFMT_S16_BE; | |
371 } | |
372 break; | |
373 case AUDIO_U16LSB: | |
374 if (value & AFMT_U16_LE) { | |
375 format = AFMT_U16_LE; | |
376 } | |
377 break; | |
378 case AUDIO_U16MSB: | |
379 if (value & AFMT_U16_BE) { | |
380 format = AFMT_U16_BE; | |
381 } | |
382 break; | |
383 default: | |
384 format = 0; | |
385 break; | |
386 } | |
387 if (!format) { | |
388 test_format = SDL_NextAudioFormat(); | |
389 } | |
390 } | |
391 if (format == 0) { | |
392 SDL_SetError("Couldn't find any hardware audio formats"); | |
393 return (-1); | |
394 } | |
395 spec->format = test_format; | |
396 | |
397 /* Set the audio format */ | |
398 value = format; | |
399 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { | |
400 SDL_SetError("Couldn't set audio format"); | |
401 return (-1); | |
402 } | |
403 | |
404 /* Set mono or stereo audio (currently only two channels supported) */ | |
405 stereo = (spec->channels > 1); | |
406 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); | |
407 if (stereo) { | |
408 spec->channels = 2; | |
409 } else { | |
410 spec->channels = 1; | |
411 } | |
412 | |
413 /* Because some drivers don't allow setting the buffer size | |
414 after setting the format, we must re-open the audio device | |
415 once we know what format and channels are supported | |
416 */ | |
417 if (DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0) { | |
418 /* Error is set by DMA_ReopenAudio() */ | |
419 return (-1); | |
420 } | |
421 | |
422 /* Memory map the audio buffer */ | |
423 if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { | |
424 SDL_SetError("Couldn't get OSPACE parameters"); | |
425 return (-1); | |
426 } | |
427 spec->size = info.fragsize; | |
428 spec->samples = spec->size / ((spec->format & 0xFF) / 8); | |
429 spec->samples /= spec->channels; | |
430 num_buffers = info.fragstotal; | |
431 dma_len = num_buffers * spec->size; | |
432 dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, | |
433 audio_fd, 0); | |
434 if (dma_buf == MAP_FAILED) { | |
435 SDL_SetError("DMA memory map failed"); | |
436 dma_buf = NULL; | |
437 return (-1); | |
438 } | |
439 SDL_memset(dma_buf, spec->silence, dma_len); | |
440 | |
441 /* Check to see if we need to use select() workaround */ | |
442 { | |
443 char *workaround; | |
444 workaround = SDL_getenv("SDL_DSP_NOSELECT"); | |
445 if (workaround) { | |
446 frame_ticks = (float) (spec->samples * 1000) / spec->freq; | |
447 next_frame = SDL_GetTicks() + frame_ticks; | |
448 } | |
449 } | |
450 | |
451 /* Trigger audio playback */ | |
452 value = 0; | |
453 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); | |
454 value = PCM_ENABLE_OUTPUT; | |
455 if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) { | |
456 SDL_SetError("Couldn't trigger audio output"); | |
457 return (-1); | |
458 } | |
459 | |
460 /* Get the parent process id (we're the parent of the audio thread) */ | |
461 parent = getpid(); | |
462 | |
463 /* We're ready to rock and roll. :-) */ | |
464 return (0); | |
465 } | |
466 | 533 |
467 /* vi: set ts=4 sw=4 expandtab: */ | 534 /* vi: set ts=4 sw=4 expandtab: */ |