summaryrefslogtreecommitdiff
path: root/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp')
-rw-r--r--libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp1697
1 files changed, 1697 insertions, 0 deletions
diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp
new file mode 100644
index 0000000000..2f6cd710ff
--- /dev/null
+++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp
@@ -0,0 +1,1697 @@
+//----------------------------------------------------------------------------------
+//
+// Copyright (c) 2008 Waves Audio Ltd. All rights reserved.
+//
+//! \file WCMRPortAudioDeviceManager.cpp
+//!
+//! WCMRPortAudioDeviceManager and related class declarations
+//!
+//---------------------------------------------------------------------------------*/
+#include "WCMRPortAudioDeviceManager.h"
+#include "MiscUtils/safe_delete.h"
+#include "UMicroseconds.h"
+#include <sstream>
+#include <algorithm>
+using namespace wvNS;
+#include "IncludeWindows.h"
+#include <MMSystem.h>
+#include "pa_asio.h"
+#include "asio.h"
+
+///< Supported Sample rates
+static const double gAllSampleRates[] =
+ {
+ 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */
+ };
+
+
+
+///< Default Supported Buffer Sizes.
+static const int gAllBufferSizes[] =
+ {
+ 32, 64, 96, 128, 192, 256, 512, 1024, 2048
+ };
+
+
+///< The default SR.
+static const int DEFAULT_SR = 44100;
+///< The default buffer size.
+static const int DEFAULT_BUFFERSIZE = 128;
+
+static const int NONE_DEVICE_ID = -1;
+
+///< Number of stalls to wait before notifying user...
+static const int NUM_STALLS_FOR_NOTIFICATION = 100; // 100 corresponds to 100 x 42 ms idle timer - about 4 seconds.
+static const int CHANGE_CHECK_COUNTER_PERIOD = 100; // 120 corresponds to 120 x 42 ms idle timer - about 4 seconds.
+
+#define HUNDRED_NANO_TO_MILLI_CONSTANT 10000
+#define CONSUMPTION_CALCULATION_INTERVAL 500 // Milli Seconds
+
+
+// This wrapper is used to adapt device DoIdle method as entry point for MS thread
+DWORD WINAPI WCMRPortAudioDevice::__DoIdle__(LPVOID lpThreadParameter)
+{
+ WCMRPortAudioDevice* pDevice = (WCMRPortAudioDevice*)lpThreadParameter;
+ pDevice->DoIdle();
+ return 0;
+}
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::WCMRPortAudioDevice
+//
+//! Constructor for the audio device. Opens the PA device
+//! and gets information about the device.
+//! Starts the thread which will process requests to this device
+//! such as determining supported sampling rates, buffer sizes, and channel counts.
+//!
+//! \param *pManager : The audio device manager that's managing this device.
+//! \param deviceID : The port audio device ID.
+//! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
+//!
+//! \return Nothing.
+//!
+//**********************************************************************************************
+WCMRPortAudioDevice::WCMRPortAudioDevice (WCMRPortAudioDeviceManager *pManager, unsigned int deviceID, bool useMultithreading, bool bNoCopy) :
+ WCMRNativeAudioDevice (pManager, useMultithreading, bNoCopy)
+ , m_SampleCounter(0)
+ , m_BufferSizeChangeRequested (0)
+ , m_BufferSizeChangeReported (0)
+ , m_ResetRequested (0)
+ , m_ResetReported (0)
+ , m_ResyncRequested (0)
+ , m_ResyncReported (0)
+ , m_DropsDetected(0)
+ , m_DropsReported(0)
+ , m_IgnoreThisDrop(true)
+ , m_hDeviceProcessingThread(NULL)
+ , m_DeviceProcessingThreadID(0)
+ , m_hUpdateDeviceInfoRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hUpdateDeviceInfoDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hActivateRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hActivationDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hDeActivateRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hDeActivationDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hStartStreamingRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hStartStreamingDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hStopStreamingRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hStopStreamingDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hResetRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hResetDone(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hResetFromDevRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hBufferSizeChangedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hSampleRateChangedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hExitIdleThread(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_hDeviceInitialized(CreateEvent(NULL, FALSE, FALSE, NULL))
+ , m_lastErr(eNoErr)
+{
+ AUTO_FUNC_DEBUG;
+
+ //Set initial device info...
+ m_DeviceID = deviceID;
+ m_PortAudioStream = NULL;
+ m_CurrentSamplingRate = DEFAULT_SR;
+ m_CurrentBufferSize = DEFAULT_BUFFERSIZE;
+ m_StopRequested = true;
+ m_pInputData = NULL;
+
+ //initialize device processing thread
+ //the divice become alive and now is able to process requests
+ m_hDeviceProcessingThread = CreateThread( NULL, 0, __DoIdle__, (LPVOID)this, 0, &m_DeviceProcessingThreadID );
+
+ if (!m_hDeviceProcessingThread)
+ {
+ DEBUG_MSG("API::Device " << m_DeviceName << " cannot create processing thread");
+ throw eGenericErr;
+ }
+
+ WaitForSingleObject(m_hDeviceInitialized, INFINITE);
+
+ if (ConnectionStatus() == DeviceErrors)
+ {
+ throw m_lastErr;
+ }
+}
+
+
+void WCMRPortAudioDevice::initDevice()
+{
+ // Initialize COM for this thread
+ std::cout << "API::Device " << m_DeviceID << " initializing COM" << std::endl;
+
+ if (S_OK == CoInitialize(NULL) )
+ {
+ // Initialize PA
+ Pa_Initialize();
+
+ updateDeviceInfo();
+
+ //should use a valid current SR...
+ if (m_SamplingRates.size())
+ {
+ //see if the current sr is present in the sr list, if not, use the first one!
+ std::vector<int>::iterator intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), m_CurrentSamplingRate);
+ if (intIter == m_SamplingRates.end())
+ {
+ //not found... use the first one
+ m_CurrentSamplingRate = m_SamplingRates[0];
+ }
+ }
+ else
+ std::cout << "API::Device " << m_DeviceName << " Device does not support any sample rate of ours" << std::endl;
+
+ //should use a valid current buffer size
+ if (m_BufferSizes.size())
+ {
+ //see if the current sr is present in the buffersize list, if not, use the first one!
+ std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), m_CurrentBufferSize);
+ if (intIter == m_BufferSizes.end())
+ {
+ //not found... use the first one
+ m_CurrentBufferSize = m_BufferSizes[0];
+ }
+ }
+
+ //build our input/output level lists
+ for (unsigned int currentChannel = 0; currentChannel < m_InputChannels.size(); currentChannel++)
+ {
+ m_InputLevels.push_back (0.0);
+ }
+
+ //build our input/output level lists
+ for (unsigned int currentChannel = 0; currentChannel < m_OutputChannels.size(); currentChannel++)
+ {
+ m_OutputLevels.push_back (0.0);
+ }
+
+ std::cout << "API::Device " << m_DeviceName << " Device has been initialized" << std::endl;
+ m_ConnectionStatus = DeviceDisconnected;
+ m_lastErr = eNoErr;
+ }
+ else
+ {
+ /*Replace with debug trace*/std::cout << "API::Device " << m_DeviceName << " cannot initialize COM" << std::endl;
+ DEBUG_MSG("Device " << m_DeviceName << " cannot initialize COM");
+ m_ConnectionStatus = DeviceErrors;
+ m_lastErr = eSomeThingNotInitailzed;
+ SetEvent(m_hExitIdleThread);
+ }
+
+ SetEvent(m_hDeviceInitialized);
+}
+
+void WCMRPortAudioDevice::terminateDevice()
+{
+ std::cout << "API::Device " << m_DeviceName << " Terminating DEVICE" << std::endl;
+
+ //If device is streaming, need to stop it!
+ if (Streaming())
+ {
+ stopStreaming();
+ }
+
+ //If device is active (meaning stream is open) we need to close it.
+ if (Active())
+ {
+ deactivateDevice();
+ }
+
+ std::cout << "API::Device " << m_DeviceName << " Terminating PA" << std::endl;
+
+ //Deinitialize PA
+ Pa_Terminate();
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::~WCMRPortAudioDevice
+//
+//! Destructor for the audio device. The base release all the connections that were created, if
+//! they have not been already destroyed! Here we simply stop streaming, and close device
+//! handles if necessary.
+//!
+//! \param none
+//!
+//! \return Nothing.
+//!
+//**********************************************************************************************
+WCMRPortAudioDevice::~WCMRPortAudioDevice ()
+{
+ AUTO_FUNC_DEBUG;
+
+ std::cout << "API::Destroying Device Instance: " << DeviceName() << std::endl;
+ try
+ {
+ //Stop deviceprocessing thread
+ SignalObjectAndWait(m_hExitIdleThread, m_hDeviceProcessingThread, INFINITE, false);
+
+ std::cout << "API::Device " << m_DeviceName << " Processing Thread is stopped" << std::endl;
+
+ CloseHandle(m_hDeviceProcessingThread);
+
+ //Now it's safe to free all event handlers
+ CloseHandle(m_hUpdateDeviceInfoRequestedEvent);
+ CloseHandle(m_hUpdateDeviceInfoDone);
+ CloseHandle(m_hActivateRequestedEvent);
+ CloseHandle(m_hActivationDone);
+ CloseHandle(m_hDeActivateRequestedEvent);
+ CloseHandle(m_hDeActivationDone);
+ CloseHandle(m_hStartStreamingRequestedEvent);
+ CloseHandle(m_hStartStreamingDone);
+ CloseHandle(m_hStopStreamingRequestedEvent);
+ CloseHandle(m_hStopStreamingDone);
+ CloseHandle(m_hResetRequestedEvent);
+ CloseHandle(m_hResetDone);
+ CloseHandle(m_hResetFromDevRequestedEvent);
+ CloseHandle(m_hBufferSizeChangedEvent);
+ CloseHandle(m_hSampleRateChangedEvent);
+ CloseHandle(m_hExitIdleThread);
+ CloseHandle(m_hDeviceInitialized);
+ }
+ catch (...)
+ {
+ //destructors should absorb exceptions, no harm in logging though!!
+ DEBUG_MSG ("Exception during destructor");
+ }
+}
+
+
+WTErr WCMRPortAudioDevice::UpdateDeviceInfo ()
+{
+ std::cout << "API::Device (ID:)" << m_DeviceID << " Updating device info" << std::endl;
+
+ SignalObjectAndWait(m_hUpdateDeviceInfoRequestedEvent, m_hUpdateDeviceInfoDone, INFINITE, false);
+
+ return eNoErr;
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::updateDeviceInfo
+//
+//! Must be called be device processing thread
+//! Updates Device Information about channels, sampling rates, buffer sizes.
+//!
+//! \return Nothing.
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/)
+{
+ AUTO_FUNC_DEBUG;
+
+ //get device info
+ const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
+
+ //update name.
+ m_DeviceName = pDeviceInfo->name;
+
+ std::cout << "API::Device " << m_DeviceName << " Getting device info " << std::endl;
+
+ //following parameters are needed opening test stream and for sample rates validation
+ PaStreamParameters inputParameters, outputParameters;
+ PaStreamParameters *pInS = NULL, *pOutS = NULL;
+
+ inputParameters.device = m_DeviceID;
+ inputParameters.channelCount = std::min<int>(2, pDeviceInfo->maxInputChannels);
+ inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
+ inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ inputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (inputParameters.channelCount)
+ pInS = &inputParameters;
+
+ outputParameters.device = m_DeviceID;
+ outputParameters.channelCount = std::min<int>(2, pDeviceInfo->maxOutputChannels);
+ outputParameters.sampleFormat = paFloat32;
+ outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ outputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (outputParameters.channelCount)
+ pOutS = &outputParameters;
+
+ std::cout << "API::Device" << m_DeviceName << " Updating sample rates " << std::endl;
+ ////////////////////////////////////////////////////////////////////////////////////
+ //update list of supported SRs...
+ m_SamplingRates.clear();
+
+ // now iterate through our standard SRs and check if they are supported by device
+ // store them for this device
+ for(int sr=0; gAllSampleRates[sr] > 0; sr++)
+ {
+ PaError err = Pa_IsFormatSupported(pInS, pOutS, gAllSampleRates[sr]);
+ if( err == paFormatIsSupported)
+ {
+ m_SamplingRates.push_back ((int)gAllSampleRates[sr]);
+ }
+ }
+
+ std::cout << "API::Device" << m_DeviceName << " Updating buffer sizes" << std::endl;
+ ///////////////////////////////////////////////////////////////////////////////////
+ //update buffer sizes
+ m_BufferSizes.clear();
+ bool useDefaultBuffers = true;
+ PaError paErr = paNoError;
+
+ //sometimes devices change buffer size if sample rate changes
+ //it updates buffer size during stream opening
+ //we need to find out how device would behave with current sample rate
+ //try opening test stream to load device driver for current sample rate and buffer size
+ //(skip this step if the device is Active)
+ if ( !Active() )
+ {
+ if (paNoError != testStateValidness(m_CurrentSamplingRate, m_CurrentBufferSize) )
+ {
+ //buffer size did change
+ Pa_Terminate();
+ Pa_Initialize();
+
+ // test validness with current sample rate and device prefered buffer size
+ paErr = testStateValidness(m_CurrentSamplingRate, 0);
+ }
+ }
+
+ if (paErr == paNoError)
+ {
+ // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel
+ long minSize, maxSize, preferredSize, granularity;
+ paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
+
+ if (paErr == paNoError)
+ {
+ std::cout << "API::Device " << m_DeviceName << " Buffers: " << minSize << " " << maxSize << " " << preferredSize << std::endl;
+
+ m_BufferSizes.push_back (preferredSize);
+ useDefaultBuffers = false;
+ }
+ else
+ {
+ std::cout << "API::Device" << m_DeviceName << " Preffered buffer size is not supported" << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "API::Device" << m_DeviceName << " Device does not start with sample rate: "<< m_CurrentSamplingRate << " and default buffer size" << std::endl;
+ }
+
+ if (useDefaultBuffers)
+ {
+ std::cout << "API::Device" << m_DeviceName << " Using default buffer sizes " <<std::endl;
+ for(int bsize=0; bsize < (sizeof(gAllBufferSizes)/sizeof(gAllBufferSizes[0])); bsize++)
+ m_BufferSizes.push_back (gAllBufferSizes[bsize]);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ //update channels info
+ {
+ int maxInputChannels = pDeviceInfo->maxInputChannels;
+ int maxOutputChannels = pDeviceInfo->maxOutputChannels;
+
+ //Update input channels
+ m_InputChannels.clear();
+ for (int channel = 0; channel < maxInputChannels; channel++)
+ {
+ std::stringstream chNameStream;
+ //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
+ chNameStream << "Input " << (channel+1);
+ m_InputChannels.push_back (chNameStream.str());
+ }
+
+
+ //Update output channels
+ m_OutputChannels.clear();
+ for (int channel = 0; channel < maxOutputChannels; channel++)
+ {
+ std::stringstream chNameStream;
+ //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
+ chNameStream << "Output " << (channel+1);
+ m_OutputChannels.push_back (chNameStream.str());
+ }
+ }
+
+ std::cout << "API::Device" << m_DeviceName << " Device info update has been finished" << std::endl;
+
+ if (callerIsWaiting)
+ SetEvent(m_hUpdateDeviceInfoDone);
+}
+
+
+PaError WCMRPortAudioDevice::testStateValidness(int sampleRate, int bufferSize)
+{
+ PaError paErr = paNoError;
+
+ //get device info
+ const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
+
+ //following parameters are needed opening test stream and for sample rates validation
+ PaStreamParameters inputParameters, outputParameters;
+ PaStreamParameters *pInS = NULL, *pOutS = NULL;
+
+ inputParameters.device = m_DeviceID;
+ inputParameters.channelCount = std::min<int>(2, pDeviceInfo->maxInputChannels);
+ inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
+ inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ inputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (inputParameters.channelCount)
+ pInS = &inputParameters;
+
+ outputParameters.device = m_DeviceID;
+ outputParameters.channelCount = std::min<int>(2, pDeviceInfo->maxOutputChannels);
+ outputParameters.sampleFormat = paFloat32;
+ outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ outputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (outputParameters.channelCount)
+ pOutS = &outputParameters;
+
+ PaStream *portAudioStream = NULL;
+
+ //sometimes devices change buffer size if sample rate changes
+ //it updates buffer size during stream opening
+ //we need to find out how device would behave with current sample rate
+ //try opening test stream to load device driver for current sample rate and buffer size
+ paErr = Pa_OpenStream (&portAudioStream, pInS, pOutS, m_CurrentSamplingRate, m_CurrentBufferSize, paDitherOff, NULL, NULL);
+
+ if (portAudioStream)
+ {
+ // close test stream
+ Pa_CloseStream (portAudioStream);
+ portAudioStream = NULL;
+ }
+
+ return paErr;
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::CurrentSamplingRate
+//
+//! The device's current sampling rate. This may be overridden, if the device needs to
+//! query the driver for the current rate.
+//!
+//! \param none
+//!
+//! \return The device's current sampling rate. -1 on error.
+//!
+//**********************************************************************************************
+int WCMRPortAudioDevice::CurrentSamplingRate ()
+{
+ AUTO_FUNC_DEBUG;
+ //ToDo: Perhaps for ASIO devices that are active, we should retrive the SR from the device...
+
+ return (m_CurrentSamplingRate);
+}
+
+
+WTErr WCMRPortAudioDevice::SetActive (bool newState)
+{
+ if (newState == true)
+ {
+ std::cout << "API::Device " << m_DeviceName << " Activation requested" << std::endl;
+ SignalObjectAndWait(m_hActivateRequestedEvent, m_hActivationDone, INFINITE, false);
+ }
+ else
+ {
+ std::cout << "API::Device " << m_DeviceName << " Deactivation requested" << std::endl;
+ SignalObjectAndWait(m_hDeActivateRequestedEvent, m_hDeActivationDone, INFINITE, false);
+ }
+
+ if (newState == Active() )
+ return eNoErr;
+ else
+ return eGenericErr;
+}
+
+
+WTErr WCMRPortAudioDevice::SetStreaming (bool newState)
+{
+ if (newState == true)
+ {
+ std::cout << "API::Device " << m_DeviceName << " Stream start requested" << std::endl;
+ SignalObjectAndWait(m_hStartStreamingRequestedEvent, m_hStartStreamingDone, INFINITE, false);
+ }
+ else
+ {
+ std::cout << "API::Device " << m_DeviceName << " Stream stop requested" << std::endl;
+ SignalObjectAndWait(m_hStopStreamingRequestedEvent, m_hStopStreamingDone, INFINITE, false);
+ }
+
+ if (newState == Streaming() )
+ return eNoErr;
+ else
+ return eGenericErr;
+}
+
+
+WTErr WCMRPortAudioDevice::ResetDevice()
+{
+ std::cout << "API::Device: " << m_DeviceName << " Reseting device" << std::endl;
+
+ SignalObjectAndWait(m_hResetRequestedEvent, m_hResetDone, INFINITE, false);
+
+ if (ConnectionStatus() == DeviceErrors)
+ {
+ return m_lastErr;
+ }
+
+ return eNoErr;
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::SetCurrentSamplingRate
+//
+//! Change the sampling rate to be used by the device.
+//!
+//! \param newRate : The rate to use (samples per sec).
+//!
+//! \return eNoErr always. The derived classes may return error codes.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::SetCurrentSamplingRate (int newRate)
+{
+ AUTO_FUNC_DEBUG;
+ std::vector<int>::iterator intIter;
+ WTErr retVal = eNoErr;
+
+ //changes the status.
+ int oldRate = CurrentSamplingRate();
+ bool oldActive = Active();
+
+ //no change, nothing to do
+ if (oldRate == newRate)
+ return (retVal);
+
+ //see if this is one of our supported rates...
+ intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), newRate);
+
+ if (intIter == m_SamplingRates.end())
+ {
+ //Can't change, perhaps use an "invalid param" type of error
+ retVal = eCommandLineParameter;
+ return (retVal);
+ }
+
+ if (Streaming())
+ {
+ //Can't change, perhaps use an "in use" type of error
+ retVal = eGenericErr;
+ return (retVal);
+ }
+
+ if (oldActive)
+ {
+ //Deactivate it for the change...
+ SetActive (false);
+ }
+
+ //make the change...
+ m_CurrentSamplingRate = newRate;
+
+ // Before reactivating the device: opening stream we should try getting buffer size update from the device
+ // because for new sampling rate some devices may change buffer size as well
+ int oldBufferSize = m_CurrentBufferSize;
+
+ retVal = ResetDevice();
+
+ //reactivate it.
+ if (oldActive && retVal == eNoErr)
+ {
+ retVal = SetActive (true);
+ }
+
+ if (retVal != eNoErr)
+ {
+ //revert changes if the device was not activated
+ m_CurrentSamplingRate = oldRate;
+ m_CurrentBufferSize = oldBufferSize;
+ int bufferSize = m_CurrentBufferSize;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize);
+ retVal = eCommandLineParameter;
+ }
+
+ return (retVal);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::CurrentBufferSize
+//
+//! The device's current buffer size in use. This may be overridden, if the device needs to
+//! query the driver for the current size.
+//!
+//! \param none
+//!
+//! \return The device's current buffer size. 0 on error.
+//!
+//**********************************************************************************************
+int WCMRPortAudioDevice::CurrentBufferSize ()
+{
+ return m_CurrentBufferSize;
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::SetCurrentBufferSize
+//
+//! Change the buffer size to be used by the device. This will most likely be overridden,
+//! the base class simply updates the member variable.
+//!
+//! \param newSize : The buffer size to use (in sample-frames)
+//!
+//! \return eNoErr always. The derived classes may return error codes.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize)
+{
+ AUTO_FUNC_DEBUG;
+ WTErr retVal = eNoErr;
+ std::vector<int>::iterator intIter;
+
+ //changes the status.
+ int oldSize = CurrentBufferSize();
+ bool oldActive = Active();
+
+ //same size, nothing to do.
+ if (oldSize == newSize)
+ return (retVal);
+
+ //see if this is one of our supported rates...
+ intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize);
+ if (intIter == m_BufferSizes.end())
+ {
+ //Can't change, perhaps use an "invalid param" type of error
+ retVal = eCommandLineParameter;
+ return (retVal);
+ }
+
+ if (Streaming())
+ {
+ //Can't change, perhaps use an "in use" type of error
+ retVal = eGenericErr;
+ return (retVal);
+ }
+
+ if (oldActive)
+ {
+ //Deactivate it for the change...
+ SetActive (false);
+ }
+
+ //make the change...
+ m_CurrentBufferSize = newSize;
+
+ //reactivate it.
+ if (oldActive)
+ {
+ retVal = SetActive (true);
+ }
+
+ return (retVal);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::ConnectionStatus
+//
+//! Retrieves the device's current connection status. This will most likely be overridden,
+//! in case some driver communication is required to query the status.
+//!
+//! \param none
+//!
+//! \return A ConnectionStates value.
+//!
+//**********************************************************************************************
+WCMRPortAudioDevice::ConnectionStates WCMRPortAudioDevice::ConnectionStatus ()
+{
+ AUTO_FUNC_DEBUG;
+ //ToDo: May want to do something more to extract the actual status!
+ return (m_ConnectionStatus);
+
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::activateDevice
+//
+//! IS CALLED BY PROCESS THREAD
+//! Sets the device into "active" state. Essentially, opens the PA device.
+//! If it's an ASIO device it may result in buffer size change in some cases.
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/)
+{
+ AUTO_FUNC_DEBUG;
+
+ PaError paErr = paNoError;
+
+ // if device is not active activate it
+ if (!Active() )
+ {
+ PaStreamParameters inputParameters, outputParameters;
+ PaStreamParameters *pInS = NULL, *pOutS = NULL;
+
+ const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
+ const PaHostApiInfo *pHostApiInfo = Pa_GetHostApiInfo(pDeviceInfo->hostApi);
+
+ inputParameters.device = m_DeviceID;
+ inputParameters.channelCount = (int)m_InputChannels.size();
+ inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
+ inputParameters.suggestedLatency = Pa_GetDeviceInfo(m_DeviceID)->defaultLowInputLatency;
+ inputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (inputParameters.channelCount)
+ pInS = &inputParameters;
+
+ outputParameters.device = m_DeviceID;
+ outputParameters.channelCount = (int)m_OutputChannels.size();
+ outputParameters.sampleFormat = paFloat32;
+ outputParameters.suggestedLatency = Pa_GetDeviceInfo(m_DeviceID)->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (outputParameters.channelCount)
+ pOutS = &outputParameters;
+
+ std::cout << "API::Device" << m_DeviceName << " Opening device stream " << std::endl;
+ std::cout << "Sample rate: " << m_CurrentSamplingRate << " buffer size: " << m_CurrentBufferSize << std::endl;
+ paErr = Pa_OpenStream(&m_PortAudioStream,
+ pInS,
+ pOutS,
+ m_CurrentSamplingRate,
+ m_CurrentBufferSize,
+ paDitherOff,
+ WCMRPortAudioDevice::TheCallback,
+ this);
+
+ if(paErr == paNoError)
+ {
+ m_DropsDetected = 0;
+ m_DropsReported = 0;
+ m_IgnoreThisDrop = true;
+
+ if (pHostApiInfo->type == paASIO)
+ {
+ m_BufferSizeChangeRequested = 0;
+ m_BufferSizeChangeReported = 0;
+ m_ResetRequested = 0;
+ m_ResetReported = 0;
+ m_ResyncRequested = 0;
+ m_ResyncReported = 0;
+ PaAsio_SetMessageHook (StaticASIOMessageHook, this);
+ }
+ m_IsActive = true;
+ m_ConnectionStatus = DeviceAvailable;
+ m_lastErr = eNoErr;
+ }
+ else
+ {
+ //failed, do not update device state
+ std::cout << "Failed to open pa stream stream " << paErr << std::endl;
+ DEBUG_MSG( "Failed to open pa stream stream " << paErr );
+ m_ConnectionStatus = DeviceErrors;
+ m_lastErr = eAsioFailed;
+ }
+
+
+ }
+
+ if (callerIsWaiting)
+ SetEvent(m_hActivationDone);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::deactivateDevice
+//
+//! IS CALLED BY PROCESS THREAD
+//! Sets the device into "inactive" state. Essentially, closes the PA device.
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::deactivateDevice (bool callerIsWaiting/*=false*/)
+{
+ AUTO_FUNC_DEBUG;
+
+ PaError paErr = paNoError;
+
+ if (Active() )
+ {
+ if (Streaming())
+ {
+ stopStreaming ();
+ }
+
+ if (m_PortAudioStream)
+ {
+ //close the stream first
+ std::cout << "API::Device" << m_DeviceName << " Closing device stream" << std::endl;
+ paErr = Pa_CloseStream (m_PortAudioStream);
+ if(paErr == paNoError)
+ {
+ m_PortAudioStream = NULL;
+ m_DropsDetected = 0;
+ m_DropsReported = 0;
+ m_IgnoreThisDrop = true;
+ m_BufferSizeChangeRequested = 0;
+ m_BufferSizeChangeReported = 0;
+ m_ResetRequested = 0;
+ m_ResetReported = 0;
+ m_ResyncRequested = 0;
+ m_ResyncReported = 0;
+ PaAsio_SetMessageHook (NULL, NULL);
+
+ //finaly set device state to "not active"
+ m_IsActive = false;
+ m_ConnectionStatus = DeviceDisconnected;
+ m_lastErr = eNoErr;
+ }
+ else
+ {
+ //failed, do not update device state
+ std::cout << "Failed to close pa stream stream " << paErr << std::endl;
+ DEBUG_MSG( "Failed to open pa stream stream " << paErr );
+ m_ConnectionStatus = DeviceErrors;
+ m_lastErr = eAsioFailed;
+ }
+ }
+ }
+
+ if (callerIsWaiting)
+ SetEvent(m_hDeActivationDone);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::startStreaming
+//
+//! Sets the devices into "streaming" state. Calls PA's Start stream routines.
+//! This roughly corresponds to calling Start on the lower level interface.
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::startStreaming (bool callerIsWaiting/*=false*/)
+{
+ AUTO_FUNC_DEBUG;
+
+ // proceed if the device is not streaming
+ if (!Streaming () )
+ {
+ PaError paErr = paNoError;
+ m_StopRequested = false;
+ m_SampleCounter = 0;
+
+ std::cout << "API::Device" << m_DeviceName << " Starting device stream" << std::endl;
+ paErr = Pa_StartStream( m_PortAudioStream );
+
+ if(paErr == paNoError)
+ {
+ // if the stream was started successfully
+ m_IsStreaming = true;
+ }
+ else
+ {
+ std::cout << "Failed to start PA stream: " << paErr << std::endl;
+ DEBUG_MSG( "Failed to start PA stream: " << paErr );
+ m_lastErr = eGenericErr;
+ }
+ }
+
+ if (callerIsWaiting)
+ SetEvent(m_hStartStreamingDone);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::stopStreaming
+//
+//! Sets the devices into "not streaming" state. Calls PA's Stop stream routines.
+//! This roughly corresponds to calling Stop on the lower level interface.
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::stopStreaming (bool callerIsWaiting/*=false*/)
+{
+ AUTO_FUNC_DEBUG;
+
+ // proceed if the device is streaming
+ if (Streaming () )
+ {
+ PaError paErr = paNoError;
+ m_StopRequested = true;
+
+ std::cout << "API::Device " << m_DeviceName << " Stopping device stream" << std::endl;
+ paErr = Pa_StopStream( m_PortAudioStream );
+
+ if(paErr == paNoError)
+ {
+ // if the stream was stopped successfully
+ m_IsStreaming = false;
+ m_pInputData = NULL;
+ }
+ else
+ {
+ std::cout << "Failed to stop PA stream: " << paErr << std::endl;
+ DEBUG_MSG( "Failed to stop PA stream " << paErr );
+ m_lastErr = eGenericErr;
+ }
+ }
+
+ if (callerIsWaiting)
+ SetEvent(m_hStopStreamingDone);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::resetDevice
+//
+//! Resets the device, updates device info. Importnat: does PA reinitialization calling
+//! Pa_terminate/Pa_initialize functions.
+//!
+//! \param none
+//!
+//! \return nothing
+//!
+//**********************************************************************************************
+void WCMRPortAudioDevice::resetDevice (bool callerIsWaiting /*=false*/ )
+{
+ std::cout << "API::Device" << m_DeviceName << "Reseting device" << std::endl;
+
+ // Keep device sates
+ bool wasStreaming = Streaming();
+ bool wasActive = Active();
+
+ // Notify the Application about reset
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
+
+ // Reset the device
+ stopStreaming();
+ deactivateDevice();
+
+ // Reinitialize PA
+ Pa_Terminate();
+ Pa_Initialize();
+
+ updateDeviceInfo();
+
+ // Cache device buffer size as it might be changed during reset
+ int oldBufferSize = m_CurrentBufferSize;
+
+ // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel
+ // Backend should always use preffered buffer size value in this case
+ long minSize, maxSize, preferredSize, granularity;
+ PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
+
+ if (paErr == paNoError)
+ {
+ m_CurrentBufferSize = preferredSize;
+ }
+ else
+ {
+ // if we can't get device buffer sizes, use the first one among supported
+ if (m_BufferSizes.size() != 0)
+ m_CurrentBufferSize = m_BufferSizes.front();
+ }
+
+ // Notify the Application about device setting changes
+ if (oldBufferSize != m_CurrentBufferSize)
+ {
+ std::cout << "API::Device" << m_DeviceName << " buffer size changed" << std::endl;
+ int bufferSize = m_CurrentBufferSize;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize);
+ }
+
+ // Activate the device if it was active before
+ if (wasActive)
+ activateDevice();
+
+ // Resume streaming if the device was streaming before
+ if(wasStreaming)
+ {
+ // Notify the Application to prepare for the stream start
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStartsStreaming);
+ startStreaming();
+ }
+
+ if (callerIsWaiting)
+ SetEvent(m_hResetDone);
+}
+
+
+#ifdef _WINDOWS
+
+long WCMRPortAudioDevice::StaticASIOMessageHook (void *pRefCon, long selector, long value, void* message, double* opt)
+{
+ if (pRefCon)
+ {
+ return ((WCMRPortAudioDevice*)(pRefCon))->ASIOMessageHook (selector, value, message, opt);
+ }
+ else
+ return -1;
+}
+
+long WCMRPortAudioDevice::ASIOMessageHook (long selector, long WCUNUSEDPARAM(value), void* WCUNUSEDPARAM(message), double* WCUNUSEDPARAM(opt))
+{
+ switch(selector)
+ {
+ case kAsioBufferSizeChange:
+ m_BufferSizeChangeRequested++;
+ std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- m_BufferSizeChangeRequested" << std::endl;
+ SetEvent(m_hBufferSizeChangedEvent);
+ break;
+
+ case kAsioResetRequest:
+ m_ResetRequested++;
+ std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResetRequest" << std::endl;
+ SetEvent(m_hResetFromDevRequestedEvent);
+ break;
+
+ case kAsioResyncRequest:
+ std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResyncRequest" << std::endl;
+ m_ResyncRequested++;
+ break;
+
+ case kAsioLatenciesChanged:
+ std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioLatenciesChanged" << std::endl;
+ SetEvent(m_hBufferSizeChangedEvent);
+ m_BufferSizeChangeRequested++;
+ break;
+
+ case kAsioOverload:
+ std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioOverload" << std::endl;
+ m_DropsDetected++;
+ break;
+ }
+ return 0;
+}
+
+#endif
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::DoIdle
+//
+//! A place for doing idle time processing. The other derived classes will probably do something
+//! meaningful.
+//!
+//! \param none
+//!
+//! \return eNoErr always.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::DoIdle ()
+{
+ WTErr retVal = eNoErr;
+
+ std::cout << "WCMRPortAudioDevice::DoIdle ()" << std::endl;
+ HANDLE hEvents[] =
+ {
+ m_hUpdateDeviceInfoRequestedEvent,
+ m_hActivateRequestedEvent,
+ m_hDeActivateRequestedEvent,
+ m_hStartStreamingRequestedEvent,
+ m_hStopStreamingRequestedEvent,
+ m_hBufferSizeChangedEvent,
+ m_hSampleRateChangedEvent,
+ m_hResetRequestedEvent,
+ m_hResetFromDevRequestedEvent,
+ m_hExitIdleThread
+ };
+
+ const size_t hEventsSize = sizeof(hEvents)/sizeof(hEvents[0]);
+
+ initDevice();
+
+ for(;;)
+ {
+ DWORD result = WaitForMultipleObjects (hEventsSize, hEvents, FALSE, INFINITE);
+ result = result - WAIT_OBJECT_0;
+
+ if ((result < 0) || (result >= hEventsSize)) {
+ std::cout << "\t\t\t\t\t\t\tWCMRPortAudioDevice::DoIdle () -> (result < 0) || (result >= hEventsSize):" << result << std::endl;
+ retVal = eGenericErr;
+ break;
+ }
+
+ if (hEvents[result] == m_hExitIdleThread) {
+ std::cout << "\t\t\t\t\t\t\tWCMRPortAudioDevice::DoIdle () -> m_hExitIdleThread" << result << std::endl;
+ retVal = eNoErr;
+ break;
+ }
+
+ if (hEvents[result] == m_hUpdateDeviceInfoRequestedEvent) {
+ std::cout << "\t\t\t\t\t\tupdate requested ..." << std::endl;
+ updateDeviceInfo(true);
+ }
+
+ if (hEvents[result] == m_hActivateRequestedEvent) {
+ std::cout << "\t\t\t\t\t\tactivation requested ..." << std::endl;
+ activateDevice(true);
+ }
+
+ if (hEvents[result] == m_hDeActivateRequestedEvent) {
+ std::cout << "\t\t\t\t\t\tdeactivation requested ..." << std::endl;
+ deactivateDevice(true);
+ }
+
+ if (hEvents[result] == m_hStartStreamingRequestedEvent) {
+ std::cout << "\t\t\t\t\t\tStart stream requested ..." << std::endl;
+ startStreaming(true);
+ }
+
+ if (hEvents[result] == m_hStopStreamingRequestedEvent) {
+ std::cout << "\t\t\t\t\t\tStop stream requested ..." << std::endl;
+ stopStreaming(true);
+ }
+
+ if (hEvents[result] == m_hResetRequestedEvent) {
+ std::cout << "\t\t\t\t\t\treset requested ..." << std::endl;
+ resetDevice(true);
+ }
+
+ if (hEvents[result] == m_hResetFromDevRequestedEvent) {
+ std::cout << "\t\t\t\t\t\treset requested from device..." << std::endl;
+ resetDevice();
+ }
+
+ if (hEvents[result] == m_hBufferSizeChangedEvent) {
+ std::cout << "\t\t\t\t\t\tbuffer size changed from device..." << std::endl;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged);
+ }
+
+ if (hEvents[result] == m_hSampleRateChangedEvent) {
+ std::cout << "\t\t\t\t\t\tsample rate changed from device..." << std::endl;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::SamplingRateChanged);
+ }
+ }
+
+ terminateDevice();
+
+ return retVal;
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::SetMonitorChannels
+//
+//! Used to set the channels to be used for monitoring.
+//!
+//! \param leftChannel : Left monitor channel index.
+//! \param rightChannel : Right monitor channel index.
+//!
+//! \return eNoErr always, the derived classes may return appropriate errors.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::SetMonitorChannels (int leftChannel, int rightChannel)
+{
+ AUTO_FUNC_DEBUG;
+ //This will most likely be overridden, the base class simply
+ //changes the member.
+ m_LeftMonitorChannel = leftChannel;
+ m_RightMonitorChannel = rightChannel;
+ return (eNoErr);
+}
+
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::SetMonitorGain
+//
+//! Used to set monitor gain (or atten).
+//!
+//! \param newGain : The new gain or atten. value to use. Specified as a linear multiplier (not dB)
+//!
+//! \return eNoErr always, the derived classes may return appropriate errors.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::SetMonitorGain (float newGain)
+{
+ AUTO_FUNC_DEBUG;
+ //This will most likely be overridden, the base class simply
+ //changes the member.
+
+ m_MonitorGain = newGain;
+ return (eNoErr);
+}
+
+
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::ShowConfigPanel
+//
+//! Used to show device specific config/control panel. Some interfaces may not support it.
+//! Some interfaces may require the device to be active before it can display a panel.
+//!
+//! \param pParam : A device/interface specific parameter, should be the app window handle for ASIO.
+//!
+//! \return eNoErr always, the derived classes may return errors.
+//!
+//**********************************************************************************************
+WTErr WCMRPortAudioDevice::ShowConfigPanel (void *pParam)
+{
+ AUTO_FUNC_DEBUG;
+ WTErr retVal = eNoErr;
+
+ if (Active())
+ {
+#ifdef _WINDOWS
+ if(Pa_GetHostApiInfo(Pa_GetDeviceInfo(m_DeviceID)->hostApi)->type == paASIO)
+ {
+ // stop and deactivate the device
+ bool wasStreaming = Streaming();
+ SetActive(false);
+ // show control panel for the device
+ if (PaAsio_ShowControlPanel (m_DeviceID, pParam) != paNoError)
+ retVal = eGenericErr;
+ // reset device to pick up changes
+ ResetDevice();
+ // restore previous state for the device
+ SetActive(true);
+ if (wasStreaming)
+ SetStreaming(true);
+ }
+#else
+ pParam = pParam;
+#endif //_windows
+ }
+
+ return (retVal);
+}
+
+
+//*****************************************************************************************************
+// WCMRPortAudioDevice::TheCallback
+//
+//! The (static) Port Audio Callback function. This is a static member. It calls on the AudioCallback in the
+//! WCMRPortAudioDevice to do the real work.
+//!
+//! \param pInputBuffer: pointer to input buffer.
+//! \param pOutputBuffer: pointer to output buffer.
+//! \param framesPerBuffer: number of sample frames per buffer.
+//! \param pTimeInfo: time info for PaStream callback.
+//! \param statusFlags:
+//! \param pUserData: pointer to user data, in our case the WCMRPortAudioDevice object.
+//!
+//! \return true to stop streaming else returns false.
+//******************************************************************************************************
+int WCMRPortAudioDevice::TheCallback (const void *pInputBuffer, void *pOutputBuffer, unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* /*pTimeInfo*/, PaStreamCallbackFlags statusFlags, void *pUserData )
+{
+ WCMRPortAudioDevice *pMyDevice = (WCMRPortAudioDevice *)pUserData;
+ if (pMyDevice)
+ return pMyDevice->AudioCallback ((float *)pInputBuffer, (float *)pOutputBuffer, framesPerBuffer,
+ (statusFlags & (paInputOverflow | paOutputUnderflow)) != 0);
+ else
+ return (true);
+
+}
+
+
+
+//**********************************************************************************************
+// WCMRPortAudioDevice::AudoiCallback
+//
+//! Here's where the actual audio processing happens. We call upon all the active connections'
+//! sinks to provide data to us which can be put/mixed in the output buffer! Also, we make the
+//! input data available to any sources that may call upon us during this time!
+//!
+//! \param *pInputBuffer : Points to a buffer with recorded data.
+//! \param *pOutputBuffer : Points to a buffer to receive playback data.
+//! \param framesPerBuffer : Number of sample frames in input and output buffers. Number of channels,
+//! which are interleaved, is fixed at Device Open (Active) time. In this implementation,
+//! the number of channels are fixed to use the maximum available.
+//! \param dropsDetected : True if dropouts were detected in input or output. Can be used to signal the GUI.
+//!
+//! \return true
+//!
+//**********************************************************************************************
+int WCMRPortAudioDevice::AudioCallback( const float *pInputBuffer, float *pOutputBuffer, unsigned long framesPerBuffer, bool dropsDetected )
+{
+ UMicroseconds theStartTime;
+
+ // detect drops
+ if (dropsDetected)
+ {
+ if (m_IgnoreThisDrop)
+ m_IgnoreThisDrop = false; //We'll ignore once, just once!
+ else
+ m_DropsDetected++;
+ }
+
+ m_pInputData = pInputBuffer;
+
+ // VKamyshniy: Is this a right place to call the client???:
+ struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData =
+ {
+ m_pInputData,
+ pOutputBuffer,
+ framesPerBuffer,
+ m_SampleCounter,
+ theStartTime.MicroSeconds()*1000
+ };
+
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData );
+
+ //Don't try to access after this call returns!
+ m_pInputData = NULL;
+
+ m_SampleCounter += framesPerBuffer;
+
+ return m_StopRequested;
+}
+
+
+
+
+//**********************************************************************************************
+// WCMRPortAudioDeviceManager::WCMRPortAudioDeviceManager
+//
+//! The constructuor, we initialize PA, and build the device list.
+//!
+//! \param *pTheClient : The manager's client object (which receives notifications).
+//! \param interfaceType : The PortAudio interface type to use for this manager - acts as a filter.
+//! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
+//!
+//! \return Nothing.
+//!
+//**********************************************************************************************
+WCMRPortAudioDeviceManager::WCMRPortAudioDeviceManager (WCMRAudioDeviceManagerClient *pTheClient,
+ eAudioDeviceFilter eCurAudioDeviceFilter, bool useMultithreading, bool bNocopy)
+ : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter)
+ , m_NoneDevice(0)
+ , m_UseMultithreading(useMultithreading)
+ , m_bNoCopyAudioBuffer(bNocopy)
+{
+ AUTO_FUNC_DEBUG;
+ std::cout << "API::PortAudioDeviceManager::PA Device manager constructor" << std::endl;
+
+ //Always create the None device first...
+ m_NoneDevice = new WCMRNativeAudioNoneDevice(this);
+
+ WTErr err = generateDeviceListImpl();
+
+ if (eNoErr != err)
+ throw err;
+
+ timeBeginPeriod (1);
+}
+
+
+//**********************************************************************************************
+// WCMRPortAudioDeviceManager::~WCMRPortAudioDeviceManager
+//
+//! It clears the device list, releasing each of the device.
+//!
+//! \param none
+//!
+//! \return Nothing.
+//!
+//**********************************************************************************************
+WCMRPortAudioDeviceManager::~WCMRPortAudioDeviceManager()
+{
+ AUTO_FUNC_DEBUG;
+
+ std::cout << "API::Destroying PortAudioDeviceManager " << std::endl;
+
+ try
+ {
+ delete m_NoneDevice;
+ }
+ catch (...)
+ {
+ //destructors should absorb exceptions, no harm in logging though!!
+ DEBUG_MSG ("Exception during destructor");
+ }
+
+ timeEndPeriod (1);
+}
+
+
+WCMRAudioDevice* WCMRPortAudioDeviceManager::initNewCurrentDeviceImpl(const std::string & deviceName)
+{
+ destroyCurrentDeviceImpl();
+
+ std::cout << "API::PortAudioDeviceManager::initNewCurrentDevice " << deviceName << std::endl;
+ if (deviceName == m_NoneDevice->DeviceName() )
+ {
+ m_CurrentDevice = m_NoneDevice;
+ return m_CurrentDevice;
+ }
+
+ DeviceInfo devInfo;
+ WTErr err = GetDeviceInfoByName(deviceName, devInfo);
+
+ if (eNoErr == err)
+ {
+ try
+ {
+ std::cout << "API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName << std::endl;
+ TRACE_MSG ("API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName);
+
+ m_CurrentDevice = new WCMRPortAudioDevice (this, devInfo.m_DeviceId, m_UseMultithreading, m_bNoCopyAudioBuffer);
+ }
+ catch (...)
+ {
+ std::cout << "Unabled to create PA Device: " << devInfo.m_DeviceId << std::endl;
+ DEBUG_MSG ("Unabled to create PA Device: " << devInfo.m_DeviceId);
+ }
+ }
+
+ return m_CurrentDevice;
+}
+
+
+void WCMRPortAudioDeviceManager::destroyCurrentDeviceImpl()
+{
+ if (m_CurrentDevice != m_NoneDevice)
+ delete m_CurrentDevice;
+
+ m_CurrentDevice = 0;
+}
+
+
+WTErr WCMRPortAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceId, std::vector<int>& sampleRates)
+{
+ sampleRates.clear();
+ const PaDeviceInfo *pPaDeviceInfo = Pa_GetDeviceInfo(deviceId);
+
+ //now find supported sample rates
+ //following parameters are needed for sample rates validation
+ PaStreamParameters inputParameters, outputParameters;
+ PaStreamParameters *pInS = NULL, *pOutS = NULL;
+
+ inputParameters.device = deviceId;
+ inputParameters.channelCount = std::min<int>(2, pPaDeviceInfo->maxInputChannels);
+ inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
+ inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ inputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (inputParameters.channelCount)
+ pInS = &inputParameters;
+
+ outputParameters.device = deviceId;
+ outputParameters.channelCount = std::min<int>(2, pPaDeviceInfo->maxOutputChannels);
+ outputParameters.sampleFormat = paFloat32;
+ outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+ outputParameters.hostApiSpecificStreamInfo = 0;
+
+ if (outputParameters.channelCount)
+ pOutS = &outputParameters;
+
+ for(int sr=0; gAllSampleRates[sr] > 0; sr++)
+ {
+ if( paFormatIsSupported == Pa_IsFormatSupported(pInS, pOutS, gAllSampleRates[sr]) )
+ {
+ sampleRates.push_back ((int)gAllSampleRates[sr]);
+ }
+ }
+}
+
+
+WTErr WCMRPortAudioDeviceManager::generateDeviceListImpl()
+{
+ std::cout << "API::PortAudioDeviceManager::Generating device list" << std::endl;
+
+ WTErr retVal = eNoErr;
+
+ //Initialize PortAudio and ASIO first
+ PaError paErr = Pa_Initialize();
+
+ if (paErr != paNoError)
+ {
+ //ToDo: throw an exception here!
+ retVal = eSomeThingNotInitailzed;
+ return retVal;
+ }
+
+ // lock DeviceInfoVec firts
+ wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
+
+ if (m_NoneDevice)
+ {
+ DeviceInfo *pDevInfo = new DeviceInfo(NONE_DEVICE_ID, m_NoneDevice->DeviceName() );
+ pDevInfo->m_AvailableSampleRates = m_NoneDevice->SamplingRates();
+ m_DeviceInfoVec.push_back(pDevInfo);
+ }
+
+ //Get device count...
+ int numDevices = Pa_GetDeviceCount();
+
+ //for each device,
+ for (int thisDeviceID = 0; thisDeviceID < numDevices; thisDeviceID++)
+ {
+ //if it's of the required type...
+ const PaDeviceInfo *pPaDeviceInfo = Pa_GetDeviceInfo(thisDeviceID);
+
+ if (Pa_GetHostApiInfo(pPaDeviceInfo->hostApi)->type == paASIO)
+ {
+ //build a device object...
+ try
+ {
+ std::cout << "API::PortAudioDeviceManager::DeviceID: " << thisDeviceID << ", Device Name: " << pPaDeviceInfo->name << std::endl;
+ TRACE_MSG ("PA DeviceID: " << thisDeviceID << ", Device Name: " << pPaDeviceInfo->name);
+
+ DeviceInfo *pDevInfo = new DeviceInfo(thisDeviceID, pPaDeviceInfo->name);
+ if (pDevInfo)
+ {
+ std::vector<int> availableSampleRates;
+ WTErr wErr = WCMRPortAudioDeviceManager::getDeviceAvailableSampleRates(thisDeviceID, availableSampleRates);
+
+ if (wErr != eNoErr)
+ {
+ DEBUG_MSG ("Failed to get device available sample rates. Device ID: " << m_DeviceID);
+ delete pDevInfo;
+ continue; //proceed to the next device
+ }
+
+ pDevInfo->m_AvailableSampleRates = availableSampleRates;
+ pDevInfo->m_MaxInputChannels = pPaDeviceInfo->maxInputChannels;
+ pDevInfo->m_MaxOutputChannels = pPaDeviceInfo->maxOutputChannels;
+
+ //Now check if this device is acceptable according to current input/output settings
+ bool bRejectDevice = false;
+ switch(m_eAudioDeviceFilter)
+ {
+ case eInputOnlyDevices:
+ if (pDevInfo->m_MaxInputChannels != 0)
+ {
+ m_DeviceInfoVec.push_back(pDevInfo);
+ }
+ else
+ {
+ // Delete unnecesarry device
+ bRejectDevice = true;
+ }
+ break;
+ case eOutputOnlyDevices:
+ if (pDevInfo->m_MaxOutputChannels != 0)
+ {
+ m_DeviceInfoVec.push_back(pDevInfo);
+ }
+ else
+ {
+ // Delete unnecesarry device
+ bRejectDevice = true;
+ }
+ break;
+ case eFullDuplexDevices:
+ if (pDevInfo->m_MaxInputChannels != 0 && pDevInfo->m_MaxOutputChannels != 0)
+ {
+ m_DeviceInfoVec.push_back(pDevInfo);
+ }
+ else
+ {
+ // Delete unnecesarry device
+ bRejectDevice = true;
+ }
+ break;
+ case eAllDevices:
+ default:
+ m_DeviceInfoVec.push_back(pDevInfo);
+ break;
+ }
+
+ if(bRejectDevice)
+ {
+ TRACE_MSG ("API::PortAudioDeviceManager::Device " << pDevInfo->m_DeviceName << "Rejected. \
+ In Channels = " << pDevInfo->m_MaxInputChannels << "Out Channels = " <<pDevInfo->m_MaxOutputChannels );
+ delete pDevInfo;
+ }
+ }
+ }
+ catch (...)
+ {
+ std::cout << "API::PortAudioDeviceManager::Unabled to create PA Device: " << std::endl;
+ DEBUG_MSG ("Unabled to create PA Device: " << thisDeviceID);
+ }
+ }
+ }
+
+ //If no devices were found, that's not a good thing!
+ if (m_DeviceInfoVec.empty() )
+ {
+ std::cout << "API::PortAudioDeviceManager::No matching PortAudio devices were found, total PA devices = " << numDevices << std::endl;
+ DEBUG_MSG ("No matching PortAudio devices were found, total PA devices = " << numDevices);
+ }
+
+ //we don't need PA initialized right now
+ Pa_Terminate();
+
+ return retVal;
+}
+
+
+WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& buffers) const
+{
+ WTErr retVal = eNoErr;
+ std::cout << "API::PortAudioDeviceManager::GetBufferSizes: getting buffer size for device: "<< deviceName << std::endl;
+ //first check if the request has been made for None device
+ if (deviceName == m_NoneDevice->DeviceName() )
+ {
+ buffers = m_NoneDevice->BufferSizes();
+ return retVal;
+ }
+
+ //if we have current device initialized and it's PA device, reset it
+ //this procedure will reset PA corrently and update info for all PA devices as well
+
+ bool paLocalInit = false;
+ WCMRPortAudioDevice* portaudioDevice = dynamic_cast<WCMRPortAudioDevice*>(m_CurrentDevice);
+ if (portaudioDevice)
+ {
+ portaudioDevice->ResetDevice();
+ }
+ else
+ {
+ //initialize PA to get buffers for the device
+ Pa_Initialize();
+ paLocalInit = true;
+ }
+
+ DeviceInfo devInfo;
+ retVal = GetDeviceInfoByName(deviceName, devInfo);
+
+ if (eNoErr == retVal)
+ {
+ //make PA request to get actual device buffer sizes
+ long minSize, maxSize, preferredSize, granularity;
+ PaError paErr = PaAsio_GetAvailableBufferSizes(devInfo.m_DeviceId, &minSize, &maxSize, &preferredSize, &granularity);
+
+ //for Windows ASIO devices we always use prefferes buffer size ONLY
+ if (paNoError == paErr )
+ {
+ buffers.push_back(preferredSize);
+ }
+ else
+ {
+ retVal = eAsioFailed;
+ std::cout << "API::PortAudioDeviceManager::GetBufferSizes: error: " << paErr << " getting buffer size fo device: "<< deviceName << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "API::PortAudioDeviceManager::GetBufferSizes: Device not found: "<< deviceName << std::endl;
+ }
+
+ //deinitialize PA now
+ if (paLocalInit)
+ Pa_Terminate();
+
+ return retVal;
+}