diff options
Diffstat (limited to 'flow/audioiojack.cpp')
-rw-r--r-- | flow/audioiojack.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/flow/audioiojack.cpp b/flow/audioiojack.cpp new file mode 100644 index 0000000..ca8110a --- /dev/null +++ b/flow/audioiojack.cpp @@ -0,0 +1,345 @@ +/* + Copyright (C) 2004 Matthias Kretz <kretz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_LIBJACK + +#include <jack/jack.h> +#include <jack/ringbuffer.h> + +#include "debug.h" +#include "audioio.h" +#include "audiosubsys.h" +#include "iomanager.h" +#include "dispatcher.h" + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#undef DEBUG_WAVEFORM +#ifdef DEBUG_WAVEFORM +#include <fstream> +#endif + +#include <cstdlib> +#include <cstring> + +namespace Arts { + +class AudioIOJack : public AudioIO, public TimeNotify { +private: +#ifdef DEBUG_WAVEFORM + std::ofstream plotfile; +#endif + char * processBuffer; + size_t buffersize; + +protected: + jack_client_t *jack; + jack_port_t *outleft, *outright; + jack_port_t *inleft, *inright; + jack_ringbuffer_t *olb, *orb, *ilb, *irb; + +public: + AudioIOJack(); + + void notifyTime(); + + void setParam( AudioParam p, int & val ); + int getParam(AudioParam param); + + static int jackCallback( jack_nframes_t, void * ); + + bool open(); + void close(); + int read(void *buffer, int size); + int write(void *buffer, int size); +}; + +REGISTER_AUDIO_IO(AudioIOJack,"jack","Jack Audio Connection Kit"); +} + +using namespace std; +using namespace Arts; + +AudioIOJack::AudioIOJack() + : +#ifdef DEBUG_WAVEFORM + plotfile( "/dev/shm/audioiojack.plot" ), +#endif + jack( 0 ) + , outleft( 0 ) + , outright( 0 ) + , inleft( 0 ) + , inright( 0 ) +{ + /* + * default parameters + */ + param( samplingRate ) = 44100; + paramStr( deviceName ) = "jack"; + param( fragmentSize ) = 512; + param( fragmentCount ) = 2; + param( channels ) = 2; + param( direction ) = 2; + param( format ) = 32; +} + +bool AudioIOJack::open() +{ + string& _error = paramStr( lastError ); + jack = jack_client_new( "artsd" ); + if( jack == 0 ) + { + _error = "Couldn't connect to jackd"; + return false; + } + + int& _sampleRate = param(samplingRate); + _sampleRate = jack_get_sample_rate( jack ); + int& _fragmentSize = param(fragmentSize); + int& _fragmentCount = param(fragmentCount); + + /* + * don't allow unreasonable large fragmentSize/Count combinations, + * because "real" hardware also doesn't + */ + + if(_fragmentSize > 1024*8) _fragmentSize = 1024*8; + + while(_fragmentSize * _fragmentCount > 1024*128) + _fragmentCount--; + + jack_set_process_callback( jack, Arts::AudioIOJack::jackCallback, this ); + + if( param( direction ) & directionWrite ) + { + outleft = jack_port_register( jack, "out_1", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + outright = jack_port_register( jack, "out_2", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + olb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + _fragmentSize * _fragmentCount ); + orb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + _fragmentSize * _fragmentCount ); + } + if( param( direction ) & directionRead ) + { + inleft = jack_port_register( jack, "in_1", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + inright = jack_port_register( jack, "in_2", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + ilb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + 1024 * 64 ); + irb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + 1024 * 64 ); + } + + if( jack_activate( jack ) ) + { + _error = "Activating as jack client failed."; + return false; + } + + const char **ports; + if( param( direction ) & directionRead ) + { + ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical + | JackPortIsOutput ); + if( ports == 0 ) + { + arts_warning( "Cannot find any capture ports to" + " connect to. You need to manually connect" + " the capture ports in jack" ); + } + else + { + if( ports[ 0 ] != 0 ) + jack_connect( jack, ports[ 0 ], + jack_port_name( inleft ) ); + if( ports[ 1 ] != 0 ) + jack_connect( jack, ports[ 1 ], + jack_port_name( inright ) ); + free( ports ); + } + } + if( param( direction ) & directionWrite ) + { + ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical + | JackPortIsInput ); + if( ports == 0 ) + { + arts_warning( "Cannot find any playback ports to" + " connect to. You need to manually connect" + " the playback ports in jack" ); + } + else + { + if( ports[ 0 ] != 0 ) + jack_connect( jack, jack_port_name( outleft ), + ports[ 0 ] ); + if( ports[ 1 ] != 0 ) + jack_connect( jack, jack_port_name( outright ), + ports[ 1 ] ); + free( ports ); + } + } + + // Install the timer + Dispatcher::the()->ioManager()->addTimer(10, this); + + return true; +} + +void AudioIOJack::close() +{ + jack_client_close( jack ); + Dispatcher::the()->ioManager()->removeTimer(this); +} + +int AudioIOJack::jackCallback( jack_nframes_t nframes, void * args ) +{ + AudioIOJack * that = static_cast<AudioIOJack*>( args ); + + that->buffersize = nframes * sizeof( jack_default_audio_sample_t ); + if( that->outleft ) + { + if( jack_ringbuffer_read_space( that->olb ) < that->buffersize ) + { + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->outleft, nframes ) ); + memset( that->processBuffer, 0, that->buffersize ); + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->outright, nframes ) ); + memset( that->processBuffer, 0, that->buffersize ); + } + else + { + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->outleft, nframes ) ); + jack_ringbuffer_read( that->olb, that->processBuffer, that->buffersize ); + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->outright, nframes ) ); + jack_ringbuffer_read( that->orb, that->processBuffer, that->buffersize ); + } + } + if( that->inleft ) + { + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->inleft, nframes ) ); + jack_ringbuffer_write( that->ilb, that->processBuffer, that->buffersize ); + that->processBuffer = static_cast<char *>( + jack_port_get_buffer( that->inright, nframes ) ); + jack_ringbuffer_write( that->irb, that->processBuffer, that->buffersize ); + } + return 0; +} + +void AudioIOJack::setParam( AudioParam p, int& val ) +{ + // don't change the format - jack only supports 32 bit float + if( p == format ) + return; + AudioIO::setParam( p, val ); +} + +int AudioIOJack::getParam(AudioParam p) +{ + switch(p) + { + case canRead: + return MIN( jack_ringbuffer_read_space( ilb ), jack_ringbuffer_read_space( irb ) ) * param( channels ); + case canWrite: + return MIN( jack_ringbuffer_write_space( olb ), jack_ringbuffer_write_space( orb ) ) * param( channels ); + default: + return AudioIO::getParam( p ); + } +} + +int AudioIOJack::read(void *buffer, int size) +{ + float * floatbuffer = static_cast<float *>( buffer ); + if( param( channels ) == 2 ) + { + float * end = ( float * )( static_cast<char *>( buffer ) + size ); + while( floatbuffer < end ) + { + jack_ringbuffer_read( ilb, ( char* )( floatbuffer++ ), sizeof( float ) ); +#ifdef DEBUG_WAVEFORM + plotfile << *( floatbuffer - 1 ) << "\n"; +#endif + jack_ringbuffer_read( irb, ( char* )( floatbuffer++ ), sizeof( float ) ); + } + } + else if( param( channels ) == 1 ) + { + jack_ringbuffer_read( ilb, ( char* )( floatbuffer ), size ); + } + return size; +} + +int AudioIOJack::write(void *buffer, int size) +{ + float * floatbuffer = static_cast<float *>( buffer ); + if( param( channels ) == 2 ) + { + float * end = ( float * )( static_cast<char *>( buffer ) + size ); + while( floatbuffer < end ) + { + jack_ringbuffer_write( olb, ( char* )( floatbuffer++ ), sizeof( float ) ); + jack_ringbuffer_write( orb, ( char* )( floatbuffer++ ), sizeof( float ) ); + } + } + else if( param( channels ) == 1 ) + { + jack_ringbuffer_write( olb, ( char* )( floatbuffer ), size ); + } + return size; +} + +void AudioIOJack::notifyTime() +{ + int& _direction = param(direction); + int& _fragmentSize = param(fragmentSize); + + for( ;; ) + { + int todo = 0; + if( ( _direction & directionRead ) && ( getParam( canRead ) >= _fragmentSize ) ) + todo |= AudioSubSystem::ioRead; + + if( ( _direction & directionWrite ) && ( getParam( canWrite ) >= _fragmentSize ) ) + todo |= AudioSubSystem::ioWrite; + + if( ! todo ) + return; + + AudioSubSystem::the()->handleIO( todo ); + } +} + +#endif |