Apply volume control at audio_mixer when possible
authorpopcornmix <popcornmix@gmail.com>
Tue, 15 Jan 2013 20:55:48 +0000 (20:55 +0000)
committerS. Davilla <davilla@4pi.com>
Fri, 18 Jan 2013 15:25:00 +0000 (10:25 -0500)
xbmc/cores/omxplayer/OMXAudio.cpp

index 1b2a1ef..10c3329 100644 (file)
 #include "guilib/LocalizeStrings.h"
 #include "cores/AudioEngine/Utils/AEConvert.h"
 
-#ifndef VOLUME_MINIMUM
-#define VOLUME_MINIMUM -6000  // -60dB
-#endif
-
 using namespace std;
 
 #define OMX_MAX_CHANNELS 10
@@ -77,6 +73,19 @@ static const uint16_t AC3FSCod   [] = {48000, 44100, 32000, 0};
 
 static const uint16_t DTSFSCod   [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0};
 
+// 7.1 downmixing coefficients
+const float downmixing_coefficients_8[OMX_AUDIO_MAXCHANNELS] = {
+  //        L       R
+  /* L */   1,      0,
+  /* R */   0,      1,
+  /* C */   0.7071, 0.7071,
+  /* LFE */ 0.7071, 0.7071,
+  /* Ls */  0.7071, 0,
+  /* Rs */  0,      0.7071,
+  /* Lr */  0.7071, 0,
+  /* Rr */  0,      0.7071
+};
+
 //////////////////////////////////////////////////////////////////////
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
@@ -198,8 +207,6 @@ bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *
 
   m_drc         = 0;
 
-  m_CurrentVolume = g_settings.m_fVolumeLevel; 
-
   memset(m_input_channels, 0x0, sizeof(m_input_channels));
   memset(m_output_channels, 0x0, sizeof(m_output_channels));
   memset(&m_wave_header, 0x0, sizeof(m_wave_header));
@@ -545,8 +552,6 @@ bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *
   m_first_frame   = true;
   m_last_pts      = DVD_NOPTS_VALUE;
 
-  SetCurrentVolume(m_CurrentVolume);
-
   CLog::Log(LOGDEBUG, "COMXAudio::Initialize Ouput bps %d samplerate %d channels %d buffer size %d bytes per second %d", 
       (int)m_pcm_output.nBitPerSample, (int)m_pcm_output.nSamplingRate, (int)m_pcm_output.nChannels, m_BufferLen, m_BytesPerSec);
   CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d buffer size %d bytes per second %d", 
@@ -694,20 +699,76 @@ bool COMXAudio::SetCurrentVolume(float fVolume)
   CSingleLock lock (m_critSection);
 
   if(!m_Initialized || m_Passthrough)
-    return -1;
-
+    return false;
+  double gain = pow(10, (g_advancedSettings.m_ac3Gain - 12.0f) / 20.0);
   m_CurrentVolume = fVolume;
 
-  OMX_AUDIO_CONFIG_VOLUMETYPE volume;
-  OMX_INIT_STRUCTURE(volume);
-  volume.nPortIndex = m_omx_render->GetInputPort();
+  if (m_format.m_channelLayout.Count() > 2)
+  {
+    double r = fVolume;
+    const float* coeff = downmixing_coefficients_8;
+    int input_channels = 0;
+
+    // normally we normalalise the levels, can be skipped (boosted) at risk of distortion
+    if(!g_guiSettings.GetBool("audiooutput.normalizelevels"))
+    {
+      double sum_L = 0;
+      double sum_R = 0;
+
+      for(size_t i = 0; i < OMX_AUDIO_MAXCHANNELS; ++i)
+      {
+        if (m_input_channels[i] == OMX_AUDIO_ChannelMax)
+          break;
+        if(i & 1)
+          sum_R += coeff[i];
+        else
+          sum_L += coeff[i];
+      }
+
+      r /= max(sum_L, sum_R);
+    }
+
+    // the analogue volume is too quiet for some. Allow use of an advancedsetting to boost this (at risk of distortion)
+    r *= gain;
 
-  volume.bLinear    = OMX_TRUE;
-  float hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, fVolume)) * 100.0f;
-  volume.sVolume.nValue = (int)hardwareVolume;
+    OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS mix;
+    OMX_INIT_STRUCTURE(mix);
+    mix.nPortIndex = m_omx_mixer.GetInputPort();
 
-  m_omx_render->SetConfig(OMX_IndexConfigAudioVolume, &volume);
+    assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 16);
 
+    for(size_t i = 0; i < 16; ++i)
+      mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r));
+
+    OMX_ERRORTYPE omx_err =
+      m_omx_mixer.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients, &mix);
+
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "%s::%s - error setting OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
+                CLASSNAME, __func__, omx_err);
+      return false;
+    }
+  }
+  else
+  {
+    OMX_AUDIO_CONFIG_VOLUMETYPE volume;
+    OMX_INIT_STRUCTURE(volume);
+    volume.nPortIndex = m_omx_render->GetInputPort();
+
+    volume.bLinear    = OMX_TRUE;
+    float hardwareVolume = fVolume * gain * 100.0f;
+    volume.sVolume.nValue = (int)(hardwareVolume + 0.5f);
+
+    OMX_ERRORTYPE omx_err =
+      m_omx_render->SetConfig(OMX_IndexConfigAudioVolume, &volume);
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "%s::%s - error setting OMX_IndexConfigAudioVolume, error 0x%08x\n",
+                CLASSNAME, __func__, omx_err);
+      return false;
+    }
+  }  
   return true;
 }
 
@@ -887,16 +948,6 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
           }
         }
 
-        if ((m_pcm_input.nChannels > m_pcm_output.nChannels) &&g_guiSettings.GetBool("audiooutput.normalizelevels"))
-        {
-          OMX_AUDIO_CONFIG_VOLUMETYPE volume;
-          OMX_INIT_STRUCTURE(volume);
-          volume.nPortIndex = m_omx_mixer.GetInputPort();
-          volume.bLinear    = OMX_FALSE;
-          volume.sVolume.nValue = (int)(g_advancedSettings.m_ac3Gain*100.0f+0.5f);
-          m_omx_mixer.SetConfig(OMX_IndexConfigAudioVolume, &volume);
-        }
-
         memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
         m_pcm_input.nSamplingRate = m_format.m_sampleRate;