comparison src/audio/nto/SDL_nto_audio.c @ 3829:d3171647e661 SDL-ryan-multiple-audio-device

Moved NTO audio driver to 1.3 API.
author Ryan C. Gordon <icculus@icculus.org>
date Sat, 07 Oct 2006 19:53:48 +0000
parents c8b3d3d13ed1
children 7df0d3efe682
comparison
equal deleted inserted replaced
3828:efdfe292bd88 3829:d3171647e661
67 {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, 67 {"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
68 {"Vortex 8820", QSA_MMAP_WORKAROUND}, 68 {"Vortex 8820", QSA_MMAP_WORKAROUND},
69 {"Vortex 8830", QSA_MMAP_WORKAROUND}, 69 {"Vortex 8830", QSA_MMAP_WORKAROUND},
70 }; 70 };
71 71
72 /* Audio driver functions */ 72
73 static void NTO_ThreadInit(_THIS); 73 static inline void
74 static int NTO_OpenAudio(_THIS, SDL_AudioSpec * spec); 74 NTO_SetError(const char *fn, int rval)
75 static void NTO_WaitAudio(_THIS); 75 {
76 static void NTO_PlayAudio(_THIS); 76 SDL_SetError("NTO: %s failed: %s", fn, snd_strerror(rval));
77 static Uint8 *NTO_GetAudioBuf(_THIS); 77 }
78 static void NTO_CloseAudio(_THIS); 78
79 79
80 /* card names check to apply the workarounds */ 80 /* card names check to apply the workarounds */
81 static int 81 static int
82 NTO_CheckBuggyCards(_THIS, unsigned long checkfor) 82 NTO_CheckBuggyCards(_THIS, unsigned long checkfor)
83 { 83 {
84 char scardname[33]; 84 char scardname[33];
85 int it; 85 int it;
86 86
87 if (snd_card_get_name(cardno, scardname, 32) < 0) { 87 if (snd_card_get_name(this->hidden->cardno, scardname, 32) < 0) {
88 return 0; 88 return 0;
89 } 89 }
90 90
91 for (it = 0; it < QSA_WA_CARDS; it++) { 91 for (it = 0; it < QSA_WA_CARDS; it++) {
92 if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) { 92 if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) {
100 } 100 }
101 101
102 static void 102 static void
103 NTO_ThreadInit(_THIS) 103 NTO_ThreadInit(_THIS)
104 { 104 {
105 int status;
106 struct sched_param param; 105 struct sched_param param;
106 int status = SchedGet(0, 0, &param);
107 107
108 /* increasing default 10 priority to 25 to avoid jerky sound */ 108 /* increasing default 10 priority to 25 to avoid jerky sound */
109 status = SchedGet(0, 0, &param);
110 param.sched_priority = param.sched_curpriority + 15; 109 param.sched_priority = param.sched_curpriority + 15;
111 status = SchedSet(0, 0, SCHED_NOCHANGE, &param); 110 status = SchedSet(0, 0, SCHED_NOCHANGE, &param);
112 } 111 }
113 112
114 /* PCM transfer channel parameters initialize function */ 113 /* PCM transfer channel parameters initialize function */
129 cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; 128 cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
130 cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; 129 cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
131 } 130 }
132 131
133 static int 132 static int
134 NTO_AudioAvailable(void) 133 NTO_Available(void)
135 { 134 {
136 /* See if we can open a nonblocking channel. 135 /* See if we can open a nonblocking channel.
137 Return value '1' means we can. 136 Return value '1' means we can.
138 Return value '0' means we cannot. */ 137 Return value '0' means we cannot. */
139 138
140 int available; 139 int available = 0;
141 int rval; 140 snd_pcm_t *handle = NULL;
142 snd_pcm_t *handle; 141 int rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS);
143
144 available = 0;
145 handle = NULL;
146
147 rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS);
148
149 if (rval >= 0) { 142 if (rval >= 0) {
150 available = 1; 143 available = 1;
151
152 if ((rval = snd_pcm_close(handle)) < 0) { 144 if ((rval = snd_pcm_close(handle)) < 0) {
153 SDL_SetError
154 ("NTO_AudioAvailable(): snd_pcm_close failed: %s\n",
155 snd_strerror(rval));
156 available = 0; 145 available = 0;
157 } 146 }
158 } else {
159 SDL_SetError
160 ("NTO_AudioAvailable(): there are no available audio devices.\n");
161 } 147 }
162 148
163 return (available); 149 return (available);
164 } 150 }
165 151
166 static void
167 NTO_DeleteAudioDevice(SDL_AudioDevice * device)
168 {
169 if ((device) && (device->hidden)) {
170 SDL_free(device->hidden);
171 }
172 if (device) {
173 SDL_free(device);
174 }
175 }
176
177 static SDL_AudioDevice *
178 NTO_CreateAudioDevice(int devindex)
179 {
180 SDL_AudioDevice *this;
181
182 /* Initialize all variables that we clean on shutdown */
183 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
184 if (this) {
185 SDL_memset(this, 0, sizeof(SDL_AudioDevice));
186 this->hidden = (struct SDL_PrivateAudioData *)
187 SDL_malloc(sizeof(struct SDL_PrivateAudioData));
188 }
189 if ((this == NULL) || (this->hidden == NULL)) {
190 SDL_OutOfMemory();
191 if (this) {
192 SDL_free(this);
193 }
194 return (0);
195 }
196 SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
197 audio_handle = NULL;
198
199 /* Set the function pointers */
200 this->ThreadInit = NTO_ThreadInit;
201 this->OpenAudio = NTO_OpenAudio;
202 this->WaitAudio = NTO_WaitAudio;
203 this->PlayAudio = NTO_PlayAudio;
204 this->GetAudioBuf = NTO_GetAudioBuf;
205 this->CloseAudio = NTO_CloseAudio;
206
207 this->free = NTO_DeleteAudioDevice;
208
209 return this;
210 }
211
212 AudioBootStrap QNXNTOAUDIO_bootstrap = {
213 DRIVER_NAME, "QNX6 QSA-NTO Audio",
214 NTO_AudioAvailable,
215 NTO_CreateAudioDevice,
216 0
217 };
218 152
219 /* This function waits until it is possible to write a full sound buffer */ 153 /* This function waits until it is possible to write a full sound buffer */
220 static void 154 static void
221 NTO_WaitAudio(_THIS) 155 NTO_WaitDevice(_THIS)
222 { 156 {
223 fd_set wfds; 157 fd_set wfds;
224 int selectret; 158 int selectret;
225 159
226 FD_ZERO(&wfds); 160 FD_ZERO(&wfds);
227 FD_SET(audio_fd, &wfds); 161 FD_SET(this->hidden->audio_fd, &wfds);
228 162
229 do { 163 do {
230 selectret = select(audio_fd + 1, NULL, &wfds, NULL, NULL); 164 selectret = select(this->hidden->audio_fd+1, NULL, &wfds, NULL, NULL);
231 switch (selectret) { 165 switch (selectret) {
232 case -1: 166 case -1:
233 case 0: 167 case 0:
234 SDL_SetError("NTO_WaitAudio(): select() failed: %s\n", 168 SDL_SetError("NTO: select() failed: %s\n", strerror(errno));
235 strerror(errno));
236 return; 169 return;
237 default: 170 default:
238 if (FD_ISSET(audio_fd, &wfds)) { 171 if (FD_ISSET(this->hidden->audio_fd, &wfds)) {
239 return; 172 return;
240 } 173 }
241 break; 174 break;
242 } 175 }
243 } 176 }
244 while (1); 177 while (1);
245 } 178 }
246 179
247 static void 180 static void
248 NTO_PlayAudio(_THIS) 181 NTO_PlayDevice(_THIS)
249 { 182 {
183 snd_pcm_channel_status_t cstatus;
250 int written, rval; 184 int written, rval;
251 int towrite; 185 int towrite;
252 void *pcmbuffer; 186 void *pcmbuffer;
253 187
254 if (!this->enabled) { 188 if ((!this->enabled) || (!this->hidden)) {
255 return; 189 return;
256 } 190 }
257 191
258 towrite = this->spec.size; 192 towrite = this->spec.size;
259 pcmbuffer = pcm_buf; 193 pcmbuffer = this->hidden->pcm_buf;
260 194
261 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ 195 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
262 do { 196 do {
263 written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); 197 written = snd_pcm_plugin_write(this->hidden->audio_handle,
198 pcmbuffer, towrite);
264 if (written != towrite) { 199 if (written != towrite) {
265 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { 200 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
266 /* Let a little CPU time go by and try to write again */ 201 /* Let a little CPU time go by and try to write again */
267 SDL_Delay(1); 202 SDL_Delay(1);
268 /* if we wrote some data */ 203 /* if we wrote some data */
269 towrite -= written; 204 towrite -= written;
270 pcmbuffer += written * this->spec.channels; 205 pcmbuffer += written * this->spec.channels;
271 continue; 206 continue;
272 } else { 207 } else if ((errno == EINVAL) || (errno == EIO)) {
273 if ((errno == EINVAL) || (errno == EIO)) { 208 SDL_memset(&cstatus, 0, sizeof (cstatus));
274 SDL_memset(&cstatus, 0, sizeof(cstatus)); 209 cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
275 cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; 210 rval = snd_pcm_plugin_status(this->hidden->audio_handle,
276 if ((rval = 211 &cstatus);
277 snd_pcm_plugin_status(audio_handle, &cstatus)) < 0) { 212 if (rval < 0) {
278 SDL_SetError 213 NTO_SetError("snd_pcm_plugin_status", rval);
279 ("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", 214 return;
280 snd_strerror(rval)); 215 }
216
217 if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
218 (cstatus.status == SND_PCM_STATUS_READY)) {
219 rval = snd_pcm_plugin_prepare(this->hidden->audio_handle,
220 SND_PCM_CHANNEL_PLAYBACK);
221 if (rval < 0) {
222 NTO_SetError("snd_pcm_plugin_prepare", rval);
281 return; 223 return;
282 } 224 }
283 if ((cstatus.status == SND_PCM_STATUS_UNDERRUN)
284 || (cstatus.status == SND_PCM_STATUS_READY)) {
285 if ((rval =
286 snd_pcm_plugin_prepare(audio_handle,
287 SND_PCM_CHANNEL_PLAYBACK))
288 < 0) {
289 SDL_SetError
290 ("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n",
291 snd_strerror(rval));
292 return;
293 }
294 }
295 continue;
296 } else {
297 return;
298 } 225 }
226 continue;
227 } else {
228 return;
299 } 229 }
300 } else { 230 } else {
301 /* we wrote all remaining data */ 231 /* we wrote all remaining data */
302 towrite -= written; 232 towrite -= written;
303 pcmbuffer += written * this->spec.channels; 233 pcmbuffer += written * this->spec.channels;
307 237
308 /* If we couldn't write, assume fatal error for now */ 238 /* If we couldn't write, assume fatal error for now */
309 if (towrite != 0) { 239 if (towrite != 0) {
310 this->enabled = 0; 240 this->enabled = 0;
311 } 241 }
312
313 return;
314 } 242 }
315 243
316 static Uint8 * 244 static Uint8 *
317 NTO_GetAudioBuf(_THIS) 245 NTO_GetDeviceBuf(_THIS)
318 { 246 {
319 return pcm_buf; 247 return this->hidden->pcm_buf;
320 } 248 }
321 249
322 static void 250 static void
323 NTO_CloseAudio(_THIS) 251 NTO_CloseDevice(_THIS)
324 { 252 {
325 int rval; 253 if (this->hidden != NULL) {
326 254 if (this->hidden->audio_handle != NULL) {
327 this->enabled = 0; 255 snd_pcm_plugin_flush(this->hidden->audio_handle,
328 256 SND_PCM_CHANNEL_PLAYBACK);
329 if (audio_handle != NULL) { 257 snd_pcm_close(this->hidden->audio_handle);
330 if ((rval = 258 this->hidden->audio_handle = NULL;
331 snd_pcm_plugin_flush(audio_handle, 259 }
332 SND_PCM_CHANNEL_PLAYBACK)) < 0) { 260 if (this->hidden->pcm_buf != NULL) {
333 SDL_SetError 261 SDL_FreeAudioMem(this->hidden->pcm_buf);
334 ("NTO_CloseAudio(): snd_pcm_plugin_flush failed: %s\n", 262 this->hidden->pcm_buf = NULL;
335 snd_strerror(rval)); 263 }
336 return; 264 SDL_free(this->hidden);
337 } 265 this->hidden = NULL;
338 if ((rval = snd_pcm_close(audio_handle)) < 0) {
339 SDL_SetError("NTO_CloseAudio(): snd_pcm_close failed: %s\n",
340 snd_strerror(rval));
341 return;
342 }
343 audio_handle = NULL;
344 } 266 }
345 } 267 }
346 268
347 static int 269 static int
348 NTO_OpenAudio(_THIS, SDL_AudioSpec * spec) 270 NTO_OpenDevice(_THIS, const char *devname, int iscapture)
349 { 271 {
350 int rval; 272 int rval = 0;
351 int format; 273 int format = 0;
352 SDL_AudioFormat test_format; 274 SDL_AudioFormat test_format = 0;
353 int found; 275 int found = 0;
354 276 snd_pcm_channel_setup_t csetup;
355 audio_handle = NULL; 277 snd_pcm_channel_params_t cparams;
356 this->enabled = 0; 278
357 279 /* Initialize all variables that we clean on shutdown */
358 if (pcm_buf != NULL) { 280 this->hidden = (struct SDL_PrivateAudioData *)
359 SDL_FreeAudioMem(pcm_buf); 281 SDL_malloc((sizeof *this->hidden));
360 pcm_buf = NULL; 282 if (this->hidden == NULL) {
361 } 283 SDL_OutOfMemory();
284 return 0;
285 }
286 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
362 287
363 /* initialize channel transfer parameters to default */ 288 /* initialize channel transfer parameters to default */
364 NTO_InitAudioParams(&cparams); 289 NTO_InitAudioParams(&cparams);
365 290
366 /* Open the audio device */ 291 /* Open the audio device */
367 rval = 292 rval = snd_pcm_open_preferred(&this->hidden->audio_handle,
368 snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, OPEN_FLAGS); 293 &this->hidden->cardno,
294 &this->hidden->deviceno, OPEN_FLAGS);
295
369 if (rval < 0) { 296 if (rval < 0) {
370 SDL_SetError("NTO_OpenAudio(): snd_pcm_open failed: %s\n", 297 NTO_CloseDevice(this);
371 snd_strerror(rval)); 298 NTO_SetError("snd_pcm_open", rval);
372 return (-1); 299 return 0;
373 } 300 }
374 301
375 if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) { 302 if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) {
376 /* enable count status parameter */ 303 /* enable count status parameter */
377 if ((rval = 304 rval = snd_pcm_plugin_set_disable(this->hidden->audio_handle,
378 snd_pcm_plugin_set_disable(audio_handle, 305 PLUGIN_DISABLE_MMAP);
379 PLUGIN_DISABLE_MMAP)) < 0) { 306 if (rval < 0) {
380 SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", 307 NTO_CloseDevice(this);
381 snd_strerror(rval)); 308 NTO_SetError("snd_pcm_plugin_set_disable", rval);
382 return (-1); 309 return 0;
383 } 310 }
384 } 311 }
385 312
386 /* Try for a closest match on audio format */ 313 /* Try for a closest match on audio format */
387 format = 0; 314 format = 0;
388 /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */ 315 /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */
389 found = 0; 316 found = 0;
390 317
391 for (test_format = SDL_FirstAudioFormat(spec->format); !found;) { 318 for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
392 /* if match found set format to equivalent ALSA format */ 319 /* if match found set format to equivalent ALSA format */
393 switch (test_format) { 320 switch (test_format) {
394 case AUDIO_U8: 321 case AUDIO_U8:
395 format = SND_PCM_SFMT_U8; 322 format = SND_PCM_SFMT_U8;
396 found = 1; 323 found = 1;
440 } 367 }
441 } 368 }
442 369
443 /* assumes test_format not 0 on success */ 370 /* assumes test_format not 0 on success */
444 if (test_format == 0) { 371 if (test_format == 0) {
445 SDL_SetError 372 NTO_CloseDevice(this);
446 ("NTO_OpenAudio(): Couldn't find any hardware audio formats"); 373 SDL_SetError("NTO: Couldn't find any hardware audio formats");
447 return (-1); 374 return 0;
448 } 375 }
449 376
450 spec->format = test_format; 377 this->spec.format = test_format;
451 378
452 /* Set the audio format */ 379 /* Set the audio format */
453 cparams.format.format = format; 380 cparams.format.format = format;
454 381
455 /* Set mono or stereo audio (currently only two channels supported) */ 382 /* Set mono or stereo audio (currently only two channels supported) */
456 cparams.format.voices = spec->channels; 383 cparams.format.voices = this->spec.channels;
457 384
458 /* Set rate */ 385 /* Set rate */
459 cparams.format.rate = spec->freq; 386 cparams.format.rate = this->spec.freq;
460 387
461 /* Setup the transfer parameters according to cparams */ 388 /* Setup the transfer parameters according to cparams */
462 rval = snd_pcm_plugin_params(audio_handle, &cparams); 389 rval = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
463 if (rval < 0) { 390 if (rval < 0) {
464 SDL_SetError 391 NTO_CloseDevice(this);
465 ("NTO_OpenAudio(): snd_pcm_channel_params failed: %s\n", 392 NTO_SetError("snd_pcm_channel_params", rval);
466 snd_strerror(rval)); 393 return 0;
467 return (-1);
468 } 394 }
469 395
470 /* Make sure channel is setup right one last time */ 396 /* Make sure channel is setup right one last time */
471 SDL_memset(&csetup, 0x00, sizeof(csetup)); 397 SDL_memset(&csetup, '\0', sizeof (csetup));
472 csetup.channel = SND_PCM_CHANNEL_PLAYBACK; 398 csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
473 if (snd_pcm_plugin_setup(audio_handle, &csetup) < 0) { 399 if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
474 SDL_SetError("NTO_OpenAudio(): Unable to setup playback channel\n"); 400 NTO_CloseDevice(this);
475 return -1; 401 SDL_SetError("NTO: Unable to setup playback channel\n");
476 } 402 return 0;
477 403 }
478 404
479 /* Calculate the final parameters for this audio specification */ 405 /* Calculate the final parameters for this audio specification */
480 SDL_CalculateAudioSpec(spec); 406 SDL_CalculateAudioSpec(&this->spec);
481 407
482 pcm_len = spec->size; 408 this->hidden->pcm_len = this->spec.size;
483 409
484 if (pcm_len == 0) { 410 if (this->hidden->pcm_len == 0) {
485 pcm_len = 411 this->hidden->pcm_len =
486 csetup.buf.block.frag_size * spec->channels * 412 csetup.buf.block.frag_size * this->spec.channels *
487 (snd_pcm_format_width(format) / 8); 413 (snd_pcm_format_width(format) / 8);
488 } 414 }
489 415
490 /* Allocate memory to the audio buffer and initialize with silence (Note that 416 /*
491 buffer size must be a multiple of fragment size, so find closest multiple) 417 * Allocate memory to the audio buffer and initialize with silence
418 * (Note that buffer size must be a multiple of fragment size, so find
419 * closest multiple)
492 */ 420 */
493 pcm_buf = (Uint8 *) SDL_AllocAudioMem(pcm_len); 421 this->hidden->pcm_buf = (Uint8 *) SDL_AllocAudioMem(this->hidden->pcm_len);
494 if (pcm_buf == NULL) { 422 if (this->hidden->pcm_buf == NULL) {
495 SDL_SetError("NTO_OpenAudio(): pcm buffer allocation failed\n"); 423 NTO_CloseDevice(this);
496 return (-1); 424 SDL_OutOfMemory();
497 } 425 return 0;
498 SDL_memset(pcm_buf, spec->silence, pcm_len); 426 }
427 SDL_memset(this->hidden->pcm_buf,this->spec.silence,this->hidden->pcm_len);
499 428
500 /* get the file descriptor */ 429 /* get the file descriptor */
501 if ((audio_fd = 430 this->hidden->audio_fd = snd_pcm_file_descriptor(this->hidden->audio_handle,
502 snd_pcm_file_descriptor(audio_handle, 431 SND_PCM_CHANNEL_PLAYBACK);
503 SND_PCM_CHANNEL_PLAYBACK)) < 0) { 432 if (this->hidden->audio_fd < 0) {
504 SDL_SetError 433 NTO_CloseDevice(this);
505 ("NTO_OpenAudio(): snd_pcm_file_descriptor failed with error code: %s\n", 434 NTO_SetError("snd_pcm_file_descriptor", rval);
506 snd_strerror(rval)); 435 return 0;
507 return (-1);
508 } 436 }
509 437
510 /* Trigger audio playback */ 438 /* Trigger audio playback */
511 rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK); 439 rval = snd_pcm_plugin_prepare(this->hidden->audio_handle,
440 SND_PCM_CHANNEL_PLAYBACK);
512 if (rval < 0) { 441 if (rval < 0) {
513 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", 442 NTO_CloseDevice(this);
514 snd_strerror(rval)); 443 NTO_SetError("snd_pcm_plugin_prepare", rval);
515 return (-1); 444 return 0;
516 } 445 }
517
518 this->enabled = 1;
519
520 /* Get the parent process id (we're the parent of the audio thread) */
521 parent = getpid();
522 446
523 /* We're really ready to rock and roll. :-) */ 447 /* We're really ready to rock and roll. :-) */
524 return (0); 448 return 1;
525 } 449 }
450
451 AudioBootStrap QNXNTOAUDIO_bootstrap = {
452 DRIVER_NAME, "QNX6 QSA-NTO Audio",
453 NTO_AudioAvailable, NTO_Init, 0
454 };
526 455
527 /* vi: set ts=4 sw=4 expandtab: */ 456 /* vi: set ts=4 sw=4 expandtab: */