Merge pull request #4950 from Memphiz/osxfixoptical
authorMemphiz <memphis@machzwo.de>
Sun, 29 Jun 2014 19:36:37 +0000 (21:36 +0200)
committerTrent Nelson <trent.nelson@pivosgroup.com>
Tue, 8 Jul 2014 07:26:52 +0000 (15:26 +0800)
[AE/osxsink] - fix wrong samplerate selection

Makefile.in
xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp
xbmc/cores/AudioEngine/Sinks/test/Makefile [new file with mode: 0644]
xbmc/cores/AudioEngine/Sinks/test/TestAESinkDARWINOSX.cpp [new file with mode: 0644]

index bee1fdd..6c55e67 100644 (file)
@@ -302,6 +302,7 @@ CHECK_LIBS = xbmc/filesystem/test/filesystemTest.a \
              xbmc/utils/test/utilsTest.a \
              xbmc/threads/test/threadTest.a \
              xbmc/interfaces/python/test/pythonSwigTest.a \
+             xbmc/cores/AudioEngine/Sinks/test/AESinkTest.a \
              xbmc/test/xbmc-test.a
 
 ifeq (@USE_WAYLAND@,1)
index c3b67d9..030f8f2 100644 (file)
@@ -589,6 +589,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;
@@ -624,10 +637,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())
@@ -722,7 +733,7 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
 
       if (score > outputScore)
       {
-        passthrough  = score > 1000;
+        passthrough  = score > 10000;
         outputScore  = score;
         outputFormat = desc;
         outputStream = *i;
diff --git a/xbmc/cores/AudioEngine/Sinks/test/Makefile b/xbmc/cores/AudioEngine/Sinks/test/Makefile
new file mode 100644 (file)
index 0000000..b37c5f4
--- /dev/null
@@ -0,0 +1,9 @@
+SRCS=TestAESinkDARWINOSX.cpp
+
+#move this out of the if block if needed
+LIB=AESinkTest.a
+
+INCLUDES += -I../../../../../lib/gtest/include
+
+include ../../../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/cores/AudioEngine/Sinks/test/TestAESinkDARWINOSX.cpp b/xbmc/cores/AudioEngine/Sinks/test/TestAESinkDARWINOSX.cpp
new file mode 100644 (file)
index 0000000..0431678
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ *      Copyright (C) 2014 Team XBMC
+ *      http://xbmc.org
+ *
+ *  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, 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 XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "gtest/gtest.h"
+
+#if defined(TARGET_DARWIN_OSX)
+#include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include <vector>
+
+extern float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format);
+
+std::vector<AudioStreamBasicDescription> stereoFormatsWithPassthrough;
+std::vector<AudioStreamBasicDescription> stereoFormatsWithoutPassthrough;
+std::vector<AudioStreamBasicDescription> allFormatsWithPassthrough;
+std::vector<AudioStreamBasicDescription> allFormatsWithoutPassthrough;
+
+void addLPCMFormats(std::vector<AudioStreamBasicDescription> &streamFormats)
+{
+  AudioStreamBasicDescription streamFormat;
+
+  FillOutASBDForLPCM(streamFormat, 96000, 8, 16, 16, false, false, false);
+  streamFormats.push_back(streamFormat); // + 0
+
+  FillOutASBDForLPCM(streamFormat, 48000, 8, 16, 16, false, false, false);
+  streamFormats.push_back(streamFormat); // + 1
+
+  FillOutASBDForLPCM(streamFormat, 44100, 8, 16, 16, false, false, false);
+  streamFormats.push_back(streamFormat); // + 2
+
+  FillOutASBDForLPCM(streamFormat, 96000, 8, 20, 20, false, false, false);
+  streamFormats.push_back(streamFormat); // + 3
+
+  FillOutASBDForLPCM(streamFormat, 48000, 8, 20, 20, false, false, false);
+  streamFormats.push_back(streamFormat); // + 4
+
+  FillOutASBDForLPCM(streamFormat, 44100, 8, 20, 20, false, false, false);
+  streamFormats.push_back(streamFormat); // + 5
+
+  FillOutASBDForLPCM(streamFormat, 96000, 8, 24, 24, false, false, false);
+  streamFormats.push_back(streamFormat); // + 6
+
+  FillOutASBDForLPCM(streamFormat, 48000, 8, 24, 24, false, false, false);
+  streamFormats.push_back(streamFormat); // + 7
+
+  FillOutASBDForLPCM(streamFormat, 44100, 8, 24, 24, false, false, false);
+  streamFormats.push_back(streamFormat); // + 8
+}
+
+void addPassthroughFormats(std::vector<AudioStreamBasicDescription> &streamFormats)
+{
+  AudioStreamBasicDescription streamFormat;
+
+  FillOutASBDForLPCM(streamFormat, 96000, 2, 16, 16, false, false, false);
+  streamFormat.mFormatID = kAudioFormat60958AC3;
+  streamFormats.push_back(streamFormat); // stereoFormtsWithoutPassthrough.size() + 0
+
+  FillOutASBDForLPCM(streamFormat, 48000, 2, 16, 16, false, false, false);
+  streamFormat.mFormatID = kAudioFormat60958AC3;
+  streamFormats.push_back(streamFormat); // stereoFormtsWithoutPassthrough.size() + 1
+
+  FillOutASBDForLPCM(streamFormat, 44100, 2, 16, 16, false, false, false);
+  streamFormat.mFormatID = kAudioFormat60958AC3;
+  streamFormats.push_back(streamFormat); // stereoFormtsWithoutPassthrough.size() + 2
+}
+
+void initStereoFormatsWithoutPassthrough()
+{
+  AudioStreamBasicDescription streamFormat;
+  FillOutASBDForLPCM(streamFormat, 96000, 2, 16, 16, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 0
+
+  FillOutASBDForLPCM(streamFormat, 48000, 2, 16, 16, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 1
+
+  FillOutASBDForLPCM(streamFormat, 44100, 2, 16, 16, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 2
+
+  FillOutASBDForLPCM(streamFormat, 96000, 2, 20, 20, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 3
+
+  FillOutASBDForLPCM(streamFormat, 48000, 2, 20, 20, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 4
+
+  FillOutASBDForLPCM(streamFormat, 44100, 2, 20, 20, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 5
+
+  FillOutASBDForLPCM(streamFormat, 96000, 2, 24, 24, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 6
+
+  FillOutASBDForLPCM(streamFormat, 48000, 2, 24, 24, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 7
+
+  FillOutASBDForLPCM(streamFormat, 44100, 2, 24, 24, false, false, false);
+  stereoFormatsWithoutPassthrough.push_back(streamFormat); // 8
+}
+
+void initStreamFormats()
+{
+  stereoFormatsWithPassthrough.clear();
+  stereoFormatsWithoutPassthrough.clear();
+  allFormatsWithPassthrough.clear();
+  allFormatsWithoutPassthrough.clear();
+
+  initStereoFormatsWithoutPassthrough();
+
+  stereoFormatsWithPassthrough = stereoFormatsWithoutPassthrough;
+  allFormatsWithoutPassthrough = stereoFormatsWithoutPassthrough;
+
+  addLPCMFormats( allFormatsWithoutPassthrough);
+
+  allFormatsWithPassthrough = allFormatsWithoutPassthrough;
+
+  addPassthroughFormats(stereoFormatsWithPassthrough);
+  addPassthroughFormats(allFormatsWithPassthrough);
+}
+
+
+AEAudioFormat getAC3AEFormat()
+{
+  AEAudioFormat srcFormat;
+  srcFormat.m_dataFormat = AE_FMT_AC3;
+  srcFormat.m_sampleRate = 48000;
+  srcFormat.m_encodedRate = 48000;
+  srcFormat.m_channelLayout = AE_CH_LAYOUT_2_0;
+  srcFormat.m_frames = 0;
+  srcFormat.m_frameSamples = 0;
+  srcFormat.m_frameSize = 0;
+  return srcFormat;
+}
+
+AEAudioFormat getStereo22050AEFormat()
+{
+  AEAudioFormat srcFormat;
+  srcFormat.m_dataFormat = AE_FMT_FLOAT;
+  srcFormat.m_sampleRate = 22050;
+  srcFormat.m_encodedRate = 0;
+  srcFormat.m_channelLayout = AE_CH_LAYOUT_2_0;
+  srcFormat.m_frames = 0;
+  srcFormat.m_frameSamples = 0;
+  srcFormat.m_frameSize = 0;
+  return srcFormat;
+}
+
+AEAudioFormat getStereo48000AEFormat()
+{
+  AEAudioFormat srcFormat;
+  srcFormat.m_dataFormat = AE_FMT_FLOAT;
+  srcFormat.m_sampleRate = 48000;
+  srcFormat.m_encodedRate = 0;
+  srcFormat.m_channelLayout = AE_CH_LAYOUT_2_0;
+  srcFormat.m_frames = 0;
+  srcFormat.m_frameSamples = 0;
+  srcFormat.m_frameSize = 0;
+  return srcFormat;
+}
+
+AEAudioFormat getLPCM96000AEFormat()
+{
+  AEAudioFormat srcFormat;
+  srcFormat.m_dataFormat = AE_FMT_FLOAT;
+  srcFormat.m_sampleRate = 96000;
+  srcFormat.m_encodedRate = 0;
+  srcFormat.m_channelLayout = AE_CH_LAYOUT_5_1;
+  srcFormat.m_frames = 0;
+  srcFormat.m_frameSamples = 0;
+  srcFormat.m_frameSize = 0;
+  return srcFormat;
+}
+
+unsigned int findMatchingFormat(const std::vector<AudioStreamBasicDescription> &formatList, const AEAudioFormat &srcFormat)
+{
+  unsigned int formatIdx = 0;
+  float highestScore = 0;
+  float currentScore = 0;
+
+//  fprintf(stderr, "%s: Matching streamFormat for source: %s with samplerate: %d\n", __FUNCTION__, CAEUtil::DataFormatToStr(srcFormat.m_dataFormat), srcFormat.m_sampleRate);
+  for (unsigned int i = 0; i < formatList.size(); i++)
+  {
+    AudioStreamBasicDescription desc = formatList[i];
+    std::string formatString;
+    currentScore = ScoreStream(desc, srcFormat);
+//    fprintf(stderr, "%s: Physical Format: %s idx: %d rated %f\n", __FUNCTION__, StreamDescriptionToString(desc, formatString), i, currentScore);
+
+    if (currentScore > highestScore)
+    {
+      formatIdx = i;
+      highestScore = currentScore;
+    }
+  }
+
+  return formatIdx;
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchAc3InStereoWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  //try to match the stream formats for ac3
+  AEAudioFormat srcFormat = getAC3AEFormat();
+  // mach ac3 in streamformats with dedicated passthrough format
+  formatIdx = findMatchingFormat(stereoFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, stereoFormatsWithoutPassthrough.size() + 1);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchAc3InStereoWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  //try to match the stream formats for ac3
+  AEAudioFormat srcFormat = getAC3AEFormat();
+  //match ac3 in streamformats without dedicated passthrough format (bitstream)
+  formatIdx = findMatchingFormat(stereoFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)1);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchAc3InAllWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  //try to match the stream formats for ac3
+  AEAudioFormat srcFormat = getAC3AEFormat();
+  // mach ac3 in streamformats with dedicated passthrough format
+  formatIdx = findMatchingFormat(allFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx,allFormatsWithoutPassthrough.size() + 1);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchAc3InAllWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  //try to match the stream formats for ac3
+  AEAudioFormat srcFormat = getAC3AEFormat();
+  //match ac3 in streamformats without dedicated passthrough format (bitstream)
+  formatIdx = findMatchingFormat(allFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)1);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo22050InStereoWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 22050hz
+  AEAudioFormat srcFormat = getStereo22050AEFormat();
+  formatIdx = findMatchingFormat(stereoFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)8);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo22050InStereoWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 22050hz
+  AEAudioFormat srcFormat = getStereo22050AEFormat();
+
+  formatIdx = findMatchingFormat(stereoFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)8);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo22050InAllWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 22050hz
+  AEAudioFormat srcFormat = getStereo22050AEFormat();
+  formatIdx = findMatchingFormat(allFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)8);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo22050InAllWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 22050hz
+  AEAudioFormat srcFormat = getStereo22050AEFormat();
+
+  formatIdx = findMatchingFormat(allFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)8);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo48000InStereoWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 48000hz
+  AEAudioFormat srcFormat = getStereo48000AEFormat();
+  formatIdx = findMatchingFormat(stereoFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)7);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo48000InStereoWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 48000hz
+  AEAudioFormat srcFormat = getStereo48000AEFormat();
+
+  formatIdx = findMatchingFormat(stereoFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)7);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo48000InAllWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 48000hz
+  AEAudioFormat srcFormat = getStereo48000AEFormat();
+  formatIdx = findMatchingFormat(allFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)7);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloatStereo48000InAllWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match stereo float 48000hz
+  AEAudioFormat srcFormat = getStereo48000AEFormat();
+
+  formatIdx = findMatchingFormat(allFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)7);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloat5_1_96000InStereoWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match lpcm float 96000hz
+  AEAudioFormat srcFormat = getLPCM96000AEFormat();
+  formatIdx = findMatchingFormat(stereoFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)6);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloat5_1_96000InStereoWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match lpcm float 96000hz
+  AEAudioFormat srcFormat = getLPCM96000AEFormat();
+
+  formatIdx = findMatchingFormat(stereoFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)6);
+}
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloat5_1_96000InAllWithPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match lpcm float 96000hz
+  AEAudioFormat srcFormat = getLPCM96000AEFormat();
+  formatIdx = findMatchingFormat(allFormatsWithPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)15);
+}
+
+
+TEST(TestAESinkDARWINOSXScoreStream, MatchFloat5_1_96000InAllWithoutPassthroughFormats)
+{
+  unsigned int formatIdx = 0;
+  initStreamFormats();
+
+  // match lpcm float 96000hz
+  AEAudioFormat srcFormat = getLPCM96000AEFormat();
+
+  formatIdx = findMatchingFormat(allFormatsWithoutPassthrough, srcFormat);
+  EXPECT_EQ(formatIdx, (unsigned int)15);
+}
+#endif //TARGET_DARWIN_OSX