Skip to content

Commit 4f7a17a

Browse files
committed
Add getDeviceHandle() to expose client-specific structs.
In cases where the caller really needs access to the API-specific handle (e.g. jack_client_t*), then it can be returned in a derived struct. Since this means that API headers must be included before RtAudio.h, it is not enabled by default; the caller must include them him/herself and define RTAUDIO_API_SPECIFIC, or RTAUDIO_API_SPECIFIC_JACK, etc., before including RtAudio.h. Then, getDeviceHandle() returns a pointer that can be safely dynamic_cast<> so that if there is an API mismatch, nullptr is returned. This commit implements it for Jack and Pulse Audio only! Example: #include <jack/jack.h> #define RTAUDIO_API_SPECIFIC_JACK #include <RtAudio.h> ... RtAudio audio(RtAudio::UNIX_JACK); .. after openStream RtAudioClientHandle *h = audio.getClientHandle(); RtAudioClientHandleJack *h_jack = dynamic_cast<RtAudioClientHandleJack*>(h); if (h_jack) { .. my_function_needing_jack_client_t(h_jack.client); } Note that the above code will not crash if RtAudio::LINUX_PULSE was selected, and only call Jack-specific functions if indeed Jack is the current API.
1 parent 0b0a081 commit 4f7a17a

File tree

2 files changed

+147
-55
lines changed

2 files changed

+147
-55
lines changed

RtAudio.cpp

Lines changed: 97 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
286286
userData, options, errorCallback );
287287
}
288288

289+
RtAudioClientHandle *RtAudio :: getClientHandle( void )
290+
{
291+
return rtapi_->getClientHandle();
292+
}
293+
289294
// *************************************************** //
290295
//
291296
// Public RtApi definitions (see end of file for
@@ -497,6 +502,9 @@ unsigned int RtApi :: getStreamSampleRate( void )
497502
return stream_.sampleRate;
498503
}
499504

505+
// Re-include RtAudio.h to get API-specific structs
506+
#define RTAUDIO_API_SPECIFIC
507+
#include "RtAudio.h"
500508

501509
// *************************************************** //
502510
//
@@ -2013,19 +2021,28 @@ const char* RtApiCore :: getErrorCode( OSStatus code )
20132021
#include <unistd.h>
20142022
#include <cstdio>
20152023

2024+
// A structure to hold various information related to the Jack API
2025+
// implementation that can be exposed to caller. Same struct is
2026+
// defined in RtAudio.h if RTAUDIO_API_SPECIFIC is defined.
2027+
struct RtAudioClientHandleJack : public RtAudioClientHandle
2028+
{
2029+
jack_client_t *client = 0;
2030+
jack_port_t **ports[2] = {0,0};
2031+
};
2032+
20162033
// A structure to hold various information related to the Jack API
20172034
// implementation.
20182035
struct JackHandle {
2019-
jack_client_t *client;
2020-
jack_port_t **ports[2];
2036+
RtAudioClientHandleJack ch;
20212037
std::string deviceName[2];
20222038
bool xrun[2];
20232039
pthread_cond_t condition;
20242040
int drainCounter; // Tracks callback counts when draining
20252041
bool internalDrain; // Indicates if stop is initiated from callback or not.
20262042

20272043
JackHandle()
2028-
:client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
2044+
: drainCounter(0), internalDrain(false)
2045+
{ xrun[0] = false; xrun[1] = false; }
20292046
};
20302047

20312048
#if !defined(__RTAUDIO_DEBUG__)
@@ -2172,6 +2189,13 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
21722189
return info;
21732190
}
21742191

2192+
RtAudioClientHandle *RtApiJack :: getClientHandle()
2193+
{
2194+
JackHandle *handle = (JackHandle *) stream_.apiHandle;
2195+
2196+
return &handle->ch;
2197+
}
2198+
21752199
static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
21762200
{
21772201
CallbackInfo *info = (CallbackInfo *) infoPointer;
@@ -2216,8 +2240,8 @@ static int jackXrun( void *infoPointer )
22162240
{
22172241
JackHandle *handle = *((JackHandle **) infoPointer);
22182242

2219-
if ( handle->ports[0] ) handle->xrun[0] = true;
2220-
if ( handle->ports[1] ) handle->xrun[1] = true;
2243+
if ( handle->ch.ports[0] ) handle->xrun[0] = true;
2244+
if ( handle->ch.ports[1] ) handle->xrun[1] = true;
22212245

22222246
return 0;
22232247
}
@@ -2246,7 +2270,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
22462270
}
22472271
else {
22482272
// The handle must have been created on an earlier pass.
2249-
client = handle->client;
2273+
client = handle->ch.client;
22502274
}
22512275

22522276
const char **ports;
@@ -2365,7 +2389,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
23652389
goto error;
23662390
}
23672391
stream_.apiHandle = (void *) handle;
2368-
handle->client = client;
2392+
handle->ch.client = client;
23692393
}
23702394
handle->deviceName[mode] = deviceName;
23712395

@@ -2403,8 +2427,8 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
24032427
}
24042428

24052429
// Allocate memory for the Jack ports (channels) identifiers.
2406-
handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
2407-
if ( handle->ports[mode] == NULL ) {
2430+
handle->ch.ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
2431+
if ( handle->ch.ports[mode] == NULL ) {
24082432
errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
24092433
goto error;
24102434
}
@@ -2419,25 +2443,26 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
24192443
stream_.mode = DUPLEX;
24202444
else {
24212445
stream_.mode = mode;
2422-
jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
2423-
jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle );
2424-
jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
2446+
jack_set_process_callback( handle->ch.client, jackCallbackHandler,
2447+
(void *) &stream_.callbackInfo );
2448+
jack_set_xrun_callback( handle->ch.client, jackXrun, (void *) &stream_.apiHandle );
2449+
jack_on_shutdown( handle->ch.client, jackShutdown, (void *) &stream_.callbackInfo );
24252450
}
24262451

24272452
// Register our ports.
24282453
char label[64];
24292454
if ( mode == OUTPUT ) {
24302455
for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
24312456
snprintf( label, 64, "outport %d", i );
2432-
handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
2433-
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
2457+
handle->ch.ports[0][i] = jack_port_register( handle->ch.client, (const char *)label,
2458+
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
24342459
}
24352460
}
24362461
else {
24372462
for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
24382463
snprintf( label, 64, "inport %d", i );
2439-
handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
2440-
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
2464+
handle->ch.ports[1][i] = jack_port_register( handle->ch.client, (const char *)label,
2465+
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
24412466
}
24422467
}
24432468

@@ -2453,10 +2478,10 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
24532478
error:
24542479
if ( handle ) {
24552480
pthread_cond_destroy( &handle->condition );
2456-
jack_client_close( handle->client );
2481+
jack_client_close( handle->ch.client );
24572482

2458-
if ( handle->ports[0] ) free( handle->ports[0] );
2459-
if ( handle->ports[1] ) free( handle->ports[1] );
2483+
if ( handle->ch.ports[0] ) free( handle->ch.ports[0] );
2484+
if ( handle->ch.ports[1] ) free( handle->ch.ports[1] );
24602485

24612486
delete handle;
24622487
stream_.apiHandle = 0;
@@ -2489,14 +2514,14 @@ void RtApiJack :: closeStream( void )
24892514
if ( handle ) {
24902515

24912516
if ( stream_.state == STREAM_RUNNING )
2492-
jack_deactivate( handle->client );
2517+
jack_deactivate( handle->ch.client );
24932518

2494-
jack_client_close( handle->client );
2519+
jack_client_close( handle->ch.client );
24952520
}
24962521

24972522
if ( handle ) {
2498-
if ( handle->ports[0] ) free( handle->ports[0] );
2499-
if ( handle->ports[1] ) free( handle->ports[1] );
2523+
if ( handle->ch.ports[0] ) free( handle->ch.ports[0] );
2524+
if ( handle->ch.ports[1] ) free( handle->ch.ports[1] );
25002525
pthread_cond_destroy( &handle->condition );
25012526
delete handle;
25022527
stream_.apiHandle = 0;
@@ -2532,7 +2557,7 @@ void RtApiJack :: startStream( void )
25322557
#endif
25332558

25342559
JackHandle *handle = (JackHandle *) stream_.apiHandle;
2535-
int result = jack_activate( handle->client );
2560+
int result = jack_activate( handle->ch.client );
25362561
if ( result ) {
25372562
errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
25382563
goto unlock;
@@ -2543,7 +2568,8 @@ void RtApiJack :: startStream( void )
25432568
// Get the list of available ports.
25442569
if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) {
25452570
result = 1;
2546-
ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
2571+
ports = jack_get_ports( handle->ch.client, handle->deviceName[0].c_str(),
2572+
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
25472573
if ( ports == NULL) {
25482574
errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
25492575
goto unlock;
@@ -2555,7 +2581,8 @@ void RtApiJack :: startStream( void )
25552581
for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
25562582
result = 1;
25572583
if ( ports[ stream_.channelOffset[0] + i ] )
2558-
result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
2584+
result = jack_connect( handle->ch.client, jack_port_name( handle->ch.ports[0][i] ),
2585+
ports[ stream_.channelOffset[0] + i ] );
25592586
if ( result ) {
25602587
free( ports );
25612588
errorText_ = "RtApiJack::startStream(): error connecting output ports!";
@@ -2567,7 +2594,8 @@ void RtApiJack :: startStream( void )
25672594

25682595
if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) {
25692596
result = 1;
2570-
ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
2597+
ports = jack_get_ports( handle->ch.client, handle->deviceName[1].c_str(),
2598+
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
25712599
if ( ports == NULL) {
25722600
errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
25732601
goto unlock;
@@ -2577,7 +2605,8 @@ void RtApiJack :: startStream( void )
25772605
for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
25782606
result = 1;
25792607
if ( ports[ stream_.channelOffset[1] + i ] )
2580-
result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
2608+
result = jack_connect( handle->ch.client, ports[ stream_.channelOffset[1] + i ],
2609+
jack_port_name( handle->ch.ports[1][i] ) );
25812610
if ( result ) {
25822611
free( ports );
25832612
errorText_ = "RtApiJack::startStream(): error connecting input ports!";
@@ -2614,7 +2643,7 @@ void RtApiJack :: stopStream( void )
26142643
}
26152644
}
26162645

2617-
jack_deactivate( handle->client );
2646+
jack_deactivate( handle->ch.client );
26182647
stream_.state = STREAM_STOPPED;
26192648
}
26202649

@@ -2711,7 +2740,8 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
27112740
if ( handle->drainCounter > 1 ) { // write zeros to the output stream
27122741

27132742
for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2714-
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2743+
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(
2744+
handle->ch.ports[0][i], (jack_nframes_t) nframes );
27152745
memset( jackbuffer, 0, bufferBytes );
27162746
}
27172747

@@ -2721,13 +2751,15 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
27212751
convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
27222752

27232753
for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2724-
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2754+
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(
2755+
handle->ch.ports[0][i], (jack_nframes_t) nframes );
27252756
memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
27262757
}
27272758
}
27282759
else { // no buffer conversion
27292760
for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2730-
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2761+
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(
2762+
handle->ch.ports[0][i], (jack_nframes_t) nframes );
27312763
memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
27322764
}
27332765
}
@@ -2743,14 +2775,16 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
27432775

27442776
if ( stream_.doConvertBuffer[1] ) {
27452777
for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
2746-
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2778+
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(
2779+
handle->ch.ports[1][i], (jack_nframes_t) nframes );
27472780
memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
27482781
}
27492782
convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
27502783
}
27512784
else { // no buffer conversion
27522785
for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2753-
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2786+
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(
2787+
handle->ch.ports[1][i], (jack_nframes_t) nframes );
27542788
memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
27552789
}
27562790
}
@@ -8493,13 +8527,17 @@ static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
84938527
{RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},
84948528
{0, PA_SAMPLE_INVALID}};
84958529

8530+
struct RtAudioClientHandlePulse : public RtAudioClientHandle
8531+
{
8532+
pa_simple *s_play = nullptr;
8533+
pa_simple *s_rec = nullptr;
8534+
};
8535+
84968536
struct PulseAudioHandle {
8497-
pa_simple *s_play;
8498-
pa_simple *s_rec;
8537+
RtAudioClientHandlePulse ch;
84998538
pthread_t thread;
85008539
pthread_cond_t runnable_cv;
8501-
bool runnable;
8502-
PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }
8540+
bool runnable = false;
85038541
};
85048542

85058543
static void rt_pa_mainloop_api_quit(int ret) {
@@ -8741,12 +8779,12 @@ void RtApiPulse::closeStream( void )
87418779
MUTEX_UNLOCK( &stream_.mutex );
87428780

87438781
pthread_join( pah->thread, 0 );
8744-
if ( pah->s_play ) {
8745-
pa_simple_flush( pah->s_play, NULL );
8746-
pa_simple_free( pah->s_play );
8782+
if ( pah->ch.s_play ) {
8783+
pa_simple_flush( pah->ch.s_play, NULL );
8784+
pa_simple_free( pah->ch.s_play );
87478785
}
8748-
if ( pah->s_rec )
8749-
pa_simple_free( pah->s_rec );
8786+
if ( pah->ch.s_rec )
8787+
pa_simple_free( pah->ch.s_rec );
87508788

87518789
pthread_cond_destroy( &pah->runnable_cv );
87528790
delete pah;
@@ -8821,7 +8859,7 @@ void RtApiPulse::callbackEvent( void )
88218859
bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *
88228860
formatBytes( stream_.userFormat );
88238861

8824-
if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {
8862+
if ( pa_simple_write( pah->ch.s_play, pulse_out, bytes, &pa_error ) < 0 ) {
88258863
errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
88268864
pa_strerror( pa_error ) << ".";
88278865
errorText_ = errorStream_.str();
@@ -8837,7 +8875,7 @@ void RtApiPulse::callbackEvent( void )
88378875
bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *
88388876
formatBytes( stream_.userFormat );
88398877

8840-
if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {
8878+
if ( pa_simple_read( pah->ch.s_rec, pulse_in, bytes, &pa_error ) < 0 ) {
88418879
errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
88428880
pa_strerror( pa_error ) << ".";
88438881
errorText_ = errorStream_.str();
@@ -8906,9 +8944,9 @@ void RtApiPulse::stopStream( void )
89068944

89078945
if ( pah ) {
89088946
pah->runnable = false;
8909-
if ( pah->s_play ) {
8947+
if ( pah->ch.s_play ) {
89108948
int pa_error;
8911-
if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
8949+
if ( pa_simple_drain( pah->ch.s_play, &pa_error ) < 0 ) {
89128950
errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
89138951
pa_strerror( pa_error ) << ".";
89148952
errorText_ = errorStream_.str();
@@ -8943,9 +8981,9 @@ void RtApiPulse::abortStream( void )
89438981

89448982
if ( pah ) {
89458983
pah->runnable = false;
8946-
if ( pah->s_play ) {
8984+
if ( pah->ch.s_play ) {
89478985
int pa_error;
8948-
if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
8986+
if ( pa_simple_flush( pah->ch.s_play, &pa_error ) < 0 ) {
89498987
errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
89508988
pa_strerror( pa_error ) << ".";
89518989
errorText_ = errorStream_.str();
@@ -9125,17 +9163,17 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
91259163
buffer_attr.fragsize = bufferBytes;
91269164
buffer_attr.maxlength = -1;
91279165

9128-
pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD,
9166+
pah->ch.s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD,
91299167
dev_input, "Record", &ss, NULL, &buffer_attr, &error );
9130-
if ( !pah->s_rec ) {
9168+
if ( !pah->ch.s_rec ) {
91319169
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";
91329170
goto error;
91339171
}
91349172
break;
91359173
case OUTPUT:
9136-
pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK,
9174+
pah->ch.s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK,
91379175
dev_output, "Playback", &ss, NULL, NULL, &error );
9138-
if ( !pah->s_play ) {
9176+
if ( !pah->ch.s_play ) {
91399177
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
91409178
goto error;
91419179
}
@@ -9233,6 +9271,12 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
92339271
return FAILURE;
92349272
}
92359273

9274+
RtAudioClientHandle* RtApiPulse :: getClientHandle( void )
9275+
{
9276+
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
9277+
return &pah->ch;
9278+
}
9279+
92369280
//******************** End of __LINUX_PULSE__ *********************//
92379281
#endif
92389282

0 commit comments

Comments
 (0)