There is this call in the docs - does it work? How do you use it? Can you feed in the raw .NAM file as JSON and use it directly?
Posts
-
RE: Simple ML neural network
-
RE: Spectral Gating
Custom c++ node.
You can get by quite easily by using the built in Juce fft:
// FFT object static constexpr int fftSize = 128; static constexpr int hopSize = 16; juce::dsp::FFT fft { (int)std::log2(fftSize) };
That would create the required object. Then you'd need to write a loop process in order to call something like:
fft.performRealOnlyForwardTransform(fftBuffer.get());
I just wrote a node recently to process an audio buffer with FFT's. It works really well.
-
RE: Multiple Global Data Cables - only the first one gets a runtime target
@griffinboy Hmmmm, that might be what I ran into. Can't remember now. But basically my c++ as an effect works well... and I'm able to update the UI any time I turn a parameter.
But when I tried to convert it to a synth... it wasn't working.
This actually makes sense, because my sendData function gets triggered from within processFrame, which gets triggered from within process()
-
RE: Multiple Global Data Cables - only the first one gets a runtime target
Do you know if DataCables work in Scriptnode Synthesizers? Because I've just taken my node and put it into one, and now I no longer get the runtime target showing up, and the data callback no longer fires.
Nothing has changed, and I only have a single instance of the node in the entire project.
If I delete the scriptnode synthesizer and re-add the script FX, then it immediately begins to work.
-
RE: Multiple Global Data Cables - only the first one gets a runtime target
@griffinboy Yep - I can't even plead ignorance either. I knew about the hashing and just totally forgot! So I'm an idiot!
-
RE: Multiple Global Data Cables - only the first one gets a runtime target
@Christoph-Hart Dannngg!!! I knew I cocked something up!!!! baaaahhh!!
Thanks Chris. No bug here. You're 100% right.
-
Multiple Global Data Cables - only the first one gets a runtime target
Custom c++ node:
#pragma once #include <JuceHeader.h> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; enum class GlobalCables { dataCable = 0, otherDataCable = 1 }; using cable_manager_t = routing::global_cable_cpp_manager< SN_GLOBAL_CABLE(-389806413), SN_GLOBAL_CABLE(-389806400) >; template <int NV> struct custom_node: public data::base, public cable_manager_t { SNEX_NODE(custom_node); struct MetadataClass { SN_NODE_ID("custom_node"); }; 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 = 1; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; custom_node() { // this doesn't do anything, but if you want the communication the other way around // (from HISE to your C++ node, this is the way to register callbacks). this->registerDataCallback<GlobalCables::dataCable>([](const var& funky) { jassertfalse; }); this->registerDataCallback<GlobalCables::otherDataCable>([](const var& funky) { jassertfalse; }); } // We don't need any of the DSP callbacks for this example so we can use these macros to // define empty methods SN_EMPTY_PREPARE; SN_EMPTY_HANDLE_EVENT; SN_EMPTY_PROCESS; SN_EMPTY_PROCESS_FRAME; SN_EMPTY_MOD; SN_EMPTY_RESET; void setExternalData(const ExternalData& data, int index) { if(data.isNotEmpty()) { // convert the data to the juce::AudioSampleBuffer class auto buffer = data.toAudioSampleBuffer(); // fetch a few properties from the buffer // (this is the place where you could do your custom processing auto magnitude = buffer.getMagnitude(0, 0, buffer.getNumSamples()); auto rms = buffer.getRMSLevel(0, 0, buffer.getNumSamples()); auto length = buffer.getNumSamples(); auto channels = buffer.getNumChannels(); hise::JSONObject obj; // write the values into the JSON object obj[String("magnitude")] = magnitude; obj[String("RMS")] = rms; obj[String("length")] = length; obj[String("numChannels")] = channels; // just attach the current knob position so you know it's working. obj[String("knobValue")] = value; // send the JSON object back to HISE this->sendDataToGlobalCable<GlobalCables::dataCable>(obj); this->sendDataToGlobalCable<GlobalCables::otherDataCable>(obj); } } template <int P> void setParameter(double v) { // just save the parameter to be send in the JSON object later. value = v; } double value = 0.0; void createParameters(ParameterDataList& data) { { parameter::data p("MyParameter", { 0.0, 1.0 }); registerCallback<0>(p); p.setDefaultValue(0.5); data.add(std::move(p)); } } }; }
Then the interface script:
Content.makeFrontInterface(600, 600); reg rm = Engine.getGlobalRoutingManager(); reg dataCable = rm.getCable("dataCable"); reg otherDataCable = rm.getCable("otherDataCable"); dataCable.registerDataCallback(function(data) { Console.print(trace(data)); }); otherDataCable.registerDataCallback(function(data) { Console.print("We could have other data here from the same node"); });
Here's the project snippet:
HiseSnippet 1238.3ocuWs0SibCE1SfY2R5tRck19Z0HdJTQiRfvsspp.IDZTKPDgR2GPB43wCwEO1i73AHsZk5i8ma+GzdNyLIYlRX2UQsKHglyU+4i+NGa5azLdbr1Pbpdw3HNw4EtCFqriZOhJTjdcHNegaXhzJhjbFcnjGSNbbDMNl6SbbV5XzKmUVlj9ye88GRkTEiOSEgboVv3+jHTXmos+9+nPJ6R84WHBK3cq86wzp1ZoNAPzRtMHQT1sza3mRQ2p3R9AZ7HhyW6t0FaxZ4ui+Far0daynay2KfFDzbS+Vs1d28Zt4dzV6tMuwVDmmcjuvpMCrTK.dmkOT6OdvH88prE3RQr.1VnPSx.XkyT2UK8wsHpkzdjP52eRoJl.Yo+rB2RYEtW6dhvWLUewBHZvaVDEKfNUJCukJAulEgWiBvaNPxo.jVNCRuxc.yHhryrf34yc6orbS.ENmJBkLeIU9pJts0fGJa8P5s7tFPXZD01tQi08f+r12VspgeimIz667NRciPwqeC2drTOjJOWmXEpaNgpfyNSsI95Ssz1HIBBwDhtmJUa0oFVEbE8TaGwMcdJ2KaEio5zLTGBWDamXWJGBLnZAIJlUnU0P+Vq5uWcEXKFqA2iLBksl0f6sTiP1dGlwxKxhk1U+EtGSmH88FQuimsqRqBdvGbu.iNzCz4ECzaOk1Ocujt5UmjZOs5TskelpVZ9q9tpd+aSAAy0FdHZzRId.LGyXmm48EXMUR3PtYcu6nxD9TGA1UYJq6SSYK1Qwx3TEbTq5oD1yh3pmpOijSDgu94dXkG4445.+h3Fq.gfSG9cvTlLV+Jtc3w2Z0QvblG0R.MiZ+DI0VtCEmika.pAkZKPpuJVXGWbN2+YsserP7Ut8EV1n4iwJyAiPk5+CLlOr6ktGEDvY1Y.bY2tu8S2jsp4S1f0DW7WjK508sMm2LMm+zcFAOxvinF9E59R53ZPqGb014.FW2anTytcf323OtqHJCBGhdTiMhpTbY7hz77rO5pRy2+gxzYrVi3AnK7zjvAv0lLd6bzA5bpf8PYxMPYjWLfq7SE9a3mbiMQYmbiMmXrP62ob68ZysoGG4eSbddVsuSbzDUWxMwH4z44tMpC+RNPJ022VGFIxosvgRpt9Z43nQZkfgpx7XBzOHTm.qZN9gq7ufJjHOePRLLsv+L0.v4z2Yf7rSgAmPgsKkAE1w8o1QX+BNuAHmbSc1TRZIrNimfU3rWXTxALuSdKRVoCfYHVOvWh3rDZOqfjA.bEdoKKI1pCuFmlSJAouzEHQ+JzzTunKyCEkSxGBFXsSxeXx3wCR7E5tBYFzlJAsN8T97GvN0uoI4Hfc5CuvIMJm8cO9MW0AFnNT+vUGmHrTy4hff1ZSTR7U.rsPWmuHdmFWEl.71q5zyC+nVilqU+d5cjSDpKQh9DDdB8gox+waVAFgPMvVCt+LqgdhDXuPnHmoXntMA4rp5IimESA22hzgGPg2mVRGovcCkW5RVRGrfRvXyJYzidwWhVYT4jUAnfGp02FRSI+K10PeJZUCoLi9ZV1zGby8YoZ.9gJkSsh6InrWSxcYarYWTDBOO8ZFqbpdTfarnAt4hFXqEMvsVz.2dQCbmEMvc+vAh+2CGj.iCxFfBMW8OJqs04HE91zza5H+C7L.wuK
You'll need to compile the custom node as "custom_node".
So what I am seeing here, and I have no idea why .... the "otherDataCable" does not get a "Runtime Target" entry in the list of targets, so its callback never seems to run.
This is using two cables with a single node.
Is this not supported?
-
RE: Is it possible to create a midi note from ScriptNode/c++ node and direct it to a synth in the module tree?
@Orvillain Right no... I get it. Use two cables. Dohhh.
-
RE: Is it possible to create a midi note from ScriptNode/c++ node and direct it to a synth in the module tree?
@Christoph-Hart Hey Chris. I don't know if I'm doing it wrong, but it doesn't seem possible to do these two things at the same time???
dataCable.registerDataCallback(function(data)
{
this is where I derive data from my custom c++ node inside the network that my dataCable is attached to
};dataCable.registerCallback(function(data)
{
this is where I would create midi notes and tell a synth to play them
}, SyncNotification);Am I misunderstanding?
-
Global Cables don't work when custom node is inside a midichain - unless you go into the scriptnode view
Here's what I'm seeing.
- Custom c++ node in a ScriptFX.
- Put c++ node inside a midichain to activate HiseEvent (midi events) being received by the node.
- On launch of the project, data-cable callback does not seem to be active. No data is received and my UI doesn't update.
- Go into the script node network view, then back to the main interface view.
- data-cable now works.
-
RE: Is it possible to create a midi note from ScriptNode/c++ node and direct it to a synth in the module tree?
@griffinboy Gotcha! Right, so I'm processing audio inside a c++ node and deriving a bunch of statistics from it, which I'm then painting in my UI. I'm not trying to do millions and millions and gajillions of floats or anything like that. I'd already assumed that was all best kept in c++
What kind of thing are you working on out of interest?
-
RE: Is it possible to create a midi note from ScriptNode/c++ node and direct it to a synth in the module tree?
So far I'm following your global cable guide and sending data from my node. I've got a fair amount of data, and it seems plenty fast. But I guess it depends what you want to do. I've got some arrays that could be thousands of samples long, and it isn't struggling.
-
RE: How does a custom c++ interface with one of its display buffers?
I ended up piping data through a global cable and plotting it to a scriptPanel - it is pretty good actually!!!
But I would like to know how to utilise the display buffers directly on the node.
-
RE: How does a custom c++ interface with one of its display buffers?
I resorted to ChatGPT. This compiles:
// === Write to display buffer === if (auto* ringBuffer = dynamic_cast<SimpleRingBuffer*>(this->externalData.obj)) { DataWriteLock lock(this); // Required to write safely auto& targetBuffer = ringBuffer->getWriteBuffer(); const int numSamples = static_cast<int>(env.size()); // Resize to match env targetBuffer.setSize(1, numSamples, false, false, true); for (int i = 0; i < numSamples; ++i) targetBuffer.setSample(0, i, env[i]); }
But I don't get any data displaying in the display buffer; whether I look at embedded or the 0th external slot.
-
RE: How does a custom c++ interface with one of its display buffers?
@griffinboy said in How does a custom c++ interface with one of its display buffers?:
Yeah it's possible. Take a look at the source for external data
You can write to display buffers. But we warned, these are ring buffers and so personally I've had issues with it wrapping and having the wrong size, stuff like that. Christoph can probably set you straight. But the methods for writing into the data slot are in this header I think!
Groovy, cheers!! I can't really suss it out.
I don't need the buffer to update with the processFrame method or anything like that. I just want to effectively set its size once, and blat a bunch of data into it. I tried accessing the ring buffer using 'rb' like this:
if (auto lock = DataReadLock(this)) { if (rb != nullptr) { rb->write(env.data(), static_cast<int>(env.size())); } } But it doesn't like the data I'm trying to feed it.
-
How does a custom c++ interface with one of its display buffers?
I am working on a custom node. I am taking in external audio data and processing it with a downsampler, a gate, and a high-pass filter.
I want to plot the result of my processing chain to the display buffer. But I don't know how.
This node is an offline node - it doesn't process frames. Well... actually that is not true... it does, but only for parameter debouncing. It processes the loaded audio in full, rather than frames or blocks; though I don't think this is really relevant to the question, it is just some background.
-
RE: Summing plugins, snake oil or useful?
@griffinboy I'm not at that point yet tbh. Most of what I'm working on right now is audio analysis in realtime and sample playback. I do really want to do a distortion/saturation effect of my own at some point though!
-
RE: Global Cables Don't Work when compiled
@Christoph-Hart said in Global Cables Don't Work when compiled:
That's precisely what the global cables are made for. The only thing you have to watch out for is that you "create" them in your script before using them in the compiled effect.
Lovely stuff. Yeah, it did baffle me a bit the idea they wouldn't work, because I've been using them in a compiled plugin. But I haven't been compiling my networks when doing so.
Which brings up possibly a n00b question - when I compile a plugin, does that automatically compile networks, or only if the network has the allowCompilation flag enabled?
-
RE: Getting the sample rate for an externalData object?
Cool thanks! I'm still figuring out how all of this interfaces, but getting there and making good progress!