RobinSchmidt / RS-MET

Codebase for RS-MET products (Robin Schmidt's Music Engineering Tools)
Other
56 stars 6 forks source link

Do you have a function to get ~RMS~ LUFS from sample data? #251

Open elanhickler opened 6 years ago

elanhickler commented 6 years ago

I need function to do something like

vector<double> waveform;~
double rms = getRMSValue(waveform);

If you need to charge for it, bill Greg @ OrangeTreeSamples!

elanhickler commented 6 years ago

Greg would rather have LUFS! If you're going to charge for something, charge for that!

RobinSchmidt commented 6 years ago

i just added the function rsArray::rootMeanSquare. (not yet tested, but it's so simple, i can't imagine anything going wrong - edit: at least mathemathically...but i forgot to add some const modifiers in called functions....fixed that). i will look into implementing LUFS next. https://en.wikipedia.org/wiki/LKFS maybe, while i'm at it, i could also implement other standardized psychoacoustic loudness measures like dB(A), dB(B), etc...i'll have to hunt down respective standardized filter coeffs and time constants (maybe, if it's for realtime metering rather than computing a single value for the whole array), maybe this one: http://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-0-200607-S!!PDF-E.pdf

RobinSchmidt commented 6 years ago

ah - i have found analog transfer functions for the A,B,C,D filters: https://en.wikipedia.org/wiki/A-weighting#Transfer_function_equivalent and the LUFS paper above has coefficients for digital filters at 48kHz. i think, i can translate them all to any sample-rate by BLT in the analog case and frequency-warping (actually also a sort of BLT) in the digital case. i just have to figure out the details...

elanhickler commented 5 years ago

does rsArray::rootMeanSquare get the peak RMS or does it get an average RMS over the entire array?

I actually need a peak RMS

RobinSchmidt commented 5 years ago

it's an average over the entire array. peak-rms sounds a bit like an oxymoron. i guess you mean taking the maximum value of a short-time rms signal?

RobinSchmidt commented 5 years ago

i just added the function getMaxShortTimeRMS. i just implemented this quickly off the cuff and didn't test it yet - so it needs some testing now.

in case you wonder, it's not defined in rsArray because it needs a filter and i don't want rsArray to depend on filters (the rsArray class is lower level and should not not have a dependency on higher level classes - i don't want to break the layered structure of the library...not yet at least)

elanhickler commented 5 years ago

Thank you! Will test... If I can manage to merge your library with my version... Oh jeez.

RobinSchmidt commented 5 years ago

maybe just copy the function over to your codebase. it's just a few lines. i'll update my juce soon (not keen on the new vst-sdk dependency, tbh - up to 5.2, juce was more or less self-contained, which is a good thing for a library). there's even a newer version out there now (i think, 5.4 or something). today is integration time for banddiagonal solver into rapt

elanhickler commented 5 years ago

I cloned your library for my reaper plugin so now just my reaper plugin is synced with your library. But I get an error "unresolved external symbol" for rapt::rsarray::meansquare. I believe I solved this in another version of your library by just making the functions use doubles and not be a template. Can you fix this? Or tell me how to fix it?

elanhickler commented 5 years ago

https://gitlab.com/Hickler/reaper_seSampleLib

See readme for the other dependencies, just your library which already contains juce and you need seObjectiveReaper. Search for RMS in action.cpp file in the reaper_seSampleLib project to find my usage and where the error is coming from.

elanhickler commented 5 years ago

oh nevermind, I'm just using getMaxShortTimeRMS function and it builds, no errors.

RobinSchmidt commented 5 years ago

ok - does it work as expected, too?

elanhickler commented 5 years ago

not working, seems to be outputting the same value. image

my usage:

double AUDIOFUNCTION::getPeakRMS(TAKE & take, double timeWindowForPeakRMS)
{
  auto summedAudio = sumChannelModeChannels(take);

  if (take.getNumChannelModeChannels() > 1)
    for (auto & sample : summedAudio)
      sample /= double(take.getNumChannels());

  return RAPT::getMaxShortTimeRMS<double>(summedAudio.data(), summedAudio.size(), take.getSampleRate()*timeWindowForPeakRMS);
}
RobinSchmidt commented 5 years ago

hmm..weird. i just added the test-function

void maxShortTimeRMS()
{
  int N = 1000;
  std::vector<double> x = createSineWave(N, 1000.0, 48000.0);
  double maxRms = RAPT::getMaxShortTimeRMS(&x[0], N, 48); // 48 samples = 1 cycle, rms = 1/sqrt(2)
  int dummy = 0;
}

to my test project and it produces a reasonable value. it's a bit inexact (pesumably) due to leakage in my implementation of the moving average but otherwise looks ok. (just in case you wonder about all my int dummy = 0 statements in my code - they are just for setting debug breakpoints mostly because vs sometimes doesn't like breakpoints at a closing brace)

elanhickler commented 5 years ago

why would I be getting a number near infinity and the same value every time? I checked my audio data, it has normal numbers. Would a 0 value sample mess it up?

RobinSchmidt commented 5 years ago

hmm - only a zero averaging length could mess it up, as far as i can see. what's your averaging length?

RobinSchmidt commented 5 years ago

wait - i can now reproduce it - with larger averaging length....

elanhickler commented 5 years ago

0.5 * samplerate is my averaging length

RobinSchmidt commented 5 years ago

yes - as said above - i can reproduce it with larger averaging length now. hmm...my i have a bug in my delayline implementation? i will figure it out....

elanhickler commented 5 years ago

what is a typical averaging length?

elanhickler commented 5 years ago

I tried 0.01 * samplerate (441 samples) and i get a maxRMS of 0.

0.1 (4410 samples) gives a maxRMS of 2.5029777399088483e+33

So I either get 0 or a giant number!?!?!

RobinSchmidt commented 5 years ago

ok - i figured it out! when you set up a delayline length greater than currently allocated memory, i re-allocate - and i forgot to initialize with zeros after such re-allocation. how embarrassing! how could this go unnoticed for so long? now, it seems to work.

what's a typical averaging length. hmm...i would probably say something like the reciprocal of the lowest expected frequency would make sense

elanhickler commented 5 years ago

that's super embarrassing.

elanhickler commented 5 years ago

...im getting 0 maxRMS every time nowwww

edit: because my audio data is zeroes... wtf... edit: because im dividing by numChannels, and numChannels is 0... wtf...

RobinSchmidt commented 5 years ago

that's super embarrassing.

yeah, i know. that code is super old

elanhickler commented 5 years ago

YAYY it's working!

I'm not embarrassed by the way.

RobinSchmidt commented 5 years ago

i forgot to initialize with zeros after such re-allocation.

actually, i should probably copy the contents of the old delayline...hmm...but i actually assume that such re-allocations are not events that occur during regular processing...dunno

edit: ...or use a std::vector and just resize it - which takes care of all that - plus i can look at the content in the debugger

RobinSchmidt commented 5 years ago

or use a std::vector and just resize it - which takes care of all that

or wait - does it? i'm actually not quite sure. i think, i've seen it initialize in debug-builds but not in release builds or something. ...which is actually a quite bad idea to do for a compiler - it suggests, all is well and good by "helpfully" initializing in a debug build. if i were a compiler, i'd initialize all uninitialized variables to NaN in debug builds. that would be really helpful

elanhickler commented 5 years ago

LNK2019 unresolved external symbol

public: static double __cdecl RAPT::rsArray::meanSquare(double const *,int)" (??$meanSquare@N@rsArray@RAPT@@SANPEBNH@Z)

referenced in

function "public: static double __cdecl RAPT::rsArray::rootMeanSquare<double>(double const *,int)" (??$rootMeanSquare@N@rsArray@RAPT@@SANPEBNH@Z)

trying to use full waveform RMS as well, ill just not use it for now. Not sure how to fix an unresolved external symbol.. well, you have your meanSquare definition in a .cpp file which is not recommended.

Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?

https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl

RobinSchmidt commented 5 years ago

you need to explicitly instantiate each rapt-template for all datatypes that you want to use. look at

rosic_TemplateInstantiations.cpp

there, i'm doing it myself - from the perspective of rapt, rosic is client code, so to speak. your link assumes that you compile each .cpp file on its own whereas i use a unity build system in which i can make sure, the compiler sees the template definition when it needs it, even though it's in a cpp file. also, defining the templates in the header comes with its own set of problems. in some contexts, the same template gets instantiated multiple times in multiple compilation units - just imagine two .cpp files include the same .h file containing a template function or -class and instantiate the same template for the same datatype in both cpp files - bäm! multiple definition linker error

RobinSchmidt commented 5 years ago

i know, i know - it's a mess. but that's the price for writing the code in terms of templates. i think, the type independency is worth it, though. ...not mainly to have single and double precision versions (although that's a welcome plus, too) but mainly to be able to make simd-multichannel versions without additional code

elanhickler commented 5 years ago

I can build a debug version, for some reason I get these unresolved external symbols for release version:

LNK2019 unresolved external symbol "int __cdecl RAPT::rsNextPowerOfTwo<int>(int)" (??$rsNextPowerOfTwo@H@RAPT@@YAHH@Z)
referenced in function
"public: void __cdecl rosic::rsArray<class rosic::rsString>::ensureAllocatedSize(int)" (?ensureAllocatedSize@?$rsArray@VrsString@rosic@@@rosic@@QEAAXH@Z)
reaper_seSampleLib_DynamicLibrary
D:\_PROGRAMMING\reaper_seSampleLib\Builds\VisualStudio2015\include_romos.obj    1   

LNK2019 unresolved external symbol
"double __cdecl RAPT::rsWrapToInterval(double,double,double)" (?rsWrapToInterval@RAPT@@YANNNN@Z)
referenced in function
"public: double __cdecl romos::BlitSaw::getDesiredFirstSample(double,double)" (?getDesiredFirstSample@BlitSaw@romos@@QEAANNN@Z)
reaper_seSampleLib_DynamicLibrary
D:\_PROGRAMMING\reaper_seSampleLib\Builds\VisualStudio2015\include_romos.obj    1   

Error   LNK2019 unresolved external symbol "double __cdecl RAPT::rsRandomUniform(double,double,int)" (?rsRandomUniform@RAPT@@YANNNH@Z)
referenced in function
"protected: virtual void __cdecl romos::ProcessingTest::fillInputSignalArraysRandomly(int)" (?fillInputSignalArraysRandomly@ProcessingTest@romos@@MEAAXH@Z)
reaper_seSampleLib_DynamicLibrary
D:\_PROGRAMMING\reaper_seSampleLib\Builds\VisualStudio2015\include_romos.obj    1
RobinSchmidt commented 5 years ago

romos.obj? that's my modular system aka liberty, or well, the modular synth code framework, liberty is based on. do you even use that in your reaper plugin? if not, throw it out. do you use a projucer project for this too? if so, throw out the romos juce module

elanhickler commented 5 years ago

fixed

elanhickler commented 5 years ago

Like a few posts above, I'm getting error:

LNK2019 unresolved external symbol

"public: static float __cdecl RAPT::rsArray::rootMeanSquare(float const * const,int)" (??$rootMeanSquare@M@rsArray@RAPT@@SAMQEBMH@Z)

I looked at rosic_TemplateInstantiations.cpp and everything is writted for classes. I don't know the syntax for instantiating a function.

I tried this, but get error:

image

Edit: Ok, figured out the syntax. put this in the CPP file above where the function is used.

template float RAPT::rsArray::rootMeanSquare(const float *, int);

Error is still not resolved.

RobinSchmidt commented 5 years ago

hmm - this:

template float RAPT::rsArray::rootMeanSquare(const float *, int);

looks actually good. however, i have just added an "inline" to the function - maybe try again with the inline (you should then get rid of the instantiation)

elanhickler commented 5 years ago

still not building 😢

I confirmed that I got the new update and inline is there.

RobinSchmidt commented 5 years ago

ah - i think, is see what's wrong. the rootMeanSquare function calls the meanSquare function, so you need an instantiation of that inner function as well. i just added

template double RAPT::rsArray::meanSquare(const double *x, int N);

to rosic and also added a little test to confirm that it builds (which it does now, after adding this). if you need it for float, add another instantiation for float. do you actually have a custom (additional) instantiation file in your codebase? the idea for the rapt library is that client code may just provide its own instantiation file, in cases when it needs some additional (or different) template instantiations, that i don't need myself

elanhickler commented 5 years ago

I need float. So I put that in the cpp file?

RobinSchmidt commented 5 years ago

if you have your own fork, you can put it there right next to the double instantiation. however, in the long run, i would recommend to just create your own additional cpp file somewhere in your framework/juce-module for the additional instantiations that are specific to your codebase

elanhickler commented 5 years ago

I don't know the first thing about solving this problem.

Where did you place template double RAPT::rsArray::meanSquare(const double *x, int N), what's the exact file name?

Adding my own template instantiation to a cpp file doesn't solve the problem, I don't know how this works or how I can make my own instantiation file.

Edit: How is it that I have not added rosic to my juce modules yet it works by adding an instantiation to a rosic file?

Edit: Ok I tried adding a float instantiation to rosic_TemplateInstantiations.cpp, still having unresolved external symbol build error for meanSquare

RobinSchmidt commented 5 years ago

How is it that I have not added rosic to my juce modules yet it works by adding an instantiation to a rosic file? Edit: Ok I tried adding a float instantiation to rosic_TemplateInstantiations.cpp, still having unresolved external symbol build error for meanSquare

oh - if you don't compile the rosic module, then placing the instantiation there should not help indeed. maybe i should try this myself with your codebase? an instantiation file is nothing really special - just a regular cpp file inside of which all the required explicit instantiations are collected - and which is compiled as part of one of your juce modules

elanhickler commented 5 years ago

https://github.com/gregjazz/SeSALT

image

RobinSchmidt commented 5 years ago

i'm getting a bunch of compiler errors: image so i don't even get to the linker stage

RobinSchmidt commented 5 years ago

i could fix the ones about the AdvancedLeakDetector by adding using namespace juce; before the class definition. but the files for the "dockable-windows" stuff are just not there in the codebase. the folder is empty. did you forget to add them and have them only locally?

elanhickler commented 5 years ago

it's a submodule, not exactly sure how that works.

https://github.com/jcredland/dockable-windows

image

image

RobinSchmidt commented 5 years ago

oookay! i added a template instantiation file to the project and modified the jucer file accordingly. if you re-generate the visual studio project from the jucer file, it should build now

it's a submodule, not exactly sure how that works.

i just manually added the required files to my local copy. i also don't know, how git submodules are supposed to work - if they are supposed to automatically being dragged in and if so, how. i'd perhaps just add copies of the files to the repo. i think, a repo should ideally be self-contained and not depend on external sources. ...that obviously may mean a lot of code duplication...dunno