2014-03-11 21:46:00 +01:00
/**
* @ author Gary P . SCAVONE
*
* @ copyright 2001 - 2013 Gary P . Scavone , all right reserved
*
* @ license like MIT ( see license file )
*/
# if defined(__UNIX_JACK__)
# include <unistd.h>
# include <limits.h>
# include <iostream>
# include <airtaudio/Interface.h>
# include <string.h>
2014-03-11 22:37:22 +01:00
airtaudio : : Api * airtaudio : : api : : Jack : : Create ( void ) {
return new airtaudio : : api : : Jack ( ) ;
}
2014-03-11 21:46:00 +01:00
// JACK is a low-latency audio server, originally written for the
// GNU/Linux operating system and now also ported to OS-X. It can
// connect a number of different applications to an audio device, as
// well as allowing them to share audio between themselves.
//
// When using JACK with RtAudio, "devices" refer to JACK clients that
// have ports connected to the server. The JACK server is typically
// started in a terminal as follows:
//
// .jackd -d alsa -d hw:0
//
// or through an interface program such as qjackctl. Many of the
// parameters normally set for a stream are fixed by the JACK server
// and can be specified when the JACK server is started. In
// particular,
//
// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
//
// specifies a sample rate of 44100 Hz, a buffer size of 512 sample
// frames, and number of buffers = 4. Once the server is running, it
// is not possible to override these values. If the values are not
// specified in the command-line, the JACK server uses default values.
//
// The JACK server does not have to be running when an instance of
// RtApiJack is created, though the function getDeviceCount() will
// report 0 devices found until JACK has been started. When no
// devices are available (i.e., the JACK server is not running), a
// stream cannot be opened.
# include <jack/jack.h>
# include <unistd.h>
# include <cstdio>
// A structure to hold various information related to the Jack API
// implementation.
struct JackHandle {
jack_client_t * client ;
jack_port_t * * ports [ 2 ] ;
std : : string deviceName [ 2 ] ;
bool xrun [ 2 ] ;
std : : condition_variable condition ;
int32_t drainCounter ; // Tracks callback counts when draining
bool internalDrain ; // Indicates if stop is initiated from callback or not.
JackHandle ( void ) :
client ( 0 ) ,
drainCounter ( 0 ) ,
internalDrain ( false ) {
ports [ 0 ] = 0 ;
ports [ 1 ] = 0 ;
xrun [ 0 ] = false ;
xrun [ 1 ] = false ;
}
} ;
static void jackSilentError ( const char * ) { } ;
airtaudio : : api : : Jack : : Jack ( void ) {
// Nothing to do here.
# if !defined(__RTAUDIO_DEBUG__)
// Turn off Jack's internal error reporting.
jack_set_error_function ( & jackSilentError ) ;
# endif
}
airtaudio : : api : : Jack : : ~ Jack ( void ) {
if ( m_stream . state ! = STREAM_CLOSED ) {
closeStream ( ) ;
}
}
uint32_t airtaudio : : api : : Jack : : getDeviceCount ( void ) {
// See if we can become a jack client.
jack_options_t options = ( jack_options_t ) ( JackNoStartServer ) ; //JackNullOption;
jack_status_t * status = NULL ;
jack_client_t * client = jack_client_open ( " RtApiJackCount " , options , status ) ;
if ( client = = NULL ) {
return 0 ;
}
const char * * ports ;
std : : string port , previousPort ;
uint32_t nChannels = 0 , nDevices = 0 ;
ports = jack_get_ports ( client , NULL , NULL , 0 ) ;
if ( ports ) {
// Parse the port names up to the first colon (:).
size_t iColon = 0 ;
do {
port = ( char * ) ports [ nChannels ] ;
iColon = port . find ( " : " ) ;
if ( iColon ! = std : : string : : npos ) {
port = port . substr ( 0 , iColon + 1 ) ;
if ( port ! = previousPort ) {
nDevices + + ;
previousPort = port ;
}
}
} while ( ports [ + + nChannels ] ) ;
free ( ports ) ;
}
jack_client_close ( client ) ;
return nDevices ;
}
airtaudio : : DeviceInfo airtaudio : : api : : Jack : : getDeviceInfo ( uint32_t _device )
{
airtaudio : : DeviceInfo info ;
info . probed = false ;
jack_options_t options = ( jack_options_t ) ( JackNoStartServer ) ; //JackNullOption
jack_status_t * status = NULL ;
jack_client_t * client = jack_client_open ( " RtApiJackInfo " , options , status ) ;
if ( client = = NULL ) {
m_errorText = " airtaudio::api::Jack::getDeviceInfo: Jack server not found or connection error! " ;
error ( airtaudio : : errorWarning ) ;
return info ;
}
const char * * ports ;
std : : string port , previousPort ;
uint32_t nPorts = 0 , nDevices = 0 ;
ports = jack_get_ports ( client , NULL , NULL , 0 ) ;
if ( ports ) {
// Parse the port names up to the first colon (:).
size_t iColon = 0 ;
do {
port = ( char * ) ports [ nPorts ] ;
iColon = port . find ( " : " ) ;
if ( iColon ! = std : : string : : npos ) {
port = port . substr ( 0 , iColon ) ;
if ( port ! = previousPort ) {
if ( nDevices = = _device ) {
info . name = port ;
}
nDevices + + ;
previousPort = port ;
}
}
} while ( ports [ + + nPorts ] ) ;
free ( ports ) ;
}
if ( _device > = nDevices ) {
jack_client_close ( client ) ;
m_errorText = " airtaudio::api::Jack::getDeviceInfo: device ID is invalid! " ;
error ( airtaudio : : errorInvalidUse ) ;
return info ;
}
// Get the current jack server sample rate.
info . sampleRates . clear ( ) ;
info . sampleRates . push_back ( jack_get_sample_rate ( client ) ) ;
// Count the available ports containing the client name as device
// channels. Jack "input ports" equal RtAudio output channels.
uint32_t nChannels = 0 ;
ports = jack_get_ports ( client , info . name . c_str ( ) , NULL , JackPortIsInput ) ;
if ( ports ) {
while ( ports [ nChannels ] ) {
nChannels + + ;
}
free ( ports ) ;
info . outputChannels = nChannels ;
}
// Jack "output ports" equal RtAudio input channels.
nChannels = 0 ;
ports = jack_get_ports ( client , info . name . c_str ( ) , NULL , JackPortIsOutput ) ;
if ( ports ) {
while ( ports [ nChannels ] ) {
nChannels + + ;
}
free ( ports ) ;
info . inputChannels = nChannels ;
}
if ( info . outputChannels = = 0 & & info . inputChannels = = 0 ) {
jack_client_close ( client ) ;
m_errorText = " airtaudio::api::Jack::getDeviceInfo: error determining Jack input/output channels! " ;
error ( airtaudio : : errorWarning ) ;
return info ;
}
// If device opens for both playback and capture, we determine the channels.
if ( info . outputChannels > 0 & & info . inputChannels > 0 ) {
info . duplexChannels = ( info . outputChannels > info . inputChannels ) ? info . inputChannels : info . outputChannels ;
}
// Jack always uses 32-bit floats.
info . nativeFormats = airtaudio : : FLOAT32 ;
// Jack doesn't provide default devices so we'll use the first available one.
if ( _device = = 0
& & info . outputChannels > 0 ) {
info . isDefaultOutput = true ;
}
if ( _device = = 0
& & info . inputChannels > 0 ) {
info . isDefaultInput = true ;
}
jack_client_close ( client ) ;
info . probed = true ;
return info ;
}
static int32_t jackCallbackHandler ( jack_nframes_t _nframes , void * _infoPointer ) {
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _infoPointer ;
airtaudio : : api : : Jack * object = ( airtaudio : : api : : Jack * ) info - > object ;
if ( object - > callbackEvent ( ( uint64_t ) _nframes ) = = false ) {
return 1 ;
}
return 0 ;
}
// This function will be called by a spawned thread when the Jack
// server signals that it is shutting down. It is necessary to handle
// it this way because the jackShutdown() function must return before
// the jack_deactivate() function (in closeStream()) will return.
static void * jackCloseStream ( void * _ptr ) {
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _ptr ;
airtaudio : : api : : Jack * object = ( airtaudio : : api : : Jack * ) info - > object ;
object - > closeStream ( ) ;
pthread_exit ( NULL ) ;
}
static void jackShutdown ( void * _infoPointer ) {
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _infoPointer ;
airtaudio : : api : : Jack * object = ( airtaudio : : api : : Jack * ) info - > object ;
// Check current stream state. If stopped, then we'll assume this
// was called as a result of a call to airtaudio::api::Jack::stopStream (the
// deactivation of a client handle causes this function to be called).
// If not, we'll assume the Jack server is shutting down or some
// other problem occurred and we should close the stream.
if ( object - > isStreamRunning ( ) = = false ) {
return ;
}
pthread_t threadId ;
pthread_create ( & threadId , NULL , jackCloseStream , info ) ;
std : : cerr < < " \n RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!! \n " < < std : : endl ;
}
static int32_t jackXrun ( void * _infoPointer ) {
JackHandle * handle = ( JackHandle * ) _infoPointer ;
if ( handle - > ports [ 0 ] ) {
handle - > xrun [ 0 ] = true ;
}
if ( handle - > ports [ 1 ] ) {
handle - > xrun [ 1 ] = true ;
}
return 0 ;
}
bool airtaudio : : api : : Jack : : probeDeviceOpen ( uint32_t device ,
airtaudio : : api : : StreamMode mode ,
uint32_t channels ,
uint32_t firstChannel ,
uint32_t sampleRate ,
airtaudio : : format format ,
uint32_t * bufferSize ,
airtaudio : : StreamOptions * options ) {
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
// Look for jack server and try to become a client (only do once per stream).
jack_client_t * client = 0 ;
if ( mode = = OUTPUT | | ( mode = = INPUT & & m_stream . mode ! = OUTPUT ) ) {
jack_options_t jackoptions = ( jack_options_t ) ( JackNoStartServer ) ; //JackNullOption;
jack_status_t * status = NULL ;
if ( options & & ! options - > streamName . empty ( ) ) {
client = jack_client_open ( options - > streamName . c_str ( ) , jackoptions , status ) ;
} else {
client = jack_client_open ( " RtApiJack " , jackoptions , status ) ;
}
if ( client = = 0 ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: Jack server not found or connection error! " ;
error ( airtaudio : : errorWarning ) ;
return FAILURE ;
}
}
else {
// The handle must have been created on an earlier pass.
client = handle - > client ;
}
const char * * ports ;
std : : string port , previousPort , deviceName ;
uint32_t nPorts = 0 , nDevices = 0 ;
ports = jack_get_ports ( client , NULL , NULL , 0 ) ;
if ( ports ) {
// Parse the port names up to the first colon (:).
size_t iColon = 0 ;
do {
port = ( char * ) ports [ nPorts ] ;
iColon = port . find ( " : " ) ;
if ( iColon ! = std : : string : : npos ) {
port = port . substr ( 0 , iColon ) ;
if ( port ! = previousPort ) {
if ( nDevices = = device ) deviceName = port ;
nDevices + + ;
previousPort = port ;
}
}
} while ( ports [ + + nPorts ] ) ;
free ( ports ) ;
}
if ( device > = nDevices ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: device ID is invalid! " ;
return FAILURE ;
}
// Count the available ports containing the client name as device
// channels. Jack "input ports" equal RtAudio output channels.
uint32_t nChannels = 0 ;
uint64_t flag = JackPortIsInput ;
if ( mode = = INPUT ) flag = JackPortIsOutput ;
ports = jack_get_ports ( client , deviceName . c_str ( ) , NULL , flag ) ;
if ( ports ) {
while ( ports [ nChannels ] ) nChannels + + ;
free ( ports ) ;
}
// Compare the jack ports for specified client to the requested number of channels.
if ( nChannels < ( channels + firstChannel ) ) {
m_errorStream < < " airtaudio::api::Jack::probeDeviceOpen: requested number of channels ( " < < channels < < " ) + offset ( " < < firstChannel < < " ) not found for specified device ( " < < device < < " : " < < deviceName < < " ). " ;
m_errorText = m_errorStream . str ( ) ;
return FAILURE ;
}
// Check the jack server sample rate.
uint32_t jackRate = jack_get_sample_rate ( client ) ;
if ( sampleRate ! = jackRate ) {
jack_client_close ( client ) ;
m_errorStream < < " airtaudio::api::Jack::probeDeviceOpen: the requested sample rate ( " < < sampleRate < < " ) is different than the JACK server rate ( " < < jackRate < < " ). " ;
m_errorText = m_errorStream . str ( ) ;
return FAILURE ;
}
m_stream . sampleRate = jackRate ;
// Get the latency of the JACK port.
ports = jack_get_ports ( client , deviceName . c_str ( ) , NULL , flag ) ;
if ( ports [ firstChannel ] ) {
// Added by Ge Wang
jack_latency_callback_mode_t cbmode = ( mode = = INPUT ? JackCaptureLatency : JackPlaybackLatency ) ;
// the range (usually the min and max are equal)
jack_latency_range_t latrange ; latrange . min = latrange . max = 0 ;
// get the latency range
jack_port_get_latency_range ( jack_port_by_name ( client , ports [ firstChannel ] ) , cbmode , & latrange ) ;
// be optimistic, use the min!
m_stream . latency [ mode ] = latrange . min ;
//m_stream.latency[mode] = jack_port_get_latency(jack_port_by_name(client, ports[ firstChannel ]));
}
free ( ports ) ;
// The jack server always uses 32-bit floating-point data.
m_stream . deviceFormat [ mode ] = FLOAT32 ;
m_stream . userFormat = format ;
if ( options & & options - > flags & NONINTERLEAVED ) m_stream . userInterleaved = false ;
else m_stream . userInterleaved = true ;
// Jack always uses non-interleaved buffers.
m_stream . deviceInterleaved [ mode ] = false ;
// Jack always provides host byte-ordered data.
m_stream . doByteSwap [ mode ] = false ;
// Get the buffer size. The buffer size and number of buffers
// (periods) is set when the jack server is started.
m_stream . bufferSize = ( int ) jack_get_buffer_size ( client ) ;
* bufferSize = m_stream . bufferSize ;
m_stream . nDeviceChannels [ mode ] = channels ;
m_stream . nUserChannels [ mode ] = channels ;
// Set flags for buffer conversion.
m_stream . doConvertBuffer [ mode ] = false ;
if ( m_stream . userFormat ! = m_stream . deviceFormat [ mode ] )
m_stream . doConvertBuffer [ mode ] = true ;
if ( m_stream . userInterleaved ! = m_stream . deviceInterleaved [ mode ] & &
m_stream . nUserChannels [ mode ] > 1 )
m_stream . doConvertBuffer [ mode ] = true ;
// Allocate our JackHandle structure for the stream.
if ( handle = = 0 ) {
try {
handle = new JackHandle ;
}
catch ( std : : bad_alloc & ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: error allocating JackHandle memory. " ;
goto error ;
}
m_stream . apiHandle = ( void * ) handle ;
handle - > client = client ;
}
handle - > deviceName [ mode ] = deviceName ;
// Allocate necessary internal buffers.
uint64_t bufferBytes ;
bufferBytes = m_stream . nUserChannels [ mode ] * * bufferSize * formatBytes ( m_stream . userFormat ) ;
m_stream . userBuffer [ mode ] = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_stream . userBuffer [ mode ] = = NULL ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: error allocating user buffer memory. " ;
goto error ;
}
if ( m_stream . doConvertBuffer [ mode ] ) {
bool makeBuffer = true ;
if ( mode = = OUTPUT )
bufferBytes = m_stream . nDeviceChannels [ 0 ] * formatBytes ( m_stream . deviceFormat [ 0 ] ) ;
else { // mode == INPUT
bufferBytes = m_stream . nDeviceChannels [ 1 ] * formatBytes ( m_stream . deviceFormat [ 1 ] ) ;
if ( m_stream . mode = = OUTPUT & & m_stream . deviceBuffer ) {
uint64_t bytesOut = m_stream . nDeviceChannels [ 0 ] * formatBytes ( m_stream . deviceFormat [ 0 ] ) ;
if ( bufferBytes < bytesOut ) makeBuffer = false ;
}
}
if ( makeBuffer ) {
bufferBytes * = * bufferSize ;
if ( m_stream . deviceBuffer ) free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_stream . deviceBuffer = = NULL ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: error allocating device buffer memory. " ;
goto error ;
}
}
}
// Allocate memory for the Jack ports (channels) identifiers.
handle - > ports [ mode ] = ( jack_port_t * * ) malloc ( sizeof ( jack_port_t * ) * channels ) ;
if ( handle - > ports [ mode ] = = NULL ) {
m_errorText = " airtaudio::api::Jack::probeDeviceOpen: error allocating port memory. " ;
goto error ;
}
m_stream . device [ mode ] = device ;
m_stream . channelOffset [ mode ] = firstChannel ;
m_stream . state = STREAM_STOPPED ;
m_stream . callbackInfo . object = ( void * ) this ;
if ( m_stream . mode = = OUTPUT & & mode = = INPUT )
// We had already set up the stream for output.
m_stream . mode = DUPLEX ;
else {
m_stream . mode = mode ;
jack_set_process_callback ( handle - > client , jackCallbackHandler , ( void * ) & m_stream . callbackInfo ) ;
jack_set_xrun_callback ( handle - > client , jackXrun , ( void * ) & handle ) ;
jack_on_shutdown ( handle - > client , jackShutdown , ( void * ) & m_stream . callbackInfo ) ;
}
// Register our ports.
char label [ 64 ] ;
if ( mode = = OUTPUT ) {
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 0 ] ; i + + ) {
snprintf ( label , 64 , " outport %d " , i ) ;
handle - > ports [ 0 ] [ i ] = jack_port_register ( handle - > client ,
( const char * ) label ,
JACK_DEFAULT_AUDIO_TYPE ,
JackPortIsOutput ,
0 ) ;
}
} else {
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 1 ] ; i + + ) {
snprintf ( label , 64 , " inport %d " , i ) ;
handle - > ports [ 1 ] [ i ] = jack_port_register ( handle - > client ,
( const char * ) label ,
JACK_DEFAULT_AUDIO_TYPE ,
JackPortIsInput ,
0 ) ;
}
}
// Setup the buffer conversion information structure. We don't use
// buffers to do channel offsets, so we override that parameter
// here.
if ( m_stream . doConvertBuffer [ mode ] ) {
setConvertInfo ( mode , 0 ) ;
}
return SUCCESS ;
error :
if ( handle ) {
jack_client_close ( handle - > client ) ;
if ( handle - > ports [ 0 ] ) {
free ( handle - > ports [ 0 ] ) ;
}
if ( handle - > ports [ 1 ] ) {
free ( handle - > ports [ 1 ] ) ;
}
delete handle ;
m_stream . apiHandle = 0 ;
}
for ( int32_t iii = 0 ; iii < 2 ; + + iii ) {
if ( m_stream . userBuffer [ iii ] ) {
free ( m_stream . userBuffer [ iii ] ) ;
m_stream . userBuffer [ iii ] = 0 ;
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = 0 ;
}
return FAILURE ;
}
void airtaudio : : api : : Jack : : closeStream ( void )
{
if ( m_stream . state = = STREAM_CLOSED ) {
m_errorText = " airtaudio::api::Jack::closeStream(): no open stream to close! " ;
error ( airtaudio : : errorWarning ) ;
return ;
}
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
if ( handle ) {
if ( m_stream . state = = STREAM_RUNNING )
jack_deactivate ( handle - > client ) ;
jack_client_close ( handle - > client ) ;
}
if ( handle ) {
if ( handle - > ports [ 0 ] ) {
free ( handle - > ports [ 0 ] ) ;
}
if ( handle - > ports [ 1 ] ) {
free ( handle - > ports [ 1 ] ) ;
}
delete handle ;
m_stream . apiHandle = 0 ;
}
for ( int32_t i = 0 ; i < 2 ; i + + ) {
if ( m_stream . userBuffer [ i ] ) {
free ( m_stream . userBuffer [ i ] ) ;
m_stream . userBuffer [ i ] = 0 ;
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = 0 ;
}
m_stream . mode = UNINITIALIZED ;
m_stream . state = STREAM_CLOSED ;
}
void airtaudio : : api : : Jack : : startStream ( void )
{
verifyStream ( ) ;
if ( m_stream . state = = STREAM_RUNNING ) {
m_errorText = " airtaudio::api::Jack::startStream(): the stream is already running! " ;
error ( airtaudio : : errorWarning ) ;
return ;
}
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
int32_t result = jack_activate ( handle - > client ) ;
if ( result ) {
m_errorText = " airtaudio::api::Jack::startStream(): unable to activate JACK client! " ;
goto unlock ;
}
const char * * ports ;
// Get the list of available ports.
if ( m_stream . mode = = OUTPUT | | m_stream . mode = = DUPLEX ) {
result = 1 ;
ports = jack_get_ports ( handle - > client , handle - > deviceName [ 0 ] . c_str ( ) , NULL , JackPortIsInput ) ;
if ( ports = = NULL ) {
m_errorText = " airtaudio::api::Jack::startStream(): error determining available JACK input ports! " ;
goto unlock ;
}
// Now make the port connections. Since RtAudio wasn't designed to
// allow the user to select particular channels of a device, we'll
// just open the first "nChannels" ports with offset.
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 0 ] ; i + + ) {
result = 1 ;
if ( ports [ m_stream . channelOffset [ 0 ] + i ] )
result = jack_connect ( handle - > client , jack_port_name ( handle - > ports [ 0 ] [ i ] ) , ports [ m_stream . channelOffset [ 0 ] + i ] ) ;
if ( result ) {
free ( ports ) ;
m_errorText = " airtaudio::api::Jack::startStream(): error connecting output ports! " ;
goto unlock ;
}
}
free ( ports ) ;
}
if ( m_stream . mode = = INPUT | | m_stream . mode = = DUPLEX ) {
result = 1 ;
ports = jack_get_ports ( handle - > client , handle - > deviceName [ 1 ] . c_str ( ) , NULL , JackPortIsOutput ) ;
if ( ports = = NULL ) {
m_errorText = " airtaudio::api::Jack::startStream(): error determining available JACK output ports! " ;
goto unlock ;
}
// Now make the port connections. See note above.
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 1 ] ; i + + ) {
result = 1 ;
if ( ports [ m_stream . channelOffset [ 1 ] + i ] )
result = jack_connect ( handle - > client , ports [ m_stream . channelOffset [ 1 ] + i ] , jack_port_name ( handle - > ports [ 1 ] [ i ] ) ) ;
if ( result ) {
free ( ports ) ;
m_errorText = " airtaudio::api::Jack::startStream(): error connecting input ports! " ;
goto unlock ;
}
}
free ( ports ) ;
}
handle - > drainCounter = 0 ;
handle - > internalDrain = false ;
m_stream . state = STREAM_RUNNING ;
unlock :
if ( result = = 0 ) return ;
error ( airtaudio : : errorSystemError ) ;
}
void airtaudio : : api : : Jack : : stopStream ( void ) {
verifyStream ( ) ;
if ( m_stream . state = = STREAM_STOPPED ) {
m_errorText = " airtaudio::api::Jack::stopStream(): the stream is already stopped! " ;
error ( airtaudio : : errorWarning ) ;
return ;
}
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
if ( m_stream . mode = = OUTPUT
| | m_stream . mode = = DUPLEX ) {
if ( handle - > drainCounter = = 0 ) {
handle - > drainCounter = 2 ;
std : : unique_lock < std : : mutex > lck ( m_stream . mutex ) ;
handle - > condition . wait ( lck ) ;
}
}
jack_deactivate ( handle - > client ) ;
m_stream . state = STREAM_STOPPED ;
}
void airtaudio : : api : : Jack : : abortStream ( void )
{
verifyStream ( ) ;
if ( m_stream . state = = STREAM_STOPPED ) {
m_errorText = " airtaudio::api::Jack::abortStream(): the stream is already stopped! " ;
error ( airtaudio : : errorWarning ) ;
return ;
}
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
handle - > drainCounter = 2 ;
stopStream ( ) ;
}
// This function will be called by a spawned thread when the user
// callback function signals that the stream should be stopped or
// aborted. It is necessary to handle it this way because the
// callbackEvent() function must return before the jack_deactivate()
// function will return.
static void jackStopStream ( void * _ptr ) {
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _ptr ;
airtaudio : : api : : Jack * object = ( airtaudio : : api : : Jack * ) info - > object ;
object - > stopStream ( ) ;
}
bool airtaudio : : api : : Jack : : callbackEvent ( uint64_t nframes )
{
if ( m_stream . state = = STREAM_STOPPED | | m_stream . state = = STREAM_STOPPING ) return SUCCESS ;
if ( m_stream . state = = STREAM_CLOSED ) {
m_errorText = " RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen! " ;
error ( airtaudio : : errorWarning ) ;
return FAILURE ;
}
if ( m_stream . bufferSize ! = nframes ) {
m_errorText = " RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process! " ;
error ( airtaudio : : errorWarning ) ;
return FAILURE ;
}
CallbackInfo * info = ( CallbackInfo * ) & m_stream . callbackInfo ;
JackHandle * handle = ( JackHandle * ) m_stream . apiHandle ;
// Check if we were draining the stream and signal is finished.
if ( handle - > drainCounter > 3 ) {
m_stream . state = STREAM_STOPPING ;
if ( handle - > internalDrain = = true ) {
new std : : thread ( jackStopStream , info ) ;
} else {
handle - > condition . notify_one ( ) ;
}
return SUCCESS ;
}
// Invoke user callback first, to get fresh output data.
if ( handle - > drainCounter = = 0 ) {
airtaudio : : AirTAudioCallback callback = ( airtaudio : : AirTAudioCallback ) info - > callback ;
double streamTime = getStreamTime ( ) ;
airtaudio : : streamStatus status = 0 ;
if ( m_stream . mode ! = INPUT & & handle - > xrun [ 0 ] = = true ) {
status | = OUTPUT_UNDERFLOW ;
handle - > xrun [ 0 ] = false ;
}
if ( m_stream . mode ! = OUTPUT & & handle - > xrun [ 1 ] = = true ) {
status | = INPUT_OVERFLOW ;
handle - > xrun [ 1 ] = false ;
}
int32_t cbReturnValue = callback ( m_stream . userBuffer [ 0 ] , m_stream . userBuffer [ 1 ] ,
m_stream . bufferSize , streamTime , status , info - > userData ) ;
if ( cbReturnValue = = 2 ) {
m_stream . state = STREAM_STOPPING ;
handle - > drainCounter = 2 ;
new std : : thread ( jackStopStream , info ) ;
return SUCCESS ;
}
else if ( cbReturnValue = = 1 ) {
handle - > drainCounter = 1 ;
handle - > internalDrain = true ;
}
}
jack_default_audio_sample_t * jackbuffer ;
uint64_t bufferBytes = nframes * sizeof ( jack_default_audio_sample_t ) ;
if ( m_stream . mode = = OUTPUT | | m_stream . mode = = DUPLEX ) {
if ( handle - > drainCounter > 1 ) { // write zeros to the output stream
for ( uint32_t i = 0 ; i < m_stream . nDeviceChannels [ 0 ] ; i + + ) {
jackbuffer = ( jack_default_audio_sample_t * ) jack_port_get_buffer ( handle - > ports [ 0 ] [ i ] , ( jack_nframes_t ) nframes ) ;
memset ( jackbuffer , 0 , bufferBytes ) ;
}
}
else if ( m_stream . doConvertBuffer [ 0 ] ) {
convertBuffer ( m_stream . deviceBuffer , m_stream . userBuffer [ 0 ] , m_stream . convertInfo [ 0 ] ) ;
for ( uint32_t i = 0 ; i < m_stream . nDeviceChannels [ 0 ] ; i + + ) {
jackbuffer = ( jack_default_audio_sample_t * ) jack_port_get_buffer ( handle - > ports [ 0 ] [ i ] , ( jack_nframes_t ) nframes ) ;
memcpy ( jackbuffer , & m_stream . deviceBuffer [ i * bufferBytes ] , bufferBytes ) ;
}
}
else { // no buffer conversion
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 0 ] ; i + + ) {
jackbuffer = ( jack_default_audio_sample_t * ) jack_port_get_buffer ( handle - > ports [ 0 ] [ i ] , ( jack_nframes_t ) nframes ) ;
memcpy ( jackbuffer , & m_stream . userBuffer [ 0 ] [ i * bufferBytes ] , bufferBytes ) ;
}
}
if ( handle - > drainCounter ) {
handle - > drainCounter + + ;
goto unlock ;
}
}
if ( m_stream . mode = = INPUT
| | m_stream . mode = = DUPLEX ) {
if ( m_stream . doConvertBuffer [ 1 ] ) {
for ( uint32_t i = 0 ; i < m_stream . nDeviceChannels [ 1 ] ; i + + ) {
jackbuffer = ( jack_default_audio_sample_t * ) jack_port_get_buffer ( handle - > ports [ 1 ] [ i ] , ( jack_nframes_t ) nframes ) ;
memcpy ( & m_stream . deviceBuffer [ i * bufferBytes ] , jackbuffer , bufferBytes ) ;
}
convertBuffer ( m_stream . userBuffer [ 1 ] , m_stream . deviceBuffer , m_stream . convertInfo [ 1 ] ) ;
} else {
// no buffer conversion
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 1 ] ; i + + ) {
jackbuffer = ( jack_default_audio_sample_t * ) jack_port_get_buffer ( handle - > ports [ 1 ] [ i ] , ( jack_nframes_t ) nframes ) ;
memcpy ( & m_stream . userBuffer [ 1 ] [ i * bufferBytes ] , jackbuffer , bufferBytes ) ;
}
}
}
unlock :
airtaudio : : Api : : tickStreamTime ( ) ;
return SUCCESS ;
}
//******************** End of __UNIX_JACK__ *********************//
# endif