diff options
author | Rainer Hochecker <fernetmenta@online.de> | 2013-12-08 11:28:39 -0800 |
---|---|---|
committer | Rainer Hochecker <fernetmenta@online.de> | 2013-12-08 11:28:39 -0800 |
commit | c530e0d2052744eebe4fd8481b3a46a8bef3fd68 (patch) | |
tree | 0639e0193f028fc1eefb15d0d53b12c59de14359 | |
parent | 9382b857e4b393210f815957426447fb3fc80753 (diff) | |
parent | d4766829253c5e9f9996a55974f9ea2c503e771d (diff) |
Merge pull request #3744 from FernetMenta/aefixes
Aefixes
-rw-r--r-- | system/settings/settings.xml | 8 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp | 32 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp | 5 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp | 122 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/AESinkALSA.h | 15 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp | 117 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp | 23 |
7 files changed, 235 insertions, 87 deletions
diff --git a/system/settings/settings.xml b/system/settings/settings.xml index 4b69b388a4..1a3aca4805 100644 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -2222,7 +2222,13 @@ <level>2</level> <default>false</default> <dependencies> - <dependency type="visible" setting="audiooutput.channels" operator="!is">1</dependency> + <dependency type="visible"> + <or> + <condition on="property" name="aesettingvisible" setting="audiooutput.channels">audiooutput.stereoupmix</condition> + <condition on="property" name="aesettingvisible" setting="audiooutput.passthrough">audiooutput.stereoupmix</condition> + <condition on="property" name="aesettingvisible" setting="audiooutput.ac3passthrough">audiooutput.stereoupmix</condition> + </or> + </dependency> </dependencies> <control type="toggle" /> </setting> diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp index a3a61647f6..c977c37b6b 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp @@ -865,6 +865,14 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt) initSink = true; m_stats.Reset(m_sinkFormat.m_sampleRate); m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float)); + + // limit buffer size in case of sink returns large buffer + unsigned int buffertime = (m_sinkFormat.m_frames*1000) / m_sinkFormat.m_sampleRate; + if (buffertime > 80) + { + CLog::Log(LOGWARNING, "ActiveAE::%s - sink returned large buffer of %d ms, reducing to 80 ms", __FUNCTION__, buffertime); + m_sinkFormat.m_frames = 80 * m_sinkFormat.m_sampleRate / 1000; + } } if (m_silenceBuffers) @@ -877,8 +885,6 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt) if (m_streams.empty()) { inputFormat = m_sinkFormat; - inputFormat.m_channelLayout = m_sinkRequestFormat.m_channelLayout; - inputFormat.m_channelLayout.ResolveChannels(m_sinkFormat.m_channelLayout); inputFormat.m_dataFormat = AE_FMT_FLOAT; inputFormat.m_frameSize = inputFormat.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(inputFormat.m_dataFormat) >> 3); @@ -967,8 +973,6 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt) else { outputFormat = m_sinkFormat; - outputFormat.m_channelLayout = m_sinkRequestFormat.m_channelLayout; - outputFormat.m_channelLayout.ResolveChannels(m_sinkFormat.m_channelLayout); outputFormat.m_dataFormat = AE_FMT_FLOAT; outputFormat.m_frameSize = outputFormat.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(outputFormat.m_dataFormat) >> 3); @@ -997,6 +1001,7 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt) // create buffer pool (*it)->m_inputBuffers = new CActiveAEBufferPool((*it)->m_format); (*it)->m_inputBuffers->Create(MAX_CACHE_LEVEL*1000); + (*it)->m_streamSpace = (*it)->m_format.m_frameSize * (*it)->m_format.m_frames; } if (initSink && (*it)->m_resampleBuffers) { @@ -1325,7 +1330,7 @@ void CActiveAE::ApplySettingsToFormat(AEAudioFormat &format, AudioSettings &sett if (m_settings.config == AE_CONFIG_FIXED || (settings.stereoupmix && format.m_channelLayout.Count() <= 2)) format.m_channelLayout = stdLayout; else - format.m_channelLayout.ResolveChannels(stdLayout);; + format.m_channelLayout.ResolveChannels(stdLayout); } // don't change from multi to stereo in AUTO mode else if ((settings.config == AE_CONFIG_AUTO) && @@ -1999,7 +2004,7 @@ void CActiveAE::LoadSettings() m_settings.channels = (m_sink.GetDeviceType(m_settings.device) == AE_DEVTYPE_IEC958) ? AE_CH_LAYOUT_2_0 : CSettings::Get().GetInt("audiooutput.channels"); m_settings.samplerate = CSettings::Get().GetInt("audiooutput.samplerate"); - m_settings.stereoupmix = (m_settings.channels > AE_CH_LAYOUT_2_0) ? CSettings::Get().GetBool("audiooutput.stereoupmix") : false; + m_settings.stereoupmix = IsSettingVisible("audiooutput.stereoupmix") ? CSettings::Get().GetBool("audiooutput.stereoupmix") : false; m_settings.normalizelevels = CSettings::Get().GetBool("audiooutput.normalizelevels"); m_settings.passthrough = m_settings.config == AE_CONFIG_FIXED ? false : CSettings::Get().GetBool("audiooutput.passthrough"); @@ -2146,6 +2151,21 @@ bool CActiveAE::IsSettingVisible(const std::string &settingId) m_sink.GetDeviceType(CSettings::Get().GetString("audiooutput.passthroughdevice")) == AE_DEVTYPE_HDMI) return true; } + else if (settingId == "audiooutput.stereoupmix") + { + if (m_sink.GetDeviceType(CSettings::Get().GetString("audiooutput.audiodevice")) != AE_DEVTYPE_IEC958) + { + if (CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0) + return true; + } + else + { + if (m_sink.HasPassthroughDevice() && + CSettings::Get().GetBool("audiooutput.passthrough") && + CSettings::Get().GetBool("audiooutput.ac3passthrough")) + return true; + } + } return false; } diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp index d57dd5a3a7..71458ee68c 100644 --- a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp +++ b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp @@ -530,6 +530,11 @@ bool CCoreAudioAE::IsSettingVisible(const std::string &settingId) else return false; } + else if (settingId == "audiooutput.stereoupmix") + { + if (CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0) + return true; + } return true; } diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp index 4f63535d2c..f1e5401ecd 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp @@ -45,12 +45,24 @@ static enum AEChannel ALSAChannelMap[ALSA_MAX_CHANNELS + 1] = { AE_CH_NULL }; -static enum AEChannel ALSAChannelMapWide[ALSA_MAX_CHANNELS + 1] = { +static enum AEChannel ALSAChannelMap51Wide[ALSA_MAX_CHANNELS + 1] = { AE_CH_FL , AE_CH_FR , AE_CH_SL , AE_CH_SR , AE_CH_FC , AE_CH_LFE , AE_CH_BL , AE_CH_BR , AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */ AE_CH_NULL }; +static enum AEChannel ALSAChannelMap71Wide[ALSA_MAX_CHANNELS + 1] = { + AE_CH_FLOC , AE_CH_FROC , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_FL , AE_CH_FR , + AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */ + AE_CH_NULL +}; + +static enum AEChannel ALSAChannelMapPassthrough[ALSA_MAX_CHANNELS + 1] = { + AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , + AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */ + AE_CH_NULL +}; + static unsigned int ALSASampleRateList[] = { 5512, @@ -87,25 +99,37 @@ CAESinkALSA::~CAESinkALSA() Deinitialize(); } -inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format) +inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format, unsigned int maxChannels) { + enum AEChannel* channelMap = ALSAChannelMap; unsigned int count = 0; - if (format.m_dataFormat == AE_FMT_AC3 || - format.m_dataFormat == AE_FMT_DTS || - format.m_dataFormat == AE_FMT_EAC3) - count = 2; + if (format.m_dataFormat == AE_FMT_AC3 || + format.m_dataFormat == AE_FMT_DTS || + format.m_dataFormat == AE_FMT_EAC3) + { + count = 2; + channelMap = ALSAChannelMapPassthrough; + } else if (format.m_dataFormat == AE_FMT_TRUEHD || format.m_dataFormat == AE_FMT_DTSHD) - count = 8; + { + count = 8; + channelMap = ALSAChannelMapPassthrough; + } else { // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels // but no BR BL channels, we use the wide map in order to open only the num of channels really // needed. - enum AEChannel* channelMap = ALSAChannelMap; if (format.m_channelLayout.HasChannel(AE_CH_SL) && !format.m_channelLayout.HasChannel(AE_CH_BL)) - channelMap = ALSAChannelMapWide; + { + channelMap = ALSAChannelMap51Wide; + } + else if (maxChannels >= 8 && format.m_channelLayout.HasChannel(AE_CH_FLOC) && !format.m_channelLayout.HasChannel(AE_CH_SL)) + { + channelMap = ALSAChannelMap71Wide; + } for (unsigned int c = 0; c < 8; ++c) for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i) if (format.m_channelLayout[i] == channelMap[c]) @@ -116,8 +140,12 @@ inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format) } CAEChannelInfo info; - for (unsigned int i = 0; i < count; ++i) - info += ALSAChannelMap[i]; + for (unsigned int i = 0; i < count && i < maxChannels+1; ++i) + info += channelMap[i]; + + CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Input Channel Count: %d Output Channel Count: %d", format.m_channelLayout.Count(), count); + CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Requested Layout: %s", std::string(format.m_channelLayout).c_str()); + CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Got Layout: %s", std::string(info).c_str()); return info; } @@ -143,20 +171,22 @@ void CAESinkALSA::GetAESParams(AEAudioFormat format, std::string& params) bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) { - CAEChannelInfo channelLayout; + CAEChannelInfo channelLayout = GetChannelLayout(format, 8); m_initDevice = device; m_initFormat = format; + ALSAConfig inconfig, outconfig; + inconfig.format = format.m_dataFormat; + inconfig.sampleRate = format.m_sampleRate; + inconfig.channels = channelLayout.Count(); /* if we are raw, correct the data format */ if (AE_IS_RAW(format.m_dataFormat)) { - channelLayout = GetChannelLayout(format); - format.m_dataFormat = AE_FMT_S16NE; - m_passthrough = true; + inconfig.format = AE_FMT_S16NE; + m_passthrough = true; } else { - channelLayout = GetChannelLayout(format); m_passthrough = false; } #if defined(HAS_LIBAMCODEC) @@ -167,14 +197,12 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) } #endif - if (channelLayout.Count() == 0) + if (inconfig.channels == 0) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout"); return false; } - format.m_channelLayout = channelLayout; - AEDeviceType devType = AEDeviceTypeFromName(device); std::string AESParams; @@ -189,7 +217,7 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) snd_config_t *config; snd_config_copy(&config, snd_config); - if (!OpenPCMDevice(device, AESParams, channelLayout.Count(), &m_pcm, config)) + if (!OpenPCMDevice(device, AESParams, inconfig.channels, &m_pcm, config)) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str()); snd_config_delete(config); @@ -205,13 +233,26 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) /* free the sound config */ snd_config_delete(config); - if (!InitializeHW(format) || !InitializeSW(format)) + if (!InitializeHW(inconfig, outconfig) || !InitializeSW(outconfig)) return false; // we want it blocking snd_pcm_nonblock(m_pcm, 0); snd_pcm_prepare (m_pcm); + if (m_passthrough && inconfig.channels != outconfig.channels) + { + CLog::Log(LOGINFO, "CAESinkALSA::Initialize - could not open required number of channels"); + return false; + } + // adjust format to the configuration we got + format.m_channelLayout = GetChannelLayout(format, outconfig.channels); + format.m_sampleRate = outconfig.sampleRate; + format.m_frames = outconfig.periodSize; + format.m_frameSize = outconfig.frameSize; + format.m_frameSamples = outconfig.periodSize * outconfig.channels; + format.m_dataFormat = outconfig.format; + m_format = format; m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate; @@ -255,7 +296,7 @@ snd_pcm_format_t CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat forma } } -bool CAESinkALSA::InitializeHW(AEAudioFormat &format) +bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig) { snd_pcm_hw_params_t *hw_params; @@ -265,35 +306,35 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) snd_pcm_hw_params_any(m_pcm, hw_params); snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); - unsigned int sampleRate = format.m_sampleRate; - unsigned int channelCount = format.m_channelLayout.Count(); + unsigned int sampleRate = inconfig.sampleRate; + unsigned int channelCount = inconfig.channels; snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); /* ensure we opened X channels or more */ - if (format.m_channelLayout.Count() > channelCount) + if (inconfig.channels > channelCount) { CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels"); } - /* update the channelLayout to what we managed to open */ - format.m_channelLayout.Reset(); - for (unsigned int i = 0; i < channelCount; ++i) - format.m_channelLayout += ALSAChannelMap[i]; + /* update outconfig */ + outconfig.channels = channelCount; + + snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format); + outconfig.format = inconfig.format; - snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat); if (fmt == SND_PCM_FORMAT_UNKNOWN) { /* if we dont support the requested format, fallback to float */ - format.m_dataFormat = AE_FMT_FLOAT; - fmt = SND_PCM_FORMAT_FLOAT; + fmt = SND_PCM_FORMAT_FLOAT; + outconfig.format = AE_FMT_FLOAT; } /* try the data format */ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { /* if the chosen format is not supported, try each one in decending order */ - CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat)); + CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(outconfig.format)); for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) @@ -322,8 +363,8 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) } /* record that the format fell back to X */ - format.m_dataFormat = i; - CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat)); + outconfig.format = i; + CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(outconfig.format)); break; } @@ -418,10 +459,9 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize); /* set the format parameters */ - format.m_sampleRate = sampleRate; - format.m_frames = periodSize; - format.m_frameSamples = periodSize * format.m_channelLayout.Count(); - format.m_frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); + outconfig.sampleRate = sampleRate; + outconfig.periodSize = periodSize; + outconfig.frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); m_bufferSize = (unsigned int)bufferSize; m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate); @@ -431,7 +471,7 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) return true; } -bool CAESinkALSA::InitializeSW(AEAudioFormat &format) +bool CAESinkALSA::InitializeSW(const ALSAConfig &inconfig) { snd_pcm_sw_params_t *sw_params; snd_pcm_uframes_t boundary; @@ -444,7 +484,7 @@ bool CAESinkALSA::InitializeSW(AEAudioFormat &format) snd_pcm_sw_params_set_silence_threshold(m_pcm, sw_params, 0); snd_pcm_sw_params_get_boundary (sw_params, &boundary); snd_pcm_sw_params_set_silence_size (m_pcm, sw_params, boundary); - snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, format.m_frames); + snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, inconfig.periodSize); if (snd_pcm_sw_params(m_pcm, sw_params) < 0) { diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h index d920fdb022..2fafacc7b5 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h @@ -52,7 +52,7 @@ public: static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); private: - CAEChannelInfo GetChannelLayout(AEAudioFormat format); + CAEChannelInfo GetChannelLayout(AEAudioFormat format, unsigned int maxChannels); void GetAESParams(const AEAudioFormat format, std::string& params); void HandleError(const char* name, int err); @@ -66,10 +66,19 @@ private: snd_pcm_t *m_pcm; int m_timeout; + struct ALSAConfig + { + unsigned int sampleRate; + unsigned int periodSize; + unsigned int frameSize; + unsigned int channels; + AEDataFormat format; + }; + static snd_pcm_format_t AEFormatToALSAFormat(const enum AEDataFormat format); - bool InitializeHW(AEAudioFormat &format); - bool InitializeSW(AEAudioFormat &format); + bool InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig); + bool InitializeSW(const ALSAConfig &inconfig); static void AppendParams(std::string &device, const std::string ¶ms); static bool TryDevice(const std::string &name, snd_pcm_t **pcmp, snd_config_t *lconf); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp index 2e774b478c..6619e5fb59 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp @@ -1017,9 +1017,18 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format) { WAVEFORMATEXTENSIBLE_IEC61937 wfxex_iec61937; WAVEFORMATEXTENSIBLE &wfxex = wfxex_iec61937.FormatExt; + bool obsolete71Wide = false; if (format.m_dataFormat <= AE_FMT_FLOAT) + { BuildWaveFormatExtensible(format, wfxex); + // handle obsolete 7.1 wide + if (wfxex.dwChannelMask == KSAUDIO_SPEAKER_7POINT1) + { + obsolete71Wide = true; + wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; + } + } else BuildWaveFormatExtensibleIEC61397(format, wfxex_iec61937); @@ -1060,51 +1069,72 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format) CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s) - trying to find a compatible format", WASAPIErrToStr(hr)); int closestMatch; + unsigned int requestedChannels = wfxex.Format.nChannels; + unsigned int noOfCh; /* The requested format is not supported by the device. Find something that works */ - for (int j = 0; j < sizeof(testFormats)/sizeof(sampleFormat); j++) + for (int layout = -1; layout <= (int)ARRAYSIZE(layoutsList); layout++) { - closestMatch = -1; - - wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wfxex.SubFormat = testFormats[j].subFormat; - wfxex.Format.wBitsPerSample = testFormats[j].bitsPerSample; - wfxex.Samples.wValidBitsPerSample = testFormats[j].validBitsPerSample; - wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); + // if requested layout is not suppported, try standard layouts with at least + // the number of channels as requested + // as the last resort try stereo + if (layout == ARRAYSIZE(layoutsList)) + { + wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + wfxex.Format.nChannels = 2; + } + else if (layout >= 0) + { + wfxex.dwChannelMask = ChLayoutToChMask(layoutsList[layout], &noOfCh); + wfxex.Format.nChannels = noOfCh; + if (noOfCh < requestedChannels) + continue; + } - for (int i = 0 ; i < WASAPISampleRateCount; i++) + for (int j = 0; j < sizeof(testFormats)/sizeof(sampleFormat); j++) { - wfxex.Format.nSamplesPerSec = WASAPISampleRates[i]; - wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; - - /* Trace format match iteration loop via log */ - #if 0 - CLog::Log(LOGDEBUG, "WASAPI: Trying Format: %s, %d, %d, %d", CAEUtil::DataFormatToStr(testFormats[j].subFormatType), - wfxex.Format.nSamplesPerSec, - wfxex.Format.wBitsPerSample, - wfxex.Samples.wValidBitsPerSample); - #endif + closestMatch = -1; - hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); + wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfxex.SubFormat = testFormats[j].subFormat; + wfxex.Format.wBitsPerSample = testFormats[j].bitsPerSample; + wfxex.Samples.wValidBitsPerSample = testFormats[j].validBitsPerSample; + wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); - if (SUCCEEDED(hr)) + for (int i = 0 ; i < WASAPISampleRateCount; i++) { - /* If the current sample rate matches the source then stop looking and use it */ - if ((WASAPISampleRates[i] == format.m_sampleRate) && (testFormats[j].subFormatType <= format.m_dataFormat)) - goto initialize; - /* If this rate is closer to the source then the previous one, save it */ - else if (closestMatch < 0 || abs((int)WASAPISampleRates[i] - (int)format.m_sampleRate) < abs((int)WASAPISampleRates[closestMatch] - (int)format.m_sampleRate)) - closestMatch = i; - } - else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + wfxex.Format.nSamplesPerSec = WASAPISampleRates[i]; + wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; + + /* Trace format match iteration loop via log */ +#if 0 + CLog::Log(LOGDEBUG, "WASAPI: Trying Format: %s, %d, %d, %d", CAEUtil::DataFormatToStr(testFormats[j].subFormatType), + wfxex.Format.nSamplesPerSec, + wfxex.Format.wBitsPerSample, + wfxex.Samples.wValidBitsPerSample); +#endif + + hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); + + if (SUCCEEDED(hr)) + { + /* If the current sample rate matches the source then stop looking and use it */ + if ((WASAPISampleRates[i] == format.m_sampleRate) && (testFormats[j].subFormatType <= format.m_dataFormat)) + goto initialize; + /* If this rate is closer to the source then the previous one, save it */ + else if (closestMatch < 0 || abs((int)WASAPISampleRates[i] - (int)format.m_sampleRate) < abs((int)WASAPISampleRates[closestMatch] - (int)format.m_sampleRate)) + closestMatch = i; + } + else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s)", WASAPIErrToStr(hr)); - } + } - if (closestMatch >= 0) - { - wfxex.Format.nSamplesPerSec = WASAPISampleRates[closestMatch]; - wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; - goto initialize; + if (closestMatch >= 0) + { + wfxex.Format.nSamplesPerSec = WASAPISampleRates[closestMatch]; + wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; + goto initialize; + } } } @@ -1116,7 +1146,22 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format) initialize: - AEChannelsFromSpeakerMask(wfxex.dwChannelMask); + // check if 7.1 wide was requested and we were able to open 8 channels + if (obsolete71Wide && (wfxex.dwChannelMask == KSAUDIO_SPEAKER_7POINT1_SURROUND)) + { + // build layout for 7.1 Wide and map it into KSAUDIO_SPEAKER_7POINT1_SURROUND + m_channelLayout.Reset(); + m_channelLayout += AE_CH_FLOC; // FLOC/FROC go into FL/FR + m_channelLayout += AE_CH_FROC; + m_channelLayout += AE_CH_FC; + m_channelLayout += AE_CH_LFE; + m_channelLayout += AE_CH_FL; // FL/FR go into SL/SR + m_channelLayout += AE_CH_FR; + m_channelLayout += AE_CH_BL; + m_channelLayout += AE_CH_BR; + } + else + AEChannelsFromSpeakerMask(wfxex.dwChannelMask); format.m_channelLayout = m_channelLayout; /* When the stream is raw, the values in the format structure are set to the link */ diff --git a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp index 69b37b470b..5d4857114b 100644 --- a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp @@ -55,11 +55,13 @@ void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs) bool srcHasSR = false; bool srcHasRL = false; bool srcHasRR = false; + bool srcHasBC = false; bool dstHasSL = false; bool dstHasSR = false; bool dstHasRL = false; bool dstHasRR = false; + bool dstHasBC = false; for (unsigned int c = 0; c < rhs.m_channelCount; ++c) switch(rhs.m_channels[c]) @@ -68,6 +70,7 @@ void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs) case AE_CH_SR: dstHasSR = true; break; case AE_CH_BL: dstHasRL = true; break; case AE_CH_BR: dstHasRR = true; break; + case AE_CH_BC: dstHasBC = true; break; default: break; } @@ -81,6 +84,7 @@ void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs) case AE_CH_SR: srcHasSR = true; break; case AE_CH_BL: srcHasRL = true; break; case AE_CH_BR: srcHasRR = true; break; + case AE_CH_BC: srcHasBC = true; break; default: break; } @@ -97,6 +101,10 @@ void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs) newInfo += m_channels[i]; } + // we let the sink do the mapping later on + if (m_channelCount == 8 && m_channelCount == rhs.Count()) + return; + /* we need to ensure we end up with rear or side channels for downmix to work */ if (srcHasSL && !dstHasSL && dstHasRL) newInfo += AE_CH_BL; @@ -107,6 +115,21 @@ void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs) if (srcHasRR && !dstHasRR && dstHasSR) newInfo += AE_CH_SR; + // mix back center if not available in destination layout + // prefer mixing into backs if available + if (srcHasBC && !dstHasBC) + { + if (dstHasRL && !newInfo.HasChannel(AE_CH_BL)) + newInfo += AE_CH_BL; + else if (dstHasSL && !newInfo.HasChannel(AE_CH_SL)) + newInfo += AE_CH_SL; + + if (dstHasRR && !newInfo.HasChannel(AE_CH_BR)) + newInfo += AE_CH_BR; + else if (dstHasSR && !newInfo.HasChannel(AE_CH_SR)) + newInfo += AE_CH_SR; + } + *this = newInfo; } |