summaryrefslogtreecommitdiffstats
path: root/flow/audioiojack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flow/audioiojack.cpp')
-rw-r--r--flow/audioiojack.cpp345
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