HISE Logo Forum
    • Categories
    • Register
    • Login

    Next HISE Developer Hang

    Scheduled Pinned Locked Moved General Questions
    49 Posts 11 Posters 1.7k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • d.healeyD
      d.healey @HISEnberg
      last edited by

      @HISEnberg said in Next HISE Developer Hang:

      Loris: I am super interested in this library but haven't begun to use it so if someone has a project to share on this that would be great

      Link Preview Image
      GitHub - christophhart/loris-tools: A collection of sample analysis tools for HISE based on the Loris library

      A collection of sample analysis tools for HISE based on the Loris library - christophhart/loris-tools

      favicon

      GitHub (github.com)

      Libre Wave - Freedom respecting instruments and effects
      My Patreon - HISE tutorials
      YouTube Channel - Public HISE tutorials

      1 Reply Last reply Reply Quote 1
      • griffinboyG
        griffinboy @HISEnberg
        last edited by

        @HISEnberg

        Oh yes indeed, some laf stuff would be nice to look at, for the new css features and different ways of working.

        Also btw about ringbuffers, if you need something in particular, I could probably help you out I've done lots of these before for delay lines and Lufs calculations

        HISEnbergH 1 Reply Last reply Reply Quote 1
        • HISEnbergH
          HISEnberg @griffinboy
          last edited by

          @griffinboy Yes please if you have a C++ script you are willing to share that would be amazing!

          griffinboyG 1 Reply Last reply Reply Quote 1
          • griffinboyG
            griffinboy @HISEnberg
            last edited by griffinboy

            @HISEnberg

            I'll get back to you on this in a bit.

            But if you want to get started immediately, I can give you my LUFS script. But I'll come up with a simpler example to give you in a bit. I've been meaning to improve my buffer stuff, infact it's one thing I wish to talk to with Christoph about... because it's possible to integrate directly with the audio buffers in hise, and c++ nodes so that the interface script can also talk to the same buffers, but I've not managed to completely get it right.

            Anyway, Here's Lufs, it's a fairly old and awful script, and you won't be able to run this script but you can read it

            #pragma once
            #include <JuceHeader.h>
            #include <limits>
            #include <atomic>
            #include <cmath>
            #include "src/GlobalCables.h"
            
            #ifndef M_PI
            #define M_PI 3.14159265358979323846
            #endif
            
            namespace project
            {
                using namespace juce;
                using namespace hise;
                using namespace scriptnode;
            
                template <int NV> struct Lufs_In : public data::base
                {
                    SNEX_NODE(Lufs_In);
            
                    struct MetadataClass
                    {
                        SN_NODE_ID("Lufs_In");
                    };
            
                    static constexpr bool isModNode() { return true; }
                    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;
            
                    // Internal Parameters
                    float sampleRate = 44100.0f;
                    float blockSize = 512.0f;
                    float lufsBlockSize = 400.0f; // Default to 400ms
                    float overlap = 0.75f;        // 75% overlap
                    ModValue modValue;
            
                    // LUFS calculation
                    juce::AudioBuffer<float> filteredRingBuffer;
                    size_t ringBufferWritePos = 0;
                    size_t ringBufferSize = 0;
                    size_t hopSize = 0;
                    double runningSum = 0.0;
            
                    // JUCE IIR Filters
                    std::array<juce::IIRFilter, 2> preFilters;
                    std::array<juce::IIRFilter, 2> weightingFilters;
            
                    // LUFS result
                    float currentLUFS = -100.0f;
            
                    // Reusable filtered buffer
                    juce::AudioBuffer<float> filteredBuffer;
            
                    // Thread safety
                    std::atomic<bool> parametersChanged{ false };
                    juce::CriticalSection processLock;
            
                    void prepare(PrepareSpecs specs)
                    {
                        juce::ScopedLock sl(processLock);
                        sampleRate = static_cast<float>(specs.sampleRate);
                        blockSize = static_cast<float>(specs.blockSize);
                        updateInternalState();
                    }
            
                    void updateInternalState()
                    {
                        // Clamp and validate parameters
                        lufsBlockSize = juce::jlimit(100.0f, 4000.0f, lufsBlockSize); // 100ms to 4000ms
                        overlap = juce::jlimit(0.0001f, 0.9999f, overlap);            // 0.01% to 99.99%
            
                        // Calculate ring buffer size and ensure it's positive
                        ringBufferSize = static_cast<size_t>(sampleRate * lufsBlockSize / 1000.0f);
                        jassert(ringBufferSize > 0 && "ringBufferSize must be greater than 0");
            
                        hopSize = std::max(static_cast<size_t>(1), static_cast<size_t>(ringBufferSize * (1.0f - overlap)));
                        filteredRingBuffer.setSize(2, static_cast<int>(ringBufferSize));
                        ringBufferWritePos = 0;
                        runningSum = 0.0;
                        filteredBuffer.setSize(2, static_cast<int>(blockSize));
            
                        calculateFilterCoefficients();
                        reset();
                    }
            
                    void calculateFilterCoefficients()
                    {
                        const double epsilon = 1e-12; // Small value to prevent division by zero
            
                        // Pre-filter coefficients (as per your original code)
                        const double db = 3.999843853973347;
                        const double f0 = 1681.974450955533;
                        const double Q = 0.7071752369554196;
                        const double K = std::tan(M_PI * f0 / sampleRate);
            
                        const double Vh = std::pow(10.0, db / 20.0);
                        const double Vb = std::pow(Vh, 0.4996667741545416);
            
                        const double denominator0 = 1.0 + K / Q + K * K + epsilon; // Added epsilon
                        const double denominator1 = 2.0 * (K * K - 1.0) / denominator0;
                        const double denominator2 = (1.0 - K / Q + K * K) / denominator0;
                        const double numerator0 = (Vh + Vb * K / Q + K * K) / denominator0;
                        const double numerator1 = 2.0 * (K * K - Vh) / denominator0;
                        const double numerator2 = (Vh - Vb * K / Q + K * K) / denominator0;
            
                        // Validate coefficients
                        jassert(!std::isnan(numerator0) && !std::isinf(numerator0));
                        jassert(!std::isnan(numerator1) && !std::isinf(numerator1));
                        jassert(!std::isnan(numerator2) && !std::isinf(numerator2));
                        jassert(!std::isnan(denominator1) && !std::isinf(denominator1));
                        jassert(!std::isnan(denominator2) && !std::isinf(denominator2));
            
                        juce::IIRCoefficients preCoeffs(numerator0, numerator1, numerator2,
                            1.0, denominator1, denominator2);
            
                        // Weighting filter coefficients (as per your original code)
                        const double f0_weighting = 38.13547087602444;
                        const double Q_weighting = 0.5003270373238773;
                        const double K_weighting = std::tan(M_PI * f0_weighting / sampleRate);
            
                        const double denominator0_weighting = 1.0 + K_weighting / Q_weighting + K_weighting * K_weighting + epsilon; // Added epsilon
                        const double denominator1_weighting = 2.0 * (K_weighting * K_weighting - 1.0) / denominator0_weighting;
                        const double denominator2_weighting = (1.0 - K_weighting / Q_weighting + K_weighting * K_weighting) / denominator0_weighting;
            
                        // Validate weighting coefficients
                        jassert(!std::isnan(denominator0_weighting) && !std::isinf(denominator0_weighting));
                        jassert(!std::isnan(denominator1_weighting) && !std::isinf(denominator1_weighting));
                        jassert(!std::isnan(denominator2_weighting) && !std::isinf(denominator2_weighting));
            
                        juce::IIRCoefficients weightingCoeffs(1.0, -2.0, 1.0,
                            1.0, denominator1_weighting, denominator2_weighting);
            
                        for (int ch = 0; ch < 2; ++ch)
                        {
                            preFilters[ch].setCoefficients(preCoeffs);
                            weightingFilters[ch].setCoefficients(weightingCoeffs);
                        }
                    }
            
                    template <typename ProcessDataType>
                    void process(ProcessDataType& data)
                    {
                        auto& fixData = data.template as<ProcessData<2>>();
                        auto numSamples = fixData.getNumSamples();
            
                        if (numSamples == 0)
                            return;
            
                        // Check if the block is silent
                        bool isSilent = true;
                        for (int ch = 0; ch < 2; ++ch)
                        {
                            auto channelData = fixData[ch];
                            for (int i = 0; i < numSamples; ++i)
                            {
                                if (channelData[i] > 0.0001f)
                                {
                                    isSilent = false;
                                    break;
                                }
                            }
                            if (!isSilent)
                                break;
                        }
            
                        // If the block is silent, return early
                        if (isSilent)
                        {
                            return;
                        }
            
                        if (parametersChanged.exchange(false))
                        {
                            juce::ScopedLock sl(processLock);
                            updateInternalState();
                        }
            
                        juce::ScopedLock sl(processLock);
            
                        // Ensure the filteredBuffer is large enough
                        if (filteredBuffer.getNumSamples() < numSamples)
                            filteredBuffer.setSize(2, numSamples);
            
                        // Create AudioBlock objects for input and filtered data
                        auto inputBlock = fixData.toAudioBlock();
                        juce::AudioBuffer<float> filteredAudioBuffer(filteredBuffer);
                        auto filteredBlock = juce::dsp::AudioBlock<float>(filteredAudioBuffer);
            
                        for (int ch = 0; ch < 2; ++ch)
                        {
                            // Copy input data to filteredBuffer
                            juce::FloatVectorOperations::copy(filteredBlock.getChannelPointer(ch),
                                inputBlock.getChannelPointer(ch),
                                static_cast<int>(numSamples));
            
                            // Apply pre-filter
                            preFilters[ch].processSamples(filteredBlock.getChannelPointer(ch), numSamples);
            
                            // Apply weighting filter
                            weightingFilters[ch].processSamples(filteredBlock.getChannelPointer(ch), numSamples);
                        }
            
                        // Store filtered samples in ring buffer and update LUFS
                        for (size_t i = 0; i < numSamples; ++i)
                        {
                            double leftSample = static_cast<double>(filteredBlock.getSample(0, static_cast<int>(i)));
                            double rightSample = static_cast<double>(filteredBlock.getSample(1, static_cast<int>(i)));
            
                            // Subtract the square of the oldest samples
                            float oldLeftSample = filteredRingBuffer.getSample(0, static_cast<int>(ringBufferWritePos));
                            float oldRightSample = filteredRingBuffer.getSample(1, static_cast<int>(ringBufferWritePos));
                            runningSum -= static_cast<double>(oldLeftSample * oldLeftSample + oldRightSample * oldRightSample);
            
                            // Add the square of the new samples
                            runningSum += leftSample * leftSample + rightSample * rightSample;
            
                            // Update ring buffer
                            filteredRingBuffer.setSample(0, static_cast<int>(ringBufferWritePos), static_cast<float>(leftSample));
                            filteredRingBuffer.setSample(1, static_cast<int>(ringBufferWritePos), static_cast<float>(rightSample));
                            ringBufferWritePos = (ringBufferWritePos + 1) % ringBufferSize;
            
                            // Calculate LUFS when we've moved by hopSize or if hopSize is 0
                            if (hopSize == 0 || ringBufferWritePos % hopSize == 0)
                            {
                                calculateLUFS();
                            }
                        }
                    }
            
                    void calculateLUFS()
                    {
                        jassert(ringBufferSize > 0 && "ringBufferSize must be greater than 0");
            
                        double meanSquared = runningSum / (2.0 * ringBufferSize);
            
                        if (meanSquared > 1e-12)
                        {
                            currentLUFS = static_cast<float>(-0.691 + 10.0 * std::log10(meanSquared));
                            currentLUFS = std::clamp(currentLUFS, -100.0f, 0.0f);
                        }
                        else
                        {
                            currentLUFS = -100.0f;
                        }
            
                        modValue.setModValue(currentLUFS + 2.96f);
                    }
            
                    float getLUFS() const { return currentLUFS; }
            
                    void handleHiseEvent(HiseEvent& e) {}
            
                    void reset()
                    {
                        juce::ScopedLock sl(processLock);
                        ringBufferWritePos = 0;
                        runningSum = 0.0;
                        filteredRingBuffer.clear();
                        for (int ch = 0; ch < 2; ++ch)
                        {
                            preFilters[ch].reset();
                            weightingFilters[ch].reset();
                        }
                        currentLUFS = -100.0f;
                    }
            
                    template <typename T> void processFrame(T& data) {}
            
                    int handleModulation(double& value)
                    {
                        return modValue.getChangedValue(value);
                    }
            
                    template <int P> void setParameter(double v)
                    {
                        if (P == 0)
                        {
                            lufsBlockSize = static_cast<float>(v);
                            parametersChanged.store(true);
                        }
                        else if (P == 1)
                        {
                            overlap = static_cast<float>(v);
                            parametersChanged.store(true);
                        }
                        reset();
                    }
            
                    void createParameters(ParameterDataList& data)
                    {
                        {
                            parameter::data p("Buffer Size (ms)", { 100.0, 4000.0 });
                            registerCallback<0>(p);
                            p.setDefaultValue(2800.0);
                            data.add(std::move(p));
                        }
                        {
                            parameter::data p("Overlap", { 0.0001, 0.9999 });
                            registerCallback<1>(p);
                            p.setDefaultValue(0.99);
                            data.add(std::move(p));
                        }
                    }
                };
            }
            
            1 Reply Last reply Reply Quote 3
            • ustkU
              ustk @clevername27
              last edited by ustk

              @clevername27

              DISCUSSIONS:

              • Advanced graphics rendering, past the poorly supported OpenGL shader on mac, what comes next?
              • Issue reports and pull-requests aren't fixed/merged as fast as our ego requests to live in peace, "yeah I want that thing fixed before I even formulate a thought about it..." ☺
              • SNEX issues/instabilities/console improvements... Not adding new functionalities though, just a more reliable environment for sketching more efficiently

              About @Christoph-Hart attending the meeting or not, I think it is a necessity though. Many of the questions that will be raised can be answered quickly without us discussing the piece of meet endlessly. Not speaking that bunch of questions might not be even discussable without him, like, "where does your love for Comic Sans MS come from..."

              Can't help pressing F5 in the forum...

              d.healeyD clevername27C 2 Replies Last reply Reply Quote 1
              • d.healeyD
                d.healey @ustk
                last edited by

                @ustk said in Next HISE Developer Hang:

                I think it is a necessity though.

                I agree, at least for all the topics mentioned here.

                Maybe it would be better to do a show and tell type thing since Christoph can't attend.

                Libre Wave - Freedom respecting instruments and effects
                My Patreon - HISE tutorials
                YouTube Channel - Public HISE tutorials

                clevername27C 1 Reply Last reply Reply Quote 1
                • clevername27C
                  clevername27 @Oli Ullmann
                  last edited by clevername27

                  This post is deleted!
                  1 Reply Last reply Reply Quote 1
                  • clevername27C
                    clevername27 @griffinboy
                    last edited by clevername27

                    @griffinboy said in Next HISE Developer Hang:

                    broadcasters / AI

                    I can help with the theory on publish and subscribe (broadcasters) and AI. If you tell me what you're looking to do, I can prepare something for the Hang.

                    1 Reply Last reply Reply Quote 0
                    • clevername27C
                      clevername27 @Oli Ullmann
                      last edited by

                      @Oli-Ullmann Is there another day you suggest I replace it with?

                      Oli UllmannO 1 Reply Last reply Reply Quote 0
                      • clevername27C
                        clevername27 @Oli Ullmann
                        last edited by

                        @Oli-Ullmann I'm thinking one thing we could do is, as a group, create a list of technical questions for @Christoph-Hart that haven't been answered on the forum. Given the specificity and focus of your question, perhaps that would address it?

                        1 Reply Last reply Reply Quote 2
                        • clevername27C
                          clevername27 @HISEnberg
                          last edited by

                          @HISEnberg I'm happy to help you one-on-one with package installers and HISE graphics over Zoom. We could also trouble-shoot the audio. DM me if interested.

                          1 Reply Last reply Reply Quote 0
                          • clevername27C
                            clevername27 @d.healey
                            last edited by clevername27

                            @d-healey said in Next HISE Developer Hang:

                            I agree, at least for all the topics mentioned here.

                            Maybe it would be better to do a show and tell type thing since Christoph can't attend.

                            Certainly helpful, tho consider perhaps @griffinboy is helping @HISEnberg with ring buffers. I can help him with package installers and graphics processing. You were able to help @Oli-Ullmann with Father Christmas. Idea: If we establish what we're going to discuss before-hand, we can establish what we have the necessary expertise for, as a group. @Christoph-Hart is kind and generous—my read from him on our last meeting was that we shouldn't wait for him, and he mentioned something about February. If there is anything I can do to make the event more meaningful for you, consider it done. (Just my $0.02.)

                            1 Reply Last reply Reply Quote 2
                            • Oli UllmannO
                              Oli Ullmann @clevername27
                              last edited by

                              @clevername27

                              Maybe you could add two more days in the week from December 9 to 15? At least here in Germany, that's a normal working week.

                              A list of questions for Christoph is a great idea! :-)

                              clevername27C 1 Reply Last reply Reply Quote 0
                              • clevername27C
                                clevername27 @ustk
                                last edited by clevername27

                                @ustk said in Next HISE Developer Hang:

                                Advanced graphics rendering, past the poorly supported OpenGL shader on mac, what comes next?

                                I can help you with this, either on the forum, DM, or we can chat on Zoom.

                                @ustk said in Next HISE Developer Hang:

                                Many of the questions that will be raised can be answered quickly without us discussing the piece of meet endlessly.

                                That's a good point (and likewise raised by @d-healey). If we sketch out what is to be discussed before-hand, and identify who has the necessary expertise, we can have a nearly-commensurate level of efficiency. At various times, I've seen you and everyone else answer complex questions in just a few sentences. (I'm bad at HISE and a mediocre programmer, so perhaps not a good example, lol). Perhaps a way forward is for us, at the meeting, to compose a specific list of questions for @Christoph-Hart that only he can answer, which I also think he'll appreciate and be proactive on. (Just my $0.02.) I also think that if we ply @d-healey with drinks and exotic chocolates, he can take us all to school.

                                d.healeyD 1 Reply Last reply Reply Quote 1
                                • d.healeyD
                                  d.healey @clevername27
                                  last edited by

                                  @clevername27 said in Next HISE Developer Hang:

                                  exotic chocolates

                                  👍

                                  Libre Wave - Freedom respecting instruments and effects
                                  My Patreon - HISE tutorials
                                  YouTube Channel - Public HISE tutorials

                                  1 Reply Last reply Reply Quote 0
                                  • clevername27C
                                    clevername27 @HISEnberg
                                    last edited by

                                    @HISEnberg said in Next HISE Developer Hang:

                                    @Lindon 's method

                                    I've seen his installer and it's dope. If you hit him on the forum, or over DM, I'm sure he'll give you a demo.

                                    LindonL 1 Reply Last reply Reply Quote 0
                                    • clevername27C
                                      clevername27 @Oli Ullmann
                                      last edited by

                                      @Oli-Ullmann Tell me a specific time and day, and I'll add it.

                                      Oli UllmannO 1 Reply Last reply Reply Quote 0
                                      • Oli UllmannO
                                        Oli Ullmann @clevername27
                                        last edited by

                                        @clevername27
                                        Ok, then I would suggest Friday, December 13th from 5pm to 6:30pm.

                                        d.healeyD 1 Reply Last reply Reply Quote 0
                                        • d.healeyD
                                          d.healey @Oli Ullmann
                                          last edited by

                                          @Oli-Ullmann said in Next HISE Developer Hang:

                                          5pm to 6:30pm.

                                          Time zone?

                                          Libre Wave - Freedom respecting instruments and effects
                                          My Patreon - HISE tutorials
                                          YouTube Channel - Public HISE tutorials

                                          Oli UllmannO 1 Reply Last reply Reply Quote 0
                                          • Oli UllmannO
                                            Oli Ullmann @d.healey
                                            last edited by

                                            @d-healey
                                            Ah sorry, I was referring to Germany, i.e. Berlin. The previous Doodel vote also showed me this time, which is why I adopted it.

                                            clevername27C 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post

                                            66

                                            Online

                                            1.7k

                                            Users

                                            11.7k

                                            Topics

                                            102.2k

                                            Posts