You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/flow/audioiojack.cpp

346 lines
8.6 KiB

/*
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