diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
commit | 42995d7bf396933ee60c5f89c354ea89cf13df0d (patch) | |
tree | cfdcea0ac57420e7baf570bfe435e107bb842541 /flow/audiosubsys.cc | |
download | arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.tar.gz arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.zip |
Copy of aRts for Trinity modifications
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/dependencies/arts@1070145 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'flow/audiosubsys.cc')
-rw-r--r-- | flow/audiosubsys.cc | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/flow/audiosubsys.cc b/flow/audiosubsys.cc new file mode 100644 index 0000000..a1238fc --- /dev/null +++ b/flow/audiosubsys.cc @@ -0,0 +1,645 @@ + /* + + Copyright (C) 2000 Stefan Westerfeld + stefan@space.twc.de + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/stat.h> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> // Needed on some systems. +#endif + +#ifdef HAVE_SYS_SOUNDCARD_H +#include <sys/soundcard.h> +#endif + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <iostream> +#include <algorithm> +#include <cstring> + +#include "debug.h" +#include "audiosubsys.h" +#include "audioio.h" + +#define DEFAULT_DEVICE_NAME "/dev/dsp" + +#undef DEBUG_WAVEFORM +#ifdef DEBUG_WAVEFORM +#include <fstream> +#endif + +using namespace std; +using namespace Arts; + +//--- automatic startup class + +static AudioSubSystemStart aStart; + +void AudioSubSystemStart::startup() +{ + _instance = new AudioSubSystem(); +} + +void AudioSubSystemStart::shutdown() +{ + delete _instance; +} + +//--- AudioSubSystemPrivate data + +class Arts::AudioSubSystemPrivate +{ +public: +#ifdef DEBUG_WAVEFORM + ofstream plotfile; +#endif + AudioIO *audioIO; + string audioIOName; + bool audioIOInit; + + unsigned int adjustDuplexOffsetIndex; + int adjustDuplexOffset[4]; + int adjustDuplexCount; +}; + +//--- AudioSubSystem implementation + +AudioSubSystem *AudioSubSystem::the() +{ + return aStart.the(); +} + +const char *AudioSubSystem::error() +{ + return _error.c_str(); +} + +AudioSubSystem::AudioSubSystem() +{ + d = new AudioSubSystemPrivate; +#ifdef DEBUG_WAVEFORM + d->plotfile.open( "/dev/shm/audiosubsystem.plot" ); +#endif + d->audioIO = 0; + d->audioIOInit = false; + + _running = false; + consumer = 0; + producer = 0; + fragment_buffer = 0; +} + +AudioSubSystem::~AudioSubSystem() +{ + delete d->audioIO; + delete d; +} + +bool AudioSubSystem::attachProducer(ASProducer *producer) +{ + assert(producer); + if(this->producer) return false; + + this->producer = producer; + return true; +} + +bool AudioSubSystem::attachConsumer(ASConsumer *consumer) +{ + assert(consumer); + if(this->consumer) return false; + + this->consumer = consumer; + return true; +} + +void AudioSubSystem::detachProducer() +{ + assert(producer); + producer = 0; + + if(_running) close(); +} + +void AudioSubSystem::detachConsumer() +{ + assert(consumer); + consumer = 0; + + if(_running) close(); +} + +/* initially creates default AudioIO */ +void AudioSubSystem::initAudioIO() +{ + /* auto detect */ + if(!d->audioIOInit) + { + string bestName; + int bestValue = 0; + + arts_debug("autodetecting driver: "); + for(int i = 0; i < AudioIO::queryAudioIOCount(); i++) + { + string name = AudioIO::queryAudioIOParamStr(i, AudioIO::name); + AudioIO *aio = AudioIO::createAudioIO(name.c_str()); + int value = aio->getParam(AudioIO::autoDetect); + + arts_debug(" - %s: %d", name.c_str(), value); + if(value > bestValue) + { + bestName = name; + bestValue = value; + } + delete aio; + } + if(bestValue) + { + arts_debug("... which means we'll default to %s", bestName.c_str()); + audioIO(bestName); + } + else + { + arts_debug("... nothing we could use as default found"); + } + } +} + +void AudioSubSystem::audioIO(const string& audioIO) +{ + if(d->audioIO) + delete d->audioIO; + + d->audioIOName = audioIO; + d->audioIO = AudioIO::createAudioIO(audioIO.c_str()); + d->audioIOInit = true; +} + +string AudioSubSystem::audioIO() +{ + initAudioIO(); + + return d->audioIOName; +} + +void AudioSubSystem::deviceName(const string& deviceName) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParamStr(AudioIO::deviceName, deviceName.c_str()); +} + +string AudioSubSystem::deviceName() +{ + initAudioIO(); + if(!d->audioIO) return ""; + + return d->audioIO->getParamStr(AudioIO::deviceName); +} + +void AudioSubSystem::fragmentCount(int fragmentCount) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParam(AudioIO::fragmentCount, fragmentCount); +} + +int AudioSubSystem::fragmentCount() +{ + initAudioIO(); + if(!d->audioIO) return 0; + + return d->audioIO->getParam(AudioIO::fragmentCount); +} + +void AudioSubSystem::fragmentSize(int fragmentSize) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParam(AudioIO::fragmentSize, fragmentSize); +} + +int AudioSubSystem::fragmentSize() +{ + initAudioIO(); + if(!d->audioIO) return 0; + + return d->audioIO->getParam(AudioIO::fragmentSize); +} + +void AudioSubSystem::samplingRate(int samplingRate) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParam(AudioIO::samplingRate, samplingRate); +} + +int AudioSubSystem::samplingRate() +{ + initAudioIO(); + if(!d->audioIO) return 0; + + return d->audioIO->getParam(AudioIO::samplingRate); +} + +void AudioSubSystem::channels(int channels) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParam(AudioIO::channels, channels); +} + +int AudioSubSystem::channels() +{ + initAudioIO(); + if(!d->audioIO) return 0; + + return d->audioIO->getParam(AudioIO::channels); +} + +void AudioSubSystem::format(int format) +{ + initAudioIO(); + if(!d->audioIO) return; + + d->audioIO->setParam(AudioIO::format, format); +} + +int AudioSubSystem::format() +{ + initAudioIO(); + if(!d->audioIO) return 0; + + return d->audioIO->getParam(AudioIO::format); +} + +int AudioSubSystem::bits() +{ + int _format = format(); + arts_assert(_format == 0 || _format == 8 || _format == 16 || _format == 17 || _format == 32); + return (_format & (32 | 16 | 8)); +} + +void AudioSubSystem::fullDuplex(bool fullDuplex) +{ + initAudioIO(); + if(!d->audioIO) return; + + int direction = fullDuplex?3:2; + d->audioIO->setParam(AudioIO::direction, direction); +} + +bool AudioSubSystem::fullDuplex() +{ + initAudioIO(); + if(!d->audioIO) return false; + + return d->audioIO->getParam(AudioIO::direction) == 3; +} + +int AudioSubSystem::selectReadFD() +{ + initAudioIO(); + if(!d->audioIO) return false; + + return d->audioIO->getParam(AudioIO::selectReadFD); +} + +int AudioSubSystem::selectWriteFD() +{ + initAudioIO(); + if(!d->audioIO) return false; + + return d->audioIO->getParam(AudioIO::selectWriteFD); +} + +bool AudioSubSystem::check() +{ + bool ok = open(); + + if(ok) close(); + return ok; +} + +bool AudioSubSystem::open() +{ + assert(!_running); + + initAudioIO(); + if(!d->audioIO) + { + if(d->audioIOName.empty()) + _error = "couldn't auto detect which audio I/O method to use"; + else + _error = "unable to select '"+d->audioIOName+"' style audio I/O"; + return false; + } + + if(d->audioIO->open()) + { + _running = true; + + _fragmentSize = d->audioIO->getParam(AudioIO::fragmentSize); + _fragmentCount = d->audioIO->getParam(AudioIO::fragmentCount); + + // allocate global buffer to do I/O + assert(fragment_buffer == 0); + fragment_buffer = new char[_fragmentSize]; + + d->adjustDuplexCount = 0; + return true; + } + else + { + _error = d->audioIO->getParamStr(AudioIO::lastError); + return false; + } +} + +void AudioSubSystem::close() +{ + assert(_running); + assert(d->audioIO); + + d->audioIO->close(); + + wBuffer.clear(); + rBuffer.clear(); + + _running = false; + if(fragment_buffer) + { + delete[] fragment_buffer; + fragment_buffer = 0; + } +} + +bool AudioSubSystem::running() +{ + return _running; +} + +void AudioSubSystem::handleIO(int type) +{ + assert(d->audioIO); + + if(type & ioRead) + { + int len = d->audioIO->read(fragment_buffer,_fragmentSize); + + if(len > 0) + { + if(rBuffer.size() < _fragmentSize * _fragmentCount * bits() / 8 * channels()) + { + rBuffer.write(len,fragment_buffer); +#ifdef DEBUG_WAVEFORM + float * end = (float *)(fragment_buffer + len); + float * floatbuffer = (float *)fragment_buffer; + while(floatbuffer < end) + { + d->plotfile << *floatbuffer++ << "\n"; + ++floatbuffer; + } +#endif + } + else + { + arts_debug( "AudioSubSystem: rBuffer is too full" ); + } + } + } + + if(type & ioWrite) + { + /* + * make sure that we have a fragment full of data at least + */ +Rewrite: + while(wBuffer.size() < _fragmentSize) + { + long wbsz = wBuffer.size(); + producer->needMore(); + + if(wbsz == wBuffer.size()) + { + /* + * Even though we asked the client to supply more + * data, he didn't give us more. So we can't supply + * output data as well. Bad luck. Might produce a + * buffer underrun - but we can't help here. + */ + arts_info("full duplex: no more data available (underrun)"); + return; + } + } + + /* + * look how much we really can write without blocking + */ + int space = d->audioIO->getParam(AudioIO::canWrite); + int can_write = min(space, _fragmentSize); + + if(can_write > 0) + { + /* + * ok, so write it (as we checked that our buffer has enough data + * to do so and the soundcardbuffer has enough data to handle this + * write, nothing can go wrong here) + */ + int rSize = wBuffer.read(can_write,fragment_buffer); + assert(rSize == can_write); + + int len = d->audioIO->write(fragment_buffer,can_write); + if(len != can_write) + arts_fatal("AudioSubSystem::handleIO: write failed\n" + "len = %d, can_write = %d, errno = %d (%s)\n\n" + "This might be a sound hardware/driver specific problem" + " (see aRts FAQ)",len,can_write,errno,strerror(errno)); + + if(fullDuplex()) + { + /* + * if we're running full duplex, here is a good place to check + * for full duplex drift + */ + d->adjustDuplexCount += can_write; + if(d->adjustDuplexCount > samplingRate()) + { + adjustDuplexBuffers(); + d->adjustDuplexCount = 0; + } + } + } + + // If we can write a fragment more, then do so right now: + if (space >= _fragmentSize*2) goto Rewrite; + } + + assert((type & ioExcept) == 0); +} + +void AudioSubSystem::read(void *buffer, int size) +{ + /* if not enough data can be read, produce some */ + while(rBuffer.size() < size) + adjustInputBuffer(1); + + /* finally, just take the data out of the input buffer */ + int rSize = rBuffer.read(size,buffer); + assert(rSize == size); +} + +void AudioSubSystem::write(void *buffer, int size) +{ + wBuffer.write(size,buffer); +} + +float AudioSubSystem::outputDelay() +{ + int fsize = _fragmentSize; + int fcount = _fragmentCount; + + if(fsize > 0 && fcount > 0) // not all AudioIO classes need to support this + { + double hardwareBuffer = fsize * fcount; + double freeOutputSpace = d->audioIO->getParam(AudioIO::canWrite); + double playSpeed = channels() * samplingRate() * (bits() / 8); + + return (hardwareBuffer - freeOutputSpace) / playSpeed; + } + else return 0.0; +} + +void AudioSubSystem::adjustDuplexBuffers() +{ + int fsize = _fragmentSize; + int fcount = _fragmentCount; + + if(fsize > 0 && fcount > 0) // not all AudioIO classes need to support this + { + int bound = 2; //max(fcount/2, 1); + int optimalOffset = fsize * (fcount + bound); + int minOffset = fsize * fcount; + int maxOffset = fsize * (fcount + 2 * bound); + + int canRead = d->audioIO->getParam(AudioIO::canRead); + if(canRead < 0) + { + arts_warning("AudioSubSystem::adjustDuplexBuffers: canRead < 0?"); + canRead = 0; + } + + int canWrite = d->audioIO->getParam(AudioIO::canWrite); + if(canWrite < 0) + { + arts_warning("AudioSubSystem::adjustDuplexBuffers: canWrite < 0?"); + canWrite = 0; + } + + int currentOffset = rBuffer.size() + wBuffer.size() + + canRead + max((fsize * fcount) - canWrite, 0); + + d->adjustDuplexOffset[d->adjustDuplexOffsetIndex++ & 3] = currentOffset; + if(d->adjustDuplexOffsetIndex <= 4) return; + + int avgOffset; + avgOffset = d->adjustDuplexOffset[0] + + d->adjustDuplexOffset[1] + + d->adjustDuplexOffset[2] + + d->adjustDuplexOffset[3]; + avgOffset /= 4; + + /* + printf("offset: %d avg %d min %d opt %d max %d\r", currentOffset, + avgOffset, minOffset, optimalOffset, maxOffset); + fflush(stdout); + */ + if(minOffset <= avgOffset && avgOffset <= maxOffset) + return; + + d->adjustDuplexOffsetIndex = 0; + int adjust = (optimalOffset - currentOffset) / _fragmentSize; + arts_debug("AudioSubSystem::adjustDuplexBuffers(%d)", adjust); + } +} + +void AudioSubSystem::adjustInputBuffer(int count) +{ + if(format() == 8) + { + memset( fragment_buffer, 0x80, _fragmentSize ); + } + else + { + memset( fragment_buffer, 0, _fragmentSize ); + } + + while(count > 0 && rBuffer.size() < _fragmentSize * _fragmentCount * 4) + { + rBuffer.write(_fragmentSize, fragment_buffer); +#ifdef DEBUG_WAVEFORM + float * end = (float *)(fragment_buffer + _fragmentSize); + float * floatbuffer = (float *)fragment_buffer; + while(floatbuffer < end) + { + d->plotfile << *floatbuffer++ << "\n"; + ++floatbuffer; + } +#endif + count--; + } + + while(count < 0 && rBuffer.size() >= _fragmentSize) + { + rBuffer.read(_fragmentSize, fragment_buffer); + count++; + } +} + +void AudioSubSystem::emergencyCleanup() +{ + if(producer || consumer) + { + fprintf(stderr, "AudioSubSystem::emergencyCleanup\n"); + + if(producer) + detachProducer(); + if(consumer) + detachConsumer(); + } +} |