RS-MET icon indicating copy to clipboard operation
RS-MET copied to clipboard

cleaning up redundant code

Open RobinSchmidt opened this issue 6 years ago • 65 comments

i just started to get rid of some code that is (for historical reasons) present in both rosic and rapt. i want to keep only the (templatized) rapt versions. that's a quite big clean up task, so i'll do it step by step incrementally (or should i say decrementally?) on the side. just for info. if some rosic function or class disappears, look for the replacement function in rapt

RobinSchmidt avatar Oct 02 '18 11:10 RobinSchmidt

ok thanks!

elanhickler avatar Oct 02 '18 12:10 elanhickler

moved a lot of code already into the attic: rosic\legacy\MovedToRAPT feels like a spring-cleaning. still lots of grunt work to do. having these double structures is the main thing that holds me back from straightening out the API inconsistencies - so when i'm done, i'm in a better position to actually (finally) offer a clean(er) API :-)

RobinSchmidt avatar Oct 05 '18 22:10 RobinSchmidt

What is an example of cleaner API?

elanhickler avatar Oct 05 '18 23:10 elanhickler

what's the difference between these and which one should I use?:

    LOWPASS_IIT,      // lowpass via impulse invariant transform
    HIGHPASS_MZT,     // highpass via matched-Z transform
    LOWPASS_BLT,      // lowpass via bilinear transform
    HIGHPASS_BLT,     // highpass via bilinear transform

elanhickler avatar Oct 06 '18 09:10 elanhickler

you removed OnePoleStereo, are you creating a generic class for any number channels?

elanhickler avatar Oct 06 '18 13:10 elanhickler

new mac error MetaControlledParameter::SetValue symbol not found for RAPT::rsClip<double,double>

elanhickler avatar Oct 06 '18 14:10 elanhickler

the impulse-invariant transform discretizes the analog prototype filter in such a way, that the digital impulse response is equal to what you would get when you just sample the analog impulse response. this is most suitable for time domain work. the matched-z transform uses the same formula also for the zeros of the filter (not only the poles) - it's not impulse invariant anymore but it's the natural generalization of IIT for filters that have zeros. the bilinear transform preserves the relationship between magnitude and phase of the analog filter. it features this sort of frequency-warping/cramping of frequencies near the nyquist limit. what to use - i think, if the filter is to be used for frequency domain work and you also have oversampling, than BLT is probably the standard way to go

RobinSchmidt avatar Oct 06 '18 16:10 RobinSchmidt

have a look at the files rosic/basics/rosic_TemplateInstantiations.h/cpp. there, i have now consolidated all the template instantiations. there's also this:

class rsOnePoleFilterStereo : public RAPT::rsOnePoleFilter<rsFloat64x2, double>
{
public:
  inline void getSampleFrameStereo(double *inL, double *inR, double *outL, double *outR)
  {
    rsFloat64x2 tmp = getSample(rsFloat64x2(*inL, *inR));
    *outL = tmp[0];
    *outR = tmp[1];
  }
};

this is now the stereo version of the one pole filter (parameters are still standard doubles but the signal is the SIMD 2-doubles-vector type). it should use the same cpu load as the mono version (i didn't measure in this case, but i once measured the same thing for EngineersFilter where it indeed worked)

RobinSchmidt avatar Oct 06 '18 16:10 RobinSchmidt

new mac error MetaControlledParameter::SetValue symbol not found for RAPT::rsClip<double,double>

ok - i inlined the function and dragged it into the header file. that should fix it

RobinSchmidt avatar Oct 06 '18 17:10 RobinSchmidt

error is still there and there is two other errors that I didn't mention earlier, commenting them out solved the problem:

duplicate explicit instantiation of RAPT:rsOnePoleFilteR<double, double> and RAPT::rsOnePoleFilter<rsFloat64x2, double>

You'll get this error when you build ToolChain on mac, but the Clip error is unique to RaPG.

elanhickler avatar Oct 06 '18 17:10 elanhickler

comment out? what? where? if you exactly know how to fix it, can you just do it and send me a pull request? working on mac is always such a pita over here (slow, unresponsive, not my main dev machine, etc.)

RobinSchmidt avatar Oct 06 '18 17:10 RobinSchmidt

When I say comment out, i mean comment out the class instantiation is rosic_TemplateInstantiations.cpp, that's not a solution, only a temporary fix... or maybe it is the solution? I dunno.

elanhickler avatar Oct 06 '18 17:10 elanhickler

If I knew how to fix it, I would.

elanhickler avatar Oct 06 '18 17:10 elanhickler

you mean, like this:

template class RAPT::rsOnePoleFilter<double, double>;
//template class RAPT::rsOnePoleFilter<rsFloat64x2, double>;
template class RAPT::rsSmoothingFilter<double, double>;

RobinSchmidt avatar Oct 06 '18 17:10 RobinSchmidt

yes, except BOTH OnePoleFilters are throwing errors.

elanhickler avatar Oct 06 '18 17:10 elanhickler

so in my synthesizers, I should use IIT/MZT.

elanhickler avatar Oct 06 '18 17:10 elanhickler

you are using oversampling there, right? so the blt warping issue is probably not a big deal. so for a synth filter, i'd probably use BLT. IIT is nice for envelope generators/followers, i.e. time domain stuff. ....but actually, it probably doesn't make a big difference anyway

RobinSchmidt avatar Oct 06 '18 18:10 RobinSchmidt

...now I understand what you mean by time vs frequency.

elanhickler avatar Oct 06 '18 18:10 elanhickler

going to do this from now on so that if you change things again like this, I can just rename the function in one place.

image

elanhickler avatar Oct 09 '18 00:10 elanhickler

oh no wonder we are getting errors with OnePoleFilter, did you mean to make the <rsFloat64x2,double> version the SAME NAME as the <double,double> version?

image

edit: wait im confused, you're just creating multiple instantiations of the class. So I'll try this:

image

elanhickler avatar Oct 09 '18 00:10 elanhickler

yes - such things like making thin wrappers are generally a good idea - especially when dealing with an API that has not yet stabilized

RobinSchmidt avatar Oct 09 '18 00:10 RobinSchmidt

robin, make up your mind! lol

image

elanhickler avatar Oct 09 '18 06:10 elanhickler

yes yes yes....i know - that's what i mean with cleaning up the API...well...one of those things. there are still many other inconsistencies as well

RobinSchmidt avatar Oct 09 '18 13:10 RobinSchmidt

OMGODD ardfodiufhndjh!!! I can't compile my function aliases because you already have them in MoreMath.h and since they are .h files they cause unresolved / undefined / already defined errors.

Sitting here trying to figure wtf is going on, damn Visual Studio doesn't tell you where the error is coming from.

I may have to invent my own names for functions or maybe ill try namespacing.

elanhickler avatar Oct 09 '18 14:10 elanhickler

MoreMath.h? ...that's an ages old file that is not used anymore...is it even included somewhere? i don't think so. that's actually supposed to be dead code that's just still sitting there...just in case i may want for some unimaginable reason look at it again

RobinSchmidt avatar Oct 09 '18 14:10 RobinSchmidt

ardfodiufhndjh!!!

image

RobinSchmidt avatar Oct 09 '18 14:10 RobinSchmidt

How do you put things in namespaces without the namespace syntax? You have whole .h files in namespaces or something. Do you have a .h file with namespace {} and then you include .h files inside those brackets?

elanhickler avatar Oct 09 '18 14:10 elanhickler

it's because the file that includes them wraps the namespace around the include statements, like in rapt/Math/Math.h:

namespace RAPT
{
#include "Misc/LinearAlgebra.h"
#include "Misc/Statistics.h"
#include "Misc/CurveFitting.h"
// ....blah blah blah....

RobinSchmidt avatar Oct 09 '18 14:10 RobinSchmidt

image

elanhickler avatar Oct 09 '18 14:10 elanhickler

f*** I've tried everything. Namespacing, not namespacing, using RAPT directly. inlining, not inlining, function definitions C++11 style, C++98 style, keeping the definition in .h, putting definition in cpp, still getting unresolved external symbols.

Using RAPT directly is working, need to get rid of the errors then try defining a function one at a time.

elanhickler avatar Oct 09 '18 15:10 elanhickler

should i take a look myself? if so, which project should i attempt to build?

RobinSchmidt avatar Oct 09 '18 16:10 RobinSchmidt

build Chaosfly, my functions are defined at the top of basic.h which is in ElanSynthLib/SynthesizerComponents/

Try swithing RAPT::rsPitchToFreq to pitchToFreq or elan::pitchToFreq, line 48 of ChaosflyCore.cpp

elanhickler avatar Oct 09 '18 16:10 elanhickler

ok - i see. you are getting a lot of name clashes. i'll try to fix...

RobinSchmidt avatar Oct 09 '18 16:10 RobinSchmidt

ok - done. chaosfly builds again. maybe there will be more problems popping up when building other projects. i will try to get rid of jura_MiscTools.h and directly use RAPT functions in my jura code instead

hah! there's even a "ToDo" comment for this already in the file...

RobinSchmidt avatar Oct 09 '18 16:10 RobinSchmidt

still can't use elan::pitchToFreq in Chaosfly... just going to use RAPT, let's figure this out another time.

edit: but it's working in RhythmAndPitchGenerator. 😕

elanhickler avatar Oct 09 '18 17:10 elanhickler

you mean this:

void ChaosflyCore::setOctaveOffset(double v)
{
  frequency = elan::pitchToFreq(elan::freqToPitch(incomingFreq) + tuneOffset + (octaveOffset = v*12));
  //....
}

...compiles without problems here

RobinSchmidt avatar Oct 09 '18 19:10 RobinSchmidt

Could you put a jassert check or automatic conversion to acceptable XML variable for parameter names? Here's exampel code. Basically, no spaces, some special characters are bad( &< >'"/ ), and cannot begin with a number, so if it begins with a number you can add an underscore at the beginning, or throw an error.

String convertToAcceptableXMLVariable(String s)
{
  s = s.replaceCharacters("&<\' \"/>", "_______");

  for (const auto & c :{ '0','1','2','3','4','5','6','7','8','9' })
  {
    if (s[0] == c)
    {
      s = "_"+s;
      break;
    }
  }

  return s;
}

elanhickler avatar Oct 09 '18 20:10 elanhickler

ok, i could be convinced to include a jassert check in Parameter::setName. i think, (silently) "auto-correcting" what client code gives me would be somehow bad. juce XmlElement has the function isValidXmlName ...but that works for xml tag names, but parameters use attributes, so we have to write some isValidXmlAttributeName function ourselves. i tried to find an authoritative source for what is legal in xml attribute names and what's not. ...i only found this, so far: http://www.informit.com/articles/article.aspx?p=27865&seqNum=11 ...it's a start, but maybe not a definitive authority. i actually wonder why juce::XmlElement doesn't have such a function already. looks like an obvious omission

RobinSchmidt avatar Oct 10 '18 16:10 RobinSchmidt

ok, done:

bool isValidXmlAttributeName(const juce::String& s)
{
  int N = s.length();
  if(N == 0)
    return false;
  if(!isLetter(s[0]))
    return false;
  for(int i = 1; i < N; i++)
    if(!isLetterOrDigit(s[i]))
      return false;
  return true;
}

...not sure, if it might be too strict (i.e. may trigger false alert for actually valid names). it accepts only names that contain only letters and digits and start with a letter.

RobinSchmidt avatar Oct 10 '18 17:10 RobinSchmidt

I'm having a bit of trouble with the new onepolefilters. Explosions, nans, infs. Here's one example:

image

elanhickler avatar Oct 10 '18 18:10 elanhickler

ok, done:

bool isValidXmlAttributeName(const juce::String& s)
{
  int N = s.length();
  if(N == 0)
    return false;
  if(!isLetter(s[0]))
    return false;
  for(int i = 1; i < N; i++)
    if(!isLetterOrDigit(s[i]))
      return false;
  return true;
}

...not sure, if it might be too strict (i.e. may trigger false alert for actually valid names). it accepts only names that contain only letters and digits and start with a letter.

you need to allow for underscore

elanhickler avatar Oct 10 '18 19:10 elanhickler

you need to allow for underscore

ok, it should accept underscores now.

how can i reproduce the NaNs?

RobinSchmidt avatar Oct 10 '18 19:10 RobinSchmidt

run chaosfly, play a midi note. Happens right away.

elanhickler avatar Oct 10 '18 19:10 elanhickler

it just plays a sine'ish tone here. edit: your inputSignal variable is already nan

RobinSchmidt avatar Oct 10 '18 19:10 RobinSchmidt

Do you know why all my plugins fail au validation? made a thread: https://forum.juce.com/t/auvaltool-fail-format-errors-cannot-perform-render-tests/29800

Did you change something in your library that would cause this that I need to know about? ToolChain does not fail.

--------------------------------------------------
RENDER TESTS:
ERROR:   Format errors. Cannot perform render tests


* * FAIL
--------------------------------------------------
AU VALIDATION FAILED: CORRECT THE ERRORS ABOVE.
--------------------------------------------------
Program ended with exit code: 255

elanhickler avatar Oct 10 '18 20:10 elanhickler

You shouldn't be getting a sine, you should be getting silence. Chaosfly can't be working differently on your computer. Ensure you're not using an old dll build.

elanhickler avatar Oct 10 '18 20:10 elanhickler

not using a dll - i'm running the standalone version. freshly built with freshly updated repo

RobinSchmidt avatar Oct 10 '18 20:10 RobinSchmidt

are you using an init patch with oversampling 1? looks like oversampling 1 produces silence, oversampling 2 works

elanhickler avatar Oct 10 '18 20:10 elanhickler

Did you change something in your library that would cause this that I need to know about? ToolChain does not fail.

well, i mean, i'm changing things here or there all the time (especially recently in my attempts to clean up the codebase) but from the top of my head, nothing that should break client code at runtime. since when does this occur?

are you using an init patch with oversampling 1?

looks like it. oversampling is at 1 (but it does produce sound). i guess, your version probably loads a hidden settings file in the background. for example, the standalone build apparently has created the file C:\Users\Rob\AppData\Roaming\Chaosfly.settings on my machine. edit: just deleted it, and started chaosfly again and it got recreated

RobinSchmidt avatar Oct 10 '18 20:10 RobinSchmidt

radar generator passes auval. really frustrating not knowing what is going on, can't find any help online.

god wtf... ok I replaced all the code in my radar generator project with RaPG code (chaosfly and RaPG both fail, RaPG is a copy of Chaosfly project and edited), auvaltool passes.

Next step is to just copy my radar generator project, use that as a template for plugins moving forward. What on earth is going on....... the code is not the problem, what is?

elanhickler avatar Oct 10 '18 23:10 elanhickler

maybe something in the settings in the jucer file? dunno, but that would be the next obvious place to look

RobinSchmidt avatar Oct 11 '18 00:10 RobinSchmidt

I looked at everything I could think of, even jucer file. Hmm... what if I'm using the wrong au type? I'll try that. If that isn't it, f*** it!

elanhickler avatar Oct 11 '18 00:10 elanhickler

i've tried searching for a reference for the error messages to figure out what the "ERROR: Format errors." wants to say - but no avail. apple's documentation seems to suck

RobinSchmidt avatar Oct 11 '18 01:10 RobinSchmidt

Yep! That was it. "aumu" (kAudioUnitType_MusicDevice) succeeded. Wow, wasted an entire day figuring this out, precious time lost.

elanhickler avatar Oct 11 '18 05:10 elanhickler

I got an inf from OnePoleFilter LOWPASS_BLT for setting the cutoff to 55096.

image

run RaPG set Amp/Fitler env to +1 Increase envelope decay to max You should get an inf, and the audio sounds terrible as you approach that.

Looks like a safe max for filter cutoff is samplerate * 0.5

elanhickler avatar Oct 11 '18 06:10 elanhickler

Looks like a safe max for filter cutoff is samplerate * 0.5

oh - yes - that could well be. we were talking about this issue already recently here: https://github.com/RobinSchmidt/RS-MET/issues/233#issuecomment-422988391 i guess, i should figure out the limiting behavior of all my filter coefficient formulas and include that info into the api documentation. i don't really want to introduce safeguards at that level of the library, because they always have a cpu hit and sometimes they are not needed. in this case, if i'm not mistaken, you could switch to the IIT based design formula...(there are already some such comments in the code of rsFirstOrderFilterBase::coeffsLowpassIIT). or introduce a safeguard yourself at a higher level

RobinSchmidt avatar Oct 11 '18 14:10 RobinSchmidt

That was it. "aumu" (kAudioUnitType_MusicDevice) succeeded. Wow, wasted an entire day figuring this out, precious time lost.

is this a setting in the jucer file or where do you set this? ....yeahh....i know.....this is frustrating....big reason why i prefer to stick to developing my math/dsp/algorithms stuff rather than sorting out all the pesky platform idiosyncrasies, making sure everything builds everywhere, create installers, bla bla bla - costs so much time and is totally uncreative

RobinSchmidt avatar Oct 11 '18 14:10 RobinSchmidt

don't limit it, put debug-only asserts like jassert, this is helpful so that I can see where my plugin is setting cutoff too high by catching it in the act. Same goes for this, put a debug assert, don't do if statement.

image

also for setting samplerate, if a plugin is setting a samplerate of 0, that should be a red flag to fix something, so no need for if statement, put in debug assert: image (I put in samplerate as a quick test to see if limiting cutoff to half samplerate is safe)

elanhickler avatar Oct 11 '18 16:10 elanhickler

That was it. "aumu" (kAudioUnitType_MusicDevice) succeeded. Wow, wasted an entire day figuring this out, precious time lost.

is this a setting in the jucer file or where do you set this? ....yeahh....i know.....this is frustrating....big reason why i prefer to stick to developing my math/dsp/algorithms stuff rather than sorting out all the pesky platform idiosyncrasies, making sure everything builds everywhere, create installers, bla bla bla - costs so much time and is totally uncreative

it is a setting in jucer, yes, the au type

elanhickler avatar Oct 11 '18 16:10 elanhickler

you need to allow for underscore

fuck! why didn't anyone tell me that:

the rules for XML element names are also the rules for XML attribute names

https://docstore.mik.ua/orelly/xml/xmlnut/ch02_04.htm

which implies, i actually can use XmlElement::isValidXmlName for attributes also

RobinSchmidt avatar Oct 11 '18 17:10 RobinSchmidt

don't limit it, put debug-only asserts like jassert, this is helpful so that I can see where my plugin is setting cutoff too high by catching it in the act. Same goes for this

yep - for the shelving gain, i agree - but for the cutoff freq, i think i actually can invoke l'Hospital's rule at the singularity ....although....i'm not really sure, if that is really the most reasonable thing to do. i guess, we may then see aliasing in the sense of: you request a cutoff of sr/2 + x and the filter will get an actual cutoff of sr/2 - x...i think that what be what the BLT formula would do, when requesting cutoffs above the nyquist limit....sooo limiting is perhaps the more reasonable way to deal with that. i'll set up an experiment to figure these things out exactly

RobinSchmidt avatar Oct 12 '18 20:10 RobinSchmidt

There may be a problem building x86 Win32, can you see if OnePoleFilter builds for you in Win32?

image

elanhickler avatar Oct 12 '18 22:10 elanhickler

ahhh...this again... yeah - i think i know how to fix this....some grunt work - scheduled for tuesday (when i have plumbers in the house and therefore can't focus on creative work anyway)

RobinSchmidt avatar Oct 15 '18 00:10 RobinSchmidt

oh - this was actually just a small change (i'm done and the plumbers are not even here yet...what to do then?...haha). i expected having to make that change in far more places, but just one was sufficient. this thing occurs when you instantiate a template with rsFloat64x2 as template parameter and try to compile for 32 bit. the cure is to pass function parameters by (const) reference instead of by value. currently, we only instantiate rsOnePoleFilter with rsFloat64x2 for the signal type. as soon as we want rsFloat64x2 also for the parameter type (if we want different cutoffs for the two channels, for example), i'll have to make the same change also to setCutoff, etc.

at some point, i should probably consistently use const reference parameters everywhere in rapt. but that would indeed be a lot of grunt work and a considerable code uglification.

RobinSchmidt avatar Oct 16 '18 09:10 RobinSchmidt

this should be moved to discussion or closed

elanhickler avatar Jan 19 '22 19:01 elanhickler