1 //----------------------------------------------------------------------------------
3 // Copyright (c) 2008 Waves Audio Ltd. All rights reserved.
5 //! \file WCMRPortAudioDeviceManager.cpp
7 //! WCMRPortAudioDeviceManager and related class declarations
9 //---------------------------------------------------------------------------------*/
10 #include "WCMRPortAudioDeviceManager.h"
11 #include "MiscUtils/safe_delete.h"
12 #include "UMicroseconds.h"
18 #include "IncludeWindows.h"
23 #define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 200
24 #define DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS 500
25 #define PROPERTY_CHANGE_TIMEOUT_SECONDS 2
26 #define PROPERTY_CHANGE_RETRIES 3
28 ///< Supported Sample rates
29 static const double gAllSampleRates[] =
31 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 -1 /* negative terminated list */
36 ///< Default Supported Buffer Sizes.
37 static const int gAllBufferSizes[] =
39 32, 64, 96, 128, 192, 256, 512, 1024, 2048
44 static const int DEFAULT_SR = 44100;
45 ///< The default buffer size.
46 static const int DEFAULT_BUFFERSIZE = 128;
48 static const int NONE_DEVICE_ID = -1;
50 ///< Number of stalls to wait before notifying user...
51 static const int NUM_STALLS_FOR_NOTIFICATION = 100; // 100 corresponds to 100 x 42 ms idle timer - about 4 seconds.
52 static const int CHANGE_CHECK_COUNTER_PERIOD = 100; // 120 corresponds to 120 x 42 ms idle timer - about 4 seconds.
54 #define HUNDRED_NANO_TO_MILLI_CONSTANT 10000
55 #define CONSUMPTION_CALCULATION_INTERVAL 500 // Milli Seconds
58 // This wrapper is used to adapt device DoIdle method as entry point for MS thread
59 DWORD WINAPI WCMRPortAudioDevice::__DoIdle__(LPVOID lpThreadParameter)
61 WCMRPortAudioDevice* pDevice = (WCMRPortAudioDevice*)lpThreadParameter;
66 //**********************************************************************************************
67 // WCMRPortAudioDevice::WCMRPortAudioDevice
69 //! Constructor for the audio device. Opens the PA device
70 //! and gets information about the device.
71 //! Starts the thread which will process requests to this device
72 //! such as determining supported sampling rates, buffer sizes, and channel counts.
74 //! \param *pManager : The audio device manager that's managing this device.
75 //! \param deviceID : The port audio device ID.
76 //! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
80 //**********************************************************************************************
81 WCMRPortAudioDevice::WCMRPortAudioDevice (WCMRPortAudioDeviceManager *pManager, unsigned int deviceID, bool useMultithreading, bool bNoCopy) :
82 WCMRNativeAudioDevice (pManager, useMultithreading, bNoCopy)
84 , m_BufferSizeChangeRequested (0)
85 , m_BufferSizeChangeReported (0)
86 , m_ResetRequested (0)
88 , m_ResyncRequested (0)
89 , m_ResyncReported (0)
92 , m_IgnoreThisDrop(true)
93 , m_hDeviceProcessingThread(NULL)
94 , m_DeviceProcessingThreadID(0)
95 , m_hUpdateDeviceInfoRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
96 , m_hUpdateDeviceInfoDone(CreateEvent(NULL, FALSE, FALSE, NULL))
97 , m_hActivateRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
98 , m_hActivationDone(CreateEvent(NULL, FALSE, FALSE, NULL))
99 , m_hDeActivateRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
100 , m_hDeActivationDone(CreateEvent(NULL, FALSE, FALSE, NULL))
101 , m_hStartStreamingRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
102 , m_hStartStreamingDone(CreateEvent(NULL, FALSE, FALSE, NULL))
103 , m_hStopStreamingRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
104 , m_hStopStreamingDone(CreateEvent(NULL, FALSE, FALSE, NULL))
105 , m_hResetRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
106 , m_hResetDone(CreateEvent(NULL, FALSE, FALSE, NULL))
107 , m_hResetFromDevRequestedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
108 , m_hBufferSizeChangedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
109 , m_hSampleRateChangedEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
110 , m_hExitIdleThread(CreateEvent(NULL, FALSE, FALSE, NULL))
111 , m_hDeviceInitialized(CreateEvent(NULL, FALSE, FALSE, NULL))
116 //Set initial device info...
117 m_DeviceID = deviceID;
118 m_PortAudioStream = NULL;
119 m_CurrentSamplingRate = DEFAULT_SR;
120 m_CurrentBufferSize = DEFAULT_BUFFERSIZE;
121 m_DefaultBufferSize = DEFAULT_BUFFERSIZE;
122 m_StopRequested = true;
125 //initialize device processing thread
126 //the divice become alive and now is able to process requests
127 m_hDeviceProcessingThread = CreateThread( NULL, 0, __DoIdle__, (LPVOID)this, 0, &m_DeviceProcessingThreadID );
129 if (!m_hDeviceProcessingThread)
131 DEBUG_MSG("API::Device " << m_DeviceName << " cannot create processing thread");
135 WaitForSingleObject(m_hDeviceInitialized, INFINITE);
137 if (ConnectionStatus() == DeviceErrors)
144 void WCMRPortAudioDevice::initDevice()
146 // Initialize COM for this thread
147 std::cout << "API::Device " << m_DeviceID << " initializing COM" << std::endl;
149 if (S_OK == CoInitialize(NULL) )
156 //should use a valid current SR...
157 if (m_SamplingRates.size())
159 //see if the current sr is present in the sr list, if not, use the first one!
160 std::vector<int>::iterator intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), m_CurrentSamplingRate);
161 if (intIter == m_SamplingRates.end())
163 //not found... use the first one
164 m_CurrentSamplingRate = m_SamplingRates[0];
168 std::cout << "API::Device " << m_DeviceName << " Device does not support any sample rate of ours" << std::endl;
170 //should use a valid current buffer size
171 if (m_BufferSizes.size())
173 //see if the current sr is present in the buffersize list, if not, use the first one!
174 std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), m_CurrentBufferSize);
175 if (intIter == m_BufferSizes.end())
177 //not found... use the first one
178 m_CurrentBufferSize = m_BufferSizes[0];
182 //build our input/output level lists
183 for (unsigned int currentChannel = 0; currentChannel < m_InputChannels.size(); currentChannel++)
185 m_InputLevels.push_back (0.0);
188 //build our input/output level lists
189 for (unsigned int currentChannel = 0; currentChannel < m_OutputChannels.size(); currentChannel++)
191 m_OutputLevels.push_back (0.0);
194 std::cout << "API::Device " << m_DeviceName << " Device has been initialized" << std::endl;
195 m_ConnectionStatus = DeviceDisconnected;
200 /*Replace with debug trace*/std::cout << "API::Device " << m_DeviceName << " cannot initialize COM" << std::endl;
201 DEBUG_MSG("Device " << m_DeviceName << " cannot initialize COM");
202 m_ConnectionStatus = DeviceErrors;
203 m_lastErr = eSomeThingNotInitailzed;
204 SetEvent(m_hExitIdleThread);
207 SetEvent(m_hDeviceInitialized);
210 void WCMRPortAudioDevice::terminateDevice()
212 std::cout << "API::Device " << m_DeviceName << " Terminating DEVICE" << std::endl;
214 //If device is streaming, need to stop it!
220 //If device is active (meaning stream is open) we need to close it.
226 std::cout << "API::Device " << m_DeviceName << " Terminating PA" << std::endl;
233 //**********************************************************************************************
234 // WCMRPortAudioDevice::~WCMRPortAudioDevice
236 //! Destructor for the audio device. The base release all the connections that were created, if
237 //! they have not been already destroyed! Here we simply stop streaming, and close device
238 //! handles if necessary.
244 //**********************************************************************************************
245 WCMRPortAudioDevice::~WCMRPortAudioDevice ()
249 std::cout << "API::Destroying Device Instance: " << DeviceName() << std::endl;
252 //Stop deviceprocessing thread
253 SignalObjectAndWait(m_hExitIdleThread, m_hDeviceProcessingThread, INFINITE, false);
255 std::cout << "API::Device " << m_DeviceName << " Processing Thread is stopped" << std::endl;
257 CloseHandle(m_hDeviceProcessingThread);
259 //Now it's safe to free all event handlers
260 CloseHandle(m_hUpdateDeviceInfoRequestedEvent);
261 CloseHandle(m_hUpdateDeviceInfoDone);
262 CloseHandle(m_hActivateRequestedEvent);
263 CloseHandle(m_hActivationDone);
264 CloseHandle(m_hDeActivateRequestedEvent);
265 CloseHandle(m_hDeActivationDone);
266 CloseHandle(m_hStartStreamingRequestedEvent);
267 CloseHandle(m_hStartStreamingDone);
268 CloseHandle(m_hStopStreamingRequestedEvent);
269 CloseHandle(m_hStopStreamingDone);
270 CloseHandle(m_hResetRequestedEvent);
271 CloseHandle(m_hResetDone);
272 CloseHandle(m_hResetFromDevRequestedEvent);
273 CloseHandle(m_hBufferSizeChangedEvent);
274 CloseHandle(m_hSampleRateChangedEvent);
275 CloseHandle(m_hExitIdleThread);
276 CloseHandle(m_hDeviceInitialized);
280 //destructors should absorb exceptions, no harm in logging though!!
281 DEBUG_MSG ("Exception during destructor");
286 WTErr WCMRPortAudioDevice::UpdateDeviceInfo ()
288 std::cout << "API::Device (ID:)" << m_DeviceID << " Updating device info" << std::endl;
290 SignalObjectAndWait(m_hUpdateDeviceInfoRequestedEvent, m_hUpdateDeviceInfoDone, INFINITE, false);
296 //**********************************************************************************************
297 // WCMRPortAudioDevice::updateDeviceInfo
299 //! Must be called be device processing thread
300 //! Updates Device Information about channels, sampling rates, buffer sizes.
304 //**********************************************************************************************
305 void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/)
310 const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
313 m_DeviceName = pDeviceInfo->name;
315 //following parameters are needed opening test stream and for sample rates validation
316 PaStreamParameters inputParameters, outputParameters;
317 PaStreamParameters *pInS = NULL, *pOutS = NULL;
319 inputParameters.device = m_DeviceID;
320 inputParameters.channelCount = pDeviceInfo->maxInputChannels;
321 inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
322 inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
323 inputParameters.hostApiSpecificStreamInfo = 0;
325 if (inputParameters.channelCount)
326 pInS = &inputParameters;
328 outputParameters.device = m_DeviceID;
329 outputParameters.channelCount = pDeviceInfo->maxOutputChannels;
330 outputParameters.sampleFormat = paFloat32;
331 outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
332 outputParameters.hostApiSpecificStreamInfo = 0;
334 if (outputParameters.channelCount)
335 pOutS = &outputParameters;
337 ////////////////////////////////////////////////////////////////////////////////////
338 //update list of supported SRs...
339 m_SamplingRates.clear();
341 // now iterate through our standard SRs and check if they are supported by device
342 // store them for this device
343 for(int sr=0; gAllSampleRates[sr] > 0; sr++)
345 PaError err = Pa_IsFormatSupported(pInS, pOutS, gAllSampleRates[sr]);
346 if( err == paFormatIsSupported)
348 m_SamplingRates.push_back ((int)gAllSampleRates[sr]);
352 ///////////////////////////////////////////////////////////////////////////////////
353 //update buffer sizes
354 m_BufferSizes.clear();
355 bool useDefaultBuffers = true;
357 // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel
358 long minSize, maxSize, preferredSize, granularity;
359 PaError err = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
361 if (err == paNoError)
363 std::cout << "API::Device " << m_DeviceName << " Buffers: " << minSize << " " << maxSize << " " << preferredSize << std::endl;
365 m_DefaultBufferSize = preferredSize;
366 m_BufferSizes.push_back (preferredSize);
367 useDefaultBuffers = false;
371 std::cout << "API::Device" << m_DeviceName << " Preffered buffer size is not supported" << std::endl;
374 if (useDefaultBuffers)
376 std::cout << "API::Device" << m_DeviceName << " Using default buffer sizes " <<std::endl;
377 for(int bsize=0; bsize < (sizeof(gAllBufferSizes)/sizeof(gAllBufferSizes[0])); bsize++)
378 m_BufferSizes.push_back (gAllBufferSizes[bsize]);
381 /////////////////////////////////////////////////////////////////////////////////////////
382 //update channels info
384 int maxInputChannels = pDeviceInfo->maxInputChannels;
385 int maxOutputChannels = pDeviceInfo->maxOutputChannels;
387 //Update input channels
388 m_InputChannels.clear();
389 for (int channel = 0; channel < maxInputChannels; channel++)
391 const char* channelName[32]; // 32 is max leth declared by PortAudio for this operation
392 std::stringstream chNameStream;
394 PaError error = PaAsio_GetInputChannelName(m_DeviceID, channel, channelName);
396 chNameStream << (channel+1) << " - ";
398 if (error == paNoError)
400 chNameStream << *channelName;
404 chNameStream << "Input " << (channel+1);
407 m_InputChannels.push_back (chNameStream.str());
411 //Update output channels
412 m_OutputChannels.clear();
413 for (int channel = 0; channel < maxOutputChannels; channel++)
415 const char* channelName[32]; // 32 is max leth declared by PortAudio for this operation
416 std::stringstream chNameStream;
418 PaError error = PaAsio_GetOutputChannelName(m_DeviceID, channel, channelName);
420 chNameStream << (channel+1) << " - ";
422 if (error == paNoError)
424 chNameStream << *channelName;
428 chNameStream << "Output " << (channel+1);
431 m_OutputChannels.push_back (chNameStream.str());
435 std::cout << "API::Device" << m_DeviceName << " Device info update has been finished" << std::endl;
438 SetEvent(m_hUpdateDeviceInfoDone);
442 PaError WCMRPortAudioDevice::testStateValidness(int sampleRate, int bufferSize)
444 PaError paErr = paNoError;
447 const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
449 //following parameters are needed opening test stream and for sample rates validation
450 PaStreamParameters inputParameters, outputParameters;
451 PaStreamParameters *pInS = NULL, *pOutS = NULL;
453 inputParameters.device = m_DeviceID;
454 inputParameters.channelCount = pDeviceInfo->maxInputChannels;
455 inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
456 inputParameters.suggestedLatency = 0;
457 inputParameters.hostApiSpecificStreamInfo = 0;
459 if (inputParameters.channelCount)
460 pInS = &inputParameters;
462 outputParameters.device = m_DeviceID;
463 outputParameters.channelCount = pDeviceInfo->maxOutputChannels;
464 outputParameters.sampleFormat = paFloat32;
465 outputParameters.suggestedLatency = 0;
466 outputParameters.hostApiSpecificStreamInfo = 0;
468 if (outputParameters.channelCount)
469 pOutS = &outputParameters;
471 PaStream *portAudioStream = NULL;
473 //sometimes devices change buffer size if sample rate changes
474 //it updates buffer size during stream opening
475 //we need to find out how device would behave with current sample rate
476 //try opening test stream to load device driver for current sample rate and buffer size
477 paErr = Pa_OpenStream (&portAudioStream, pInS, pOutS, sampleRate, bufferSize, paDitherOff, NULL, NULL);
482 Pa_CloseStream (portAudioStream);
483 portAudioStream = NULL;
490 //**********************************************************************************************
491 // WCMRPortAudioDevice::CurrentSamplingRate
493 //! The device's current sampling rate. This may be overridden, if the device needs to
494 //! query the driver for the current rate.
498 //! \return The device's current sampling rate. -1 on error.
500 //**********************************************************************************************
501 int WCMRPortAudioDevice::CurrentSamplingRate ()
504 //ToDo: Perhaps for ASIO devices that are active, we should retrive the SR from the device...
506 return (m_CurrentSamplingRate);
510 WTErr WCMRPortAudioDevice::SetActive (bool newState)
512 if (newState == true)
514 std::cout << "API::Device " << m_DeviceName << " Activation requested" << std::endl;
515 SignalObjectAndWait(m_hActivateRequestedEvent, m_hActivationDone, INFINITE, false);
519 std::cout << "API::Device " << m_DeviceName << " Deactivation requested" << std::endl;
520 SignalObjectAndWait(m_hDeActivateRequestedEvent, m_hDeActivationDone, INFINITE, false);
523 if (newState == Active() )
530 WTErr WCMRPortAudioDevice::SetStreaming (bool newState)
532 if (newState == true)
534 std::cout << "API::Device " << m_DeviceName << " Stream start requested" << std::endl;
535 SignalObjectAndWait(m_hStartStreamingRequestedEvent, m_hStartStreamingDone, INFINITE, false);
539 std::cout << "API::Device " << m_DeviceName << " Stream stop requested" << std::endl;
540 SignalObjectAndWait(m_hStopStreamingRequestedEvent, m_hStopStreamingDone, INFINITE, false);
543 if (newState == Streaming() )
550 WTErr WCMRPortAudioDevice::ResetDevice()
552 std::cout << "API::Device: " << m_DeviceName << " Reseting device" << std::endl;
554 SignalObjectAndWait(m_hResetRequestedEvent, m_hResetDone, INFINITE, false);
556 if (ConnectionStatus() == DeviceErrors)
565 //**********************************************************************************************
566 // WCMRPortAudioDevice::SetCurrentSamplingRate
568 //! Change the sampling rate to be used by the device.
570 //! \param newRate : The rate to use (samples per sec).
572 //! \return eNoErr always. The derived classes may return error codes.
574 //**********************************************************************************************
575 WTErr WCMRPortAudioDevice::SetCurrentSamplingRate (int newRate)
578 std::vector<int>::iterator intIter;
579 WTErr retVal = eNoErr;
581 //changes the status.
582 int oldRate = CurrentSamplingRate();
583 bool oldActive = Active();
585 //no change, nothing to do
586 if (oldRate == newRate)
589 //see if this is one of our supported rates...
590 intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), newRate);
592 if (intIter == m_SamplingRates.end())
594 //Can't change, perhaps use an "invalid param" type of error
595 retVal = eCommandLineParameter;
601 //Can't change, perhaps use an "in use" type of error
602 retVal = eGenericErr;
607 m_CurrentSamplingRate = newRate;
608 PaError paErr = PaAsio_SetStreamSampleRate (m_PortAudioStream, m_CurrentSamplingRate);
609 Pa_Sleep(PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); // sleep some time to make sure the change has place
611 if (paErr != paNoError)
613 std::cout << "Sample rate change failed: " << Pa_GetErrorText (paErr) << std::endl;
614 if (paErr == paUnanticipatedHostError)
615 std::cout << "Details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl;
617 retVal = eWrongObjectState;
624 //**********************************************************************************************
625 // WCMRPortAudioDevice::CurrentBufferSize
627 //! The device's current buffer size in use. This may be overridden, if the device needs to
628 //! query the driver for the current size.
632 //! \return The device's current buffer size. 0 on error.
634 //**********************************************************************************************
635 int WCMRPortAudioDevice::CurrentBufferSize ()
637 return m_CurrentBufferSize;
641 //**********************************************************************************************
642 // WCMRPortAudioDevice::SetCurrentBufferSize
644 //! Change the buffer size to be used by the device. This will most likely be overridden,
645 //! the base class simply updates the member variable.
647 //! \param newSize : The buffer size to use (in sample-frames)
649 //! \return eNoErr always. The derived classes may return error codes.
651 //**********************************************************************************************
652 WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize)
655 WTErr retVal = eNoErr;
656 std::vector<int>::iterator intIter;
658 //changes the status.
659 int oldSize = CurrentBufferSize();
660 bool oldActive = Active();
662 //same size, nothing to do.
663 if (oldSize == newSize)
668 //Can't change, perhaps use an "in use" type of error
669 retVal = eGenericErr;
673 std::cout << "Setting buffer: " << newSize << std::endl;
675 //see if this is one of our supported rates...
676 intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize);
677 if (intIter == m_BufferSizes.end())
679 //Sample rate proposed by client is not supported any more
680 if (m_BufferSizes.size() == 1)
682 // we have only one aloved buffer size which is preffered by PA
683 // this is the only value which could be set
684 newSize = m_BufferSizes[0];
685 int bufferSize = newSize;
686 // notify client to update sample rate after us
687 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize);
691 // more then one buffer size value is available
692 //Can't change, perhaps use an "invalid param" type of error
693 retVal = eCommandLineParameter;
700 //Deactivate it for the change...
705 m_CurrentBufferSize = newSize;
710 retVal = SetActive (true);
717 //**********************************************************************************************
718 // WCMRPortAudioDevice::ConnectionStatus
720 //! Retrieves the device's current connection status. This will most likely be overridden,
721 //! in case some driver communication is required to query the status.
725 //! \return A ConnectionStates value.
727 //**********************************************************************************************
728 WCMRPortAudioDevice::ConnectionStates WCMRPortAudioDevice::ConnectionStatus ()
731 //ToDo: May want to do something more to extract the actual status!
732 return (m_ConnectionStatus);
737 //**********************************************************************************************
738 // WCMRPortAudioDevice::activateDevice
740 //! IS CALLED BY PROCESS THREAD
741 //! Sets the device into "active" state. Essentially, opens the PA device.
742 //! If it's an ASIO device it may result in buffer size change in some cases.
744 //**********************************************************************************************
745 void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/)
749 PaError paErr = paNoError;
751 // if device is not active activate it
754 std::list<long> buffersSizes;
755 buffersSizes.push_back(m_CurrentBufferSize);
757 long minSize, maxSize, preferredSize, granularity;
758 PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
760 if (paErr == paNoError)
762 buffersSizes.push_front(preferredSize);
765 PaStreamParameters inputParameters, outputParameters;
766 PaStreamParameters *pInS = NULL, *pOutS = NULL;
768 const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
769 const PaHostApiInfo *pHostApiInfo = Pa_GetHostApiInfo(pDeviceInfo->hostApi);
771 inputParameters.device = m_DeviceID;
772 inputParameters.channelCount = (int)m_InputChannels.size();
773 inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
774 inputParameters.suggestedLatency = Pa_GetDeviceInfo(m_DeviceID)->defaultLowInputLatency;
775 inputParameters.hostApiSpecificStreamInfo = 0;
777 if (inputParameters.channelCount)
778 pInS = &inputParameters;
780 outputParameters.device = m_DeviceID;
781 outputParameters.channelCount = (int)m_OutputChannels.size();
782 outputParameters.sampleFormat = paFloat32;
783 outputParameters.suggestedLatency = Pa_GetDeviceInfo(m_DeviceID)->defaultLowOutputLatency;
784 outputParameters.hostApiSpecificStreamInfo = 0;
786 if (outputParameters.channelCount)
787 pOutS = &outputParameters;
789 // try opening stream with current buffer and the rest if not successful
790 std::list<long>::const_iterator bufferIter = buffersSizes.begin();
791 for (; bufferIter != buffersSizes.end(); ++bufferIter) {
793 std::cout << "API::Device" << m_DeviceName << " Opening device stream " << std::endl;
794 std::cout << "Sample rate: " << m_CurrentSamplingRate << " buffer size: " << *bufferIter << std::endl;
795 paErr = Pa_OpenStream(&m_PortAudioStream,
798 m_CurrentSamplingRate,
801 WCMRPortAudioDevice::TheCallback,
804 if(paErr == paNoError)
809 std::cout << "Cannot open streamm with buffer: "<< *bufferIter << " Error: " << Pa_GetErrorText (paErr) << std::endl;
811 if (paErr == paUnanticipatedHostError)
812 std::cout << "Error details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl;
815 if(paErr == paNoError)
817 long minSize, maxSize, preferredSize, granularity;
818 PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
820 if (paErr == paNoError && m_CurrentBufferSize != preferredSize)
822 m_CurrentBufferSize = preferredSize;
823 m_BufferSizes.clear();
824 m_BufferSizes.push_back(preferredSize);
825 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&preferredSize);
830 m_IgnoreThisDrop = true;
832 if (pHostApiInfo->type == paASIO)
834 m_BufferSizeChangeRequested = 0;
835 m_BufferSizeChangeReported = 0;
836 m_ResetRequested = 0;
838 m_ResyncRequested = 0;
839 m_ResyncReported = 0;
840 PaAsio_SetMessageHook (StaticASIOMessageHook, this);
843 m_ConnectionStatus = DeviceAvailable;
848 //failed, do not update device state
849 std::cout << "Failed to open pa stream: " << Pa_GetErrorText (paErr) << std::endl;
850 DEBUG_MSG( "Failed to open pa stream: " << Pa_GetErrorText (paErr) );
851 m_ConnectionStatus = DeviceErrors;
852 m_lastErr = eAsioFailed;
859 SetEvent(m_hActivationDone);
863 //**********************************************************************************************
864 // WCMRPortAudioDevice::deactivateDevice
866 //! IS CALLED BY PROCESS THREAD
867 //! Sets the device into "inactive" state. Essentially, closes the PA device.
869 //**********************************************************************************************
870 void WCMRPortAudioDevice::deactivateDevice (bool callerIsWaiting/*=false*/)
874 PaError paErr = paNoError;
883 if (m_PortAudioStream)
885 //close the stream first
886 std::cout << "API::Device" << m_DeviceName << " Closing device stream" << std::endl;
887 paErr = Pa_CloseStream (m_PortAudioStream);
888 if(paErr == paNoError)
890 m_PortAudioStream = NULL;
893 m_IgnoreThisDrop = true;
894 m_BufferSizeChangeRequested = 0;
895 m_BufferSizeChangeReported = 0;
896 m_ResetRequested = 0;
898 m_ResyncRequested = 0;
899 m_ResyncReported = 0;
900 PaAsio_SetMessageHook (NULL, NULL);
902 //finaly set device state to "not active"
904 m_ConnectionStatus = DeviceDisconnected;
909 //failed, do not update device state
910 std::cout << "Failed to close pa stream stream " << Pa_GetErrorText (paErr) << std::endl;
911 DEBUG_MSG( "Failed to open pa stream stream " << Pa_GetErrorText (paErr) );
912 m_ConnectionStatus = DeviceErrors;
913 m_lastErr = eAsioFailed;
919 SetEvent(m_hDeActivationDone);
923 //**********************************************************************************************
924 // WCMRPortAudioDevice::startStreaming
926 //! Sets the devices into "streaming" state. Calls PA's Start stream routines.
927 //! This roughly corresponds to calling Start on the lower level interface.
929 //**********************************************************************************************
930 void WCMRPortAudioDevice::startStreaming (bool callerIsWaiting/*=false*/)
934 // proceed if the device is not streaming
937 PaError paErr = paNoError;
938 m_StopRequested = false;
941 std::cout << "API::Device" << m_DeviceName << " Starting device stream" << std::endl;
944 const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID);
946 unsigned int inChannelCount = pDeviceInfo->maxInputChannels;
947 unsigned int outChannelCount = pDeviceInfo->maxOutputChannels;
949 paErr = Pa_StartStream( m_PortAudioStream );
951 if(paErr == paNoError)
953 // if the stream was started successfully
954 m_IsStreaming = true;
955 std::cout << "API::Device" << m_DeviceName << " Device is streaming" << std::endl;
959 std::cout << "Failed to start PA stream: " << Pa_GetErrorText (paErr) << std::endl;
960 DEBUG_MSG( "Failed to start PA stream: " << Pa_GetErrorText (paErr) );
961 m_lastErr = eGenericErr;
966 SetEvent(m_hStartStreamingDone);
970 //**********************************************************************************************
971 // WCMRPortAudioDevice::stopStreaming
973 //! Sets the devices into "not streaming" state. Calls PA's Stop stream routines.
974 //! This roughly corresponds to calling Stop on the lower level interface.
976 //**********************************************************************************************
977 void WCMRPortAudioDevice::stopStreaming (bool callerIsWaiting/*=false*/)
981 // proceed if the device is streaming
984 PaError paErr = paNoError;
985 m_StopRequested = true;
987 std::cout << "API::Device " << m_DeviceName << " Stopping device stream" << std::endl;
988 paErr = Pa_StopStream( m_PortAudioStream );
990 if(paErr == paNoError)
992 // if the stream was stopped successfully
993 m_IsStreaming = false;
998 std::cout << "Failed to stop PA stream: " << Pa_GetErrorText (paErr) << std::endl;
999 DEBUG_MSG( "Failed to stop PA stream " << Pa_GetErrorText (paErr) );
1000 m_lastErr = eGenericErr;
1004 if (callerIsWaiting)
1005 SetEvent(m_hStopStreamingDone);
1009 //**********************************************************************************************
1010 // WCMRPortAudioDevice::resetDevice
1012 //! Resets the device, updates device info. Importnat: does PA reinitialization calling
1013 //! Pa_terminate/Pa_initialize functions.
1019 //**********************************************************************************************
1020 void WCMRPortAudioDevice::resetDevice (bool callerIsWaiting /*=false*/ )
1022 std::cout << "API::Device" << m_DeviceName << "Reseting device" << std::endl;
1024 PaError paErr = paNoError;
1026 // Keep device sates
1027 bool wasStreaming = Streaming();
1028 bool wasActive = Active();
1034 // Cache device buffer size as it might be changed during reset
1035 int oldBufferSize = m_CurrentBufferSize;
1037 // Now, validate the state and update device info if required
1038 unsigned int retry = PROPERTY_CHANGE_RETRIES;
1045 std::cout << "Updating device state... " << std::endl;
1046 // update device info
1050 long minSize, maxSize, preferredSize, granularity;
1051 PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity);
1053 if (paErr != paNoError)
1057 m_CurrentBufferSize = preferredSize;
1059 paErr = testStateValidness(m_CurrentSamplingRate, m_CurrentBufferSize);
1060 if (paNoError == paErr)
1062 std::cout << "Device state is valid" << std::endl;
1066 std::cout << "Cannot start with current state: sr: " << m_CurrentSamplingRate << " bs:" << m_CurrentBufferSize \
1067 << "\nReason: " << Pa_GetErrorText (paErr) << std::endl;
1068 if (paErr == paUnanticipatedHostError)
1069 std::cout << "Details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl;
1071 std::cout << "Will try again in " << DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS << "msec" << std::endl;
1073 Pa_Sleep(DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS);
1076 if (paErr == paNoError)
1078 // Notify the Application about device setting changes
1079 if (oldBufferSize != m_CurrentBufferSize)
1081 std::cout << "API::Device" << m_DeviceName << " buffer size changed" << std::endl;
1082 int bufferSize = m_CurrentBufferSize;
1083 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize);
1086 // Activate the device if it was active before
1090 // Resume streaming if the device was streaming before
1091 if(wasStreaming && m_lastErr == eNoErr && m_ConnectionStatus == DeviceAvailable)
1093 // Notify the Application to prepare for the stream start
1094 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStartsStreaming);
1098 m_ConnectionStatus = DeviceErrors;
1099 m_lastErr = eWrongObjectState;
1102 if (callerIsWaiting)
1103 SetEvent(m_hResetDone);
1107 #ifdef PLATFORM_WINDOWS
1109 long WCMRPortAudioDevice::StaticASIOMessageHook (void *pRefCon, long selector, long value, void* message, double* opt)
1113 return ((WCMRPortAudioDevice*)(pRefCon))->ASIOMessageHook (selector, value, message, opt);
1119 long WCMRPortAudioDevice::ASIOMessageHook (long selector, long WCUNUSEDPARAM(value), void* WCUNUSEDPARAM(message), double* WCUNUSEDPARAM(opt))
1123 case kAsioResyncRequest:
1124 m_ResyncRequested++;
1125 std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResyncRequest" << std::endl;
1126 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
1129 case kAsioLatenciesChanged:
1130 m_BufferSizeChangeRequested++;
1131 std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioLatenciesChanged" << std::endl;
1132 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
1135 case kAsioBufferSizeChange:
1136 m_BufferSizeChangeRequested++;
1137 std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- m_BufferSizeChangeRequested" << std::endl;
1138 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
1141 case kAsioResetRequest:
1143 std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResetRequest" << std::endl;
1144 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
1149 std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioOverload" << std::endl;
1150 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::Dropout);
1159 //**********************************************************************************************
1160 // WCMRPortAudioDevice::DoIdle
1162 //! A place for doing idle time processing. The other derived classes will probably do something
1167 //! \return eNoErr always.
1169 //**********************************************************************************************
1170 WTErr WCMRPortAudioDevice::DoIdle ()
1172 WTErr retVal = eNoErr;
1174 std::cout << "WCMRPortAudioDevice::DoIdle ()" << std::endl;
1177 m_hUpdateDeviceInfoRequestedEvent,
1178 m_hActivateRequestedEvent,
1179 m_hDeActivateRequestedEvent,
1180 m_hStartStreamingRequestedEvent,
1181 m_hStopStreamingRequestedEvent,
1182 m_hBufferSizeChangedEvent,
1183 m_hSampleRateChangedEvent,
1184 m_hResetRequestedEvent,
1185 m_hResetFromDevRequestedEvent,
1189 const size_t hEventsSize = sizeof(hEvents)/sizeof(hEvents[0]);
1195 DWORD result = WaitForMultipleObjects (hEventsSize, hEvents, FALSE, INFINITE);
1196 result = result - WAIT_OBJECT_0;
1198 if ((result < 0) || (result >= hEventsSize)) {
1199 std::cout << "\t\t\t\t\t\t\tWCMRPortAudioDevice::DoIdle () -> (result < 0) || (result >= hEventsSize):" << result << std::endl;
1200 retVal = eGenericErr;
1204 if (hEvents[result] == m_hExitIdleThread) {
1205 std::cout << "\t\t\t\t\t\t\tWCMRPortAudioDevice::DoIdle () -> m_hExitIdleThread" << result << std::endl;
1210 if (hEvents[result] == m_hUpdateDeviceInfoRequestedEvent) {
1211 std::cout << "\t\t\t\t\t\tupdate requested ..." << std::endl;
1212 updateDeviceInfo(true);
1215 if (hEvents[result] == m_hActivateRequestedEvent) {
1216 std::cout << "\t\t\t\t\t\tactivation requested ..." << std::endl;
1217 activateDevice(true);
1220 if (hEvents[result] == m_hDeActivateRequestedEvent) {
1221 std::cout << "\t\t\t\t\t\tdeactivation requested ..." << std::endl;
1222 deactivateDevice(true);
1225 if (hEvents[result] == m_hStartStreamingRequestedEvent) {
1226 std::cout << "\t\t\t\t\t\tStart stream requested ..." << std::endl;
1227 startStreaming(true);
1230 if (hEvents[result] == m_hStopStreamingRequestedEvent) {
1231 std::cout << "\t\t\t\t\t\tStop stream requested ..." << std::endl;
1232 stopStreaming(true);
1235 if (hEvents[result] == m_hResetRequestedEvent) {
1236 std::cout << "\t\t\t\t\t\treset requested ..." << std::endl;
1240 if (hEvents[result] == m_hResetFromDevRequestedEvent) {
1241 std::cout << "\t\t\t\t\t\treset requested from device..." << std::endl;
1245 if (hEvents[result] == m_hBufferSizeChangedEvent) {
1246 std::cout << "\t\t\t\t\t\tbuffer size changed from device..." << std::endl;
1247 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged);
1250 if (hEvents[result] == m_hSampleRateChangedEvent) {
1251 std::cout << "\t\t\t\t\t\tsample rate changed from device..." << std::endl;
1252 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::SamplingRateChanged);
1262 //**********************************************************************************************
1263 // WCMRPortAudioDevice::SetMonitorChannels
1265 //! Used to set the channels to be used for monitoring.
1267 //! \param leftChannel : Left monitor channel index.
1268 //! \param rightChannel : Right monitor channel index.
1270 //! \return eNoErr always, the derived classes may return appropriate errors.
1272 //**********************************************************************************************
1273 WTErr WCMRPortAudioDevice::SetMonitorChannels (int leftChannel, int rightChannel)
1276 //This will most likely be overridden, the base class simply
1277 //changes the member.
1278 m_LeftMonitorChannel = leftChannel;
1279 m_RightMonitorChannel = rightChannel;
1285 //**********************************************************************************************
1286 // WCMRPortAudioDevice::SetMonitorGain
1288 //! Used to set monitor gain (or atten).
1290 //! \param newGain : The new gain or atten. value to use. Specified as a linear multiplier (not dB)
1292 //! \return eNoErr always, the derived classes may return appropriate errors.
1294 //**********************************************************************************************
1295 WTErr WCMRPortAudioDevice::SetMonitorGain (float newGain)
1298 //This will most likely be overridden, the base class simply
1299 //changes the member.
1301 m_MonitorGain = newGain;
1308 //**********************************************************************************************
1309 // WCMRPortAudioDevice::ShowConfigPanel
1311 //! Used to show device specific config/control panel. Some interfaces may not support it.
1312 //! Some interfaces may require the device to be active before it can display a panel.
1314 //! \param pParam : A device/interface specific parameter, should be the app window handle for ASIO.
1316 //! \return eNoErr always, the derived classes may return errors.
1318 //**********************************************************************************************
1319 WTErr WCMRPortAudioDevice::ShowConfigPanel (void *pParam)
1322 WTErr retVal = eNoErr;
1326 #ifdef PLATFORM_WINDOWS
1327 if(Pa_GetHostApiInfo(Pa_GetDeviceInfo(m_DeviceID)->hostApi)->type == paASIO)
1329 // stop and deactivate the device
1330 bool wasStreaming = Streaming();
1332 // show control panel for the device
1333 if (PaAsio_ShowControlPanel (m_DeviceID, pParam) != paNoError)
1334 retVal = eGenericErr;
1335 // reset device to pick up changes
1337 // restore previous state for the device
1351 //*****************************************************************************************************
1352 // WCMRPortAudioDevice::TheCallback
1354 //! The (static) Port Audio Callback function. This is a static member. It calls on the AudioCallback in the
1355 //! WCMRPortAudioDevice to do the real work.
1357 //! \param pInputBuffer: pointer to input buffer.
1358 //! \param pOutputBuffer: pointer to output buffer.
1359 //! \param framesPerBuffer: number of sample frames per buffer.
1360 //! \param pTimeInfo: time info for PaStream callback.
1361 //! \param statusFlags:
1362 //! \param pUserData: pointer to user data, in our case the WCMRPortAudioDevice object.
1364 //! \return true to stop streaming else returns false.
1365 //******************************************************************************************************
1366 int WCMRPortAudioDevice::TheCallback (const void *pInputBuffer, void *pOutputBuffer, unsigned long framesPerBuffer,
1367 const PaStreamCallbackTimeInfo* /*pTimeInfo*/, PaStreamCallbackFlags statusFlags, void *pUserData )
1369 WCMRPortAudioDevice *pMyDevice = (WCMRPortAudioDevice *)pUserData;
1371 return pMyDevice->AudioCallback ((float *)pInputBuffer, (float *)pOutputBuffer, framesPerBuffer,
1372 (statusFlags & (paInputOverflow | paOutputUnderflow)) != 0);
1380 //**********************************************************************************************
1381 // WCMRPortAudioDevice::AudoiCallback
1383 //! Here's where the actual audio processing happens. We call upon all the active connections'
1384 //! sinks to provide data to us which can be put/mixed in the output buffer! Also, we make the
1385 //! input data available to any sources that may call upon us during this time!
1387 //! \param *pInputBuffer : Points to a buffer with recorded data.
1388 //! \param *pOutputBuffer : Points to a buffer to receive playback data.
1389 //! \param framesPerBuffer : Number of sample frames in input and output buffers. Number of channels,
1390 //! which are interleaved, is fixed at Device Open (Active) time. In this implementation,
1391 //! the number of channels are fixed to use the maximum available.
1392 //! \param dropsDetected : True if dropouts were detected in input or output. Can be used to signal the GUI.
1396 //**********************************************************************************************
1397 int WCMRPortAudioDevice::AudioCallback( const float *pInputBuffer, float *pOutputBuffer, unsigned long framesPerBuffer, bool dropsDetected )
1399 UMicroseconds theStartTime;
1404 if (m_IgnoreThisDrop)
1405 m_IgnoreThisDrop = false; //We'll ignore once, just once!
1410 m_pInputData = pInputBuffer;
1412 // VKamyshniy: Is this a right place to call the client???:
1413 struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData =
1419 theStartTime.MicroSeconds()*1000
1422 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData );
1424 //Don't try to access after this call returns!
1425 m_pInputData = NULL;
1427 m_SampleCounter += framesPerBuffer;
1429 return m_StopRequested;
1435 //**********************************************************************************************
1436 // WCMRPortAudioDeviceManager::WCMRPortAudioDeviceManager
1438 //! The constructuor, we initialize PA, and build the device list.
1440 //! \param *pTheClient : The manager's client object (which receives notifications).
1441 //! \param interfaceType : The PortAudio interface type to use for this manager - acts as a filter.
1442 //! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
1444 //! \return Nothing.
1446 //**********************************************************************************************
1447 WCMRPortAudioDeviceManager::WCMRPortAudioDeviceManager (WCMRAudioDeviceManagerClient *pTheClient,
1448 eAudioDeviceFilter eCurAudioDeviceFilter, bool useMultithreading, bool bNocopy)
1449 : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter)
1451 , m_UseMultithreading(useMultithreading)
1452 , m_bNoCopyAudioBuffer(bNocopy)
1455 std::cout << "API::PortAudioDeviceManager::PA Device manager constructor" << std::endl;
1457 //Always create the None device first...
1458 m_NoneDevice = new WCMRNativeAudioNoneDevice(this);
1460 WTErr err = generateDeviceListImpl();
1465 timeBeginPeriod (1);
1469 //**********************************************************************************************
1470 // WCMRPortAudioDeviceManager::~WCMRPortAudioDeviceManager
1472 //! It clears the device list, releasing each of the device.
1476 //! \return Nothing.
1478 //**********************************************************************************************
1479 WCMRPortAudioDeviceManager::~WCMRPortAudioDeviceManager()
1483 std::cout << "API::Destroying PortAudioDeviceManager " << std::endl;
1487 delete m_NoneDevice;
1491 //destructors should absorb exceptions, no harm in logging though!!
1492 DEBUG_MSG ("Exception during destructor");
1499 WCMRAudioDevice* WCMRPortAudioDeviceManager::initNewCurrentDeviceImpl(const std::string & deviceName)
1501 destroyCurrentDeviceImpl();
1503 std::cout << "API::PortAudioDeviceManager::initNewCurrentDevice " << deviceName << std::endl;
1504 if (deviceName == m_NoneDevice->DeviceName() )
1506 m_CurrentDevice = m_NoneDevice;
1507 return m_CurrentDevice;
1511 WTErr err = GetDeviceInfoByName(deviceName, devInfo);
1517 std::cout << "API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName << std::endl;
1518 TRACE_MSG ("API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName);
1520 m_CurrentDevice = new WCMRPortAudioDevice (this, devInfo.m_DeviceId, m_UseMultithreading, m_bNoCopyAudioBuffer);
1524 std::cout << "Unabled to create PA Device: " << devInfo.m_DeviceId << std::endl;
1525 DEBUG_MSG ("Unabled to create PA Device: " << devInfo.m_DeviceId);
1529 return m_CurrentDevice;
1533 void WCMRPortAudioDeviceManager::destroyCurrentDeviceImpl()
1535 if (m_CurrentDevice != m_NoneDevice)
1536 delete m_CurrentDevice;
1538 m_CurrentDevice = 0;
1542 WTErr WCMRPortAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceId, std::vector<int>& sampleRates)
1544 WTErr retVal = eNoErr;
1546 sampleRates.clear();
1547 const PaDeviceInfo *pPaDeviceInfo = Pa_GetDeviceInfo(deviceId);
1549 //now find supported sample rates
1550 //following parameters are needed for sample rates validation
1551 PaStreamParameters inputParameters, outputParameters;
1552 PaStreamParameters *pInS = NULL, *pOutS = NULL;
1554 inputParameters.device = deviceId;
1555 inputParameters.channelCount = std::min<int>(2, pPaDeviceInfo->maxInputChannels);
1556 inputParameters.sampleFormat = paFloat32 | paNonInterleaved;
1557 inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
1558 inputParameters.hostApiSpecificStreamInfo = 0;
1560 if (inputParameters.channelCount)
1561 pInS = &inputParameters;
1563 outputParameters.device = deviceId;
1564 outputParameters.channelCount = std::min<int>(2, pPaDeviceInfo->maxOutputChannels);
1565 outputParameters.sampleFormat = paFloat32;
1566 outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
1567 outputParameters.hostApiSpecificStreamInfo = 0;
1569 if (outputParameters.channelCount)
1570 pOutS = &outputParameters;
1572 for(int sr=0; gAllSampleRates[sr] > 0; sr++)
1574 if( paFormatIsSupported == Pa_IsFormatSupported(pInS, pOutS, gAllSampleRates[sr]) )
1576 sampleRates.push_back ((int)gAllSampleRates[sr]);
1584 WTErr WCMRPortAudioDeviceManager::generateDeviceListImpl()
1586 std::cout << "API::PortAudioDeviceManager::Generating device list" << std::endl;
1588 WTErr retVal = eNoErr;
1590 //Initialize PortAudio and ASIO first
1591 PaError paErr = Pa_Initialize();
1593 if (paErr != paNoError)
1595 //ToDo: throw an exception here!
1596 retVal = eSomeThingNotInitailzed;
1600 // lock DeviceInfoVec firts
1601 wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
1605 DeviceInfo *pDevInfo = new DeviceInfo(NONE_DEVICE_ID, m_NoneDevice->DeviceName() );
1606 pDevInfo->m_AvailableSampleRates = m_NoneDevice->SamplingRates();
1607 m_DeviceInfoVec.push_back(pDevInfo);
1610 //Get device count...
1611 int numDevices = Pa_GetDeviceCount();
1614 for (int thisDeviceID = 0; thisDeviceID < numDevices; thisDeviceID++)
1616 //if it's of the required type...
1617 const PaDeviceInfo *pPaDeviceInfo = Pa_GetDeviceInfo(thisDeviceID);
1619 if (Pa_GetHostApiInfo(pPaDeviceInfo->hostApi)->type == paASIO)
1621 //build a device object...
1624 std::cout << "API::PortAudioDeviceManager::DeviceID: " << thisDeviceID << ", Device Name: " << pPaDeviceInfo->name << std::endl;
1625 TRACE_MSG ("PA DeviceID: " << thisDeviceID << ", Device Name: " << pPaDeviceInfo->name);
1627 DeviceInfo *pDevInfo = new DeviceInfo(thisDeviceID, pPaDeviceInfo->name);
1630 std::vector<int> availableSampleRates;
1631 WTErr wErr = WCMRPortAudioDeviceManager::getDeviceAvailableSampleRates(thisDeviceID, availableSampleRates);
1635 DEBUG_MSG ("Failed to get device available sample rates. Device ID: " << m_DeviceID);
1637 continue; //proceed to the next device
1640 pDevInfo->m_AvailableSampleRates = availableSampleRates;
1641 pDevInfo->m_MaxInputChannels = pPaDeviceInfo->maxInputChannels;
1642 pDevInfo->m_MaxOutputChannels = pPaDeviceInfo->maxOutputChannels;
1644 //Now check if this device is acceptable according to current input/output settings
1645 bool bRejectDevice = false;
1646 switch(m_eAudioDeviceFilter)
1648 case eInputOnlyDevices:
1649 if (pDevInfo->m_MaxInputChannels != 0)
1651 m_DeviceInfoVec.push_back(pDevInfo);
1655 // Delete unnecesarry device
1656 bRejectDevice = true;
1659 case eOutputOnlyDevices:
1660 if (pDevInfo->m_MaxOutputChannels != 0)
1662 m_DeviceInfoVec.push_back(pDevInfo);
1666 // Delete unnecesarry device
1667 bRejectDevice = true;
1670 case eFullDuplexDevices:
1671 if (pDevInfo->m_MaxInputChannels != 0 && pDevInfo->m_MaxOutputChannels != 0)
1673 m_DeviceInfoVec.push_back(pDevInfo);
1677 // Delete unnecesarry device
1678 bRejectDevice = true;
1683 m_DeviceInfoVec.push_back(pDevInfo);
1689 TRACE_MSG ("API::PortAudioDeviceManager::Device " << pDevInfo->m_DeviceName << "Rejected. \
1690 In Channels = " << pDevInfo->m_MaxInputChannels << "Out Channels = " <<pDevInfo->m_MaxOutputChannels );
1697 std::cout << "API::PortAudioDeviceManager::Unabled to create PA Device: " << std::endl;
1698 DEBUG_MSG ("Unabled to create PA Device: " << thisDeviceID);
1703 //If no devices were found, that's not a good thing!
1704 if (m_DeviceInfoVec.empty() )
1706 std::cout << "API::PortAudioDeviceManager::No matching PortAudio devices were found, total PA devices = " << numDevices << std::endl;
1707 DEBUG_MSG ("No matching PortAudio devices were found, total PA devices = " << numDevices);
1710 //we don't need PA initialized right now
1717 WTErr WCMRPortAudioDeviceManager::getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const
1719 sampleRates.clear ();
1721 WTErr retVal = eNoErr;
1723 if (m_CurrentDevice && deviceName == m_CurrentDevice->DeviceName() )
1725 sampleRates.assign(m_CurrentDevice->SamplingRates().begin(), m_CurrentDevice->SamplingRates().end() );
1730 retVal = GetDeviceInfoByName(deviceName, devInfo);
1732 if (eNoErr == retVal)
1734 sampleRates.assign(devInfo.m_AvailableSampleRates.begin(), devInfo.m_AvailableSampleRates.end() );
1738 std::cout << "API::PortAudioDeviceManager::GetSampleRates: Device not found: "<< deviceName << std::endl;
1745 WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& buffers) const
1747 WTErr retVal = eNoErr;
1751 //first check if the request has been made for None device
1752 if (deviceName == m_NoneDevice->DeviceName() )
1754 buffers.assign(m_NoneDevice->BufferSizes().begin(), m_NoneDevice->BufferSizes().end() );
1758 if (m_CurrentDevice && deviceName == m_CurrentDevice->DeviceName() )
1760 buffers.assign(m_CurrentDevice->BufferSizes().begin(), m_CurrentDevice->BufferSizes().end() );
1767 retVal = GetDeviceInfoByName(deviceName, devInfo);
1769 if (eNoErr == retVal)
1771 //make PA request to get actual device buffer sizes
1772 long minSize, maxSize, preferredSize, granularity;
1773 PaError paErr = PaAsio_GetAvailableBufferSizes(devInfo.m_DeviceId, &minSize, &maxSize, &preferredSize, &granularity);
1775 //for Windows ASIO devices we always use prefferes buffer size ONLY
1776 if (paNoError == paErr )
1778 buffers.push_back(preferredSize);
1782 retVal = eAsioFailed;
1783 std::cout << "API::PortAudioDeviceManager::GetBufferSizes: error: " << Pa_GetErrorText (paErr) << " getting buffer sizes for device: "<< deviceName << std::endl;
1788 std::cout << "API::PortAudioDeviceManager::GetBufferSizes: Device not found: "<< deviceName << std::endl;