#include "SDL_internal.h"
#ifdef SDL_AUDIO_DRIVER_SNDIO
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <poll.h>
#include <unistd.h>
#include "../SDL_sysaudio.h"
#include "SDL_sndioaudio.h"
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
#endif
#ifndef INFTIM
#define INFTIM -1
#endif
#ifndef SIO_DEVANY
#define SIO_DEVANY "default"
#endif
static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int);
static void (*SNDIO_sio_close)(struct sio_hdl *);
static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
static int (*SNDIO_sio_start)(struct sio_hdl *);
static int (*SNDIO_sio_stop)(struct sio_hdl *);
static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
static int (*SNDIO_sio_nfds)(struct sio_hdl *);
static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
static int (*SNDIO_sio_eof)(struct sio_hdl *);
static void (*SNDIO_sio_initpar)(struct sio_par *);
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
static SDL_SharedObject *sndio_handle = NULL;
static bool load_sndio_sym(const char *fn, void **addr)
{
*addr = SDL_LoadFunction(sndio_handle, fn);
if (!*addr) {
return false; }
return true;
}
#define SDL_SNDIO_SYM(x) \
if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \
return false
#else
#define SDL_SNDIO_SYM(x) SNDIO_##x = x
#endif
static bool load_sndio_syms(void)
{
SDL_SNDIO_SYM(sio_open);
SDL_SNDIO_SYM(sio_close);
SDL_SNDIO_SYM(sio_setpar);
SDL_SNDIO_SYM(sio_getpar);
SDL_SNDIO_SYM(sio_start);
SDL_SNDIO_SYM(sio_stop);
SDL_SNDIO_SYM(sio_read);
SDL_SNDIO_SYM(sio_write);
SDL_SNDIO_SYM(sio_nfds);
SDL_SNDIO_SYM(sio_pollfd);
SDL_SNDIO_SYM(sio_revents);
SDL_SNDIO_SYM(sio_eof);
SDL_SNDIO_SYM(sio_initpar);
return true;
}
#undef SDL_SNDIO_SYM
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
SDL_ELF_NOTE_DLOPEN(
"audio-libsndio",
"Support for audio through libsndio",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
)
static void UnloadSNDIOLibrary(void)
{
if (sndio_handle) {
SDL_UnloadObject(sndio_handle);
sndio_handle = NULL;
}
}
static bool LoadSNDIOLibrary(void)
{
bool result = true;
if (!sndio_handle) {
sndio_handle = SDL_LoadObject(sndio_library);
if (!sndio_handle) {
result = false; } else {
result = load_sndio_syms();
if (!result) {
UnloadSNDIOLibrary();
}
}
}
return result;
}
#else
static void UnloadSNDIOLibrary(void)
{
}
static bool LoadSNDIOLibrary(void)
{
load_sndio_syms();
return true;
}
#endif
static bool SNDIO_WaitDevice(SDL_AudioDevice *device)
{
const bool recording = device->recording;
while (!SDL_GetAtomicInt(&device->shutdown)) {
if (SNDIO_sio_eof(device->hidden->dev)) {
return false;
}
const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, recording ? POLLIN : POLLOUT);
if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) {
return false;
}
const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd);
if (recording && (revents & POLLIN)) {
break;
} else if (!recording && (revents & POLLOUT)) {
break;
} else if (revents & POLLHUP) {
return false;
}
}
return true;
}
static bool SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) {
return false; }
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
return true;
}
static int SNDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
const size_t br = SNDIO_sio_read(device->hidden->dev, buffer, buflen);
if ((br == 0) && SNDIO_sio_eof(device->hidden->dev)) {
return -1;
}
return (int) br;
}
static void SNDIO_FlushRecording(SDL_AudioDevice *device)
{
char buf[512];
while (!SDL_GetAtomicInt(&device->shutdown) && (SNDIO_sio_read(device->hidden->dev, buf, sizeof(buf)) > 0)) {
}
}
static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
return device->hidden->mixbuf;
}
static void SNDIO_CloseDevice(SDL_AudioDevice *device)
{
if (device->hidden) {
if (device->hidden->dev) {
SNDIO_sio_stop(device->hidden->dev);
SNDIO_sio_close(device->hidden->dev);
}
SDL_free(device->hidden->pfd);
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
device->hidden = NULL;
}
}
static bool SNDIO_OpenDevice(SDL_AudioDevice *device)
{
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (!device->hidden) {
return false;
}
device->hidden->dev = SNDIO_sio_open(SIO_DEVANY,
device->recording ? SIO_REC : SIO_PLAY, device->recording);
if (!device->hidden->dev) {
return SDL_SetError("sio_open() failed");
}
device->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(device->hidden->dev));
if (!device->hidden->pfd) {
return false;
}
struct sio_par par;
SNDIO_sio_initpar(&par);
par.rate = device->spec.freq;
par.pchan = device->spec.channels;
par.round = device->sample_frames;
par.appbufsz = par.round * 2;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) {
if (!SDL_AUDIO_ISFLOAT(test_format)) {
par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
par.bits = SDL_AUDIO_BITSIZE(test_format);
if (SNDIO_sio_setpar(device->hidden->dev, &par) == 0) {
continue;
}
if (SNDIO_sio_getpar(device->hidden->dev, &par) == 0) {
return SDL_SetError("sio_getpar() failed");
}
if (par.bps != SIO_BPS(par.bits)) {
continue;
}
if ((par.bits == 8 * par.bps) || (par.msb)) {
break;
}
}
}
if (!test_format) {
return SDL_SetError("sndio: Unsupported audio format");
}
if ((par.bps == 4) && (par.sig) && (par.le)) {
device->spec.format = SDL_AUDIO_S32LE;
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
device->spec.format = SDL_AUDIO_S32BE;
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
device->spec.format = SDL_AUDIO_S16LE;
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
device->spec.format = SDL_AUDIO_S16BE;
} else if ((par.bps == 1) && (par.sig)) {
device->spec.format = SDL_AUDIO_S8;
} else if ((par.bps == 1) && (!par.sig)) {
device->spec.format = SDL_AUDIO_U8;
} else {
return SDL_SetError("sndio: Got unsupported hardware audio format.");
}
device->spec.freq = par.rate;
device->spec.channels = par.pchan;
device->sample_frames = par.round;
SDL_UpdatedAudioDeviceFormat(device);
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (!device->hidden->mixbuf) {
return false;
}
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
if (!SNDIO_sio_start(device->hidden->dev)) {
return SDL_SetError("sio_start() failed");
}
return true; }
static void SNDIO_Deinitialize(void)
{
UnloadSNDIOLibrary();
}
static void SNDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
{
*default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)0x1);
*default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)0x2);
}
static bool SNDIO_Init(SDL_AudioDriverImpl *impl)
{
if (!LoadSNDIOLibrary()) {
return false;
}
impl->OpenDevice = SNDIO_OpenDevice;
impl->WaitDevice = SNDIO_WaitDevice;
impl->PlayDevice = SNDIO_PlayDevice;
impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
impl->CloseDevice = SNDIO_CloseDevice;
impl->WaitRecordingDevice = SNDIO_WaitDevice;
impl->RecordDevice = SNDIO_RecordDevice;
impl->FlushRecording = SNDIO_FlushRecording;
impl->Deinitialize = SNDIO_Deinitialize;
impl->DetectDevices = SNDIO_DetectDevices;
impl->HasRecordingSupport = true;
return true;
}
AudioBootStrap SNDIO_bootstrap = {
"sndio", "OpenBSD sndio", SNDIO_Init, false, false
};
#endif