qnxaudio: Rewrite for SDL3 audio APIs.

I have no way to compile or test this atm, so this will likely need
further attention. I ended up cleaning this up a ton and adding missing
features, so the code changes are pretty dramatic vs a simple conversion
to SDL3...so tread carefully in here.
This commit is contained in:
Ryan C. Gordon
2023-07-22 13:25:04 -04:00
parent 455eef4cd9
commit 2c578bd0d5
2 changed files with 232 additions and 405 deletions

View File

@@ -19,13 +19,7 @@
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
/* // !!! FIXME: can this target support hotplugging?
* !!! FIXME: streamline this a little by removing all the
* !!! FIXME: if (capture) {} else {} sections that are identical
* !!! FIXME: except for one flag.
*/
/* !!! FIXME: can this target support hotplugging? */
#include "../../SDL_internal.h" #include "../../SDL_internal.h"
@@ -48,7 +42,7 @@
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_qsa_audio.h" #include "SDL_qsa_audio.h"
/* default channel communication parameters */ // default channel communication parameters
#define DEFAULT_CPARAMS_RATE 44100 #define DEFAULT_CPARAMS_RATE 44100
#define DEFAULT_CPARAMS_VOICES 1 #define DEFAULT_CPARAMS_VOICES 1
@@ -56,32 +50,17 @@
#define DEFAULT_CPARAMS_FRAGS_MIN 1 #define DEFAULT_CPARAMS_FRAGS_MIN 1
#define DEFAULT_CPARAMS_FRAGS_MAX 1 #define DEFAULT_CPARAMS_FRAGS_MAX 1
/* List of found devices */ #define QSA_MAX_NAME_LENGTH 81+16 // Hardcoded in QSA, can't be changed
#define QSA_MAX_DEVICES 32
#define QSA_MAX_NAME_LENGTH 81+16 /* Hardcoded in QSA, can't be changed */
typedef struct _QSA_Device
{
char name[QSA_MAX_NAME_LENGTH]; /* Long audio device name for SDL */
int cardno;
int deviceno;
} QSA_Device;
QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
uint32_t qsa_playback_devices;
QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
uint32_t qsa_capture_devices;
static int QSA_SetError(const char *fn, int status) static int QSA_SetError(const char *fn, int status)
{ {
return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status)); return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
} }
/* !!! FIXME: does this need to be here? Does the SDL version not work? */ // !!! FIXME: does this need to be here? Does the SDL version not work?
static void QSA_ThreadInit(SDL_AudioDevice *_this) static void QSA_ThreadInit(SDL_AudioDevice *device)
{ {
/* Increase default 10 priority to 25 to avoid jerky sound */ // Increase default 10 priority to 25 to avoid jerky sound
struct sched_param param; struct sched_param param;
if (SchedGet(0, 0, &param) != -1) { if (SchedGet(0, 0, &param) != -1) {
param.sched_priority = param.sched_curpriority + 15; param.sched_priority = param.sched_curpriority + 15;
@@ -89,7 +68,7 @@ static void QSA_ThreadInit(SDL_AudioDevice *_this)
} }
} }
/* PCM channel parameters initialize function */ // PCM channel parameters initialize function
static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars) static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
{ {
SDL_zerop(cpars); SDL_zerop(cpars);
@@ -106,209 +85,150 @@ static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
} }
/* This function waits until it is possible to write a full sound buffer */ // This function waits until it is possible to write a full sound buffer
static void QSA_WaitDevice(SDL_AudioDevice *_this) static void QSA_WaitDevice(SDL_AudioDevice *device)
{ {
int result; int result;
/* Setup timeout for playing one fragment equal to 2 seconds */ // Setup timeout for playing one fragment equal to 2 seconds
/* If timeout occurred than something wrong with hardware or driver */ // If timeout occurred than something wrong with hardware or driver
/* For example, Vortex 8820 audio driver stucks on second DAC because */ // For example, Vortex 8820 audio driver stucks on second DAC because
/* it doesn't exist ! */ // it doesn't exist !
result = SDL_IOReady(_this->hidden->audio_fd, result = SDL_IOReady(device->hidden->audio_fd,
_this->hidden->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE, device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
2 * 1000); 2 * 1000);
switch (result) { switch (result) {
case -1: case -1:
SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno)); SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno)); // !!! FIXME: Should we just disconnect the device in this case?
break; break;
case 0: case 0:
SDL_SetError("QSA: timeout on buffer waiting occurred"); device->hidden->timeout_on_wait = SDL_TRUE; // !!! FIXME: Should we just disconnect the device in this case?
_this->hidden->timeout_on_wait = 1;
break; break;
default: default:
_this->hidden->timeout_on_wait = 0; device->hidden->timeout_on_wait = SDL_FALSE;
break; break;
} }
} }
static void QSA_PlayDevice(SDL_AudioDevice *_this) static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
snd_pcm_channel_status_t cstatus; if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
int written;
int status;
int towrite;
void *pcmbuffer;
if (!SDL_AtomicGet(&_this->enabled) || !_this->hidden) {
return; return;
} }
towrite = _this->spec.size; int towrite = buflen;
pcmbuffer = _this->hidden->pcm_buf;
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */ // Write the audio data, checking for EAGAIN (buffer full) and underrun
do { while ((towrite > 0) && !SDL_AtomicGet(&device->shutdown));
written = const int bw = snd_pcm_plugin_write(device->hidden->audio_handle, buffer, towrite);
snd_pcm_plugin_write(_this->hidden->audio_handle, pcmbuffer, if (bw != towrite) {
towrite); // Check if samples playback got stuck somewhere in hardware or in the audio device driver
if (written != towrite) { if ((errno == EAGAIN) && (bw == 0)) {
/* Check if samples playback got stuck somewhere in hardware or in */ if (device->hidden->timeout_on_wait) {
/* the audio device driver */ return; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
if ((errno == EAGAIN) && (written == 0)) {
if (_this->hidden->timeout_on_wait != 0) {
SDL_SetError("QSA: buffer playback timeout");
return;
} }
} }
/* Check for errors or conditions */ // Check for errors or conditions
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
/* Let a little CPU time go by and try to write again */ SDL_Delay(1); // Let a little CPU time go by and try to write again
SDL_Delay(1);
/* if we wrote some data */ // if we wrote some data
towrite -= written; towrite -= bw;
pcmbuffer += written * _this->spec.channels; buffer += bw * device->spec.channels;
continue;
} else if ((errno == EINVAL) || (errno == EIO)) {
snd_pcm_channel_status_t cstatus;
SDL_zero(cstatus);
cstatus.channel = device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status);
return; // !!! FIXME: disconnect the device?
} else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) {
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status);
return; // !!! FIXME: disconnect the device?
}
}
continue; continue;
} else { } else {
if ((errno == EINVAL) || (errno == EIO)) { return; // !!! FIXME: disconnect the device?
SDL_zero(cstatus);
if (!_this->hidden->iscapture) {
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
}
status =
snd_pcm_plugin_status(_this->hidden->audio_handle,
&cstatus);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status);
return;
}
if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
(cstatus.status == SND_PCM_STATUS_READY)) {
if (!_this->hidden->iscapture) {
status =
snd_pcm_plugin_prepare(_this->hidden->
audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
status =
snd_pcm_plugin_prepare(_this->hidden->
audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status);
return;
}
}
continue;
} else {
return;
}
} }
} else { } else {
/* we wrote all remaining data */ // we wrote all remaining data
towrite -= written; towrite -= bw;
pcmbuffer += written * _this->spec.channels; buffer += bw * device->spec.channels;
} }
} while ((towrite > 0) && SDL_AtomicGet(&_this->enabled)); }
/* If we couldn't write, assume fatal error for now */ // If we couldn't write, assume fatal error for now
if (towrite != 0) { if (towrite != 0) {
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
} }
} }
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->pcm_buf; return device->hidden->pcm_buf;
} }
static void QSA_CloseDevice(SDL_AudioDevice *_this) static void QSA_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->audio_handle != NULL) { if (device->hidden) {
#if _NTO_VERSION < 710 if (device->hidden->audio_handle != NULL) {
if (!_this->hidden->iscapture) { #if _NTO_VERSION < 710
/* Finish playing available samples */ // Finish playing available samples or cancel unread samples during capture
snd_pcm_plugin_flush(_this->hidden->audio_handle, snd_pcm_plugin_flush(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
SND_PCM_CHANNEL_PLAYBACK); #endif
} else { snd_pcm_close(device->hidden->audio_handle);
/* Cancel unread samples during capture */
snd_pcm_plugin_flush(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
} }
#endif
snd_pcm_close(_this->hidden->audio_handle);
}
SDL_free(_this->hidden->pcm_buf); SDL_free(device->hidden->pcm_buf);
SDL_free(_this->hidden); SDL_free(device->hidden);
device->hidden = NULL;
}
} }
static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int QSA_OpenDevice(SDL_AudioDevice *device)
{ {
#if 0 if (device->iscapture) {
/* !!! FIXME: SDL2 used to pass this handle. What's the alternative? */ return SDL_SetError("SDL capture support isn't available on QNX atm"); // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
const QSA_Device *device = (const QSA_Device *) handle; }
#else
const QSA_Device *device = NULL;
#endif
int status = 0;
int format = 0;
SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts;
snd_pcm_channel_setup_t csetup;
snd_pcm_channel_params_t cparams;
SDL_bool iscapture = _this->iscapture;
/* Initialize all variables that we clean on shutdown */ SDL_assert(device->handle != NULL); // NULL used to mean "system default device" in SDL2; it does not mean that in SDL3.
_this->hidden = const Uint32 sdlhandle = (Uint32) ((size_t) device->handle);
(struct SDL_PrivateAudioData *) SDL_calloc(1, const uint32_t cardno = (uint32_t) (sdlhandle & 0xFFFF);
(sizeof const uint32_t deviceno = (uint32_t) ((sdlhandle >> 16) & 0xFFFF);
(struct const SDL_bool iscapture = device->iscapture;
SDL_PrivateAudioData))); int status = 0;
if (_this->hidden == NULL) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof (struct SDL_PrivateAudioData)));
if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* Initialize channel transfer parameters to default */ // Initialize channel transfer parameters to default
snd_pcm_channel_params_t cparams;
QSA_InitAudioParams(&cparams); QSA_InitAudioParams(&cparams);
/* Initialize channel direction: capture or playback */ // Open requested audio device
_this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE; status = snd_pcm_open(&device->hidden->audio_handle, cardno, deviceno, iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
if (device != NULL) {
/* Open requested audio device */
_this->hidden->deviceno = device->deviceno;
_this->hidden->cardno = device->cardno;
status = snd_pcm_open(&_this->hidden->audio_handle,
device->cardno, device->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
} else {
/* Open system default audio device */
status = snd_pcm_open_preferred(&_this->hidden->audio_handle,
&_this->hidden->cardno,
&_this->hidden->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
}
/* Check if requested device is opened */
if (status < 0) { if (status < 0) {
_this->hidden->audio_handle = NULL; device->hidden->audio_handle = NULL;
return QSA_SetError("snd_pcm_open", status); return QSA_SetError("snd_pcm_open", status);
} }
/* Try for a closest match on audio format */ // Try for a closest match on audio format
closefmts = SDL_ClosestAudioFormats(_this->spec.format); SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
/* if match found set format to equivalent QSA format */ // if match found set format to equivalent QSA format
switch (test_format) { switch (test_format) {
#define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: format = SND_PCM_SFMT_##qsafmt; break #define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: cparams.format.format = SND_PCM_SFMT_##qsafmt; break
CHECKFMT(U8, U8); CHECKFMT(U8, U8);
CHECKFMT(S8, S8); CHECKFMT(S8, S8);
CHECKFMT(S16LSB, S16_LE); CHECKFMT(S16LSB, S16_LE);
@@ -323,267 +243,192 @@ static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
break; break;
} }
/* assumes test_format not 0 on success */ // assumes test_format not 0 on success
if (test_format == 0) { if (test_format == 0) {
return SDL_SetError("QSA: Couldn't find any hardware audio formats"); return SDL_SetError("QSA: Couldn't find any hardware audio formats");
} }
_this->spec.format = test_format; device->spec.format = test_format;
/* Set the audio format */ // Set mono/stereo/4ch/6ch/8ch audio
cparams.format.format = format; cparams.format.voices = device->spec.channels;
/* Set mono/stereo/4ch/6ch/8ch audio */ // Set rate
cparams.format.voices = _this->spec.channels; cparams.format.rate = device->spec.freq;
/* Set rate */ // Setup the transfer parameters according to cparams
cparams.format.rate = _this->spec.freq; status = snd_pcm_plugin_params(device->hidden->audio_handle, &cparams);
/* Setup the transfer parameters according to cparams */
status = snd_pcm_plugin_params(_this->hidden->audio_handle, &cparams);
if (status < 0) { if (status < 0) {
return QSA_SetError("snd_pcm_plugin_params", status); return QSA_SetError("snd_pcm_plugin_params", status);
} }
/* Make sure channel is setup right one last time */ // Make sure channel is setup right one last time
snd_pcm_channel_setup_t csetup;
SDL_zero(csetup); SDL_zero(csetup);
if (!_this->hidden->iscapture) { csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
csetup.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
} else {
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
}
/* Setup an audio channel */
if (snd_pcm_plugin_setup(_this->hidden->audio_handle, &csetup) < 0) {
return SDL_SetError("QSA: Unable to setup channel"); return SDL_SetError("QSA: Unable to setup channel");
} }
/* Calculate the final parameters for this audio specification */ device->sample_frames = csetup.buf.block.frag_size;
SDL_CalculateAudioSpec(&_this->spec);
_this->hidden->pcm_len = _this->spec.size; // Calculate the final parameters for this audio specification
SDL_UpdatedAudioDeviceFormat(device);
if (_this->hidden->pcm_len == 0) { device->hidden->pcm_buf = (Uint8 *) SDL_malloc(device->buffer_size);
_this->hidden->pcm_len = if (device->hidden->pcm_buf == NULL) {
csetup.buf.block.frag_size * _this->spec.channels *
(snd_pcm_format_width(format) / 8);
}
/*
* Allocate memory to the audio buffer and initialize with silence
* (Note that buffer size must be a multiple of fragment size, so find
* closest multiple)
*/
_this->hidden->pcm_buf =
(Uint8 *) SDL_malloc(_this->hidden->pcm_len);
if (_this->hidden->pcm_buf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->pcm_buf, _this->spec.silence, SDL_memset(device->hidden->pcm_buf, device->silence_value, device->buffer_size);
_this->hidden->pcm_len);
/* get the file descriptor */ // get the file descriptor
if (!_this->hidden->iscapture) { device->hidden->audio_fd = snd_pcm_file_descriptor(device->hidden->audio_handle, csetup.channel);
_this->hidden->audio_fd = if (device->hidden->audio_fd < 0) {
snd_pcm_file_descriptor(_this->hidden->audio_handle, return QSA_SetError("snd_pcm_file_descriptor", device->hidden->audio_fd);
SND_PCM_CHANNEL_PLAYBACK);
} else {
_this->hidden->audio_fd =
snd_pcm_file_descriptor(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (_this->hidden->audio_fd < 0) {
return QSA_SetError("snd_pcm_file_descriptor", status);
}
/* Prepare an audio channel */
if (!_this->hidden->iscapture) {
/* Prepare audio playback */
status =
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
/* Prepare audio capture */
status =
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
} }
// Prepare an audio channel
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, csetup.channel)
if (status < 0) { if (status < 0) {
return QSA_SetError("snd_pcm_plugin_prepare", status); return QSA_SetError("snd_pcm_plugin_prepare", status);
} }
/* We're really ready to rock and roll. :-) */ return 0; // We're really ready to rock and roll. :-)
return 0;
} }
static void QSA_DetectDevices(void) static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
{ {
uint32_t it; switch (qnxfmt) {
uint32_t cards; #define CHECKFMT(sdlfmt, qsafmt) case SND_PCM_SFMT_##qsafmt: return SDL_AUDIO_##sdlfmt
uint32_t devices; CHECKFMT(U8, U8);
int32_t status; CHECKFMT(S8, S8);
CHECKFMT(S16LSB, S16_LE);
CHECKFMT(S16MSB, S16_BE);
CHECKFMT(S32LSB, S32_LE);
CHECKFMT(S32MSB, S32_BE);
CHECKFMT(F32LSB, FLOAT_LE);
CHECKFMT(F32MSB, FLOAT_BE);
#undef CHECKFMT
default: break;
}
return SDL_AUDIO_S16SYS; // oh well.
}
/* Detect amount of available devices */ static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
/* this value can be changed in the runtime */ {
cards = snd_cards(); // Detect amount of available devices
// this value can be changed in the runtime
int num_cards = 0;
(void) snd_cards_list(NULL, 0, &alloc_num_cards);
SDL_bool isstack = SDL_FALSE;
int *cards = SDL_small_alloc(int, num_cards, &isstack);
if (!cards) {
return; // we're in trouble.
}
int overflow_cards = 0;
const int total_num_cards = snd_cards_list(cards, num_cards, &overflow_cards);
// if overflow_cards > 0 or total_num_cards > num_cards, it changed at the last moment; oh well, we lost some.
num_cards = SDL_min(num_cards, total_num_cards); // ...but make sure it didn't _shrink_.
/* If io-audio manager is not running we will get 0 as number */ // If io-audio manager is not running we will get 0 as number of available audio devices
/* of available audio devices */ if (num_cards == 0) { // not any available audio devices?
if (cards == 0) { SDL_small_free(cards, isstack);
/* We have no any available audio devices */
return; return;
} }
/* !!! FIXME: code duplication */ // Find requested devices by type
/* Find requested devices by type */ for (int it = 0; it < num_cards; it++) {
{ /* output devices */ const int card = cards[it];
/* Playback devices enumeration requested */ for (uint32_t deviceno = 0; ; deviceno++) {
for (it = 0; it < cards; it++) { int32_t status;
devices = 0; char name[QSA_MAX_NAME_LENGTH];
do {
status =
snd_card_get_longname(it,
qsa_playback_device
[qsa_playback_devices].name,
QSA_MAX_NAME_LENGTH);
if (status == EOK) {
snd_pcm_t *handle;
/* Add device number to device name */ status = snd_card_get_longname(card, name, sizeof (name));
sprintf(qsa_playback_device[qsa_playback_devices].name + if (status == EOK) {
SDL_strlen(qsa_playback_device snd_pcm_t *handle;
[qsa_playback_devices].name), " d%d",
devices);
/* Store associated card number id */ // Add device number to device name
qsa_playback_device[qsa_playback_devices].cardno = it; char fullname[QSA_MAX_NAME_LENGTH + 32];
SDL_snprintf(fullname, sizeof (fullname), "%s d%d", name, (int) deviceno);
/* Check if this device id could play anything */ // Check if this device id could play anything
status = SDL_bool iscapture = SDL_FALSE;
snd_pcm_open(&handle, it, devices, status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_PLAYBACK);
SND_PCM_OPEN_PLAYBACK); if (status != EOK) { // no? See if it's a capture device instead.
#if 0 // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_CAPTURE);
if (status == EOK) { if (status == EOK) {
qsa_playback_device[qsa_playback_devices].deviceno = iscapture = SDL_TRUE;
devices; }
status = snd_pcm_close(handle); #endif
if (status == EOK) { }
/* Note that spec is NULL, because we are required to open the device before
* acquiring the mix format, making this information inaccessible at if (status == EOK) {
* enumeration time SDL_AudioSpec spec;
*/ SDL_AudioSpec *pspec = &spec;
SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, NULL, &qsa_playback_device[qsa_playback_devices]); snd_pcm_channel_setup_t csetup;
qsa_playback_devices++; SDL_zero(csetup);
} csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
pspec = NULL; // go on without spec info.
} else { } else {
/* Check if we got end of devices list */ spec.format = QnxFormatToSDLFormat(csetup.format.format);
if (status == -ENOENT) { spec.channels = csetup.format.channels;
break; spec.freq = csetup.format.rate;
} }
status = snd_pcm_close(handle);
if (status == EOK) {
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
SDL_assert(card <= 0xFFFF);
SDL_assert(deviceno <= 0xFFFF);
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
SDL_AddAudioDevice(iscapture, fullname, pspec, (void *) ((size_t) sdlhandle));
} }
} else { } else {
break; // Check if we got end of devices list
if (status == -ENOENT) {
break;
}
} }
} else {
/* Check if we reached maximum devices count */
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
break;
}
devices++;
} while (1);
/* Check if we reached maximum devices count */
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
break; break;
} }
} }
} }
{ /* capture devices */ SDL_small_free(cards, isstack);
/* Capture devices enumeration requested */
for (it = 0; it < cards; it++) {
devices = 0;
do {
status =
snd_card_get_longname(it,
qsa_capture_device
[qsa_capture_devices].name,
QSA_MAX_NAME_LENGTH);
if (status == EOK) {
snd_pcm_t *handle;
/* Add device number to device name */ // Try to open the "preferred" devices, which will tell us the card/device pairs for the default devices.
sprintf(qsa_capture_device[qsa_capture_devices].name + snd_pcm_t handle;
SDL_strlen(qsa_capture_device int cardno, deviceno;
[qsa_capture_devices].name), " d%d", if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK) == 0) {
devices); snd_pcm_close(handle);
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
SDL_assert(cardno <= 0xFFFF);
SDL_assert(deviceno <= 0xFFFF);
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
*default_output = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
}
/* Store associated card number id */ if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_CAPTURE) == 0) {
qsa_capture_device[qsa_capture_devices].cardno = it; snd_pcm_close(handle);
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
/* Check if this device id could play anything */ SDL_assert(cardno <= 0xFFFF);
status = SDL_assert(deviceno <= 0xFFFF);
snd_pcm_open(&handle, it, devices, const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
SND_PCM_OPEN_CAPTURE); *default_capture = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
if (status == EOK) {
qsa_capture_device[qsa_capture_devices].deviceno =
devices;
status = snd_pcm_close(handle);
if (status == EOK) {
/* Note that spec is NULL, because we are required to open the device before
* acquiring the mix format, making this information inaccessible at
* enumeration time
*/
SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, NULL, &qsa_capture_device[qsa_capture_devices]);
qsa_capture_devices++;
}
} else {
/* Check if we got end of devices list */
if (status == -ENOENT) {
break;
}
}
/* Check if we reached maximum devices count */
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
break;
}
} else {
break;
}
devices++;
} while (1);
/* Check if we reached maximum devices count */
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
break;
}
}
} }
} }
static void QSA_Deinitialize(void) static void QSA_Deinitialize(void)
{ {
/* Clear devices array on shutdown */ // nothing to do here atm.
/* !!! FIXME: we zero these on init...any reason to do it here? */
SDL_zeroa(qsa_playback_device);
SDL_zeroa(qsa_capture_device);
qsa_playback_devices = 0;
qsa_capture_devices = 0;
} }
static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl) static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
{ {
/* Clear devices array */
SDL_zeroa(qsa_playback_device);
SDL_zeroa(qsa_capture_device);
qsa_playback_devices = 0;
qsa_capture_devices = 0;
/* Set function pointers */
/* DeviceLock and DeviceUnlock functions are used default, */
/* provided by SDL, which uses pthread_mutex for lock/unlock */
impl->DetectDevices = QSA_DetectDevices; impl->DetectDevices = QSA_DetectDevices;
impl->OpenDevice = QSA_OpenDevice; impl->OpenDevice = QSA_OpenDevice;
impl->ThreadInit = QSA_ThreadInit; impl->ThreadInit = QSA_ThreadInit;
@@ -592,21 +437,16 @@ static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
impl->GetDeviceBuf = QSA_GetDeviceBuf; impl->GetDeviceBuf = QSA_GetDeviceBuf;
impl->CloseDevice = QSA_CloseDevice; impl->CloseDevice = QSA_CloseDevice;
impl->Deinitialize = QSA_Deinitialize; impl->Deinitialize = QSA_Deinitialize;
impl->LockDevice = NULL;
impl->UnlockDevice = NULL;
impl->ProvidesOwnCallbackThread = 0; // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
impl->HasCaptureSupport = 1; //impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = 0;
impl->OnlyHasDefaultCaptureDevice = 0;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap QSAAUDIO_bootstrap = { AudioBootStrap QSAAUDIO_bootstrap = {
"qsa", "QNX QSA Audio", QSA_Init, 0 "qsa", "QNX QSA Audio", QSA_Init, 0
}; };
#endif /* SDL_AUDIO_DRIVER_QNX */ #endif // SDL_AUDIO_DRIVER_QNX
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -30,23 +30,10 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* SDL capture state */ snd_pcm_t *audio_handle; // The audio device handle
SDL_bool iscapture; int audio_fd; // The audio file descriptor, for selecting on
SDL_bool timeout_on_wait; // Select timeout status
/* The audio device handle */ Uint8 *pcm_buf; // Raw mixing buffer
int cardno;
int deviceno;
snd_pcm_t *audio_handle;
/* The audio file descriptor */
int audio_fd;
/* Select timeout status */
uint32_t timeout_on_wait;
/* Raw mixing buffer */
Uint8 *pcm_buf;
Uint32 pcm_len;
}; };
#endif /* __SDL_QSA_AUDIO_H__ */ #endif /* __SDL_QSA_AUDIO_H__ */