• Draw Alert Window -> LookAndFeel

    3
    0 Votes
    3 Posts
    104 Views
    Oli UllmannO

    @d-healey thank you.

  • Panel Timer best practise

    7
    0 Votes
    7 Posts
    173 Views
    Oli UllmannO

    @aaronventure
    Good point, thanks to you! I actually only use it for UI animations. So it's actually useful that the timer is stopped when the UI is closed, as it doesn't consume CPU resources.

  • C++ Global Cables: How to manage multiple cables?

    Solved
    5
    0 Votes
    5 Posts
    153 Views
    griffinboyG

    Thanks I took a proper look and I was just misunderstanding it.

    The instructions tell you exactly what to do.

    I think I fell into a trap because I coded up my nodes when i only had one global cable.

    When you ask Hise for the GC C++ code for more than one cable, it becomes clearer how it's working.

    // Use this enum to refer to the cables, eg. this->setGlobalCableValue<GlobalCables::GC1>(0.4) enum class GlobalCables { GC1 = 0, GC2 = 1 }; // Subclass your node from this using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(70357), SN_GLOBAL_CABLE(70358)>;
  • On Note On

    6
    0 Votes
    6 Posts
    174 Views
    d.healeyD

    @Lawrence said in On Note On:

    I have opened that function and it still doesn't show what MIDI notes are coming in. I'll have to dig deeper.

    Snippet

  • custom peak meter.

    5
    0 Votes
    5 Posts
    257 Views
    Oli UllmannO

    @ulrik
    @d-healey
    Thank you very much, that has solved the problem! I should have had a look at the documentation! :-)

  • String.split() bug??

    5
    1 Votes
    5 Posts
    138 Views
    dannytaurusD

    Checked the source and the highlighted line in the split function substrings only the first character of the separator so it seems intentional.

    // HISE/hi_scripting/scripting/engine/JavascriptEngineObjects.cpp static var split(Args a) { const String str(a.thisObject.toString()); const String sep(getString(a, 0)); StringArray strings; if (sep.isNotEmpty()) strings.addTokens(str, sep.substring(0, 1), ""); // 👈 THIS LINE else // special-case for empty separator: split all chars separately for (String::CharPointerType pos = str.getCharPointer(); !pos.isEmpty(); ++pos) strings.add(String::charToString(*pos)); var array; for (int i = 0; i < strings.size(); ++i) array.append(strings[i]); return array; }
  • How do Presets Load Before onInit?

    Unsolved
    9
    0 Votes
    9 Posts
    338 Views
    clevername27C

    @Orvillain Yes I am.

  • Absolute X/Y for mouse?

    4
    0 Votes
    4 Posts
    173 Views
    ustkU

    @Dan-Korneff If they are real components then @aaronventure is right, but if they are child panels (from addChildPanel()) then I reckon having an issue not getting the right global position

  • Sampler Group Fade

    7
    0 Votes
    7 Posts
    159 Views
    Oli UllmannO

    @d-healey Yes, I have now tried it again. Apparently there is a problem in my project. It worked with a completely new project! Thank you very much! :-)

  • Broadcasters working inside HISE but not inside compiled plugin

    Solved
    15
    0 Votes
    15 Posts
    408 Views
    OrvillainO

    @aaronventure No idea tbh!!

  • Dynamic Sub ComboBox

    7
    0 Votes
    7 Posts
    323 Views
    Matt_SFM

    @Christoph-Hart Perfect, it's working now with your fix. Thank you very much!

    ComboBox1.set("items", itemList.join("\n"));

    Yeah, I almost never use this method, I didn't thought about it. Thank you also for the reminder :)

  • Frontend macros - display UI component?

    2
    0 Votes
    2 Posts
    203 Views
  • Is it possible to set a custom StepSize

    3
    0 Votes
    3 Posts
    128 Views
    Adam_GA

    @ustk you sir are the man, thank you

  • Updating Default Preset?

    Solved
    5
    0 Votes
    5 Posts
    188 Views
    clevername27C

    @Christoph-Hart Thank you, understood.

  • Colours

    7
    0 Votes
    7 Posts
    249 Views
    U

    @Shelest thanx )))

  • Best Way to Implement an Infinite Text Console?

    Solved
    5
    0 Votes
    5 Posts
    184 Views
    clevername27C

    @ustk Very cool - I gave up a lot quicker, lol. Thanks on the offer, very kind of you.

  • Controlling FX from a slider?

    6
    0 Votes
    6 Posts
    152 Views
    G

    @d-healey Thanks David

  • Open-Source Faust Transient Shaper

    11
    5 Votes
    11 Posts
    1k Views
    orangeO

    Does this code work for you?

    When I load this, as soon as I tweak the Sustain knob, it breaks the sound and creates sound glitch.

    declare name "Whetstone Transient Shaper"; declare author "Evermind"; import("stdfaust.lib"); WINDOW = 30 / 1000; low_cutoff = hslider("v:Whetstone Transient Shaper/[2]Low Cutoff[unit:Hz]",10,10,20000,10); high_cutoff = hslider("v:Whetstone Transient Shaper/[3]High Cutoff[unit:Hz]", 20000,20,20000,10); do_monitor = button("v:Whetstone Transient Shaper/h:[4]Monitoring/[0]Monitor selected band") : ba.toggle : vbargraph("v:Whetstone Transient Shaper/h:[4]Monitoring/[1]ON[style:led]", 0, 1); attack = hslider("v:Whetstone Transient Shaper/[0]Attack[unit:dB]",0,-12,+12,0.1) : ba.db2linear : si.smoo; sustain = hslider("v:Whetstone Transient Shaper/[1]Sustain[unit:dB]",0,-12,+12,0.1) : ba.db2linear : si.smoo; process = sp.stereoize(split_shape_merge); split_shape_merge = _ : split_active_and_passive : (_ <: _ * get_gain(_ : change)), ba.bypass1(do_monitor != 1, *(0)) :> _; //split signal into two parts $active, $passive //$active is resonant low- and high-passed between low_cutoff and high_cutoff respectively. This is fed directly to output if monitoring is on //$passive is resonant high- and low-passed between low_cutoff and high-cutoff respectively split_active_and_passive = _ <: (fi.resonhp(low_cutoff, 1, 1) : fi.resonlp(high_cutoff, 1, 1)), (fi.resonlp(low_cutoff, 1, 1) + fi.resonhp(high_cutoff, 1, 1)); //$active is analyzed with RMS over N ms divided by a lag filter applied to the RMS to get a rise speed over time to get $change change = ba.slidingRMS(ba.sec2samp(WINDOW)) <: _ - si.lag_ud(WINDOW, WINDOW); //If $change is positive, $gain is $attack //If $change is negative, $gain is $sustain //Smooth $gain get_gain(x) = ba.if(x > 0, attack,sustain) : si.lag_ud(WINDOW,WINDOW);
  • Aliasing when Resampling (C++ Project)

    Solved
    6
    0 Votes
    6 Posts
    218 Views
    griffinboyG

    @Christoph-Hart

    Yep, took it down immediately realising the sillyness!

    This is a good solution now, it seems to work fine.
    I've not done resampling before, I was tying myself in knots. Best to make something functional and robust before looking at optimisations.

    This seems to work so I'll leave it here in case anyone needed to see the interpolation method. I need to do more learning myself

    // ==========================| Sampler v0.58 : by Griffinboy |========================== #pragma once #include <JuceHeader.h> #include <array> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; // Template class for a sampler with a specified number of voices (NV) template <int NV> struct Griffin_Sampler : public data::base { SNEX_NODE(Griffin_Sampler); struct MetadataClass { SN_NODE_ID("Griffin_Sampler"); }; // ==========================| Node Properties |========================== // Define properties of the node static constexpr bool isModNode() { return true; } // Indicates this is a modulation node static constexpr bool isPolyphonic() { return NV > 1; } // Determines if the sampler is polyphonic static constexpr bool hasTail() { return false; } // Indicates if the node has a tail (sustain) static constexpr bool isSuspendedOnSilence() { return false; } // Determines if processing is suspended on silence static constexpr int getFixChannelAmount() { return 2; } // Fixed number of audio channels // Define the number of various components 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; // ==========================| Global Variables |========================== using InterpolatorType = index::hermite<index::unscaled<double, index::clamped<0, false>>>; static const int NUM_CHANNELS = 2; // Number of audio channels ExternalData data; // External data for the sampler span<dyn<float>, NUM_CHANNELS> sample; // Sample data for each channel double sr = 0.0; // Sample rate double file_sr = 0.0; // File sample rate // Looping and playback parameters double loopStart = 0.0; double loopEnd = 1.0; bool loopEnabled = false; double pitchModulation = 0.0; double speedMultiplier = 1.0; // Structure to hold voice-specific data struct VoiceData { double uptime = 0.0; // Time since the voice started playing double delta = 1.0 / (1 << 28); // Increment per sample int midiNote = 60; // MIDI note number bool isLooping = false; // Looping state bool isPlaying = false; // Playback state // Resets the voice data void reset() { uptime = 0.0; isPlaying = true; } // Advances the playback position and handles looping double bump(double loopStart, double loopEnd, double numSamples) { if (!isPlaying) return numSamples; double rv = uptime; uptime += delta; if (isLooping && uptime >= loopEnd && loopEnd > loopStart) { double loopLength = loopEnd - loopStart; uptime = loopStart + std::fmod(uptime - loopStart, loopLength); } else if (uptime >= numSamples) { uptime = numSamples; isPlaying = false; } return rv; } }; PolyData<VoiceData, NV> voiceData; // Container for voice data ModValue gate; // Modulation value for gate std::array<float, 128> pitchRatios; // Precomputed pitch ratios for MIDI notes // ==========================| Prepare |========================== // Prepares the sampler for playback void prepare(PrepareSpecs specs) { voiceData.prepare(specs); sr = specs.sampleRate; initPitchRatios(); if (data.numSamples > 0 && sr != 0.0) { updateAllVoiceDeltas(); } validateLoopPoints(); } // Initializes pitch ratios for MIDI notes void initPitchRatios() { constexpr int rootNote = 60; // Middle C for (int i = 0; i < 128; ++i) pitchRatios[i] = std::pow(2.0f, (i - rootNote) / 12.0f); } // Validates and adjusts loop points void validateLoopPoints() { if (data.numSamples > 0) { loopStart = juce::jlimit(0.0, 1.0, loopStart); loopEnd = juce::jlimit(loopStart, 1.0, loopEnd); } else { loopStart = 0.0; loopEnd = 1.0; } } // ==========================| Reset |========================== // Resets all voices void reset() { for (auto& v : voiceData) v.reset(); } // ==========================| Process |========================== // Processes audio data template <typename ProcessDataType> void process(ProcessDataType& data) { int numSamples = data.getNumSamples(); if (numSamples == 0 || this->data.numSamples == 0) return; DataReadLock sl(this->data); auto& v = voiceData.get(); auto& fixData = data.template as<ProcessData<NUM_CHANNELS>>(); auto audioBlock = fixData.toAudioBlock(); processBlock(audioBlock, v, numSamples); this->data.setDisplayedValue(v.uptime); gate.setModValueIfChanged(static_cast<double>(v.isPlaying)); } // Processes a block of audio samples void processBlock(juce::dsp::AudioBlock<float>& audioBlock, VoiceData& v, int numSamples) { auto* leftChannelData = audioBlock.getChannelPointer(0); auto* rightChannelData = audioBlock.getChannelPointer(1); double numSamplesDouble = static_cast<double>(data.numSamples); double loopStartSamples = loopStart * numSamplesDouble; double loopEndSamples = loopEnd * numSamplesDouble; for (int i = 0; i < numSamples; ++i) { double thisUptime = v.bump(loopStartSamples, loopEndSamples, numSamplesDouble); if (v.isPlaying) { InterpolatorType idx(thisUptime); leftChannelData[i] = sample[0][idx]; rightChannelData[i] = sample[1][idx]; } else { leftChannelData[i] = 0.0f; rightChannelData[i] = 0.0f; } } } template <typename FrameDataType> void processFrame(FrameDataType& fd) { // Implement if needed } // ==========================| Set Parameter |========================== // Sets a parameter value and validates if necessary template <int P> void setParameter(double v) { bool needsValidation = false; if (P == 0) { loopStart = v; needsValidation = true; } else if (P == 1) { loopEnd = v; needsValidation = true; } else if (P == 2) { loopEnabled = v > 0.5; } else if (P == 3) { pitchModulation = v; updateAllVoiceDeltas(); } else if (P == 4) { speedMultiplier = v; updateAllVoiceDeltas(); } if (needsValidation) { validateLoopPoints(); } } // ==========================| Create Parameters |========================== // Creates and registers parameters for the sampler void createParameters(ParameterDataList& data) { { parameter::data p("Loop Start", { 0.0, 1.0, 0.01 }); registerCallback<0>(p); p.setDefaultValue(0.0); data.add(std::move(p)); } { parameter::data p("Loop End", { 0.0, 1.0, 0.01 }); registerCallback<1>(p); p.setDefaultValue(1.0); data.add(std::move(p)); } { parameter::data p("Loop Enable", { 0.0, 1.0, 1.0 }); registerCallback<2>(p); p.setDefaultValue(0.0); data.add(std::move(p)); } { parameter::data p("Pitch", { -12.0, 12.0, 0.001 }); registerCallback<3>(p); p.setDefaultValue(0.0); data.add(std::move(p)); } { parameter::data p("Speed", { 0.5, 2.0, 0.01 }); registerCallback<4>(p); p.setDefaultValue(1.0); data.add(std::move(p)); } } // ==========================| External Data |========================== // Sets external data for the sampler void setExternalData(const ExternalData& ed, int index) { data = ed; ed.referBlockTo(sample[0], 0); ed.referBlockTo(sample[1], 1); if (data.numSamples > 0 && sr != 0.0) { updateAllVoiceDeltas(); validateLoopPoints(); } } // ==========================| Handle HISE Event |========================== // Handles MIDI events for note on and note off void handleHiseEvent(HiseEvent& e) { if (e.isNoteOn()) { auto& v = voiceData.get(); v.midiNote = e.getNoteNumber(); v.delta = calculateDelta(v.midiNote); v.reset(); v.isLooping = loopEnabled; } else if (e.isNoteOff()) { auto& v = voiceData.get(); if (v.midiNote == e.getNoteNumber()) { v.isPlaying = false; } } } // ==========================| Modulation Slot |========================== // Handles modulation changes bool handleModulation(double& value) { return gate.getChangedValue(value); } // ==========================| Helper Functions |========================== // Updates the delta for all voices based on current parameters void updateAllVoiceDeltas() { for (auto& v : voiceData) { v.delta = calculateDelta(v.midiNote); } } // Calculates the delta for a given MIDI note double calculateDelta(int midiNote) { double pitchRatio = std::pow(2.0, pitchModulation / 12.0); return (data.sampleRate / sr) * pitchRatios[midiNote] * pitchRatio * speedMultiplier; } }; }
  • Event data inside of External c++ node

    Solved
    6
    0 Votes
    6 Posts
    357 Views
    Christoph HartC

    @griffinboy said in Event data inside of External c++ node:

    I must not understand how it's working.

    The secret ingredient is this template container class:

    Link Preview Image HISE | ScriptNode | PolyData

    My Funky Summary

    favicon

    (docs.hise.dev)

    Relevant part:

    // If you want to change the data through a parameter callback // it's recommended to use the iterator. template void setParameter(double value) { // the iterator will either loop through // all data elements if it's outside a // voice rendering (most likely caused by a UI callback) // or just one element of the active voice (most likely used // by modulation inside the audio rendering). for(auto& d: data) d = (float)value; }

    So if you're modulating a parameter that is changing a PolyData object through a cable connection, it will change only the data for the current voice if the source event is happening in the audio rendering (like the control node) OR change all voices if the source event is outside of the audio rendering context (like when moving a slider).

    Be aware that this is multithreading-safe, so if you call this from the UI thread while the audio is rendering, it will still correctly detect that it should change all voices (it achieves this by registering the audio thread ID to the polyvoice manager that you will give it in the prepare call)

15

Online

1.8k

Users

12.1k

Topics

105.5k

Posts