X-Git-Url: http://code.vuplus.com/gitweb/?a=blobdiff_plain;f=xbmc%2Fcores%2FAudioEngine%2FSinks%2FAESinkDARWINOSX.cpp;h=d3903687f4b2ba06e33d2f5e5f8bb03ddb588764;hb=1459cb92701d4db3cde0efc39130968cd5586dee;hp=b150fe0b5faeab8f8ba4e44a8a48aed9b5d35f3a;hpb=733d030bcde2f97a433c2bc251fd4551bae876ec;p=vuplus_xbmc diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp index b150fe0..d390368 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp @@ -24,6 +24,7 @@ #include "cores/AudioEngine/Utils/AERingBuffer.h" #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h" #include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h" +#include "cores/AudioEngine/Sinks/osx/CoreAudioChannelLayout.h" #include "osx/DarwinUtils.h" #include "utils/log.h" #include "utils/StringUtils.h" @@ -33,6 +34,7 @@ #include #define CA_MAX_CHANNELS 16 +// default channel map - in case it can't be fetched from the device static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = { AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR , AE_CH_UNKNOWN1 , @@ -46,6 +48,195 @@ static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = { AE_CH_NULL }; +// map coraudio channel labels to activeae channel labels +static enum AEChannel CAChannelToAEChannel(AudioChannelLabel CAChannelLabel) +{ + enum AEChannel ret = AE_CH_NULL; + static unsigned int unknownChannel = AE_CH_UNKNOWN1; + switch(CAChannelLabel) + { + case kAudioChannelLabel_Left: + ret = AE_CH_FL; + break; + case kAudioChannelLabel_Right: + ret = AE_CH_FR; + break; + case kAudioChannelLabel_Center: + ret = AE_CH_FC; + break; + case kAudioChannelLabel_LFEScreen: + ret = AE_CH_LFE; + break; + case kAudioChannelLabel_LeftSurroundDirect: + ret = AE_CH_SL; + break; + case kAudioChannelLabel_RightSurroundDirect: + ret = AE_CH_SR; + break; + case kAudioChannelLabel_LeftCenter: + ret = AE_CH_FLOC; + break; + case kAudioChannelLabel_RightCenter: + ret = AE_CH_FROC; + break; + case kAudioChannelLabel_CenterSurround: + ret = AE_CH_TC; + break; + case kAudioChannelLabel_LeftSurround: + ret = AE_CH_SL; + break; + case kAudioChannelLabel_RightSurround: + ret = AE_CH_SR; + break; + case kAudioChannelLabel_VerticalHeightLeft: + ret = AE_CH_TFL; + break; + case kAudioChannelLabel_VerticalHeightRight: + ret = AE_CH_TFR; + break; + case kAudioChannelLabel_VerticalHeightCenter: + ret = AE_CH_TFC; + break; + case kAudioChannelLabel_TopCenterSurround: + ret = AE_CH_TC; + break; + case kAudioChannelLabel_TopBackLeft: + ret = AE_CH_TBL; + break; + case kAudioChannelLabel_TopBackRight: + ret = AE_CH_TBR; + break; + case kAudioChannelLabel_TopBackCenter: + ret = AE_CH_TBC; + break; + case kAudioChannelLabel_RearSurroundLeft: + ret = AE_CH_BL; + break; + case kAudioChannelLabel_RearSurroundRight: + ret = AE_CH_BR; + break; + case kAudioChannelLabel_LeftWide: + ret = AE_CH_BLOC; + break; + case kAudioChannelLabel_RightWide: + ret = AE_CH_BROC; + break; + case kAudioChannelLabel_LFE2: + ret = AE_CH_LFE; + break; + case kAudioChannelLabel_LeftTotal: + ret = AE_CH_FL; + break; + case kAudioChannelLabel_RightTotal: + ret = AE_CH_FR; + break; + case kAudioChannelLabel_HearingImpaired: + ret = AE_CH_FC; + break; + case kAudioChannelLabel_Narration: + ret = AE_CH_FC; + break; + case kAudioChannelLabel_Mono: + ret = AE_CH_FC; + break; + case kAudioChannelLabel_DialogCentricMix: + ret = AE_CH_FC; + break; + case kAudioChannelLabel_CenterSurroundDirect: + ret = AE_CH_TC; + break; + case kAudioChannelLabel_Haptic: + ret = AE_CH_FC; + break; + default: + ret = (enum AEChannel)unknownChannel++; + } + if (unknownChannel > AE_CH_UNKNOWN8) + unknownChannel = AE_CH_UNKNOWN1; + + return ret; +} + +//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) +static void GetAEChannelMap(CCoreAudioDevice &device, CAEChannelInfo &channelMap, unsigned int channelsPerFrame) +{ + 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 8 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 = device.GetPreferredChannelLayoutForStereo(calayout); + else + mapAvailable = device.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()); +} + static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate) { for (size_t i = 0; i < list.size(); ++i) @@ -197,15 +388,6 @@ static void EnumerateDevices(CADeviceList &list) break; } - // add channel info - CAEChannelInfo channel_info; - for (UInt32 chan = 0; chan < CA_MAX_CHANNELS && chan < desc.mChannelsPerFrame; ++chan) - { - if (!device.m_channels.HasChannel(CAChannelMap[chan])) - device.m_channels += CAChannelMap[chan]; - channel_info += CAChannelMap[chan]; - } - // add sample rate info // for devices which return kAudioStreamAnyRatee // we add 44.1khz and 48khz - user can use @@ -256,6 +438,10 @@ static void EnumerateDevices(CADeviceList &list) } else// treat all other digital passthrough devices as optical device.m_deviceType = AE_DEVTYPE_IEC958; + + //treat all other digital devices as HDMI to let options open to the user + if (device.m_deviceType == AE_DEVTYPE_PCM) + device.m_deviceType = AE_DEVTYPE_HDMI; } // devicename based overwrites from former code - maybe FIXME at some point when we @@ -264,7 +450,9 @@ static void EnumerateDevices(CADeviceList &list) device.m_deviceType = AE_DEVTYPE_HDMI; if (hasDisplayPortName) device.m_deviceType = AE_DEVTYPE_DP; - + + //get channel map to match the devices channel layout as set in audio-midi-setup + GetAEChannelMap(caDevice, device.m_channels, caDevice.GetTotalOutputChannels()); list.push_back(std::make_pair(deviceID, device)); //in the first place of the list add the default device @@ -405,6 +593,19 @@ CAESinkDARWINOSX::~CAESinkDARWINOSX() RegisterDeviceChangedCB(false, this); } +float scoreSampleRate(Float64 destinationRate, unsigned int sourceRate) +{ + float score = 0; + double intPortion; + double fracPortion = modf(destinationRate / sourceRate, &intPortion); + + score += (1 - fracPortion) * 1000; // prefer sample rates that are multiples of the source sample rate + score += (intPortion == 1.0) ? 500 : 0; // prefer exact matches over other multiples + score += (intPortion > 1 && intPortion < 100) ? (100 - intPortion) / 100 * 100 : 0; // prefer smaller multiples otherwise + + return score; +} + float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format) { float score = 0; @@ -440,10 +641,8 @@ float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat & { // non-passthrough, whatever works is fine if (desc.mFormatID == kAudioFormatLinearPCM) { - if (desc.mSampleRate == format.m_sampleRate) - score += 10; - else if (desc.mSampleRate > format.m_sampleRate) - score += 1; + score += scoreSampleRate(desc.mSampleRate, format.m_sampleRate); + if (desc.mChannelsPerFrame == format.m_channelLayout.Count()) score += 5; else if (desc.mChannelsPerFrame > format.m_channelLayout.Count()) @@ -503,6 +702,7 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) bool passthrough = false; UInt32 outputIndex = 0; + UInt32 numOutputChannels = 0; float outputScore = 0; AudioStreamBasicDescription outputFormat = {0}; AudioStreamID outputStream = 0; @@ -537,7 +737,7 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) if (score > outputScore) { - passthrough = score > 1000; + passthrough = score > 10000; outputScore = score; outputFormat = desc; outputStream = *i; @@ -548,11 +748,12 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) } m_planar = false; + numOutputChannels = outputFormat.mChannelsPerFrame; if (streams.size() > 1 && outputFormat.mChannelsPerFrame == 1) { - CLog::Log(LOGDEBUG, "%s Found planar audio with %u channels?", __FUNCTION__, streams.size()); - outputFormat.mChannelsPerFrame = std::min((size_t)format.m_channelLayout.Count(), streams.size()); + numOutputChannels = std::min((size_t)format.m_channelLayout.Count(), streams.size()); m_planar = true; + CLog::Log(LOGDEBUG, "%s Found planar audio with %u channels using %u of them.", __FUNCTION__, (unsigned int)streams.size(), (unsigned int)numOutputChannels); } if (!outputFormat.mFormatID) @@ -563,14 +764,7 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) /* Update our AE format */ format.m_sampleRate = outputFormat.mSampleRate; - if (outputFormat.mChannelsPerFrame != format.m_channelLayout.Count()) - { /* update the channel count. We assume that they're layed out as given in CAChannelMap. - if they're not, this is plain wrong */ - format.m_channelLayout.Reset(); - for (unsigned int i = 0; i < outputFormat.mChannelsPerFrame && i < CA_MAX_CHANNELS; i++) - format.m_channelLayout += CAChannelMap[i]; - } - + m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM; std::string formatString; @@ -592,6 +786,10 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString)); CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString)); + // update the channel map based on the new stream format + GetAEChannelMap(m_device, format.m_channelLayout, numOutputChannels); + + m_latentFrames = m_device.GetNumLatencyFrames(); m_latentFrames += m_outputStream.GetNumLatencyFrames();