24 #if SDL_AUDIO_DRIVER_QSA
33 #include <sys/select.h>
34 #include <sys/neutrino.h>
35 #include <sys/asoundlib.h>
39 #include "../SDL_audiomem.h"
40 #include "../SDL_audio_c.h"
44 #define DEFAULT_CPARAMS_RATE 44100
45 #define DEFAULT_CPARAMS_VOICES 1
47 #define DEFAULT_CPARAMS_FRAG_SIZE 4096
48 #define DEFAULT_CPARAMS_FRAGS_MIN 1
49 #define DEFAULT_CPARAMS_FRAGS_MAX 1
51 #define QSA_NO_WORKAROUNDS 0x00000000
52 #define QSA_MMAP_WORKAROUND 0x00000001
57 unsigned long bugtype;
60 #define QSA_WA_CARDS 3
61 #define QSA_MAX_CARD_NAME_LENGTH 33
63 struct BuggyCards buggycards[QSA_WA_CARDS] = {
64 {
"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
65 {
"Vortex 8820", QSA_MMAP_WORKAROUND},
66 {
"Vortex 8830", QSA_MMAP_WORKAROUND},
70 #define QSA_MAX_DEVICES 32
71 #define QSA_MAX_NAME_LENGTH 81+16
73 typedef struct _QSA_Device
75 char name[QSA_MAX_NAME_LENGTH];
80 QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
83 QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
87 QSA_SetError(
const char *fn,
int status)
89 return SDL_SetError(
"QSA: %s() failed: %s", fn, snd_strerror(status));
94 QSA_CheckBuggyCards(
_THIS,
unsigned long checkfor)
96 char scardname[QSA_MAX_CARD_NAME_LENGTH];
100 (this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH - 1) < 0) {
104 for (it = 0; it < QSA_WA_CARDS; it++) {
105 if (
SDL_strcmp(buggycards[it].cardname, scardname) == 0) {
106 if (buggycards[it].bugtype == checkfor) {
117 QSA_ThreadInit(
_THIS)
119 struct sched_param
param;
123 status = SchedGet(0, 0, &
param);
124 param.sched_priority =
param.sched_curpriority + 15;
125 status = SchedSet(0, 0, SCHED_NOCHANGE, &
param);
130 QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
132 SDL_memset(cpars, 0,
sizeof(snd_pcm_channel_params_t));
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;
149 QSA_WaitDevice(
_THIS)
156 if (!this->hidden->iscapture) {
158 FD_SET(this->hidden->audio_fd, &wfds);
161 FD_SET(this->hidden->audio_fd, &rfds);
171 this->hidden->timeout_on_wait = 0;
173 if (!this->hidden->iscapture) {
175 select(this->hidden->audio_fd + 1,
NULL, &wfds,
NULL,
179 select(this->hidden->audio_fd + 1, &rfds,
NULL,
NULL,
186 SDL_SetError(
"QSA: select() failed: %s", strerror(errno));
193 this->hidden->timeout_on_wait = 1;
199 if (!this->hidden->iscapture) {
200 if (FD_ISSET(this->hidden->audio_fd, &wfds)) {
204 if (FD_ISSET(this->hidden->audio_fd, &rfds)) {
215 QSA_PlayDevice(
_THIS)
217 snd_pcm_channel_status_t cstatus;
223 if ((!this->
enabled) || (!this->hidden)) {
227 towrite = this->spec.size;
228 pcmbuffer = this->hidden->pcm_buf;
233 snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer,
235 if (written != towrite) {
238 if ((errno == EAGAIN) && (written == 0)) {
239 if (this->hidden->timeout_on_wait != 0) {
246 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
252 pcmbuffer += written * this->spec.channels;
255 if ((errno == EINVAL) || (errno == EIO)) {
257 if (!this->hidden->iscapture) {
258 cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
260 cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
264 snd_pcm_plugin_status(this->hidden->audio_handle,
267 QSA_SetError(
"snd_pcm_plugin_status", status);
271 if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
272 (cstatus.status == SND_PCM_STATUS_READY)) {
273 if (!this->hidden->iscapture) {
275 snd_pcm_plugin_prepare(this->hidden->
277 SND_PCM_CHANNEL_PLAYBACK);
280 snd_pcm_plugin_prepare(this->hidden->
282 SND_PCM_CHANNEL_CAPTURE);
285 QSA_SetError(
"snd_pcm_plugin_prepare", status);
297 pcmbuffer += written * this->spec.channels;
299 }
while ((towrite > 0) && (this->
enabled));
308 QSA_GetDeviceBuf(
_THIS)
310 return this->hidden->pcm_buf;
314 QSA_CloseDevice(
_THIS)
316 if (this->hidden !=
NULL) {
317 if (this->hidden->audio_handle !=
NULL) {
318 if (!this->hidden->iscapture) {
320 snd_pcm_plugin_flush(this->hidden->audio_handle,
321 SND_PCM_CHANNEL_PLAYBACK);
324 snd_pcm_plugin_flush(this->hidden->audio_handle,
325 SND_PCM_CHANNEL_CAPTURE);
327 snd_pcm_close(this->hidden->audio_handle);
328 this->hidden->audio_handle =
NULL;
332 this->hidden->pcm_buf =
NULL;
340 QSA_OpenDevice(
_THIS,
const char *devname,
int iscapture)
346 snd_pcm_channel_setup_t csetup;
347 snd_pcm_channel_params_t cparams;
355 if (this->hidden ==
NULL) {
361 QSA_InitAudioParams(&cparams);
367 if ((!this->hidden->iscapture) && (devname !=
NULL)) {
377 this->hidden->deviceno = qsa_playback_device[device].deviceno;
378 this->hidden->cardno = qsa_playback_device[device].cardno;
382 if (device >= qsa_playback_devices) {
383 QSA_CloseDevice(
this);
390 if ((this->hidden->iscapture) && (devname !=
NULL)) {
401 this->hidden->deviceno = qsa_capture_device[device].deviceno;
402 this->hidden->cardno = qsa_capture_device[device].cardno;
406 if (device >= qsa_capture_devices) {
407 QSA_CloseDevice(
this);
414 if (devname ==
NULL) {
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);
422 status = snd_pcm_open_preferred(&this->hidden->audio_handle,
423 &this->hidden->cardno,
424 &this->hidden->deviceno,
425 SND_PCM_OPEN_CAPTURE);
429 if (!this->hidden->iscapture) {
431 snd_pcm_open(&this->hidden->audio_handle,
432 this->hidden->cardno, this->hidden->deviceno,
433 SND_PCM_OPEN_PLAYBACK);
436 snd_pcm_open(&this->hidden->audio_handle,
437 this->hidden->cardno, this->hidden->deviceno,
438 SND_PCM_OPEN_CAPTURE);
444 this->hidden->audio_handle =
NULL;
445 QSA_CloseDevice(
this);
446 return QSA_SetError(
"snd_pcm_open", status);
449 if (!QSA_CheckBuggyCards(
this, QSA_MMAP_WORKAROUND)) {
452 snd_pcm_plugin_set_disable(this->hidden->audio_handle,
453 PLUGIN_DISABLE_MMAP);
455 QSA_CloseDevice(
this);
456 return QSA_SetError(
"snd_pcm_plugin_set_disable", status);
467 switch (test_format) {
470 format = SND_PCM_SFMT_U8;
476 format = SND_PCM_SFMT_S8;
482 format = SND_PCM_SFMT_S16_LE;
488 format = SND_PCM_SFMT_S16_BE;
494 format = SND_PCM_SFMT_U16_LE;
500 format = SND_PCM_SFMT_U16_BE;
506 format = SND_PCM_SFMT_S32_LE;
512 format = SND_PCM_SFMT_S32_BE;
518 format = SND_PCM_SFMT_FLOAT_LE;
524 format = SND_PCM_SFMT_FLOAT_BE;
540 if (test_format == 0) {
541 QSA_CloseDevice(
this);
542 return SDL_SetError(
"QSA: Couldn't find any hardware audio formats");
545 this->spec.format = test_format;
548 cparams.format.format =
format;
551 cparams.format.voices = this->spec.channels;
554 cparams.format.rate = this->spec.freq;
557 status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
559 QSA_CloseDevice(
this);
560 return QSA_SetError(
"snd_pcm_channel_params", status);
565 if (!this->hidden->iscapture) {
566 csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
568 csetup.channel = SND_PCM_CHANNEL_CAPTURE;
572 if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
573 QSA_CloseDevice(
this);
580 this->hidden->pcm_len = this->spec.size;
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);
593 this->hidden->pcm_buf =
595 if (this->hidden->pcm_buf ==
NULL) {
596 QSA_CloseDevice(
this);
599 SDL_memset(this->hidden->pcm_buf, this->spec.silence,
600 this->hidden->pcm_len);
603 if (!this->hidden->iscapture) {
604 this->hidden->audio_fd =
605 snd_pcm_file_descriptor(this->hidden->audio_handle,
606 SND_PCM_CHANNEL_PLAYBACK);
608 this->hidden->audio_fd =
609 snd_pcm_file_descriptor(this->hidden->audio_handle,
610 SND_PCM_CHANNEL_CAPTURE);
613 if (this->hidden->audio_fd < 0) {
614 QSA_CloseDevice(
this);
615 return QSA_SetError(
"snd_pcm_file_descriptor", status);
619 if (!this->hidden->iscapture) {
622 snd_pcm_plugin_prepare(this->hidden->audio_handle,
623 SND_PCM_CHANNEL_PLAYBACK);
627 snd_pcm_plugin_prepare(this->hidden->audio_handle,
628 SND_PCM_CHANNEL_CAPTURE);
632 QSA_CloseDevice(
this);
633 return QSA_SetError(
"snd_pcm_plugin_prepare", status);
662 for (it = 0; it < cards; it++) {
666 snd_card_get_longname(it,
668 [qsa_playback_devices].
name,
669 QSA_MAX_NAME_LENGTH);
674 sprintf(qsa_playback_device[qsa_playback_devices].
name +
676 [qsa_playback_devices].
name),
" d%d",
680 qsa_playback_device[qsa_playback_devices].cardno = it;
684 snd_pcm_open(&handle, it, devices,
685 SND_PCM_OPEN_PLAYBACK);
687 qsa_playback_device[qsa_playback_devices].deviceno =
689 status = snd_pcm_close(handle);
691 addfn(qsa_playback_device[qsa_playback_devices].
name);
692 qsa_playback_devices++;
696 if (status == -ENOENT) {
705 if (qsa_playback_devices >= QSA_MAX_DEVICES) {
712 if (qsa_playback_devices >= QSA_MAX_DEVICES) {
718 for (it = 0; it < cards; it++) {
722 snd_card_get_longname(it,
724 [qsa_capture_devices].
name,
725 QSA_MAX_NAME_LENGTH);
730 sprintf(qsa_capture_device[qsa_capture_devices].
name +
732 [qsa_capture_devices].
name),
" d%d",
736 qsa_capture_device[qsa_capture_devices].cardno = it;
740 snd_pcm_open(&handle, it, devices,
741 SND_PCM_OPEN_CAPTURE);
743 qsa_capture_device[qsa_capture_devices].deviceno =
745 status = snd_pcm_close(handle);
747 addfn(qsa_capture_device[qsa_capture_devices].
name);
748 qsa_capture_devices++;
752 if (status == -ENOENT) {
758 if (qsa_capture_devices >= QSA_MAX_DEVICES) {
768 if (qsa_capture_devices >= QSA_MAX_DEVICES) {
778 if (!this->hidden->iscapture) {
779 if (this->hidden->audio_handle !=
NULL) {
781 snd_pcm_plugin_flush(this->hidden->audio_handle,
782 SND_PCM_CHANNEL_PLAYBACK);
785 if (this->hidden->audio_handle !=
NULL) {
787 snd_pcm_plugin_flush(this->hidden->audio_handle,
788 SND_PCM_CHANNEL_CAPTURE);
794 QSA_Deinitialize(
void)
798 sizeof(QSA_Device) * QSA_MAX_DEVICES);
800 sizeof(QSA_Device) * QSA_MAX_DEVICES);
801 qsa_playback_devices = 0;
802 qsa_capture_devices = 0;
808 snd_pcm_t *handle =
NULL;
813 sizeof(QSA_Device) * QSA_MAX_DEVICES);
815 sizeof(QSA_Device) * QSA_MAX_DEVICES);
816 qsa_playback_devices = 0;
817 qsa_capture_devices = 0;
842 status = snd_cards();
852 "qsa",
"QNX QSA Audio", QSA_Init, 0
void(* CloseDevice)(_THIS)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
DECLSPEC void *SDLCALL SDL_calloc(size_t nmemb, size_t size)
void(* SDL_AddAudioDevice)(const char *name)
void(* WaitDevice)(_THIS)
AudioBootStrap QSAAUDIO_bootstrap
GLenum GLsizei const GLuint GLboolean enabled
Uint8 *(* GetDeviceBuf)(_THIS)
DECLSPEC void SDLCALL SDL_free(void *mem)
int ProvidesOwnCallbackThread
EGLImageKHR EGLint * name
Uint16 SDL_AudioFormat
Audio format flags.
int(* OpenDevice)(_THIS, const char *devname, int iscapture)
SDL_AudioFormat SDL_NextAudioFormat(void)
void(* PlayDevice)(_THIS)
void(* DetectDevices)(int iscapture, SDL_AddAudioDevice addfn)
int OnlyHasDefaultOutputDevice
DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
Wait a specified number of milliseconds before returning.
DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len)
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
void(* Deinitialize)(void)
#define SDL_AllocAudioMem
GLbitfield GLuint64 timeout
DECLSPEC size_t SDLCALL SDL_strlen(const char *str)
#define SDL_OutOfMemory()
void(* ThreadInit)(_THIS)
void(* LockDevice)(_THIS)
uint8_t Uint8
An unsigned 8-bit integer type.
int OnlyHasDefaultInputDevice
EGLImageKHR EGLint EGLint * handle
void(* UnlockDevice)(_THIS)