Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
elanhickler opened this issue Oct 22, 2018 · 47 comments
Open

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

elanhickler opened this issue Oct 22, 2018 · 47 comments

Comments

@elanhickler
Copy link
Contributor

elanhickler commented Oct 22, 2018

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
Copy link
Contributor Author

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

@elanhickler elanhickler changed the title Do you have a function to get RMS from sample data? Do you have a function to get ~RMS~ LUFS from sample data? Oct 22, 2018
@elanhickler elanhickler changed the title Do you have a function to get ~RMS~ LUFS from sample data? Do you have a function to get <s>RMS</s> LUFS from sample data? Oct 22, 2018
@elanhickler elanhickler changed the title Do you have a function to get <s>RMS</s> LUFS from sample data? Do you have a function to get ~RMS~ LUFS from sample data? Oct 22, 2018
@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Oct 23, 2018

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
Copy link
Owner

RobinSchmidt commented Oct 23, 2018

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
Copy link
Contributor Author

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Contributor Author

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

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Feb 23, 2019

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@RobinSchmidt
Copy link
Owner

ok - does it work as expected, too?

@elanhickler
Copy link
Contributor Author

elanhickler commented Mar 2, 2019

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
Copy link
Owner

RobinSchmidt commented Mar 4, 2019

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
Copy link
Contributor Author

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
Copy link
Owner

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

@RobinSchmidt
Copy link
Owner

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

@elanhickler
Copy link
Contributor Author

0.5 * samplerate is my averaging length

@RobinSchmidt
Copy link
Owner

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
Copy link
Contributor Author

what is a typical averaging length?

@elanhickler
Copy link
Contributor Author

elanhickler commented Mar 4, 2019

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
Copy link
Owner

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
Copy link
Contributor Author

that's super embarrassing.

@elanhickler
Copy link
Contributor Author

elanhickler commented Mar 4, 2019

...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
Copy link
Owner

that's super embarrassing.

yeah, i know. that code is super old

@elanhickler
Copy link
Contributor Author

YAYY it's working!

I'm not embarrassed by the way.

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Mar 4, 2019

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
Copy link
Owner

RobinSchmidt commented Mar 4, 2019

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
Copy link
Contributor Author

elanhickler commented Mar 4, 2019

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
Copy link
Owner

RobinSchmidt commented Mar 4, 2019

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
Copy link
Owner

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
Copy link
Contributor Author

elanhickler commented Mar 4, 2019

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
Copy link
Owner

RobinSchmidt commented Mar 4, 2019

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
Copy link
Contributor Author

fixed

@elanhickler
Copy link
Contributor Author

elanhickler commented Apr 28, 2019

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
Copy link
Owner

RobinSchmidt commented Apr 29, 2019

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
Copy link
Contributor Author

elanhickler commented Apr 29, 2019

still not building 😢

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

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Apr 29, 2019

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
Copy link
Contributor Author

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

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Apr 29, 2019

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
Copy link
Contributor Author

elanhickler commented Apr 29, 2019

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
Copy link
Owner

RobinSchmidt commented Apr 29, 2019

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
Copy link
Contributor Author

@RobinSchmidt
Copy link
Owner

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

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented Apr 30, 2019

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
Copy link
Contributor Author

elanhickler commented Apr 30, 2019

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

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

image

image

@RobinSchmidt
Copy link
Owner

RobinSchmidt commented May 1, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants