RtAudio provides a common API (Application Programming Interface)
for realtime audio input/output across Linux (native ALSA, Jack,
- and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows
+ and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
(DirectSound and ASIO) operating systems.
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes
- Copyright (c) 2001-2007 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
*/
/************************************************************************/
-// RtAudio: Version 4.0.3
+// RtAudio: Version 4.0.7
#include "RtAudio.h"
#include <iostream>
+#include <cstdlib>
+#include <cstring>
+#include <climits>
// Static variable definitions.
const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
#endif
}
+unsigned int RtApi :: getStreamSampleRate( void )
+{
+ verifyStream();
+
+ return stream_.sampleRate;
+}
+
// *************************************************** //
//
// implementation.
struct CoreHandle {
AudioDeviceID id[2]; // device ids
- UInt32 iStream[2]; // device stream index (first for mono mode)
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ AudioDeviceIOProcID procId[2];
+#endif
+ UInt32 iStream[2]; // device stream index (or first if using multiple)
+ UInt32 nStreams[2]; // number of streams to use
bool xrun[2];
char *deviceBuffer;
pthread_cond_t condition;
bool internalDrain; // Indicates if stop is initiated from callback or not.
CoreHandle()
- :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+ :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
};
RtApiCore :: RtApiCore()
{
// 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 );
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 );
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 );
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 );
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 );
// 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 );
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();
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 );
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();
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();
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();
// 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;
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,
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;
// 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;
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;
}
- // Search for a stream that contains the desired number of
+ // Search for one or more streams that contain the desired number of
// channels. CoreAudio devices can have an arbitrary number of
// streams and each stream can have an arbitrary number of channels.
// For each stream, a single buffer of interleaved samples is
- // provided. RtAudio currently only supports the use of one stream
- // of interleaved data or multiple consecutive single-channel
- // streams. Thus, our search below is limited to these two
- // contexts.
- unsigned int streamChannels = 0, nStreams = 0;
- UInt32 iChannel = 0, iStream = 0;
- unsigned int offsetCounter = firstChannel;
- stream_.deviceInterleaved[mode] = true;
- nStreams = bufferList->mNumberBuffers;
+ // provided. RtAudio prefers the use of one stream of interleaved
+ // data or multiple consecutive single-channel streams. However, we
+ // now support multiple consecutive multi-channel streams of
+ // interleaved data as well.
+ UInt32 iStream, offsetCounter = firstChannel;
+ UInt32 nStreams = bufferList->mNumberBuffers;
+ bool monoMode = false;
bool foundStream = false;
+ // First check that the device supports the requested number of
+ // channels.
+ UInt32 deviceChannels = 0;
+ for ( iStream=0; iStream<nStreams; iStream++ )
+ deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;
+
+ if ( deviceChannels < ( channels + firstChannel ) ) {
+ free( bufferList );
+ errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";
+ errorText_ = errorStream_.str();
+ return FAILURE;
+ }
+
+ // Look for a single stream meeting our needs.
+ UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
for ( iStream=0; iStream<nStreams; iStream++ ) {
streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
if ( streamChannels >= channels + offsetCounter ) {
- iChannel += offsetCounter;
+ firstStream = iStream;
+ channelOffset = offsetCounter;
foundStream = true;
break;
}
if ( streamChannels > offsetCounter ) break;
offsetCounter -= streamChannels;
- iChannel += streamChannels;
}
- // If we didn't find a single stream above, see if we can meet
- // the channel specification in mono mode (i.e. using separate
- // non-interleaved buffers). This can only work if there are N
- // consecutive one-channel streams, where N is the number of
- // desired channels (+ channel offset).
+ // If we didn't find a single stream above, then we should be able
+ // to meet the channel specification with multiple streams.
if ( foundStream == false ) {
- unsigned int counter = 0;
+ monoMode = true;
offsetCounter = firstChannel;
- iChannel = 0;
for ( iStream=0; iStream<nStreams; iStream++ ) {
streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
- if ( offsetCounter ) {
- if ( streamChannels > offsetCounter ) break;
- offsetCounter -= streamChannels;
- }
- else if ( streamChannels == 1 )
- counter++;
- else
- counter = 0;
- if ( counter == channels ) {
- iStream -= channels - 1;
- iChannel -= channels - 1;
- stream_.deviceInterleaved[mode] = false;
- foundStream = true;
- break;
- }
- iChannel += streamChannels;
+ if ( streamChannels > offsetCounter ) break;
+ offsetCounter -= streamChannels;
}
- }
- free( bufferList );
- if ( foundStream == false ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
- errorText_ = errorStream_.str();
- return FAILURE;
+ firstStream = iStream;
+ channelOffset = offsetCounter;
+ Int32 channelCounter = channels + offsetCounter - streamChannels;
+
+ if ( streamChannels > 1 ) monoMode = false;
+ while ( channelCounter > 0 ) {
+ streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
+ if ( streamChannels > 1 ) monoMode = false;
+ channelCounter -= streamChannels;
+ streamCount++;
+ }
}
+ free( bufferList );
+
// 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();
else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
- // Set the buffer size. For mono mode, I'm assuming we only need to
- // make this setting for the master channel.
+ // Set the buffer size. For multiple streams, I'm assuming we only
+ // 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 << ").";
stream_.bufferSize = *bufferSize;
stream_.nBuffers = 1;
- // Get the stream ID(s) so we can set the stream format. In mono
- // mode, we'll have to do this for each stream (channel).
+ // 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 );
- if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
- else nStreams = channels;
bool updateFormat;
- for ( unsigned int i=0; i<nStreams; i++ ) {
+ for ( UInt32 i=0; i<streamCount; i++ ) {
- result = AudioStreamGetProperty( streamIDs[iStream+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 << ").";
}
if ( updateFormat ) {
- result = AudioStreamSetProperty( streamIDs[iStream+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();
}
// Now check the physical format.
- result = AudioStreamGetProperty( streamIDs[iStream+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();
// 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[iStream+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[iStream+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[iStream+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[iStream+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[iStream+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();
}
}
+
// 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.
- UInt32 latency, channel = 0;
+
+ UInt32 latency;
dataSize = sizeof( UInt32 );
- AudioDevicePropertyID property = kAudioDevicePropertyLatency;
- for ( int i=0; i<2; i++ ) {
- if ( hasProperty( id, channel, isInput, property ) == true ) break;
- channel = iChannel + 1 + i;
- }
- if ( channel <= iChannel + 1 ) {
- 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 << ").";
}
}
- // Now try to get the stream latency. For "mono" mode, I assume the
- // latency is equal for all single-channel streams.
- result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
+ // Now try to get the stream latency. For multiple streams, I assume the
+ // latency is equal for each.
+ 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 << ").";
stream_.userFormat = format;
stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
- if ( stream_.deviceInterleaved[mode] )
+ if ( streamCount == 1 )
stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
- else // mono mode
+ else // multiple streams
stream_.nDeviceChannels[mode] = channels;
stream_.nUserChannels[mode] = channels;
- stream_.channelOffset[mode] = iChannel; // offset within a CoreAudio stream
+ stream_.channelOffset[mode] = channelOffset; // offset within a CoreAudio stream
if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
else stream_.userInterleaved = true;
+ stream_.deviceInterleaved[mode] = true;
+ if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;
// Set flags for buffer conversion.
stream_.doConvertBuffer[mode] = false;
stream_.doConvertBuffer[mode] = true;
if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
+ if ( streamCount == 1 ) {
+ if ( stream_.nUserChannels[mode] > 1 &&
+ stream_.userInterleaved != stream_.deviceInterleaved[mode] )
+ stream_.doConvertBuffer[mode] = true;
+ }
+ else if ( monoMode && stream_.userInterleaved )
stream_.doConvertBuffer[mode] = true;
// Allocate our CoreHandle structure for the stream.
}
else
handle = (CoreHandle *) stream_.apiHandle;
- handle->iStream[mode] = iStream;
+ handle->iStream[mode] = firstStream;
+ handle->nStreams[mode] = streamCount;
handle->id[mode] = id;
// Allocate necessary internal buffers.
- unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+ unsigned long bufferBytes;
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
if ( stream_.userBuffer[mode] == NULL ) {
errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
}
// If possible, we will make use of the CoreAudio stream buffers as
- // "device buffers". However, we can't do this if the device
- // buffers are non-interleaved ("mono" mode).
- if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) {
+ // "device buffers". However, we can't do this if using multiple
+ // streams.
+ if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
bool makeBuffer = true;
bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
goto error;
}
-
- // Save a pointer to our own device buffer in the CoreHandle
- // structure because we may need to use the stream_.deviceBuffer
- // variable to point to the CoreAudio buffer before buffer
- // conversion (if we have a duplex stream with two different
- // conversion schemes).
- handle->deviceBuffer = stream_.deviceBuffer;
}
}
stream_.state = STREAM_STOPPED;
stream_.callbackInfo.object = (void *) this;
- // Setup the buffer conversion information structure. We override
- // the channel offset value and perform our own setting for that
- // here.
+ // Setup the buffer conversion information structure.
if ( stream_.doConvertBuffer[mode] ) {
- setConvertInfo( mode, 0 );
-
- // Add channel offset for interleaved channels.
- if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) {
- if ( mode == OUTPUT ) {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].outOffset[k] += firstChannel;
- }
- else {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].inOffset[k] += firstChannel;
- }
- }
+ if ( streamCount > 1 ) setConvertInfo( mode, 0 );
+ else setConvertInfo( mode, channelOffset );
}
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
// Only one callback procedure per device.
stream_.mode = DUPLEX;
else {
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
+#else
+ // deprecated in favor of AudioDeviceCreateIOProcID()
result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
+#endif
if ( result != noErr ) {
errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
errorText_ = errorStream_.str();
}
// 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;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[0], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
+#else
+ // deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+#endif
}
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[1], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
+#else
+ // deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+#endif
}
for ( int i=0; i<2; i++ ) {
}
}
- if ( handle->deviceBuffer ) {
- free( handle->deviceBuffer );
+ if ( stream_.deviceBuffer ) {
+ free( stream_.deviceBuffer );
stream_.deviceBuffer = 0;
}
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
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();
}
}
+ stream_.state = STREAM_STOPPED;
+
unlock:
MUTEX_UNLOCK( &stream_.mutex );
- stream_.state = STREAM_STOPPED;
if ( result == noErr ) return;
error( RtError::SYSTEM_ERROR );
}
MUTEX_LOCK( &stream_.mutex );
+ // The state might change while waiting on a mutex.
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return SUCCESS;
+ }
+
AudioDeviceID outputDevice = handle->id[0];
// Invoke user callback to get fresh output data UNLESS we are
if ( handle->drainCounter > 1 ) { // write zeros to the output stream
- if ( stream_.deviceInterleaved[0] ) {
+ if ( handle->nStreams[0] == 1 ) {
memset( outBufferList->mBuffers[handle->iStream[0]].mData,
0,
outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
}
- else {
- for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+ else { // fill multiple streams with zeros
+ for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
0,
outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
}
}
}
- else if ( stream_.doConvertBuffer[0] ) {
-
- if ( stream_.deviceInterleaved[0] )
- stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
- else
- stream_.deviceBuffer = handle->deviceBuffer;
-
- convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
-
- if ( !stream_.deviceInterleaved[0] ) {
- UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
- for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
- memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
- &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
- }
+ else if ( handle->nStreams[0] == 1 ) {
+ if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer
+ convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,
+ stream_.userBuffer[0], stream_.convertInfo[0] );
}
-
- }
- else {
- if ( stream_.deviceInterleaved[0] ) {
+ else { // copy from user buffer
memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
stream_.userBuffer[0],
outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
}
- else {
+ }
+ else { // fill multiple streams
+ Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];
+ if ( stream_.doConvertBuffer[0] ) {
+ convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+ inBuffer = (Float32 *) stream_.deviceBuffer;
+ }
+
+ if ( stream_.deviceInterleaved[0] == false ) { // mono mode
UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
- for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+ for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
- &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
+ (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
+ }
+ }
+ else { // fill multiple multi-channel streams with interleaved data
+ UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;
+ Float32 *out, *in;
+
+ bool inInterleaved = ( stream_.userInterleaved ) ? true : false;
+ UInt32 inChannels = stream_.nUserChannels[0];
+ if ( stream_.doConvertBuffer[0] ) {
+ inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+ inChannels = stream_.nDeviceChannels[0];
+ }
+
+ if ( inInterleaved ) inOffset = 1;
+ else inOffset = stream_.bufferSize;
+
+ channelsLeft = inChannels;
+ for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
+ in = inBuffer;
+ out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;
+ streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
+
+ outJump = 0;
+ // Account for possible channel offset in first stream
+ if ( i == 0 && stream_.channelOffset[0] > 0 ) {
+ streamChannels -= stream_.channelOffset[0];
+ outJump = stream_.channelOffset[0];
+ out += outJump;
+ }
+
+ // Account for possible unfilled channels at end of the last stream
+ if ( streamChannels > channelsLeft ) {
+ outJump = streamChannels - channelsLeft;
+ streamChannels = channelsLeft;
+ }
+
+ // Determine input buffer offsets and skips
+ if ( inInterleaved ) {
+ inJump = inChannels;
+ in += inChannels - channelsLeft;
+ }
+ else {
+ inJump = 1;
+ in += (inChannels - channelsLeft) * inOffset;
+ }
+
+ for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+ for ( unsigned int j=0; j<streamChannels; j++ ) {
+ *out++ = in[j*inOffset];
+ }
+ out += outJump;
+ in += inJump;
+ }
+ channelsLeft -= streamChannels;
}
}
}
}
}
- AudioDeviceID inputDevice = handle->id[1];
+ AudioDeviceID inputDevice;
+ inputDevice = handle->id[1];
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
- if ( stream_.doConvertBuffer[1] ) {
+ if ( handle->nStreams[1] == 1 ) {
+ if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer
+ convertBuffer( stream_.userBuffer[1],
+ (char *) inBufferList->mBuffers[handle->iStream[1]].mData,
+ stream_.convertInfo[1] );
+ }
+ else { // copy to user buffer
+ memcpy( stream_.userBuffer[1],
+ inBufferList->mBuffers[handle->iStream[1]].mData,
+ inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+ }
+ }
+ else { // read from multiple streams
+ Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];
+ if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;
- if ( stream_.deviceInterleaved[1] )
- stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
- else {
- stream_.deviceBuffer = (char *) handle->deviceBuffer;
+ if ( stream_.deviceInterleaved[1] == false ) { // mono mode
UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
- for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
- memcpy( &stream_.deviceBuffer[i*bufferBytes],
+ for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+ memcpy( (void *)&outBuffer[i*stream_.bufferSize],
inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
}
}
+ else { // read from multiple multi-channel streams
+ UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;
+ Float32 *out, *in;
+
+ bool outInterleaved = ( stream_.userInterleaved ) ? true : false;
+ UInt32 outChannels = stream_.nUserChannels[1];
+ if ( stream_.doConvertBuffer[1] ) {
+ outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+ outChannels = stream_.nDeviceChannels[1];
+ }
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+ if ( outInterleaved ) outOffset = 1;
+ else outOffset = stream_.bufferSize;
+
+ channelsLeft = outChannels;
+ for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {
+ out = outBuffer;
+ in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;
+ streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
+
+ inJump = 0;
+ // Account for possible channel offset in first stream
+ if ( i == 0 && stream_.channelOffset[1] > 0 ) {
+ streamChannels -= stream_.channelOffset[1];
+ inJump = stream_.channelOffset[1];
+ in += inJump;
+ }
- }
- else {
- memcpy( stream_.userBuffer[1],
- inBufferList->mBuffers[handle->iStream[1]].mData,
- inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+ // Account for possible unread channels at end of the last stream
+ if ( streamChannels > channelsLeft ) {
+ inJump = streamChannels - channelsLeft;
+ streamChannels = channelsLeft;
+ }
+
+ // Determine output buffer offsets and skips
+ if ( outInterleaved ) {
+ outJump = outChannels;
+ out += outChannels - channelsLeft;
+ }
+ else {
+ outJump = 1;
+ out += (outChannels - channelsLeft) * outOffset;
+ }
+
+ for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+ for ( unsigned int j=0; j<streamChannels; j++ ) {
+ out[j*outOffset] = *in++;
+ }
+ out += outJump;
+ in += inJump;
+ }
+ channelsLeft -= streamChannels;
+ }
+ }
+
+ if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer
+ convertBuffer( stream_.userBuffer[1],
+ stream_.deviceBuffer,
+ stream_.convertInfo[1] );
+ }
}
}
const char* RtApiCore :: getErrorCode( OSStatus code )
{
- switch( code ) {
+ switch( code ) {
case kAudioHardwareNotRunningError:
return "kAudioHardwareNotRunningError";
default:
return "CoreAudio unknown error";
- }
+ }
}
-//******************** End of __MACOSX_CORE__ *********************//
+ //******************** End of __MACOSX_CORE__ *********************//
#endif
#if defined(__UNIX_JACK__)
#include <jack/jack.h>
#include <unistd.h>
+#include <cstdio>
// A structure to hold various information related to the Jack API
// implementation.
: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()
{
// Nothing to do here.
+#if !defined(__RTAUDIO_DEBUG__)
+ // Turn off Jack's internal error reporting.
+ jack_set_error_function( &jackSilentError );
+#endif
}
RtApiJack :: ~RtApiJack()
unsigned int RtApiJack :: getDeviceCount( void )
{
// See if we can become a jack client.
- jack_client_t *client = jack_client_new( "RtApiJackCount" );
+ jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
+ jack_status_t *status = NULL;
+ jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
if ( client == 0 ) return 0;
const char **ports;
ports = jack_get_ports( client, NULL, NULL, 0 );
if ( ports ) {
// Parse the port names up to the first colon (:).
- unsigned int iColon = 0;
+ size_t iColon = 0;
do {
port = (char *) ports[ nChannels ];
iColon = port.find(":");
RtAudio::DeviceInfo info;
info.probed = false;
- jack_client_t *client = jack_client_new( "RtApiJackInfo" );
+ jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption
+ jack_status_t *status = NULL;
+ jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
if ( client == 0 ) {
errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
error( RtError::WARNING );
ports = jack_get_ports( client, NULL, NULL, 0 );
if ( ports ) {
// Parse the port names up to the first colon (:).
- unsigned int iColon = 0;
+ size_t iColon = 0;
do {
port = (char *) ports[ nPorts ];
iColon = port.find(":");
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;
// 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;
}
// Look for jack server and try to become a client (only do once per stream).
jack_client_t *client = 0;
if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
+ jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
+ jack_status_t *status = NULL;
if ( options && !options->streamName.empty() )
- client = jack_client_new( options->streamName.c_str() );
+ client = jack_client_open( options->streamName.c_str(), jackoptions, status );
else
- client = jack_client_new( "RtApiJack" );
+ client = jack_client_open( "RtApiJack", jackoptions, status );
if ( client == 0 ) {
errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
error( RtError::WARNING );
ports = jack_get_ports( client, NULL, NULL, 0 );
if ( ports ) {
// Parse the port names up to the first colon (:).
- unsigned int iColon = 0;
+ size_t iColon = 0;
do {
port = (char *) ports[ nPorts ];
iColon = port.find(":");
// Count the available ports containing the client name as device
// channels. Jack "input ports" equal RtAudio output channels.
unsigned int nChannels = 0;
- unsigned long flag = JackPortIsOutput;
- if ( mode == INPUT ) flag = JackPortIsInput;
+ unsigned long flag = JackPortIsInput;
+ if ( mode == INPUT ) flag = JackPortIsOutput;
ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
if ( ports ) {
while ( ports[ nChannels ] ) nChannels++;
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
JackHandle *handle = (JackHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
stopStream();
}
+// This function will be called by a spawned thread when the user
+// callback function signals that the stream should be stopped or
+// aborted. It is necessary to handle it this way because the
+// callbackEvent() function must return before the jack_deactivate()
+// function will return.
+extern "C" void *jackStopStream( void *ptr )
+{
+ CallbackInfo *info = (CallbackInfo *) ptr;
+ RtApiJack *object = (RtApiJack *) info->object;
+
+ object->stopStream();
+
+ pthread_exit( NULL );
+}
+
bool RtApiJack :: callbackEvent( unsigned long nframes )
{
if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
// Check if we were draining the stream and signal is finished.
if ( handle->drainCounter > 3 ) {
- if ( handle->internalDrain == false )
- pthread_cond_signal( &handle->condition );
+ if ( handle->internalDrain == true ) {
+ pthread_create( &threadId, NULL, jackStopStream, info );
+ }
else
- stopStream();
+ pthread_cond_signal( &handle->condition );
return SUCCESS;
}
MUTEX_LOCK( &stream_.mutex );
+ // The state might change while waiting on a mutex.
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return SUCCESS;
+ }
+
// Invoke user callback first, to get fresh output data.
if ( handle->drainCounter == 0 ) {
RtAudioCallback callback = (RtAudioCallback) info->callback;
stream_.bufferSize, streamTime, status, info->userData );
if ( handle->drainCounter == 2 ) {
MUTEX_UNLOCK( &stream_.mutex );
- abortStream();
+ ThreadHandle id;
+ pthread_create( &id, NULL, jackStopStream, info );
return SUCCESS;
}
else if ( handle->drainCounter == 1 )
RtApi::tickStreamTime();
return SUCCESS;
}
-//******************** End of __UNIX_JACK__ *********************//
+ //******************** End of __UNIX_JACK__ *********************//
#endif
#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
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();
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
else if ( granularity == -1 ) {
// Make sure bufferSize is a power of two.
- double power = std::log10( (double) *bufferSize ) / log10( 2.0 );
- *bufferSize = (int) pow( 2.0, floor(power+0.5) );
+ int log2_of_min_size = 0;
+ int log2_of_max_size = 0;
+
+ for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
+ if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
+ if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
+ }
+
+ long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
+ int min_delta_num = log2_of_min_size;
+
+ for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
+ long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
+ if (current_delta < min_delta) {
+ min_delta = current_delta;
+ min_delta_num = i;
+ }
+ }
+
+ *bufferSize = ( (unsigned int)1 << min_delta_num );
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
- else *bufferSize = preferSize;
}
else if ( granularity != 0 ) {
// Set to an even multiple of granularity, rounding up.
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
if ( handle->drainCounter == 0 ) {
// The following lines were commented-out because some behavior was
// noted where the device buffers need to be zeroed to avoid
- // continuing sound, even when the device buffers are completed
+ // continuing sound, even when the device buffers are completely
// disposed. So now, calling abort is the same as calling stop.
- //AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- //handle->drainCounter = 1;
+ // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+ // handle->drainCounter = 1;
stopStream();
}
};
static Messages m[] =
- {
- { ASE_NotPresent, "Hardware input or output is not present or available." },
- { ASE_HWMalfunction, "Hardware is malfunctioning." },
- { ASE_InvalidParameter, "Invalid input parameter." },
- { ASE_InvalidMode, "Invalid mode." },
- { ASE_SPNotAdvancing, "Sample position not advancing." },
- { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
- { ASE_NoMemory, "Not enough memory to complete the request." }
- };
+ {
+ { ASE_NotPresent, "Hardware input or output is not present or available." },
+ { ASE_HWMalfunction, "Hardware is malfunctioning." },
+ { ASE_InvalidParameter, "Invalid input parameter." },
+ { ASE_InvalidMode, "Invalid mode." },
+ { ASE_SPNotAdvancing, "Sample position not advancing." },
+ { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
+ { ASE_NoMemory, "Not enough memory to complete the request." }
+ };
for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
if ( m[i].value == result ) return m[i].message;
// 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
+ // missing from latest mingw winapi
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
#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;
: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,
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
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 ( device >= dsDevices.size() ) {
+ errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
+ error( RtError::INVALID_USE );
}
- if ( dsinfo.name.empty() ) goto probeInput;
+ 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 );
errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
errorText_ = errorStream_.str();
error( RtError::WARNING );
- return info;
+ goto probeInput;
}
// Get output channel information.
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;
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;
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;
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 ) {
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
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;
}
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;
// 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
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;
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;
}
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;
}
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
- result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+ // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
+ // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+ // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
+ 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;
}
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;
}
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,
result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
if ( FAILED( result ) ) {
bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+ DSBCAPS_GLOBALFOCUS |
DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_LOCSOFTWARE ); // Force software mixing
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;
}
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;
}
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;
}
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;
}
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;
}
// Update wave format structure and buffer information.
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+ dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+
+ // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+ 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.
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;
+ }
+
+ // Get the buffer size ... might be different from what we specified.
+ DSCBCAPS dscbcaps;
+ dscbcaps.dwSize = sizeof( DSCBCAPS );
+ result = buffer->GetCaps( &dscbcaps );
+ if ( FAILED( result ) ) {
+ input->Release();
+ buffer->Release();
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
errorText_ = errorStream_.str();
return FAILURE;
}
+ 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;
}
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;
}
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.";
goto error;
}
+ // Boost DS thread priority
+ SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
return SUCCESS;
error:
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;
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 );
}
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 );
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
HRESULT result = 0;
LPVOID audioPtr;
DWORD dataLen;
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
result = buffer->Stop();
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
// we won't have old data playing.
result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
// Unlock the DS buffer
result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
result = buffer->Stop();
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
// we won't have old data playing.
result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
// Unlock the DS buffer
result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
errorText_ = errorStream_.str();
goto unlock;
}
timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
+
if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
}
void RtApiDs :: callbackEvent()
{
if ( stream_.state == STREAM_STOPPED ) {
- Sleep(50); // sleep 50 milliseconds
+ Sleep( 50 ); // sleep 50 milliseconds
return;
}
MUTEX_LOCK( &stream_.mutex );
+ // The state might change while waiting on a mutex.
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
// Invoke user callback to get fresh output data UNLESS we are
// draining stream.
if ( handle->drainCounter == 0 ) {
}
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;
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 ) {
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!";
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++;
}
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,
// 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!";
}
// 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.
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 );
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;
hr = object->GetCaps( &caps );
if ( hr == DS_OK ) {
if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
- info->counter++;
+ validDevice = true;
}
object->Release();
}
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 ) {
+ switch ( code ) {
case DSERR_ALLOCATED:
return "Already allocated";
default:
return "DirectSound unknown error";
- }
+ }
}
//******************** End of __WINDOWS_DS__ *********************//
#endif
#include <alsa/asoundlib.h>
#include <unistd.h>
-// A structure to hold various information related to the ALSA API
-// implementation.
+ // A structure to hold various information related to the ALSA API
+ // implementation.
struct AlsaHandle {
snd_pcm_t *handles[2];
bool synchronized;
bool xrun[2];
+ pthread_cond_t runnable;
AlsaHandle()
:synchronized(false) { xrun[0] = false; xrun[1] = false; }
errorText_ = errorStream_.str();
error( RtError::WARNING );
goto nextcard;
- }
- subdevice = -1;
- while( 1 ) {
+ }
+ subdevice = -1;
+ while( 1 ) {
result = snd_ctl_pcm_next_device( handle, &subdevice );
- if ( result < 0 ) {
+ if ( result < 0 ) {
errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
error( RtError::WARNING );
break;
}
- if ( subdevice < 0 )
+ if ( subdevice < 0 )
break;
nDevices++;
}
errorText_ = errorStream_.str();
error( RtError::WARNING );
goto nextcard;
- }
- subdevice = -1;
- while( 1 ) {
+ }
+ subdevice = -1;
+ while( 1 ) {
result = snd_ctl_pcm_next_device( chandle, &subdevice );
- if ( result < 0 ) {
+ if ( result < 0 ) {
errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
error( RtError::WARNING );
break;
}
- if ( subdevice < 0 ) break;
+ if ( subdevice < 0 ) break;
if ( nDevices == device ) {
sprintf( name, "hw:%d,%d", card, subdevice );
goto foundDevice;
foundDevice:
+ // If a stream is already open, we cannot probe the stream devices.
+ // Thus, use the saved results.
+ if ( stream_.state != STREAM_CLOSED &&
+ ( stream_.device[0] == device || stream_.device[1] == device ) ) {
+ if ( device >= devices_.size() ) {
+ errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
+ error( RtError::WARNING );
+ return info;
+ }
+ return devices_[ device ];
+ }
+
int openMode = SND_PCM_ASYNC;
snd_pcm_stream_t stream;
- snd_pcm_info_t *pcminfo;
- snd_pcm_info_alloca( &pcminfo );
+ snd_pcm_info_t *pcminfo;
+ snd_pcm_info_alloca( &pcminfo );
snd_pcm_t *phandle;
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca( ¶ms );
return info;
}
+void RtApiAlsa :: saveDeviceInfo( void )
+{
+ devices_.clear();
+
+ unsigned int nDevices = getDeviceCount();
+ devices_.resize( nDevices );
+ for ( unsigned int i=0; i<nDevices; i++ )
+ devices_[i] = getDeviceInfo( i );
+}
+
bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
return FAILURE;
- }
- subdevice = -1;
- while( 1 ) {
+ }
+ subdevice = -1;
+ while( 1 ) {
result = snd_ctl_pcm_next_device( chandle, &subdevice );
- if ( result < 0 ) break;
- if ( subdevice < 0 ) break;
+ if ( result < 0 ) break;
+ if ( subdevice < 0 ) break;
if ( nDevices == device ) {
sprintf( name, "hw:%d,%d", card, subdevice );
+ snd_ctl_close( chandle );
goto foundDevice;
}
nDevices++;
foundDevice:
+ // The getDeviceInfo() function will not work for a device that is
+ // already open. Thus, we'll probe the system before opening a
+ // stream and save the results for use by getDeviceInfo().
+ if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
+ this->saveDeviceInfo();
+
snd_pcm_stream_t stream;
if ( mode == OUTPUT )
stream = SND_PCM_STREAM_PLAYBACK;
}
// Set the buffer number, which in ALSA is referred to as the "period".
- int dir;
+ int totalSize, dir = 0;
unsigned int periods = 0;
if ( options ) periods = options->numberOfBuffers;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
- // Even though the hardware might allow 1 buffer, it won't work reliably.
- if ( periods < 2 ) periods = 2;
- result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
+ totalSize = *bufferSize * periods;
// Set the buffer (or period) size.
snd_pcm_uframes_t periodSize = *bufferSize;
}
*bufferSize = periodSize;
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
+ else periods = totalSize / *bufferSize;
+ // Even though the hardware might allow 1 buffer, it won't work reliably.
+ if ( periods < 2 ) periods = 2;
+ result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
+ if ( result < 0 ) {
+ snd_pcm_close( phandle );
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
+ errorText_ = errorStream_.str();
+ return FAILURE;
+ }
+
// If attempting to setup a duplex stream, the bufferSize parameter
// MUST be the same in both directions!
if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
snd_pcm_sw_params_alloca( &sw_params );
snd_pcm_sw_params_current( phandle, sw_params );
snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
- snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, 0x7fffffff );
+ snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
- snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX );
+
+ // The following two settings were suggested by Theo Veenker
+ //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
+ //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
+
+ // here are two options for a fix
+ //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
+ snd_pcm_uframes_t val;
+ snd_pcm_sw_params_get_boundary( sw_params, &val );
+ snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
+
result = snd_pcm_sw_params( phandle, sw_params );
if ( result < 0 ) {
snd_pcm_close( phandle );
errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
goto error;
}
+
+ if ( pthread_cond_init( &apiInfo->runnable, NULL ) ) {
+ errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
+ goto error;
+ }
+
stream_.apiHandle = (void *) apiInfo;
apiInfo->handles[0] = 0;
apiInfo->handles[1] = 0;
stream_.callbackInfo.object = (void *) this;
// Set the thread attributes for joinable and realtime scheduling
- // priority. The higher priority will only take affect if the
- // program is run as root or suid.
+ // priority (optional). The higher priority will only take affect
+ // if the program is run as root or suid. Note, under Linux
+ // processes with CAP_SYS_NICE privilege, a user can change
+ // scheduling policy and priority (thus need not be root). See
+ // POSIX "capabilities".
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
- pthread_attr_setschedpolicy( &attr, SCHED_RR );
+ if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+ struct sched_param param;
+ int priority = options->priority;
+ int min = sched_get_priority_min( SCHED_RR );
+ int max = sched_get_priority_max( SCHED_RR );
+ if ( priority < min ) priority = min;
+ else if ( priority > max ) priority = max;
+ param.sched_priority = priority;
+ pthread_attr_setschedparam( &attr, ¶m );
+ pthread_attr_setschedpolicy( &attr, SCHED_RR );
+ }
+ else
+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
#else
pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
#endif
error:
if ( apiInfo ) {
+ pthread_cond_destroy( &apiInfo->runnable );
if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
delete apiInfo;
return;
}
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
stream_.callbackInfo.isRunning = false;
+ MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED )
+ pthread_cond_signal( &apiInfo->runnable );
+ MUTEX_UNLOCK( &stream_.mutex );
pthread_join( stream_.callbackInfo.thread, NULL );
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
if ( stream_.state == STREAM_RUNNING ) {
stream_.state = STREAM_STOPPED;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
}
if ( apiInfo ) {
+ pthread_cond_destroy( &apiInfo->runnable );
if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
delete apiInfo;
unlock:
MUTEX_UNLOCK( &stream_.mutex );
+ pthread_cond_signal( &apiInfo->runnable );
+
if ( result >= 0 ) return;
error( RtError::SYSTEM_ERROR );
}
return;
}
- // Change the state before the lock to improve shutdown response
- // when using a callback.
- stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
int result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
}
unlock:
+ stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
if ( result >= 0 ) return;
return;
}
- // Change the state before the lock to improve shutdown response
- // when using a callback.
- stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
int result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
}
unlock:
+ stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
- stream_.state = STREAM_STOPPED;
if ( result >= 0 ) return;
error( RtError::SYSTEM_ERROR );
}
void RtApiAlsa :: callbackEvent()
{
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
if ( stream_.state == STREAM_STOPPED ) {
- if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
- return;
+ MUTEX_LOCK( &stream_.mutex );
+ pthread_cond_wait( &apiInfo->runnable, &stream_.mutex );
+ if ( stream_.state != STREAM_RUNNING ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+ MUTEX_UNLOCK( &stream_.mutex );
}
if ( stream_.state == STREAM_CLOSED ) {
}
int doStopStream = 0;
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
double streamTime = getStreamTime();
RtAudioStreamStatus status = 0;
apiInfo->xrun[1] = false;
}
doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+ stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+
+ if ( doStopStream == 2 ) {
+ abortStream();
+ return;
+ }
MUTEX_LOCK( &stream_.mutex );
}
if ( result < (int) stream_.bufferSize ) {
- // Either an error or underrun occured.
+ // Either an error or overrun occured.
if ( result == -EPIPE ) {
snd_pcm_state_t state = snd_pcm_state( handle[1] );
if ( state == SND_PCM_STATE_XRUN ) {
errorText_ = errorStream_.str();
}
error( RtError::WARNING );
- goto unlock;
+ goto tryOutput;
}
// Do byte swapping if necessary.
if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
}
+ tryOutput:
+
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
// Setup parameters and do buffer conversion if necessary.
RtApi::tickStreamTime();
if ( doStopStream == 1 ) this->stopStream();
- else if ( doStopStream == 2 ) this->abortStream();
}
extern "C" void *alsaCallbackHandler( void *ptr )
RtApiAlsa *object = (RtApiAlsa *) info->object;
bool *isRunning = &info->isRunning;
-#ifdef SCHED_RR
- // Set a higher scheduler priority (P.J. Leonard)
- struct sched_param param;
- int min = sched_get_priority_min( SCHED_RR );
- int max = sched_get_priority_max( SCHED_RR );
- param.sched_priority = min + ( max - min ) / 2; // Is this the best number?
- sched_setscheduler( 0, SCHED_RR, ¶m );
-#endif
-
while ( *isRunning == true ) {
pthread_testcancel();
object->callbackEvent();
int id[2]; // device ids
bool xrun[2];
bool triggered;
+ pthread_cond_t runnable;
OssHandle()
:triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
return 0;
}
+ close( mixerfd );
return sysinfo.numaudios;
}
bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
+ unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options )
{
// For duplex operation, specifically set this mode (this doesn't seem to work).
/*
- if ( flags | O_RDWR ) {
+ if ( flags | O_RDWR ) {
result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
if ( result == -1) {
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
+ errorText_ = errorStream_.str();
+ return FAILURE;
+ }
}
- }
*/
// Check the device channel support.
goto error;
}
+ if ( pthread_cond_init( &handle->runnable, NULL ) ) {
+ errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
+ goto error;
+ }
+
stream_.apiHandle = (void *) handle;
}
else {
pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
- pthread_attr_setschedpolicy( &attr, SCHED_RR );
+ if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+ struct sched_param param;
+ int priority = options->priority;
+ int min = sched_get_priority_min( SCHED_RR );
+ int max = sched_get_priority_max( SCHED_RR );
+ if ( priority < min ) priority = min;
+ else if ( priority > max ) priority = max;
+ param.sched_priority = priority;
+ pthread_attr_setschedparam( &attr, ¶m );
+ pthread_attr_setschedpolicy( &attr, SCHED_RR );
+ }
+ else
+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
#else
pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
#endif
error:
if ( handle ) {
+ pthread_cond_destroy( &handle->runnable );
if ( handle->id[0] ) close( handle->id[0] );
if ( handle->id[1] ) close( handle->id[1] );
delete handle;
return;
}
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;
stream_.callbackInfo.isRunning = false;
+ MUTEX_LOCK( &stream_.mutex );
+ if ( stream_.state == STREAM_STOPPED )
+ pthread_cond_signal( &handle->runnable );
+ MUTEX_UNLOCK( &stream_.mutex );
pthread_join( stream_.callbackInfo.thread, NULL );
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
if ( stream_.state == STREAM_RUNNING ) {
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
}
if ( handle ) {
+ pthread_cond_destroy( &handle->runnable );
if ( handle->id[0] ) close( handle->id[0] );
if ( handle->id[1] ) close( handle->id[1] );
delete handle;
// when fed samples.
MUTEX_UNLOCK( &stream_.mutex );
+
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;
+ pthread_cond_signal( &handle->runnable );
}
void RtApiOss :: stopStream()
return;
}
- // Change the state before the lock to improve shutdown response
- // when using a callback.
- stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
+ // The state might change while waiting on a mutex.
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
int result = 0;
OssHandle *handle = (OssHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
}
unlock:
+ stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
- stream_.state = STREAM_STOPPED;
if ( result != -1 ) return;
error( RtError::SYSTEM_ERROR );
}
return;
}
- // Change the state before the lock to improve shutdown response
- // when using a callback.
- stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
+ // The state might change while waiting on a mutex.
+ if ( stream_.state == STREAM_STOPPED ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+
int result = 0;
OssHandle *handle = (OssHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
}
unlock:
+ stream_.state = STREAM_STOPPED;
MUTEX_UNLOCK( &stream_.mutex );
- stream_.state = STREAM_STOPPED;
if ( result != -1 ) return;
error( RtError::SYSTEM_ERROR );
}
void RtApiOss :: callbackEvent()
{
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;
if ( stream_.state == STREAM_STOPPED ) {
- if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
- return;
+ MUTEX_LOCK( &stream_.mutex );
+ pthread_cond_wait( &handle->runnable, &stream_.mutex );
+ if ( stream_.state != STREAM_RUNNING ) {
+ MUTEX_UNLOCK( &stream_.mutex );
+ return;
+ }
+ MUTEX_UNLOCK( &stream_.mutex );
}
if ( stream_.state == STREAM_CLOSED ) {
RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
double streamTime = getStreamTime();
RtAudioStreamStatus status = 0;
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
status |= RTAUDIO_OUTPUT_UNDERFLOW;
handle->xrun[0] = false;
}
doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+ if ( doStopStream == 2 ) {
+ this->abortStream();
+ return;
+ }
MUTEX_LOCK( &stream_.mutex );
handle->xrun[0] = true;
errorText_ = "RtApiOss::callbackEvent: audio write error.";
error( RtError::WARNING );
- goto unlock;
+ // Continue on to input section.
}
}
RtApi::tickStreamTime();
if ( doStopStream == 1 ) this->stopStream();
- else if ( doStopStream == 2 ) this->abortStream();
}
extern "C" void *ossCallbackHandler( void *ptr )
RtApiOss *object = (RtApiOss *) info->object;
bool *isRunning = &info->isRunning;
-#ifdef SCHED_RR
- // Set a higher scheduler priority (P.J. Leonard)
- struct sched_param param;
- param.sched_priority = 39; // Is this the best number?
- sched_setscheduler( 0, SCHED_RR, ¶m );
-#endif
-
while ( *isRunning == true ) {
pthread_testcancel();
object->callbackEvent();
stream_.callbackInfo.userData = 0;
stream_.callbackInfo.isRunning = false;
for ( int i=0; i<2; i++ ) {
- stream_.device[i] = 0;
+ stream_.device[i] = 11111;
stream_.doConvertBuffer[i] = false;
stream_.deviceInterleaved[i] = true;
stream_.doByteSwap[i] = false;
if (info.inFormat == RTAUDIO_SINT8) {
signed char *in = (signed char *)inBuffer;
- scale = 1.0 / 128.0;
+ scale = 1.0 / 127.5;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT16) {
Int16 *in = (Int16 *)inBuffer;
- scale = 1.0 / 32768.0;
+ scale = 1.0 / 32767.5;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT24) {
Int32 *in = (Int32 *)inBuffer;
- scale = 1.0 / 8388608.0;
+ scale = 1.0 / 8388607.5;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT32) {
Int32 *in = (Int32 *)inBuffer;
- scale = 1.0 / 2147483648.0;
+ scale = 1.0 / 2147483647.5;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
if (info.inFormat == RTAUDIO_SINT8) {
signed char *in = (signed char *)inBuffer;
- scale = 1.0 / 128.0;
+ scale = (Float32) ( 1.0 / 127.5 );
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT16) {
Int16 *in = (Int16 *)inBuffer;
- scale = 1.0 / 32768.0;
+ scale = (Float32) ( 1.0 / 32767.5 );
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT24) {
Int32 *in = (Int32 *)inBuffer;
- scale = 1.0 / 8388608.0;
+ scale = (Float32) ( 1.0 / 8388607.5 );
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
}
else if (info.inFormat == RTAUDIO_SINT32) {
Int32 *in = (Int32 *)inBuffer;
- scale = 1.0 / 2147483648.0;
+ scale = (Float32) ( 1.0 / 2147483647.5 );
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+ out[info.outOffset[j]] += 0.5;
out[info.outOffset[j]] *= scale;
}
in += info.inJump;
Float32 *in = (Float32 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float64 *in = (Float64 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float32 *in = (Float32 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388608.0);
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float64 *in = (Float64 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float32 *in = (Float32 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
+ out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float64 *in = (Float64 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
+ out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float32 *in = (Float32 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
+ out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
Float64 *in = (Float64 *)inBuffer;
for (unsigned int i=0; i<stream_.bufferSize; i++) {
for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
+ out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
}
in += info.inJump;
out += info.outJump;
}
}
+ //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
+ //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
+ //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
+
void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
{
register char val;
*(ptr) = *(ptr+1);
*(ptr+1) = val;
- // Increment 4 bytes.
- ptr += 4;
+ // Increment 3 more bytes.
+ ptr += 3;
}
}
else if ( format == RTAUDIO_FLOAT64 ) {
*(ptr) = *(ptr+1);
*(ptr+1) = val;
- // Increment 8 bytes.
- ptr += 8;
+ // Increment 5 more bytes.
+ ptr += 5;
}
}
}
-// Indentation settings for Vim and Emacs
-//
-// Local Variables:
-// c-basic-offset: 2
-// indent-tabs-mode: nil
-// End:
-//
-// vim: et sts=2 sw=2
+ // Indentation settings for Vim and Emacs
+ //
+ // Local Variables:
+ // c-basic-offset: 2
+ // indent-tabs-mode: nil
+ // End:
+ //
+ // vim: et sts=2 sw=2