X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fbackends%2Fwavesaudio%2Fwavesapi%2Fdevicemanager%2FWCMRCoreAudioDeviceManager.cpp;h=5c1ceac63efd90add3c0530d80c15e24c8f8d4a3;hb=000609901b6b8d81d3088578917c9b48680a28e7;hp=b66d2519cab7e155f3f2535fd488ceac5bb276f4;hpb=8a6762f189680a7ae6d2060aab97be0c43ae227a;p=ardour.git diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp index b66d2519ca..5c1ceac63e 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp @@ -1,23 +1,6 @@ -/* - 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 //! @@ -42,7 +25,7 @@ using namespace wvNS; ///< 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 */ }; @@ -58,6 +41,8 @@ static const int DEFAULT_SR = 44100; ///< The default buffer size. static const int DEFAULT_BUFFERSIZE = 128; +static const int NONE_DEVICE_ID = -1; + ///< Number of stalls to wait before notifying user... static const int NUM_STALLS_FOR_NOTIFICATION = 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. @@ -166,7 +151,7 @@ WCMRCoreAudioDevice::WCMRCoreAudioDevice (WCMRCoreAudioDeviceManager *pManager, m_CurrentBufferSize = (int)bufferSize; - UpdateDeviceInfo(true /*updateSRSupported*/, true /* updateBufferSizes */); + UpdateDeviceInfo(); //should use a valid current SR... if (m_SamplingRates.size()) @@ -252,14 +237,11 @@ WCMRCoreAudioDevice::~WCMRCoreAudioDevice () // 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; @@ -272,17 +254,8 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceInfo (bool updateSRSupported, bool update 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) { @@ -383,7 +356,7 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceInputs() 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 @@ -414,12 +387,54 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceInputs() // 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; @@ -497,10 +512,51 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceOutputs() 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; @@ -786,7 +842,7 @@ WTErr WCMRCoreAudioDevice::SetCurrentSamplingRate (int newRate) retVal = SetAndCheckCurrentSamplingRate (newRate); if(retVal == eNoErr) { - retVal = UpdateDeviceInfo (false/*updateSRSupported*/, true/*updateBufferSizes*/); + retVal = UpdateDeviceInfo (); } //reactivate it. @@ -1205,12 +1261,17 @@ WTErr WCMRCoreAudioDevice::EnableListeners() 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); @@ -1266,14 +1327,18 @@ WTErr WCMRCoreAudioDevice::DisableListeners() #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 @@ -1342,15 +1407,19 @@ void WCMRCoreAudioDevice::PropertyChangeProc (AudioDevicePropertyID inPropertyID 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; } @@ -1616,35 +1685,7 @@ WTErr WCMRCoreAudioDevice::SetupAUHAL() 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) @@ -1684,8 +1725,6 @@ WTErr WCMRCoreAudioDevice::TearDownAUHAL() CloseComponent(m_AUHALAudioUnit); m_AUHALAudioUnit = NULL; } - - safe_delete_array(m_pInputData); return retVal; } @@ -1716,7 +1755,6 @@ WTErr WCMRCoreAudioDevice::SetActive (bool newState) if (newState) { - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL."); retVal = SetupAUHAL(); @@ -1759,7 +1797,7 @@ WTErr WCMRCoreAudioDevice::SetActive (bool newState) m_DropsReported = 0; m_IgnoreThisDrop = true; - UpdateDeviceInfo(true /*updateSRSupported */, true /* updateBufferSizes#*/); + UpdateDeviceInfo(); } @@ -1864,7 +1902,6 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) SetupToneGenerator (); #endif //WV_USE_TONE_GEN - m_StopRequested = false; m_SampleCountAtLastIdle = 0; m_StalledSampleCounter = 0; m_SampleCounter = 0; @@ -1881,6 +1918,8 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) err = AudioOutputUnitStart (m_AUHALAudioUnit); + m_StopRequested = false; + if(err) { DEBUG_MSG( "Failed to start AudioUnit, err " << err ); @@ -1895,11 +1934,11 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) 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); } @@ -1938,6 +1977,7 @@ Exit: //********************************************************************************************** WTErr WCMRCoreAudioDevice::DoIdle () { + /* if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported) { m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged); @@ -1990,7 +2030,7 @@ WTErr WCMRCoreAudioDevice::DoIdle () m_StalledSampleCounter = 0; m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount); } - } + }*/ return (eNoErr); @@ -2082,6 +2122,18 @@ WTErr WCMRCoreAudioDevice::ShowConfigPanel (void */*pParam*/) { 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); } @@ -2143,19 +2195,38 @@ OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioAction 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? @@ -2164,7 +2235,6 @@ OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioAction 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; } @@ -2317,14 +2387,10 @@ OSStatus WCMRCoreAudioDevice::GetStreamLatency(AudioDeviceID device, bool isInpu //! \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; @@ -2347,13 +2413,13 @@ WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerCli } //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; } @@ -2376,25 +2442,7 @@ WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager() 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::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 (...) { @@ -2405,313 +2453,595 @@ WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager() } -//********************************************************************************************** -// 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& 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::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(*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(*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 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& 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& 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; +}