You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/flow/audioiosndio.cpp

311 lines
6.0 KiB

/*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_LIBSNDIO
#include <sys/types.h>
#include <errno.h>
#include <math.h>
#include <sndio.h>
#include <poll.h>
#include "debug.h"
#include "audioio.h"
#include "audiosubsys.h"
#include "iomanager.h"
#include "dispatcher.h"
#include <cstdlib>
#include <cstring>
int bps, chans;
long long realpos, playpos, recpos;
void movecb(void *addr, int delta)
{
realpos += delta * (int)(bps * chans);
}
namespace Arts {
class AudioIOSNDIO : public AudioIO, public TimeNotify {
protected:
struct sio_hdl *hdl;
struct sio_par par;
int bufsz;
int bufused;
int duplex;
public:
AudioIOSNDIO();
void notifyTime();
void setParam(AudioParam param, int& value);
int getParam(AudioParam param);
bool open();
void close();
int read(void *buffer, int size);
int write(void *buffer, int size);
};
REGISTER_AUDIO_IO(AudioIOSNDIO,"sndio","libsndio");
}
using namespace std;
using namespace Arts;
AudioIOSNDIO::AudioIOSNDIO()
{
/* default parameters */
param(samplingRate) = 48000;
paramStr(deviceName) = "";
param(fragmentSize) = 4096;
param(fragmentCount) = 4;
param(format) = 16;
param(channels) = 2;
param(direction) = directionWrite;
}
bool AudioIOSNDIO::open()
{
string& _error = paramStr(lastError);
string& _deviceName = paramStr(deviceName);
int& _channels = param(channels);
int& _fragmentSize = param(fragmentSize);
int& _fragmentCount = param(fragmentCount);
int& _samplingRate = param(samplingRate);
int& _format = param(format);
struct sio_par testpar;
char dev[PATH_MAX];
int mode, bpf;
duplex = 0;
if (param(direction) == (directionRead | directionWrite)) {
mode = SIO_PLAY | SIO_REC;
duplex = 1;
} else if (param(direction) == directionWrite) {
mode = SIO_PLAY;
} else {
_error = "invalid direction";
return false;
}
strlcpy(dev, _deviceName.c_str(), PATH_MAX);
if (strcmp(dev, "") == 0)
hdl = sio_open(NULL, mode, 0);
else
hdl = sio_open(dev, mode, 0);
if (hdl == NULL) {
_error = "device ";
if (strcmp(_deviceName.c_str(), "") == 0)
_error += "(default sndio device)";
else
_error += _deviceName.c_str();
_error += " can't be opened";
return false;
}
sio_initpar(&par);
if (_format == 8) {
par.bits = 8;
par.sig = 0;
} else {
par.bits = 16;
par.sig = 1;
par.le = 1;
}
if (duplex)
par.pchan = par.rchan = _channels;
else
par.pchan = _channels;
par.rate = _samplingRate;
/* limit the buffer size for hardware constraints */
if (_fragmentSize > 1024*16)
_fragmentSize = 1024*16;
while (_fragmentSize * _fragmentCount > 1024*32)
_fragmentCount--;
bpf = ((par.bits / NBBY) * par.pchan);
par.round = _fragmentSize / bpf;
par.appbufsz = _fragmentSize * _fragmentCount / bpf;
testpar = par;
char details[128];
snprintf(details, 128, " rate=%d pchan=%d bits=%d le=%d sig=%d sz=%d",
par.rate, par.pchan, par.bits, par.le, par.sig, par.appbufsz);
if (!sio_setpar(hdl, &par)) {
_error = "sio_setpar failed:";
_error += details;
close();
return false;
}
if (!sio_getpar(hdl, &par)) {
_error = "sio_getpar failed";
close();
return false;
}
if (testpar.bits != par.bits ||
testpar.sig != par.sig ||
testpar.le != par.le ||
testpar.pchan != par.pchan ||
fabs((testpar.rate - par.rate)/testpar.rate) > 0.05) {
_error = "returned params do not match requested params";
close();
return false;
}
bpf = par.pchan * par.bps;
bufsz = par.bufsz * bpf;
::bps = par.bps;
::chans = par.pchan;
::realpos = ::playpos = ::recpos = 0;
sio_onmove(hdl, ::movecb, NULL);
if (!sio_start(hdl)) {
_error = "sio_start failed";
close();
return false;
}
/* use a timer instead of select() */
Dispatcher::the()->ioManager()->addTimer(10, this);
return true;
}
void AudioIOSNDIO::close()
{
if (hdl != NULL) {
sio_close(hdl);
hdl = NULL;
}
}
void AudioIOSNDIO::setParam(AudioParam p, int& value)
{
param(p) = value;
}
int AudioIOSNDIO::getParam(AudioParam p)
{
struct pollfd gpfd;
int n, events;
/* update ::realpos */
if ((p == canRead || p == canWrite) && hdl != NULL) {
events = POLLOUT;
if (duplex)
events |= POLLIN;
n = sio_pollfd(hdl, &gpfd, events);
while (poll(&gpfd, n, 0) < 0 && errno == EINTR)
;
sio_revents(hdl, &gpfd);
}
switch(p) {
case canRead:
bufused = (::realpos - ::recpos < 0) ? 0 : ::realpos - ::recpos;
return bufused;
break;
case canWrite:
bufused = (::realpos < 0) ? ::playpos : ::playpos - ::realpos;
return bufsz - bufused;
break;
case autoDetect:
return 15;
break;
default:
return param(p);
break;
}
}
int AudioIOSNDIO::read(void *buffer, int size)
{
arts_assert(hdl != NULL);
size_t result;
result = sio_read(hdl, buffer, size);
::recpos += result;
return (int)result;
}
int AudioIOSNDIO::write(void *buffer, int size)
{
arts_assert(hdl != NULL);
size_t result;
result = sio_write(hdl, buffer, size);
::playpos += result;
return (int)result;
}
void AudioIOSNDIO::notifyTime()
{
int& _direction = param(direction);
for (;;) {
int todo = 0;
if ((_direction & directionRead) && (getParam(canRead) > 0))
todo |= AudioSubSystem::ioRead;
if ((_direction & directionWrite) && (getParam(canWrite) > 0))
todo |= AudioSubSystem::ioWrite;
if (todo == 0)
return;
AudioSubSystem::the()->handleIO(todo);
}
}
#endif