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: */