How to create a custom node, as promised.
I apologise in advance for the awful quality video (quite dry and boring)! But the good news is that I have some nicer quality ones in the pipeline. However I thought I should get this out as soon as possible since some people were waiting on it.
Hope it helps.
Windows only, I'll do a mac tutorial later.
Template v1.1:
// ==========================| NodeTemplate v1.0 : by Griffinboy |==========================
#pragma once
#include <JuceHeader.h>
namespace project
{
using namespace juce;
using namespace hise;
using namespace scriptnode;
template <int NV>
// --- Replace with Name
struct ExternalNodeTemplate : public data::base
{ // ------ Replace with Name
SNEX_NODE(ExternalNodeTemplate);
struct MetadataClass
{ // --------- Replace with "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;
// ==========================| Global Variables |==========================
// ==========================| 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 |==========================
// Blocks of audio enter the script here
template <typename ProcessDataType>
void process(ProcessDataType& data)
{
// Convert the audio data to a fixed-channel format for efficient processing
auto& fixData = data.template as<ProcessData<getFixChannelAmount()>>();
// Get pointers to each channel's audio data
// Changes to these variables will now directly modify the original audio buffer
auto audioBlock = fixData.toAudioBlock();
auto* leftChannelData = audioBlock.getChannelPointer(0);
auto* rightChannelData = audioBlock.getChannelPointer(1);
// Get the number of samples (one channel) for this block
int numSamples = data.getNumSamples();
// Pass each channel's audio to the appropriate AudioEffect class
leftChannelEffect.process(leftChannelData, numSamples);
rightChannelEffect.process(rightChannelData, numSamples);
}
// ==========================| AudioEffect Class |==========================
class AudioEffect
{
public:
AudioEffect(float initialGain = 1.0f)
{
smoothGain.set(initialGain); // Initialize sfloat with initial gain
}
void prepare(double sampleRate, double timeInMilliseconds)
{
smoothGain.prepare(sampleRate, timeInMilliseconds);
}
void process(float* samples, int numSamples)
{
for (int i = 0; i < numSamples; ++i)
{
samples[i] *= smoothGain.advance(); // Apply the gain using the smoothed parameter
}
}
void updateGain(float newGain)
{
smoothGain.set(newGain);
}
private:
sfloat smoothGain; // Declare smoothGain variable, using sfloat type for smoothing
};
// ==========================| Set Parameter |==========================
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 |==========================
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));
}
}
// ==========================| External Data |==========================
void setExternalData(const ExternalData& data, int index) {}
// ==========================| Handle HISE Event |==========================
void handleHiseEvent(HiseEvent& e) {}
// ==========================| Modulation Slot |==========================
// ( first enable isModNode() at the top of script )
/*
ModValue modValue;
int handleModulation(double& value)
{
return modValue.getChangedValue(value);
}
// Creates callback so that altering the 'modValue' var elsewhere will now update the mod slot
modValue.setModValue(0);
*/
// processFrame: Needed for compiler, does nothing
template <typename FrameDataType>
void processFrame(FrameDataType& data) {}
private:
AudioEffect leftChannelEffect;
AudioEffect rightChannelEffect;
};
}