surroundize

Tool/PipeWire filter to convert stereo audio to surround
git clone https://git.neptards.moe/u3shit/surroundize.git
Log | Files | Refs | README | LICENSE

ffdshow_decoder.cpp (8905B)


      1 /*
      2  * Copyright (c) 2004-2006 Milan Cutka
      3  * based on mplayer HRTF plugin by ylai
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program; if not, write to the Free Software
     17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18  */
     19 
     20 #include "ffdshow_decoder.hpp"
     21 
     22 #include "firfilter.hpp"
     23 
     24 #include <cmath>
     25 #include <cstring>
     26 
     27 float FfdshowDecoder::passive_lock(float x)
     28 {
     29     static const float MATAGCLOCK = 0.2f;  /* AGC range (around 1) where the matrix behaves passively */
     30     const float x1 = x - 1;
     31     const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
     32     return x1 - x1 / (1 + ax1s * ax1s) + 1;
     33 }
     34 
     35 void FfdshowDecoder::matrix_decode(const float *in, const int k, const int il,
     36         const int ir, bool decode_rear,
     37         const int dlbuflen,
     38         float l_fwr, float r_fwr,
     39         float lpr_fwr, float lmr_fwr,
     40         float *adapt_l_gain, float *adapt_r_gain,
     41         float *adapt_lpr_gain, float *adapt_lmr_gain,
     42         float *lf, float *rf, float *lr,
     43         float *rr, float *cf) const
     44 {
     45     static const float M9_03DB = 0.3535533906f;
     46     static const float MATAGCTRIG = 8.0f;  /* (Fuzzy) AGC trigger */
     47     static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */
     48     static const float MATCOMPGAIN = 0.37f; /* Cross talk compensation gain,  0.50 - 0.55 is full cancellation. */
     49 
     50     const int kr = k % dlbuflen;
     51     float l_gain = (l_fwr + r_fwr) / (1 + l_fwr + l_fwr);
     52     float r_gain = (l_fwr + r_fwr) / (1 + r_fwr + r_fwr);
     53     /* The 2nd axis has strong gain fluctuations, and therefore require
     54        limits.  The factor corresponds to the 1 / amplification of (Lt
     55        - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
     56        dialogues).  It should be bigger than -12 dB to prevent
     57        distortion. */
     58     float lmr_lim_fwr = lmr_fwr > M9_03DB * lpr_fwr ? lmr_fwr : M9_03DB * lpr_fwr;
     59     float lpr_gain = (lpr_fwr + lmr_lim_fwr) / (1 + lpr_fwr + lpr_fwr);
     60     float lmr_gain = (lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr);
     61     float lmr_unlim_gain = (lpr_fwr + lmr_fwr) / (1 + lmr_fwr + lmr_fwr);
     62     float lpr, lmr;
     63     float l_agc, r_agc, lpr_agc, lmr_agc;
     64     float f, d_gain, c_gain, c_agc_cfk;
     65 
     66     /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
     67     /* AGC adaption */
     68     d_gain = (fabs(l_gain - *adapt_l_gain) + fabs(r_gain - *adapt_r_gain)) * 0.5f;
     69     f = d_gain * (1.0f / MATAGCTRIG);
     70     f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
     71     *adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain;
     72     *adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain;
     73     /* Matrix */
     74     l_agc = in[il] * passive_lock(*adapt_l_gain);
     75     r_agc = in[ir] * passive_lock(*adapt_r_gain);
     76     cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
     77     if (decode_rear) {
     78         lr[kr] = rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
     79         /* Stereo rear channel is steered with the same AGC steering as
     80            the decoding matrix. Note this requires a fast updating AGC
     81            at the order of 20 ms (which is the case here). */
     82         lr[kr] *= (l_fwr + l_fwr) / (1 + l_fwr + r_fwr);
     83         rr[kr] *= (r_fwr + r_fwr) / (1 + l_fwr + r_fwr);
     84     }
     85 
     86     /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
     87     lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
     88     lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
     89     /* AGC adaption */
     90     d_gain = fabs(lmr_unlim_gain - *adapt_lmr_gain);
     91     f = d_gain * (1.0f / MATAGCTRIG);
     92     f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
     93     *adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain;
     94     *adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain;
     95     /* Matrix */
     96     lpr_agc = lpr * passive_lock(*adapt_lpr_gain);
     97     lmr_agc = lmr * passive_lock(*adapt_lmr_gain);
     98     lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
     99     rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
    100 
    101     /*** CENTER FRONT CANCELLATION ***/
    102     /* A heuristic approach exploits that Lt + Rt gain contains the
    103        information about Lt, Rt correlation.  This effectively reshapes
    104        the front and rear "cones" to concentrate Lt + Rt to C and
    105        introduce Lt - Rt in L, R. */
    106     /* 0.67677 is the empirical lower bound for lpr_gain. */
    107     c_gain = 8 * (*adapt_lpr_gain - 0.67677f);
    108     c_gain = c_gain > 0 ? c_gain : 0;
    109     /* c_gain should not be too high, not even reaching full
    110        cancellation (~ 0.50 - 0.55 at current AGC implementation), or
    111        the center will sound too narrow. */
    112     c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
    113     c_agc_cfk = c_gain * cf[k];
    114     lf[k] -= c_agc_cfk;
    115     rf[k] -= c_agc_cfk;
    116     cf[k] += c_agc_cfk + c_agc_cfk;
    117 }
    118 
    119 std::unique_ptr<float[]> FfdshowDecoder::calc_coefficients_125Hz_lowpass()
    120 {
    121   len125 = 256;
    122   float f = rate * (125.0f / 2);
    123   std::unique_ptr<float[]> coeffs{TfirFilter::design_fir(
    124       &len125, &f, TfirFilter::Type::LOWPASS, TfirFilter::Window::HAMMING, 0)};
    125   static const float M3_01DB = 0.7071067812f;
    126   for (unsigned int i = 0; i < len125; i++) {
    127     coeffs[i] *= M3_01DB;
    128   }
    129   return coeffs;
    130 }
    131 
    132 static const unsigned int FWRDURATION = 240;    /* FWR average duration (samples) */
    133 
    134 std::span<const float> FfdshowDecoder::Decode(std::span<const float> input)
    135 {
    136   constexpr unsigned out_channels = 6;
    137 
    138   const float* in = input.data(); // Input audio data
    139   const float* end = in + input.size(); // Loop end
    140   out_buf.resize(out_channels * (input.size() / 2));
    141   float* out = out_buf.data();
    142   while (in < end) {
    143     const int k = cyc_pos;
    144 
    145     const int fwr_pos = (k + FWRDURATION) % dlbuflen;
    146     /* Update the full wave rectified total amplitude */
    147     /* Input matrix decoder */
    148     l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]);
    149     r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]);
    150     lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]);
    151     lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
    152 
    153     /* Matrix encoded 2 channel sources */
    154     fwrbuf_l[k] = in[0];
    155     fwrbuf_r[k] = in[1];
    156     matrix_decode(in, k, 0, 1, true, dlbuflen,
    157                   l_fwr, r_fwr,
    158                   lpr_fwr, lmr_fwr,
    159                   &adapt_l_gain, &adapt_r_gain,
    160                   &adapt_lpr_gain, &adapt_lmr_gain,
    161                   &lf[0], &rf[0], &lr[0], &rr[0], &cf[0]);
    162 
    163     out[0] = lf[k];
    164     out[1] = rf[k];
    165     out[2] = center_gain * cf[k];
    166     if (enable_lfe)
    167     {
    168       LFE_buf[lfe_pos] = (out[0] + out[1]) / 2;
    169       out[3] = TfirFilter::firfilter(
    170         LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe.get());
    171       lfe_pos++;
    172       if (lfe_pos == len125) lfe_pos = 0;
    173     }
    174     else out[3] = 0;
    175     out[4] = lr[k];
    176     out[5] = rr[k];
    177     // Next sample...
    178     in += 2;
    179     out += out_channels;
    180     cyc_pos--;
    181     if (cyc_pos < 0) {
    182       cyc_pos += dlbuflen;
    183     }
    184   }
    185 
    186   return out_buf;
    187 }
    188 
    189 void FfdshowDecoder::Init(float rate)
    190 {
    191   if (rate < 0) return;
    192   l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
    193   std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f);
    194   std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f);
    195   adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0;
    196   std::fill(lf.begin(), lf.end(), 0.0f);
    197   std::fill(rf.begin(), rf.end(), 0.0f);
    198   std::fill(lr.begin(), lr.end(), 0.0f);
    199   std::fill(rr.begin(), rr.end(), 0.0f);
    200   std::fill(cf.begin(), cf.end(), 0.0f);
    201   std::fill(cr.begin(), cr.end(), 0.0f);
    202   lfe_pos = 0;
    203   memset(LFE_buf, 0, sizeof(LFE_buf));
    204   filter_coefs_lfe.reset();
    205 
    206   this->rate = rate;
    207   //dlbuflen = std::max(FWRDURATION, (cfg_delay / rate / 1000)); //+(len7000-1);
    208   dlbuflen = FWRDURATION; // TODO delay is always 0
    209   cyc_pos = dlbuflen - 1;
    210   fwrbuf_l.resize(dlbuflen);
    211   fwrbuf_r.resize(dlbuflen);
    212   lf.resize(dlbuflen);
    213   rf.resize(dlbuflen);
    214   lr.resize(dlbuflen);
    215   rr.resize(dlbuflen);
    216   cf.resize(dlbuflen);
    217   cr.resize(dlbuflen);
    218   filter_coefs_lfe = calc_coefficients_125Hz_lowpass();
    219 }
    220 
    221 std::vector<Option> FfdshowDecoder::GetOptions()
    222 {
    223   return {
    224     {
    225       "enable_lfe", "BOOL", "Enable LFE output (default: true)",
    226       [this](std::string_view sv) { enable_lfe = FromString<bool>(sv); },
    227     },
    228     {
    229       "center_gain", "FLOAT", "Center gain (default: 1)",
    230       [this](std::string_view sv) { center_gain = FromString<float>(sv); },
    231     },
    232   };
    233 }
    234 
    235 std::vector<Channel::E> FfdshowDecoder::GetChannels()
    236 {
    237   return {Channel::FRONT_LEFT, Channel::FRONT_RIGHT, Channel::FRONT_CENTER,
    238           Channel::LFE, Channel::REAR_LEFT, Channel::REAR_RIGHT};
    239 }