openbsd-ports/audio/fluidsynth/files/fluid_libsndio.c
jakemsr a6e78cfa88 - add sndio backend
- drop OSS audio support but keep "OSS" midi support
2008-12-26 08:45:12 +00:00

373 lines
9.5 KiB
C

/* libsndio backend for FluidSynth - A Software Synthesizer
*
* Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* fluid_libsndio.c
*
* Driver for the libsndio audio access library
*/
#include "fluid_synth.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#if LIBSNDIO_SUPPORT
#include <sndio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
/** fluid_libsndio_audio_driver_t
*
* This structure should not be accessed directly. Use audio port
* functions instead.
*/
typedef struct {
fluid_audio_driver_t driver;
fluid_synth_t* synth;
fluid_audio_callback_t read;
void* buffer;
pthread_t thread;
int cont;
struct sio_hdl *hdl;
struct sio_par par;
int buffer_size;
int buffer_byte_size;
fluid_audio_func_t callback;
void* data;
float* buffers[2];
} fluid_libsndio_audio_driver_t;
int delete_fluid_libsndio_audio_driver(fluid_audio_driver_t* p);
/* local utilities */
static void* fluid_libsndio_audio_run(void* d);
static void* fluid_libsndio_audio_run2(void* d);
void
fluid_libsndio_audio_driver_settings(fluid_settings_t* settings)
{
fluid_settings_register_str(settings, "audio.libsndio.device", NULL, 0, NULL, NULL);
}
/*
* new_fluid_libsndio_audio_driver
*/
fluid_audio_driver_t*
new_fluid_libsndio_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
{
fluid_libsndio_audio_driver_t* dev = NULL;
int queuesize;
double sample_rate;
int periods, period_size;
char* devname;
pthread_attr_t attr;
int err;
dev = FLUID_NEW(fluid_libsndio_audio_driver_t);
if (dev == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_libsndio_audio_driver_t));
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size);
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
dev->hdl = NULL;
dev->synth = synth;
dev->callback = NULL;
dev->data = NULL;
dev->cont = 1;
dev->buffer_size = (int) period_size;
queuesize = (int) (periods * period_size);
if (!fluid_settings_getstr(settings, "audio.libsndio.device", &devname)) {
devname = NULL;
}
dev->hdl = sio_open(devname, SIO_PLAY, 0);
if (dev->hdl == NULL) {
FLUID_LOG(FLUID_ERR, "libsndio could not be opened for writing");
goto error_recovery;
}
sio_initpar(&dev->par);
if (fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
dev->par.bits = 16;
#ifdef WORDS_BIGENDIAN
dev->par.le = 0;
#else
dev->par.le = 1;
#endif
dev->read = fluid_synth_write_s16;
dev->buffer_byte_size = dev->buffer_size * 4;
} else {
FLUID_LOG(FLUID_ERR, "Unknown sample format");
goto error_recovery;
}
dev->buffer = FLUID_MALLOC(dev->buffer_byte_size);
if (dev->buffer == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
dev->par.appbufsz = dev->buffer_size * periods;
dev->par.round = dev->buffer_size;
dev->par.pchan = 2;
dev->par.rate = sample_rate;
if (!sio_setpar(dev->hdl, &dev->par)) {
FLUID_LOG(FLUID_ERR, "Couldn't set libsndio audio parameters");
goto error_recovery;
}
if (!sio_getpar(dev->hdl, &dev->par)) {
FLUID_LOG(FLUID_ERR, "Couldn't get libsndio audio parameters");
goto error_recovery;
} else if (dev->par.pchan != 2 || dev->par.rate != sample_rate ||
dev->par.bits != 16) {
FLUID_LOG(FLUID_ERR, "Couldn't set libsndio audio parameters as desired");
goto error_recovery;
}
if (!sio_start(dev->hdl)) {
FLUID_LOG(FLUID_ERR, "Couldn't start libsndio");
goto error_recovery;
}
if (pthread_attr_init(&attr)) {
FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes");
goto error_recovery;
}
err = pthread_create(&dev->thread, &attr, fluid_libsndio_audio_run, (void*) dev);
if (err) {
FLUID_LOG(FLUID_ERR, "Couldn't create audio thread");
goto error_recovery;
}
return (fluid_audio_driver_t*) dev;
error_recovery:
delete_fluid_libsndio_audio_driver((fluid_audio_driver_t*) dev);
return NULL;
}
fluid_audio_driver_t*
new_fluid_libsndio_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data)
{
fluid_libsndio_audio_driver_t* dev = NULL;
int queuesize;
double sample_rate;
int periods, period_size;
char* devname;
pthread_attr_t attr;
int err;
dev = FLUID_NEW(fluid_libsndio_audio_driver_t);
if (dev == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_libsndio_audio_driver_t));
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size);
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
dev->hdl = NULL;
dev->synth = NULL;
dev->read = NULL;
dev->callback = func;
dev->data = data;
dev->cont = 1;
dev->buffer_size = (int) period_size;
queuesize = (int) (periods * period_size);
dev->buffer_byte_size = dev->buffer_size * 2 * 2; /* 2 channels * 16 bits audio */
if (!fluid_settings_getstr(settings, "audio.libsndio.device", &devname)) {
devname = NULL;
}
dev->hdl = sio_open(devname, SIO_PLAY, 0);
if (dev->hdl == NULL) {
FLUID_LOG(FLUID_ERR, "libsndio could not be opened for writing");
goto error_recovery;
}
sio_initpar(&dev->par);
dev->par.appbufsz = dev->buffer_size * periods;
dev->par.round = dev->buffer_size;
dev->par.bits = 16;
#ifdef WORDS_BIGENDIAN
dev->par.le = 0;
#else
dev->par.le = 1;
#endif
dev->par.pchan = 2;
dev->par.rate = sample_rate;
if (!sio_setpar(dev->hdl, &dev->par)){
FLUID_LOG(FLUID_ERR, "Can't configure libsndio parameters");
goto error_recovery;
}
if (!sio_getpar(dev->hdl, &dev->par)) {
FLUID_LOG(FLUID_ERR, "Couldn't get libsndio audio parameters");
goto error_recovery;
} else if (dev->par.pchan != 2 || dev->par.rate != sample_rate ||
dev->par.bits != 16) {
FLUID_LOG(FLUID_ERR, "Couldn't set libsndio audio parameters as desired");
goto error_recovery;
}
/* allocate the buffers. FIXME!!! don't use interleaved samples */
dev->buffer = FLUID_MALLOC(dev->buffer_byte_size);
dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size);
dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size);
if ((dev->buffer == NULL) || (dev->buffers[0] == NULL) || (dev->buffers[1] == NULL)) {
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
if (!sio_start(dev->hdl)) {
FLUID_LOG(FLUID_ERR, "Couldn't start libsndio");
goto error_recovery;
}
if (pthread_attr_init(&attr)) {
FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes");
goto error_recovery;
}
err = pthread_create(&dev->thread, &attr, fluid_libsndio_audio_run2, (void*) dev);
if (err) {
FLUID_LOG(FLUID_ERR, "Couldn't create audio2 thread");
goto error_recovery;
}
return (fluid_audio_driver_t*) dev;
error_recovery:
delete_fluid_libsndio_audio_driver((fluid_audio_driver_t*) dev);
return NULL;
}
/*
* delete_fluid_libsndio_audio_driver
*/
int
delete_fluid_libsndio_audio_driver(fluid_audio_driver_t* p)
{
fluid_libsndio_audio_driver_t* dev = (fluid_libsndio_audio_driver_t*) p;
if (dev == NULL) {
return FLUID_OK;
}
dev->cont = 0;
if (dev->thread) {
if (pthread_join(dev->thread, NULL)) {
FLUID_LOG(FLUID_ERR, "Failed to join the audio thread");
return FLUID_FAILED;
}
}
if (dev->hdl) {
sio_close(dev->hdl);
}
if (dev->buffer != NULL) {
FLUID_FREE(dev->buffer);
}
FLUID_FREE(dev);
return FLUID_OK;
}
/*
* fluid_libsndio_audio_run
*/
void*
fluid_libsndio_audio_run(void* d)
{
fluid_libsndio_audio_driver_t* dev = (fluid_libsndio_audio_driver_t*) d;
fluid_synth_t* synth = dev->synth;
void* buffer = dev->buffer;
int len = dev->buffer_size;
/* it's as simple as that: */
while (dev->cont)
{
dev->read (synth, len, buffer, 0, 2, buffer, 1, 2);
sio_write (dev->hdl, buffer, dev->buffer_byte_size);
}
FLUID_LOG(FLUID_DBG, "Audio thread finished");
pthread_exit(NULL);
return 0; /* not reached */
}
/*
* fluid_libsndio_audio_run
*/
void*
fluid_libsndio_audio_run2(void* d)
{
fluid_libsndio_audio_driver_t* dev = (fluid_libsndio_audio_driver_t*) d;
short* buffer = (short*) dev->buffer;
float* left = dev->buffers[0];
float* right = dev->buffers[1];
int buffer_size = dev->buffer_size;
int dither_index = 0;
FLUID_LOG(FLUID_DBG, "Audio thread running");
/* it's as simple as that: */
while (dev->cont)
{
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->buffers);
fluid_synth_dither_s16 (&dither_index, buffer_size, left, right,
buffer, 0, 2, buffer, 1, 2);
sio_write (dev->hdl, buffer, dev->buffer_byte_size);
}
FLUID_LOG(FLUID_DBG, "Audio thread finished");
pthread_exit(NULL);
return 0; /* not reached */
}
#endif /*#if LIBSNDIO_SUPPORT */