+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == false )\r
+ SetEvent( handle->condition );\r
+ else\r
+ stopStream();\r
+ return;\r
+ }\r
+\r
+ // Invoke user callback to get fresh output data UNLESS we are\r
+ // draining stream.\r
+ if ( handle->drainCounter == 0 ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ abortStream();\r
+ return;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ HRESULT result;\r
+ DWORD currentWritePointer, safeWritePointer;\r
+ DWORD currentReadPointer, safeReadPointer;\r
+ UINT nextWritePointer;\r
+\r
+ LPVOID buffer1 = NULL;\r
+ LPVOID buffer2 = NULL;\r
+ DWORD bufferSize1 = 0;\r
+ DWORD bufferSize2 = 0;\r
+\r
+ char *buffer;\r
+ long bufferBytes;\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+\r
+ if ( buffersRolling == false ) {\r
+ if ( stream_.mode == DUPLEX ) {\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ // It takes a while for the devices to get rolling. As a result,\r
+ // there's no guarantee that the capture and write device pointers\r
+ // will move in lockstep. Wait here for both devices to start\r
+ // rolling, and then set our buffer pointers accordingly.\r
+ // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
+ // bytes later than the write buffer.\r
+\r
+ // Stub: a serious risk of having a pre-emptive scheduling round\r
+ // take place between the two GetCurrentPosition calls... but I'm\r
+ // really not sure how to solve the problem. Temporarily boost to\r
+ // Realtime priority, maybe; but I'm not sure what priority the\r
+ // DirectSound service threads run at. We *should* be roughly\r
+ // within a ms or so of correct.\r
+\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+\r
+ DWORD startSafeWritePointer, startSafeReadPointer;\r
+\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ while ( true ) {\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
+ Sleep( 1 );\r
+ }\r
+\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
+ handle->bufferPointer[1] = safeReadPointer;\r
+ }\r
+ else if ( stream_.mode == OUTPUT ) {\r
+\r
+ // Set the proper nextWritePosition after initial startup.\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
+ }\r
+\r
+ buffersRolling = true;\r
+ }\r
+\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ \r
+ LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+\r
+ if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ memset( stream_.userBuffer[0], 0, bufferBytes );\r
+ }\r
+\r
+ // Setup parameters and do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
+ bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[0];\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ }\r
+\r
+ // No byte swapping necessary in DirectSound implementation.\r
+\r
+ // Ahhh ... windoze. 16-bit data is signed but 8-bit data is\r
+ // unsigned. So, we need to convert our signed 8-bit data here to\r
+ // unsigned.\r
+ if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
+ for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
+\r
+ DWORD dsBufferSize = handle->dsBufferSize[0];\r
+ nextWritePointer = handle->bufferPointer[0];\r
+\r
+ DWORD endWrite, leadPointer;\r
+ while ( true ) {\r
+ // Find out where the read and "safe write" pointers are.\r
+ result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ // We will copy our output buffer into the region between\r
+ // safeWritePointer and leadPointer. If leadPointer is not\r
+ // beyond the next endWrite position, wait until it is.\r
+ leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
+ if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
+ if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
+ endWrite = nextWritePointer + bufferBytes;\r
+\r
+ // Check whether the entire write region is behind the play pointer.\r
+ if ( leadPointer >= endWrite ) break;\r
+\r
+ // If we are here, then we must wait until the leadPointer advances\r
+ // beyond the end of our next write region. We use the\r
+ // Sleep() function to suspend operation until that happens.\r
+ double millis = ( endWrite - leadPointer ) * 1000.0;\r
+ millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
+ if ( millis < 1.0 ) millis = 1.0;\r
+ Sleep( (DWORD) millis );\r
+ }\r
+\r
+ if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
+ || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
+ // We've strayed into the forbidden zone ... resync the read pointer.\r
+ handle->xrun[0] = true;\r
+ nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
+ if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
+ handle->bufferPointer[0] = nextWritePointer;\r
+ endWrite = nextWritePointer + bufferBytes;\r
+ }\r
+\r
+ // Lock free space in the buffer\r
+ result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
+ &bufferSize1, &buffer2, &bufferSize2, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ // Copy our buffer into the DS buffer\r
+ CopyMemory( buffer1, buffer, bufferSize1 );\r
+ if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
+\r
+ // Update our buffer offset and unlock sound buffer\r
+ dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
+ handle->bufferPointer[0] = nextWritePointer;\r
+ }\r
+\r
+ // Don't bother draining input\r
+ if ( handle->drainCounter ) {\r
+ handle->drainCounter++;\r
+ goto unlock;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters.\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
+ bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[1];\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ }\r
+\r
+ LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ long nextReadPointer = handle->bufferPointer[1];\r
+ DWORD dsBufferSize = handle->dsBufferSize[1];\r
+\r
+ // Find out where the write and "safe read" pointers are.\r
+ result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
+ DWORD endRead = nextReadPointer + bufferBytes;\r
+\r
+ // Handling depends on whether we are INPUT or DUPLEX. \r
+ // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
+ // then a wait here will drag the write pointers into the forbidden zone.\r
+ // \r
+ // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
+ // it's in a safe position. This causes dropouts, but it seems to be the only \r
+ // practical way to sync up the read and write pointers reliably, given the \r
+ // the very complex relationship between phase and increment of the read and write \r
+ // pointers.\r
+ //\r
+ // In order to minimize audible dropouts in DUPLEX mode, we will\r
+ // provide a pre-roll period of 0.5 seconds in which we return\r
+ // zeros from the read buffer while the pointers sync up.\r
+\r
+ if ( stream_.mode == DUPLEX ) {\r
+ if ( safeReadPointer < endRead ) {\r
+ if ( duplexPrerollBytes <= 0 ) {\r
+ // Pre-roll time over. Be more agressive.\r
+ int adjustment = endRead-safeReadPointer;\r
+\r
+ handle->xrun[1] = true;\r
+ // Two cases:\r
+ // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
+ // and perform fine adjustments later.\r
+ // - small adjustments: back off by twice as much.\r
+ if ( adjustment >= 2*bufferBytes )\r
+ nextReadPointer = safeReadPointer-2*bufferBytes;\r
+ else\r
+ nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
+\r
+ if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
+\r
+ }\r
+ else {\r
+ // In pre=roll time. Just do it.\r
+ nextReadPointer = safeReadPointer - bufferBytes;\r
+ while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
+ }\r
+ endRead = nextReadPointer + bufferBytes;\r
+ }\r
+ }\r
+ else { // mode == INPUT\r
+ while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
+ // See comments for playback.\r
+ double millis = (endRead - safeReadPointer) * 1000.0;\r
+ millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
+ if ( millis < 1.0 ) millis = 1.0;\r
+ Sleep( (DWORD) millis );\r
+\r
+ // Wake up and find out where we are now.\r
+ result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ \r
+ if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
+ }\r
+ }\r
+\r
+ // Lock free space in the buffer\r
+ result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
+ &bufferSize1, &buffer2, &bufferSize2, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ if ( duplexPrerollBytes <= 0 ) {\r
+ // Copy our buffer into the DS buffer\r
+ CopyMemory( buffer, buffer1, bufferSize1 );\r
+ if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
+ }\r
+ else {\r
+ memset( buffer, 0, bufferSize1 );\r
+ if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
+ duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
+ }\r
+\r
+ // Update our buffer offset and unlock sound buffer\r
+ nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
+ dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ handle->bufferPointer[1] = nextReadPointer;\r
+\r
+ // No byte swapping necessary in DirectSound implementation.\r
+\r
+ // If necessary, convert 8-bit data from unsigned to signed.\r
+ if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
+ for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
+\r
+ // Do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[1] )\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+ }\r
+\r
+ unlock:\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ RtApi::tickStreamTime();\r
+}\r
+\r
+// Definitions for utility functions and callbacks\r
+// specific to the DirectSound implementation.\r
+\r
+static unsigned __stdcall callbackHandler( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiDs *object = (RtApiDs *) info->object;\r
+ bool* isRunning = &info->isRunning;\r
+\r
+ while ( *isRunning == true ) {\r
+ object->callbackEvent();\r
+ }\r
+\r
+ _endthreadex( 0 );\r
+ return 0;\r
+}\r
+\r
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
+ LPCTSTR description,\r
+ LPCTSTR /*module*/,\r
+ LPVOID lpContext )\r
+{\r
+ struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
+ std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
+\r
+ HRESULT hr;\r
+ bool validDevice = false;\r
+ if ( probeInfo.isInput == true ) {\r
+ DSCCAPS caps;\r
+ LPDIRECTSOUNDCAPTURE object;\r
+\r
+ hr = DirectSoundCaptureCreate( lpguid, &object, NULL );\r
+ if ( hr != DS_OK ) return TRUE;\r
+\r
+ caps.dwSize = sizeof(caps);\r
+ hr = object->GetCaps( &caps );\r
+ if ( hr == DS_OK ) {\r
+ if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
+ validDevice = true;\r
+ }\r
+ object->Release();\r
+ }\r
+ else {\r
+ DSCAPS caps;\r
+ LPDIRECTSOUND object;\r
+ hr = DirectSoundCreate( lpguid, &object, NULL );\r
+ if ( hr != DS_OK ) return TRUE;\r
+\r
+ caps.dwSize = sizeof(caps);\r
+ hr = object->GetCaps( &caps );\r
+ if ( hr == DS_OK ) {\r
+ if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
+ validDevice = true;\r
+ }\r
+ object->Release();\r
+ }\r
+\r
+ // If good device, then save its name and guid.\r
+ std::string name = convertCharPointerToStdString( description );\r
+ //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
+ if ( lpguid == NULL )\r
+ name = "Default Device";\r
+ if ( validDevice ) {\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
+ if ( dsDevices[i].name == name ) {\r
+ dsDevices[i].found = true;\r
+ if ( probeInfo.isInput ) {\r
+ dsDevices[i].id[1] = lpguid;\r
+ dsDevices[i].validId[1] = true;\r
+ }\r
+ else {\r
+ dsDevices[i].id[0] = lpguid;\r
+ dsDevices[i].validId[0] = true;\r
+ }\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ DsDevice device;\r
+ device.name = name;\r
+ device.found = true;\r
+ if ( probeInfo.isInput ) {\r
+ device.id[1] = lpguid;\r
+ device.validId[1] = true;\r
+ }\r
+ else {\r
+ device.id[0] = lpguid;\r
+ device.validId[0] = true;\r
+ }\r
+ dsDevices.push_back( device );\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+static const char* getErrorString( int code )\r
+{\r
+ switch ( code ) {\r
+\r
+ case DSERR_ALLOCATED:\r
+ return "Already allocated";\r
+\r
+ case DSERR_CONTROLUNAVAIL:\r
+ return "Control unavailable";\r
+\r
+ case DSERR_INVALIDPARAM:\r
+ return "Invalid parameter";\r
+\r
+ case DSERR_INVALIDCALL:\r
+ return "Invalid call";\r
+\r
+ case DSERR_GENERIC:\r
+ return "Generic error";\r
+\r
+ case DSERR_PRIOLEVELNEEDED:\r
+ return "Priority level needed";\r
+\r
+ case DSERR_OUTOFMEMORY:\r
+ return "Out of memory";\r
+\r
+ case DSERR_BADFORMAT:\r
+ return "The sample rate or the channel format is not supported";\r
+\r
+ case DSERR_UNSUPPORTED:\r
+ return "Not supported";\r
+\r
+ case DSERR_NODRIVER:\r
+ return "No driver";\r
+\r
+ case DSERR_ALREADYINITIALIZED:\r
+ return "Already initialized";\r
+\r
+ case DSERR_NOAGGREGATION:\r
+ return "No aggregation";\r
+\r
+ case DSERR_BUFFERLOST:\r
+ return "Buffer lost";\r
+\r
+ case DSERR_OTHERAPPHASPRIO:\r
+ return "Another application already has priority";\r
+\r
+ case DSERR_UNINITIALIZED:\r
+ return "Uninitialized";\r
+\r
+ default:\r
+ return "DirectSound unknown error";\r
+ }\r
+}\r
+//******************** End of __WINDOWS_DS__ *********************//\r
+#endif\r
+\r
+\r
+#if defined(__LINUX_ALSA__)\r
+\r
+#include <alsa/asoundlib.h>\r
+#include <unistd.h>\r
+\r
+ // A structure to hold various information related to the ALSA API\r
+ // implementation.\r
+struct AlsaHandle {\r
+ snd_pcm_t *handles[2];\r
+ bool synchronized;\r
+ bool xrun[2];\r
+ pthread_cond_t runnable_cv;\r
+ bool runnable;\r
+\r
+ AlsaHandle()\r
+ :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
+};\r
+\r
+static void *alsaCallbackHandler( void * ptr );\r
+\r
+RtApiAlsa :: RtApiAlsa()\r
+{\r
+ // Nothing to do here.\r
+}\r
+\r
+RtApiAlsa :: ~RtApiAlsa()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+unsigned int RtApiAlsa :: getDeviceCount( void )\r
+{\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *handle;\r
+\r
+ // Count cards and devices\r
+ card = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &handle, name, 0 );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto nextcard;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( handle, &subdevice );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ break;\r
+ }\r
+ if ( subdevice < 0 )\r
+ break;\r
+ nDevices++;\r
+ }\r
+ nextcard:\r
+ snd_ctl_close( handle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &handle, "default", 0 );\r
+ if (result == 0) {\r
+ nDevices++;\r
+ snd_ctl_close( handle );\r
+ }\r
+\r
+ return nDevices;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *chandle;\r
+\r
+ // Count cards and devices\r
+ card = -1;\r
+ subdevice = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto nextcard;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ break;\r
+ }\r
+ if ( subdevice < 0 ) break;\r
+ if ( nDevices == device ) {\r
+ sprintf( name, "hw:%d,%d", card, subdevice );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+ nextcard:\r
+ snd_ctl_close( chandle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+ if ( result == 0 ) {\r
+ if ( nDevices == device ) {\r
+ strcpy( name, "default" );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+\r
+ if ( nDevices == 0 ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ foundDevice:\r
+\r
+ // If a stream is already open, we cannot probe the stream devices.\r
+ // Thus, use the saved results.\r
+ if ( stream_.state != STREAM_CLOSED &&\r
+ ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
+ snd_ctl_close( chandle );\r
+ if ( device >= devices_.size() ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+ return devices_[ device ];\r
+ }\r
+\r
+ int openMode = SND_PCM_ASYNC;\r
+ snd_pcm_stream_t stream;\r
+ snd_pcm_info_t *pcminfo;\r
+ snd_pcm_info_alloca( &pcminfo );\r
+ snd_pcm_t *phandle;\r
+ snd_pcm_hw_params_t *params;\r
+ snd_pcm_hw_params_alloca( ¶ms );\r
+\r
+ // First try for playback unless default device (which has subdev -1)\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+ if ( subdevice != -1 ) {\r
+ snd_pcm_info_set_device( pcminfo, subdevice );\r
+ snd_pcm_info_set_subdevice( pcminfo, 0 );\r
+\r
+ result = snd_ctl_pcm_info( chandle, pcminfo );\r
+ if ( result < 0 ) {\r
+ // Device probably doesn't support playback.\r
+ goto captureProbe;\r
+ }\r
+ }\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+\r
+ // Get output channel information.\r
+ unsigned int value;\r
+ result = snd_pcm_hw_params_get_channels_max( params, &value );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+ info.outputChannels = value;\r
+ snd_pcm_close( phandle );\r
+\r
+ captureProbe:\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+\r
+ // Now try for capture unless default device (with subdev = -1)\r
+ if ( subdevice != -1 ) {\r
+ result = snd_ctl_pcm_info( chandle, pcminfo );\r
+ snd_ctl_close( chandle );\r
+ if ( result < 0 ) {\r
+ // Device probably doesn't support capture.\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+ }\r
+ else\r
+ snd_ctl_close( chandle );\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+\r
+ result = snd_pcm_hw_params_get_channels_max( params, &value );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+ info.inputChannels = value;\r
+ snd_pcm_close( phandle );\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ // ALSA doesn't provide default devices so we'll use the first available one.\r
+ if ( device == 0 && info.outputChannels > 0 )\r
+ info.isDefaultOutput = true;\r
+ if ( device == 0 && info.inputChannels > 0 )\r
+ info.isDefaultInput = true;\r
+\r
+ probeParameters:\r
+ // At this point, we just need to figure out the supported data\r
+ // formats and sample rates. We'll proceed by opening the device in\r
+ // the direction with the maximum number of channels, or playback if\r
+ // they are equal. This might limit our sample rate options, but so\r
+ // be it.\r
+\r
+ if ( info.outputChannels >= info.inputChannels )\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ else\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Test our discrete set of sample rate values.\r
+ info.sampleRates.clear();\r
+ for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
+ if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {\r
+ info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+\r
+ if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
+ info.preferredSampleRate = SAMPLE_RATES[i];\r
+ }\r
+ }\r
+ if ( info.sampleRates.size() == 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Probe the supported data formats ... we don't care about endian-ness just yet\r
+ snd_pcm_format_t format;\r
+ info.nativeFormats = 0;\r
+ format = SND_PCM_FORMAT_S8;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT8;\r
+ format = SND_PCM_FORMAT_S16;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT16;\r
+ format = SND_PCM_FORMAT_S24;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT24;\r
+ format = SND_PCM_FORMAT_S32;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT32;\r
+ format = SND_PCM_FORMAT_FLOAT;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_FLOAT32;\r
+ format = SND_PCM_FORMAT_FLOAT64;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_FLOAT64;\r
+\r
+ // Check that we have at least one supported format\r
+ if ( info.nativeFormats == 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get the device name\r
+ char *cardname;\r
+ result = snd_card_get_name( card, &cardname );\r
+ if ( result >= 0 ) {\r
+ sprintf( name, "hw:%s,%d", cardname, subdevice );\r
+ free( cardname );\r
+ }\r
+ info.name = name;\r
+\r
+ // That's all ... close the device and return\r
+ snd_pcm_close( phandle );\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+void RtApiAlsa :: saveDeviceInfo( void )\r
+{\r
+ devices_.clear();\r
+\r
+ unsigned int nDevices = getDeviceCount();\r
+ devices_.resize( nDevices );\r
+ for ( unsigned int i=0; i<nDevices; i++ )\r
+ devices_[i] = getDeviceInfo( i );\r
+}\r
+\r
+bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+\r
+{\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ snd_output_t *out;\r
+ snd_output_stdio_attach(&out, stderr, 0);\r
+#endif\r
+\r
+ // I'm not using the "plug" interface ... too much inconsistent behavior.\r
+\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *chandle;\r
+\r
+ if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
+ snprintf(name, sizeof(name), "%s", "default");\r
+ else {\r
+ // Count cards and devices\r
+ card = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
+ if ( result < 0 ) break;\r
+ if ( subdevice < 0 ) break;\r
+ if ( nDevices == device ) {\r
+ sprintf( name, "hw:%d,%d", card, subdevice );\r
+ snd_ctl_close( chandle );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+ snd_ctl_close( chandle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+ if ( result == 0 ) {\r
+ if ( nDevices == device ) {\r
+ strcpy( name, "default" );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ foundDevice:\r
+\r
+ // The getDeviceInfo() function will not work for a device that is\r
+ // already open. Thus, we'll probe the system before opening a\r
+ // stream and save the results for use by getDeviceInfo().\r
+ if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
+ this->saveDeviceInfo();\r
+\r
+ snd_pcm_stream_t stream;\r
+ if ( mode == OUTPUT )\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ else\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+\r
+ snd_pcm_t *phandle;\r
+ int openMode = SND_PCM_ASYNC;\r
+ result = snd_pcm_open( &phandle, name, stream, openMode );\r
+ if ( result < 0 ) {\r
+ if ( mode == OUTPUT )\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
+ else\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Fill the parameter structure.\r
+ snd_pcm_hw_params_t *hw_params;\r
+ snd_pcm_hw_params_alloca( &hw_params );\r
+ result = snd_pcm_hw_params_any( phandle, hw_params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
+ snd_pcm_hw_params_dump( hw_params, out );\r
+#endif\r
+\r
+ // Set access ... check user preference.\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
+ stream_.userInterleaved = false;\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
+ if ( result < 0 ) {\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
+ stream_.deviceInterleaved[mode] = true;\r
+ }\r
+ else\r
+ stream_.deviceInterleaved[mode] = false;\r
+ }\r
+ else {\r
+ stream_.userInterleaved = true;\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
+ if ( result < 0 ) {\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
+ stream_.deviceInterleaved[mode] = false;\r
+ }\r
+ else\r
+ stream_.deviceInterleaved[mode] = true;\r
+ }\r
+\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Determine how to set the device format.\r
+ stream_.userFormat = format;\r
+ snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
+\r
+ if ( format == RTAUDIO_SINT8 )\r
+ deviceFormat = SND_PCM_FORMAT_S8;\r
+ else if ( format == RTAUDIO_SINT16 )\r
+ deviceFormat = SND_PCM_FORMAT_S16;\r
+ else if ( format == RTAUDIO_SINT24 )\r
+ deviceFormat = SND_PCM_FORMAT_S24;\r
+ else if ( format == RTAUDIO_SINT32 )\r
+ deviceFormat = SND_PCM_FORMAT_S32;\r
+ else if ( format == RTAUDIO_FLOAT32 )\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT;\r
+ else if ( format == RTAUDIO_FLOAT64 )\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
+\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
+ stream_.deviceFormat[mode] = format;\r
+ goto setFormat;\r
+ }\r
+\r
+ // The user requested format is not natively supported by the device.\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
+ if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S32;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S24;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S16;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S8;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ goto setFormat;\r
+ }\r
+\r
+ // If we get here, no supported format was found.\r
+ snd_pcm_close( phandle );\r