SDL_qsa_audio.c (27766B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "../../SDL_internal.h" 23 24#if SDL_AUDIO_DRIVER_QSA 25 26#include <errno.h> 27#include <unistd.h> 28#include <fcntl.h> 29#include <signal.h> 30#include <sys/types.h> 31#include <sys/time.h> 32#include <sched.h> 33#include <sys/select.h> 34#include <sys/neutrino.h> 35#include <sys/asoundlib.h> 36 37#include "SDL_timer.h" 38#include "SDL_audio.h" 39#include "../SDL_audiomem.h" 40#include "../SDL_audio_c.h" 41#include "SDL_qsa_audio.h" 42 43/* default channel communication parameters */ 44#define DEFAULT_CPARAMS_RATE 44100 45#define DEFAULT_CPARAMS_VOICES 1 46 47#define DEFAULT_CPARAMS_FRAG_SIZE 4096 48#define DEFAULT_CPARAMS_FRAGS_MIN 1 49#define DEFAULT_CPARAMS_FRAGS_MAX 1 50 51#define QSA_NO_WORKAROUNDS 0x00000000 52#define QSA_MMAP_WORKAROUND 0x00000001 53 54struct BuggyCards 55{ 56 char *cardname; 57 unsigned long bugtype; 58}; 59 60#define QSA_WA_CARDS 3 61#define QSA_MAX_CARD_NAME_LENGTH 33 62 63struct BuggyCards buggycards[QSA_WA_CARDS] = { 64 {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, 65 {"Vortex 8820", QSA_MMAP_WORKAROUND}, 66 {"Vortex 8830", QSA_MMAP_WORKAROUND}, 67}; 68 69/* List of found devices */ 70#define QSA_MAX_DEVICES 32 71#define QSA_MAX_NAME_LENGTH 81+16 /* Hardcoded in QSA, can't be changed */ 72 73typedef struct _QSA_Device 74{ 75 char name[QSA_MAX_NAME_LENGTH]; /* Long audio device name for SDL */ 76 int cardno; 77 int deviceno; 78} QSA_Device; 79 80QSA_Device qsa_playback_device[QSA_MAX_DEVICES]; 81uint32_t qsa_playback_devices; 82 83QSA_Device qsa_capture_device[QSA_MAX_DEVICES]; 84uint32_t qsa_capture_devices; 85 86static SDL_INLINE int 87QSA_SetError(const char *fn, int status) 88{ 89 return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status)); 90} 91 92/* card names check to apply the workarounds */ 93static int 94QSA_CheckBuggyCards(_THIS, unsigned long checkfor) 95{ 96 char scardname[QSA_MAX_CARD_NAME_LENGTH]; 97 int it; 98 99 if (snd_card_get_name 100 (this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH - 1) < 0) { 101 return 0; 102 } 103 104 for (it = 0; it < QSA_WA_CARDS; it++) { 105 if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) { 106 if (buggycards[it].bugtype == checkfor) { 107 return 1; 108 } 109 } 110 } 111 112 return 0; 113} 114 115/* !!! FIXME: does this need to be here? Does the SDL version not work? */ 116static void 117QSA_ThreadInit(_THIS) 118{ 119 struct sched_param param; 120 int status; 121 122 /* Increase default 10 priority to 25 to avoid jerky sound */ 123 status = SchedGet(0, 0, ¶m); 124 param.sched_priority = param.sched_curpriority + 15; 125 status = SchedSet(0, 0, SCHED_NOCHANGE, ¶m); 126} 127 128/* PCM channel parameters initialize function */ 129static void 130QSA_InitAudioParams(snd_pcm_channel_params_t * cpars) 131{ 132 SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t)); 133 134 cpars->channel = SND_PCM_CHANNEL_PLAYBACK; 135 cpars->mode = SND_PCM_MODE_BLOCK; 136 cpars->start_mode = SND_PCM_START_DATA; 137 cpars->stop_mode = SND_PCM_STOP_STOP; 138 cpars->format.format = SND_PCM_SFMT_S16_LE; 139 cpars->format.interleave = 1; 140 cpars->format.rate = DEFAULT_CPARAMS_RATE; 141 cpars->format.voices = DEFAULT_CPARAMS_VOICES; 142 cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; 143 cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; 144 cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; 145} 146 147/* This function waits until it is possible to write a full sound buffer */ 148static void 149QSA_WaitDevice(_THIS) 150{ 151 fd_set wfds; 152 fd_set rfds; 153 int selectret; 154 struct timeval timeout; 155 156 if (!this->hidden->iscapture) { 157 FD_ZERO(&wfds); 158 FD_SET(this->hidden->audio_fd, &wfds); 159 } else { 160 FD_ZERO(&rfds); 161 FD_SET(this->hidden->audio_fd, &rfds); 162 } 163 164 do { 165 /* Setup timeout for playing one fragment equal to 2 seconds */ 166 /* If timeout occured than something wrong with hardware or driver */ 167 /* For example, Vortex 8820 audio driver stucks on second DAC because */ 168 /* it doesn't exist ! */ 169 timeout.tv_sec = 2; 170 timeout.tv_usec = 0; 171 this->hidden->timeout_on_wait = 0; 172 173 if (!this->hidden->iscapture) { 174 selectret = 175 select(this->hidden->audio_fd + 1, NULL, &wfds, NULL, 176 &timeout); 177 } else { 178 selectret = 179 select(this->hidden->audio_fd + 1, &rfds, NULL, NULL, 180 &timeout); 181 } 182 183 switch (selectret) { 184 case -1: 185 { 186 SDL_SetError("QSA: select() failed: %s", strerror(errno)); 187 return; 188 } 189 break; 190 case 0: 191 { 192 SDL_SetError("QSA: timeout on buffer waiting occured"); 193 this->hidden->timeout_on_wait = 1; 194 return; 195 } 196 break; 197 default: 198 { 199 if (!this->hidden->iscapture) { 200 if (FD_ISSET(this->hidden->audio_fd, &wfds)) { 201 return; 202 } 203 } else { 204 if (FD_ISSET(this->hidden->audio_fd, &rfds)) { 205 return; 206 } 207 } 208 } 209 break; 210 } 211 } while (1); 212} 213 214static void 215QSA_PlayDevice(_THIS) 216{ 217 snd_pcm_channel_status_t cstatus; 218 int written; 219 int status; 220 int towrite; 221 void *pcmbuffer; 222 223 if ((!this->enabled) || (!this->hidden)) { 224 return; 225 } 226 227 towrite = this->spec.size; 228 pcmbuffer = this->hidden->pcm_buf; 229 230 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ 231 do { 232 written = 233 snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer, 234 towrite); 235 if (written != towrite) { 236 /* Check if samples playback got stuck somewhere in hardware or in */ 237 /* the audio device driver */ 238 if ((errno == EAGAIN) && (written == 0)) { 239 if (this->hidden->timeout_on_wait != 0) { 240 SDL_SetError("QSA: buffer playback timeout"); 241 return; 242 } 243 } 244 245 /* Check for errors or conditions */ 246 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { 247 /* Let a little CPU time go by and try to write again */ 248 SDL_Delay(1); 249 250 /* if we wrote some data */ 251 towrite -= written; 252 pcmbuffer += written * this->spec.channels; 253 continue; 254 } else { 255 if ((errno == EINVAL) || (errno == EIO)) { 256 SDL_memset(&cstatus, 0, sizeof(cstatus)); 257 if (!this->hidden->iscapture) { 258 cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; 259 } else { 260 cstatus.channel = SND_PCM_CHANNEL_CAPTURE; 261 } 262 263 status = 264 snd_pcm_plugin_status(this->hidden->audio_handle, 265 &cstatus); 266 if (status < 0) { 267 QSA_SetError("snd_pcm_plugin_status", status); 268 return; 269 } 270 271 if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || 272 (cstatus.status == SND_PCM_STATUS_READY)) { 273 if (!this->hidden->iscapture) { 274 status = 275 snd_pcm_plugin_prepare(this->hidden-> 276 audio_handle, 277 SND_PCM_CHANNEL_PLAYBACK); 278 } else { 279 status = 280 snd_pcm_plugin_prepare(this->hidden-> 281 audio_handle, 282 SND_PCM_CHANNEL_CAPTURE); 283 } 284 if (status < 0) { 285 QSA_SetError("snd_pcm_plugin_prepare", status); 286 return; 287 } 288 } 289 continue; 290 } else { 291 return; 292 } 293 } 294 } else { 295 /* we wrote all remaining data */ 296 towrite -= written; 297 pcmbuffer += written * this->spec.channels; 298 } 299 } while ((towrite > 0) && (this->enabled)); 300 301 /* If we couldn't write, assume fatal error for now */ 302 if (towrite != 0) { 303 this->enabled = 0; 304 } 305} 306 307static Uint8 * 308QSA_GetDeviceBuf(_THIS) 309{ 310 return this->hidden->pcm_buf; 311} 312 313static void 314QSA_CloseDevice(_THIS) 315{ 316 if (this->hidden != NULL) { 317 if (this->hidden->audio_handle != NULL) { 318 if (!this->hidden->iscapture) { 319 /* Finish playing available samples */ 320 snd_pcm_plugin_flush(this->hidden->audio_handle, 321 SND_PCM_CHANNEL_PLAYBACK); 322 } else { 323 /* Cancel unread samples during capture */ 324 snd_pcm_plugin_flush(this->hidden->audio_handle, 325 SND_PCM_CHANNEL_CAPTURE); 326 } 327 snd_pcm_close(this->hidden->audio_handle); 328 this->hidden->audio_handle = NULL; 329 } 330 331 SDL_FreeAudioMem(this->hidden->pcm_buf); 332 this->hidden->pcm_buf = NULL; 333 334 SDL_free(this->hidden); 335 this->hidden = NULL; 336 } 337} 338 339static int 340QSA_OpenDevice(_THIS, const char *devname, int iscapture) 341{ 342 int status = 0; 343 int format = 0; 344 SDL_AudioFormat test_format = 0; 345 int found = 0; 346 snd_pcm_channel_setup_t csetup; 347 snd_pcm_channel_params_t cparams; 348 349 /* Initialize all variables that we clean on shutdown */ 350 this->hidden = 351 (struct SDL_PrivateAudioData *) SDL_calloc(1, 352 (sizeof 353 (struct 354 SDL_PrivateAudioData))); 355 if (this->hidden == NULL) { 356 return SDL_OutOfMemory(); 357 } 358 SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData)); 359 360 /* Initialize channel transfer parameters to default */ 361 QSA_InitAudioParams(&cparams); 362 363 /* Initialize channel direction: capture or playback */ 364 this->hidden->iscapture = iscapture; 365 366 /* Find deviceid and cardid by device name for playback */ 367 if ((!this->hidden->iscapture) && (devname != NULL)) { 368 uint32_t device; 369 int32_t status; 370 371 /* Search in the playback devices */ 372 device = 0; 373 do { 374 status = SDL_strcmp(qsa_playback_device[device].name, devname); 375 if (status == 0) { 376 /* Found requested device */ 377 this->hidden->deviceno = qsa_playback_device[device].deviceno; 378 this->hidden->cardno = qsa_playback_device[device].cardno; 379 break; 380 } 381 device++; 382 if (device >= qsa_playback_devices) { 383 QSA_CloseDevice(this); 384 return SDL_SetError("No such playback device"); 385 } 386 } while (1); 387 } 388 389 /* Find deviceid and cardid by device name for capture */ 390 if ((this->hidden->iscapture) && (devname != NULL)) { 391 /* Search in the capture devices */ 392 uint32_t device; 393 int32_t status; 394 395 /* Searching in the playback devices */ 396 device = 0; 397 do { 398 status = SDL_strcmp(qsa_capture_device[device].name, devname); 399 if (status == 0) { 400 /* Found requested device */ 401 this->hidden->deviceno = qsa_capture_device[device].deviceno; 402 this->hidden->cardno = qsa_capture_device[device].cardno; 403 break; 404 } 405 device++; 406 if (device >= qsa_capture_devices) { 407 QSA_CloseDevice(this); 408 return SDL_SetError("No such capture device"); 409 } 410 } while (1); 411 } 412 413 /* Check if SDL requested default audio device */ 414 if (devname == NULL) { 415 /* Open system default audio device */ 416 if (!this->hidden->iscapture) { 417 status = snd_pcm_open_preferred(&this->hidden->audio_handle, 418 &this->hidden->cardno, 419 &this->hidden->deviceno, 420 SND_PCM_OPEN_PLAYBACK); 421 } else { 422 status = snd_pcm_open_preferred(&this->hidden->audio_handle, 423 &this->hidden->cardno, 424 &this->hidden->deviceno, 425 SND_PCM_OPEN_CAPTURE); 426 } 427 } else { 428 /* Open requested audio device */ 429 if (!this->hidden->iscapture) { 430 status = 431 snd_pcm_open(&this->hidden->audio_handle, 432 this->hidden->cardno, this->hidden->deviceno, 433 SND_PCM_OPEN_PLAYBACK); 434 } else { 435 status = 436 snd_pcm_open(&this->hidden->audio_handle, 437 this->hidden->cardno, this->hidden->deviceno, 438 SND_PCM_OPEN_CAPTURE); 439 } 440 } 441 442 /* Check if requested device is opened */ 443 if (status < 0) { 444 this->hidden->audio_handle = NULL; 445 QSA_CloseDevice(this); 446 return QSA_SetError("snd_pcm_open", status); 447 } 448 449 if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) { 450 /* Disable QSA MMAP plugin for buggy audio drivers */ 451 status = 452 snd_pcm_plugin_set_disable(this->hidden->audio_handle, 453 PLUGIN_DISABLE_MMAP); 454 if (status < 0) { 455 QSA_CloseDevice(this); 456 return QSA_SetError("snd_pcm_plugin_set_disable", status); 457 } 458 } 459 460 /* Try for a closest match on audio format */ 461 format = 0; 462 /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */ 463 found = 0; 464 465 for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) { 466 /* if match found set format to equivalent QSA format */ 467 switch (test_format) { 468 case AUDIO_U8: 469 { 470 format = SND_PCM_SFMT_U8; 471 found = 1; 472 } 473 break; 474 case AUDIO_S8: 475 { 476 format = SND_PCM_SFMT_S8; 477 found = 1; 478 } 479 break; 480 case AUDIO_S16LSB: 481 { 482 format = SND_PCM_SFMT_S16_LE; 483 found = 1; 484 } 485 break; 486 case AUDIO_S16MSB: 487 { 488 format = SND_PCM_SFMT_S16_BE; 489 found = 1; 490 } 491 break; 492 case AUDIO_U16LSB: 493 { 494 format = SND_PCM_SFMT_U16_LE; 495 found = 1; 496 } 497 break; 498 case AUDIO_U16MSB: 499 { 500 format = SND_PCM_SFMT_U16_BE; 501 found = 1; 502 } 503 break; 504 case AUDIO_S32LSB: 505 { 506 format = SND_PCM_SFMT_S32_LE; 507 found = 1; 508 } 509 break; 510 case AUDIO_S32MSB: 511 { 512 format = SND_PCM_SFMT_S32_BE; 513 found = 1; 514 } 515 break; 516 case AUDIO_F32LSB: 517 { 518 format = SND_PCM_SFMT_FLOAT_LE; 519 found = 1; 520 } 521 break; 522 case AUDIO_F32MSB: 523 { 524 format = SND_PCM_SFMT_FLOAT_BE; 525 found = 1; 526 } 527 break; 528 default: 529 { 530 break; 531 } 532 } 533 534 if (!found) { 535 test_format = SDL_NextAudioFormat(); 536 } 537 } 538 539 /* assumes test_format not 0 on success */ 540 if (test_format == 0) { 541 QSA_CloseDevice(this); 542 return SDL_SetError("QSA: Couldn't find any hardware audio formats"); 543 } 544 545 this->spec.format = test_format; 546 547 /* Set the audio format */ 548 cparams.format.format = format; 549 550 /* Set mono/stereo/4ch/6ch/8ch audio */ 551 cparams.format.voices = this->spec.channels; 552 553 /* Set rate */ 554 cparams.format.rate = this->spec.freq; 555 556 /* Setup the transfer parameters according to cparams */ 557 status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams); 558 if (status < 0) { 559 QSA_CloseDevice(this); 560 return QSA_SetError("snd_pcm_channel_params", status); 561 } 562 563 /* Make sure channel is setup right one last time */ 564 SDL_memset(&csetup, 0, sizeof(csetup)); 565 if (!this->hidden->iscapture) { 566 csetup.channel = SND_PCM_CHANNEL_PLAYBACK; 567 } else { 568 csetup.channel = SND_PCM_CHANNEL_CAPTURE; 569 } 570 571 /* Setup an audio channel */ 572 if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) { 573 QSA_CloseDevice(this); 574 return SDL_SetError("QSA: Unable to setup channel"); 575 } 576 577 /* Calculate the final parameters for this audio specification */ 578 SDL_CalculateAudioSpec(&this->spec); 579 580 this->hidden->pcm_len = this->spec.size; 581 582 if (this->hidden->pcm_len == 0) { 583 this->hidden->pcm_len = 584 csetup.buf.block.frag_size * this->spec.channels * 585 (snd_pcm_format_width(format) / 8); 586 } 587 588 /* 589 * Allocate memory to the audio buffer and initialize with silence 590 * (Note that buffer size must be a multiple of fragment size, so find 591 * closest multiple) 592 */ 593 this->hidden->pcm_buf = 594 (Uint8 *) SDL_AllocAudioMem(this->hidden->pcm_len); 595 if (this->hidden->pcm_buf == NULL) { 596 QSA_CloseDevice(this); 597 return SDL_OutOfMemory(); 598 } 599 SDL_memset(this->hidden->pcm_buf, this->spec.silence, 600 this->hidden->pcm_len); 601 602 /* get the file descriptor */ 603 if (!this->hidden->iscapture) { 604 this->hidden->audio_fd = 605 snd_pcm_file_descriptor(this->hidden->audio_handle, 606 SND_PCM_CHANNEL_PLAYBACK); 607 } else { 608 this->hidden->audio_fd = 609 snd_pcm_file_descriptor(this->hidden->audio_handle, 610 SND_PCM_CHANNEL_CAPTURE); 611 } 612 613 if (this->hidden->audio_fd < 0) { 614 QSA_CloseDevice(this); 615 return QSA_SetError("snd_pcm_file_descriptor", status); 616 } 617 618 /* Prepare an audio channel */ 619 if (!this->hidden->iscapture) { 620 /* Prepare audio playback */ 621 status = 622 snd_pcm_plugin_prepare(this->hidden->audio_handle, 623 SND_PCM_CHANNEL_PLAYBACK); 624 } else { 625 /* Prepare audio capture */ 626 status = 627 snd_pcm_plugin_prepare(this->hidden->audio_handle, 628 SND_PCM_CHANNEL_CAPTURE); 629 } 630 631 if (status < 0) { 632 QSA_CloseDevice(this); 633 return QSA_SetError("snd_pcm_plugin_prepare", status); 634 } 635 636 /* We're really ready to rock and roll. :-) */ 637 return 0; 638} 639 640static void 641QSA_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) 642{ 643 uint32_t it; 644 uint32_t cards; 645 uint32_t devices; 646 int32_t status; 647 648 /* Detect amount of available devices */ 649 /* this value can be changed in the runtime */ 650 cards = snd_cards(); 651 652 /* If io-audio manager is not running we will get 0 as number */ 653 /* of available audio devices */ 654 if (cards == 0) { 655 /* We have no any available audio devices */ 656 return; 657 } 658 659 /* Find requested devices by type */ 660 if (!iscapture) { 661 /* Playback devices enumeration requested */ 662 for (it = 0; it < cards; it++) { 663 devices = 0; 664 do { 665 status = 666 snd_card_get_longname(it, 667 qsa_playback_device 668 [qsa_playback_devices].name, 669 QSA_MAX_NAME_LENGTH); 670 if (status == EOK) { 671 snd_pcm_t *handle; 672 673 /* Add device number to device name */ 674 sprintf(qsa_playback_device[qsa_playback_devices].name + 675 SDL_strlen(qsa_playback_device 676 [qsa_playback_devices].name), " d%d", 677 devices); 678 679 /* Store associated card number id */ 680 qsa_playback_device[qsa_playback_devices].cardno = it; 681 682 /* Check if this device id could play anything */ 683 status = 684 snd_pcm_open(&handle, it, devices, 685 SND_PCM_OPEN_PLAYBACK); 686 if (status == EOK) { 687 qsa_playback_device[qsa_playback_devices].deviceno = 688 devices; 689 status = snd_pcm_close(handle); 690 if (status == EOK) { 691 addfn(qsa_playback_device[qsa_playback_devices].name); 692 qsa_playback_devices++; 693 } 694 } else { 695 /* Check if we got end of devices list */ 696 if (status == -ENOENT) { 697 break; 698 } 699 } 700 } else { 701 break; 702 } 703 704 /* Check if we reached maximum devices count */ 705 if (qsa_playback_devices >= QSA_MAX_DEVICES) { 706 break; 707 } 708 devices++; 709 } while (1); 710 711 /* Check if we reached maximum devices count */ 712 if (qsa_playback_devices >= QSA_MAX_DEVICES) { 713 break; 714 } 715 } 716 } else { 717 /* Capture devices enumeration requested */ 718 for (it = 0; it < cards; it++) { 719 devices = 0; 720 do { 721 status = 722 snd_card_get_longname(it, 723 qsa_capture_device 724 [qsa_capture_devices].name, 725 QSA_MAX_NAME_LENGTH); 726 if (status == EOK) { 727 snd_pcm_t *handle; 728 729 /* Add device number to device name */ 730 sprintf(qsa_capture_device[qsa_capture_devices].name + 731 SDL_strlen(qsa_capture_device 732 [qsa_capture_devices].name), " d%d", 733 devices); 734 735 /* Store associated card number id */ 736 qsa_capture_device[qsa_capture_devices].cardno = it; 737 738 /* Check if this device id could play anything */ 739 status = 740 snd_pcm_open(&handle, it, devices, 741 SND_PCM_OPEN_CAPTURE); 742 if (status == EOK) { 743 qsa_capture_device[qsa_capture_devices].deviceno = 744 devices; 745 status = snd_pcm_close(handle); 746 if (status == EOK) { 747 addfn(qsa_capture_device[qsa_capture_devices].name); 748 qsa_capture_devices++; 749 } 750 } else { 751 /* Check if we got end of devices list */ 752 if (status == -ENOENT) { 753 break; 754 } 755 } 756 757 /* Check if we reached maximum devices count */ 758 if (qsa_capture_devices >= QSA_MAX_DEVICES) { 759 break; 760 } 761 } else { 762 break; 763 } 764 devices++; 765 } while (1); 766 767 /* Check if we reached maximum devices count */ 768 if (qsa_capture_devices >= QSA_MAX_DEVICES) { 769 break; 770 } 771 } 772 } 773} 774 775static void 776QSA_WaitDone(_THIS) 777{ 778 if (!this->hidden->iscapture) { 779 if (this->hidden->audio_handle != NULL) { 780 /* Wait till last fragment is played and stop channel */ 781 snd_pcm_plugin_flush(this->hidden->audio_handle, 782 SND_PCM_CHANNEL_PLAYBACK); 783 } 784 } else { 785 if (this->hidden->audio_handle != NULL) { 786 /* Discard all unread data and stop channel */ 787 snd_pcm_plugin_flush(this->hidden->audio_handle, 788 SND_PCM_CHANNEL_CAPTURE); 789 } 790 } 791} 792 793static void 794QSA_Deinitialize(void) 795{ 796 /* Clear devices array on shutdown */ 797 SDL_memset(qsa_playback_device, 0x00, 798 sizeof(QSA_Device) * QSA_MAX_DEVICES); 799 SDL_memset(qsa_capture_device, 0x00, 800 sizeof(QSA_Device) * QSA_MAX_DEVICES); 801 qsa_playback_devices = 0; 802 qsa_capture_devices = 0; 803} 804 805static int 806QSA_Init(SDL_AudioDriverImpl * impl) 807{ 808 snd_pcm_t *handle = NULL; 809 int32_t status = 0; 810 811 /* Clear devices array */ 812 SDL_memset(qsa_playback_device, 0x00, 813 sizeof(QSA_Device) * QSA_MAX_DEVICES); 814 SDL_memset(qsa_capture_device, 0x00, 815 sizeof(QSA_Device) * QSA_MAX_DEVICES); 816 qsa_playback_devices = 0; 817 qsa_capture_devices = 0; 818 819 /* Set function pointers */ 820 /* DeviceLock and DeviceUnlock functions are used default, */ 821 /* provided by SDL, which uses pthread_mutex for lock/unlock */ 822 impl->DetectDevices = QSA_DetectDevices; 823 impl->OpenDevice = QSA_OpenDevice; 824 impl->ThreadInit = QSA_ThreadInit; 825 impl->WaitDevice = QSA_WaitDevice; 826 impl->PlayDevice = QSA_PlayDevice; 827 impl->GetDeviceBuf = QSA_GetDeviceBuf; 828 impl->CloseDevice = QSA_CloseDevice; 829 impl->WaitDone = QSA_WaitDone; 830 impl->Deinitialize = QSA_Deinitialize; 831 impl->LockDevice = NULL; 832 impl->UnlockDevice = NULL; 833 834 impl->OnlyHasDefaultOutputDevice = 0; 835 impl->ProvidesOwnCallbackThread = 0; 836 impl->SkipMixerLock = 0; 837 impl->HasCaptureSupport = 1; 838 impl->OnlyHasDefaultOutputDevice = 0; 839 impl->OnlyHasDefaultInputDevice = 0; 840 841 /* Check if io-audio manager is running or not */ 842 status = snd_cards(); 843 if (status == 0) { 844 /* if no, return immediately */ 845 return 1; 846 } 847 848 return 1; /* this audio target is available. */ 849} 850 851AudioBootStrap QSAAUDIO_bootstrap = { 852 "qsa", "QNX QSA Audio", QSA_Init, 0 853}; 854 855#endif /* SDL_AUDIO_DRIVER_QSA */ 856 857/* vi: set ts=4 sw=4 expandtab: */