diff options
| author | Gary Scavone <gary@music.mcgill.ca> | 2010-02-04 18:55:39 +0000 |
|---|---|---|
| committer | Stephen Sinclair <sinclair@music.mcgill.ca> | 2013-10-11 01:38:26 +0200 |
| commit | 1aeb3051b91d55cb23e15d5b03fe71a7cb447860 (patch) | |
| tree | fd9a51a51dd398488a4ca9b67645bbc9e7547443 /RtAudio.cpp | |
| parent | caedd756a73367c72e8c2c64c5554419d2168b97 (diff) | |
Many updates for release 4.0.7 (gs).
Diffstat (limited to 'RtAudio.cpp')
| -rw-r--r-- | RtAudio.cpp | 965 |
1 files changed, 489 insertions, 476 deletions
diff --git a/RtAudio.cpp b/RtAudio.cpp index 8d47585..78259e9 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -10,7 +10,7 @@ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2009 Gary P. Scavone + Copyright (c) 2001-2010 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -38,13 +38,13 @@ */ /************************************************************************/ -// RtAudio: Version 4.0.6 +// RtAudio: Version 4.0.7 #include "RtAudio.h" #include <iostream> #include <cstdlib> #include <cstring> -#include <limits.h> +#include <climits> // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; @@ -435,7 +435,8 @@ unsigned int RtApiCore :: getDeviceCount( void ) { // Find out how many audio devices there are, if any. UInt32 dataSize; - OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL ); + AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!"; error( RtError::WARNING ); @@ -452,9 +453,8 @@ unsigned int RtApiCore :: getDefaultInputDevice( void ) AudioDeviceID id; UInt32 dataSize = sizeof( AudioDeviceID ); - OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, - &dataSize, &id ); - + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device."; error( RtError::WARNING ); @@ -463,7 +463,8 @@ unsigned int RtApiCore :: getDefaultInputDevice( void ) dataSize *= nDevices; AudioDeviceID deviceList[ nDevices ]; - result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList ); + property.mSelector = kAudioHardwarePropertyDevices; + result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs."; error( RtError::WARNING ); @@ -485,18 +486,18 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void ) AudioDeviceID id; UInt32 dataSize = sizeof( AudioDeviceID ); - OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, - &dataSize, &id ); - + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device."; error( RtError::WARNING ); return 0; } - dataSize *= nDevices; + dataSize = sizeof( AudioDeviceID ) * nDevices; AudioDeviceID deviceList[ nDevices ]; - result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList ); + property.mSelector = kAudioHardwarePropertyDevices; + result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs."; error( RtError::WARNING ); @@ -530,7 +531,11 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) AudioDeviceID deviceList[ nDevices ]; UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; - OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList ); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, + 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs."; error( RtError::WARNING ); @@ -541,39 +546,43 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) // Get the device name. info.name.erase(); - char name[256]; - dataSize = 256; - result = AudioDeviceGetProperty( id, 0, false, - kAudioDevicePropertyDeviceManufacturer, - &dataSize, name ); - + CFStringRef cfname; + dataSize = sizeof( CFStringRef ); + property.mSelector = kAudioObjectPropertyManufacturer; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer."; errorText_ = errorStream_.str(); error( RtError::WARNING ); return info; } - info.name.append( (const char *)name, strlen(name) ); + + const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); + info.name.append( (const char *)mname, strlen(mname) ); info.name.append( ": " ); + CFRelease( cfname ); - dataSize = 256; - result = AudioDeviceGetProperty( id, 0, false, - kAudioDevicePropertyDeviceName, - &dataSize, name ); + property.mSelector = kAudioObjectPropertyName; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name."; errorText_ = errorStream_.str(); error( RtError::WARNING ); return info; } + + const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); info.name.append( (const char *)name, strlen(name) ); + CFRelease( cfname ); // Get the output stream "configuration". AudioBufferList *bufferList = nil; - result = AudioDeviceGetPropertyInfo( id, 0, false, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - if (result != noErr || dataSize == 0) { + property.mSelector = kAudioDevicePropertyStreamConfiguration; + property.mScope = kAudioDevicePropertyScopeOutput; + // property.mElement = kAudioObjectPropertyElementWildcard; + dataSize = 0; + result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); + if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtError::WARNING ); @@ -588,10 +597,8 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) return info; } - result = AudioDeviceGetProperty( id, 0, false, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - if ( result != noErr ) { + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); + if ( result != noErr || dataSize == 0 ) { free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -606,10 +613,9 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) free( bufferList ); // Get the input stream "configuration". - result = AudioDeviceGetPropertyInfo( id, 0, true, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - if (result != noErr || dataSize == 0) { + property.mScope = kAudioDevicePropertyScopeInput; + result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); + if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtError::WARNING ); @@ -624,10 +630,8 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) return info; } - result = AudioDeviceGetProperty( id, 0, true, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - if ( result != noErr ) { + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); + if (result != noErr || dataSize == 0) { free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -650,10 +654,9 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( info.outputChannels == 0 ) isInput = true; // Determine the supported sample rates. - result = AudioDeviceGetPropertyInfo( id, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &dataSize, NULL ); - + property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput; + result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != kAudioHardwareNoError || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info."; errorText_ = errorStream_.str(); @@ -663,10 +666,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) UInt32 nRanges = dataSize / sizeof( AudioValueRange ); AudioValueRange rangeList[ nRanges ]; - result = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &dataSize, &rangeList ); - + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList ); if ( result != kAudioHardwareNoError ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates."; errorText_ = errorStream_.str(); @@ -698,10 +698,10 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) // no interest to the client. info.nativeFormats = RTAUDIO_FLOAT32; - if ( getDefaultOutputDevice() == device ) - info.isDefaultOutput = true; - if ( getDefaultInputDevice() == device ) - info.isDefaultInput = true; + if ( info.outputChannels > 0 ) + if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; + if ( info.inputChannels > 0 ) + if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; info.probed = true; return info; @@ -724,29 +724,24 @@ OSStatus callbackHandler( AudioDeviceID inDevice, return kAudioHardwareNoError; } -OSStatus deviceListener( AudioDeviceID inDevice, - UInt32 channel, - Boolean isInput, - AudioDevicePropertyID propertyID, +OSStatus deviceListener( AudioObjectID inDevice, + UInt32 nAddresses, + const AudioObjectPropertyAddress properties[], void* handlePointer ) { CoreHandle *handle = (CoreHandle *) handlePointer; - if ( propertyID == kAudioDeviceProcessorOverload ) { - if ( isInput ) - handle->xrun[1] = true; - else - handle->xrun[0] = true; + for ( UInt32 i=0; i<nAddresses; i++ ) { + if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) { + if ( properties[i].mScope == kAudioDevicePropertyScopeInput ) + handle->xrun[1] = true; + else + handle->xrun[0] = true; + } } return kAudioHardwareNoError; } -static bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property ) -{ - OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL ); - return result == 0; -} - bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, @@ -768,7 +763,11 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne AudioDeviceID deviceList[ nDevices ]; UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; - OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList ); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, + 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs."; return FAILURE; @@ -778,25 +777,19 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // Setup for stream mode. bool isInput = false; - if ( mode == INPUT ) isInput = true; - - // Set or disable "hog" mode. - dataSize = sizeof( UInt32 ); - UInt32 doHog = 0; - if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1; - result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!"; - errorText_ = errorStream_.str(); - return FAILURE; + if ( mode == INPUT ) { + isInput = true; + property.mScope = kAudioDevicePropertyScopeInput; } + else + property.mScope = kAudioDevicePropertyScopeOutput; // Get the stream "configuration". - AudioBufferList *bufferList; - result = AudioDeviceGetPropertyInfo( id, 0, isInput, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - if (result != noErr || dataSize == 0) { + AudioBufferList *bufferList = nil; + dataSize = 0; + property.mSelector = kAudioDevicePropertyStreamConfiguration; + result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); + if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; @@ -809,11 +802,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne return FAILURE; } - result = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - if ( result != noErr ) { - free( bufferList ); + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); + if (result != noErr || dataSize == 0) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; @@ -888,9 +878,9 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // Determine the buffer size. AudioValueRange bufferRange; dataSize = sizeof( AudioValueRange ); - result = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyBufferFrameSizeRange, - &dataSize, &bufferRange ); + property.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange ); + if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -905,9 +895,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // need to make this setting for the master channel. UInt32 theSize = (UInt32) *bufferSize; dataSize = sizeof( UInt32 ); - result = AudioDeviceSetProperty( id, NULL, 0, isInput, - kAudioDevicePropertyBufferFrameSize, - dataSize, &theSize ); + property.mSelector = kAudioDevicePropertyBufferFrameSize; + result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ")."; @@ -927,30 +916,75 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; - // Get the stream ID(s) so we can set the stream format. We'll have - // to do this for each stream. + // Check and if necessary, change the sample rate for the device. + Float64 nominalRate; + dataSize = sizeof( Float64 ); + property.mSelector = kAudioDevicePropertyNominalSampleRate; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate ); + + if ( result != noErr ) { + errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // Only change the sample rate if off by more than 1 Hz. + if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) { + nominalRate = (Float64) sampleRate; + result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate ); + + if ( result != noErr ) { + errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } + } + + // Try to set "hog" mode ... it's not clear to me this is working. + if ( options && options->flags & RTAUDIO_HOG_DEVICE ) { + pid_t hog_pid; + dataSize = sizeof( hog_pid ); + property.mSelector = kAudioDevicePropertyHogMode; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid ); + if ( result != noErr ) { + errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!"; + errorText_ = errorStream_.str(); + return FAILURE; + } + + if ( hog_pid != getpid() ) { + hog_pid = getpid(); + result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid ); + if ( result != noErr ) { + errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!"; + errorText_ = errorStream_.str(); + return FAILURE; + } + } + } + + // Get the stream ID(s) so we can set the stream format. AudioStreamID streamIDs[ nStreams ]; dataSize = nStreams * sizeof( AudioStreamID ); - result = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyStreams, - &dataSize, &streamIDs ); + property.mSelector = kAudioDevicePropertyStreams; + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &streamIDs ); + if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } - // Now set the stream format. Also, check the physical format of the - // device and change that if necessary. + // Now set the stream format for each stream. Also, check the + // physical format of the device and change that if necessary. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); bool updateFormat; for ( UInt32 i=0; i<streamCount; i++ ) { - result = AudioStreamGetProperty( streamIDs[firstStream+i], 0, - kAudioStreamPropertyVirtualFormat, - &dataSize, &description ); + property.mSelector = kAudioStreamPropertyVirtualFormat; + result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ")."; @@ -973,9 +1007,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } if ( updateFormat ) { - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, - kAudioStreamPropertyVirtualFormat, - dataSize, &description ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -984,9 +1016,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } // Now check the physical format. - result = AudioStreamGetProperty( streamIDs[firstStream+i], 0, - kAudioStreamPropertyPhysicalFormat, - &dataSize, &description ); + property.mSelector = kAudioStreamPropertyPhysicalFormat; + result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -1000,34 +1031,44 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // We'll try higher bit rates first and then work our way down. testDescription.mBitsPerChannel = 32; + testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame; + testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket; formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger; testDescription.mFormatFlags = formatFlags; - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription ); if ( result == noErr ) continue; testDescription = description; testDescription.mBitsPerChannel = 32; + testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame; + testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket; formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat; testDescription.mFormatFlags = formatFlags; - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription ); if ( result == noErr ) continue; testDescription = description; testDescription.mBitsPerChannel = 24; + testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame; + testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket; testDescription.mFormatFlags = formatFlags; - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription ); if ( result == noErr ) continue; testDescription = description; testDescription.mBitsPerChannel = 16; + testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame; + testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket; testDescription.mFormatFlags = formatFlags; - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription ); if ( result == noErr ) continue; testDescription = description; testDescription.mBitsPerChannel = 8; + testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame; + testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket; testDescription.mFormatFlags = formatFlags; - result = AudioStreamSetProperty( streamIDs[firstStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription ); + result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -1036,17 +1077,17 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } } + // Get the stream latency. There can be latency in both the device // and the stream. First, attempt to get the device latency on the // master channel or the first open channel. Errors that might // occur here are not deemed critical. - // ***** CHECK THIS ***** // - UInt32 latency, channel = 0; + UInt32 latency; dataSize = sizeof( UInt32 ); - AudioDevicePropertyID property = kAudioDevicePropertyLatency; - if ( hasProperty( id, channel, isInput, property ) == true ) { - result = AudioDeviceGetProperty( id, channel, isInput, property, &dataSize, &latency ); + property.mSelector = kAudioDevicePropertyLatency; + if ( AudioObjectHasProperty( id, &property ) == true ) { + result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency ); if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency; else { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ")."; @@ -1057,7 +1098,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // Now try to get the stream latency. For multiple streams, I assume the // latency is equal for each. - result = AudioStreamGetProperty( streamIDs[firstStream], 0, property, &dataSize, &latency ); + result = AudioObjectGetPropertyData( streamIDs[firstStream], &property, 0, NULL, &dataSize, &latency ); if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency; else { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ")."; @@ -1190,9 +1231,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } // Setup the device property listener for over/underload. - result = AudioDeviceAddPropertyListener( id, 0, isInput, - kAudioDeviceProcessorOverload, - deviceListener, (void *) handle ); + property.mSelector = kAudioDeviceProcessorOverload; + result = AudioObjectAddPropertyListener( id, &property, deviceListener, (void *) handle ); return SUCCESS; @@ -1340,7 +1380,9 @@ 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(); @@ -1720,6 +1762,7 @@ const char* RtApiCore :: getErrorCode( OSStatus code ) #include <jack/jack.h> #include <unistd.h> +#include <cstdio> // A structure to hold various information related to the Jack API // implementation. @@ -1736,6 +1779,7 @@ struct JackHandle { :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; } }; +ThreadHandle threadId; void jackSilentError( const char * ) {}; RtApiJack :: RtApiJack() @@ -1884,6 +1928,19 @@ int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) return 0; } +// This function will be called by a spawned thread when the Jack +// server signals that it is shutting down. It is necessary to handle +// it this way because the jackShutdown() function must return before +// the jack_deactivate() function (in closeStream()) will return. +extern "C" void *jackCloseStream( void *ptr ) +{ + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiJack *object = (RtApiJack *) info->object; + + object->closeStream(); + + pthread_exit( NULL ); +} void jackShutdown( void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; @@ -1896,7 +1953,7 @@ void jackShutdown( void *infoPointer ) // other problem occurred and we should close the stream. if ( object->isStreamRunning() == false ) return; - object->closeStream(); + pthread_create( &threadId, NULL, jackCloseStream, info ); std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl; } @@ -2352,8 +2409,7 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { if ( handle->internalDrain == true ) { - ThreadHandle id; - pthread_create( &id, NULL, jackStopStream, info ); + pthread_create( &threadId, NULL, jackStopStream, info ); } else pthread_cond_signal( &handle->condition ); @@ -2630,10 +2686,10 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) info.nativeFormats |= RTAUDIO_FLOAT64; - if ( getDefaultOutputDevice() == device ) - info.isDefaultOutput = true; - if ( getDefaultInputDevice() == device ) - info.isDefaultInput = true; + if ( info.outputChannels > 0 ) + if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; + if ( info.inputChannels > 0 ) + if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; info.probed = true; drivers.removeCurrentDriver(); @@ -3413,13 +3469,14 @@ static const char* getAsioErrorString( ASIOError result ) // Modified by Robin Davies, October 2005 // - Improvements to DirectX pointer chasing. -// - Backdoor RtDsStatistics hook provides DirectX performance information. // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30. // - Auto-call CoInitialize for DSOUND and ASIO platforms. // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007 +// Changed device query structure for RtAudio 4.0.7, January 2010 #include <dsound.h> #include <assert.h> +#include <algorithm> #if defined(__MINGW32__) // missing from latest mingw winapi @@ -3435,14 +3492,6 @@ static const char* getAsioErrorString( ASIOError result ) #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually. #endif -static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) -{ - if ( laterPointer > earlierPointer ) - return laterPointer - earlierPointer; - else - return laterPointer - earlierPointer + bufferSize; -} - static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) { if ( pointer > bufferSize ) pointer -= bufferSize; @@ -3468,25 +3517,6 @@ struct DsHandle { :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; } }; -/* -RtApiDs::RtDsStatistics RtApiDs::statistics; - -// Provides a backdoor hook to monitor for DirectSound read overruns and write underruns. -RtApiDs::RtDsStatistics RtApiDs::getDsStatistics() -{ - RtDsStatistics s = statistics; - - // update the calculated fields. - if ( s.inputFrameSize != 0 ) - s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate; - - if ( s.outputFrameSize != 0 ) - s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate; - - return s; -} -*/ - // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, @@ -3494,23 +3524,22 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR module, LPVOID lpContext ); -static char* getErrorString( int code ); +static const char* getErrorString( int code ); extern "C" unsigned __stdcall callbackHandler( void *ptr ); -struct EnumInfo { - bool isInput; - bool getDefault; - bool findIndex; - unsigned int counter; - unsigned int index; - LPGUID id; +struct DsDevice { + LPGUID id[2]; + bool validId[2]; + bool found; std::string name; - EnumInfo() - : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {} + DsDevice() + : found(false) { validId[0] = false; validId[1] = false; } }; +std::vector< DsDevice > dsDevices; + RtApiDs :: RtApiDs() { // Dsound will run both-threaded. If CoInitialize fails, then just @@ -3526,105 +3555,82 @@ RtApiDs :: ~RtApiDs() if ( stream_.state != STREAM_CLOSED ) closeStream(); } -unsigned int RtApiDs :: getDefaultInputDevice( void ) +// The DirectSound default output is always the first device. +unsigned int RtApiDs :: getDefaultOutputDevice( void ) { - // Count output devices. - EnumInfo info; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!"; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); - return 0; - } - - // Now enumerate input devices until we find the id = NULL. - info.isInput = true; - info.getDefault = true; - result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!"; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); - return 0; - } - - if ( info.counter > 0 ) return info.counter - 1; return 0; } -unsigned int RtApiDs :: getDefaultOutputDevice( void ) +// The DirectSound default input is always the first input device, +// which is the first capture device enumerated. +unsigned int RtApiDs :: getDefaultInputDevice( void ) { - // Enumerate output devices until we find the id = NULL. - EnumInfo info; - info.getDefault = true; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!"; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); - return 0; - } - - if ( info.counter > 0 ) return info.counter - 1; return 0; } unsigned int RtApiDs :: getDeviceCount( void ) { - // Count DirectSound devices. - EnumInfo info; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info ); + // Set query flag for previously found devices to false, so that we + // can check for any devices that have disappeared. + for ( unsigned int i=0; i<dsDevices.size(); i++ ) + dsDevices[i].found = false; + + // Query DirectSound devices. + bool isInput = false; + HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); } - // Count DirectSoundCapture devices. - info.isInput = true; - result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info ); + // Query DirectSoundCapture devices. + isInput = true; + result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); } - return info.counter; + // Clean out any devices that may have disappeared. + std::vector< DsDevice > :: iterator it; + for ( it=dsDevices.begin(); it < dsDevices.end(); it++ ) + if ( it->found == false ) dsDevices.erase( it ); + + return dsDevices.size(); } RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) { - // Because DirectSound always enumerates input and output devices - // separately (and because we don't attempt to combine devices - // internally), none of our "devices" will ever be duplex. - RtAudio::DeviceInfo info; info.probed = false; - // Enumerate through devices to find the id (if it exists). Note - // that we have to do the output enumeration first, even if this is - // an input device, in order for the device counter to be correct. - EnumInfo dsinfo; - dsinfo.findIndex = true; - dsinfo.index = device; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!"; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); + if ( dsDevices.size() == 0 ) { + // Force a query of all devices + getDeviceCount(); + if ( dsDevices.size() == 0 ) { + errorText_ = "RtApiDs::getDeviceInfo: no devices found!"; + error( RtError::INVALID_USE ); + } } - if ( dsinfo.name.empty() ) goto probeInput; + if ( device >= dsDevices.size() ) { + errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!"; + error( RtError::INVALID_USE ); + } + + HRESULT result; + if ( dsDevices[ device ].validId[0] == false ) goto probeInput; LPDIRECTSOUND output; DSCAPS outCaps; - result = DirectSoundCreate( dsinfo.id, &output, NULL ); + result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); - return info; + goto probeInput; } outCaps.dwSize = sizeof( outCaps ); @@ -3634,7 +3640,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); - return info; + goto probeInput; } // Get output channel information. @@ -3657,28 +3663,18 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; - // Copy name and return. - info.name = dsinfo.name; - - info.probed = true; - return info; - - probeInput: - - dsinfo.isInput = true; - result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!"; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); + if ( dsDevices[ device ].validId[1] == false ) { + info.name = dsDevices[ device ].name; + info.probed = true; + return info; } - if ( dsinfo.name.empty() ) return info; + probeInput: LPDIRECTSOUNDCAPTURE input; - result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL ); + result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); return info; @@ -3689,7 +3685,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtError::WARNING ); return info; @@ -3699,6 +3695,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) info.inputChannels = inCaps.dwChannels; // Get sample rate and format information. + std::vector<unsigned int> rates; if ( inCaps.dwChannels == 2 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16; @@ -3710,16 +3707,16 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.sampleRates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 ); + if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 ); + if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 ); + if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 ); + if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.sampleRates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 ); + if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 ); + if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 ); + if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 ); + if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 ); } } else if ( inCaps.dwChannels == 1 ) { @@ -3733,16 +3730,16 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.sampleRates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 ); + if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 ); + if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 ); + if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 ); + if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.sampleRates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.push_back( 96000 ); + if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 ); + if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 ); + if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 ); + if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 ); } } else info.inputChannels = 0; // technically, this would be an error @@ -3751,11 +3748,28 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( info.inputChannels == 0 ) return info; - if ( getDefaultInputDevice() == device ) - info.isDefaultInput = true; + // Copy the supported rates to the info structure but avoid duplication. + bool found; + for ( unsigned int i=0; i<rates.size(); i++ ) { + found = false; + for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) { + if ( rates[i] == info.sampleRates[j] ) { + found = true; + break; + } + } + if ( found == false ) info.sampleRates.push_back( rates[i] ); + } + sort( info.sampleRates.begin(), info.sampleRates.end() ); + + // If device opens for both playback and capture, we determine the channels. + if ( info.outputChannels > 0 && info.inputChannels > 0 ) + info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; + + if ( device == 0 ) info.isDefaultInput = true; // Copy name and return. - info.name = dsinfo.name; + info.name = dsDevices[ device ].name; info.probed = true; return info; } @@ -3770,35 +3784,28 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned return FAILURE; } - // Enumerate through devices to find the id (if it exists). Note - // that we have to do the output enumeration first, even if this is - // an input device, in order for the device counter to be correct. - EnumInfo dsinfo; - dsinfo.findIndex = true; - dsinfo.index = device; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!"; - errorText_ = errorStream_.str(); + unsigned int nDevices = dsDevices.size(); + if ( nDevices == 0 ) { + // This should not happen because a check is made before this function is called. + errorText_ = "RtApiDs::probeDeviceOpen: no devices found!"; + return FAILURE; + } + + if ( device >= nDevices ) { + // This should not happen because a check is made before this function is called. + errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!"; return FAILURE; } if ( mode == OUTPUT ) { - if ( dsinfo.name.empty() ) { + if ( dsDevices[ device ].validId[0] == false ) { errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!"; errorText_ = errorStream_.str(); return FAILURE; } } else { // mode == INPUT - dsinfo.isInput = true; - HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - if ( dsinfo.name.empty() ) { + if ( dsDevices[ device ].validId[1] == false ) { errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!"; errorText_ = errorStream_.str(); return FAILURE; @@ -3809,9 +3816,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned // instead of GetForegroundWindow() is supposed to avoid problems // that occur when the application's window is not the foreground // window. Also, if the application window closes before the - // DirectSound buffer, DirectSound can crash. However, for console - // applications, no sound was produced when using GetDesktopWindow(). - HWND hWnd = GetForegroundWindow(); + // DirectSound buffer, DirectSound can crash. In the past, I had + // problems when using GetDesktopWindow() but it seems fine now + // (January 2010). I'll leave it commented here. + // HWND hWnd = GetForegroundWindow(); + HWND hWnd = GetDesktopWindow(); // Check the numberOfBuffers parameter and limit the lowest value to // two. This is a judgement call and a value of two is probably too @@ -3821,6 +3830,10 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2; if ( nBuffers < 2 ) nBuffers = 3; + // Check the lower range of the user-specified buffer size and set + // (arbitrarily) to a lower bound of 32. + if ( *bufferSize < 32 ) *bufferSize = 32; + // Create the wave format structure. The data format setting will // be determined later. WAVEFORMATEX waveFormat; @@ -3829,19 +3842,20 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned waveFormat.nChannels = channels + firstChannel; waveFormat.nSamplesPerSec = (unsigned long) sampleRate; - // Determine the device buffer size. By default, 32k, but we will - // grow it to make allowances for very large software buffer sizes. - DWORD dsBufferSize = 0; + // Determine the device buffer size. By default, we'll use the value + // defined above (32K), but we will grow it to make allowances for + // very large software buffer sizes. + DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;; DWORD dsPointerLeadTime = 0; - long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this void *ohandle = 0, *bhandle = 0; + HRESULT result; if ( mode == OUTPUT ) { LPDIRECTSOUND output; - result = DirectSoundCreate( dsinfo.id, &output, NULL ); + result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -3851,14 +3865,14 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = output->GetCaps( &outCaps ); if ( FAILED( result ) ) { output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Check channel information. if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback."; + errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback."; errorText_ = errorStream_.str(); return FAILURE; } @@ -3882,8 +3896,8 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. - while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes ) - bufferBytes *= 2; + while ( dsPointerLeadTime * 2U > dsBufferSize ) + dsBufferSize *= 2; // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes. // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); @@ -3891,7 +3905,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); if ( FAILED( result ) ) { output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -3910,7 +3924,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -3919,20 +3933,19 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = buffer->SetFormat( &waveFormat ); if ( FAILED( result ) ) { output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Setup the secondary DS buffer description. - dsBufferSize = (DWORD) bufferBytes; ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE ); // Force hardware mixing - bufferDescription.dwBufferBytes = bufferBytes; + bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Try to create the secondary DS buffer. If that doesn't work, @@ -3946,7 +3959,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -3959,21 +3972,21 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( FAILED( result ) ) { output->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } - bufferBytes = dsbcaps.dwBufferBytes; + dsBufferSize = dsbcaps.dwBufferBytes; // Lock the DS buffer LPVOID audioPtr; DWORD dataLen; - result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 ); + result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -3986,12 +3999,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( FAILED( result ) ) { output->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } - dsBufferSize = bufferBytes; ohandle = (void *) output; bhandle = (void *) buffer; } @@ -3999,9 +4011,9 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( mode == INPUT ) { LPDIRECTSOUNDCAPTURE input; - result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL ); + result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -4011,7 +4023,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -4055,17 +4067,16 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. - while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes ) - bufferBytes *= 2; + while ( dsPointerLeadTime * 2U > dsBufferSize ) + dsBufferSize *= 2; // Setup the secondary DS buffer description. - dsBufferSize = bufferBytes; DSCBUFFERDESC bufferDescription; ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSCBUFFERDESC ); bufferDescription.dwFlags = 0; bufferDescription.dwReserved = 0; - bufferDescription.dwBufferBytes = bufferBytes; + bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Create the capture buffer. @@ -4073,7 +4084,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { input->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -4085,21 +4096,26 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( FAILED( result ) ) { input->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } - bufferBytes = dscbcaps.dwBufferBytes; + dsBufferSize = dscbcaps.dwBufferBytes; + + // NOTE: We could have a problem here if this is a duplex stream + // and the play and capture hardware buffer sizes are different + // (I'm actually not sure if that is a problem or not). + // Currently, we are not verifying that. // Lock the capture buffer LPVOID audioPtr; DWORD dataLen; - result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 ); + result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } @@ -4112,12 +4128,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned if ( FAILED( result ) ) { input->Release(); buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!"; + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } - dsBufferSize = bufferBytes; ohandle = (void *) input; bhandle = (void *) buffer; } @@ -4143,7 +4158,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); + long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory."; @@ -4321,22 +4336,15 @@ void RtApiDs :: startStream() return; } - // Increase scheduler frequency on lesser windows (a side-effect of - // increasing timer accuracy). On greater windows (Win2K or later), - // this is already in effect. - MUTEX_LOCK( &stream_.mutex ); DsHandle *handle = (DsHandle *) stream_.apiHandle; + // Increase scheduler frequency on lesser windows (a side-effect of + // increasing timer accuracy). On greater windows (Win2K or later), + // this is already in effect. timeBeginPeriod( 1 ); - /* - memset( &statistics, 0, sizeof( statistics ) ); - statistics.sampleRate = stream_.sampleRate; - statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0]; - */ - buffersRolling = false; duplexPrerollBytes = 0; @@ -4347,7 +4355,6 @@ void RtApiDs :: startStream() HRESULT result = 0; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Play( 0, 0, DSBPLAY_LOOPING ); @@ -4359,7 +4366,6 @@ void RtApiDs :: startStream() } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; result = buffer->Start( DSCBSTART_LOOPING ); @@ -4504,7 +4510,7 @@ void RtApiDs :: abortStream() void RtApiDs :: callbackEvent() { if ( stream_.state == STREAM_STOPPED ) { - Sleep(50); // sleep 50 milliseconds + Sleep( 50 ); // sleep 50 milliseconds return; } @@ -4560,14 +4566,9 @@ void RtApiDs :: callbackEvent() } HRESULT result; - DWORD currentWritePos, safeWritePos; - DWORD currentReadPos, safeReadPos; - DWORD leadPos; - UINT nextWritePos; - -#ifdef GENERATE_DEBUG_LOG - DWORD writeTime, readTime; -#endif + DWORD currentWritePointer, safeWritePointer; + DWORD currentReadPointer, safeReadPointer; + UINT nextWritePointer; LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; @@ -4577,63 +4578,79 @@ void RtApiDs :: callbackEvent() char *buffer; long bufferBytes; - if ( stream_.mode == DUPLEX && !buffersRolling ) { - //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + if ( buffersRolling == false ) { + if ( stream_.mode == DUPLEX ) { + //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); - // It takes a while for the devices to get rolling. As a result, - // there's no guarantee that the capture and write device pointers - // will move in lockstep. Wait here for both devices to start - // rolling, and then set our buffer pointers accordingly. - // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 - // bytes later than the write buffer. + // It takes a while for the devices to get rolling. As a result, + // there's no guarantee that the capture and write device pointers + // will move in lockstep. Wait here for both devices to start + // rolling, and then set our buffer pointers accordingly. + // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 + // bytes later than the write buffer. - // Stub: a serious risk of having a pre-emptive scheduling round - // take place between the two GetCurrentPosition calls... but I'm - // really not sure how to solve the problem. Temporarily boost to - // Realtime priority, maybe; but I'm not sure what priority the - // DirectSound service threads run at. We *should* be roughly - // within a ms or so of correct. + // Stub: a serious risk of having a pre-emptive scheduling round + // take place between the two GetCurrentPosition calls... but I'm + // really not sure how to solve the problem. Temporarily boost to + // Realtime priority, maybe; but I'm not sure what priority the + // DirectSound service threads run at. We *should* be roughly + // within a ms or so of correct. - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; + LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - DWORD initialWritePos, initialSafeWritePos; - DWORD initialReadPos, initialSafeReadPos; + DWORD startSafeWritePointer, startSafeReadPointer; - result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); - } - result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); - } - while ( true ) { - result = dsWriteBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos ); + result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - result = dsCaptureBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos ); + result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break; - Sleep( 1 ); + while ( true ) { + result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; + errorText_ = errorStream_.str(); + error( RtError::SYSTEM_ERROR ); + } + result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; + errorText_ = errorStream_.str(); + error( RtError::SYSTEM_ERROR ); + } + if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; + Sleep( 1 ); + } + + //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + + handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; + if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; + handle->bufferPointer[1] = safeReadPointer; } + else if ( stream_.mode == OUTPUT ) { - //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + // Set the proper nextWritePosition after initial startup. + LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; + errorText_ = errorStream_.str(); + error( RtError::SYSTEM_ERROR ); + } + handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; + if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; + } buffersRolling = true; - handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] ); - handle->bufferPointer[1] = safeReadPos; } if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { @@ -4668,62 +4685,51 @@ void RtApiDs :: callbackEvent() for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 ); DWORD dsBufferSize = handle->dsBufferSize[0]; - nextWritePos = handle->bufferPointer[0]; + nextWritePointer = handle->bufferPointer[0]; - DWORD endWrite; + DWORD endWrite, leadPointer; while ( true ) { // Find out where the read and "safe write" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos ); + result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - leadPos = safeWritePos + handle->dsPointerLeadTime[0]; - if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize; - if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset - endWrite = nextWritePos + bufferBytes; + // We will copy our output buffer into the region between + // safeWritePointer and leadPointer. If leadPointer is not + // beyond the next endWrite position, wait until it is. + leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; + //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; + if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize; + if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset + endWrite = nextWritePointer + bufferBytes; // Check whether the entire write region is behind the play pointer. - if ( leadPos >= endWrite ) break; - - // If we are here, then we must wait until the play pointer gets - // beyond the write region. The approach here is to use the - // Sleep() function to suspend operation until safePos catches - // up. Calculate number of milliseconds to wait as: - // time = distance * (milliseconds/second) * fudgefactor / - // ((bytes/sample) * (samples/second)) - // A "fudgefactor" less than 1 is used because it was found - // that sleeping too long was MUCH worse than sleeping for - // several shorter periods. - double millis = ( endWrite - leadPos ) * 900.0; + if ( leadPointer >= endWrite ) break; + + // If we are here, then we must wait until the leadPointer advances + // beyond the end of our next write region. We use the + // Sleep() function to suspend operation until that happens. + double millis = ( endWrite - leadPointer ) * 1000.0; millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; - if ( millis > 50.0 ) { - static int nOverruns = 0; - ++nOverruns; - } Sleep( (DWORD) millis ); } - //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) { - // statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ); - //} - - if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize ) - || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) { + if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize ) + || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { // We've strayed into the forbidden zone ... resync the read pointer. - //++statistics.numberOfWriteUnderruns; handle->xrun[0] = true; - nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize; - while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize; - handle->bufferPointer[0] = nextWritePos; - endWrite = nextWritePos + bufferBytes; + nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; + if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize; + handle->bufferPointer[0] = nextWritePointer; + endWrite = nextWritePointer + bufferBytes; } // Lock free space in the buffer - result = dsBuffer->Lock( nextWritePos, bufferBytes, &buffer1, + result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; @@ -4742,8 +4748,8 @@ void RtApiDs :: callbackEvent() errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize; - handle->bufferPointer[0] = nextWritePos; + nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; + handle->bufferPointer[0] = nextWritePointer; if ( handle->drainCounter ) { handle->drainCounter++; @@ -4766,19 +4772,19 @@ void RtApiDs :: callbackEvent() } LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - long nextReadPos = handle->bufferPointer[1]; + long nextReadPointer = handle->bufferPointer[1]; DWORD dsBufferSize = handle->dsBufferSize[1]; // Find out where the write and "safe read" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos ); + result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset - DWORD endRead = nextReadPos + bufferBytes; + if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset + DWORD endRead = nextReadPointer + bufferBytes; // Handling depends on whether we are INPUT or DUPLEX. // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, @@ -4795,60 +4801,54 @@ void RtApiDs :: callbackEvent() // zeros from the read buffer while the pointers sync up. if ( stream_.mode == DUPLEX ) { - if ( safeReadPos < endRead ) { + if ( safeReadPointer < endRead ) { if ( duplexPrerollBytes <= 0 ) { // Pre-roll time over. Be more agressive. - int adjustment = endRead-safeReadPos; + int adjustment = endRead-safeReadPointer; handle->xrun[1] = true; - //++statistics.numberOfReadOverruns; // Two cases: // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, // and perform fine adjustments later. // - small adjustments: back off by twice as much. if ( adjustment >= 2*bufferBytes ) - nextReadPos = safeReadPos-2*bufferBytes; + nextReadPointer = safeReadPointer-2*bufferBytes; else - nextReadPos = safeReadPos-bufferBytes-adjustment; + nextReadPointer = safeReadPointer-bufferBytes-adjustment; - //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos; - //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize; - if ( nextReadPos < 0 ) nextReadPos += dsBufferSize; + if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } else { // In pre=roll time. Just do it. - nextReadPos = safeReadPos-bufferBytes; - while ( nextReadPos < 0 ) nextReadPos += dsBufferSize; + nextReadPointer = safeReadPointer - bufferBytes; + while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } - endRead = nextReadPos + bufferBytes; + endRead = nextReadPointer + bufferBytes; } } else { // mode == INPUT - while ( safeReadPos < endRead ) { + while ( safeReadPointer < endRead ) { // See comments for playback. - double millis = (endRead - safeReadPos) * 900.0; + double millis = (endRead - safeReadPointer) * 1000.0; millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); - // Wake up, find out where we are now - result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos ); + // Wake up and find out where we are now. + result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset + if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset } } - //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) ) - // statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ); - // Lock free space in the buffer - result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1, + result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; @@ -4868,14 +4868,14 @@ void RtApiDs :: callbackEvent() } // Update our buffer offset and unlock sound buffer - nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize; + nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize; dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorText_ = errorStream_.str(); error( RtError::SYSTEM_ERROR ); } - handle->bufferPointer[1] = nextReadPos; + handle->bufferPointer[1] = nextReadPointer; // No byte swapping necessary in DirectSound implementation. @@ -4887,20 +4887,6 @@ void RtApiDs :: callbackEvent() if ( stream_.doConvertBuffer[1] ) convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } -#ifdef GENERATE_DEBUG_LOG - if ( currentDebugLogEntry < debugLog.size() ) - { - TTickRecord &r = debugLog[currentDebugLogEntry++]; - r.currentReadPointer = currentReadPos; - r.safeReadPointer = safeReadPos; - r.currentWritePointer = currentWritePos; - r.safeWritePointer = safeWritePos; - r.readTime = readTime; - r.writeTime = writeTime; - r.nextReadPointer = handles[1].bufferPointer; - r.nextWritePointer = handles[0].bufferPointer; - } -#endif unlock: MUTEX_UNLOCK( &stream_.mutex ); @@ -4949,10 +4935,11 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR module, LPVOID lpContext ) { - EnumInfo *info = (EnumInfo *) lpContext; + bool *isInput = (bool *) lpContext; HRESULT hr; - if ( info->isInput == true ) { + bool validDevice = false; + if ( *isInput == true ) { DSCCAPS caps; LPDIRECTSOUNDCAPTURE object; @@ -4963,7 +4950,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwChannels > 0 && caps.dwFormats > 0 ) - info->counter++; + validDevice = true; } object->Release(); } @@ -4977,23 +4964,49 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO ) - info->counter++; + validDevice = true; } object->Release(); } - if ( info->getDefault && lpguid == NULL ) return FALSE; + // If good device, then save its name and guid. + std::string name = convertTChar( description ); + if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) + name = "Default Device"; + if ( validDevice ) { + for ( unsigned int i=0; i<dsDevices.size(); i++ ) { + if ( dsDevices[i].name == name ) { + dsDevices[i].found = true; + if ( *isInput ) { + dsDevices[i].id[1] = lpguid; + dsDevices[i].validId[1] = true; + } + else { + dsDevices[i].id[0] = lpguid; + dsDevices[i].validId[0] = true; + } + return TRUE; + } + } - if ( info->findIndex && info->counter > info->index ) { - info->id = lpguid; - info->name = convertTChar( description ); - return FALSE; + DsDevice device; + device.name = name; + device.found = true; + if ( *isInput ) { + device.id[1] = lpguid; + device.validId[1] = true; + } + else { + device.id[0] = lpguid; + device.validId[0] = true; + } + dsDevices.push_back( device ); } return TRUE; } -static char* getErrorString( int code ) +static const char* getErrorString( int code ) { switch ( code ) { |
