bool CAUOutputDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout) { if (!m_DeviceId) return false; UInt32 propertySize = 0; Boolean writable = false; OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propertySize, &writable); if (ret) return false; void* pBuf = malloc(propertySize); ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propertySize, pBuf); if (ret) CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: " "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str()); else { // Copy the result into the caller's instance layout.CopyLayout(*((AudioChannelLayout*)pBuf)); } free(pBuf); return (ret == noErr); }
bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout) const { if (!m_DeviceId) return false; AudioObjectPropertyAddress propertyAddress; propertyAddress.mScope = kAudioDevicePropertyScopeOutput; propertyAddress.mElement = 0; propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; UInt32 propertySize = 0; OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize); if (ret) return false; void* pBuf = malloc(propertySize); ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf); if (ret != noErr) CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: " "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str()); else { // Copy the result into the caller's instance layout.CopyLayout(*((AudioChannelLayout*)pBuf)); } free(pBuf); return (ret == noErr); }
bool CCoreAudioDevice::GetPreferredChannelLayoutForStereo(CCoreAudioChannelLayout &layout) const { if (!m_DeviceId) return false; AudioObjectPropertyAddress propertyAddress; propertyAddress.mScope = kAudioDevicePropertyScopeOutput; propertyAddress.mElement = 0; propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelsForStereo; UInt32 channels[2];// this will receive the channel labels UInt32 propertySize = sizeof(channels); OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &channels); if (ret != noErr) CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayoutForStereo: " "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str()); else { // Copy/generate a layout into the result into the caller's instance layout.CopyLayoutForStereo(channels); } return (ret == noErr); }
//Note: in multichannel mode CA will either pull 2 channels of data (stereo) or 6/8 channels of data //(every speaker setup with more then 2 speakers). The difference between the number of real speakers //and 6/8 channels needs to be padded with unknown channels so that the sample size fits 6/8 channels // //device [in] - the device whose channel layout should be used //channelMap [in/out] - if filled it will it indicates that we are called from initialize and we log the requested map, out returns the channelMap for device //channelsPerFrame [in] - the number of channels this device is configured to (e.x. 2 or 6/8) void AEDeviceEnumerationOSX::GetAEChannelMap(CAEChannelInfo &channelMap, unsigned int channelsPerFrame) const { CCoreAudioChannelLayout calayout; bool logMapping = channelMap.Count() > 0; // only log if the engine requests a layout during init bool mapAvailable = false; unsigned int numberChannelsInDeviceLayout = CA_MAX_CHANNELS; // default number of channels from CAChannelMap AudioChannelLayout *layout = NULL; // try to fetch either the multichannel or the stereo channel layout from the device if (channelsPerFrame == 2 || channelMap.Count() == 2) mapAvailable = m_caDevice.GetPreferredChannelLayoutForStereo(calayout); else mapAvailable = m_caDevice.GetPreferredChannelLayout(calayout); // if a map was fetched - check if it is usable if (mapAvailable) { layout = calayout; if (layout == NULL || layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) mapAvailable = false;// wrong map format else numberChannelsInDeviceLayout = layout->mNumberChannelDescriptions; } // start the mapping action // the number of channels to be added to the outgoing channelmap // this is CA_MAX_CHANNELS at max and might be lower for some output devices (channelsPerFrame) unsigned int numChannelsToMap = std::min((unsigned int)CA_MAX_CHANNELS, (unsigned int)channelsPerFrame); // if there was a map fetched we force the number of // channels to map to channelsPerFrame (this allows mapping // of more then CA_MAX_CHANNELS if needed) if (mapAvailable) numChannelsToMap = channelsPerFrame; std::string layoutStr; if (logMapping) { CLog::Log(LOGDEBUG, "%s Engine requests layout %s", __FUNCTION__, ((std::string)channelMap).c_str()); if (mapAvailable) CLog::Log(LOGDEBUG, "%s trying to map to %s layout: %s", __FUNCTION__, channelsPerFrame == 2 ? "stereo" : "multichannel", calayout.ChannelLayoutToString(*layout, layoutStr)); else CLog::Log(LOGDEBUG, "%s no map available - using static multichannel map layout", __FUNCTION__); } channelMap.Reset();// start with an empty map for (unsigned int channel = 0; channel < numChannelsToMap; channel++) { // we only try to map channels which are defined in the device layout enum AEChannel currentChannel; if (channel < numberChannelsInDeviceLayout) { // get the channel from the fetched map if (mapAvailable) currentChannel = caChannelToAEChannel(layout->mChannelDescriptions[channel].mChannelLabel); else// get the channel from the default map currentChannel = CAChannelMap[channel]; } else// fill with unknown channels currentChannel = caChannelToAEChannel(kAudioChannelLabel_Unknown); if(!channelMap.HasChannel(currentChannel))// only add if not already added channelMap += currentChannel; } if (logMapping) CLog::Log(LOGDEBUG, "%s mapped channels to layout %s", __FUNCTION__, ((std::string)channelMap).c_str()); }