kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

ebu.cpp (11468B)


      1 /**
      2  * KFR (https://www.kfrlib.com)
      3  * Copyright (C) 2016-2023 Dan Cazarin
      4  * See LICENSE.txt for details
      5  */
      6 
      7 #include <kfr/dsp/ebu.hpp>
      8 #include <kfr/dsp/oscillators.hpp>
      9 
     10 CMT_PRAGMA_MSVC(warning(push))
     11 CMT_PRAGMA_MSVC(warning(disable : 4244))
     12 
     13 namespace kfr
     14 {
     15 inline namespace CMT_ARCH_NAME
     16 {
     17 
     18 struct TestFragment
     19 {
     20     float gain; // dB
     21     float duration; // seconds
     22     float frequency; // Hz
     23 };
     24 
     25 struct TestFragmentMultichannel
     26 {
     27     float gain_L_R; // dB
     28     float gain_C; // dB
     29     float gain_Ls_Rs; // dB
     30     float duration; // seconds
     31     float frequency; // Hz
     32 };
     33 
     34 template <typename T>
     35 static void ebu_test_stereo(int sample_rate, const std::initializer_list<TestFragment>& fragments, T refM,
     36                             T refS, T refI, T refLRA)
     37 {
     38     ebu_r128<T> loudness(sample_rate, { Speaker::Left, Speaker::Right });
     39 
     40     size_t total_length = 0;
     41     for (const TestFragment& f : fragments)
     42     {
     43         total_length += static_cast<size_t>(f.duration * sample_rate);
     44     }
     45 
     46     univector<T> left_right(total_length);
     47     size_t pos = 0;
     48     for (const TestFragment& f : fragments)
     49     {
     50         const size_t len           = static_cast<size_t>(f.duration * sample_rate);
     51         left_right.slice(pos, len) = dB_to_amp(f.gain) * sinenorm(phasor<float>(f.frequency, sample_rate));
     52         pos += len;
     53     }
     54 
     55     for (size_t i = 0; i < total_length / loudness.packet_size(); i++)
     56     {
     57         loudness.process_packet({ left_right.slice(i * loudness.packet_size(), loudness.packet_size()),
     58                                   left_right.slice(i * loudness.packet_size(), loudness.packet_size()) });
     59     }
     60     T M, S, I, RL, RH;
     61     loudness.get_values(M, S, I, RL, RH);
     62     if (!std::isnan(refM))
     63     {
     64         testo::scope s(as_string("M = ", fmt<'f', -1, 2>(M)));
     65         CHECK(std::abs(M - refM) < 0.05f);
     66     }
     67     if (!std::isnan(refS))
     68     {
     69         testo::scope s(as_string("S = ", fmt<'f', -1, 2>(S)));
     70         CHECK(std::abs(S - refS) < 0.05f);
     71     }
     72     if (!std::isnan(refI))
     73     {
     74         testo::scope s(as_string("I = ", fmt<'f', -1, 2>(I)));
     75         CHECK(std::abs(I - refI) < 0.05f);
     76     }
     77     if (!std::isnan(refLRA))
     78     {
     79         testo::scope s(as_string("LRA = ", fmt<'f', -1, 2>((RH - RL))));
     80         CHECK(std::abs((RH - RL) - refLRA) < 0.05f);
     81     }
     82 }
     83 
     84 template <typename T>
     85 static void ebu_test_multichannel(int sample_rate,
     86                                   const std::initializer_list<TestFragmentMultichannel>& fragments, T refM,
     87                                   T refS, T refI, T refLRA)
     88 {
     89     ebu_r128<T> loudness(sample_rate, { Speaker::Left, Speaker::Right, Speaker::Center, Speaker::LeftSurround,
     90                                         Speaker::RightSurround });
     91 
     92     size_t total_length = 0;
     93     for (const TestFragmentMultichannel& f : fragments)
     94     {
     95         total_length += static_cast<size_t>(f.duration * sample_rate);
     96     }
     97 
     98     univector<T> left_right(total_length);
     99     univector<T> center(total_length);
    100     univector<T> surround(total_length);
    101     size_t pos = 0;
    102     for (const TestFragmentMultichannel& f : fragments)
    103     {
    104         const size_t len = static_cast<size_t>(f.duration * sample_rate);
    105         left_right.slice(pos, len) =
    106             dB_to_amp(f.gain_L_R) * sinenorm(phasor<float>(f.frequency, sample_rate));
    107         center.slice(pos, len) = dB_to_amp(f.gain_C) * sinenorm(phasor<float>(f.frequency, sample_rate));
    108         surround.slice(pos, len) =
    109             dB_to_amp(f.gain_Ls_Rs) * sinenorm(phasor<float>(f.frequency, sample_rate));
    110         pos += len;
    111     }
    112 
    113     for (size_t i = 0; i < total_length / loudness.packet_size(); i++)
    114     {
    115         loudness.process_packet({ left_right.slice(i * loudness.packet_size(), loudness.packet_size()),
    116                                   left_right.slice(i * loudness.packet_size(), loudness.packet_size()),
    117                                   center.slice(i * loudness.packet_size(), loudness.packet_size()),
    118                                   surround.slice(i * loudness.packet_size(), loudness.packet_size()),
    119                                   surround.slice(i * loudness.packet_size(), loudness.packet_size()) });
    120     }
    121     T M, S, I, RL, RH;
    122     loudness.get_values(M, S, I, RL, RH);
    123     if (!std::isnan(refM))
    124     {
    125         testo::scope s(as_string("M = ", fmt<'f', -1, 2>(M)));
    126         CHECK(std::abs(M - refM) < 0.1f);
    127     }
    128     if (!std::isnan(refS))
    129     {
    130         testo::scope s(as_string("S = ", fmt<'f', -1, 2>(S)));
    131         CHECK(std::abs(S - refS) < 0.1f);
    132     }
    133     if (!std::isnan(refI))
    134     {
    135         testo::scope s(as_string("I = ", fmt<'f', -1, 2>(I)));
    136         CHECK(std::abs(I - refI) < 0.1f);
    137     }
    138     if (!std::isnan(refLRA))
    139     {
    140         testo::scope s(as_string("LRA = ", fmt<'f', -1, 2>((RH - RL))));
    141         CHECK(std::abs((RH - RL) - refLRA) < 0.1f);
    142     }
    143 }
    144 
    145 TEST(ebu_stereo_1_and_2)
    146 {
    147     testo::matrix(named("type")        = ctypes_t<float, double>{},
    148                   named("sample_rate") = std::vector<int>{ 44100, 48000 },
    149                   [](auto type, int sample_rate)
    150                   {
    151                       using T = typename decltype(type)::type;
    152 
    153                       ebu_test_stereo<T>(sample_rate, { { -23.f, 20.f, 1000.f } }, -23.f, -23.f, -23.f, NAN);
    154                       ebu_test_stereo<T>(sample_rate, { { -33.f, 20.f, 1000.f } }, -33.f, -33.f, -33.f, NAN);
    155                   });
    156 }
    157 
    158 TEST(ebu_stereo_3_4_and_5)
    159 {
    160     testo::matrix(
    161         named("type") = ctypes_t<float, double>{}, named("sample_rate") = std::vector<int>{ 44100, 48000 },
    162         [](auto type, int sample_rate)
    163         {
    164             using T = typename decltype(type)::type;
    165 
    166             ebu_test_stereo<T>(sample_rate,
    167                                { { -36.f, 10.f, 1000.f }, { -23.f, 60.f, 1000.f }, { -36.f, 10.f, 1000.f } },
    168                                NAN, NAN, -23.f, NAN);
    169             ebu_test_stereo<T>(sample_rate,
    170                                { { -72.f, 10.f, 1000.f },
    171                                  { -36.f, 10.f, 1000.f },
    172                                  { -23.f, 60.f, 1000.f },
    173                                  { -36.f, 10.f, 1000.f },
    174                                  { -72.f, 10.f, 1000.f } },
    175                                NAN, NAN, -23.f, NAN);
    176         });
    177 }
    178 
    179 TEST(ebu_multichannel_6)
    180 {
    181     testo::matrix(named("type")        = ctypes_t<float, double>{},
    182                   named("sample_rate") = std::vector<int>{ 44100, 48000 },
    183                   [](auto type, int sample_rate)
    184                   {
    185                       using T = typename decltype(type)::type;
    186 
    187                       ebu_test_multichannel<T>(sample_rate, { { -28.f, -24.f, -30.f, 20.f, 1000.f } }, NAN,
    188                                                NAN, -23.f, NAN);
    189                   });
    190 }
    191 
    192 TEST(ebu_stereo_9)
    193 {
    194     testo::matrix(named("type")        = ctypes_t<float, double>{},
    195                   named("sample_rate") = std::vector<int>{ 44100, 48000 },
    196                   [](auto type, int sample_rate)
    197                   {
    198                       using T = typename decltype(type)::type;
    199 
    200                       ebu_test_stereo<T>(sample_rate,
    201                                          { { -20.f, 1.34f, 1000.f },
    202                                            { -30.f, 1.66f, 1000.f },
    203                                            { -20.f, 1.34f, 1000.f },
    204                                            { -30.f, 1.66f, 1000.f },
    205                                            { -20.f, 1.34f, 1000.f },
    206                                            { -30.f, 1.66f, 1000.f },
    207                                            { -20.f, 1.34f, 1000.f },
    208                                            { -30.f, 1.66f, 1000.f },
    209                                            { -20.f, 1.34f, 1000.f },
    210                                            { -30.f, 1.66f, 1000.f } },
    211                                          NAN, -23.f, NAN, NAN);
    212                   });
    213 }
    214 
    215 TEST(ebu_stereo_12)
    216 {
    217     testo::matrix(
    218         named("type") = ctypes_t<float, double>{}, named("sample_rate") = std::vector<int>{ 44100, 48000 },
    219         [](auto type, int sample_rate)
    220         {
    221             using T = typename decltype(type)::type;
    222 
    223             ebu_test_stereo<T>(sample_rate,
    224                                { { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    225                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    226                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    227                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    228                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    229                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    230                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    231                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    232                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    233                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    234                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    235                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    236                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    237                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    238                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f },
    239                                  { -30.f, 0.22f, 1000.f }, { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f },
    240                                  { -20.f, 0.18f, 1000.f }, { -30.f, 0.22f, 1000.f } },
    241                                -23.f, NAN, NAN, NAN);
    242         });
    243 }
    244 
    245 TEST(ebu_lra_1_2_3_and_4)
    246 {
    247     testo::matrix(named("type")        = ctypes_t<float, double>{},
    248                   named("sample_rate") = std::vector<int>{ 44100, 48000 },
    249                   [](auto type, int sample_rate)
    250                   {
    251                       using T = typename decltype(type)::type;
    252 
    253                       ebu_test_stereo<T>(sample_rate, { { -20.f, 20.f, 1000.f }, { -30.f, 20.f, 1000.f } },
    254                                          NAN, NAN, NAN, 10.f);
    255 
    256                       ebu_test_stereo<T>(sample_rate, { { -20.f, 20.f, 1000.f }, { -15.f, 20.f, 1000.f } },
    257                                          NAN, NAN, NAN, 5.f);
    258 
    259                       ebu_test_stereo<T>(sample_rate, { { -40.f, 20.f, 1000.f }, { -20.f, 20.f, 1000.f } },
    260                                          NAN, NAN, NAN, 20.f);
    261 
    262                       ebu_test_stereo<T>(sample_rate,
    263                                          { { -50.f, 20.f, 1000.f },
    264                                            { -35.f, 20.f, 1000.f },
    265                                            { -20.f, 20.f, 1000.f },
    266                                            { -35.f, 20.f, 1000.f },
    267                                            { -50.f, 20.f, 1000.f } },
    268                                          NAN, NAN, NAN, 15.f);
    269                   });
    270 }
    271 } // namespace CMT_ARCH_NAME
    272 
    273 } // namespace kfr
    274 
    275 CMT_PRAGMA_MSVC(warning(pop))