From d6fd5da548d89f18642849f72295f6fbed440dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Andriot?= Date: Sun, 14 Sep 2014 20:27:49 +0200 Subject: [PATCH] Add openbsd sndio support --- CMakeLists.txt | 15 +++ config.h.cmake | 1 + flow/CMakeLists.txt | 4 +- flow/audioiosndio.cc | 310 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 flow/audioiosndio.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a717527..47215ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option( WITH_VORBIS "Enable Ogg/Vorbis support" ON ) option( WITH_MAD "Enable MAD mp3 decoder support" ON ) option( WITH_ESOUND "Enable ESOUND support" ${WITH_ALL_OPTIONS} ) option( WITH_JACK "Enable JACK support" ${WITH_ALL_OPTIONS} ) +option( WITH_SNDIO "Enable SNDIO support" OFF ) option( WITH_GCC_VISIBILITY "Enable fvisibility and fvisibility-inlines-hidden" ${WITH_ALL_OPTIONS} ) @@ -188,6 +189,20 @@ if( WITH_JACK ) endif( WITH_JACK ) +##### check for SNDIO ########################### + +set( HAVE_LIBSNDIO 0 ) +if( WITH_SNDIO ) + check_include_file( "sndio.h" HAVE_SNDIO_H ) + if( HAVE_SNDIO_H ) + set( HAVE_LIBSNDIO 1 ) + set( LIBSNDIO_LIBRARIES "sndio" ) + else( HAVE_SNDIO_H ) + tde_message_fatal( "SNDIO support is requested, but `sndio.h` was not found" ) + endif( HAVE_SNDIO_H ) +endif( WITH_SNDIO ) + + ##### check for glib/gthread modules ############ pkg_search_module( GLIB2 glib-2.0 ) diff --git a/config.h.cmake b/config.h.cmake index e653857..655821d 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -39,3 +39,4 @@ #define HAVE_IOCTL_INT_ULONGINT_DOTS 3 #cmakedefine HAVE_LIBJACK 1 +#cmakedefine HAVE_LIBSNDIO 1 diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index da41aee..4a8492d 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -57,7 +57,7 @@ set( ${target}_SRCS fft.c stereofftscope_impl.cc virtualports.cc bus.cc audiomanager_impl.cc synth_record_impl.cc resample.cc audioio.cc audioiooss.cc audioioalsa.cc audioioalsa9.cc - audioionull.cc audioiolibaudioio.cc audioioesd.cc + audioionull.cc audioiolibaudioio.cc audioioesd.cc audioiosndio.cc audioiojack.cc audioiosun.cc audioioaix.cc audioionas.cc cpuinfo.cc audioioossthreaded.cc audiotobytestream_impl.cc audioiosgi.cc audioiocsl.cc audioiomas.cc datahandle_impl.cc @@ -66,7 +66,7 @@ set( ${target}_SRCS tde_add_library( ${target} SHARED SOURCES ${${target}_SRCS} VERSION 1.0.0 - LINK artsgsl-static artsgslpp-static artsflow_idl-shared ${AUDIOFILE_LIBRARIES} ${LIBJACK_LIBRARIES} + LINK artsgsl-static artsgslpp-static artsflow_idl-shared ${AUDIOFILE_LIBRARIES} ${LIBJACK_LIBRARIES} ${LIBSNDIO_LIBRARIES} DESTINATION ${LIB_INSTALL_DIR} ) diff --git a/flow/audioiosndio.cc b/flow/audioiosndio.cc new file mode 100644 index 0000000..03efd1b --- /dev/null +++ b/flow/audioiosndio.cc @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2008 Jacob Meuser + * + * 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 +#endif + +#ifdef HAVE_LIBSNDIO + +#include +#include +#include + +#include +#include + +#include "debug.h" +#include "audioio.h" +#include "audiosubsys.h" +#include "iomanager.h" +#include "dispatcher.h" + +#include +#include + +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