diff options
| author | Gary Scavone <gary@music.mcgill.ca> | 2012-06-13 20:27:59 +0000 |
|---|---|---|
| committer | Stephen Sinclair <sinclair@music.mcgill.ca> | 2013-10-11 01:38:30 +0200 |
| commit | 5d8514d7eb3918a947ec97b45f4105630c64468d (patch) | |
| tree | 6f77b4faa95b24b978de75e7a78ecbb223be0707 /RtAudio.cpp | |
| parent | 758cf4789a38b161024f5371fd041b5489cf9921 (diff) | |
Mutex removal from several APIs, addition of PulseAudio support, documentation updates for 4.0.11 release.
Diffstat (limited to 'RtAudio.cpp')
| -rw-r--r-- | RtAudio.cpp | 597 |
1 files changed, 456 insertions, 141 deletions
diff --git a/RtAudio.cpp b/RtAudio.cpp index 47e5804..823faaf 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -58,7 +58,7 @@ const unsigned int RtApi::SAMPLE_RATES[] = { #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
#define MUTEX_LOCK(A) EnterCriticalSection(A)
#define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
-#elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
+#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
// pthread API
#define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
#define MUTEX_DESTROY(A) pthread_mutex_destroy(A)
@@ -87,6 +87,9 @@ void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw() #if defined(__LINUX_ALSA__)
apis.push_back( LINUX_ALSA );
#endif
+#if defined(__LINUX_PULSE__)
+ apis.push_back( LINUX_PULSE );
+#endif
#if defined(__LINUX_OSS__)
apis.push_back( LINUX_OSS );
#endif
@@ -106,7 +109,7 @@ void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw() void RtAudio :: openRtApi( RtAudio::Api api )
{
- if (rtapi_)
+ if ( rtapi_ )
delete rtapi_;
rtapi_ = 0;
@@ -118,6 +121,10 @@ void RtAudio :: openRtApi( RtAudio::Api api ) if ( api == LINUX_ALSA )
rtapi_ = new RtApiAlsa();
#endif
+#if defined(__LINUX_PULSE__)
+ if ( api == LINUX_PULSE )
+ rtapi_ = new RtApiPulse();
+#endif
#if defined(__LINUX_OSS__)
if ( api == LINUX_OSS )
rtapi_ = new RtApiOss();
@@ -1357,8 +1364,6 @@ void RtApiCore :: startStream( void ) return;
}
- //MUTEX_LOCK( &stream_.mutex );
-
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -1387,8 +1392,6 @@ void RtApiCore :: startStream( void ) stream_.state = STREAM_RUNNING;
unlock:
- //MUTEX_UNLOCK( &stream_.mutex );
-
if ( result == noErr ) return;
error( RtError::SYSTEM_ERROR );
}
@@ -1402,15 +1405,6 @@ void RtApiCore :: stopStream( void ) return;
}
- /*
- MUTEX_LOCK( &stream_.mutex );
-
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- */
-
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -1420,9 +1414,7 @@ void RtApiCore :: stopStream( void ) pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
}
- //MUTEX_UNLOCK( &stream_.mutex );
result = AudioDeviceStop( handle->id[0], callbackHandler );
- //MUTEX_LOCK( &stream_.mutex );
if ( result != noErr ) {
errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
errorText_ = errorStream_.str();
@@ -1432,9 +1424,7 @@ void RtApiCore :: stopStream( void ) if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
- //MUTEX_UNLOCK( &stream_.mutex );
result = AudioDeviceStop( handle->id[1], callbackHandler );
- //MUTEX_LOCK( &stream_.mutex );
if ( result != noErr ) {
errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
errorText_ = errorStream_.str();
@@ -1445,8 +1435,6 @@ void RtApiCore :: stopStream( void ) stream_.state = STREAM_STOPPED;
unlock:
- //MUTEX_UNLOCK( &stream_.mutex );
-
if ( result == noErr ) return;
error( RtError::SYSTEM_ERROR );
}
@@ -1477,7 +1465,6 @@ extern "C" void *coreStopStream( void *ptr ) RtApiCore *object = (RtApiCore *) info->object;
object->stopStream();
-
pthread_exit( NULL );
}
@@ -1498,26 +1485,14 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, // Check if we were draining the stream and signal is finished.
if ( handle->drainCounter > 3 ) {
- if ( handle->internalDrain == true ) {
- stream_.state = STREAM_STOPPING;
+ stream_.state = STREAM_STOPPING;
+ if ( handle->internalDrain == true )
pthread_create( &threadId, NULL, coreStopStream, info );
- //stopStream();
- }
else // external call to stopStream()
pthread_cond_signal( &handle->condition );
return SUCCESS;
}
- /*
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return SUCCESS;
- }
- */
-
AudioDeviceID outputDevice = handle->id[0];
// Invoke user callback to get fresh output data UNLESS we are
@@ -1539,7 +1514,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, info->userData );
if ( cbReturnValue == 2 ) {
- //MUTEX_UNLOCK( &stream_.mutex );
+ stream_.state = STREAM_STOPPING;
handle->drainCounter = 2;
abortStream();
return SUCCESS;
@@ -2327,8 +2302,6 @@ void RtApiJack :: startStream( void ) return;
}
- MUTEX_LOCK(&stream_.mutex);
-
JackHandle *handle = (JackHandle *) stream_.apiHandle;
int result = jack_activate( handle->client );
if ( result ) {
@@ -2390,8 +2363,6 @@ void RtApiJack :: startStream( void ) stream_.state = STREAM_RUNNING;
unlock:
- MUTEX_UNLOCK(&stream_.mutex);
-
if ( result == 0 ) return;
error( RtError::SYSTEM_ERROR );
}
@@ -2405,13 +2376,6 @@ void RtApiJack :: stopStream( void ) return;
}
- MUTEX_LOCK( &stream_.mutex );
-
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
-
JackHandle *handle = (JackHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -2423,8 +2387,6 @@ void RtApiJack :: stopStream( void ) jack_deactivate( handle->client );
stream_.state = STREAM_STOPPED;
-
- MUTEX_UNLOCK( &stream_.mutex );
}
void RtApiJack :: abortStream( void )
@@ -2453,13 +2415,12 @@ extern "C" void *jackStopStream( void *ptr ) RtApiJack *object = (RtApiJack *) info->object;
object->stopStream();
-
pthread_exit( NULL );
}
bool RtApiJack :: callbackEvent( unsigned long nframes )
{
- if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
error( RtError::WARNING );
@@ -2476,6 +2437,8 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) // Check if we were draining the stream and signal is finished.
if ( handle->drainCounter > 3 ) {
+
+ stream_.state = STREAM_STOPPING;
if ( handle->internalDrain == true )
pthread_create( &threadId, NULL, jackStopStream, info );
else
@@ -2483,14 +2446,6 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) return SUCCESS;
}
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return SUCCESS;
- }
-
// Invoke user callback first, to get fresh output data.
if ( handle->drainCounter == 0 ) {
RtAudioCallback callback = (RtAudioCallback) info->callback;
@@ -2507,9 +2462,9 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, info->userData );
if ( cbReturnValue == 2 ) {
- MUTEX_UNLOCK( &stream_.mutex );
- ThreadHandle id;
+ stream_.state = STREAM_STOPPING;
handle->drainCounter = 2;
+ ThreadHandle id;
pthread_create( &id, NULL, jackStopStream, info );
return SUCCESS;
}
@@ -2571,8 +2526,6 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) }
unlock:
- MUTEX_UNLOCK(&stream_.mutex);
-
RtApi::tickStreamTime();
return SUCCESS;
}
@@ -3189,8 +3142,6 @@ void RtApiAsio :: startStream() return;
}
- //MUTEX_LOCK( &stream_.mutex );
-
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
ASIOError result = ASIOStart();
if ( result != ASE_OK ) {
@@ -3206,8 +3157,6 @@ void RtApiAsio :: startStream() asioXRun = false;
unlock:
- //MUTEX_UNLOCK( &stream_.mutex );
-
stopThreadCalled = false;
if ( result == ASE_OK ) return;
@@ -3223,23 +3172,11 @@ void RtApiAsio :: stopStream() return;
}
- /*
- MUTEX_LOCK( &stream_.mutex );
-
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- */
-
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
if ( handle->drainCounter == 0 ) {
handle->drainCounter = 2;
- // MUTEX_UNLOCK( &stream_.mutex );
WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
- //ResetEvent( handle->condition );
- // MUTEX_LOCK( &stream_.mutex );
}
}
@@ -3251,8 +3188,6 @@ void RtApiAsio :: stopStream() errorText_ = errorStream_.str();
}
- // MUTEX_UNLOCK( &stream_.mutex );
-
if ( result == ASE_OK ) return;
error( RtError::SYSTEM_ERROR );
}
@@ -3286,15 +3221,13 @@ extern "C" unsigned __stdcall asioStopStream( void *ptr ) RtApiAsio *object = (RtApiAsio *) info->object;
object->stopStream();
-
_endthreadex( 0 );
return 0;
}
bool RtApiAsio :: callbackEvent( long bufferIndex )
{
- if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
- if ( stopThreadCalled ) return SUCCESS;
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
error( RtError::WARNING );
@@ -3306,22 +3239,18 @@ bool RtApiAsio :: callbackEvent( long bufferIndex ) // Check if we were draining the stream and signal if finished.
if ( handle->drainCounter > 3 ) {
+
+ stream_.state = STREAM_STOPPING;
if ( handle->internalDrain == false )
SetEvent( handle->condition );
else { // spawn a thread to stop the stream
unsigned threadId;
- stopThreadCalled = true;
stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
&stream_.callbackInfo, 0, &threadId );
}
return SUCCESS;
}
- /*MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) goto unlock; */
-
// Invoke user callback to get fresh output data UNLESS we are
// draining stream.
if ( handle->drainCounter == 0 ) {
@@ -3339,11 +3268,9 @@ bool RtApiAsio :: callbackEvent( long bufferIndex ) int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, info->userData );
if ( cbReturnValue == 2 ) {
- // MUTEX_UNLOCK( &stream_.mutex );
- // abortStream();
- unsigned threadId;
- stopThreadCalled = true;
+ stream_.state = STREAM_STOPPING;
handle->drainCounter = 2;
+ unsigned threadId;
stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
&stream_.callbackInfo, 0, &threadId );
return SUCCESS;
@@ -3447,8 +3374,6 @@ bool RtApiAsio :: callbackEvent( long bufferIndex ) // drivers apparently do not function correctly without it.
ASIOOutputReady();
- // MUTEX_UNLOCK( &stream_.mutex );
-
RtApi::tickStreamTime();
return SUCCESS;
}
@@ -4447,8 +4372,6 @@ void RtApiDs :: startStream() return;
}
- //MUTEX_LOCK( &stream_.mutex );
-
DsHandle *handle = (DsHandle *) stream_.apiHandle;
// Increase scheduler frequency on lesser windows (a side-effect of
@@ -4493,8 +4416,6 @@ void RtApiDs :: startStream() stream_.state = STREAM_RUNNING;
unlock:
- // MUTEX_UNLOCK( &stream_.mutex );
-
if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
}
@@ -4507,15 +4428,6 @@ void RtApiDs :: stopStream() return;
}
- /*
- MUTEX_LOCK( &stream_.mutex );
-
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- */
-
HRESULT result = 0;
LPVOID audioPtr;
DWORD dataLen;
@@ -4523,10 +4435,7 @@ void RtApiDs :: stopStream() if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
if ( handle->drainCounter == 0 ) {
handle->drainCounter = 2;
- // MUTEX_UNLOCK( &stream_.mutex );
WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
- //ResetEvent( handle->condition );
- // MUTEX_LOCK( &stream_.mutex );
}
stream_.state = STREAM_STOPPED;
@@ -4604,8 +4513,6 @@ void RtApiDs :: stopStream() unlock:
timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
- // MUTEX_UNLOCK( &stream_.mutex );
-
if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
}
@@ -4626,7 +4533,7 @@ void RtApiDs :: abortStream() void RtApiDs :: callbackEvent()
{
- if ( stream_.state == STREAM_STOPPED ) {
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {
Sleep( 50 ); // sleep 50 milliseconds
return;
}
@@ -4642,6 +4549,8 @@ void RtApiDs :: callbackEvent() // Check if we were draining the stream and signal is finished.
if ( handle->drainCounter > stream_.nBuffers + 2 ) {
+
+ stream_.state = STREAM_STOPPING;
if ( handle->internalDrain == false )
SetEvent( handle->condition );
else
@@ -4649,16 +4558,6 @@ void RtApiDs :: callbackEvent() return;
}
- /*
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- */
-
// Invoke user callback to get fresh output data UNLESS we are
// draining stream.
if ( handle->drainCounter == 0 ) {
@@ -4676,7 +4575,7 @@ void RtApiDs :: callbackEvent() int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, info->userData );
if ( cbReturnValue == 2 ) {
- // MUTEX_UNLOCK( &stream_.mutex );
+ stream_.state = STREAM_STOPPING;
handle->drainCounter = 2;
abortStream();
return;
@@ -5011,8 +4910,6 @@ void RtApiDs :: callbackEvent() }
unlock:
- // MUTEX_UNLOCK( &stream_.mutex );
-
RtApi::tickStreamTime();
}
@@ -6139,11 +6036,6 @@ void RtApiAlsa :: stopStream() stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
- //if ( stream_.state == STREAM_STOPPED ) {
- // MUTEX_UNLOCK( &stream_.mutex );
- // return;
- //}
-
int result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
@@ -6169,7 +6061,6 @@ void RtApiAlsa :: stopStream() }
unlock:
- stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
if ( result >= 0 ) return;
@@ -6188,11 +6079,6 @@ void RtApiAlsa :: abortStream() stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
- //if ( stream_.state == STREAM_STOPPED ) {
- // MUTEX_UNLOCK( &stream_.mutex );
- // return;
- //}
-
int result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
@@ -6215,7 +6101,6 @@ void RtApiAlsa :: abortStream() }
unlock:
- stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
if ( result >= 0 ) return;
@@ -6425,6 +6310,436 @@ extern "C" void *alsaCallbackHandler( void *ptr ) //******************** End of __LINUX_ALSA__ *********************//
#endif
+#if defined(__LINUX_PULSE__)
+
+// Code written by Peter Meerwald, pmeerw@pmeerw.net
+// and Tristan Matthews.
+
+#include <pulse/error.h>
+#include <pulse/simple.h>
+#include <cstdio>
+
+namespace {
+const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
+ 44100, 48000, 96000, 0}; }
+
+struct rtaudio_pa_format_mapping_t {
+ RtAudioFormat rtaudio_format;
+ pa_sample_format_t pa_format;
+};
+
+static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
+ {RTAUDIO_SINT16, PA_SAMPLE_S16LE},
+ {RTAUDIO_SINT32, PA_SAMPLE_S32LE},
+ {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},
+ {0, PA_SAMPLE_INVALID}};
+
+struct PulseAudioHandle {
+ pa_simple *s_play;
+ pa_simple *s_rec;
+ pthread_t thread;
+ pthread_cond_t runnable_cv;
+ bool runnable;
+ PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }
+};
+
+RtApiPulse::~RtApiPulse()
+{
+ if ( stream_.state != STREAM_CLOSED )
+ closeStream();
+}
+
+unsigned int RtApiPulse::getDeviceCount( void )
+{
+ return 1;
+}
+
+RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int device )
+{
+ RtAudio::DeviceInfo info;
+ info.probed = true;
+ info.name = "PulseAudio";
+ info.outputChannels = 2;
+ info.inputChannels = 2;
+ info.duplexChannels = 2;
+ info.isDefaultOutput = true;
+ info.isDefaultInput = true;
+
+ for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
+ info.sampleRates.push_back( *sr );
+
+ info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
+
+ return info;
+}
+
+extern "C" void *pulseaudio_callback( void * user )
+{
+ CallbackInfo *cbi = static_cast<CallbackInfo *>( user );
+ RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
+ volatile bool *isRunning = &cbi->isRunning;
+
+ while ( *isRunning ) {
+ pthread_testcancel();
+ context->callbackEvent();
+ }
+
+ pthread_exit( NULL );
+}
+
+void RtApiPulse::closeStream( void )
+{
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+ stream_.callbackInfo.isRunning = false;
+ if ( pah ) {
+ MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ pah->runnable = true;
+ pthread_cond_signal( &pah->runnable_cv );
+ }
+ MUTEX_UNLOCK( &stream_.mutex );
+
+ pthread_join( pah->thread, 0 );
+ if ( pah->s_play ) {
+ pa_simple_flush( pah->s_play, NULL );
+ pa_simple_free( pah->s_play );
+ }
+ if ( pah->s_rec )
+ pa_simple_free( pah->s_rec );
+
+ pthread_cond_destroy( &pah->runnable_cv );
+ delete pah;
+ stream_.apiHandle = 0;
+ }
+
+ if ( stream_.userBuffer[0] ) {
+ free( stream_.userBuffer[0] );
+ stream_.userBuffer[0] = 0;
+ }
+ if ( stream_.userBuffer[1] ) {
+ free( stream_.userBuffer[1] );
+ stream_.userBuffer[1] = 0;
+ }
+
+ stream_.state = STREAM_CLOSED;
+ stream_.mode = UNINITIALIZED;
+}
+
+void RtApiPulse::callbackEvent( void )
+{
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_LOCK( &stream_.mutex );
+ while ( !pah->runnable )
+ pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );
+
+ if ( stream_.state != STREAM_RUNNING ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+ MUTEX_UNLOCK( &stream_.mutex );
+ }
+
+ if ( stream_.state == STREAM_CLOSED ) {
+ errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "
+ "this shouldn't happen!";
+ error( RtError::WARNING );
+ return;
+ }
+
+ RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+ double streamTime = getStreamTime();
+ RtAudioStreamStatus status = 0;
+ int doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+ stream_.bufferSize, streamTime, status,
+ stream_.callbackInfo.userData );
+
+ if ( doStopStream == 2 ) {
+ abortStream();
+ return;
+ }
+
+ MUTEX_LOCK( &stream_.mutex );
+
+ if ( stream_.state != STREAM_RUNNING )
+ goto unlock;
+
+ int pa_error;
+ size_t bytes;
+ switch ( stream_.mode ) {
+ case INPUT:
+ bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
+ if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ error( RtError::WARNING );
+ }
+ break;
+ case OUTPUT:
+ bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );
+ if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ error( RtError::WARNING );
+ }
+ break;
+ case DUPLEX:
+ bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
+ if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ error( RtError::WARNING );
+ }
+ bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );
+ if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0) {
+ errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ error( RtError::WARNING );
+ }
+ break;
+ default:
+ // ERROR
+ break;
+ }
+
+ unlock:
+ MUTEX_UNLOCK( &stream_.mutex );
+ RtApi::tickStreamTime();
+
+ if ( doStopStream == 1 )
+ stopStream();
+}
+
+void RtApiPulse::startStream( void )
+{
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+ if ( stream_.state == STREAM_CLOSED ) {
+ errorText_ = "RtApiPulse::startStream(): the stream is not open!";
+ error( RtError::INVALID_USE );
+ return;
+ }
+ if ( stream_.state == STREAM_RUNNING ) {
+ errorText_ = "RtApiPulse::startStream(): the stream is already running!";
+ error( RtError::WARNING );
+ return;
+ }
+
+ MUTEX_LOCK( &stream_.mutex );
+
+ stream_.state = STREAM_RUNNING;
+
+ pah->runnable = true;
+ pthread_cond_signal( &pah->runnable_cv );
+ MUTEX_UNLOCK( &stream_.mutex );
+}
+
+void RtApiPulse::stopStream( void )
+{
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+ if ( stream_.state == STREAM_CLOSED ) {
+ errorText_ = "RtApiPulse::stopStream(): the stream is not open!";
+ error( RtError::INVALID_USE );
+ return;
+ }
+ if ( stream_.state == STREAM_STOPPED ) {
+ errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";
+ error( RtError::WARNING );
+ return;
+ }
+
+ stream_.state = STREAM_STOPPED;
+ MUTEX_LOCK( &stream_.mutex );
+
+ if ( pah && pah->s_play ) {
+ int pa_error;
+ if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ MUTEX_UNLOCK( &stream_.mutex );
+ error( RtError::SYSTEM_ERROR );
+ }
+ }
+
+ stream_.state = STREAM_STOPPED;
+ MUTEX_UNLOCK( &stream_.mutex );
+}
+
+void RtApiPulse::abortStream( void )
+{
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );
+
+ if ( stream_.state == STREAM_CLOSED ) {
+ errorText_ = "RtApiPulse::abortStream(): the stream is not open!";
+ error( RtError::INVALID_USE );
+ return;
+ }
+ if ( stream_.state == STREAM_STOPPED ) {
+ errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";
+ error( RtError::WARNING );
+ return;
+ }
+
+ stream_.state = STREAM_STOPPED;
+ MUTEX_LOCK( &stream_.mutex );
+
+ if ( pah && pah->s_play ) {
+ int pa_error;
+ if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ MUTEX_UNLOCK( &stream_.mutex );
+ error( RtError::SYSTEM_ERROR );
+ }
+ }
+
+ stream_.state = STREAM_STOPPED;
+ MUTEX_UNLOCK( &stream_.mutex );
+}
+
+bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
+ unsigned int channels, unsigned int firstChannel,
+ unsigned int sampleRate, RtAudioFormat format,
+ unsigned int *bufferSize, RtAudio::StreamOptions *options )
+{
+ PulseAudioHandle *pah = 0;
+ unsigned long bufferBytes = 0;
+ pa_sample_spec ss;
+
+ if ( device != 0 ) return false;
+ if ( mode != INPUT && mode != OUTPUT ) return false;
+ if ( channels != 1 && channels != 2 ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";
+ return false;
+ }
+ ss.channels = channels;
+
+ if ( firstChannel != 0 ) return false;
+
+ bool sr_found = false;
+ for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {
+ if ( sampleRate == *sr ) {
+ sr_found = true;
+ stream_.sampleRate = sampleRate;
+ ss.rate = sampleRate;
+ break;
+ }
+ }
+ if ( !sr_found ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";
+ return false;
+ }
+
+ bool sf_found = 0;
+ for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;
+ sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {
+ if ( format == sf->rtaudio_format ) {
+ sf_found = true;
+ stream_.userFormat = sf->rtaudio_format;
+ ss.format = sf->pa_format;
+ break;
+ }
+ }
+ if ( !sf_found ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample format.";
+ return false;
+ }
+
+ if ( options && ( options->flags & RTAUDIO_NONINTERLEAVED ) ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: only interleaved audio data supported.";
+ return false;
+ }
+
+ stream_.userInterleaved = true;
+ stream_.nBuffers = 1;
+
+ stream_.deviceInterleaved[mode] = true;
+ stream_.doByteSwap[mode] = false;
+ stream_.doConvertBuffer[mode] = false;
+ stream_.deviceFormat[mode] = stream_.userFormat;
+ stream_.nUserChannels[mode] = channels;
+ stream_.nDeviceChannels[mode] = channels;
+ stream_.channelOffset[mode] = 0;
+
+ // Allocate necessary internal buffers.
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+ if ( stream_.userBuffer[mode] == NULL ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";
+ goto error;
+ }
+ stream_.bufferSize = *bufferSize;
+
+ if ( !stream_.apiHandle ) {
+ PulseAudioHandle *pah = new PulseAudioHandle;
+ if ( !pah ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";
+ goto error;
+ }
+
+ stream_.apiHandle = pah;
+ if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";
+ goto error;
+ }
+ }
+ pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+ int error;
+ switch ( mode ) {
+ case INPUT:
+ pah->s_rec = pa_simple_new( NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &error );
+ if ( !pah->s_rec ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";
+ goto error;
+ }
+ break;
+ case OUTPUT:
+ pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
+ if ( !pah->s_play ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+
+ if ( stream_.mode == UNINITIALIZED )
+ stream_.mode = mode;
+ else if ( stream_.mode == mode )
+ goto error;
+ else
+ stream_.mode = DUPLEX;
+
+ stream_.state = STREAM_STOPPED;
+
+ if ( !stream_.callbackInfo.isRunning ) {
+ stream_.callbackInfo.object = this;
+ stream_.callbackInfo.isRunning = true;
+ if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {
+ errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
+ goto error;
+ }
+ }
+ return true;
+
+ error:
+ closeStream();
+ return false;
+}
+
+//******************** End of __LINUX_PULSE__ *********************//
+#endif
#if defined(__LINUX_OSS__)
|
