Mercurial > sdl-ios-xcode
comparison src/audio/dsp/SDL_dspaudio.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 |
---|---|
53 | 53 |
54 /* The tag name used by DSP audio */ | 54 /* The tag name used by DSP audio */ |
55 #define DSP_DRIVER_NAME "dsp" | 55 #define DSP_DRIVER_NAME "dsp" |
56 | 56 |
57 /* Open the audio device for playback, and don't block if busy */ | 57 /* Open the audio device for playback, and don't block if busy */ |
58 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) | 58 #define OPEN_FLAGS_OUTPUT (O_WRONLY|O_NONBLOCK) |
59 | 59 #define OPEN_FLAGS_INPUT (O_RDONLY|O_NONBLOCK) |
60 /* Audio driver functions */ | 60 |
61 static int DSP_OpenAudio(_THIS, SDL_AudioSpec * spec); | 61 static char **outputDevices = NULL; |
62 static void DSP_WaitAudio(_THIS); | 62 static int outputDeviceCount = 0; |
63 static void DSP_PlayAudio(_THIS); | 63 static char **inputDevices = NULL; |
64 static Uint8 *DSP_GetAudioBuf(_THIS); | 64 static int inputDeviceCount = 0; |
65 static void DSP_CloseAudio(_THIS); | 65 |
66 | 66 static inline void |
67 /* Audio driver bootstrap functions */ | 67 free_device_list(char ***devs, int *count) |
68 { | |
69 SDL_FreeUnixAudioDevices(devs, count); | |
70 } | |
71 | |
72 static inline void | |
73 build_device_list(int iscapture, char ***devs, int *count) | |
74 { | |
75 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); | |
76 free_device_list(devs, count); | |
77 SDL_EnumUnixAudioDevices(flags, 0, NULL, devs, count); | |
78 } | |
79 | |
80 static inline void | |
81 build_device_lists(void) | |
82 { | |
83 build_device_list(0, &outputDevices, &outputDeviceCount); | |
84 build_device_list(1, &inputDevices, &inputDeviceCount); | |
85 } | |
86 | |
87 | |
88 static inline void | |
89 free_device_lists(void) | |
90 { | |
91 free_device_list(&outputDevices, &outputDeviceCount); | |
92 free_device_list(&inputDevices, &inputDeviceCount); | |
93 } | |
94 | |
95 | |
96 static void | |
97 DSP_Deinitialize(void) | |
98 { | |
99 free_device_lists(); | |
100 } | |
101 | |
68 | 102 |
69 static int | 103 static int |
70 Audio_Available(void) | 104 DSP_DetectDevices(int iscapture) |
71 { | 105 { |
72 int fd; | 106 if (iscapture) { |
73 int available; | 107 build_device_list(1, &inputDevices, &inputDeviceCount); |
74 | 108 return inputDeviceCount; |
75 available = 0; | 109 } else { |
76 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); | 110 build_device_list(0, &outputDevices, &outputDeviceCount); |
77 if (fd >= 0) { | 111 return outputDeviceCount; |
78 available = 1; | 112 } |
79 close(fd); | 113 |
80 } | 114 return 0; /* shouldn't ever hit this. */ |
81 return (available); | 115 } |
82 } | 116 |
117 static const char * | |
118 DSP_GetDeviceName(int index, int iscapture) | |
119 { | |
120 if ((iscapture) && (index < inputDeviceCount)) { | |
121 return inputDevices[index]; | |
122 } else if ((!iscapture) && (index < outputDeviceCount)) { | |
123 return outputDevices[index]; | |
124 } | |
125 | |
126 SDL_SetError("No such device"); | |
127 return NULL; | |
128 } | |
129 | |
83 | 130 |
84 static void | 131 static void |
85 Audio_DeleteDevice(SDL_AudioDevice * device) | 132 DSP_CloseDevice(_THIS) |
86 { | 133 { |
87 SDL_free(device->hidden); | 134 if (this->hidden != NULL) { |
88 SDL_free(device); | 135 if (this->hidden->mixbuf != NULL) { |
89 } | 136 SDL_FreeAudioMem(this->hidden->mixbuf); |
90 | 137 this->hidden->mixbuf = NULL; |
91 static SDL_AudioDevice * | 138 } |
92 Audio_CreateDevice(int devindex) | 139 if (this->hidden->audio_fd >= 0) { |
93 { | 140 close(this->hidden->audio_fd); |
94 SDL_AudioDevice *this; | 141 this->hidden->audio_fd = -1; |
95 | 142 } |
96 /* Initialize all variables that we clean on shutdown */ | 143 SDL_free(this->hidden); |
97 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | 144 this->hidden = NULL; |
98 if (this) { | 145 } |
99 SDL_memset(this, 0, (sizeof *this)); | 146 } |
100 this->hidden = (struct SDL_PrivateAudioData *) | 147 |
101 SDL_malloc((sizeof *this->hidden)); | |
102 } | |
103 if ((this == NULL) || (this->hidden == NULL)) { | |
104 SDL_OutOfMemory(); | |
105 if (this) { | |
106 SDL_free(this); | |
107 } | |
108 return (0); | |
109 } | |
110 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
111 audio_fd = -1; | |
112 | |
113 /* Set the function pointers */ | |
114 this->OpenAudio = DSP_OpenAudio; | |
115 this->WaitAudio = DSP_WaitAudio; | |
116 this->PlayAudio = DSP_PlayAudio; | |
117 this->GetAudioBuf = DSP_GetAudioBuf; | |
118 this->CloseAudio = DSP_CloseAudio; | |
119 | |
120 this->free = Audio_DeleteDevice; | |
121 | |
122 return this; | |
123 } | |
124 | |
125 AudioBootStrap DSP_bootstrap = { | |
126 DSP_DRIVER_NAME, "OSS /dev/dsp standard audio", | |
127 Audio_Available, Audio_CreateDevice | |
128 }; | |
129 | |
130 /* This function waits until it is possible to write a full sound buffer */ | |
131 static void | |
132 DSP_WaitAudio(_THIS) | |
133 { | |
134 /* Not needed at all since OSS handles waiting automagically */ | |
135 } | |
136 | |
137 static void | |
138 DSP_PlayAudio(_THIS) | |
139 { | |
140 if (write(audio_fd, mixbuf, mixlen) == -1) { | |
141 perror("Audio write"); | |
142 this->enabled = 0; | |
143 } | |
144 #ifdef DEBUG_AUDIO | |
145 fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen); | |
146 #endif | |
147 } | |
148 | |
149 static Uint8 * | |
150 DSP_GetAudioBuf(_THIS) | |
151 { | |
152 return (mixbuf); | |
153 } | |
154 | |
155 static void | |
156 DSP_CloseAudio(_THIS) | |
157 { | |
158 if (mixbuf != NULL) { | |
159 SDL_FreeAudioMem(mixbuf); | |
160 mixbuf = NULL; | |
161 } | |
162 if (audio_fd >= 0) { | |
163 close(audio_fd); | |
164 audio_fd = -1; | |
165 } | |
166 } | |
167 | 148 |
168 static int | 149 static int |
169 DSP_OpenAudio(_THIS, SDL_AudioSpec * spec) | 150 DSP_OpenDevice(_THIS, const char *devname, int iscapture) |
170 { | 151 { |
171 char audiodev[1024]; | 152 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); |
172 int format; | 153 int format; |
173 int value; | 154 int value; |
174 int frag_spec; | 155 int frag_spec; |
175 SDL_AudioFormat test_format; | 156 SDL_AudioFormat test_format; |
176 | 157 |
158 /* We don't care what the devname is...we'll try to open anything. */ | |
159 /* ...but default to first name in the list... */ | |
160 if (devname == NULL) { | |
161 if ( ((iscapture) && (inputDeviceCount == 0)) || | |
162 ((!iscapture) && (outputDeviceCount == 0)) ) { | |
163 SDL_SetError("No such audio device"); | |
164 return 0; | |
165 } | |
166 devname = ((iscapture) ? inputDevices[0] : outputDevices[0]); | |
167 } | |
168 | |
169 /* Initialize all variables that we clean on shutdown */ | |
170 this->hidden = (struct SDL_PrivateAudioData *) | |
171 SDL_malloc((sizeof *this->hidden)); | |
172 if (this->hidden == NULL) { | |
173 SDL_OutOfMemory(); | |
174 return 0; | |
175 } | |
176 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
177 | |
177 /* Open the audio device */ | 178 /* Open the audio device */ |
178 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); | 179 this->hidden->audio_fd = open(devname, flags, 0); |
179 if (audio_fd < 0) { | 180 if (this->hidden->audio_fd < 0) { |
180 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); | 181 DSP_CloseDevice(this); |
181 return (-1); | 182 SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); |
182 } | 183 return 0; |
183 mixbuf = NULL; | 184 } |
185 this->hidden->mixbuf = NULL; | |
184 | 186 |
185 /* Make the file descriptor use blocking writes with fcntl() */ | 187 /* Make the file descriptor use blocking writes with fcntl() */ |
186 { | 188 { |
187 long flags; | 189 long ctlflags; |
188 flags = fcntl(audio_fd, F_GETFL); | 190 ctlflags = fcntl(this->hidden->audio_fd, F_GETFL); |
189 flags &= ~O_NONBLOCK; | 191 ctlflags &= ~O_NONBLOCK; |
190 if (fcntl(audio_fd, F_SETFL, flags) < 0) { | 192 if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) { |
193 DSP_CloseDevice(this); | |
191 SDL_SetError("Couldn't set audio blocking mode"); | 194 SDL_SetError("Couldn't set audio blocking mode"); |
192 DSP_CloseAudio(this); | 195 return 0; |
193 return (-1); | |
194 } | 196 } |
195 } | 197 } |
196 | 198 |
197 /* Get a list of supported hardware formats */ | 199 /* Get a list of supported hardware formats */ |
198 if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { | 200 if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { |
199 perror("SNDCTL_DSP_GETFMTS"); | 201 perror("SNDCTL_DSP_GETFMTS"); |
202 DSP_CloseDevice(this); | |
200 SDL_SetError("Couldn't get audio format list"); | 203 SDL_SetError("Couldn't get audio format list"); |
201 DSP_CloseAudio(this); | 204 return 0; |
202 return (-1); | |
203 } | 205 } |
204 | 206 |
205 /* Try for a closest match on audio format */ | 207 /* Try for a closest match on audio format */ |
206 format = 0; | 208 format = 0; |
207 for (test_format = SDL_FirstAudioFormat(spec->format); | 209 for (test_format = SDL_FirstAudioFormat(this->spec.format); |
208 !format && test_format;) { | 210 !format && test_format;) { |
209 #ifdef DEBUG_AUDIO | 211 #ifdef DEBUG_AUDIO |
210 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); | 212 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); |
211 #endif | 213 #endif |
212 switch (test_format) { | 214 switch (test_format) { |
253 if (!format) { | 255 if (!format) { |
254 test_format = SDL_NextAudioFormat(); | 256 test_format = SDL_NextAudioFormat(); |
255 } | 257 } |
256 } | 258 } |
257 if (format == 0) { | 259 if (format == 0) { |
260 DSP_CloseDevice(this); | |
258 SDL_SetError("Couldn't find any hardware audio formats"); | 261 SDL_SetError("Couldn't find any hardware audio formats"); |
259 DSP_CloseAudio(this); | 262 return 0; |
260 return (-1); | 263 } |
261 } | 264 this->spec.format = test_format; |
262 spec->format = test_format; | |
263 | 265 |
264 /* Set the audio format */ | 266 /* Set the audio format */ |
265 value = format; | 267 value = format; |
266 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { | 268 if ( (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || |
269 (value != format) ) { | |
267 perror("SNDCTL_DSP_SETFMT"); | 270 perror("SNDCTL_DSP_SETFMT"); |
271 DSP_CloseDevice(this); | |
268 SDL_SetError("Couldn't set audio format"); | 272 SDL_SetError("Couldn't set audio format"); |
269 DSP_CloseAudio(this); | 273 return 0; |
270 return (-1); | |
271 } | 274 } |
272 | 275 |
273 /* Set the number of channels of output */ | 276 /* Set the number of channels of output */ |
274 value = spec->channels; | 277 value = this->spec.channels; |
275 if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { | 278 if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { |
276 perror("SNDCTL_DSP_CHANNELS"); | 279 perror("SNDCTL_DSP_CHANNELS"); |
280 DSP_CloseDevice(this); | |
277 SDL_SetError("Cannot set the number of channels"); | 281 SDL_SetError("Cannot set the number of channels"); |
278 DSP_CloseAudio(this); | 282 return 0; |
279 return (-1); | 283 } |
280 } | 284 this->spec.channels = value; |
281 spec->channels = value; | |
282 | 285 |
283 /* Set the DSP frequency */ | 286 /* Set the DSP frequency */ |
284 value = spec->freq; | 287 value = this->spec.freq; |
285 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { | 288 if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { |
286 perror("SNDCTL_DSP_SPEED"); | 289 perror("SNDCTL_DSP_SPEED"); |
290 DSP_CloseDevice(this); | |
287 SDL_SetError("Couldn't set audio frequency"); | 291 SDL_SetError("Couldn't set audio frequency"); |
288 DSP_CloseAudio(this); | 292 return 0; |
289 return (-1); | 293 } |
290 } | 294 this->spec.freq = value; |
291 spec->freq = value; | |
292 | 295 |
293 /* Calculate the final parameters for this audio specification */ | 296 /* Calculate the final parameters for this audio specification */ |
294 SDL_CalculateAudioSpec(spec); | 297 SDL_CalculateAudioSpec(&this->spec); |
295 | 298 |
296 /* Determine the power of two of the fragment size */ | 299 /* Determine the power of two of the fragment size */ |
297 for (frag_spec = 0; (0x01U << frag_spec) < spec->size; ++frag_spec); | 300 for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec); |
298 if ((0x01U << frag_spec) != spec->size) { | 301 if ((0x01U << frag_spec) != this->spec.size) { |
302 DSP_CloseDevice(this); | |
299 SDL_SetError("Fragment size must be a power of two"); | 303 SDL_SetError("Fragment size must be a power of two"); |
300 DSP_CloseAudio(this); | 304 return 0; |
301 return (-1); | |
302 } | 305 } |
303 frag_spec |= 0x00020000; /* two fragments, for low latency */ | 306 frag_spec |= 0x00020000; /* two fragments, for low latency */ |
304 | 307 |
305 /* Set the audio buffering parameters */ | 308 /* Set the audio buffering parameters */ |
306 #ifdef DEBUG_AUDIO | 309 #ifdef DEBUG_AUDIO |
307 fprintf(stderr, "Requesting %d fragments of size %d\n", | 310 fprintf(stderr, "Requesting %d fragments of size %d\n", |
308 (frag_spec >> 16), 1 << (frag_spec & 0xFFFF)); | 311 (frag_spec >> 16), 1 << (frag_spec & 0xFFFF)); |
309 #endif | 312 #endif |
310 if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { | 313 if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { |
311 perror("SNDCTL_DSP_SETFRAGMENT"); | 314 perror("SNDCTL_DSP_SETFRAGMENT"); |
312 } | 315 } |
313 #ifdef DEBUG_AUDIO | 316 #ifdef DEBUG_AUDIO |
314 { | 317 { |
315 audio_buf_info info; | 318 audio_buf_info info; |
316 ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info); | 319 ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info); |
317 fprintf(stderr, "fragments = %d\n", info.fragments); | 320 fprintf(stderr, "fragments = %d\n", info.fragments); |
318 fprintf(stderr, "fragstotal = %d\n", info.fragstotal); | 321 fprintf(stderr, "fragstotal = %d\n", info.fragstotal); |
319 fprintf(stderr, "fragsize = %d\n", info.fragsize); | 322 fprintf(stderr, "fragsize = %d\n", info.fragsize); |
320 fprintf(stderr, "bytes = %d\n", info.bytes); | 323 fprintf(stderr, "bytes = %d\n", info.bytes); |
321 } | 324 } |
322 #endif | 325 #endif |
323 | 326 |
324 /* Allocate mixing buffer */ | 327 /* Allocate mixing buffer */ |
325 mixlen = spec->size; | 328 this->hidden->mixlen = this->spec.size; |
326 mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen); | 329 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); |
327 if (mixbuf == NULL) { | 330 if (this->hidden->mixbuf == NULL) { |
328 DSP_CloseAudio(this); | 331 DSP_CloseDevice(this); |
329 return (-1); | 332 SDL_OutOfMemory(); |
330 } | 333 return 0; |
331 SDL_memset(mixbuf, spec->silence, spec->size); | 334 } |
332 | 335 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); |
333 /* Get the parent process id (we're the parent of the audio thread) */ | |
334 parent = getpid(); | |
335 | 336 |
336 /* We're ready to rock and roll. :-) */ | 337 /* We're ready to rock and roll. :-) */ |
337 return (0); | 338 return 1; |
338 } | 339 } |
340 | |
341 | |
342 static void | |
343 DSP_PlayDevice(_THIS) | |
344 { | |
345 const Uint8 *mixbuf = this->hidden->mixbuf; | |
346 const int mixlen = this->hidden->mixlen; | |
347 if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) { | |
348 perror("Audio write"); | |
349 this->enabled = 0; | |
350 } | |
351 #ifdef DEBUG_AUDIO | |
352 fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen); | |
353 #endif | |
354 } | |
355 | |
356 static Uint8 * | |
357 DSP_GetDeviceBuf(_THIS) | |
358 { | |
359 return (this->hidden->mixbuf); | |
360 } | |
361 | |
362 static int | |
363 DSP_Init(SDL_AudioDriverImpl *impl) | |
364 { | |
365 /* Set the function pointers */ | |
366 impl->DetectDevices = DSP_DetectDevices; | |
367 impl->GetDeviceName = DSP_GetDeviceName; | |
368 impl->OpenDevice = DSP_OpenDevice; | |
369 impl->PlayDevice = DSP_PlayDevice; | |
370 impl->GetDeviceBuf = DSP_GetDeviceBuf; | |
371 impl->CloseDevice = DSP_CloseDevice; | |
372 impl->Deinitialize = DSP_Deinitialize; | |
373 | |
374 build_device_lists(); | |
375 return 1; | |
376 } | |
377 | |
378 | |
379 AudioBootStrap DSP_bootstrap = { | |
380 DSP_DRIVER_NAME, "OSS /dev/dsp standard audio", DSP_Init, 0 | |
381 }; | |
339 | 382 |
340 /* vi: set ts=4 sw=4 expandtab: */ | 383 /* vi: set ts=4 sw=4 expandtab: */ |