|
|
@@ -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__)
|
|
|
|
|
|
|
|