Updated to latest version of RtAudio with support for WASAPI using MinGW.

This commit is contained in:
Gary Scavone
2014-04-23 11:39:32 -04:00
parent f1c929a6c4
commit 1c5d42e64c
2 changed files with 364 additions and 157 deletions

View File

@@ -38,7 +38,7 @@
*/
/************************************************************************/
// RtAudio: Version 4.1.0
// RtAudio: Version 4.1.1pre
#include "RtAudio.h"
#include <iostream>
@@ -3578,21 +3578,22 @@ static const char* getAsioErrorString( ASIOError result )
#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API
// Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014
// - Introduces support for the Windows WASAPI API
// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required
// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface
// - Includes automatic internal conversion of sample rate, buffer size and channel count
#ifndef INITGUID
#define INITGUID
#endif
#include <audioclient.h>
#include <avrt.h>
#include <functiondiscoverykeys.h>
#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>
//=============================================================================
#define EXIT_ON_ERROR( hr, errorType, errorText )\
if ( FAILED( hr ) )\
{\
errorText_ = __FUNCTION__ ": " errorText;\
error( errorType );\
goto Exit;\
}
#define SAFE_RELEASE( objectPtr )\
if ( objectPtr )\
{\
@@ -3767,8 +3768,7 @@ private:
// channel counts between HW and the user. The convertBufferWasapi function is used to perform
// these conversions between HwIn->UserIn and UserOut->HwOut during the stream callback loop.
// This sample rate converter favors speed over quality, and works best with conversions between
// one rate and its multiple. RtApiWasapi will not populate a device's sample rate list with rates
// that may cause artifacts via this conversion.
// one rate and its multiple.
void convertBufferWasapi( char* outBuffer,
const char* inBuffer,
const unsigned int& inChannelCount,
@@ -3783,7 +3783,7 @@ void convertBufferWasapi( char* outBuffer,
float sampleRatio = ( float ) outSampleRate / inSampleRate;
float sampleStep = 1.0f / sampleRatio;
float inSampleFraction = 0.0f;
unsigned int commonChannelCount = min( inChannelCount, outChannelCount );
unsigned int commonChannelCount = std::min( inChannelCount, outChannelCount );
outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );
@@ -3851,7 +3851,7 @@ RtApiWasapi::RtApiWasapi()
if ( !FAILED( hr ) )
coInitialized_ = true;
// instantiate device enumerator
// Instantiate device enumerator
hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,
CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),
( void** ) &deviceEnumerator_ );
@@ -3888,26 +3888,43 @@ unsigned int RtApiWasapi::getDeviceCount( void )
IMMDeviceCollection* captureDevices = NULL;
IMMDeviceCollection* renderDevices = NULL;
// count capture devices
// Count capture devices
errorText_.clear();
HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";
goto Exit;
}
hr = captureDevices->GetCount( &captureDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";
goto Exit;
}
// count render devices
// Count render devices
hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";
goto Exit;
}
hr = renderDevices->GetCount( &renderDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";
goto Exit;
}
Exit:
// release all references
SAFE_RELEASE( captureDevices );
SAFE_RELEASE( renderDevices );
return captureDeviceCount + renderDeviceCount;
if ( errorText_.empty() )
return captureDeviceCount + renderDeviceCount;
error( RtAudioError::DRIVER_ERROR );
return 0;
}
//-----------------------------------------------------------------------------
@@ -3938,67 +3955,105 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
// probed
info.probed = false;
// count capture devices
// Count capture devices
errorText_.clear();
RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";
goto Exit;
}
hr = captureDevices->GetCount( &captureDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";
goto Exit;
}
// count render devices
// Count render devices
hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";
goto Exit;
}
hr = renderDevices->GetCount( &renderDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";
goto Exit;
}
// validate device index
if ( device >= captureDeviceCount + renderDeviceCount )
EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );
if ( device >= captureDeviceCount + renderDeviceCount ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";
errorType = RtAudioError::INVALID_USE;
goto Exit;
}
// determine whether index falls within capture or render devices
if ( device >= renderDeviceCount ) {
hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";
goto Exit;
}
isCaptureDevice = true;
}
else {
hr = renderDevices->Item( device, &devicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";
goto Exit;
}
isCaptureDevice = false;
}
// get default device name
if ( isCaptureDevice ) {
hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default render device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";
goto Exit;
}
}
else {
hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default capture device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";
goto Exit;
}
}
hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to open default device property store" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";
goto Exit;
}
PropVariantInit( &defaultDeviceNameProp );
hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default device property: PKEY_Device_FriendlyName" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";
goto Exit;
}
deviceName = defaultDeviceNameProp.pwszVal;
defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );
// name
hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to open device property store" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";
goto Exit;
}
PropVariantInit( &deviceNameProp );
hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device property: PKEY_Device_FriendlyName" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";
goto Exit;
}
deviceName = deviceNameProp.pwszVal;
info.name = std::string( deviceName.begin(), deviceName.end() );
@@ -4015,10 +4070,16 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
// channel count
hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";
goto Exit;
}
hr = audioClient->GetMixFormat( &deviceFormat );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";
goto Exit;
}
if ( isCaptureDevice ) {
info.inputChannels = deviceFormat->nChannels;
@@ -4034,18 +4095,9 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
// sample rates
info.sampleRates.clear();
// allow support for sample rates that are multiples of the base rate
// 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++ ) {
if ( SAMPLE_RATES[i] < deviceFormat->nSamplesPerSec ) {
if ( deviceFormat->nSamplesPerSec % SAMPLE_RATES[i] == 0 ) {
info.sampleRates.push_back( SAMPLE_RATES[i] );
}
}
else {
if ( SAMPLE_RATES[i] % deviceFormat->nSamplesPerSec == 0 ) {
info.sampleRates.push_back( SAMPLE_RATES[i] );
}
}
info.sampleRates.push_back( SAMPLE_RATES[i] );
}
// native format
@@ -4099,6 +4151,8 @@ Exit:
CoTaskMemFree( deviceFormat );
CoTaskMemFree( closestMatchFormat );
if ( !errorText_.empty() )
error( errorType );
return info;
}
@@ -4133,7 +4187,7 @@ unsigned int RtApiWasapi::getDefaultInputDevice( void )
void RtApiWasapi::closeStream( void )
{
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiWasapi::closeStream: No open stream to close";
errorText_ = "RtApiWasapi::closeStream: No open stream to close.";
error( RtAudioError::WARNING );
return;
}
@@ -4154,7 +4208,7 @@ void RtApiWasapi::closeStream( void )
if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )
CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );
delete stream_.apiHandle;
delete ( WasapiHandle* ) stream_.apiHandle;
stream_.apiHandle = NULL;
for ( int i = 0; i < 2; i++ ) {
@@ -4180,7 +4234,7 @@ void RtApiWasapi::startStream( void )
verifyStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiWasapi::startStream: The stream is already running";
errorText_ = "RtApiWasapi::startStream: The stream is already running.";
error( RtAudioError::WARNING );
return;
}
@@ -4189,10 +4243,10 @@ void RtApiWasapi::startStream( void )
stream_.state = STREAM_RUNNING;
// create WASAPI stream thread
stream_.callbackInfo.thread = ( unsigned int ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );
stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );
if ( !stream_.callbackInfo.thread ) {
errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread";
errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";
error( RtAudioError::THREAD_ERROR );
}
else {
@@ -4208,7 +4262,7 @@ void RtApiWasapi::stopStream( void )
verifyStream();
if ( stream_.state == STREAM_STOPPED ) {
errorText_ = "RtApiWasapi::stopStream: The stream is already stopped";
errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";
error( RtAudioError::WARNING );
return;
}
@@ -4228,8 +4282,9 @@ void RtApiWasapi::stopStream( void )
if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream";
errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";
error( RtAudioError::DRIVER_ERROR );
return;
}
}
@@ -4237,18 +4292,20 @@ void RtApiWasapi::stopStream( void )
if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream";
errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";
error( RtAudioError::DRIVER_ERROR );
return;
}
}
// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread";
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
error( RtAudioError::THREAD_ERROR );
return;
}
stream_.callbackInfo.thread = NULL;
stream_.callbackInfo.thread = (ThreadHandle) NULL;
}
//-----------------------------------------------------------------------------
@@ -4258,7 +4315,7 @@ void RtApiWasapi::abortStream( void )
verifyStream();
if ( stream_.state == STREAM_STOPPED ) {
errorText_ = "RtApiWasapi::abortStream: The stream is already stopped";
errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";
error( RtAudioError::WARNING );
return;
}
@@ -4275,8 +4332,9 @@ void RtApiWasapi::abortStream( void )
if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream";
errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";
error( RtAudioError::DRIVER_ERROR );
return;
}
}
@@ -4284,18 +4342,20 @@ void RtApiWasapi::abortStream( void )
if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream";
errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";
error( RtAudioError::DRIVER_ERROR );
return;
}
}
// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread";
errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";
error( RtAudioError::THREAD_ERROR );
return;
}
stream_.callbackInfo.thread = NULL;
stream_.callbackInfo.thread = (ThreadHandle) NULL;
}
//-----------------------------------------------------------------------------
@@ -4313,66 +4373,109 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
IMMDeviceCollection* renderDevices = NULL;
IMMDevice* devicePtr = NULL;
WAVEFORMATEX* deviceFormat = NULL;
unsigned int bufferBytes;
stream_.state = STREAM_STOPPED;
// create API Handle if not already created
if ( !stream_.apiHandle )
stream_.apiHandle = ( void* ) new WasapiHandle();
// count capture devices
// Count capture devices
errorText_.clear();
RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";
goto Exit;
}
hr = captureDevices->GetCount( &captureDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";
goto Exit;
}
// count render devices
// Count render devices
hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";
goto Exit;
}
hr = renderDevices->GetCount( &renderDeviceCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";
goto Exit;
}
// validate device index
if ( device >= captureDeviceCount + renderDeviceCount )
EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );
if ( device >= captureDeviceCount + renderDeviceCount ) {
errorType = RtAudioError::INVALID_USE;
errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";
goto Exit;
}
// determine whether index falls within capture or render devices
if ( device >= renderDeviceCount ) {
if ( mode != INPUT )
EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Capture device selected as output device" );
if ( mode != INPUT ) {
errorType = RtAudioError::INVALID_USE;
errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";
goto Exit;
}
// retrieve captureAudioClient from devicePtr
IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";
goto Exit;
}
hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
NULL, ( void** ) &captureAudioClient );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
goto Exit;
}
hr = captureAudioClient->GetMixFormat( &deviceFormat );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
goto Exit;
}
stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
}
else {
if ( mode != OUTPUT )
EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Render device selected as input device" );
if ( mode != OUTPUT ) {
errorType = RtAudioError::INVALID_USE;
errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";
goto Exit;
}
// retrieve renderAudioClient from devicePtr
IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
hr = renderDevices->Item( device, &devicePtr );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";
goto Exit;
}
hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
NULL, ( void** ) &renderAudioClient );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
goto Exit;
}
hr = renderAudioClient->GetMixFormat( &deviceFormat );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
goto Exit;
}
stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
@@ -4388,7 +4491,6 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
}
stream_.device[mode] = device;
stream_.state = STREAM_STOPPED;
stream_.doByteSwap[mode] = false;
stream_.sampleRate = sampleRate;
stream_.bufferSize = *bufferSize;
@@ -4416,11 +4518,14 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
setConvertInfo( mode, 0 );
// Allocate necessary internal buffers
unsigned int bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );
bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );
stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );
if ( !stream_.userBuffer[mode] )
EXIT_ON_ERROR( -1, RtAudioError::MEMORY_ERROR, "Error allocating user buffer memory" );
if ( !stream_.userBuffer[mode] ) {
errorType = RtAudioError::MEMORY_ERROR;
errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";
goto Exit;
}
if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )
stream_.callbackInfo.priority = 15;
@@ -4434,17 +4539,17 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
Exit:
//clean up
SAFE_RELEASE( captureDevices );
SAFE_RELEASE( renderDevices );
SAFE_RELEASE( devicePtr );
CoTaskMemFree( deviceFormat );
// if method failed, close the stream
if ( methodResult == FAILURE )
closeStream();
if ( !errorText_.empty() )
error( errorType );
return methodResult;
}
@@ -4497,6 +4602,26 @@ void RtApiWasapi::wasapiThread()
WasapiBuffer captureBuffer;
WasapiBuffer renderBuffer;
// declare local stream variables
RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
BYTE* streamBuffer = NULL;
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();
RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
// Attempt to assign "Pro Audio" characteristic to thread
HMODULE AvrtDll = LoadLibrary( "AVRT.dll" );
if ( AvrtDll ) {
@@ -4509,7 +4634,10 @@ void RtApiWasapi::wasapiThread()
// start capture stream if applicable
if ( captureAudioClient ) {
hr = captureAudioClient->GetMixFormat( &captureFormat );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
goto Exit;
}
captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );
@@ -4524,19 +4652,31 @@ void RtApiWasapi::wasapiThread()
desiredBufferPeriod,
captureFormat,
NULL );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize capture audio client" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";
goto Exit;
}
hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),
( void** ) &captureClient );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture client handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";
goto Exit;
}
// configure captureEvent to trigger on every available capture buffer
captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( !captureEvent )
EXIT_ON_ERROR( -1, RtAudioError::SYSTEM_ERROR, "Unable to create capture event" );
if ( !captureEvent ) {
errorType = RtAudioError::SYSTEM_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";
goto Exit;
}
hr = captureAudioClient->SetEventHandle( captureEvent );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set capture event handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";
goto Exit;
}
( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;
@@ -4544,7 +4684,10 @@ void RtApiWasapi::wasapiThread()
unsigned int inBufferSize = 0;
hr = captureAudioClient->GetBufferSize( &inBufferSize );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to get capture buffer size" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";
goto Exit;
}
// scale outBufferSize according to stream->user sample rate ratio
unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];
@@ -4555,17 +4698,26 @@ void RtApiWasapi::wasapiThread()
// reset the capture stream
hr = captureAudioClient->Reset();
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset capture stream" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
goto Exit;
}
// start the capture stream
hr = captureAudioClient->Start();
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start capture stream" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
goto Exit;
}
}
// start render stream if applicable
if ( renderAudioClient ) {
hr = renderAudioClient->GetMixFormat( &renderFormat );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
goto Exit;
}
renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );
@@ -4580,19 +4732,31 @@ void RtApiWasapi::wasapiThread()
desiredBufferPeriod,
renderFormat,
NULL );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize render audio client" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";
goto Exit;
}
hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),
( void** ) &renderClient );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render client handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";
goto Exit;
}
// configure renderEvent to trigger on every available render buffer
renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( !renderEvent )
EXIT_ON_ERROR( -1, RtAudioError::SYSTEM_ERROR, "Unable to create render event" );
if ( !renderEvent ) {
errorType = RtAudioError::SYSTEM_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";
goto Exit;
}
hr = renderAudioClient->SetEventHandle( renderEvent );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set render event handle" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";
goto Exit;
}
( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;
( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;
@@ -4600,7 +4764,10 @@ void RtApiWasapi::wasapiThread()
unsigned int outBufferSize = 0;
hr = renderAudioClient->GetBufferSize( &outBufferSize );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to get render buffer size" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";
goto Exit;
}
// scale inBufferSize according to user->stream sample rate ratio
unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];
@@ -4611,46 +4778,41 @@ void RtApiWasapi::wasapiThread()
// reset the render stream
hr = renderAudioClient->Reset();
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset render stream" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
goto Exit;
}
// start the render stream
hr = renderAudioClient->Start();
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start render stream" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";
goto Exit;
}
}
// declare local stream variables
RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
BYTE* streamBuffer = NULL;
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
unsigned int deviceBufferSize = 0;
if ( stream_.mode == INPUT ) {
deviceBufferSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
convBuffSize = ( size_t ) ( 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 ) {
deviceBufferSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[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 ) {
deviceBufferSize = 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] ) );
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] ) );
}
char* convBuffer = ( char* ) malloc( deviceBufferSize );
stream_.deviceBuffer = ( char* ) malloc( deviceBufferSize );
if ( !convBuffer || !stream_.deviceBuffer )
EXIT_ON_ERROR( -1, RtAudioError::MEMORY_ERROR, "Error allocating device buffer memory" );
convBuffer = ( char* ) malloc( convBuffSize );
stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );
if ( !convBuffer || !stream_.deviceBuffer ) {
errorType = RtAudioError::MEMORY_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
goto Exit;
}
// stream process loop
while ( stream_.state != STREAM_STOPPING ) {
@@ -4716,26 +4878,32 @@ void RtApiWasapi::wasapiThread()
// Handle return value from callback
if ( callbackResult == 1 ) {
// instantiate a thread to stop this thread
HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, NULL, NULL );
HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );
if ( !threadHandle ) {
EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream stop thread" );
errorType = RtAudioError::THREAD_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";
goto Exit;
}
else if ( !CloseHandle( threadHandle ) ) {
EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream stop thread handle" );
errorType = RtAudioError::THREAD_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";
goto Exit;
}
callbackStopped = true;
}
else if ( callbackResult == 2 ) {
// instantiate a thread to stop this thread
HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, NULL, NULL );
HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );
if ( !threadHandle ) {
EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream abort thread" );
errorType = RtAudioError::THREAD_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";
goto Exit;
}
else if ( !CloseHandle( threadHandle ) ) {
EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream abort thread handle" );
errorType = RtAudioError::THREAD_ERROR;
errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";
goto Exit;
}
callbackStopped = true;
@@ -4802,7 +4970,10 @@ void RtApiWasapi::wasapiThread()
hr = captureClient->GetBuffer( &streamBuffer,
&bufferFrameCount,
&captureFlags, NULL, NULL );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";
goto Exit;
}
if ( bufferFrameCount != 0 ) {
// Push capture buffer into inputBuffer
@@ -4812,20 +4983,29 @@ void RtApiWasapi::wasapiThread()
{
// Release capture buffer
hr = captureClient->ReleaseBuffer( bufferFrameCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
goto Exit;
}
}
else
{
// Inform WASAPI that capture was unsuccessful
hr = captureClient->ReleaseBuffer( 0 );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
goto Exit;
}
}
}
else
{
// Inform WASAPI that capture was unsuccessful
hr = captureClient->ReleaseBuffer( 0 );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
goto Exit;
}
}
}
@@ -4844,16 +5024,25 @@ void RtApiWasapi::wasapiThread()
// Get render buffer from stream
hr = renderAudioClient->GetBufferSize( &bufferFrameCount );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer size" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";
goto Exit;
}
hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer padding" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";
goto Exit;
}
bufferFrameCount -= numFramesPadding;
if ( bufferFrameCount != 0 ) {
hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";
goto Exit;
}
// Pull next buffer from outputBuffer
// Fill render buffer with next buffer
@@ -4863,20 +5052,29 @@ void RtApiWasapi::wasapiThread()
{
// Release render buffer
hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
goto Exit;
}
}
else
{
// Inform WASAPI that render was unsuccessful
hr = renderClient->ReleaseBuffer( 0, 0 );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
goto Exit;
}
}
}
else
{
// Inform WASAPI that render was unsuccessful
hr = renderClient->ReleaseBuffer( 0, 0 );
EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );
if ( FAILED( hr ) ) {
errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
goto Exit;
}
}
}
@@ -4901,6 +5099,11 @@ Exit:
// update stream state
stream_.state = STREAM_STOPPED;
if ( errorText_.empty() )
return;
else
error( errorType );
}
//******************** End of __WINDOWS_WASAPI__ *********************//