Replace convertBufferWasapi with new WasapiResampler
authorMarcus Tomlinson <themarcustomlinson@gmail.com>
Sat, 23 Dec 2017 08:59:28 +0000 (10:59 +0200)
committerMarcus Tomlinson <marcustomlinson@MacBook-Air.local>
Wed, 22 Aug 2018 08:29:11 +0000 (09:29 +0100)
RtAudio.cpp
RtAudio.h

index db7ccd8f517896c1faa51011696aa51410505b67..4eaa7e7644067950201635f37a6a35af1bcf5436 100644 (file)
@@ -3694,6 +3694,14 @@ static const char* getAsioErrorString( ASIOError result )
 #include <mmdeviceapi.h>
 #include <functiondiscoverykeys_devpkey.h>
 
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfplay.h>
+#include <Wmcodecdsp.h>
+
+#pragma comment( lib, "mfplat.lib" )
+#pragma comment( lib, "wmcodecdspuuid" )
+
 //=============================================================================
 
 #define SAFE_RELEASE( objectPtr )\
@@ -3867,148 +3875,183 @@ 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 )
+// between HW and the user. The WasapiResampler class is used to perform this conversion between
+// HwIn->UserIn and UserOut->HwOut during the stream callback loop.
+class WasapiResampler
+{
+public:
+  WasapiResampler( bool isFloat, unsigned int bitsPerSample, unsigned int channelCount,
+                   unsigned int inSampleRate, unsigned int outSampleRate )
+    : _bytesPerSample( bitsPerSample / 8 )
+    , _channelCount( channelCount )
+    , _sampleRatio( ( float ) outSampleRate / inSampleRate )
+    , _transformUnk( NULL )
+    , _transform( NULL )
+    , _resamplerProps( NULL )
+    , _mediaType( NULL )
+    , _inputMediaType( NULL )
+    , _outputMediaType( NULL )
   {
-    // 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;
+    // 1. Initialization
 
-      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;
-      }
+    MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET );
 
-      // jump to next in sample
-      inSampleFraction += sampleStep;
-    }
+    // 2. Create Resampler Transform Object
+
+    CoCreateInstance( CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER,
+                      IID_IUnknown, ( void** ) &_transformUnk );
+
+    _transformUnk->QueryInterface( IID_PPV_ARGS( &_transform ) );
+
+    _transformUnk->QueryInterface( IID_PPV_ARGS( &_resamplerProps ) );
+    _resamplerProps->SetHalfFilterLength( 60 ); // best conversion quality
+
+                                                // 3. Specify input / output format
+
+    MFCreateMediaType( &_mediaType );
+    _mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
+    _mediaType->SetGUID( MF_MT_SUBTYPE, isFloat ? MFAudioFormat_Float : MFAudioFormat_PCM );
+    _mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channelCount );
+    _mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSampleRate );
+    _mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, _bytesPerSample * channelCount );
+    _mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * inSampleRate );
+    _mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample );
+    _mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE );
+
+    MFCreateMediaType( &_inputMediaType );
+    _mediaType->CopyAllItems( _inputMediaType );
+
+    _transform->SetInputType( 0, _inputMediaType, 0 );
+
+    MFCreateMediaType( &_outputMediaType );
+    _mediaType->CopyAllItems( _outputMediaType );
+
+    _outputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, outSampleRate );
+    _outputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * outSampleRate );
+
+    _transform->SetOutputType( 0, _outputMediaType, 0 );
+
+    // 4. Send stream start messages to Resampler
+
+    _transform->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, NULL );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL );
+  }
+
+  ~WasapiResampler()
+  {
+    // 8. Send stream stop messages to Resampler
+
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_STREAMING, NULL );
+
+    // 9. Cleanup
+
+    MFShutdown();
+
+    SAFE_RELEASE( _transformUnk );
+    SAFE_RELEASE( _transform );
+    SAFE_RELEASE( _resamplerProps );
+    SAFE_RELEASE( _mediaType );
+    SAFE_RELEASE( _inputMediaType );
+    SAFE_RELEASE( _outputMediaType );
   }
-  else // else interpolate
+
+  void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount )
   {
-    // frame-by-frame, copy each relative input sample into it's corresponding output sample
-    for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
+    unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount;
+    if ( _sampleRatio == 1 )
     {
-      unsigned int inSample = ( unsigned int ) inSampleFraction;
-      float inSampleDec = inSampleFraction - inSample;
-      unsigned int frameInSample = inSample * channelCount;
-      unsigned int frameOutSample = outSample * channelCount;
+      // no sample rate conversion required
+      memcpy( outBuffer, inBuffer, inputBufferSize );
+      outSampleCount = inSampleCount;
+      return;
+    }
 
-      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;
-        }
-      }
+    unsigned int outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount );
+
+    IMFMediaBuffer* rInBuffer;
+    IMFSample* rInSample;
+    BYTE* rInByteBuffer = NULL;
+
+    // 5. Create Sample object from input data
+
+    MFCreateMemoryBuffer( inputBufferSize, &rInBuffer );
+
+    rInBuffer->Lock( &rInByteBuffer, NULL, NULL );
+    memcpy( rInByteBuffer, inBuffer, inputBufferSize );
+    rInBuffer->Unlock();
+    rInByteBuffer = NULL;
 
-      // jump to next in sample
-      inSampleFraction += sampleStep;
+    rInBuffer->SetCurrentLength( inputBufferSize );
+
+    MFCreateSample( &rInSample );
+    rInSample->AddBuffer( rInBuffer );
+
+    // 6. Pass input data to Resampler
+
+    _transform->ProcessInput( 0, rInSample, 0 );
+
+    SAFE_RELEASE( rInBuffer );
+    SAFE_RELEASE( rInSample );
+
+    // 7. Perform sample rate conversion
+
+    IMFMediaBuffer* rOutBuffer = NULL;
+    BYTE* rOutByteBuffer = NULL;
+
+    MFT_OUTPUT_DATA_BUFFER rOutDataBuffer;
+    DWORD rStatus;
+    DWORD rBytes = outputBufferSize; // maximum bytes accepted per ProcessOutput
+
+                                     // 7.1 Create Sample object for output data
+
+    memset( &rOutDataBuffer, 0, sizeof rOutDataBuffer );
+    MFCreateSample( &( rOutDataBuffer.pSample ) );
+    MFCreateMemoryBuffer( rBytes, &rOutBuffer );
+    rOutDataBuffer.pSample->AddBuffer( rOutBuffer );
+    rOutDataBuffer.dwStreamID = 0;
+    rOutDataBuffer.dwStatus = 0;
+    rOutDataBuffer.pEvents = NULL;
+
+    // 7.2 Get output data from Resampler
+
+    if ( _transform->ProcessOutput( 0, 1, &rOutDataBuffer, &rStatus ) == MF_E_TRANSFORM_NEED_MORE_INPUT )
+    {
+      outSampleCount = 0;
+      SAFE_RELEASE( rOutBuffer );
+      SAFE_RELEASE( rOutDataBuffer.pSample );
+      return;
     }
+
+    // 7.3 Write output data to outBuffer
+
+    SAFE_RELEASE( rOutBuffer );
+    rOutDataBuffer.pSample->ConvertToContiguousBuffer( &rOutBuffer );
+    rOutBuffer->GetCurrentLength( &rBytes );
+
+    rOutBuffer->Lock( &rOutByteBuffer, NULL, NULL );
+    memcpy( outBuffer, rOutByteBuffer, rBytes );
+    rOutBuffer->Unlock();
+    rOutByteBuffer = NULL;
+
+    outSampleCount = rBytes / _bytesPerSample / _channelCount;
+    SAFE_RELEASE( rOutBuffer );
+    SAFE_RELEASE( rOutDataBuffer.pSample );
   }
-}
+
+private:
+  unsigned int _bytesPerSample;
+  unsigned int _channelCount;
+  float _sampleRatio;
+
+  IUnknown* _transformUnk;
+  IMFTransform* _transform;
+  IWMResamplerProps* _resamplerProps;
+  IMFMediaType* _mediaType;
+  IMFMediaType* _inputMediaType;
+  IMFMediaType* _outputMediaType;
+};
 
 //-----------------------------------------------------------------------------
 
index 34a2534859fab7dcd85d4434d472209a09d1bbe4..f37b9c268df86f01ca4cfd5ed014548909056a2c 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
@@ -1013,7 +1013,7 @@ class RtApiWasapi : public RtApi
 {
 public:
   RtApiWasapi();
-  ~RtApiWasapi();
+  virtual ~RtApiWasapi();
 
   RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
   unsigned int getDeviceCount( void );