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

@@ -45,7 +45,7 @@
#ifndef __RTAUDIO_H #ifndef __RTAUDIO_H
#define __RTAUDIO_H #define __RTAUDIO_H
#define RTAUDIO_VERSION "4.1.0" #define RTAUDIO_VERSION "4.1.1pre"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -567,11 +567,15 @@ class RtAudio
}; };
// Operating system dependent thread functionality. // Operating system dependent thread functionality.
#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h> #include <windows.h>
#include <process.h> #include <process.h>
typedef ULONG_PTR ThreadHandle; typedef uintptr_t ThreadHandle;
typedef CRITICAL_SECTION StreamMutex; typedef CRITICAL_SECTION StreamMutex;
#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)

View File

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