Browse Source

Mutex removal from several APIs, addition of PulseAudio support, documentation updates for 4.0.11 release.

tags/4.0.11
Gary Scavone Stephen Sinclair 13 years ago
parent
commit
5d8514d7eb
18 changed files with 1808 additions and 1618 deletions
  1. +456
    -141
      RtAudio.cpp
  2. +35
    -2
      RtAudio.h
  3. +6
    -0
      configure.ac
  4. +1
    -1
      doc/doxygen/Doxyfile
  5. +9
    -0
      doc/doxygen/acknowledge.txt
  6. +1
    -3
      doc/doxygen/apinotes.txt
  7. +8
    -0
      doc/doxygen/compiling.txt
  8. +1
    -1
      doc/doxygen/footer.html
  9. +1
    -1
      doc/doxygen/license.txt
  10. +2
    -2
      doc/doxygen/tutorial.txt
  11. +3
    -2
      doc/release.txt
  12. +1276
    -1459
      include/soundcard.h
  13. +2
    -1
      install
  14. +1
    -1
      readme
  15. +3
    -1
      tests/duplex.cpp
  16. +1
    -1
      tests/playsaw.cpp
  17. +1
    -1
      tests/testall.cpp
  18. +1
    -1
      tests/teststops.cpp

+ 456
- 141
RtAudio.cpp View File

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


+ 35
- 2
RtAudio.h View File

@@ -210,6 +210,7 @@ class RtAudio
enum Api {
UNSPECIFIED, /*!< Search for a working compiled API. */
LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */
LINUX_PULSE, /*!< The Linux PulseAudio API. */
LINUX_OSS, /*!< The Linux Open Sound System API. */
UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */
MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */
@@ -511,7 +512,7 @@ class RtAudio
typedef unsigned long ThreadHandle;
typedef CRITICAL_SECTION StreamMutex;

#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__)
// Using pthread library for various flavors of unix.
#include <pthread.h>

@@ -552,7 +553,7 @@ struct CallbackInfo {
// Note that RtApi is an abstract base class and cannot be
// explicitly instantiated. The class RtAudio will create an
// instance of an RtApi subclass (RtApiOss, RtApiAlsa,
// RtApiJack, RtApiCore, RtApiAl, RtApiDs, or RtApiAsio).
// RtApiJack, RtApiCore, RtApiDs, or RtApiAsio).
//
// **************************************************************** //

@@ -912,6 +913,38 @@ public:

#endif

#if defined(__LINUX_PULSE__)

class RtApiPulse: public RtApi
{
public:
~RtApiPulse();
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; };
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );

// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
void callbackEvent( void );

private:

std::vector<RtAudio::DeviceInfo> devices_;
void saveDeviceInfo( void );
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
};

#endif

#if defined(__LINUX_OSS__)

class RtApiOss: public RtApi


+ 6
- 0
configure.ac View File

@@ -80,6 +80,12 @@ case $host in
AC_MSG_RESULT(using ALSA)
AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], )

# Look for PULSE flag
AC_ARG_WITH(pulse, [ --with-pulse = choose PulseAudio API support (linux only)], [
api="$api -D__LINUX_PULSE__"
AC_MSG_RESULT(using PulseAudio)
AC_CHECK_LIB(pulse-simple, pa_simple_new, , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!))], )

# Look for OSS flag
AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [
api="$api -D__LINUX_OSS__"


+ 1
- 1
doc/doxygen/Doxyfile View File

@@ -31,7 +31,7 @@ PROJECT_NAME = RtAudio
# This could be handy for archiving the generated documentation or
# if some version control system is used.

PROJECT_NUMBER = 4.0.10
PROJECT_NUMBER = 4.0.11

# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.


+ 9
- 0
doc/doxygen/acknowledge.txt View File

@@ -2,11 +2,20 @@

Many thanks to the following people for providing bug fixes and improvements:
<UL>
<LI>Stefan Arisona</LI>
<LI>Vincent B&eacute;nony</LI>
<LI>Rasmus Ekman</LI>
<LI>Anders Ervik</LI>
<LI>Robin Davies (Windows DS and ASIO)</LI>
<LI>Martin Koegler</LI>
<LI>Dmitry Kostjuchenko</LI>
<LI>Oliver Larkin</LI>
<LI>Antoine Lefebvre</LI>
<LI>Carlos Luna</LI>
<LI>Dominic Mazzoni</LI>
<LI>Tristan Matthews</LI>
<LI>Peter Meerwald (PulseAudio)</LI>
<LI>Benjamin Schroeder</LI>
<LI>Ryan Williams (Windows non-MS compiler ASIO support)</LI>
<LI>Ed Wildgoose (Linux ALSA and Jack)</LI>



+ 1
- 3
doc/doxygen/apinotes.txt View File

@@ -4,14 +4,12 @@ RtAudio is designed to provide a common API across the various supported operati

\section linux Linux:

RtAudio for Linux was developed under Redhat distributions 7.0 - Fedora. Three different audio APIs are supported on Linux platforms: <A href="http://www.opensound.com/oss.html">OSS</A> (versions >= 4.0), <A href="http://www.alsa-project.org/">ALSA</A>, and <A href="http://jackit.sourceforge.net/">Jack</A>. Note that RtAudio now only supports the newer version 4.0 OSS API. The ALSA API is now part of the Linux kernel and offers significantly better functionality than the OSS API. RtAudio provides support for the 1.0 and higher versions of ALSA. Jack is a low-latency audio server written primarily for the GNU/Linux operating system. It can connect a number of different applications to an audio device, as well as allow them to share audio between themselves. Input/output latency on the order of 15 milliseconds can typically be achieved using any of the Linux APIs by fine-tuning the RtAudio buffer parameters (without kernel modifications). Latencies on the order of 5 milliseconds or less can be achieved using a low-latency kernel patch and increasing FIFO scheduling priority. The pthread library, which is used for callback functionality, is a standard component of all Linux distributions.
RtAudio for Linux was developed under Redhat distributions 7.0 - Fedora. Four different audio APIs are supported on Linux platforms: <A href="http://www.opensound.com/oss.html">OSS</A> (versions >= 4.0), <A href="http://www.alsa-project.org/">ALSA</A>, <A href="http://jackit.sourceforge.net/">Jack</A>, and <A href="http://www.freedesktop.org/wiki/Software/PulseAudio">PulseAudio</A>. Note that RtAudio now only supports the newer version 4.0 OSS API. The ALSA API is now part of the Linux kernel and offers significantly better functionality than the OSS API. RtAudio provides support for the 1.0 and higher versions of ALSA. Jack is a low-latency audio server written primarily for the GNU/Linux operating system. It can connect a number of different applications to an audio device, as well as allow them to share audio between themselves. Input/output latency on the order of 15 milliseconds can typically be achieved using any of the Linux APIs by fine-tuning the RtAudio buffer parameters (without kernel modifications). Latencies on the order of 5 milliseconds or less can be achieved using a low-latency kernel patch and increasing FIFO scheduling priority. The pthread library, which is used for callback functionality, is a standard component of all Linux distributions.

The ALSA library includes OSS emulation support. That means that you can run programs compiled for the OSS API even when using the ALSA drivers and library. It should be noted however that OSS emulation under ALSA is not perfect. Specifically, channel number queries seem to consistently produce invalid results. While OSS emulation is successful for the majority of RtAudio tests, it is recommended that the native ALSA implementation of RtAudio be used on systems which have ALSA drivers installed.

The ALSA implementation of RtAudio makes no use of the ALSA "plug" interface. All necessary data format conversions, channel compensation, de-interleaving, and byte-swapping is handled by internal RtAudio routines.

At the moment, only one RtAudio instance can be connected to the Jack server.

\section macosx Macintosh OS-X (CoreAudio and Jack):

The Apple CoreAudio API is designed to use a separate callback procedure for each of its audio devices. A single RtAudio duplex stream using two different devices is supported, though it cannot be guaranteed to always behave correctly because we cannot synchronize these two callbacks. The <I>numberOfBuffers</I> parameter to the RtAudio::openStream() function has no affect in this implementation.


+ 8
- 0
doc/doxygen/compiling.txt View File

@@ -26,6 +26,14 @@ In order to compile RtAudio for a specific OS and audio API, it is necessary to
<TD><TT>asound, pthread</TT></TD>
<TD><TT>g++ -Wall -D__LINUX_ALSA__ -o audioprobe audioprobe.cpp RtAudio.cpp -lasound -lpthread</TT></TD>
</TR>
<TR>
<TD>Linux</TD>
<TD>PulseAudio</TD>
<TD>RtApiPulse</TD>
<TD>__LINUX_PULSE__</TD>
<TD><TT>pthread</TT></TD>
<TD><TT>g++ -Wall -D__LINUX_PULSE__ -o audioprobe audioprobe.cpp RtAudio.cpp -lpthread</TT></TD>
</TR>
<TR>
<TD>Linux</TD>
<TD>OSS</TD>


+ 1
- 1
doc/doxygen/footer.html View File

@@ -1,7 +1,7 @@
<HR>

<table><tr><td><img src="../images/mcgill.gif" width=165></td>
<td>&copy;2001-2010 Gary P. Scavone, McGill University. All Rights Reserved.<br>Maintained by <a href="http://www.music.mcgill.ca/~gary/">Gary P. Scavone</a>.</td></tr>
<td>&copy;2001-2012 Gary P. Scavone, McGill University. All Rights Reserved.<br>Maintained by <a href="http://www.music.mcgill.ca/~gary/">Gary P. Scavone</a>.</td></tr>
</table>

</BODY>


+ 1
- 1
doc/doxygen/license.txt View File

@@ -1,7 +1,7 @@
/*! \page license License

RtAudio: a set of realtime audio i/o C++ classes<BR>
Copyright (c) 2001-2011 Gary P. Scavone
Copyright (c) 2001-2012 Gary P. Scavone

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files


+ 2
- 2
doc/doxygen/tutorial.txt View File

@@ -1,6 +1,6 @@
/*! \mainpage The RtAudio Home Page

RtAudio is a set of C++ classes that provide a common API (Application Programming Interface) for realtime audio input/output across Linux, Macintosh OS-X and Windows (DirectSound and ASIO) operating systems. RtAudio significantly simplifies the process of interacting with computer audio hardware. It was designed with the following objectives:
RtAudio is a set of C++ classes that provide a common API (Application Programming Interface) for realtime audio input/output across Linux, Macintosh OS-X and Windows operating systems. RtAudio significantly simplifies the process of interacting with computer audio hardware. It was designed with the following objectives:

<UL>
<LI>object-oriented C++ design</LI>
@@ -32,7 +32,7 @@ Devices are now re-enumerated every time the RtAudio::getDeviceCount(), RtAudio:

\section download Download

Latest Release (?? June 2012): <A href="http://www.music.mcgill.ca/~gary/rtaudio/release/rtaudio-4.0.11.tar.gz">Version 4.0.11</A>
Latest Release (14 June 2012): <A href="http://www.music.mcgill.ca/~gary/rtaudio/release/rtaudio-4.0.11.tar.gz">Version 4.0.11</A>

\section documentation Documentation Links



+ 3
- 2
doc/release.txt View File

@@ -1,9 +1,10 @@
RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.

By Gary P. Scavone, 2001-2012.

v4.0.11: (?? June 2012)
v4.0.11: (14 June 2012)
- fixes for memory leaks in ALSA (thanks to Martin Koegler)
- PulseAudio API support added (thanks to Peter Meerwald and Tristan Matthews)
- bitwise format flag fixes in OS-X (Benjamin Schroeder and Stefan Arisona)
- changes to stopStream / drain flag to avoid hung state in ASIO, DS, OS-X, and Jack APIs (Rasmus Ekman and Carlos Luna)



+ 1276
- 1459
include/soundcard.h
File diff suppressed because it is too large
View File


+ 2
- 1
install View File

@@ -1,4 +1,4 @@
RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.

By Gary P. Scavone, 2001-2012.

@@ -17,6 +17,7 @@ A few options can be passed to configure, including:

--enable-debug = enable various debug output
--with-alsa = choose native ALSA API support (linux only)
--with-pulse = choose native PulseAudio API support (linux only)
--with-oss = choose OSS API support (linux only)
--with-jack = choose JACK server support (linux or Macintosh OS-X)
--with-core = choose CoreAudio API support (Macintosh OS-X only)


+ 1
- 1
readme View File

@@ -1,4 +1,4 @@
RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.

By Gary P. Scavone, 2001-2012.



+ 3
- 1
tests/duplex.cpp View File

@@ -19,19 +19,21 @@ typedef signed long MY_TYPE;

typedef char MY_TYPE;
#define FORMAT RTAUDIO_SINT8
*/

typedef signed short MY_TYPE;
#define FORMAT RTAUDIO_SINT16

/*
typedef signed long MY_TYPE;
#define FORMAT RTAUDIO_SINT32

typedef float MY_TYPE;
#define FORMAT RTAUDIO_FLOAT32
*/

typedef double MY_TYPE;
#define FORMAT RTAUDIO_FLOAT64
*/

void usage( void ) {
// Error function in case of incorrect command-line


+ 1
- 1
tests/playsaw.cpp View File

@@ -157,7 +157,7 @@ int main( int argc, char *argv[] )
dac.showWarnings( true );

// Set our stream parameters for output only.
bufferFrames = 256;
bufferFrames = 512;
RtAudio::StreamParameters oParams;
oParams.deviceId = device;
oParams.nChannels = channels;


+ 1
- 1
tests/testall.cpp View File

@@ -122,7 +122,7 @@ int main( int argc, char *argv[] )
dac.showWarnings( true );

// Set our stream parameters for output only.
bufferFrames = 256;
bufferFrames = 512;
RtAudio::StreamParameters oParams, iParams;
oParams.deviceId = oDevice;
oParams.nChannels = channels;


+ 1
- 1
tests/teststops.cpp View File

@@ -111,7 +111,7 @@ int main( int argc, char *argv[] )
pausetime = PAUSETIME * 1000;
// Set our stream parameters for a duplex stream.
bufferFrames = 256;
bufferFrames = 512;
RtAudio::StreamParameters oParams, iParams;
oParams.deviceId = oDevice;
oParams.nChannels = mydata.channels;


Loading…
Cancel
Save