-/*
- Copyright (C) 2013 Waves Audio Ltd.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
//----------------------------------------------------------------------------------
//
+// Copyright (c) 2008 Waves Audio Ltd. All rights reserved.
//
//! \file WCMRCoreAudioDeviceManager.cpp
//!
///< Supported Sample rates
static const double gAllSampleRates[] =
{
- 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */
+ 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, -1 /* negative terminated list */
};
///< The default buffer size.
static const int DEFAULT_BUFFERSIZE = 128;
+static const int NONE_DEVICE_ID = -1;
+
///< Number of stalls to wait before notifying user...
static const int NUM_STALLS_FOR_NOTIFICATION = 2 * 50; // 2*50 corresponds to 2 * 50 x 42 ms idle timer - about 4 seconds.
static const int CHANGE_CHECK_COUNTER_PERIOD = 100; // 120 corresponds to 120 x 42 ms idle timer - about 4 seconds.
m_CurrentBufferSize = (int)bufferSize;
- UpdateDeviceInfo(true /*updateSRSupported*/, true /* updateBufferSizes */);
+ UpdateDeviceInfo();
//should use a valid current SR...
if (m_SamplingRates.size())
// WCMRCoreAudioDevice::UpdateDeviceInfo
//
//! Updates Device Information about channels, sampling rates, buffer sizes.
-//!
-//! \param updateSRSupported : Is Sampling Rate support needs to be updated.
-//! \param updateBufferSizes : Is buffer size support needs to be updated.
//!
//! \return WTErr.
//!
//**********************************************************************************************
-WTErr WCMRCoreAudioDevice::UpdateDeviceInfo (bool updateSRSupported, bool updateBufferSizes)
+WTErr WCMRCoreAudioDevice::UpdateDeviceInfo ()
{
AUTO_FUNC_DEBUG;
WTErr errSR = eNoErr;
WTErr errBS = eNoErr;
- if (updateSRSupported)
- {
- errSR = UpdateDeviceSampleRates();
- }
-
- //update SR list... This is done conditionally, because some devices may not like
- //changing the SR later on, just to check on things.
- if (updateBufferSizes)
- {
- errBS = UpdateDeviceBufferSizes();
- }
+ errSR = UpdateDeviceSampleRates();
+ errBS = UpdateDeviceBufferSizes();
if(errName != eNoErr || errIn != eNoErr || errOut != eNoErr || errSR != eNoErr || errBS != eNoErr)
{
memset (pStreamBuffers, 0, propSize);
// Get the Input channels
- err = AudioDeviceGetProperty (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
+ err = AudioDeviceGetProperty (m_DeviceID, 0, true/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
if (err == kAudioHardwareNoError)
{
// Calculate the number of input channels
// Update input channels
m_InputChannels.clear();
+
for (int channel = 0; channel < maxInputChannels; channel++)
{
+ CFStringRef cfName;
std::stringstream chNameStream;
- //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
- chNameStream << "Input " << (channel+1);
+ UInt32 nameSize = 0;
+ OSStatus error = kAudioHardwareNoError;
+
+ error = AudioDeviceGetPropertyInfo (m_DeviceID,
+ channel + 1,
+ true /* Input */,
+ kAudioDevicePropertyChannelNameCFString,
+ &nameSize,
+ NULL);
+
+ if (error == kAudioHardwareNoError)
+ {
+ error = AudioDeviceGetProperty (m_DeviceID,
+ channel + 1,
+ true /* Input */,
+ kAudioDevicePropertyChannelNameCFString,
+ &nameSize,
+ &cfName);
+ }
+
+ bool decoded = false;
+ char* cstr_name = 0;
+ if (error == kAudioHardwareNoError)
+ {
+ CFIndex length = CFStringGetLength(cfName);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ cstr_name = new char[maxSize];
+ decoded = CFStringGetCString(cfName, cstr_name, maxSize, kCFStringEncodingUTF8);
+ }
+
+ chNameStream << (channel+1) << " - ";
+
+ if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
+ chNameStream << cstr_name;
+ }
+ else
+ {
+ chNameStream << "Input " << (channel+1);
+ }
+
m_InputChannels.push_back (chNameStream.str());
+
+ delete [] cstr_name;
}
return retVal;
m_OutputChannels.clear();
for (int channel = 0; channel < maxOutputChannels; channel++)
{
+ CFStringRef cfName;
std::stringstream chNameStream;
- //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces
- chNameStream << "Output " << (channel+1);
+ UInt32 nameSize = 0;
+ OSStatus error = kAudioHardwareNoError;
+
+ error = AudioDeviceGetPropertyInfo (m_DeviceID,
+ channel + 1,
+ false /* Output */,
+ kAudioDevicePropertyChannelNameCFString,
+ &nameSize,
+ NULL);
+
+ if (error == kAudioHardwareNoError)
+ {
+ error = AudioDeviceGetProperty (m_DeviceID,
+ channel + 1,
+ false /* Output */,
+ kAudioDevicePropertyChannelNameCFString,
+ &nameSize,
+ &cfName);
+ }
+
+ bool decoded = false;
+ char* cstr_name = 0;
+ if (error == kAudioHardwareNoError )
+ {
+ CFIndex length = CFStringGetLength(cfName);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ cstr_name = new char[maxSize];
+ decoded = CFStringGetCString(cfName, cstr_name, maxSize, kCFStringEncodingUTF8);
+ }
+
+ chNameStream << (channel+1) << " - ";
+
+ if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
+ chNameStream << cstr_name;
+ }
+ else
+ {
+ chNameStream << "Output " << (channel+1);
+ }
+
m_OutputChannels.push_back (chNameStream.str());
+
+ delete [] cstr_name;
}
return retVal;
retVal = SetAndCheckCurrentSamplingRate (newRate);
if(retVal == eNoErr)
{
- retVal = UpdateDeviceInfo (false/*updateSRSupported*/, true/*updateBufferSizes*/);
+ retVal = UpdateDeviceInfo ();
}
//reactivate it.
goto Exit;
}
-#if ENABLE_DEVICE_CHANGE_LISTNER
+#if ENABLE_DEVICE_CHANGE_LISTNER
{
//listner for device change...
- err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
- StaticPropertyChangeProc, this);
+ err = AudioDeviceAddPropertyListener (m_DeviceID,
+ kAudioPropertyWildcardChannel,
+ true,
+ kAudioDevicePropertyDeviceHasChanged,
+ StaticPropertyChangeProc,
+ this);
+
if (err)
{
DEBUG_MSG("Couldn't Setup device change Property Listner, error = " << err);
#if ENABLE_DEVICE_CHANGE_LISTNER
{
- err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
- StaticPropertyChangeProc);
-
+ err = AudioDeviceRemovePropertyListener (m_DeviceID,
+ kAudioPropertyWildcardChannel,
+ true/* Input */,
+ kAudioDevicePropertyDeviceHasChanged,
+ StaticPropertyChangeProc);
+
if (err)
{
- DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err);
+ DEBUG_MSG("Couldn't Cleanup device input stream change Property Listner, error = " << err);
//not sure if we need to report this...
}
+
}
#endif //ENABLE_DEVICE_CHANGE_LISTNER
case kAudioDevicePropertyDeviceHasChanged:
{
m_ResetRequested++;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
}
break;
#endif //ENABLE_DEVICE_CHANGE_LISTNER
case kAudioDeviceProcessorOverload:
+ {
if (m_IgnoreThisDrop)
m_IgnoreThisDrop = false; //We'll ignore once, just once!
else
m_DropsDetected++;
+ m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::Dropout );
break;
+ }
default:
break;
}
retVal = EnableListeners();
if (retVal != eNoErr)
goto Exit;
-
- //also prepare the buffer list for input...
- if (!m_InputChannels.empty())
- {
-
- //now setup the buffer list.
- memset (&m_InputAudioBufferList, 0, sizeof (m_InputAudioBufferList));
- m_InputAudioBufferList.mNumberBuffers = 1;
- m_InputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size();
- m_InputAudioBufferList.mBuffers[0].mDataByteSize = m_InputAudioBufferList.mBuffers[0].mNumberChannels *
- m_CurrentBufferSize * sizeof(float);
- //allocate the data buffer...
- try
- {
- m_pInputData = new float[m_InputAudioBufferList.mBuffers[0].mNumberChannels * m_CurrentBufferSize];
- }
- catch (...)
- {
- retVal = eMemNewFailed;
- goto Exit;
- }
-
- m_InputAudioBufferList.mBuffers[0].mData = m_pInputData;
-
- //zero it out...
- memset (m_InputAudioBufferList.mBuffers[0].mData, 0, m_InputAudioBufferList.mBuffers[0].mDataByteSize);
- }
-
//initialize the audio-unit now!
err = AudioUnitInitialize(m_AUHALAudioUnit);
if (err != kAudioHardwareNoError)
CloseComponent(m_AUHALAudioUnit);
m_AUHALAudioUnit = NULL;
}
-
- safe_delete_array(m_pInputData);
return retVal;
}
if (newState)
{
-
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL.");
retVal = SetupAUHAL();
m_DropsReported = 0;
m_IgnoreThisDrop = true;
- UpdateDeviceInfo(true /*updateSRSupported */, true /* updateBufferSizes#*/);
+ UpdateDeviceInfo();
}
SetupToneGenerator ();
#endif //WV_USE_TONE_GEN
- m_StopRequested = false;
m_SampleCountAtLastIdle = 0;
m_StalledSampleCounter = 0;
m_SampleCounter = 0;
err = AudioOutputUnitStart (m_AUHALAudioUnit);
+ m_StopRequested = false;
+
if(err)
{
DEBUG_MSG( "Failed to start AudioUnit, err " << err );
err = AudioOutputUnitStop (m_AUHALAudioUnit);
if (!err)
{
- if (!m_InputChannels.empty());
+ //if (!m_InputChannels.empty());
{
err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT);
}
- if (!m_OutputChannels.empty());
+ //if (!m_OutputChannels.empty());
{
err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT);
}
//**********************************************************************************************
WTErr WCMRCoreAudioDevice::DoIdle ()
{
+ /*
if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported)
{
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged);
m_StalledSampleCounter = 0;
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount);
}
- }
+ }*/
return (eNoErr);
{
LSOpenFSRef(&theAppFSRef, NULL);
}
+ else
+ {
+ // open default AudioMIDISetup if device app is not found
+ CFStringRef audiMidiSetupApp = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
+ theError = LSFindApplicationForInfo(kLSUnknownCreator, audiMidiSetupApp, NULL, &theAppFSRef, NULL);
+
+ if (!theError)
+ {
+ LSOpenFSRef(&theAppFSRef, NULL);
+ }
+ }
+
CFRelease (configAP);
}
OSStatus retVal = 0;
if (m_StopRequested)
- goto Exit;
+ return retVal;
if (m_IOProcThreadPort == 0)
m_IOProcThreadPort = mach_thread_self ();
//cannot really deal with it unless the number of frames are the same as our buffer size!
if (inNumberFrames != (UInt32)m_CurrentBufferSize)
- goto Exit;
+ return retVal;
//Retrieve the input data...
if (!m_InputChannels.empty())
{
- retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &m_InputAudioBufferList);
+ UInt32 expectedDataSize = m_InputChannels.size() * m_CurrentBufferSize * sizeof(float);
+ AudioBufferList inputAudioBufferList;
+ inputAudioBufferList.mNumberBuffers = 1;
+ inputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size();
+ inputAudioBufferList.mBuffers[0].mDataByteSize = expectedDataSize;
+ inputAudioBufferList.mBuffers[0].mData = NULL;//new float[expectedDataSize]; // we are going to get buffer from CoreAudio
+
+ retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &inputAudioBufferList);
+
+ if (retVal == kAudioHardwareNoError &&
+ inputAudioBufferList.mBuffers[0].mNumberChannels == m_InputChannels.size() &&
+ inputAudioBufferList.mBuffers[0].mDataByteSize == expectedDataSize )
+ {
+ m_pInputData = (float*)inputAudioBufferList.mBuffers[0].mData;
+ }
+ else
+ {
+ m_pInputData = NULL;
+ return retVal;
+ }
}
//is this an input only device?
else if ((!m_OutputChannels.empty()) && (ioData->mBuffers[0].mNumberChannels == m_OutputChannels.size()))
AudioCallback ((float *)ioData->mBuffers[0].mData, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime);
-Exit:
return retVal;
}
//! \return Nothing.
//!
//**********************************************************************************************
-WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient, eAudioDeviceFilter eCurAudioDeviceFilter
- , bool useMultithreading, eCABS_Method eCABS_method, bool bNocopy)
- : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter
- )
- , m_UpdateDeviceListRequested(0)
- , m_UpdateDeviceListProcessed(0)
+WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient,
+ eAudioDeviceFilter eCurAudioDeviceFilter, bool useMultithreading, bool bNocopy)
+ : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter)
, m_UseMultithreading (useMultithreading)
- , m_eCABS_Method(eCABS_method)
, m_bNoCopyAudioBuffer(bNocopy)
{
AUTO_FUNC_DEBUG;
}
//add a listener to find out when devices change...
- AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, StaticPropertyChangeProc, this);
-
+ AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, HardwarePropertyChangeCallback, this);
+
//Always add the None device first...
- m_Devices.push_back (new WCMRNativeAudioNoneDevice(this));
+ m_NoneDevice = new WCMRNativeAudioNoneDevice(this);
//prepare our initial list...
- UpdateDeviceList_Private();
+ generateDeviceListImpl();
return;
}
try
{
- AudioHardwareRemovePropertyListener (kAudioHardwarePropertyDevices, StaticPropertyChangeProc);
-
- //Note: We purposely release the device list here, instead of
- //depending on the superclass to do it, as by the time the superclass'
- //destructor executes, we will have called Pa_Terminate()!
-
- //Need to call release on our devices, and erase them from list
- std::vector<WCMRAudioDevice*>::iterator deviceIter;
- while (m_Devices.size())
- {
- WCMRAudioDevice *pDeviceToRelease = m_Devices.back();
- m_Devices.pop_back();
-
- SAFE_RELEASE (pDeviceToRelease);
- }
-
-
- //The derived classes may want to do additional de-int!
-
+ delete m_NoneDevice;
}
catch (...)
{
}
-//**********************************************************************************************
-// WCMRCoreAudioDeviceManager::StaticPropertyChangeProc
-//
-//! The property change listener for the Audio Device Manager. It calls upon (non-static) PropertyChangeProc
-//! to do the actual work.
-//!
-//! \param iPropertyID : the property that has changed.
-//! \param inClientData : What was supplied at init time.
-//!
-//! \return if parameters are incorrect, or the value returned by PropertyChangeProc.
-//!
-//**********************************************************************************************
-OSStatus WCMRCoreAudioDeviceManager::StaticPropertyChangeProc (AudioHardwarePropertyID inPropertyID, void* inClientData)
+WCMRAudioDevice* WCMRCoreAudioDeviceManager::initNewCurrentDeviceImpl(const std::string & deviceName)
{
- WCMRCoreAudioDeviceManager *pMyManager = (WCMRCoreAudioDeviceManager *)inClientData;
-
- if (pMyManager)
- return pMyManager->PropertyChangeProc (inPropertyID);
-
- return 0;
+ destroyCurrentDeviceImpl();
+
+ std::cout << "API::PortAudioDeviceManager::initNewCurrentDevice " << deviceName << std::endl;
+ if (deviceName == m_NoneDevice->DeviceName() )
+ {
+ m_CurrentDevice = m_NoneDevice;
+ return m_CurrentDevice;
+ }
+
+ DeviceInfo devInfo;
+ WTErr err = GetDeviceInfoByName(deviceName, devInfo);
+
+ if (eNoErr == err)
+ {
+ try
+ {
+ std::cout << "API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName << std::endl;
+ TRACE_MSG ("API::PortAudioDeviceManager::Creating PA device: " << devInfo.m_DeviceId << ", Device Name: " << devInfo.m_DeviceName);
+
+ m_CurrentDevice = new WCMRCoreAudioDevice (this, devInfo.m_DeviceId, m_UseMultithreading, m_bNoCopyAudioBuffer);
+ }
+ catch (...)
+ {
+ std::cout << "Unabled to create PA Device: " << devInfo.m_DeviceId << std::endl;
+ DEBUG_MSG ("Unabled to create PA Device: " << devInfo.m_DeviceId);
+ }
+ }
+
+ return m_CurrentDevice;
}
-
-//**********************************************************************************************
-// WCMRCoreAudioDeviceManager::PropertyChangeProc
-//
-//! The property change listener for the Audio Device Manager. Currently we only listen for the
-//! device list change (device arrival/removal, and accordingly cause an update to the device list.
-//! Note that the actual update happens from the DoIdle() call to prevent multi-threading related issues.
-//!
-//! \param
-//!
-//! \return Nothing.
-//!
-//**********************************************************************************************
-OSStatus WCMRCoreAudioDeviceManager::PropertyChangeProc (AudioHardwarePropertyID inPropertyID)
+void WCMRCoreAudioDeviceManager::destroyCurrentDeviceImpl()
{
- OSStatus retVal = 0;
- switch (inPropertyID)
+ if (m_CurrentDevice != m_NoneDevice)
+ delete m_CurrentDevice;
+
+ m_CurrentDevice = 0;
+}
+
+
+WTErr WCMRCoreAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceId, std::vector<int>& sampleRates)
+{
+ AUTO_FUNC_DEBUG;
+
+ WTErr retVal = eNoErr;
+ OSStatus err = kAudioHardwareNoError;
+ UInt32 propSize = 0;
+
+ sampleRates.clear();
+
+ //! 1. Get sample rate property size.
+ err = AudioDeviceGetPropertyInfo(deviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL);
+ if (err == kAudioHardwareNoError)
{
- case kAudioHardwarePropertyDevices:
- m_UpdateDeviceListRequested++;
- break;
- default:
- break;
+ //! 2. Get property: cannels output.
+
+ // Allocate size according to the number of audio values
+ int numRates = propSize / sizeof(AudioValueRange);
+ AudioValueRange* supportedRates = new AudioValueRange[numRates];
+
+ // Get sampling rates from Audio device
+ err = AudioDeviceGetProperty(deviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates);
+ if (err == kAudioHardwareNoError)
+ {
+ //! 3. Update sample rates
+
+ // now iterate through our standard SRs
+ for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++)
+ {
+ //check to see if our SR is in the supported rates...
+ for (int deviceSR = 0; deviceSR < numRates; deviceSR++)
+ {
+ if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) &&
+ (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR]))
+ {
+ sampleRates.push_back ((int)gAllSampleRates[ourSR]);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str());
+ }
+
+ delete [] supportedRates;
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str());
}
return retVal;
}
-
-//**********************************************************************************************
-// WCMRCoreAudioDeviceManager::remove_pattern
-//
-//! remove a substring from a given string
-//!
-//! \param original_str - original string
-//! \param pattern_str - pattern to find
-//! \param return_str - the return string - without the pattern substring
-//!
-//! \return Nothing.
-//!
-//**********************************************************************************************
-void WCMRCoreAudioDeviceManager::remove_pattern(const std::string& original_str, const std::string& pattern_str, std::string& return_str)
-{
- char *orig_c_str = new char[original_str.size() + 1];
- char* strSavePtr;
- strcpy(orig_c_str, original_str.c_str());
- char *p_splited_orig_str = strtok_r(orig_c_str," ", &strSavePtr);
- std::ostringstream stream_str;
- while (p_splited_orig_str != 0)
+
+WTErr WCMRCoreAudioDeviceManager::getDeviceMaxInputChannels(DeviceID deviceId, unsigned int& inputChannels)
+{
+ AUTO_FUNC_DEBUG;
+ WTErr retVal = eNoErr;
+ OSStatus err = kAudioHardwareNoError;
+ UInt32 propSize = 0;
+ inputChannels = 0;
+
+ // 1. Get property cannels input size.
+ err = AudioDeviceGetPropertyInfo (deviceId, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
+ if (err == kAudioHardwareNoError)
+ {
+ //! 2. Get property: cannels input.
+
+ // Allocate size according to the property size. Note that this is a variable sized struct...
+ AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
+
+ if (pStreamBuffers)
+ {
+ memset (pStreamBuffers, 0, propSize);
+
+ // Get the Input channels
+ err = AudioDeviceGetProperty (deviceId, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
+ if (err == kAudioHardwareNoError)
+ {
+ // Calculate the number of input channels
+ for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
+ {
+ inputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
+ }
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Input channels. Device Name: " << m_DeviceName.c_str());
+ }
+
+ free (pStreamBuffers);
+ }
+ else
+ {
+ retVal = eMemOutOfMemory;
+ DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
+ }
+ }
+ else
{
- int cmp_res = strcmp(p_splited_orig_str, pattern_str.c_str()); // might need Ignore case ( stricmp OR strcasecmp)
- if ( cmp_res != 0)
- stream_str << p_splited_orig_str << " ";
- p_splited_orig_str = strtok_r(NULL," ", &strSavePtr);
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Input channels property size. Device Name: " << m_DeviceName.c_str());
}
- delete[] orig_c_str;
- return_str = stream_str.str();
+
+ return retVal;
}
+
+WTErr WCMRCoreAudioDeviceManager::getDeviceMaxOutputChannels(DeviceID deviceId, unsigned int& outputChannels)
+{
+ AUTO_FUNC_DEBUG;
+
+ WTErr retVal = eNoErr;
+ OSStatus err = kAudioHardwareNoError;
+ UInt32 propSize = 0;
+ outputChannels = 0;
-//**********************************************************************************************
-// WCMRCoreAudioDeviceManager::UpdateDeviceList_Private
-//
-//! Updates the list of devices maintained by the manager. If devices have gone away, they are removed
-//! if new devices have been connected, they are added to the list.
-//!
-//! \param none
-//!
-//! \return eNoErr on success, an error code on failure.
-//!
-//**********************************************************************************************
-WTErr WCMRCoreAudioDeviceManager::UpdateDeviceList_Private()
+ //! 1. Get property cannels output size.
+ err = AudioDeviceGetPropertyInfo (deviceId, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
+ if (err == kAudioHardwareNoError)
+ {
+ //! 2. Get property: cannels output.
+
+ // Allocate size according to the property size. Note that this is a variable sized struct...
+ AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize);
+ if (pStreamBuffers)
+ {
+ memset (pStreamBuffers, 0, propSize);
+
+ // Get the Output channels
+ err = AudioDeviceGetProperty (deviceId, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers);
+ if (err == kAudioHardwareNoError)
+ {
+ // Calculate the number of output channels
+ for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++)
+ {
+ outputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels;
+ }
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Output channels. Device Name: " << m_DeviceName.c_str());
+ }
+ free (pStreamBuffers);
+ }
+ else
+ {
+ retVal = eMemOutOfMemory;
+ DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str());
+ }
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Output channels property size. Device Name: " << m_DeviceName.c_str());
+ }
+
+ return retVal;
+}
+
+
+WTErr WCMRCoreAudioDeviceManager::generateDeviceListImpl()
{
AUTO_FUNC_DEBUG;
+ // lock the list first
+ wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
+ m_DeviceInfoVec.clear();
+
+ //First, get info from None device which is always present
+ if (m_NoneDevice)
+ {
+ DeviceInfo *pDevInfo = new DeviceInfo(NONE_DEVICE_ID, m_NoneDevice->DeviceName() );
+ pDevInfo->m_AvailableSampleRates = m_NoneDevice->SamplingRates();
+ m_DeviceInfoVec.push_back(pDevInfo);
+ }
WTErr retVal = eNoErr;
OSStatus osErr = noErr;
AudioDeviceID* deviceIDs = 0;
- size_t reportedDeviceIndex = 0;
openlog("WCMRCoreAudioDeviceManager", LOG_PID | LOG_CONS, LOG_USER);
-
- try
- {
-
- // Define 2 vectors for input and output - only for eMatchedDuplexDevices case
- WCMRAudioDeviceList adOnlyIn;
- WCMRAudioDeviceList adOnlyOut;
+ try
+ {
//Get device count...
UInt32 propSize = 0;
osErr = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &propSize, NULL);
ASSERT_ERROR(osErr, "AudioHardwareGetProperty 1");
if (WUIsError(osErr))
throw osErr;
-
+
size_t numDevices = propSize / sizeof (AudioDeviceID);
deviceIDs = new AudioDeviceID[numDevices];
-
+
//retrieve the device IDs
propSize = numDevices * sizeof (AudioDeviceID);
osErr = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &propSize, deviceIDs);
ASSERT_ERROR(osErr, "Error while getting audio devices: AudioHardwareGetProperty 2");
if (WUIsError(osErr))
throw osErr;
-
- //first go through our list of devices, remove the ones that are no longer present...
- std::vector<WCMRAudioDevice*>::iterator deviceIter;
- for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); /*This is purposefully blank*/)
+
+ //now add the ones that are not there...
+ for (size_t deviceIndex = 0; deviceIndex < numDevices; deviceIndex++)
{
- WCMRCoreAudioDevice *pDeviceToWorkUpon = dynamic_cast<WCMRCoreAudioDevice *>(*deviceIter);
-
- //it's possible that the device is actually not a core audio device - perhaps a none device...
- if (!pDeviceToWorkUpon)
- {
- deviceIter++;
- continue;
- }
-
- AudioDeviceID myDeviceID = pDeviceToWorkUpon->DeviceID();
- bool deviceFound = false;
- for (reportedDeviceIndex = 0; reportedDeviceIndex < numDevices; reportedDeviceIndex++)
+ DeviceInfo* pDevInfo = 0;
+
+ //Get device name and create new DeviceInfo entry
+ //Get property name size.
+ osErr = AudioDeviceGetPropertyInfo(deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL);
+ if (osErr == kAudioHardwareNoError)
{
- if (myDeviceID == deviceIDs[reportedDeviceIndex])
+ //Get property: name.
+ char* deviceName = new char[propSize];
+ osErr = AudioDeviceGetProperty(deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName);
+ if (osErr == kAudioHardwareNoError)
{
- deviceFound = true;
- break;
+ pDevInfo = new DeviceInfo(deviceIDs[deviceIndex], deviceName);
}
- }
-
- if (!deviceFound)
- {
- //it's no longer there, need to remove it!
- WCMRAudioDevice *pTheDeviceToErase = *deviceIter;
- deviceIter = m_Devices.erase (deviceIter);
- if (pTheDeviceToErase->Active())
+ else
{
- NotifyClient (WCMRAudioDeviceManagerClient::DeviceConnectionLost);
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device name. Device ID: " << m_DeviceID);
}
- SAFE_RELEASE (pTheDeviceToErase);
+
+ delete [] deviceName;
}
else
- deviceIter++;
- }
-
- //now add the ones that are not there...
- for (reportedDeviceIndex = 0; reportedDeviceIndex < numDevices; reportedDeviceIndex++)
- {
- bool deviceFound = false;
- for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); deviceIter++)
{
- WCMRCoreAudioDevice *pDeviceToWorkUpon = dynamic_cast<WCMRCoreAudioDevice *>(*deviceIter);
- //it's possible that the device is actually not a core audio device - perhaps a none device...
- if (!pDeviceToWorkUpon)
- continue;
-
- if (pDeviceToWorkUpon->DeviceID() == deviceIDs[reportedDeviceIndex])
- {
- deviceFound = true;
- break;
- }
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device name property size. Device ID: " << m_DeviceID);
}
-
- if (!deviceFound)
+
+ if (pDevInfo)
{
- //add it to our list...
- //build a device object...
- WCMRCoreAudioDevice *pNewDevice = new WCMRCoreAudioDevice (this, deviceIDs[reportedDeviceIndex], m_UseMultithreading, m_bNoCopyAudioBuffer);
- bool bDeleteNewDevice = true;
+ //Retrieve all the information we need for the device
+ WTErr wErr = eNoErr;
- if (pNewDevice)
+ //Get available sample rates for the device
+ std::vector<int> availableSampleRates;
+ wErr = getDeviceAvailableSampleRates(pDevInfo->m_DeviceId, availableSampleRates);
+
+ if (wErr != eNoErr)
+ {
+ DEBUG_MSG ("Failed to get device available sample rates. Device ID: " << m_DeviceID);
+ delete pDevInfo;
+ continue; //proceed to the next device
+ }
+
+ pDevInfo->m_AvailableSampleRates = availableSampleRates;
+
+ //Get max input channels
+ uint32_t maxInputChannels;
+ wErr = getDeviceMaxInputChannels(pDevInfo->m_DeviceId, maxInputChannels);
+
+ if (wErr != eNoErr)
+ {
+ DEBUG_MSG ("Failed to get device max input channels count. Device ID: " << m_DeviceID);
+ delete pDevInfo;
+ continue; //proceed to the next device
+ }
+
+ pDevInfo->m_MaxInputChannels = maxInputChannels;
+
+ //Get max output channels
+ uint32_t maxOutputChannels;
+ wErr = getDeviceMaxOutputChannels(pDevInfo->m_DeviceId, maxOutputChannels);
+
+ if (wErr != eNoErr)
+ {
+ DEBUG_MSG ("Failed to get device max output channels count. Device ID: " << m_DeviceID);
+ delete pDevInfo;
+ continue; //proceed to the next device
+ }
+
+ pDevInfo->m_MaxOutputChannels = maxOutputChannels;
+
+ //Now check if this device is acceptable according to current input/output settings
+ bool bRejectDevice = false;
+ switch(m_eAudioDeviceFilter)
{
-
- // Don't delete the new device by default, since most cases use it
- bDeleteNewDevice = false;
-
- // Insert the new device to the device list according to its enum
- switch(m_eAudioDeviceFilter)
- {
case eInputOnlyDevices:
- if ((int) pNewDevice->InputChannels().size() != 0)
+ if (pDevInfo->m_MaxInputChannels != 0)
{
- m_Devices.push_back (pNewDevice);
+ m_DeviceInfoVec.push_back(pDevInfo);
}
else
{
// Delete unnecesarry device
- bDeleteNewDevice = true;
+ bRejectDevice = true;
}
break;
case eOutputOnlyDevices:
- if ((int) pNewDevice->OutputChannels().size() != 0)
+ if (pDevInfo->m_MaxOutputChannels != 0)
{
- m_Devices.push_back (pNewDevice);
+ m_DeviceInfoVec.push_back(pDevInfo);
}
else
{
// Delete unnecesarry device
- bDeleteNewDevice = true;
+ bRejectDevice = true;
}
break;
case eFullDuplexDevices:
- if ((int) pNewDevice->InputChannels().size() != 0 && (int) pNewDevice->OutputChannels().size() != 0)
+ if (pDevInfo->m_MaxInputChannels != 0 && pDevInfo->m_MaxOutputChannels != 0)
{
- m_Devices.push_back (pNewDevice);
+ m_DeviceInfoVec.push_back(pDevInfo);
}
else
{
// Delete unnecesarry device
- bDeleteNewDevice = true;
+ bRejectDevice = true;
}
break;
case eAllDevices:
default:
- m_Devices.push_back (pNewDevice);
+ m_DeviceInfoVec.push_back(pDevInfo);
break;
- }
}
- if(bDeleteNewDevice)
+ if(bRejectDevice)
{
syslog (LOG_NOTICE, "%s rejected, In Channels = %d, Out Channels = %d\n",
- pNewDevice->DeviceName().c_str(), (int) pNewDevice->InputChannels().size(),
- (int) pNewDevice->OutputChannels().size());
+ pDevInfo->m_DeviceName.c_str(), pDevInfo->m_MaxInputChannels, pDevInfo->m_MaxOutputChannels);
// 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.
- SAFE_RELEASE(pNewDevice);
+ delete pDevInfo;
}
}
}
-
-
+
+
//If no devices were found, that's not a good thing!
- if (m_Devices.empty())
+ if (m_DeviceInfoVec.empty())
{
DEBUG_MSG ("No matching CoreAudio devices were found\n");
- }
-
-
- m_UpdateDeviceListRequested = m_UpdateDeviceListProcessed = 0;
-
+ }
}
catch (...)
{
if (WUNoError(retVal))
retVal = eCoreAudioFailed;
}
-
- safe_delete_array(deviceIDs);
+
+ delete[] deviceIDs;
closelog();
-
+
return retVal;
}
+WTErr WCMRCoreAudioDeviceManager::updateDeviceListImpl()
+{
+ wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceInfoVecMutex);
+ WTErr err = generateDeviceListImpl();
+
+ if (eNoErr != err)
+ {
+ std::cout << "API::PortAudioDeviceManager::updateDeviceListImpl: Device list update error: "<< err << std::endl;
+ return err;
+ }
+
+ if (m_CurrentDevice)
+ {
+ // if we have device initialized we should find out if this device is still connected
+ DeviceInfo devInfo;
+ WTErr deviceLookUpErr = GetDeviceInfoByName(m_CurrentDevice->DeviceName(), devInfo );
+
+ if (eNoErr != deviceLookUpErr)
+ {
+ NotifyClient (WCMRAudioDeviceManagerClient::IODeviceDisconnected);
+ return err;
+ }
+ }
+
+ NotifyClient (WCMRAudioDeviceManagerClient::DeviceListChanged);
+
+ return err;
+}
+
-//**********************************************************************************************
-// WCMRCoreAudioDeviceManager::DoIdle
-//
-//! Used for idle time processing. This calls each device's DoIdle so that it can perform it's own idle processing.
-//! Also, if a device list change is detected, it updates the device list.
-//!
-//! \param none
-//!
-//! \return noErr if no devices have returned an error. An error code if any of the devices returned error.
-//!
-//**********************************************************************************************
-WTErr WCMRCoreAudioDeviceManager::DoIdle()
+WTErr WCMRCoreAudioDeviceManager::getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const
{
- //WTErr retVal = eNoErr;
+ AUTO_FUNC_DEBUG;
+
+ WTErr retVal = eNoErr;
+ OSStatus err = kAudioHardwareNoError;
+ UInt32 propSize = 0;
+
+ sampleRates.clear();
+
+ //first check if the request has been made for None device
+ if (deviceName == m_NoneDevice->DeviceName() )
+ {
+ sampleRates = m_NoneDevice->SamplingRates();
+ return retVal;
+ }
+
+ if (m_CurrentDevice && m_CurrentDevice->DeviceName () == deviceName) {
+ sampleRates.assign(m_CurrentDevice->SamplingRates().begin(), m_CurrentDevice->SamplingRates().end() );
+ return retVal;
+ }
+ DeviceInfo devInfo;
+ retVal = GetDeviceInfoByName(deviceName, devInfo);
+
+ //! 1. Get sample rate property size.
+ err = AudioDeviceGetPropertyInfo(devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL);
+
+ if (err == kAudioHardwareNoError)
+ {
+ //! 2. Get property: cannels output.
+
+ // Allocate size accrding to the number of audio values
+ int numRates = propSize / sizeof(AudioValueRange);
+ AudioValueRange* supportedRates = new AudioValueRange[numRates];
+
+ // Get sampling rates from Audio device
+ err = AudioDeviceGetProperty(devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates);
+
+ if (err == kAudioHardwareNoError)
+ {
+ //! 3. Update sample rates
+
+ // now iterate through our standard SRs
+ for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++)
+ {
+ //check to see if our SR is in the supported rates...
+ for (int deviceSR = 0; deviceSR < numRates; deviceSR++)
+ {
+ if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) &&
+ (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR]))
+ {
+ sampleRates.push_back ((int)gAllSampleRates[ourSR]);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str());
+ }
+
+ delete [] supportedRates;
+ }
+ else
{
- //wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex);
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str());
+ }
- //If there's something specific to CoreAudio manager idle handling do it here...
- if (m_UpdateDeviceListRequested != m_UpdateDeviceListProcessed)
+ devInfo.m_AvailableSampleRates.assign(sampleRates.begin(), sampleRates.end() );
+
+ return retVal;
+}
+
+
+WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& bufferSizes) const
+{
+ AUTO_FUNC_DEBUG;
+
+ WTErr retVal = eNoErr;
+ OSStatus err = kAudioHardwareNoError;
+ UInt32 propSize = 0;
+
+ bufferSizes.clear();
+
+ //first check if the request has been made for None device
+ if (deviceName == m_NoneDevice->DeviceName() )
+ {
+ bufferSizes = m_NoneDevice->BufferSizes();
+ return retVal;
+ }
+
+ if (m_CurrentDevice && m_CurrentDevice->DeviceName () == deviceName) {
+ bufferSizes.assign(m_CurrentDevice->BufferSizes().begin(), m_CurrentDevice->BufferSizes().end() );
+ return retVal;
+ }
+
+ DeviceInfo devInfo;
+ retVal = GetDeviceInfoByName(deviceName, devInfo);
+
+ if (eNoErr == retVal)
+ {
+ // 1. Get buffer size range
+ AudioValueRange bufferSizesRange;
+ propSize = sizeof (AudioValueRange);
+ err = AudioDeviceGetProperty (devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &bufferSizesRange);
+ if(err == kAudioHardwareNoError)
{
- m_UpdateDeviceListProcessed = m_UpdateDeviceListRequested;
- UpdateDeviceList_Private();
- NotifyClient (WCMRAudioDeviceManagerClient::DeviceListChanged);
+ // 2. Run on all ranges and add them to the list
+ for(int bsize=0; gAllBufferSizes[bsize] > 0; bsize++)
+ {
+ if ((bufferSizesRange.mMinimum <= gAllBufferSizes[bsize]) && (bufferSizesRange.mMaximum >= gAllBufferSizes[bsize]))
+ {
+ bufferSizes.push_back (gAllBufferSizes[bsize]);
+ }
+ }
+
+ //if we didn't get a single hit, let's simply add the min. and the max...
+ if (bufferSizes.empty())
+ {
+ bufferSizes.push_back ((int)bufferSizesRange.mMinimum);
+ bufferSizes.push_back ((int)bufferSizesRange.mMaximum);
+ }
}
- }
+ else
+ {
+ retVal = eCoreAudioFailed;
+ DEBUG_MSG("Failed to get device buffer sizes range. Device Name: " << m_DeviceName.c_str());
+ }
+ }
+ else
+ {
+ retVal = eRMResNotFound;
+ std::cout << "API::PortAudioDeviceManager::GetBufferSizes: Device not found: "<< deviceName << std::endl;
+ }
- //Note that the superclass is going to call all the devices' DoIdle() anyway...
- return (WCMRAudioDeviceManager::DoIdle());
+
+ return retVal;
}
+
+OSStatus WCMRCoreAudioDeviceManager::HardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData)
+{
+ switch (inPropertyID)
+ {
+ case kAudioHardwarePropertyDevices:
+ {
+ WCMRCoreAudioDeviceManager* pManager = (WCMRCoreAudioDeviceManager*)inClientData;
+ if (pManager)
+ pManager->updateDeviceListImpl();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}