@resonant iirc NAM files are just large JSON objects which can be embedded in the final plugin easily
test it yourself, rename a NAM file to .json and open it in something like Sublime Text
@resonant iirc NAM files are just large JSON objects which can be embedded in the final plugin easily
test it yourself, rename a NAM file to .json and open it in something like Sublime Text
@resonant Nope, I vibe coded a manual NAM wrapper for Altar:
https://github.com/nytemairqt/altar/blob/main/DspNetworks/ThirdParty/amp.h
This was all a big surprise to wake up to.
Altar had quite a bit of vibe-coding involved, specifically on the modular drag-n-drop FX panels, and some of the third-party DSP nodes. I used various models available on Github Copilot (mostly Claude) because it seems to handle multiple files / larger context window better than the standalone models.
That being said, it still required quite a bit of manual fixing up. A common one mentioned above was using var instead of local with inline functions etc, and it would default to using JUCE buffers instead of the templated SNEX methods which I assume comes with a performance hit.
I think a good idea would be to establish some sort of global guiding prompt which includes some of the most important best practices for HISE and appending that to individual case-specific prompts eg:
"Use this bestPractices.md to familiarize yourself with the HISE codebase, remember to make use of the existing templated functions in the auto-generated files and only use JUCE methods as a fallback" etc etc
@Christoph-Hart said in (ScriptNode) Smeary Reverb / Washy Cymbals?:
@iamlamprey haha everybody always rips out the dither stuff from air windows :)
i assume he has it there for a reason but most DAWs apply dithering now anyway and I can't hear a difference lol
Okie, here's the wrapper if anyone else needs it:
// ==================================| Third Party Node Template |==================================
#pragma once
#include <JuceHeader.h>
namespace project
{
using namespace juce;
using namespace hise;
using namespace scriptnode;
// ==========================| The node class with all required callbacks |==========================
template <int NV> struct phaseNudge: public data::base
{
// Metadata Definitions ------------------------------------------------------------------------
SNEX_NODE(phaseNudge);
struct MetadataClass
{
SN_NODE_ID("phaseNudge");
};
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;
void prepare(PrepareSpecs specs)
{
}
void reset()
{
}
void handleHiseEvent(HiseEvent& e)
{
(void)e;
}
double dL[1503];
double dR[1503];
int one = 1;
int maxdelay = 9001;
double A = 0.0;
double B = 1.0;
uint32_t fpdL;
uint32_t fpdR;
int allpasstemp;
double outallpass = 0.618033988749894848204586;
int maxdelayTarget = (int)(pow(A,3)*1501.0);
double wet = B;
double bridgerectifier;
double inputSampleL;
double inputSampleR;
double drySampleL;
double drySampleR;
template <typename T> void process(T& data)
{
const int numSamples = data.getNumSamples();
const int numChannels = jmin(2, (int)data.getNumChannels());
maxdelayTarget = (int)(pow(A, 3) * 1501.0);
wet = B;
dyn<float> chL, chR;
int idx = 0;
for (auto ch : data)
{
if (idx == 0) chL = data.toChannelData(ch);
else if (idx == 1) chR = data.toChannelData(ch);
++idx;
}
if (numChannels == 1) chR = chL;
for (int s = 0; s < numSamples; ++s)
{
double dryL = chL[s];
double dryR = numChannels > 1 ? chR[s] : dryL;
double xL = chL[s];
double xR = numChannels > 1 ? chR[s] : xL;
// might add dithering later
// bridge rectifier
xL /= 4.0;
xR /= 4.0;
double br = std::fabs(xL);
br = std::sin(br);
if (xL > 0) xL = br;
else xL = -br;
br = std::fabs(xR);
br = std::sin(br);
if (xR > 0) xR = br;
else xR = -br;
// allpass
if (std::fabs(maxdelay - maxdelayTarget) > 1500) maxdelay = maxdelayTarget;
if (maxdelay < maxdelayTarget)
{
maxdelay++;
dL[maxdelay] = (dL[0] + dL[maxdelay-1]) / 2.0;
dR[maxdelay] = (dR[0] + dR[maxdelay-1]) / 2.0;
}
if (maxdelay > maxdelayTarget)
{
maxdelay--;
dL[maxdelay] = (dL[0] + dL[maxdelay]) / 2.0;
dR[maxdelay] = (dR[0] + dR[maxdelay]) / 2.0;
}
allpasstemp = one - 1;
if (allpasstemp < 0 || allpasstemp > maxdelay) allpasstemp = maxdelay;
xL -= dL[allpasstemp] * outallpass;
xR -= dR[allpasstemp] * outallpass;
dL[one] = xL;
dR[one] = xR;
xL *= outallpass;
xR *= outallpass;
one--;
if (one < 0 || one > maxdelay) { one = maxdelay; }
xL += (dL[one]);
xR += (dR[one]);
// second bridge rectifier stage
br = std::fabs(xL);
br = 1.0 - std::cos(br);
if (xL > 0) xL -= br;
else xL += br;
br = std::fabs(xR);
br = 1.0 - std::cos(br);
if (xR > 0) xR -= br;
else xR += br;
xL *= 4.0;
xR *= 4.0;
// apply dry / wet
if (wet < 1.0)
{
xL = (dryL * (1.0 - wet)) + (xL * wet);
xR = (dryR * (1.0 - wet)) + (xR * wet);
}
// apply dither goes here
// write back to channelData
chL[s] = xL;
chR[s] = xR;
}
}
template <typename T> void processFrame(T& data)
{
}
int handleModulation(double& value)
{
return 0;
}
void setExternalData(const ExternalData& data, int index)
{
}
template <int P> void setParameter(double v)
{
switch (P)
{
case 0: { A = v; break; }
case 1: { B = v; break; }
}
}
void createParameters(ParameterDataList& data)
{
{
parameter::data p("PhaseNudge", { 0.0, 1.0 });
registerCallback<0>(p);
p.setDefaultValue(0.5);
data.add(std::move(p));
}
{
parameter::data p("Dry/Wet", { 0.0, 1.0 });
registerCallback<1>(p);
p.setDefaultValue(0.5);
data.add(std::move(p));
}
}
}; // end struct
} // end namespace
It's missing the dithering stuff, but it sounds basically identical to the original implementation, just make sure to set up the third party node using the template and call it "phaseNudge.h"
@Lindon That's a good idea, I'll keep it in the back pocket for now because I want to keep everything in scriptnode but if I can't figure it out I'll make a wrapper
@Lindon Yep that's the one I'm following :)
he's using an airwindows plugin for the allpass but the HISE allpass node sounds nothing like that, not sure how all the coefficient stuff works
I ended up going a harmonic + stochastic approach which is showings its obvious limitations
I'm noodling at physically modeled drums, specifically cymbals and I need an efficient way to "smear" the excitation impulse
I know the typical route is to use allpass filters in the delay line, but I find I have to use a math.mul at 0.5 just to keep the audio from blowing up.
I currently have 5 delays with different fractional delay times and I'm also passing a long noise tail into it but it just sounds like a noisy sawtooth buzz rather than a nice chorusy wash.
Not sure how to proceed towards that metallic ringing without overloading the delay lines or the CPU
HISE is a big codebase with a variety of dependencies, I think the risk of hallucination & context-rot is a lot bigger than your average JUCE plugin.
There's also a lot of templated functions, specific versions of things like JUCE and the C++ version HISE uses which LLMs usually don't "keep in mind".
Also GLM4.7 has noticeably outperformed Claude & GPT 5.2, at least in my own tests 
I'm sure Christoph uses some of these tools occasionally, but I think if he started depending on them for the short-term benefit we'd probably end up with a lot more bugs in the long term
@David-Healey said in Apple is ridiculous:
Usually below that text you quoted there is a tiny link that says something like Continue on Web.
yeh there's nothing, I've clicked every link on the page, I even tried signing out and clicking Subscribe and logging back in and it just flops
I've opened a ticket but I assume the answer will be "use our 1.5 stars app"
I'm trying to renew my apple developer subscription, instead of a "Renew" button, I'm greeted with this:

I can't download the app from the app store (VM is using an older version of MacOS), and every support article, web link, even the emails they sent me with manual renew links, all just go straight back to the main account home page.
Has anyone had to deal with this crap recently and found a solution? All of my other devices are PC so I can't download the Developer app at all
@Orvillain I believe David made one into a pull request, one was from the forum (I think it was also David's, so it's probably a PR as well). the others were specific to the Rubberband library, but I'm no longer using it in Altar anyway, they were just namespace conflicts with using namespace juce
@cemeterychips You can clone/download it or just copy the stuff you need from the amp.h file, you'll also have to get the dependencies like RTNeural and RTNeural-NAM
If you don't know C++ concepts like pointers and classes/structs then it will be quite a struggle, and you'll also need to connect the NAM stuff to the interface if you want to dynamically load/swap models at runtime.
Also I highly recommend David's course if you're new to HISE, before jumping into any C++ stuff:
@cemeterychips Altar has working NAM:
https://github.com/nytemairqt/altar
Check the DspNetworks/ThirdParty/amp.h file, it's using RTNeural & RTNeural-NAM
Edit: Sorry didn't realize you were new to HISE, this is definitely a bit more advanced of an implementation, I had to go this route because I couldn't get the NAM node working
@parabuh I gave up trying and switched to Signalsmith which sounds better anyway (but has a bit more latency)
@David-Healey said in Invalid use of incomplete type vSIMDType:
did you make a pull request?
you vastly overestimate my understanding of git 
@Christoph-Hart said in C++ Version Preprocessor?:
everything will explode
I am yet to experience an explosion, RTNeural-NAM is using std::span and the Linux/Mac compilers are complaining unless I switch the AutoGeneratedProject.jucer to C++20
C++17 works fine on Windows so I'm not even sure if the span is ever actually created
Edit: Yep it's some weird ghost function which isn't being called anywhere, might be for a specific edge-case but I can comment it out and use C++17
Is there a preprocessor definition (in HISE settings) to set the C++ version for export?
@David-Healey said in Invalid use of incomplete type vSIMDType:
We know the issue isn't in the JUCE modules because those haven't been touched.
The reference to Point is ambiguous error I'm getting is a confirmed JUCE issue (it was patched in a later version of JUCE), but it's caused by a clash with the Rubberband library so it's not HISE's fault. Either way I'm going to test the signalsmith shifter instead which sounds better out of the box anyway
I'm also getting a bunch of fftw errors when compiling your fork (development branch), I assume I need to install that on MacOS?
Edit: Yep signalsmith solved all of my woes 