What is the process for writing my own module (not scriptnode)
-
@griffinboy That's awesome. I best get to learning!
-
Alright this is pushed now. You can send any type of data through a global cable and pick it up in your C++ node.
Here's a snippet that shows the usage. It contains a AudioWaveform connected to an external C++ node that will scan the audio file and send back some properties in a custom JSON object.
In order to use that example you'll also need to create a External C++ node and compile that before loading the snippet.
- Create an empty project
- Use Tools -> create C++ third party template. Use this exact string as class name:
data_node
, otherwise the compiler won't pick it up. - Replace the entire autogenerated code in the file
DspNetworks/ThirdParty/data_node.h
with the code below. - Compile the networks and load the snippet.
- Load an audio file into the waveform on the interface, sit back in awe and see how the console spits out a JSON that was generated in C++.
#pragma once #include <JuceHeader.h> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; enum class GlobalCables { dataCable = 0 }; using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(-389806413)>; template <int NV> struct data_node: public data::base, public cable_manager_t { SNEX_NODE(data_node); struct MetadataClass { SN_NODE_ID("data_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; data_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; }); } // 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["magnitude"] = magnitude; obj["RMS"] = rms; obj["length"] = length; obj["numChannels"] = channels; // just attach the current knob position so you know it's working. obj["knobValue"] = value; // send the JSON object back to HISE this->sendDataToGlobalCable<GlobalCables::dataCable>(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)); } } }; }
-
@Christoph-Hart Did you forget to put in the hise snippet Chris, or am I not getting it???
-
@Orvillain oops yeah haha will post it when I get back.
-
@Christoph-Hart No problem! Thanks for the quick turnaround. This is going to be seriously cool, I can feel it!!
-
@Christoph-Hart Hey dude, any ETA on that snippet?
-
@Orvillain There you go:
HiseSnippet 1230.3ocuWs0aiTCE1SZmtz.qDU.uOpOkBUQIj1rcYEhjlKKAHsQaJk8sJGONIl3wdjGOsMfVo9HOxOU9G.G6YlLSXydQQvlJUkyM6OeNemicFojDZTjTgbJe0xPJx4SbGuTnm2YNlIPC5B5c6h03qnQZz4KCwQQTejiyNO2X2Y+cQ1O+02cNliEDZtJD5ZIiP+IV.SmqcTqejw48w9zqXAE79jVCHRQGIWFCXYG2ZnPLYAdF8BrwsRtnuGGMG47kt0O6oD7IjSaVmdxjl9Owu4DxYjFSwM7e5o3F1u1rQiFHm854yzR0XMVSiPN6dtze434x6DIav0rH1DN0HTGMF14D08kbeyQznE0YNi6OJKIEgfUYTdJamjT1m6Nj4yVoOO08oVCd4QTLA5TZc3syZvqdQ3Uq.71.jbJ.ocSfzAtiIJVnN2hAOer6.glplhg5TQnj3KpzCNtcjfGBc0.7BZeEHrJhJMqU6XO3eG8rxkgZUj16VrxSE38sd8DyXBZ0YT8y4xIX9KjwZlX1Pr.pfpJqGgOPm5fgiFDnJvDjUpxgqLbnIfURUUzYrH.EcsZ37I.ynxzXAQyjhJF+Np7uWde.5QRv8PESnqbX21W09a7Nz6q7zJC7s9AK7qN5YYw5IEWH0zKEUrKP4WU16eaZ5zMZyjlTRN2b31fYC2V81BrhHNXBUcLjP3wzUNB0u0IEtuYRQQNKIopUvQoXffouLjJdSLYTZo1PfRQEzxnszmuHk9zN1mI+E7szoRU.h4anzqoqNxd.J10axy1FVEVLiBGDEz965XLY0zS3mIi94AlZZFB.v..LjpzLyY2oK8VX.RBgde2tznEZYn02fPov.8s.u2uBqOzp0xbgen0cLe87UJ9yGZElksF3amKZ2Bu9uDxludGGzqK8i4X85C.LiISM.Df055LcVhHldYwwn+mMU38EhG3NhoIy2LFKsALBUq+OvX5rzG61a5TJQmCvcc6+xO.CNcS1+xokYXOMa9ZU8hf3h3.KwpOia6ScLD5z4nN+gadKenhFhUzqji33kUhvAgb5K.fer2DtjrXL62nu9bhTl24FOpPliEBJOZaFmr26cpp9auRsZhtVwfVHW37OFtplP6jhNSNnjYpRhbsrd7wTguU3ugOoFquJgAFqmYLefD5Bp9NoZgsFk9cjyiLEj8cuSgCCghv0TUjgt57H2ZUg+Ps4b4clYCrThLTlr5FI4KCmKELhQUhGY3tcfLVny.O7FiqvLtg4ONNBFd5eoXL3r8gMFl2ERe3a60GSfr5xQXy.iCbMieA5JUUkrh1lCzbZiI2l7dlbqlUL6YOIYL.fA1gavLTfnA1SxCIacx83lgr2HLZVCJelKvb9Un8oZtCaZ6Kt.uK.LBqfXfqeS5Xxj.mGxDWmL820TBPCw2uRtNHaf5icGtLOlTy6At2rIpKcJNlqKrFmZ2bnC49r6EJzjUTBYFe5SuO4PkCDqzJXX4xEtRADJbXVyhcVfQBlzUJoyePz0FqDLOC2.G4bobQ.1RM2pau9fzHEfIJ4MjjYClC2GY0.IDQJ6anQ1aC2dG.OX8FBY8k50B7q21.arsAdx1F3oaafM21.ex1F3Yu6.M+dh1wZYPxDNDZ3ndVRniSOg4wx1KmP+Cf1ouMo
-
@Christoph-Hart Right! That is really cool! I'll dig into this when I get some chance and see how I can leverage this kind of system for my thing.
-
@Christoph-Hart I think this is going to be amazing. Been looking at the snippet, and it mostly makes sense. Being able to throw data from a custom node to the UI is exactly what I want to do.
So ultimately, consider the title of this thread to be null and void - custom node is where it is at! I've just got to port my HISEscript over to c++ and start properly learning scriptnode.
-
Thanks, I'm already making use of the new Global Cable feature!
It works great.Here we are sending the drawing straight from Hise into a c++ node for playback!
-
@griffinboy woah thats sick.
-
Noice. Are you doing any kind of band limiting with the drawings to reduce aliasing?
-
@griffinboy Coool!!! I've done this a while back using a hidden sliderpack with 1024 sliders with a drawing method and a SNEX node if I reckon well. It was working but wasn't very effective and aliasing was awful. Yours seems to be very responsive!
-
@griffinboy yes - very impressive - so you are going to use this to build a poly synth?
-
no that's next. I'm either going to use the Vital or Serum method. Serum's is simpler so I might go with that. Precomputed mip maps using fir filters. Easier to program but it will use a lot of memory... Maybe I'll end up switching later down the line to a realtime FFT based mip map method like vital!
There is clear aliasing in the video - but it's not 'awful' that's probably because I am processing the drawing in real time using a buffer. I've got interpolation between a few different points using splines. I'm probably going to expose smoothing as a parameter for the user. This wasn't done to affect the sound of the waveform, I am just ocd about drawing shaky lines
-
@griffinboy
This looks great! :-)
Do you have any kind of hint how to realize this? -
@griffinboy Nice!
-
@griffinboy said in What is the process for writing my own module (not scriptnode):
Precomputed mip maps using fir filters. Easier to program but it will use a lot of memory...
Yup, that's what I'm doing with the wavetable synthesiser module too. Sounds good enough for me.
-
That's good to know.
Doesn't it use a lot of memory? You'd have to store maps for each wavetable frame right, for various octaves?
Did you find a way around this or is that just the cost of precomputation?Vital gets around the memory issue by creating mip maps in real time. You probably already know, but it stores waveforms in the freq domain, silencing aliasing bins, and then inverse fft. Super nice, but the optimization sounds far more challenging since now the burden is on the cpu for efficient realtime fft.
-
This is potentially for a commercial project,
However if my the client says it's okay, I'll share how to do this.It's about time for a new c++ node tutorial I think...