From 8b543a2850d9d24f85cca9229169fe5027ab863e Mon Sep 17 00:00:00 2001 From: Marcus Tomlinson Date: Tue, 17 Oct 2017 20:55:18 +0200 Subject: [PATCH] Allow only the device's native sample rate in WASAPI API Revert "fix" for Issue #69 --- RtAudio.cpp | 233 ++++++---------------------------------------------- 1 file changed, 27 insertions(+), 206 deletions(-) diff --git a/RtAudio.cpp b/RtAudio.cpp index 1d23516..21d6192 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -3864,152 +3864,6 @@ private: //----------------------------------------------------------------------------- -// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate -// between HW and the user. The convertBufferWasapi function is used to perform this conversion -// between HwIn->UserIn and UserOut->HwOut during the stream callback loop. -// This sample rate converter works best with conversions between one rate and its multiple. -void convertBufferWasapi( char* outBuffer, - const char* inBuffer, - const unsigned int& channelCount, - const unsigned int& inSampleRate, - const unsigned int& outSampleRate, - const unsigned int& inSampleCount, - unsigned int& outSampleCount, - const RtAudioFormat& format ) -{ - // calculate the new outSampleCount and relative sampleStep - float sampleRatio = ( float ) outSampleRate / inSampleRate; - float sampleRatioInv = ( float ) 1 / sampleRatio; - float sampleStep = 1.0f / sampleRatio; - float inSampleFraction = 0.0f; - - // for cmath functions - using namespace std; - - outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio ); - - // if inSampleRate is a multiple of outSampleRate (or vice versa) there's no need to interpolate - if ( floor( sampleRatio ) == sampleRatio || floor( sampleRatioInv ) == sampleRatioInv ) - { - // frame-by-frame, copy each relative input sample into it's corresponding output sample - for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) - { - unsigned int inSample = ( unsigned int ) inSampleFraction; - - switch ( format ) - { - case RTAUDIO_SINT8: - memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) ); - break; - case RTAUDIO_SINT16: - memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) ); - break; - case RTAUDIO_SINT24: - memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) ); - break; - case RTAUDIO_SINT32: - memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) ); - break; - case RTAUDIO_FLOAT32: - memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) ); - break; - case RTAUDIO_FLOAT64: - memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) ); - break; - } - - // jump to next in sample - inSampleFraction += sampleStep; - } - } - else // else interpolate - { - // frame-by-frame, copy each relative input sample into it's corresponding output sample - for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) - { - unsigned int inSample = ( unsigned int ) inSampleFraction; - float inSampleDec = inSampleFraction - inSample; - unsigned int frameInSample = inSample * channelCount; - unsigned int frameOutSample = outSample * channelCount; - - switch ( format ) - { - case RTAUDIO_SINT8: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - char fromSample = ( ( char* ) inBuffer )[ frameInSample + channel ]; - char toSample = ( ( char* ) inBuffer )[ frameInSample + channelCount + channel ]; - char sampleDiff = ( char ) ( ( toSample - fromSample ) * inSampleDec ); - ( ( char* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - case RTAUDIO_SINT16: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - short fromSample = ( ( short* ) inBuffer )[ frameInSample + channel ]; - short toSample = ( ( short* ) inBuffer )[ frameInSample + channelCount + channel ]; - short sampleDiff = ( short ) ( ( toSample - fromSample ) * inSampleDec ); - ( ( short* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - case RTAUDIO_SINT24: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - int fromSample = ( ( S24* ) inBuffer )[ frameInSample + channel ].asInt(); - int toSample = ( ( S24* ) inBuffer )[ frameInSample + channelCount + channel ].asInt(); - int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec ); - ( ( S24* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - case RTAUDIO_SINT32: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - int fromSample = ( ( int* ) inBuffer )[ frameInSample + channel ]; - int toSample = ( ( int* ) inBuffer )[ frameInSample + channelCount + channel ]; - int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec ); - ( ( int* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - case RTAUDIO_FLOAT32: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - float fromSample = ( ( float* ) inBuffer )[ frameInSample + channel ]; - float toSample = ( ( float* ) inBuffer )[ frameInSample + channelCount + channel ]; - float sampleDiff = ( toSample - fromSample ) * inSampleDec; - ( ( float* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - case RTAUDIO_FLOAT64: - { - for ( unsigned int channel = 0; channel < channelCount; channel++ ) - { - double fromSample = ( ( double* ) inBuffer )[ frameInSample + channel ]; - double toSample = ( ( double* ) inBuffer )[ frameInSample + channelCount + channel ]; - double sampleDiff = ( toSample - fromSample ) * inSampleDec; - ( ( double* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff; - } - break; - } - } - - // jump to next in sample - inSampleFraction += sampleStep; - } - } -} - -//----------------------------------------------------------------------------- - // A structure to hold various information related to the WASAPI implementation. struct WasapiHandle { @@ -4275,15 +4129,12 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device ) info.duplexChannels = 0; } - // sample rates - info.sampleRates.clear(); - - // allow support for all sample rates as we have a built-in sample rate converter - for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { - info.sampleRates.push_back( SAMPLE_RATES[i] ); - } + // sample rates (WASAPI only supports the one native sample rate) info.preferredSampleRate = deviceFormat->nSamplesPerSec; + info.sampleRates.clear(); + info.sampleRates.push_back( deviceFormat->nSamplesPerSec ); + // native format info.nativeFormats = 0; @@ -4559,6 +4410,7 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne WAVEFORMATEX* deviceFormat = NULL; unsigned int bufferBytes; stream_.state = STREAM_STOPPED; + RtAudio::DeviceInfo deviceInfo; // create API Handle if not already created if ( !stream_.apiHandle ) @@ -4599,6 +4451,16 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne goto Exit; } + deviceInfo = getDeviceInfo( device ); + + // validate sample rate + if ( sampleRate != deviceInfo.preferredSampleRate ) + { + errorType = RtAudioError::INVALID_USE; + errorText_ = "RtApiWasapi::probeDeviceOpen: " + std::to_string( sampleRate ) + "Hz sample rate not supported. This device only supports " + std::to_string( deviceInfo.preferredSampleRate ) + "Hz."; + goto Exit; + } + // determine whether index falls within capture or render devices if ( device >= renderDeviceCount ) { if ( mode != INPUT ) { @@ -4682,7 +4544,7 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.nUserChannels[mode] = channels; stream_.channelOffset[mode] = firstChannel; stream_.userFormat = format; - stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats; + stream_.deviceFormat[mode] = deviceInfo.nativeFormats; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; @@ -4782,8 +4644,6 @@ void RtApiWasapi::wasapiThread() WAVEFORMATEX* captureFormat = NULL; WAVEFORMATEX* renderFormat = NULL; - float captureSrRatio = 0.0f; - float renderSrRatio = 0.0f; WasapiBuffer captureBuffer; WasapiBuffer renderBuffer; @@ -4793,15 +4653,11 @@ void RtApiWasapi::wasapiThread() unsigned long captureFlags = 0; unsigned int bufferFrameCount = 0; unsigned int numFramesPadding = 0; - unsigned int convBufferSize = 0; bool callbackPushed = false; bool callbackPulled = false; bool callbackStopped = false; int callbackResult = 0; - // convBuffer is used to store converted buffers between WASAPI and the user - char* convBuffer = NULL; - unsigned int convBuffSize = 0; unsigned int deviceBuffSize = 0; errorText_.clear(); @@ -4824,11 +4680,8 @@ void RtApiWasapi::wasapiThread() goto Exit; } - captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); - // initialize capture stream according to desire buffer size - float desiredBufferSize = stream_.bufferSize * captureSrRatio; - REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec ); + REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / captureFormat->nSamplesPerSec ); if ( !captureClient ) { hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, @@ -4875,7 +4728,7 @@ void RtApiWasapi::wasapiThread() } // scale outBufferSize according to stream->user sample rate ratio - unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; + unsigned int outBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[INPUT]; inBufferSize *= stream_.nDeviceChannels[INPUT]; // set captureBuffer size @@ -4904,11 +4757,8 @@ void RtApiWasapi::wasapiThread() goto Exit; } - renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); - // initialize render stream according to desire buffer size - float desiredBufferSize = stream_.bufferSize * renderSrRatio; - REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec ); + REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / renderFormat->nSamplesPerSec ); if ( !renderClient ) { hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, @@ -4955,7 +4805,7 @@ void RtApiWasapi::wasapiThread() } // scale inBufferSize according to user->stream sample rate ratio - unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; + unsigned int inBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[OUTPUT]; outBufferSize *= stream_.nDeviceChannels[OUTPUT]; // set renderBuffer size @@ -4978,23 +4828,18 @@ void RtApiWasapi::wasapiThread() if ( stream_.mode == INPUT ) { using namespace std; // for roundf - convBuffSize = ( size_t ) roundf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); } else if ( stream_.mode == OUTPUT ) { - convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); } else if ( stream_.mode == DUPLEX ) { - convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), - ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); } - convBuffer = ( char* ) malloc( convBuffSize ); stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize ); - if ( !convBuffer || !stream_.deviceBuffer ) { + if ( !stream_.deviceBuffer ) { errorType = RtAudioError::MEMORY_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory."; goto Exit; @@ -5006,26 +4851,15 @@ void RtApiWasapi::wasapiThread() // Callback Input // ============== // 1. Pull callback buffer from inputBuffer - // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count - // Convert callback buffer to user format + // 2. If 1. was successful: Convert callback buffer to user format if ( captureAudioClient ) { // Pull callback buffer from inputBuffer - callbackPulled = captureBuffer.pullBuffer( convBuffer, - ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT], + callbackPulled = captureBuffer.pullBuffer( stream_.deviceBuffer, + ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[INPUT], stream_.deviceFormat[INPUT] ); if ( callbackPulled ) { - // Convert callback buffer to user sample rate - convertBufferWasapi( stream_.deviceBuffer, - convBuffer, - stream_.nDeviceChannels[INPUT], - captureFormat->nSamplesPerSec, - stream_.sampleRate, - ( unsigned int ) ( stream_.bufferSize * captureSrRatio ), - convBufferSize, - stream_.deviceFormat[INPUT] ); - if ( stream_.doConvertBuffer[INPUT] ) { // Convert callback buffer to user format convertBuffer( stream_.userBuffer[INPUT], @@ -5099,8 +4933,7 @@ void RtApiWasapi::wasapiThread() // Callback Output // =============== // 1. Convert callback buffer to stream format - // 2. Convert callback buffer to stream sample rate and channel count - // 3. Push callback buffer into outputBuffer + // 2. Push callback buffer into outputBuffer if ( renderAudioClient && callbackPulled ) { if ( stream_.doConvertBuffer[OUTPUT] ) { @@ -5111,19 +4944,9 @@ void RtApiWasapi::wasapiThread() } - // Convert callback buffer to stream sample rate - convertBufferWasapi( convBuffer, - stream_.deviceBuffer, - stream_.nDeviceChannels[OUTPUT], - stream_.sampleRate, - renderFormat->nSamplesPerSec, - stream_.bufferSize, - convBufferSize, - stream_.deviceFormat[OUTPUT] ); - // Push callback buffer into outputBuffer - callbackPushed = renderBuffer.pushBuffer( convBuffer, - convBufferSize * stream_.nDeviceChannels[OUTPUT], + callbackPushed = renderBuffer.pushBuffer( stream_.deviceBuffer, + stream_.bufferSize * stream_.nDeviceChannels[OUTPUT], stream_.deviceFormat[OUTPUT] ); } else { @@ -5269,8 +5092,6 @@ Exit: CoTaskMemFree( captureFormat ); CoTaskMemFree( renderFormat ); - free ( convBuffer ); - CoUninitialize(); // update stream state -- 2.30.2