[rbp/omxplayer] Add Dynamic Range Compression scheme
authorpopcornmix <popcornmix@gmail.com>
Wed, 4 Sep 2013 12:38:57 +0000 (13:38 +0100)
committerpopcornmix <popcornmix@gmail.com>
Fri, 6 Sep 2013 18:07:52 +0000 (19:07 +0100)
This adds a DRC scheme. A number of dBs of amplification can be chosen.
The audio_decode component is setup to do the usual downmix operation early
before most of the audio buffering. This means we get a second or two to
detect if the downmix would overflow. This allows the amplification to be
attenuated in time to avoid the clipping.
The attenuation gradually decays when overflow condition stops.

Also adds a gui setting to boost centre channel.

Requires updated firmware

language/English/strings.po
system/settings/rbp.xml
xbmc/cores/omxplayer/OMXAudio.cpp
xbmc/cores/omxplayer/OMXAudio.h
xbmc/cores/omxplayer/OMXPlayer.cpp
xbmc/cores/omxplayer/OMXPlayerAudio.h

index 3f957b0..0e7ecd5 100644 (file)
@@ -14337,6 +14337,11 @@ msgctxt "#36542"
 msgid "Output to both analogue (headphones) and HDMI"
 msgstr ""
 
+#: system/settings/rbp.xml
+msgctxt "#36543"
+msgid "Enable this to make dialogue louder compared to background sounds when downmixing multichannel audio"
+msgstr ""
+
 #reserved strings 365XX
 
 #: xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
@@ -14390,3 +14395,8 @@ msgstr ""
 msgctxt "#37017"
 msgid "Dual audio output"
 msgstr ""
+
+#: system/settings/rbp.xml
+msgctxt "#37018"
+msgid "Boost centre channel when downmixing"
+msgstr ""
index 5293091..ddd6635 100644 (file)
           <level>2</level>
           <default>false</default>
         </setting>
+        <setting id="audiooutput.boostcentre" type="boolean" label="37018" help="36543">
+          <level>2</level>
+          <default>false</default>
+        </setting>
       </group>
       <group id="2">
         <visible>false</visible>
index d2864b9..cbf04ef 100644 (file)
@@ -89,6 +89,19 @@ const float downmixing_coefficients_8[OMX_AUDIO_MAXCHANNELS] = {
   /* Rr */  0,      0.7071
 };
 
+// 7.1 downmixing coefficients with boosted centre channel
+const float downmixing_coefficients_8_boostcentre[OMX_AUDIO_MAXCHANNELS] = {
+  //        L       R
+  /* L */   0.7071, 0,
+  /* R */   0,      0.7071,
+  /* C */   1,      1,
+  /* LFE */ 0.7071, 0.7071,
+  /* Ls */  0.7071, 0,
+  /* Rs */  0,      0.7071,
+  /* Lr */  0.7071, 0,
+  /* Rr */  0,      0.7071
+};
+
 //////////////////////////////////////////////////////////////////////
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
@@ -106,6 +119,10 @@ COMXAudio::COMXAudio() :
   m_ChunkLen        (0      ),
   m_OutputChannels  (0      ),
   m_BitsPerSample   (0      ),
+  m_maxLevel        (0.0f   ),
+  m_amplification   (1.0f   ),
+  m_attenuation     (1.0f   ),
+  m_desired_attenuation(1.0f),
   m_omx_clock       (NULL   ),
   m_av_clock        (NULL   ),
   m_settings_changed(false  ),
@@ -200,6 +217,7 @@ bool COMXAudio::PortSettingsChanged()
       return false;
   }
 
+  SetDynamicRangeCompression((long)(CMediaSettings::Get().GetCurrentVideoSettings().m_VolumeAmplification * 100));
   ApplyVolume();
 
   if( m_omx_mixer.IsInitialized() )
@@ -518,7 +536,10 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
   memset(&m_wave_header, 0x0, sizeof(m_wave_header));
 
   for(int i = 0; i < OMX_AUDIO_MAXCHANNELS; i++)
+  {
     m_pcm_input.eChannelMapping[i] = OMX_AUDIO_ChannelNone;
+    m_input_channels[i] = OMX_AUDIO_ChannelMax;
+  }
 
   m_output_channels[0] = OMX_AUDIO_ChannelLF;
   m_output_channels[1] = OMX_AUDIO_ChannelRF;
@@ -846,6 +867,10 @@ void COMXAudio::Flush()
 //***********************************************************************************************
 void COMXAudio::SetDynamicRangeCompression(long drc)
 {
+  CSingleLock lock (m_critSection);
+  m_amplification = powf(10.0f, (float)drc / 2000.0f);
+  if (m_settings_changed)
+    ApplyVolume();
 }
 
 //***********************************************************************************************
@@ -876,87 +901,68 @@ bool COMXAudio::ApplyVolume(void)
 
   float fVolume = m_Mute ? VOLUME_MINIMUM : m_CurrentVolume;
 
+  // the analogue volume is too quiet for some. Allow use of an advancedsetting to boost this (at risk of distortion) (deprecated)
   double gain = pow(10, (g_advancedSettings.m_ac3Gain - 12.0f) / 20.0);
+  double r = 1.0;
+  const float* coeff = downmixing_coefficients_8;
 
-  if (m_format.m_channelLayout.Count() > 2)
-  {
-    double r = fVolume;
-    const float* coeff = downmixing_coefficients_8;
+  // alternate coffeciciants that boost centre channel more
+  if(!CSettings::Get().GetBool("audiooutput.boostcentre"))
+    coeff = downmixing_coefficients_8_boostcentre;
 
-    // normally we normalalise the levels, can be skipped (boosted) at risk of distortion
-    if(!CSettings::Get().GetBool("audiooutput.normalizelevels"))
+  // normally we normalise the levels, can be skipped (boosted) at risk of distortion
+  if(!CSettings::Get().GetBool("audiooutput.normalizelevels"))
+  {
+    double sum_L = 0;
+    double sum_R = 0;
+    for(size_t i = 0; i < OMX_AUDIO_MAXCHANNELS; ++i)
     {
-      double sum_L = 0;
-      double sum_R = 0;
+      if (m_input_channels[i] == OMX_AUDIO_ChannelMax)
+        break;
+      if(i & 1)
+        sum_R += coeff[i];
+      else
+        sum_L += coeff[i];
+    }
 
-      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);
+  }
+  r *= gain;
 
-      r /= max(sum_L, sum_R);
-    }
+  OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS mix;
+  OMX_INIT_STRUCTURE(mix);
+  OMX_ERRORTYPE omx_err;
 
-    // the analogue volume is too quiet for some. Allow use of an advancedsetting to boost this (at risk of distortion)
-    r *= gain;
+  assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 16);
 
-    OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS mix;
-    OMX_INIT_STRUCTURE(mix);
-    mix.nPortIndex = m_omx_mixer.GetInputPort();
+  // reduce scaling so overflow can be seen
+  for(size_t i = 0; i < 16; ++i)
+    mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r * 0.01f));
 
-    assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 16);
+  mix.nPortIndex = m_omx_decoder.GetInputPort();
+  omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients, &mix);
+  if(omx_err != OMX_ErrorNone)
+  {
+    CLog::Log(LOGERROR, "%s::%s - error setting decoder OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
+              CLASSNAME, __func__, omx_err);
+    return false;
+  }
 
+  if (m_amplification != 1.0)
+  {
     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);
+      mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r * fVolume * m_amplification * m_attenuation));
 
+    mix.nPortIndex = m_omx_mixer.GetInputPort();
+    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",
+      CLog::Log(LOGERROR, "%s::%s - error setting mixer OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
                 CLASSNAME, __func__, omx_err);
       return false;
     }
   }
-  else
-  {
-    OMX_AUDIO_CONFIG_VOLUMETYPE volume;
-    OMX_INIT_STRUCTURE(volume);
-
-    volume.bLinear    = OMX_TRUE;
-    float hardwareVolume = fVolume * gain * 100.0f;
-    volume.sVolume.nValue = (int)(hardwareVolume + 0.5f);
-
-    if(m_omx_render_analog.IsInitialized())
-    {
-      volume.nPortIndex = m_omx_render_analog.GetInputPort();
-      OMX_ERRORTYPE omx_err = m_omx_render_analog.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;
-      }
-    }
-    if(m_omx_render_hdmi.IsInitialized())
-    {
-      volume.nPortIndex = m_omx_render_hdmi.GetInputPort();
-      OMX_ERRORTYPE omx_err = m_omx_render_hdmi.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;
-      }
-    }
-  }
-  CLog::Log(LOGINFO, "%s::%s - Volume=%.2f\n", CLASSNAME, __func__, fVolume);
+  CLog::Log(LOGINFO, "%s::%s - Volume=%.2f (* %.2f * %.2f)\n", CLASSNAME, __func__, fVolume, m_amplification, m_attenuation);
   return true;
 }
 
@@ -1171,6 +1177,27 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
     }
   }
 
+  if (m_amplification != 1.0)
+  {
+    double level_pts = 0.0;
+    float level = GetMaxLevel(level_pts);
+    if (level_pts != 0.0)
+    {
+      float alpha_h = -1.0f/(0.025f*log10f(0.999f));
+      float alpha_r = -1.0f/(0.100f*log10f(0.900f));
+      float hold    = powf(10.0f, -1.0f / (alpha_h * g_advancedSettings.m_limiterHold));
+      float release = powf(10.0f, -1.0f / (alpha_r * g_advancedSettings.m_limiterRelease));
+      m_maxLevel = level > m_maxLevel ? level : hold * m_maxLevel + (1.0f-hold) * level;
+
+      float amp = m_amplification * m_desired_attenuation;
+
+      // want m_maxLevel * amp -> 1.0
+      m_desired_attenuation = std::min(1.0f, std::max(m_desired_attenuation / (amp * m_maxLevel), 1.0f/m_amplification));
+      m_attenuation = release * m_attenuation + (1.0f-release) * m_desired_attenuation;
+
+      ApplyVolume();
+    }
+  }
   return len;
 }
 
@@ -1269,6 +1296,33 @@ unsigned int COMXAudio::GetAudioRenderingLatency()
   return param.nU32;
 }
 
+float COMXAudio::GetMaxLevel(double &pts)
+{
+  CSingleLock lock (m_critSection);
+
+  if(!m_Initialized)
+    return 0;
+
+  OMX_CONFIG_BRCMAUDIOMAXSAMPLE param;
+  OMX_INIT_STRUCTURE(param);
+
+  if(m_omx_decoder.IsInitialized())
+  {
+    param.nPortIndex = m_omx_decoder.GetInputPort();
+
+    OMX_ERRORTYPE omx_err = m_omx_decoder.GetConfig(OMX_IndexConfigBrcmAudioMaxSample, &param);
+
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigBrcmAudioMaxSample error 0x%08x\n",
+        CLASSNAME, __func__, omx_err);
+      return 0;
+    }
+  }
+  pts = FromOMXTime(param.nTimeStamp);
+  return (float)param.nMaxSample * (100.0f / (1<<15));
+}
+
 void COMXAudio::SubmitEOS()
 {
   CSingleLock lock (m_critSection);
index 09993e6..f287b0a 100644 (file)
@@ -72,6 +72,7 @@ public:
   void SetVolume(float nVolume);
   void SetMute(bool bOnOff);
   void SetDynamicRangeCompression(long drc);
+  float GetDynamicRangeAmplification() { return 20.0f * log10f(m_amplification * m_attenuation); }
   bool ApplyVolume();
   int SetPlaySpeed(int iSpeed);
   void SubmitEOS();
@@ -94,6 +95,7 @@ public:
 
   bool BadState() { return !m_Initialized; };
   unsigned int GetAudioRenderingLatency();
+  float GetMaxLevel(double &pts);
   void VizPacket(const void* data, unsigned int len, double pts);
 
 private:
@@ -109,6 +111,10 @@ private:
   unsigned int  m_ChunkLen;
   unsigned int  m_OutputChannels;
   unsigned int  m_BitsPerSample;
+  float         m_maxLevel;
+  float         m_amplification;
+  float         m_attenuation;
+  float         m_desired_attenuation;
   COMXCoreComponent *m_omx_clock;
   OMXClock       *m_av_clock;
   bool          m_settings_changed;
index dbe8ba5..6c13e35 100644 (file)
@@ -2875,7 +2875,7 @@ void COMXPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
         strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
     }
 
-    strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s af:%d%% vf:%d%% )"
+    strGeneralInfo.Format("C( ad:% 6.3f a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s af:%d%% vf:%d%% amp:% 5.2f )"
                          , m_omxPlayerAudio.GetDelay()
                          , dDiff
                          , strEDL.c_str()
@@ -2884,7 +2884,8 @@ void COMXPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
                          , (int)(m_omxPlayerVideo.GetRelativeUsage()*100)
                          , strBuf.c_str()
                          , m_audio_fifo
-                         , m_video_fifo);
+                         , m_video_fifo
+                         , m_omxPlayerAudio.GetDynamicRangeAmplification());
 
   }
 }
@@ -4464,6 +4465,7 @@ void COMXPlayer::GetAudioCapabilities(std::vector<int> &audioCaps)
   audioCaps.push_back(IPC_AUD_OFFSET);
   audioCaps.push_back(IPC_AUD_SELECT_STREAM);
   audioCaps.push_back(IPC_AUD_SELECT_OUTPUT);
+  audioCaps.push_back(IPC_AUD_AMP);
 }
 
 void COMXPlayer::GetSubtitleCapabilities(std::vector<int> &subCaps)
index fd8873b..19f50f7 100644 (file)
@@ -112,6 +112,7 @@ public:
   void SetVolume(float fVolume)                          { m_omxAudio.SetVolume(fVolume); }
   void SetMute(bool bOnOff)                              { m_omxAudio.SetMute(bOnOff); }
   void SetDynamicRangeCompression(long drc)              { m_omxAudio.SetDynamicRangeCompression(drc); }
+  float GetDynamicRangeAmplification()                   { return m_omxAudio.GetDynamicRangeAmplification(); }
   void SetSpeed(int iSpeed);
   int  GetAudioBitrate();
   std::string GetPlayerInfo();