1 //----------------------------------------------------------------------------------
3 // Copyright (c) 2008 Waves Audio Ltd. All rights reserved.
5 //! \file WCMRCoreAudioDeviceManager.cpp
7 //! WCMRCoreAudioDeviceManager and related class declarations
9 //---------------------------------------------------------------------------------*/
10 #include "WCMRCoreAudioDeviceManager.h"
11 #include <CoreServices/CoreServices.h>
12 #include "MiscUtils/safe_delete.h"
16 // This flag is turned to 1, but it does not work with aggregated devices.
17 // due to problems with aggregated devices this flag is not functional there
18 #define ENABLE_DEVICE_CHANGE_LISTNER 1
20 #define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 10
21 #define PROPERTY_CHANGE_TIMEOUT_SECONDS 5
22 #define USE_IOCYCLE_TIMES 1 ///< Set this to 0 to use individual thread cpu measurement
25 ///< Supported Sample rates
26 static const double gAllSampleRates[] =
28 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */
32 ///< Default Supported Buffer Sizes.
33 static const int gAllBufferSizes[] =
35 32, 64, 96, 128, 192, 256, 512, 1024, 2048, -1 /* negative terminated list */
40 static const int DEFAULT_SR = 44100;
41 ///< The default buffer size.
42 static const int DEFAULT_BUFFERSIZE = 128;
44 static const int NONE_DEVICE_ID = -1;
46 ///< Number of stalls to wait before notifying user...
47 static const int NUM_STALLS_FOR_NOTIFICATION = 2 * 50; // 2*50 corresponds to 2 * 50 x 42 ms idle timer - about 4 seconds.
48 static const int CHANGE_CHECK_COUNTER_PERIOD = 100; // 120 corresponds to 120 x 42 ms idle timer - about 4 seconds.
50 #define AUHAL_OUTPUT_ELEMENT 0
51 #define AUHAL_INPUT_ELEMENT 1
53 #include <sys/sysctl.h>
55 static int getProcessorCount()
58 size_t size = sizeof(count);
60 if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0))
63 //if something did not work, let's revert to a safe value...
71 //**********************************************************************************************
72 // WCMRCoreAudioDevice::WCMRCoreAudioDevice
74 //! Constructor for the audio device. Opens the PA device and gets information about the device.
75 //! such as determining supported sampling rates, buffer sizes, and channel counts.
77 //! \param *pManager : The audio device manager that's managing this device.
78 //! \param deviceID : The port audio device ID.
79 //! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
83 //**********************************************************************************************
84 WCMRCoreAudioDevice::WCMRCoreAudioDevice (WCMRCoreAudioDeviceManager *pManager, AudioDeviceID deviceID, bool useMultithreading, bool bNocopy)
85 : WCMRNativeAudioDevice (pManager, useMultithreading, bNocopy)
86 , m_SampleCountAtLastIdle (0)
87 , m_StalledSampleCounter(0)
89 , m_BufferSizeChangeRequested (0)
90 , m_BufferSizeChangeReported (0)
91 , m_ResetRequested (0)
93 , m_ResyncRequested (0)
94 , m_ResyncReported (0)
95 , m_SRChangeRequested (0)
96 , m_SRChangeReported (0)
97 , m_ChangeCheckCounter(0)
98 , m_IOProcThreadPort (0)
101 , m_IgnoreThisDrop(true)
105 , m_ToneDataSamples (0)
106 , m_NextSampleToUse (0)
107 #endif //WV_USE_TONE_GEN
111 OSStatus err = kAudioHardwareNoError;
113 //Update device info...
114 m_DeviceID = deviceID;
116 m_CurrentSamplingRate = DEFAULT_SR;
117 m_CurrentBufferSize = DEFAULT_BUFFERSIZE;
118 m_StopRequested = true;
121 m_CPUCount = getProcessorCount();
122 m_LastCPULog = wvThread::now() - 10 * wvThread::ktdOneSecond;
127 @constant kAudioDevicePropertyNominalSampleRate
128 A Float64 that indicates the current nominal sample rate of the AudioDevice.
130 Float64 currentNominalRate;
131 propSize = sizeof (currentNominalRate);
132 err = kAudioHardwareNoError;
133 if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate) != kAudioHardwareNoError)
134 err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate);
136 if (err == kAudioHardwareNoError)
137 m_CurrentSamplingRate = (int)currentNominalRate;
140 @constant kAudioDevicePropertyBufferFrameSize
141 A UInt32 whose value indicates the number of frames in the IO buffers.
145 propSize = sizeof (bufferSize);
146 err = kAudioHardwareNoError;
147 if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferSize) != kAudioHardwareNoError)
148 err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferSize);
150 if (err == kAudioHardwareNoError)
151 m_CurrentBufferSize = (int)bufferSize;
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 //should use a valid current buffer size
169 if (m_BufferSizes.size())
171 //see if the current sr is present in the buffersize list, if not, use the first one!
172 std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), m_CurrentBufferSize);
173 if (intIter == m_BufferSizes.end())
175 //not found... use the first one
176 m_CurrentBufferSize = m_BufferSizes[0];
180 //build our input/output level lists
181 for (unsigned int currentChannel = 0; currentChannel < m_InputChannels.size(); currentChannel++)
183 m_InputLevels.push_back (0.0);
186 //build our input/output level lists
187 for (unsigned int currentChannel = 0; currentChannel < m_OutputChannels.size(); currentChannel++)
189 m_OutputLevels.push_back (0.0);
196 //**********************************************************************************************
197 // WCMRCoreAudioDevice::~WCMRCoreAudioDevice
199 //! Destructor for the audio device. The base release all the connections that were created, if
200 //! they have not been already destroyed! Here we simply stop streaming, and close device
201 //! handles if necessary.
207 //**********************************************************************************************
208 WCMRCoreAudioDevice::~WCMRCoreAudioDevice ()
214 //If device is streaming, need to stop it!
217 SetStreaming (false);
220 //If device is active (meaning stream is open) we need to close it.
229 //destructors should absorb exceptions, no harm in logging though!!
230 DEBUG_MSG ("Exception during destructor");
236 //**********************************************************************************************
237 // WCMRCoreAudioDevice::UpdateDeviceInfo
239 //! Updates Device Information about channels, sampling rates, buffer sizes.
243 //**********************************************************************************************
244 WTErr WCMRCoreAudioDevice::UpdateDeviceInfo ()
248 WTErr retVal = eNoErr;
250 // Update all devices parts regardless of errors
251 WTErr errName = UpdateDeviceName();
252 WTErr errIn = UpdateDeviceInputs();
253 WTErr errOut = UpdateDeviceOutputs();
254 WTErr errSR = eNoErr;
255 WTErr errBS = eNoErr;
257 errSR = UpdateDeviceSampleRates();
258 errBS = UpdateDeviceBufferSizes();
260 if(errName != eNoErr || errIn != eNoErr || errOut != eNoErr || errSR != eNoErr || errBS != eNoErr)
262 retVal = eCoreAudioFailed;
268 //**********************************************************************************************
269 // WCMRCoreAudioDevice::UpdateDeviceName
271 //! Updates Device name.
273 //! Use 'kAudioDevicePropertyDeviceName'
275 //! 1. Get property name size.
276 //! 2. Get property: name.
280 //**********************************************************************************************
281 WTErr WCMRCoreAudioDevice::UpdateDeviceName()
284 WTErr retVal = eNoErr;
285 OSStatus err = kAudioHardwareNoError;
288 // Initiate name to unknown.
289 m_DeviceName = "Unknown";
291 //! 1. Get property name size.
292 err = AudioDeviceGetPropertyInfo(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL);
293 if (err == kAudioHardwareNoError)
295 //! 2. Get property: name.
296 char* deviceName = new char[propSize];
297 err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName);
298 if (err == kAudioHardwareNoError)
300 m_DeviceName = deviceName;
304 retVal = eCoreAudioFailed;
305 DEBUG_MSG("Failed to get device name. Device ID: " << m_DeviceID);
308 delete [] deviceName;
312 retVal = eCoreAudioFailed;
313 DEBUG_MSG("Failed to get device name property size. Device ID: " << m_DeviceID);
319 //**********************************************************************************************
320 // WCMRCoreAudioDevice::UpdateDeviceInputs
322 //! Updates Device Inputs.
324 //! Use 'kAudioDevicePropertyStreamConfiguration'
325 //! This property returns the stream configuration of the device in an
326 //! AudioBufferList (with the buffer pointers set to NULL) which describes the
327 //! list of streams and the number of channels in each stream. This corresponds
328 //! to what will be passed into the IOProc.
330 //! 1. Get property cannels input size.
331 //! 2. Get property: cannels input.
332 //! 3. Update input channels
336 //**********************************************************************************************
337 WTErr WCMRCoreAudioDevice::UpdateDeviceInputs()
340 WTErr retVal = eNoErr;
341 OSStatus err = kAudioHardwareNoError;
343 int maxInputChannels = 0;
345 // 1. Get property cannels input size.
346 err = AudioDeviceGetPropertyInfo (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
347 if (err == kAudioHardwareNoError)
349 //! 2. Get property: cannels input.
351 // Allocate size according to the property size. Note that this is a variable sized struct...
352 AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
356 memset (pStreamBuffers, 0, propSize);
358 // Get the Input channels
359 err = AudioDeviceGetProperty (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
360 if (err == kAudioHardwareNoError)
362 // Calculate the number of input channels
363 for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
365 maxInputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
370 retVal = eCoreAudioFailed;
371 DEBUG_MSG("Failed to get device Input channels. Device Name: " << m_DeviceName.c_str());
374 free (pStreamBuffers);
378 retVal = eMemOutOfMemory;
379 DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
384 retVal = eCoreAudioFailed;
385 DEBUG_MSG("Failed to get device Input channels property size. Device Name: " << m_DeviceName.c_str());
388 // Update input channels
389 m_InputChannels.clear();
390 for (int channel = 0; channel < maxInputChannels; channel++)
392 std::stringstream chNameStream;
393 //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
394 chNameStream << "Input " << (channel+1);
395 m_InputChannels.push_back (chNameStream.str());
401 //**********************************************************************************************
402 // WCMRCoreAudioDevice::UpdateDeviceOutputs
404 //! Updates Device Outputs.
406 //! Use 'kAudioDevicePropertyStreamConfiguration'
407 //! This property returns the stream configuration of the device in an
408 //! AudioBufferList (with the buffer pointers set to NULL) which describes the
409 //! list of streams and the number of channels in each stream. This corresponds
410 //! to what will be passed into the IOProc.
412 //! 1. Get property cannels output size.
413 //! 2. Get property: cannels output.
414 //! 3. Update output channels
418 //**********************************************************************************************
419 WTErr WCMRCoreAudioDevice::UpdateDeviceOutputs()
423 WTErr retVal = eNoErr;
424 OSStatus err = kAudioHardwareNoError;
426 int maxOutputChannels = 0;
428 //! 1. Get property cannels output size.
429 err = AudioDeviceGetPropertyInfo (m_DeviceID, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
430 if (err == kAudioHardwareNoError)
432 //! 2. Get property: cannels output.
434 // Allocate size according to the property size. Note that this is a variable sized struct...
435 AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
438 memset (pStreamBuffers, 0, propSize);
440 // Get the Output channels
441 err = AudioDeviceGetProperty (m_DeviceID, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
442 if (err == kAudioHardwareNoError)
444 // Calculate the number of output channels
445 for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
447 maxOutputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
452 retVal = eCoreAudioFailed;
453 DEBUG_MSG("Failed to get device Output channels. Device Name: " << m_DeviceName.c_str());
455 free (pStreamBuffers);
459 retVal = eMemOutOfMemory;
460 DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
465 retVal = eCoreAudioFailed;
466 DEBUG_MSG("Failed to get device Output channels property size. Device Name: " << m_DeviceName.c_str());
469 // Update output channels
470 m_OutputChannels.clear();
471 for (int channel = 0; channel < maxOutputChannels; channel++)
473 std::stringstream chNameStream;
474 //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
475 chNameStream << "Output " << (channel+1);
476 m_OutputChannels.push_back (chNameStream.str());
482 //**********************************************************************************************
483 // WCMRCoreAudioDevice::UpdateDeviceSampleRates
485 //! Updates Device Sample rates.
487 //! Use 'kAudioDevicePropertyAvailableNominalSampleRates'
489 //! 1. Get sample rate property size.
490 //! 2. Get property: sample rates.
491 //! 3. Update sample rates
495 //**********************************************************************************************
496 WTErr WCMRCoreAudioDevice::UpdateDeviceSampleRates()
500 WTErr retVal = eNoErr;
501 OSStatus err = kAudioHardwareNoError;
504 m_SamplingRates.clear();
506 //! 1. Get sample rate property size.
507 err = AudioDeviceGetPropertyInfo(m_DeviceID, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL);
508 if (err == kAudioHardwareNoError)
510 //! 2. Get property: cannels output.
512 // Allocate size accrding to the number of audio values
513 int numRates = propSize / sizeof(AudioValueRange);
514 AudioValueRange* supportedRates = new AudioValueRange[numRates];
516 // Get sampling rates from Audio device
517 err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates);
518 if (err == kAudioHardwareNoError)
520 //! 3. Update sample rates
522 // now iterate through our standard SRs
523 for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++)
525 //check to see if our SR is in the supported rates...
526 for (int deviceSR = 0; deviceSR < numRates; deviceSR++)
528 if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) &&
529 (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR]))
531 m_SamplingRates.push_back ((int)gAllSampleRates[ourSR]);
539 retVal = eCoreAudioFailed;
540 DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str());
543 delete [] supportedRates;
547 retVal = eCoreAudioFailed;
548 DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str());
555 //**********************************************************************************************
556 // WCMRCoreAudioDevice::UpdateDeviceBufferSizes_Simple
558 // Use kAudioDevicePropertyBufferFrameSizeRange
560 // in case of 'eMatchedDuplexDevices' and a matching device exists return common device name
561 // in all other cases retur base class function implementation
563 // 1. Get buffer size range
564 // 2. Run on all ranges and add them to the list
566 // \return error code
568 //**********************************************************************************************
569 WTErr WCMRCoreAudioDevice::UpdateDeviceBufferSizes ()
573 WTErr retVal = eNoErr;
574 OSStatus err = kAudioHardwareNoError;
577 // Clear buffer sizes
578 m_BufferSizes.clear();
580 // 1. Get buffer size range
581 AudioValueRange bufferSizesRange;
582 propSize = sizeof (AudioValueRange);
583 err = AudioDeviceGetProperty (m_DeviceID, 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &bufferSizesRange);
584 if(err == kAudioHardwareNoError)
586 // 2. Run on all ranges and add them to the list
587 for(int bsize=0; gAllBufferSizes[bsize] > 0; bsize++)
589 if ((bufferSizesRange.mMinimum <= gAllBufferSizes[bsize]) && (bufferSizesRange.mMaximum >= gAllBufferSizes[bsize]))
591 m_BufferSizes.push_back (gAllBufferSizes[bsize]);
595 //if we didn't get a single hit, let's simply add the min. and the max...
596 if (m_BufferSizes.empty())
598 m_BufferSizes.push_back ((int)bufferSizesRange.mMinimum);
599 m_BufferSizes.push_back ((int)bufferSizesRange.mMaximum);
604 retVal = eCoreAudioFailed;
605 DEBUG_MSG("Failed to get device buffer sizes range. Device Name: " << m_DeviceName.c_str());
612 //**********************************************************************************************
613 // WCMRCoreAudioDevice::DeviceName
615 //! in case of 'eMatchedDuplexDevices' and a matching device exists return common device name
616 //! in all other cases retur base class function implementation
620 //! \return current device name
622 //**********************************************************************************************
623 const std::string& WCMRCoreAudioDevice::DeviceName() const
625 return WCMRAudioDevice::DeviceName();
628 //**********************************************************************************************
629 // WCMRCoreAudioDevice::InputChannels
631 //! return base class function implementation
635 //! \return base class function implementation
637 //**********************************************************************************************
638 const std::vector<std::string>& WCMRCoreAudioDevice::InputChannels()
640 return WCMRAudioDevice::InputChannels();
643 //**********************************************************************************************
644 // WCMRCoreAudioDevice::OutputChannels
646 //! in case of 'eMatchedDuplexDevices' return matching device output channel if there is one
647 //! in all other cases retur base class function implementation
651 //! \return list of output channels of current device
653 //**********************************************************************************************
654 const std::vector<std::string>& WCMRCoreAudioDevice::OutputChannels()
656 return WCMRAudioDevice::OutputChannels();
660 //**********************************************************************************************
661 // WCMRCoreAudioDevice::SamplingRates
663 //! in case of 'eMatchedDuplexDevices' and a matching device exists return common sample rate
664 //! in all other cases retur base class function implementation
668 //! \return current sample rate
670 //**********************************************************************************************
671 const std::vector<int>& WCMRCoreAudioDevice::SamplingRates()
673 return WCMRAudioDevice::SamplingRates();
676 //**********************************************************************************************
677 // WCMRCoreAudioDevice::CurrentSamplingRate
679 //! The device's current sampling rate. This may be overridden, if the device needs to
680 //! query the driver for the current rate.
684 //! \return The device's current sampling rate. -1 on error.
686 //**********************************************************************************************
687 int WCMRCoreAudioDevice::CurrentSamplingRate ()
690 //ToDo: Perhaps for ASIO devices that are active, we should retrive the SR from the device...
692 OSStatus err = kAudioHardwareNoError;
694 Float64 currentNominalRate;
695 propSize = sizeof (currentNominalRate);
696 err = kAudioHardwareNoError;
697 if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate) != kAudioHardwareNoError)
698 err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate);
700 if (err == kAudioHardwareNoError)
701 m_CurrentSamplingRate = (int)currentNominalRate;
704 DEBUG_MSG("Unable to get sampling rate!");
707 return (m_CurrentSamplingRate);
713 //**********************************************************************************************
714 // WCMRCoreAudioDevice::SetCurrentSamplingRate
716 //! Change the sampling rate to be used by the device.
718 //! \param newRate : The rate to use (samples per sec).
720 //! \return eNoErr always. The derived classes may return error codes.
722 //**********************************************************************************************
723 WTErr WCMRCoreAudioDevice::SetCurrentSamplingRate (int newRate)
726 std::vector<int>::iterator intIter;
727 WTErr retVal = eNoErr;
729 //changes the status.
730 int oldRate = CurrentSamplingRate();
731 bool oldActive = Active();
733 //no change, nothing to do
734 if (oldRate == newRate)
737 //see if this is one of our supported rates...
738 intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), newRate);
739 if (intIter == m_SamplingRates.end())
741 //Can't change, perhaps use an "invalid param" type of error
742 retVal = eCommandLineParameter;
748 //Can't change, perhaps use an "in use" type of error
749 retVal = eGenericErr;
755 //Deactivate it for the change...
759 retVal = SetAndCheckCurrentSamplingRate (newRate);
762 retVal = UpdateDeviceInfo ();
768 retVal = SetActive (true);
777 //**********************************************************************************************
778 // WCMRCoreAudioDevice::SetAndCheckCurrentSamplingRate
780 //! Change the sampling rate to be used by the device.
782 //! \param newRate : The rate to use (samples per sec).
784 //! \return eNoErr always. The derived classes may return error codes.
786 //**********************************************************************************************
787 WTErr WCMRCoreAudioDevice::SetAndCheckCurrentSamplingRate (int newRate)
790 std::vector<int>::iterator intIter;
791 WTErr retVal = eNoErr;
792 OSStatus err = kAudioHardwareNoError;
795 // 1. Set new sampling rate
796 Float64 newNominalRate = newRate;
797 propSize = sizeof (Float64);
798 err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 0, kAudioDevicePropertyNominalSampleRate, propSize, &newNominalRate);
800 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Changed the Sampling Rate.");
802 if (err != kAudioHardwareNoError)
804 retVal = eCoreAudioFailed;
805 DEBUG_MSG ("Unable to set SR! Device name: " << m_DeviceName.c_str());
809 // 2. wait for the SR to actually change...
811 // Set total time out time
812 int tryAgain = ((PROPERTY_CHANGE_TIMEOUT_SECONDS * 1000) / PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS) ;
814 Float64 actualSamplingRate = 0.0;
816 // Run as ling as time out is not finished
819 // Get current sampling rate
820 err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSamplingRate);
821 if (err == kAudioHardwareNoError)
823 if (actualSamplingRate == newNominalRate)
825 //success, let's get out!
831 //error reading rate, but let's not complain too much!
832 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Could not read Sampling Rate for verification.");
833 DEBUG_MSG ("Unable to get SR. Device name: " << m_DeviceName.c_str());
836 // oh well...there's always another millisecond...
837 wvThread::sleep_milliseconds (PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS);
842 // If sample rate actually changed
845 // Update member with new rate
846 m_CurrentSamplingRate = newRate;
849 snprintf (debugMsg, sizeof(debugMsg), "Actual Wait for SR Change was %d milliseconds", actualWait * PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS);
850 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg);
852 // If sample rate did not change after time out
855 // Update member with last read value
856 m_CurrentSamplingRate = static_cast<int>(actualSamplingRate);
859 snprintf (debugMsg, sizeof(debugMsg), "Unable to change SR, even after waiting for %d milliseconds", actualWait * PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS);
860 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg);
868 //**********************************************************************************************
869 // WCMRCoreAudioDevice::BufferSizes
871 //! in case of 'eMatchedDuplexDevices' and a matching device exists return common buffer sizes
872 //! in all other cases retur base class function implementation
876 //! \return current sample rate
878 //**********************************************************************************************
879 const std::vector<int>& WCMRCoreAudioDevice::BufferSizes()
881 return WCMRAudioDevice::BufferSizes();
885 //**********************************************************************************************
886 // WCMRCoreAudioDevice::CurrentBufferSize
888 //! The device's current buffer size in use. This may be overridden, if the device needs to
889 //! query the driver for the current size.
893 //! \return The device's current buffer size. 0 on error.
895 //**********************************************************************************************
896 int WCMRCoreAudioDevice::CurrentBufferSize ()
900 return (m_CurrentBufferSize);
905 //**********************************************************************************************
906 // WCMRCoreAudioDevice::SetCurrentBufferSize
908 //! Change the buffer size to be used by the device. This will most likely be overridden,
909 //! the base class simply updates the member variable.
911 //! \param newSize : The buffer size to use (in sample-frames)
913 //! \return eNoErr always. The derived classes may return error codes.
915 //**********************************************************************************************
916 WTErr WCMRCoreAudioDevice::SetCurrentBufferSize (int newSize)
919 WTErr retVal = eNoErr;
920 std::vector<int>::iterator intIter;
922 //changes the status.
923 int oldSize = CurrentBufferSize();
924 bool oldActive = Active();
926 //same size, nothing to do.
927 if (oldSize == newSize)
932 //Can't change, perhaps use an "in use" type of error
933 retVal = eGenericErr;
939 //Deactivate it for the change...
943 // when audio device is inactive it is safe to set a working buffer size according to new buffer size
944 // if 'newSize' is not a valid buffer size, another valid buffer size will be set
945 retVal = SetWorkingBufferSize(newSize);
948 DEBUG_MSG("Unable to set a working buffer size. Device Name: " << DeviceName().c_str());
955 retVal = SetActive (true);
958 DEBUG_MSG("Unable to activate device. Device Name: " << DeviceName().c_str());
968 WTErr WCMRCoreAudioDevice::SetWorkingBufferSize(int newSize)
971 WTErr retVal = eNoErr;
972 OSStatus err = kAudioHardwareNoError;
974 // 1. Set new buffer size
975 err = SetBufferSizesByIO(newSize);
977 // If there's no error it means this buffer size is supported
978 if(err == kAudioHardwareNoError)
980 m_CurrentBufferSize = newSize;
982 // If there was an error it means that this buffer size was not supported
985 // In case the new buffer size could not be set, set another working buffer size
987 // Run on all buffer sizes:
989 // Try setting buffer sizes that are bigger then selected buffer size first,
990 // Since bigger buffer sizes usually work safer
991 for(std::vector<int>::const_iterator iter = m_BufferSizes.begin();iter != m_BufferSizes.end();++iter)
997 // Try setting current buffer size
998 err = SetBufferSizesByIO(nCurBS);
1000 // in case buffer size is valid
1001 if(err == kAudioHardwareNoError)
1003 // Set current buffer size
1004 m_CurrentBufferSize = nCurBS;
1010 // If bigger buffer sizes failed, go to smaller buffer sizes
1011 if(err != kAudioHardwareNoError)
1013 for(std::vector<int>::const_iterator iter = m_BufferSizes.begin();iter != m_BufferSizes.end();++iter)
1017 if(nCurBS < newSize)
1019 // Try setting current buffer size
1020 err = SetBufferSizesByIO(*iter);
1022 // in case buffer size is valid
1023 if(err == kAudioHardwareNoError)
1025 // Set current buffer size
1026 m_CurrentBufferSize = *iter;
1033 // Check if a valid buffer size was found
1034 if(err == kAudioHardwareNoError)
1036 // Notify that a different sample rate is set
1038 snprintf (debugMsg, sizeof(debugMsg), "Could not set buffer size: %d, Set buffer size to: %d.", newSize, m_CurrentBufferSize);
1039 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg);
1041 // if there was no buffer size that could be set
1044 // Set the parameter buffer size by default, set a debug message
1045 m_CurrentBufferSize = newSize;
1046 DEBUG_MSG("Unable to set any buffer size. Device Name: " << m_DeviceName.c_str());
1053 OSStatus WCMRCoreAudioDevice::SetBufferSizesByIO(int newSize)
1055 OSStatus err = kAudioHardwareNoError;
1057 // 1. Set new buffer size
1058 UInt32 bufferSize = (UInt32)newSize;
1059 UInt32 propSize = sizeof (UInt32);
1061 // Set new buffer size to input
1062 if (!m_InputChannels.empty())
1064 err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 1, kAudioDevicePropertyBufferFrameSize, propSize, &bufferSize);
1068 err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 0, kAudioDevicePropertyBufferFrameSize, propSize, &bufferSize);
1074 //**********************************************************************************************
1075 // WCMRCoreAudioDevice::ConnectionStatus
1077 //! Retrieves the device's current connection status. This will most likely be overridden,
1078 //! in case some driver communication is required to query the status.
1082 //! \return A ConnectionStates value.
1084 //**********************************************************************************************
1085 WCMRCoreAudioDevice::ConnectionStates WCMRCoreAudioDevice::ConnectionStatus ()
1088 //ToDo: May want to do something more to extract the actual status!
1089 return (m_ConnectionStatus);
1094 //**********************************************************************************************
1095 // WCMRCoreAudioDevice::EnableAudioUnitIO
1097 //! Sets up the AUHAL for IO, allowing changes to the devices to be used by the AudioUnit.
1101 //! \return eNoErr on success, an error code on failure.
1103 //**********************************************************************************************
1104 WTErr WCMRCoreAudioDevice::EnableAudioUnitIO()
1107 WTErr retVal = eNoErr;
1108 OSStatus err = kAudioHardwareNoError;
1110 UInt32 enableIO = 1;
1111 if (!m_InputChannels.empty())
1115 //You must enable the Audio Unit (AUHAL) for input
1117 //Enable input on the AUHAL
1118 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1119 kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
1120 AUHAL_INPUT_ELEMENT,
1121 &enableIO, sizeof(enableIO));
1125 DEBUG_MSG("Couldn't Enable IO on input scope of input element, error = " << err);
1126 retVal = eGenericErr;
1131 //disable Output on the AUHAL if there's no output
1132 if (m_OutputChannels.empty())
1137 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1138 kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1139 AUHAL_OUTPUT_ELEMENT,
1140 &enableIO, sizeof(enableIO));
1144 DEBUG_MSG("Couldn't Enable/Disable IO on output scope of output element, error = " << err);
1145 retVal = eGenericErr;
1154 //**********************************************************************************************
1155 // WCMRCoreAudioDevice::EnableListeners
1157 //! Sets up listeners to listen for Audio Device property changes, so that app can be notified.
1161 //! \return eNoErr on success, an error code on failure.
1163 //**********************************************************************************************
1164 WTErr WCMRCoreAudioDevice::EnableListeners()
1167 WTErr retVal = eNoErr;
1168 OSStatus err = kAudioHardwareNoError;
1170 //listner for SR change...
1171 err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate,
1172 StaticPropertyChangeProc, this);
1176 DEBUG_MSG("Couldn't Setup SR Property Listner, error = " << err);
1177 retVal = eGenericErr;
1181 #if ENABLE_DEVICE_CHANGE_LISTNER
1183 //listner for device change...
1184 err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
1185 StaticPropertyChangeProc, this);
1189 DEBUG_MSG("Couldn't Setup device change Property Listner, error = " << err);
1190 retVal = eGenericErr;
1194 #endif //ENABLE_DEVICE_CHANGE_LISTNER
1196 //listner for dropouts...
1197 err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDeviceProcessorOverload,
1198 StaticPropertyChangeProc, this);
1202 DEBUG_MSG("Couldn't Setup Processor Overload Property Listner, error = " << err);
1203 retVal = eGenericErr;
1214 //**********************************************************************************************
1215 // WCMRCoreAudioDevice::DisableListeners
1217 //! Undoes the work done by EnableListeners
1221 //! \return eNoErr on success, an error code on failure.
1223 //**********************************************************************************************
1224 WTErr WCMRCoreAudioDevice::DisableListeners()
1227 WTErr retVal = eNoErr;
1228 OSStatus err = kAudioHardwareNoError;
1230 //listner for SR change...
1231 err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate,
1232 StaticPropertyChangeProc);
1236 DEBUG_MSG("Couldn't Cleanup SR Property Listner, error = " << err);
1237 //not sure if we need to report this...
1240 #if ENABLE_DEVICE_CHANGE_LISTNER
1242 err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
1243 StaticPropertyChangeProc);
1247 DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err);
1248 //not sure if we need to report this...
1251 #endif //ENABLE_DEVICE_CHANGE_LISTNER
1253 err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDeviceProcessorOverload,
1254 StaticPropertyChangeProc);
1258 DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err);
1259 //not sure if we need to report this...
1267 //**********************************************************************************************
1268 // WCMRCoreAudioDevice::StaticPropertyChangeProc
1270 //! The property change function called (as a result of EnableListeners) when device properties change.
1271 //! It calls upon the non-static PropertyChangeProc to do the work.
1273 //! \param inDevice : The audio device in question.
1274 //! \param inChannel : The channel on which the property has change.
1275 //! \param isInput : If the change is for Input.
1276 //! \param inPropertyID : The property that has changed.
1277 //! \param inClientData: What was passed when listener was enabled, in our case teh WCMRCoreAudioDevice object.
1279 //! \return 0 always.
1281 //**********************************************************************************************
1282 OSStatus WCMRCoreAudioDevice::StaticPropertyChangeProc (AudioDeviceID /*inDevice*/, UInt32 /*inChannel*/, Boolean /*isInput*/,
1283 AudioDevicePropertyID inPropertyID, void *inClientData)
1287 WCMRCoreAudioDevice* pCoreDevice = (WCMRCoreAudioDevice *)inClientData;
1288 pCoreDevice->PropertyChangeProc (inPropertyID);
1296 //**********************************************************************************************
1297 // WCMRCoreAudioDevice::PropertyChangeProc
1299 //! The non-static property change proc. Gets called when properties change. Since this gets called
1300 //! on an arbitrary thread, we simply update the request counters and return.
1304 //! \return nothing.
1306 //**********************************************************************************************
1307 void WCMRCoreAudioDevice::PropertyChangeProc (AudioDevicePropertyID inPropertyID)
1309 switch (inPropertyID)
1311 case kAudioDevicePropertyNominalSampleRate:
1312 m_SRChangeRequested++;
1314 #if ENABLE_DEVICE_CHANGE_LISTNER
1315 case kAudioDevicePropertyDeviceHasChanged:
1320 #endif //ENABLE_DEVICE_CHANGE_LISTNER
1321 case kAudioDeviceProcessorOverload:
1322 if (m_IgnoreThisDrop)
1323 m_IgnoreThisDrop = false; //We'll ignore once, just once!
1333 //**********************************************************************************************
1334 // WCMRCoreAudioDevice::SetupAUHAL
1336 //! Sets up the AUHAL AudioUnit for device IO.
1340 //! \return eNoErr on success, an error code on failure.
1342 //**********************************************************************************************
1343 WTErr WCMRCoreAudioDevice::SetupAUHAL()
1346 WTErr retVal = eNoErr;
1347 OSStatus err = kAudioHardwareNoError;
1348 UInt32 propSize = 0;
1350 ComponentDescription desc;
1351 AudioStreamBasicDescription streamFormatToUse, auhalStreamFormat;
1353 //There are several different types of Audio Units.
1354 //Some audio units serve as Outputs, Mixers, or DSP
1355 //units. See AUComponent.h for listing
1356 desc.componentType = kAudioUnitType_Output;
1358 //Every Component has a subType, which will give a clearer picture
1359 //of what this components function will be.
1360 desc.componentSubType = kAudioUnitSubType_HALOutput;
1362 //all Audio Units in AUComponent.h must use
1363 //"kAudioUnitManufacturer_Apple" as the Manufacturer
1364 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1365 desc.componentFlags = 0;
1366 desc.componentFlagsMask = 0;
1368 //Finds a component that meets the desc spec's
1369 comp = FindNextComponent(NULL, &desc);
1372 DEBUG_MSG("Couldn't find AUHAL Component");
1373 retVal = eGenericErr;
1377 //gains access to the services provided by the component
1378 OpenAComponent(comp, &m_AUHALAudioUnit);
1381 retVal = EnableAudioUnitIO();
1382 if (retVal != eNoErr)
1385 //Now setup the device to use by the audio unit...
1388 if (!m_InputChannels.empty())
1390 err = AudioUnitSetProperty(m_AUHALAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
1391 kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT,
1392 &m_DeviceID, sizeof(m_DeviceID));
1396 DEBUG_MSG("Couldn't Set the audio device property for Input Element Global scope, error = " << err);
1397 retVal = eGenericErr;
1403 if (!m_OutputChannels.empty())
1405 err = AudioUnitSetProperty(m_AUHALAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
1406 kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT,
1407 &m_DeviceID, sizeof(m_DeviceID));
1411 DEBUG_MSG("Couldn't Set the audio device property for Output Element Global scope, error = " << err);
1412 retVal = eGenericErr;
1417 //also set Sample Rate...
1419 retVal = SetAndCheckCurrentSamplingRate(m_CurrentSamplingRate);
1420 if(retVal != eNoErr)
1422 DEBUG_MSG ("Unable to set SR, error = " << err);
1427 //now set the buffer size...
1429 err = SetWorkingBufferSize(m_CurrentBufferSize);
1432 DEBUG_MSG("Couldn't Set the buffer size property, error = " << err);
1433 //we don't really quit here..., just keep going even if this does not work,
1434 //the AUHAL is supposed to take care of this by way of slicing...
1435 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Could not set buffer size.");
1442 UInt32 quality = kAudioConverterQuality_Max;
1443 propSize = sizeof (quality);
1444 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1445 kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global,
1446 AUHAL_OUTPUT_ELEMENT,
1447 &quality, sizeof (quality));
1449 if (err != kAudioHardwareNoError)
1451 DEBUG_MSG ("Unable to set Convertor Quality, error = " << err);
1452 retVal = eGenericErr;
1457 memset (&auhalStreamFormat, 0, sizeof (auhalStreamFormat));
1458 propSize = sizeof (auhalStreamFormat);
1459 err = AudioUnitGetProperty(m_AUHALAudioUnit,
1460 kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1461 AUHAL_INPUT_ELEMENT,
1462 &auhalStreamFormat, &propSize);
1463 if (err != kAudioHardwareNoError)
1465 DEBUG_MSG ("Unable to get Input format, error = " << err);
1466 retVal = eGenericErr;
1470 if (auhalStreamFormat.mSampleRate != (Float64)m_CurrentSamplingRate)
1472 TRACE_MSG ("AUHAL's Input SR differs from expected SR, expected = " << m_CurrentSamplingRate << ", AUHAL's = " << (UInt32)auhalStreamFormat.mSampleRate);
1475 //format, and slice size...
1476 memset (&streamFormatToUse, 0, sizeof (streamFormatToUse));
1477 streamFormatToUse.mFormatID = kAudioFormatLinearPCM;
1478 streamFormatToUse.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
1479 streamFormatToUse.mFramesPerPacket = 1;
1480 streamFormatToUse.mBitsPerChannel = sizeof (float) * 8;
1481 streamFormatToUse.mSampleRate = auhalStreamFormat.mSampleRate;
1483 if (!m_InputChannels.empty())
1485 streamFormatToUse.mChannelsPerFrame = m_InputChannels.size();
1486 streamFormatToUse.mBytesPerFrame = sizeof (float)*streamFormatToUse.mChannelsPerFrame;
1487 streamFormatToUse.mBytesPerPacket = streamFormatToUse.mBytesPerFrame;
1488 propSize = sizeof (streamFormatToUse);
1489 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1490 kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1491 AUHAL_INPUT_ELEMENT,
1492 &streamFormatToUse, sizeof (streamFormatToUse));
1494 if (err != kAudioHardwareNoError)
1496 DEBUG_MSG ("Unable to set Input format, error = " << err);
1497 retVal = eGenericErr;
1501 UInt32 bufferSize = m_CurrentBufferSize;
1502 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1503 kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output,
1504 AUHAL_INPUT_ELEMENT,
1505 &bufferSize, sizeof (bufferSize));
1507 if (err != kAudioHardwareNoError)
1509 DEBUG_MSG ("Unable to set Input frames, error = " << err);
1510 retVal = eGenericErr;
1516 if (!m_OutputChannels.empty())
1518 err = AudioUnitGetProperty(m_AUHALAudioUnit,
1519 kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1520 AUHAL_OUTPUT_ELEMENT,
1521 &auhalStreamFormat, &propSize);
1522 if (err != kAudioHardwareNoError)
1524 DEBUG_MSG ("Unable to get Output format, error = " << err);
1525 retVal = eGenericErr;
1529 if (auhalStreamFormat.mSampleRate != (Float64)m_CurrentSamplingRate)
1531 TRACE_MSG ("AUHAL's Output SR differs from expected SR, expected = " << m_CurrentSamplingRate << ", AUHAL's = " << (UInt32)auhalStreamFormat.mSampleRate);
1535 streamFormatToUse.mChannelsPerFrame = m_OutputChannels.size();
1536 streamFormatToUse.mBytesPerFrame = sizeof (float)*streamFormatToUse.mChannelsPerFrame;
1537 streamFormatToUse.mBytesPerPacket = streamFormatToUse.mBytesPerFrame;
1538 streamFormatToUse.mSampleRate = auhalStreamFormat.mSampleRate;
1539 propSize = sizeof (streamFormatToUse);
1540 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1541 kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1542 AUHAL_OUTPUT_ELEMENT,
1543 &streamFormatToUse, sizeof (streamFormatToUse));
1545 if (err != kAudioHardwareNoError)
1547 DEBUG_MSG ("Unable to set Output format, error = " << err);
1548 retVal = eGenericErr;
1552 UInt32 bufferSize = m_CurrentBufferSize;
1553 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1554 kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Input,
1555 AUHAL_OUTPUT_ELEMENT,
1556 &bufferSize, sizeof (bufferSize));
1558 if (err != kAudioHardwareNoError)
1560 DEBUG_MSG ("Unable to set Output frames, error = " << err);
1561 retVal = eGenericErr;
1567 //setup callback (IOProc)
1569 AURenderCallbackStruct renderCallback;
1570 memset (&renderCallback, 0, sizeof (renderCallback));
1571 propSize = sizeof (renderCallback);
1572 renderCallback.inputProc = StaticAudioIOProc;
1573 renderCallback.inputProcRefCon = this;
1575 err = AudioUnitSetProperty(m_AUHALAudioUnit,
1576 (m_OutputChannels.empty() ? (AudioUnitPropertyID)kAudioOutputUnitProperty_SetInputCallback : (AudioUnitPropertyID)kAudioUnitProperty_SetRenderCallback),
1577 kAudioUnitScope_Output,
1578 m_OutputChannels.empty() ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT,
1579 &renderCallback, sizeof (renderCallback));
1581 if (err != kAudioHardwareNoError)
1583 DEBUG_MSG ("Unable to set callback, error = " << err);
1584 retVal = eGenericErr;
1589 retVal = EnableListeners();
1590 if (retVal != eNoErr)
1593 //also prepare the buffer list for input...
1594 if (!m_InputChannels.empty())
1597 //now setup the buffer list.
1598 memset (&m_InputAudioBufferList, 0, sizeof (m_InputAudioBufferList));
1599 m_InputAudioBufferList.mNumberBuffers = 1;
1600 m_InputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size();
1601 m_InputAudioBufferList.mBuffers[0].mDataByteSize = m_InputAudioBufferList.mBuffers[0].mNumberChannels *
1602 m_CurrentBufferSize * sizeof(float);
1603 //allocate the data buffer...
1606 m_pInputData = new float[m_InputAudioBufferList.mBuffers[0].mNumberChannels * m_CurrentBufferSize];
1610 retVal = eMemNewFailed;
1614 m_InputAudioBufferList.mBuffers[0].mData = m_pInputData;
1617 memset (m_InputAudioBufferList.mBuffers[0].mData, 0, m_InputAudioBufferList.mBuffers[0].mDataByteSize);
1621 //initialize the audio-unit now!
1622 err = AudioUnitInitialize(m_AUHALAudioUnit);
1623 if (err != kAudioHardwareNoError)
1625 DEBUG_MSG ("Unable to Initialize AudioUnit = " << err);
1626 retVal = eGenericErr;
1631 if (retVal != eNoErr)
1639 //**********************************************************************************************
1640 // WCMRCoreAudioDevice::TearDownAUHAL
1642 //! Undoes the work done by SetupAUHAL
1646 //! \return eNoErr on success, an error code on failure.
1648 //**********************************************************************************************
1649 WTErr WCMRCoreAudioDevice::TearDownAUHAL()
1651 WTErr retVal = eNoErr;
1653 if (m_AUHALAudioUnit)
1655 DisableListeners ();
1656 AudioUnitUninitialize(m_AUHALAudioUnit);
1657 CloseComponent(m_AUHALAudioUnit);
1658 m_AUHALAudioUnit = NULL;
1661 safe_delete_array(m_pInputData);
1668 //**********************************************************************************************
1669 // WCMRCoreAudioDevice::SetActive
1671 //! Sets the device's activation status. Essentially, opens or closes the PA device.
1672 //! If it's an ASIO device it may result in buffer size change in some cases.
1674 //! \param newState : Should be true to activate, false to deactivate. This roughly corresponds
1675 //! to opening and closing the device handle/stream/audio unit.
1677 //! \return eNoErr on success, an error code otherwise.
1679 //**********************************************************************************************
1680 WTErr WCMRCoreAudioDevice::SetActive (bool newState)
1684 WTErr retVal = eNoErr;
1686 if (Active() == newState)
1693 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL.");
1694 retVal = SetupAUHAL();
1696 if (retVal != eNoErr)
1699 m_BufferSizeChangeRequested = 0;
1700 m_BufferSizeChangeReported = 0;
1701 m_ResetRequested = 0;
1702 m_ResetReported = 0;
1703 m_ResyncRequested = 0;
1704 m_ResyncReported = 0;
1705 m_SRChangeRequested = 0;
1706 m_SRChangeReported = 0;
1707 m_DropsDetected = 0;
1708 m_DropsReported = 0;
1709 m_IgnoreThisDrop = true;
1715 SetStreaming (false);
1718 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Tearing down AUHAL.");
1719 retVal = TearDownAUHAL();
1720 if (retVal != eNoErr)
1723 m_BufferSizeChangeRequested = 0;
1724 m_BufferSizeChangeReported = 0;
1725 m_ResetRequested = 0;
1726 m_ResetReported = 0;
1727 m_ResyncRequested = 0;
1728 m_ResyncReported = 0;
1729 m_SRChangeRequested = 0;
1730 m_SRChangeReported = 0;
1731 m_DropsDetected = 0;
1732 m_DropsReported = 0;
1733 m_IgnoreThisDrop = true;
1739 m_IsActive = newState;
1747 //**********************************************************************************************
1748 // WCMRCoreAudioDevice::SetupToneGenerator
1750 //! Sets up the Tone generator - only if a file /tmp/tonegen.txt is present. If the file is
1751 //! present, it reads the value in the file and uses that as the frequency for the tone. This
1752 //! code attempts to create an array of samples that would constitute an integral number of
1753 //! cycles - for the currently active sampling rate. If tonegen is active, then the input
1754 //! from the audio device is ignored, instead a data is supplied from the tone generator's
1755 //! array - for all channels. The array is in m_pToneData, the size of the array is in
1756 //! m_ToneDataSamples, and m_NextSampleToUse holds the index in the array from where
1757 //! the next sample is going to be taken.
1760 //! \return : Nothing
1762 //**********************************************************************************************
1763 void WCMRCoreAudioDevice::SetupToneGenerator ()
1765 safe_delete_array(m_pToneData);
1766 m_ToneDataSamples = 0;
1768 //if tonegen exists?
1769 FILE *toneGenHandle = fopen ("/tmp/tonegen.txt", "r");
1773 fscanf(toneGenHandle, "%d", &toneFreq);
1774 if ((toneFreq <= 0) || (toneFreq > (m_CurrentSamplingRate/2)))
1780 m_ToneDataSamples = m_CurrentSamplingRate / toneFreq;
1781 int toneDataSamplesFrac = m_CurrentSamplingRate % m_ToneDataSamples;
1783 while (toneDataSamplesFrac)
1785 m_ToneDataSamples = (uint32_t)((pow(10, powerOfTen) * m_CurrentSamplingRate) / toneFreq);
1786 toneDataSamplesFrac = m_CurrentSamplingRate % m_ToneDataSamples;
1791 m_pToneData = new float_t[m_ToneDataSamples];
1793 //fill with a -6dB Sine Tone
1794 uint32_t numSamplesLeft = m_ToneDataSamples;
1795 float_t *pNextSample = m_pToneData;
1797 double phaseIncrement = (M_PI * 2.0 * toneFreq ) / ((double)m_CurrentSamplingRate);
1798 while (numSamplesLeft)
1800 *pNextSample = (float_t)(0.5 * sin(phase));
1801 phase += phaseIncrement;
1806 m_NextSampleToUse = 0;
1808 fclose(toneGenHandle);
1811 #endif //WV_USE_TONE_GEN
1814 //**********************************************************************************************
1815 // WCMRCoreAudioDevice::SetStreaming
1817 //! Sets the device's streaming status. Calls PA's Start/Stop stream routines.
1819 //! \param newState : Should be true to start streaming, false to stop streaming. This roughly
1820 //! corresponds to calling Start/Stop on the lower level interface.
1822 //! \return eNoErr always, the derived classes may return appropriate error code.
1824 //**********************************************************************************************
1825 WTErr WCMRCoreAudioDevice::SetStreaming (bool newState)
1828 WTErr retVal = eNoErr;
1829 ComponentResult err = 0;
1831 if (Streaming () == newState)
1837 SetupToneGenerator ();
1838 #endif //WV_USE_TONE_GEN
1840 m_StopRequested = false;
1841 m_SampleCountAtLastIdle = 0;
1842 m_StalledSampleCounter = 0;
1843 m_SampleCounter = 0;
1844 m_IOProcThreadPort = 0;
1845 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Starting AUHAL.");
1847 if (m_UseMultithreading)
1849 //set thread constraints...
1850 unsigned int periodAndConstraintUS = (unsigned int)((1000000.0 * m_CurrentBufferSize) / m_CurrentSamplingRate);
1851 unsigned int computationUS = (unsigned int)(0.8 * periodAndConstraintUS); //assuming we may want to use up to 80% CPU
1852 //ErrandManager().SetRealTimeConstraintsForAllThreads (periodAndConstraintUS, computationUS, periodAndConstraintUS);
1855 err = AudioOutputUnitStart (m_AUHALAudioUnit);
1859 DEBUG_MSG( "Failed to start AudioUnit, err " << err );
1860 retVal = eGenericErr;
1866 m_StopRequested = true;
1867 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Stopping AUHAL.");
1868 err = AudioOutputUnitStop (m_AUHALAudioUnit);
1871 if (!m_InputChannels.empty());
1873 err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT);
1875 if (!m_OutputChannels.empty());
1877 err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT);
1883 DEBUG_MSG( "Failed to stop AudioUnit " << err );
1884 retVal = eGenericErr;
1887 m_IOProcThreadPort = 0;
1890 // After units restart, reset request for reset and SR change
1891 m_SRChangeReported = m_SRChangeRequested;
1892 m_ResetReported = m_ResetRequested;
1894 m_IsStreaming = newState;
1901 //**********************************************************************************************
1902 // WCMRCoreAudioDevice::DoIdle
1904 //! A place for doing idle time processing. The other derived classes will probably do something
1909 //! \return eNoErr always.
1911 //**********************************************************************************************
1912 WTErr WCMRCoreAudioDevice::DoIdle ()
1914 if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported)
1916 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged);
1917 m_BufferSizeChangeReported = m_BufferSizeChangeRequested;
1920 if (m_ResetRequested != m_ResetReported)
1922 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
1923 m_ResetReported = m_ResetRequested;
1927 if (m_ResyncRequested != m_ResyncReported)
1929 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestResync);
1930 m_ResyncReported = m_ResyncRequested;
1933 if (m_SRChangeReported != m_SRChangeRequested)
1935 m_SRChangeReported = m_SRChangeRequested;
1936 int newSR = CurrentSamplingRate();
1937 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::SamplingRateChanged, (void *)newSR);
1940 if (m_DropsReported != m_DropsDetected)
1942 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDroppedSamples);
1943 m_DropsReported = m_DropsDetected;
1947 //Perhaps add checks to make sure a stream counter is incrementing if
1948 //stream is supposed to be streaming!
1952 int64_t currentSampleCount = m_SampleCounter;
1953 if (m_SampleCountAtLastIdle == currentSampleCount)
1954 m_StalledSampleCounter++;
1957 m_SampleCountAtLastIdle = (int)currentSampleCount;
1958 m_StalledSampleCounter = 0;
1961 if (m_StalledSampleCounter > NUM_STALLS_FOR_NOTIFICATION)
1963 m_StalledSampleCounter = 0;
1964 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount);
1976 //**********************************************************************************************
1977 // WCMRCoreAudioDevice::SetMonitorChannels
1979 //! Used to set the channels to be used for monitoring.
1981 //! \param leftChannel : Left monitor channel index.
1982 //! \param rightChannel : Right monitor channel index.
1984 //! \return eNoErr always, the derived classes may return appropriate errors.
1986 //**********************************************************************************************
1987 WTErr WCMRCoreAudioDevice::SetMonitorChannels (int leftChannel, int rightChannel)
1990 //This will most likely be overridden, the base class simply
1991 //changes the member.
1992 m_LeftMonitorChannel = leftChannel;
1993 m_RightMonitorChannel = rightChannel;
1999 //**********************************************************************************************
2000 // WCMRCoreAudioDevice::SetMonitorGain
2002 //! Used to set monitor gain (or atten).
2004 //! \param newGain : The new gain or atten. value to use. Specified as a linear multiplier (not dB)
2006 //! \return eNoErr always, the derived classes may return appropriate errors.
2008 //**********************************************************************************************
2009 WTErr WCMRCoreAudioDevice::SetMonitorGain (float newGain)
2012 //This will most likely be overridden, the base class simply
2013 //changes the member.
2016 m_MonitorGain = newGain;
2023 //**********************************************************************************************
2024 // WCMRCoreAudioDevice::ShowConfigPanel
2026 //! Used to show device specific config/control panel. Some interfaces may not support it.
2027 //! Some interfaces may require the device to be active before it can display a panel.
2029 //! \param pParam : A device/interface specific parameter, should be the app window handle for ASIO.
2031 //! \return eNoErr always, the derived classes may return errors.
2033 //**********************************************************************************************
2034 WTErr WCMRCoreAudioDevice::ShowConfigPanel (void */*pParam*/)
2037 WTErr retVal = eNoErr;
2039 CFStringRef configAP;
2040 UInt32 propSize = sizeof (configAP);
2042 @constant kAudioDevicePropertyConfigurationApplication
2043 A CFString that contains the bundle ID for an application that provides a
2044 GUI for configuring the AudioDevice. By default, the value of this property
2045 is the bundle ID for Audio MIDI Setup. The caller is responsible for
2046 releasing the returned CFObject.
2049 if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyConfigurationApplication, &propSize, &configAP) == kAudioHardwareNoError)
2051 // get the FSRef of the config app
2053 OSStatus theError = LSFindApplicationForInfo(kLSUnknownCreator, configAP, NULL, &theAppFSRef, NULL);
2056 LSOpenFSRef(&theAppFSRef, NULL);
2058 CFRelease (configAP);
2065 //**********************************************************************************************
2066 // WCMRCoreAudioDevice::StaticAudioIOProc
2068 //! The AudioIOProc that gets called when the AudioUnit is ready with recorded audio, and wants to get audio.
2069 //! This one simply calls the non-static member.
2071 //! \param inRefCon : What was passed when setting up the Callback (in our case a pointer to teh WCMRCoreAudioDevice object).
2072 //! \param ioActionFlags : What actios has to be taken.
2073 //! \param inTimeStamp: When the data will be played back.
2074 //! \param inBusNumber : The AU element.
2075 //! \param inNumberFrames: Number af Audio frames that are requested.
2076 //! \param ioData : Where the playback data is to be placed.
2078 //! \return 0 always
2080 //**********************************************************************************************
2081 OSStatus WCMRCoreAudioDevice::StaticAudioIOProc(void *inRefCon, AudioUnitRenderActionFlags * ioActionFlags,
2082 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
2083 AudioBufferList *ioData)
2085 WCMRCoreAudioDevice *pMyDevice = (WCMRCoreAudioDevice *)inRefCon;
2087 return pMyDevice->AudioIOProc (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
2095 //**********************************************************************************************
2096 // WCMRCoreAudioDevice::AudioIOProc
2098 //! The non-static AudioIOProc that gets called when the AudioUnit is ready with recorded audio, and wants to get audio.
2099 //! We retrieve the recorded audio, and then do our processing, to generate audio to be played back.
2101 //! \param ioActionFlags : What actios has to be taken.
2102 //! \param inTimeStamp: When the data will be played back.
2103 //! \param inBusNumber : The AU element.
2104 //! \param inNumberFrames: Number af Audio frames that are requested.
2105 //! \param ioData : Where the playback data is to be placed.
2107 //! \return 0 always
2109 //**********************************************************************************************
2110 OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioActionFlags,
2111 const AudioTimeStamp *inTimeStamp, UInt32 /*inBusNumber*/, UInt32 inNumberFrames,
2112 AudioBufferList *ioData)
2114 UInt64 theStartTime = AudioGetCurrentHostTime();
2116 OSStatus retVal = 0;
2118 if (m_StopRequested)
2121 if (m_IOProcThreadPort == 0)
2122 m_IOProcThreadPort = mach_thread_self ();
2124 //cannot really deal with it unless the number of frames are the same as our buffer size!
2125 if (inNumberFrames != (UInt32)m_CurrentBufferSize)
2128 //Retrieve the input data...
2129 if (!m_InputChannels.empty())
2131 retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &m_InputAudioBufferList);
2134 //is this an input only device?
2135 if (m_OutputChannels.empty())
2136 AudioCallback (NULL, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime);
2137 else if ((!m_OutputChannels.empty()) && (ioData->mBuffers[0].mNumberChannels == m_OutputChannels.size()))
2138 AudioCallback ((float *)ioData->mBuffers[0].mData, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime);
2145 //**********************************************************************************************
2146 // WCMRCoreAudioDevice::AudioCallback
2148 //! Here's where the actual audio processing happens. We call upon all the active connections'
2149 //! sinks to provide data to us which can be put/mixed in the output buffer! Also, we make the
2150 //! input data available to any sources that may call upon us during this time!
2152 //! \param *pOutputBuffer : Points to a buffer to receive playback data. For Input only devices, this will be NULL
2153 //! \param framesPerBuffer : Number of sample frames in input and output buffers. Number of channels,
2154 //! which are interleaved, is fixed at Device Open (Active) time. In this implementation,
2155 //! the number of channels are fixed to use the maximum available.
2159 //**********************************************************************************************
2160 int WCMRCoreAudioDevice::AudioCallback (float *pOutputBuffer, unsigned long framesPerBuffer, uint32_t inSampleTime, uint64_t inCycleStartTime)
2162 struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData =
2168 AudioConvertHostTimeToNanos(inCycleStartTime)
2171 m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData);
2173 m_SampleCounter += framesPerBuffer;
2174 return m_StopRequested;
2178 //**********************************************************************************************
2179 // WCMRCoreAudioDevice::GetLatency
2181 //! Get Latency for device.
2183 //! Use 'kAudioDevicePropertyLatency' and 'kAudioDevicePropertySafetyOffset' + GetStreamLatencies
2185 //! \param isInput : Return latency for the input if isInput is true, otherwise the output latency
2186 //! wiil be returned.
2187 //! \return Latency in samples.
2189 //**********************************************************************************************
2190 uint32_t WCMRCoreAudioDevice::GetLatency(bool isInput)
2192 WTErr retVal = eNoErr;
2193 OSStatus err = kAudioHardwareNoError;
2195 UInt32 propSize = sizeof(UInt32);
2200 std::vector<int> streamLatencies;
2203 err = AudioDeviceGetProperty(m_DeviceID, 0, isInput, kAudioDevicePropertyLatency, &propSize, &value1);
2204 if (err != kAudioHardwareNoError)
2206 DEBUG_MSG("GetLatency kAudioDevicePropertyLatency err = " << err);
2209 err = AudioDeviceGetProperty(m_DeviceID, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &value2);
2210 if (err != kAudioHardwareNoError)
2212 DEBUG_MSG("GetLatency kAudioDevicePropertySafetyOffset err = " << err);
2215 latency = value1 + value2;
2217 err = GetStreamLatency(m_DeviceID, isInput, streamLatencies);
2218 if (err == kAudioHardwareNoError)
2220 for ( int i = 0; i < streamLatencies.size(); i++) {
2221 latency += streamLatencies[i];
2228 //**********************************************************************************************
2229 // WCMRCoreAudioDevice::GetStreamLatency
2231 //! Get stream latency for device.
2233 //! \param deviceID : The audio device ID.
2235 //! \param isInput : Return latency for the input if isInput is true, otherwise the output latency
2236 //! wiil be returned.
2237 //**********************************************************************************************
2238 OSStatus WCMRCoreAudioDevice::GetStreamLatency(AudioDeviceID device, bool isInput, std::vector<int>& latencies)
2240 OSStatus err = kAudioHardwareNoError;
2241 UInt32 outSize1, outSize2, outSize3;
2242 Boolean outWritable;
2244 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable);
2246 int stream_count = outSize1 / sizeof(UInt32);
2247 AudioStreamID streamIDs[stream_count];
2248 AudioBufferList bufferList[stream_count];
2249 UInt32 streamLatency;
2250 outSize2 = sizeof(UInt32);
2252 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs);
2254 DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreams err = " << err);
2258 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable);
2260 DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = " << err);
2264 for (int i = 0; i < stream_count; i++) {
2265 err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency);
2267 DEBUG_MSG("GetStreamLatencies kAudioStreamPropertyLatency err = " << err);
2270 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList);
2272 DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = " << err);
2275 latencies.push_back(streamLatency);
2282 //**********************************************************************************************
2283 // WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager
2285 //! The constructuor, we initialize PA, and build the device list.
2287 //! \param *pTheClient : The manager's client object (which receives notifications).
2288 //! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true.
2290 //! \return Nothing.
2292 //**********************************************************************************************
2293 WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient,
2294 eAudioDeviceFilter eCurAudioDeviceFilter, bool useMultithreading, bool bNocopy)
2295 : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter)
2296 , m_UseMultithreading (useMultithreading)
2297 , m_bNoCopyAudioBuffer(bNocopy)
2301 //first of all, tell HAL to use it's own run loop, not to wait for our runloop to do
2302 //it's dirty work...
2303 //Essentially, this makes the HAL on Snow Leopard behave like Leopard.
2304 //It's not yet (as of October 2009 documented), but the following discussion
2305 //has the information provided by Jeff Moore @ Apple:
2306 // http://lists.apple.com/archives/coreaudio-api/2009/Oct/msg00214.html
2308 // As per Jeff's suggestion, opened an Apple Bug on this - ID# 7364011
2310 CFRunLoopRef nullRunLoop = 0;
2311 OSStatus err = AudioHardwareSetProperty (kAudioHardwarePropertyRunLoop, sizeof(CFRunLoopRef), &nullRunLoop);
2313 if (err != kAudioHardwareNoError)
2315 syslog (LOG_NOTICE, "Unable to set RunLoop for Audio Hardware");
2318 //add a listener to find out when devices change...
2319 AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, DevicePropertyChangeCallback, this);
2321 //Always add the None device first...
2322 m_NoneDevice = new WCMRNativeAudioNoneDevice(this);
2324 //prepare our initial list...
2325 generateDeviceListImpl();
2332 //**********************************************************************************************
2333 // WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager
2335 //! It clears the device list, releasing each of the device.
2339 //! \return Nothing.
2341 //**********************************************************************************************
2342 WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager()
2348 delete m_NoneDevice;
2352 //destructors should absorb exceptions, no harm in logging though!!
2353 DEBUG_MSG ("Exception during destructor");
2359 WCMRAudioDevice* WCMRCoreAudioDeviceManager::initNewCurrentDeviceImpl(const std::string & deviceName)
2361 destroyCurrentDeviceImpl();
2363 std::cout << "API::PortAudioDeviceManager::initNewCurrentDevice " << deviceName << std::endl;
2364 if (deviceName == m_NoneDevice->DeviceName() )
2366 m_CurrentDevice = m_NoneDevice;
2367 return m_CurrentDevice;
2371 WTErr err = GetDeviceInfoByName(deviceName, devInfo);
2377 std::cout << "API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName << std::endl;
2378 TRACE_MSG ("API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName);
2380 m_CurrentDevice = new WCMRCoreAudioDevice (this, devInfo.m_DeviceId, m_UseMultithreading, m_bNoCopyAudioBuffer);
2384 std::cout << "Unabled to create PA Device: " << devInfo.m_DeviceId << std::endl;
2385 DEBUG_MSG ("Unabled to create PA Device: " << devInfo.m_DeviceId);
2389 return m_CurrentDevice;
2393 void WCMRCoreAudioDeviceManager::destroyCurrentDeviceImpl()
2395 if (m_CurrentDevice != m_NoneDevice)
2396 delete m_CurrentDevice;
2398 m_CurrentDevice = 0;
2402 WTErr WCMRCoreAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceId, std::vector<int>& sampleRates)
2406 WTErr retVal = eNoErr;
2407 OSStatus err = kAudioHardwareNoError;
2408 UInt32 propSize = 0;
2410 sampleRates.clear();
2412 //! 1. Get sample rate property size.
2413 err = AudioDeviceGetPropertyInfo(deviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL);
2414 if (err == kAudioHardwareNoError)
2416 //! 2. Get property: cannels output.
2418 // Allocate size accrding to the number of audio values
2419 int numRates = propSize / sizeof(AudioValueRange);
2420 AudioValueRange* supportedRates = new AudioValueRange[numRates];
2422 // Get sampling rates from Audio device
2423 err = AudioDeviceGetProperty(deviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates);
2424 if (err == kAudioHardwareNoError)
2426 //! 3. Update sample rates
2428 // now iterate through our standard SRs
2429 for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++)
2431 //check to see if our SR is in the supported rates...
2432 for (int deviceSR = 0; deviceSR < numRates; deviceSR++)
2434 if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) &&
2435 (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR]))
2437 sampleRates.push_back ((int)gAllSampleRates[ourSR]);
2445 retVal = eCoreAudioFailed;
2446 DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str());
2449 delete [] supportedRates;
2453 retVal = eCoreAudioFailed;
2454 DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str());
2461 WTErr WCMRCoreAudioDeviceManager::getDeviceMaxInputChannels(DeviceID deviceId, unsigned int& inputChannels)
2464 WTErr retVal = eNoErr;
2465 OSStatus err = kAudioHardwareNoError;
2466 UInt32 propSize = 0;
2469 // 1. Get property cannels input size.
2470 err = AudioDeviceGetPropertyInfo (deviceId, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
2471 if (err == kAudioHardwareNoError)
2473 //! 2. Get property: cannels input.
2475 // Allocate size according to the property size. Note that this is a variable sized struct...
2476 AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
2480 memset (pStreamBuffers, 0, propSize);
2482 // Get the Input channels
2483 err = AudioDeviceGetProperty (deviceId, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
2484 if (err == kAudioHardwareNoError)
2486 // Calculate the number of input channels
2487 for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
2489 inputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
2494 retVal = eCoreAudioFailed;
2495 DEBUG_MSG("Failed to get device Input channels. Device Name: " << m_DeviceName.c_str());
2498 free (pStreamBuffers);
2502 retVal = eMemOutOfMemory;
2503 DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
2508 retVal = eCoreAudioFailed;
2509 DEBUG_MSG("Failed to get device Input channels property size. Device Name: " << m_DeviceName.c_str());
2516 WTErr WCMRCoreAudioDeviceManager::getDeviceMaxOutputChannels(DeviceID deviceId, unsigned int& outputChannels)
2520 WTErr retVal = eNoErr;
2521 OSStatus err = kAudioHardwareNoError;
2522 UInt32 propSize = 0;
2525 //! 1. Get property cannels output size.
2526 err = AudioDeviceGetPropertyInfo (deviceId, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
2527 if (err == kAudioHardwareNoError)
2529 //! 2. Get property: cannels output.
2531 // Allocate size according to the property size. Note that this is a variable sized struct...
2532 AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
2535 memset (pStreamBuffers, 0, propSize);
2537 // Get the Output channels
2538 err = AudioDeviceGetProperty (deviceId, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
2539 if (err == kAudioHardwareNoError)
2541 // Calculate the number of output channels
2542 for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
2544 outputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
2549 retVal = eCoreAudioFailed;
2550 DEBUG_MSG("Failed to get device Output channels. Device Name: " << m_DeviceName.c_str());
2552 free (pStreamBuffers);
2556 retVal = eMemOutOfMemory;
2557 DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
2562 retVal = eCoreAudioFailed;
2563 DEBUG_MSG("Failed to get device Output channels property size. Device Name: " << m_DeviceName.c_str());
2570 WTErr WCMRCoreAudioDeviceManager::generateDeviceListImpl()
2574 // lock the list first
2575 wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
2576 m_DeviceInfoVec.clear();
2578 //First, get info from None device which is always present
2581 DeviceInfo *pDevInfo = new DeviceInfo(NONE_DEVICE_ID, m_NoneDevice->DeviceName() );
2582 pDevInfo->m_AvailableSampleRates = m_NoneDevice->SamplingRates();
2583 m_DeviceInfoVec.push_back(pDevInfo);
2586 WTErr retVal = eNoErr;
2587 OSStatus osErr = noErr;
2588 AudioDeviceID* deviceIDs = 0;
2590 openlog("WCMRCoreAudioDeviceManager", LOG_PID | LOG_CONS, LOG_USER);
2594 //Get device count...
2595 UInt32 propSize = 0;
2596 osErr = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &propSize, NULL);
2597 ASSERT_ERROR(osErr, "AudioHardwareGetProperty 1");
2598 if (WUIsError(osErr))
2601 size_t numDevices = propSize / sizeof (AudioDeviceID);
2602 deviceIDs = new AudioDeviceID[numDevices];
2604 //retrieve the device IDs
2605 propSize = numDevices * sizeof (AudioDeviceID);
2606 osErr = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &propSize, deviceIDs);
2607 ASSERT_ERROR(osErr, "Error while getting audio devices: AudioHardwareGetProperty 2");
2608 if (WUIsError(osErr))
2611 //now add the ones that are not there...
2612 for (size_t deviceIndex = 0; deviceIndex < numDevices; deviceIndex++)
2614 DeviceInfo* pDevInfo = 0;
2616 //Get device name and create new DeviceInfo entry
2617 //Get property name size.
2618 osErr = AudioDeviceGetPropertyInfo(deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL);
2619 if (osErr == kAudioHardwareNoError)
2621 //Get property: name.
2622 char* deviceName = new char[propSize];
2623 osErr = AudioDeviceGetProperty(deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName);
2624 if (osErr == kAudioHardwareNoError)
2626 pDevInfo = new DeviceInfo(deviceIDs[deviceIndex], deviceName);
2630 retVal = eCoreAudioFailed;
2631 DEBUG_MSG("Failed to get device name. Device ID: " << m_DeviceID);
2634 delete [] deviceName;
2638 retVal = eCoreAudioFailed;
2639 DEBUG_MSG("Failed to get device name property size. Device ID: " << m_DeviceID);
2644 //Retrieve all the information we need for the device
2645 WTErr wErr = eNoErr;
2647 //Get available sample rates for the device
2648 std::vector<int> availableSampleRates;
2649 wErr = getDeviceAvailableSampleRates(pDevInfo->m_DeviceId, availableSampleRates);
2653 DEBUG_MSG ("Failed to get device available sample rates. Device ID: " << m_DeviceID);
2655 continue; //proceed to the next device
2658 pDevInfo->m_AvailableSampleRates = availableSampleRates;
2660 //Get max input channels
2661 uint32_t maxInputChannels;
2662 wErr = getDeviceMaxInputChannels(pDevInfo->m_DeviceId, maxInputChannels);
2666 DEBUG_MSG ("Failed to get device max input channels count. Device ID: " << m_DeviceID);
2668 continue; //proceed to the next device
2671 pDevInfo->m_MaxInputChannels = maxInputChannels;
2673 //Get max output channels
2674 uint32_t maxOutputChannels;
2675 wErr = getDeviceMaxOutputChannels(pDevInfo->m_DeviceId, maxOutputChannels);
2679 DEBUG_MSG ("Failed to get device max output channels count. Device ID: " << m_DeviceID);
2681 continue; //proceed to the next device
2684 pDevInfo->m_MaxOutputChannels = maxOutputChannels;
2686 //Now check if this device is acceptable according to current input/output settings
2687 bool bRejectDevice = false;
2688 switch(m_eAudioDeviceFilter)
2690 case eInputOnlyDevices:
2691 if (pDevInfo->m_MaxInputChannels != 0)
2693 m_DeviceInfoVec.push_back(pDevInfo);
2697 // Delete unnecesarry device
2698 bRejectDevice = true;
2701 case eOutputOnlyDevices:
2702 if (pDevInfo->m_MaxOutputChannels != 0)
2704 m_DeviceInfoVec.push_back(pDevInfo);
2708 // Delete unnecesarry device
2709 bRejectDevice = true;
2712 case eFullDuplexDevices:
2713 if (pDevInfo->m_MaxInputChannels != 0 && pDevInfo->m_MaxOutputChannels != 0)
2715 m_DeviceInfoVec.push_back(pDevInfo);
2719 // Delete unnecesarry device
2720 bRejectDevice = true;
2725 m_DeviceInfoVec.push_back(pDevInfo);
2731 syslog (LOG_NOTICE, "%s rejected, In Channels = %d, Out Channels = %d\n",
2732 pDevInfo->m_DeviceName.c_str(), pDevInfo->m_MaxInputChannels, pDevInfo->m_MaxOutputChannels);
2733 // In case of Input and Output both channels being Zero, we will release memory; since we created CoreAudioDevice but we are Not adding it in list.
2740 //If no devices were found, that's not a good thing!
2741 if (m_DeviceInfoVec.empty())
2743 DEBUG_MSG ("No matching CoreAudio devices were found\n");
2748 if (WUNoError(retVal))
2749 retVal = eCoreAudioFailed;
2759 WTErr WCMRCoreAudioDeviceManager::updateDeviceListImpl()
2761 wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
2762 WTErr err = generateDeviceListImpl();
2766 std::cout << "API::PortAudioDeviceManager::updateDeviceListImpl: Device list update error: "<< err << std::endl;
2770 if (m_CurrentDevice)
2772 // if we have device initialized we should find out if this device is still connected
2774 WTErr deviceLookUpErr = GetDeviceInfoByName(m_CurrentDevice->DeviceName(), devInfo );
2776 if (eNoErr != deviceLookUpErr)
2778 NotifyClient (WCMRAudioDeviceManagerClient::IODeviceDisconnected);
2783 NotifyClient (WCMRAudioDeviceManagerClient::DeviceListChanged);
2789 WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& bufferSizes) const
2793 WTErr retVal = eNoErr;
2794 OSStatus err = kAudioHardwareNoError;
2795 UInt32 propSize = 0;
2797 bufferSizes.clear();
2799 //first check if the request has been made for None device
2800 if (deviceName == m_NoneDevice->DeviceName() )
2802 bufferSizes = m_NoneDevice->BufferSizes();
2807 retVal = GetDeviceInfoByName(deviceName, devInfo);
2809 if (eNoErr == retVal)
2811 // 1. Get buffer size range
2812 AudioValueRange bufferSizesRange;
2813 propSize = sizeof (AudioValueRange);
2814 err = AudioDeviceGetProperty (devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &bufferSizesRange);
2815 if(err == kAudioHardwareNoError)
2817 // 2. Run on all ranges and add them to the list
2818 for(int bsize=0; gAllBufferSizes[bsize] > 0; bsize++)
2820 if ((bufferSizesRange.mMinimum <= gAllBufferSizes[bsize]) && (bufferSizesRange.mMaximum >= gAllBufferSizes[bsize]))
2822 bufferSizes.push_back (gAllBufferSizes[bsize]);
2826 //if we didn't get a single hit, let's simply add the min. and the max...
2827 if (bufferSizes.empty())
2829 bufferSizes.push_back ((int)bufferSizesRange.mMinimum);
2830 bufferSizes.push_back ((int)bufferSizesRange.mMaximum);
2835 retVal = eCoreAudioFailed;
2836 DEBUG_MSG("Failed to get device buffer sizes range. Device Name: " << m_DeviceName.c_str());
2841 retVal = eRMResNotFound;
2842 std::cout << "API::PortAudioDeviceManager::GetBufferSizes: Device not found: "<< deviceName << std::endl;
2850 OSStatus WCMRCoreAudioDeviceManager::DevicePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData)
2852 switch (inPropertyID)
2854 case kAudioHardwarePropertyDevices:
2856 WCMRCoreAudioDeviceManager* pManager = (WCMRCoreAudioDeviceManager*)inClientData;
2858 pManager->updateDeviceListImpl();