parent
c73ca85a76
commit
d6fd5da548
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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
|
Loading…
Reference in new issue