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

Library with Decimator and Interpolator classes #677

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

fhchl
Copy link

@fhchl fhchl commented Nov 20, 2021

Pull request related to post at https://forum.bela.io/d/1907-decimation-and-interpolation-library

libraries/Resample/Resample.h offers classes for Decimation and Interpolation by integer order factors. See example at examples/Audio/resample/render.cpp. The needed anti-aliasing filters are pre-computed by the included python script.

Works fine with my own application at home, but haven't written a thorough test yet.

I am not used to write C++ code or design anti-aliasing filters, so would be happy for any feedback!

Todo:

  • test
  • docs

fhchl added 2 commits October 31, 2021 21:54
Update design_fir.py

fix filename
move example
Copy link
Contributor

@giuliomoro giuliomoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the notes made inline, the style is not consistent with our style guide. Namely, you should use tabs for indentation of the C++ code.

#include <libraries/AudioFile/AudioFile.h>
#include <libraries/Scope/Scope.h>

#include "Resample.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be libraries/Resample/Resample.h ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

return false;
};

x = new float[blockSize]();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use raw pointers, you should correspondingly use delete [] x and delete [] y in cleanup(). Even better, you should probably use std::vector instead.

Copy link
Author

@fhchl fhchl Jan 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Changed the example to use stack initialized arrays (is that the right name?) as in examples/Audio/convolver/render.cpp.

}

// upsample
interpolator.process(x, y);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If using std::vector for x and y, you could rewrite this method to take (const) std::vector& as arguments, or if you keep the same interface, pass x.data() and y.data().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the interface from Convolver.process. The examples now also work the same, see above.

examples/Audio/resample/render.cpp Outdated Show resolved Hide resolved
bool setup(BelaContext* context, void* userData) {
unsigned int sampleRateResampled = context->audioSampleRate / gDecimationFactor;

blockSize = context->audioFrames;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to use context->audioFrames directly where possible. It normally doesn't make much sense to cache its value in a global variable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

pass
file = open(fname, "a")

file.write("#include <vector>\n\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When generating a header file, I'd suggest using std::array for this(as the array lengths are known at compile time and immutable).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you then recommend to convert the arrays into vectors during load (in get_fir)?

file.write(f"// numTaps: {len(tapsmin)}\n")
file.write(f"// ripple: {ripple[quality]}\n")
file.write(f"// phase: minimum\n")
file.write(f"std::vector<float> fir_{down}_{quality}_minphase = "+"{\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if using std::array, you'll have to add the array size within the template arguments.


f = w * 0.5*sr/np.pi # Convert w to Hz.

plt.figure(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do these plt.xxx commands work fine on Bela? I am wondering if it'll crash because of lack of display. In the latter case they should probably made conditional.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script was meant to be run on a system that has scipy installed, see above comment. Is that a problem?


void Interpolator::process(ne10_float32_t* outBlock, const ne10_float32_t* inBlock) {
if (factor == 1 && outBlock != inBlock) {
memcpy(outBlock, inBlock, blockSizeIn * sizeof *outBlock);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use parenthesis for sizeof? I find it clearer that way especially in these non-trivial expressions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

std::vector<float> fir = get_fir(L, quality, phase);
uint filtSize = fir.size();
// numTaps must be a multiple of the interpolation factor
uint numTaps = ceil((float)filtSize / L) * L;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a fan of aligning the = becuase if you add a variable with a longer name you'll need to move everything else (or risk of leaving a mess as it happens at line 104

    std::vector<float> fir      = get_fir(L, quality, phase);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

@fhchl
Copy link
Author

fhchl commented Jan 2, 2022

Thanks a lot for the thorough review, @giuliomoro! I tried to improve the code using your comments, but I am not used to programming in C++ and/or code reviews on GitHub so its a learning process for me. 😸

How should we handle the generation of the filter coefficients? As mentioned above, the python distribution on Bela seems not to include SciPy so running the scripts on Bela seems tricky.

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

Successfully merging this pull request may close these issues.

2 participants