Are you trying to use the built in interpolator in hise? I've done this before in c++ but I don't understand it super well. However I've gotten it to work
Posts made by griffinboy
-
RE: SNEX interpolate table
-
RE: only when plugin is visible?
I made a post about this where I posted a code snippet that will completely bypass a timer when the ui is closed
-
RE: LinnDrum clone: great success, thanks guys!
Prolific! You'll have 100 effects out before I have one!
-
RE: Next HISE Developer Hang
I'll get back to you on this in a bit.
But if you want to get started immediately, I can give you my LUFS script. But I'll come up with a simpler example to give you in a bit. I've been meaning to improve my buffer stuff, infact it's one thing I wish to talk to with Christoph about... because it's possible to integrate directly with the audio buffers in hise, and c++ nodes so that the interface script can also talk to the same buffers, but I've not managed to completely get it right.
Anyway, Here's Lufs, it's a fairly old and awful script, and you won't be able to run this script but you can read it
#pragma once #include <JuceHeader.h> #include <limits> #include <atomic> #include <cmath> #include "src/GlobalCables.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif namespace project { using namespace juce; using namespace hise; using namespace scriptnode; template <int NV> struct Lufs_In : public data::base { SNEX_NODE(Lufs_In); struct MetadataClass { SN_NODE_ID("Lufs_In"); }; static constexpr bool isModNode() { return true; } 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; // Internal Parameters float sampleRate = 44100.0f; float blockSize = 512.0f; float lufsBlockSize = 400.0f; // Default to 400ms float overlap = 0.75f; // 75% overlap ModValue modValue; // LUFS calculation juce::AudioBuffer<float> filteredRingBuffer; size_t ringBufferWritePos = 0; size_t ringBufferSize = 0; size_t hopSize = 0; double runningSum = 0.0; // JUCE IIR Filters std::array<juce::IIRFilter, 2> preFilters; std::array<juce::IIRFilter, 2> weightingFilters; // LUFS result float currentLUFS = -100.0f; // Reusable filtered buffer juce::AudioBuffer<float> filteredBuffer; // Thread safety std::atomic<bool> parametersChanged{ false }; juce::CriticalSection processLock; void prepare(PrepareSpecs specs) { juce::ScopedLock sl(processLock); sampleRate = static_cast<float>(specs.sampleRate); blockSize = static_cast<float>(specs.blockSize); updateInternalState(); } void updateInternalState() { // Clamp and validate parameters lufsBlockSize = juce::jlimit(100.0f, 4000.0f, lufsBlockSize); // 100ms to 4000ms overlap = juce::jlimit(0.0001f, 0.9999f, overlap); // 0.01% to 99.99% // Calculate ring buffer size and ensure it's positive ringBufferSize = static_cast<size_t>(sampleRate * lufsBlockSize / 1000.0f); jassert(ringBufferSize > 0 && "ringBufferSize must be greater than 0"); hopSize = std::max(static_cast<size_t>(1), static_cast<size_t>(ringBufferSize * (1.0f - overlap))); filteredRingBuffer.setSize(2, static_cast<int>(ringBufferSize)); ringBufferWritePos = 0; runningSum = 0.0; filteredBuffer.setSize(2, static_cast<int>(blockSize)); calculateFilterCoefficients(); reset(); } void calculateFilterCoefficients() { const double epsilon = 1e-12; // Small value to prevent division by zero // Pre-filter coefficients (as per your original code) const double db = 3.999843853973347; const double f0 = 1681.974450955533; const double Q = 0.7071752369554196; const double K = std::tan(M_PI * f0 / sampleRate); const double Vh = std::pow(10.0, db / 20.0); const double Vb = std::pow(Vh, 0.4996667741545416); const double denominator0 = 1.0 + K / Q + K * K + epsilon; // Added epsilon const double denominator1 = 2.0 * (K * K - 1.0) / denominator0; const double denominator2 = (1.0 - K / Q + K * K) / denominator0; const double numerator0 = (Vh + Vb * K / Q + K * K) / denominator0; const double numerator1 = 2.0 * (K * K - Vh) / denominator0; const double numerator2 = (Vh - Vb * K / Q + K * K) / denominator0; // Validate coefficients jassert(!std::isnan(numerator0) && !std::isinf(numerator0)); jassert(!std::isnan(numerator1) && !std::isinf(numerator1)); jassert(!std::isnan(numerator2) && !std::isinf(numerator2)); jassert(!std::isnan(denominator1) && !std::isinf(denominator1)); jassert(!std::isnan(denominator2) && !std::isinf(denominator2)); juce::IIRCoefficients preCoeffs(numerator0, numerator1, numerator2, 1.0, denominator1, denominator2); // Weighting filter coefficients (as per your original code) const double f0_weighting = 38.13547087602444; const double Q_weighting = 0.5003270373238773; const double K_weighting = std::tan(M_PI * f0_weighting / sampleRate); const double denominator0_weighting = 1.0 + K_weighting / Q_weighting + K_weighting * K_weighting + epsilon; // Added epsilon const double denominator1_weighting = 2.0 * (K_weighting * K_weighting - 1.0) / denominator0_weighting; const double denominator2_weighting = (1.0 - K_weighting / Q_weighting + K_weighting * K_weighting) / denominator0_weighting; // Validate weighting coefficients jassert(!std::isnan(denominator0_weighting) && !std::isinf(denominator0_weighting)); jassert(!std::isnan(denominator1_weighting) && !std::isinf(denominator1_weighting)); jassert(!std::isnan(denominator2_weighting) && !std::isinf(denominator2_weighting)); juce::IIRCoefficients weightingCoeffs(1.0, -2.0, 1.0, 1.0, denominator1_weighting, denominator2_weighting); for (int ch = 0; ch < 2; ++ch) { preFilters[ch].setCoefficients(preCoeffs); weightingFilters[ch].setCoefficients(weightingCoeffs); } } template <typename ProcessDataType> void process(ProcessDataType& data) { auto& fixData = data.template as<ProcessData<2>>(); auto numSamples = fixData.getNumSamples(); if (numSamples == 0) return; // Check if the block is silent bool isSilent = true; for (int ch = 0; ch < 2; ++ch) { auto channelData = fixData[ch]; for (int i = 0; i < numSamples; ++i) { if (channelData[i] > 0.0001f) { isSilent = false; break; } } if (!isSilent) break; } // If the block is silent, return early if (isSilent) { return; } if (parametersChanged.exchange(false)) { juce::ScopedLock sl(processLock); updateInternalState(); } juce::ScopedLock sl(processLock); // Ensure the filteredBuffer is large enough if (filteredBuffer.getNumSamples() < numSamples) filteredBuffer.setSize(2, numSamples); // Create AudioBlock objects for input and filtered data auto inputBlock = fixData.toAudioBlock(); juce::AudioBuffer<float> filteredAudioBuffer(filteredBuffer); auto filteredBlock = juce::dsp::AudioBlock<float>(filteredAudioBuffer); for (int ch = 0; ch < 2; ++ch) { // Copy input data to filteredBuffer juce::FloatVectorOperations::copy(filteredBlock.getChannelPointer(ch), inputBlock.getChannelPointer(ch), static_cast<int>(numSamples)); // Apply pre-filter preFilters[ch].processSamples(filteredBlock.getChannelPointer(ch), numSamples); // Apply weighting filter weightingFilters[ch].processSamples(filteredBlock.getChannelPointer(ch), numSamples); } // Store filtered samples in ring buffer and update LUFS for (size_t i = 0; i < numSamples; ++i) { double leftSample = static_cast<double>(filteredBlock.getSample(0, static_cast<int>(i))); double rightSample = static_cast<double>(filteredBlock.getSample(1, static_cast<int>(i))); // Subtract the square of the oldest samples float oldLeftSample = filteredRingBuffer.getSample(0, static_cast<int>(ringBufferWritePos)); float oldRightSample = filteredRingBuffer.getSample(1, static_cast<int>(ringBufferWritePos)); runningSum -= static_cast<double>(oldLeftSample * oldLeftSample + oldRightSample * oldRightSample); // Add the square of the new samples runningSum += leftSample * leftSample + rightSample * rightSample; // Update ring buffer filteredRingBuffer.setSample(0, static_cast<int>(ringBufferWritePos), static_cast<float>(leftSample)); filteredRingBuffer.setSample(1, static_cast<int>(ringBufferWritePos), static_cast<float>(rightSample)); ringBufferWritePos = (ringBufferWritePos + 1) % ringBufferSize; // Calculate LUFS when we've moved by hopSize or if hopSize is 0 if (hopSize == 0 || ringBufferWritePos % hopSize == 0) { calculateLUFS(); } } } void calculateLUFS() { jassert(ringBufferSize > 0 && "ringBufferSize must be greater than 0"); double meanSquared = runningSum / (2.0 * ringBufferSize); if (meanSquared > 1e-12) { currentLUFS = static_cast<float>(-0.691 + 10.0 * std::log10(meanSquared)); currentLUFS = std::clamp(currentLUFS, -100.0f, 0.0f); } else { currentLUFS = -100.0f; } modValue.setModValue(currentLUFS + 2.96f); } float getLUFS() const { return currentLUFS; } void handleHiseEvent(HiseEvent& e) {} void reset() { juce::ScopedLock sl(processLock); ringBufferWritePos = 0; runningSum = 0.0; filteredRingBuffer.clear(); for (int ch = 0; ch < 2; ++ch) { preFilters[ch].reset(); weightingFilters[ch].reset(); } currentLUFS = -100.0f; } template <typename T> void processFrame(T& data) {} int handleModulation(double& value) { return modValue.getChangedValue(value); } template <int P> void setParameter(double v) { if (P == 0) { lufsBlockSize = static_cast<float>(v); parametersChanged.store(true); } else if (P == 1) { overlap = static_cast<float>(v); parametersChanged.store(true); } reset(); } void createParameters(ParameterDataList& data) { { parameter::data p("Buffer Size (ms)", { 100.0, 4000.0 }); registerCallback<0>(p); p.setDefaultValue(2800.0); data.add(std::move(p)); } { parameter::data p("Overlap", { 0.0001, 0.9999 }); registerCallback<1>(p); p.setDefaultValue(0.99); data.add(std::move(p)); } } }; }
-
RE: Next HISE Developer Hang
Oh yes indeed, some laf stuff would be nice to look at, for the new css features and different ways of working.
Also btw about ringbuffers, if you need something in particular, I could probably help you out I've done lots of these before for delay lines and Lufs calculations
-
RE: Next HISE Developer Hang
Thanks, yes, broadcasters / AI are both things that I've been blindly stumbling through. I use both, but probably not with best practices! I wouldn't mind a discussion around these.
At somepoint down the line I'd like to look in detail at c++ third party nodes, which wound probably require Christoph - I've been using these for quite a while now but the way I program my nodes doesn't fully agree with the way Hise works, it would be nice to some things laid bare, and see how Christoph would recommend working, especially to integrate more cleanly with hise's inbuilt systems.
A super advanced topic I'd also like to tackle in the future is more integration of RT Neural. For instance I would like to have a neural network that can output more than just audio data - but rather can control other parameters. Or at the least can be integrated into a c++ node to control other parts of the script / generate data that can be stored into arrays. I have some rather grand plans, perhaps this is not the biggest concern for in terms of features for hise at present though : )
-
RE: NeuralNetworkExample wont compile
@hisefilo
uh oh. I've been using tensorflow.
I'd better check this out and see whether I have the same issue on my machine. I'm running windows. -
RE: Making button flash upon mouseclick AND midi note
I've only ever done it with panels, I'm not sure about laf.
But one way to do it is yes, to have a way to store data for whether a particular pad is on or off. And in your external callbacks for midi activity, you could set this data and then call repaint.The ideal solution would be to create a 'flash' script which would take care of this behavior automatically and redraw it to create the animation. I believe there is a function available that allows you to call another function after waiting a certain amount of time. This could be used as a 'wait' so that you would first send a command to draw the panel lit up, wait, and then call the command to revert it after a set amount of time has passed
-
RE: Making button flash upon mouseclick AND midi note
@Morphoice
Have you got LAF for the buttons? You will have to do it in the LAF. That is where you can change the graphics of the button.You can have if statements in there, and you can re-call the painting using .repaint()
Meaning a different callback can be used to set a state of a button and tell it to repaint itself in a particular way.
-
RE: Making button flash upon mouseclick AND midi note
@Morphoice
Shoot, I assumed you were using a single panel for all these!
I see that David has recommended you so that.Using multiple panels will be easier if you are new to hise scripting, you can even spawn these procedurally using the 'child' system, and use an array to store the object Ids for hooking up the callbacks.
-
RE: Making button flash upon mouseclick AND midi note
Don't trigger them using the mouse, trigger them entirely when the midi event happens.
In the on-note callback you can query the note number and trigger the appropriate pad animation. Create a script that handles a 'flash' animation, and trigger it with the correct details from the note callback.
You can use the mouse callback to trigger the note events, and then the note events will already be set up to handle animation
I'm new to the midi side of hise, but from a design perspective this would be my guess at how to approach it. Other people may have better ideas
-
RE: 8 bit sample playback with various speed
Yeah that'll help, I thought of that but didn't know whether that mirrors what one of these machines actually does. I'm not super knowledgable about early digital
-
RE: 8 bit sample playback with various speed
Yeah you'll need a custom implementation.
I'm working on one but I can't promise it'll be done soon I've got lots of other work to to!
I do want to start handing out freebies, but as you can see lots of my work isn't quite ready : )It's amazing that you own this hardware, perhaps I should reach out, I was thinking to get in contact with someone who owns hardware to buy some specific recordings. I'm working on some realistic oscillator simulations and need validation data.
-
RE: Saturation Models (Neve, Tweaker, Oxford Inflator) in FAUST
The Inflator yes, is a completely a linear transfer function, split into 3 bands.
You can create programs to measure and derive the transfer functions. -
RE: Retro 80s Tape Wow & Flutter with faust
Let me know if it works as expected. You should see harmonics coming from the sine wave the more you boost volume into the node (using the simple gain node)
The oversampling likely got rid of the instability. I mostly find issues with stability on 2x oversampling and so it needs the clipper for when boosting > 30db into the node you get issues.
Sine wave, and drum loops are the best ways to test distorting effects.
As you can clearly see the results. -
RE: Retro 80s Tape Wow & Flutter with faust
You can do that using the wet dry scriptnode template.
It's in the list of nodes.
The Node I've given you is still under development. It does work but yeah, all the messyness is not tucked away. It does work - But it's not a neat solution until I release the official version
-
RE: Retro 80s Tape Wow & Flutter with faust
To find the right place to clip, use a hise Math.clip node before the hysteresis, and a simple gain node before that. Now drive the gain node into the hysteresis at somepoint it will collapse. Lower the Clip node until it no longer collapses.
Node graph:
Simple Gain
Math.clip
Oversample 4x - JG_Tape_Model -
send a sine wave into it and visualise using fft