C++/SNEX smooth parameter
-
@griffinboy oh ok! I need this in an oversampling context too, because the more the DSP processing required, the more the crackles...
-
@ustk
Math.smoothstep
maybe? -
@griffinboy What kind of issues? Compile errors?
-
@orange Smooth step is to "de-linearise" a value and make it more "sigmoid like" curve
-
No, it just becomes "not smooth" anymore.
I need to look into the code for sfloat because maybe I'm just using it wrong, but it works fine in a simpler context.Perhaps do a custom implementation for now, or look at how to use the one in Juce.
-
@griffinboy I used this a few days ago but I am confused about
set
The doc says
Sets a new target value and resets the ramp position to the beginning.
The beginning of what? If the value has not yet reached the target, would that cause a jump?
Or the new ramp is always calculated between the current position and the new target? -
Or the new ramp is always calculated between the current position and the new target
Yes this.
There‘s no reason why it shouldn‘t work within oversampling but you have to adjust the smoothing rate in the prepare callback.
-
Bingo that's probably what I missed. I had a teeny tiny smoothing rate.
With oversampling that now becomes very short!
Thx -
Here is one of my older c++ templates. Take a look at sfloat here:
// This is an example template. The functions that appear in this code must not be deleted, many of these are templated and as such are expected by the compiler. Other programs connected to this one supply each of the functions with data. // When taking this template and adapting it for our unique program we are trying to code, feel free to change the EFFECT class into one that is specific for our effect! That class is not templated and can be changed. // Block processing is preferred, and using a class like this for it is also good practice. Feel free to remove the gain effect, as that is only there to show you proper usage of the framework. #pragma once #include <JuceHeader.h> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; template <int NV> // Name struct ExternalNodeTemplate : public data::base { // Name SNEX_NODE(ExternalNodeTemplate); struct MetadataClass { // Name SN_NODE_ID("ExternalNodeTemplate"); }; // Node properties static constexpr bool isModNode() { return false; } static constexpr bool isPolyphonic() { return NV > 1; } static constexpr bool hasTail() { return false; } static constexpr bool isSuspendedOnSilence() { return false; } static constexpr int getFixChannelAmount() { return 2; } static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 0; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; // Prepare: Called on init, and when sample rate changes void prepare(PrepareSpecs specs) { float sampleRate = specs.sampleRate; float numChannels = specs.numChannels; leftChannelEffect.prepare(sampleRate, 5.0); // 5ms smoothing on the gain parameter rightChannelEffect.prepare(sampleRate, 5.0); } // Reset: Called when the plugin is reloaded void reset() {} // Process: Audioblocks enter the script here template <typename ProcessDataType> void process(ProcessDataType& data) { auto& fixData = data.template as<ProcessData<getFixChannelAmount()>>(); auto audioBlock = fixData.toAudioBlock(); // Get pointers to channel data auto* leftChannelData = audioBlock.getChannelPointer(0); auto* rightChannelData = audioBlock.getChannelPointer(1); float blockSize = data.getNumSamples(); // Send each channel to the appropiate AudioEffect class leftChannelEffect.process(leftChannelData, blockSize); rightChannelEffect.process(rightChannelData, blockSize); // We use blocks rather than frames becuase there are more samples than channels (Better SIMD) } // AudioEffect class: Handles audio processing class AudioEffect { public: // Constructor to initialize gain AudioEffect(float initialGain = 1.0f) { smoothGain.set(initialGain); // Initialize sfloat with initial gain } // Prepare the smoothedFloat with sample rate and time void prepare(double sampleRate, double timeInMilliseconds) { smoothGain.prepare(sampleRate, timeInMilliseconds); } // Process function: Recieves a block of samples void process(float* samples, int numSamples) { // Iterate over each sample in the block for (int i = 0; i < numSamples; ++i) { samples[i] *= smoothGain.advance(); // Apply the effect using the smoothed parameter } } // Update Gain: Called by setParameter function void updateGain(float newGain) { smoothGain.set(newGain); } private: sfloat smoothGain; // Instance of sfloat for smooth gain }; template <int P> void setParameter(double v) { if (P == 0) leftChannelEffect.updateGain(static_cast<float>(v)); // Update gain for left channel else if (P == 1) rightChannelEffect.updateGain(static_cast<float>(v)); // Update gain for right channel } // Create parameters on the GUI void createParameters(ParameterDataList& data) { { // { min, max, step } < id > parameter::data p("Left Gain", { 0.1, 2.0, 0.01 }); registerCallback< 0 >(p); p.setDefaultValue(1.0); data.add(std::move(p)); } { parameter::data p("Right Gain", { 0.1, 2.0, 0.01 }); registerCallback< 1 >(p); p.setDefaultValue(1.0); data.add(std::move(p)); } } // // Interact with external data (eg. an external buffer) void setExternalData(const ExternalData& data, int index) {} // Handle HISE events: Process MIDI or other events void handleHiseEvent(HiseEvent& e) {} // Handle external modulation slot ( first enable isModNode() ) /* ModValue modValue; int handleModulation(double& value) { return modValue.getChangedValue(value); } // setting modValue updates the external modulation modValue.setModValue(0); */ // processFrame: Needed for compiler, does nothing template <typename FrameDataType> void processFrame(FrameDataType& data) {} private: // Create instances of AudioEffect for left and right channels AudioEffect leftChannelEffect; AudioEffect rightChannelEffect; }; }
-
@Christoph-Hart Yeah I have noticed it was necessary to update the timing as samplerate changes
@griffinboy Perfect thanks mate!