• Setting server headers for downloads

    23
    0 Votes
    23 Posts
    3k Views
    T

    @d-healey @Christoph-Hart I'm running into a similar problem where I need to include some headers in the download call. Have you found a solution to this issue?

  • Proper MIDI automation of controls across multiple MIDI channels?

    25
    0 Votes
    25 Posts
    3k Views
    A

    @Lindon said in Proper MIDI automation of controls across multiple MIDI channels?:

    cant recall who made these but I use them alot - so thanks whoever...

    Yeah check the topic I linked to, these are the same docs. It's @modularsamples

  • add/remove modulators in compiled plug-in

    Solved
    3
    0 Votes
    3 Posts
    286 Views
    Oli UllmannO

    @d-healey
    That's what I thought. Thank you very much for your answer.

  • Routing Matrix for 9 Samplers, Insert & Send FX.

    9
    0 Votes
    9 Posts
    681 Views
    ChazroxC

    @d-healey reading up on that now! 🙏

  • Audio stops playing while mics are being purged.

    5
    1 Votes
    5 Posts
    1k Views
    SimonS

    @Christoph-Hart I understand it's probably a pain to implement, but in Kontakt you can load or unload samples and it doesn't stop playback, so it is a small deviation from what people are used to.

  • Creating and Accessing Encoded, User-facing Containers?

    8
    0 Votes
    8 Posts
    667 Views
    C

    @aaronventure Help me with the ScriptNode, and I'll help you with this? (And thanks.)

  • LAF - fillRect - modulating x value doesn't do anything

    4
    0 Votes
    4 Posts
    353 Views
    DanHD

    @d-healey @Lindon ah yeah, good point 😆

    Sorted now, thanks!

  • Knob LAF - Curve line (or maybe use a bit of an arc?)

    11
    0 Votes
    11 Posts
    664 Views
    OrvillainO

    @DanH said in Knob LAF - Curve line (or maybe use a bit of an arc?):

    @Orvillain In my original example it is a rectangle. In @ustk's solution it is an arc.

    Yes. It never should have been is my point!😁

  • Issue with .getLocalBounds() ?

    3
    0 Votes
    3 Posts
    261 Views
    VirtualVirginV

    @d-healey Thanks. I thought "getLocalBounds()" would get the x and y from the parent component x and y , not the global x.

  • How many lines were written?

    Unsolved
    9
    0 Votes
    9 Posts
    609 Views
    A

    @Oli-Ullmann yes but the Graphics method takes in your current font for accurate string width

  • Delayed broadcaster fires twice

    Solved
    11
    1 Votes
    11 Posts
    682 Views
    ustkU

    @d-healey @orange Interesting, the zoom handle I was using is crashing a lot too so I removed it... I'll study and try that implementation, thanks, Dave! ☺

  • Images Stopped Loading in the Compiled Plugin

    Solved
    3
    0 Votes
    3 Posts
    282 Views
    C

    @d-healey Hi Dave, thanks for answering. Yes, I have. I don't remember changing anything in my code…but I must have changed something?

  • Tempo Sync Not changing Knob Modes. Change Knob Modes??

    Solved
    5
    0 Votes
    5 Posts
    305 Views
    ChazroxC

    @d-healey I see it now. Thank you sir. Just making sure I understand.

  • "../../" Includes a bad practice?

    2
    0 Votes
    2 Posts
    217 Views
    d.healeyD

    @iamlamprey said in "../../" Includes a bad practice?:

    I'm migrating everything over to Rhapsody

    DM me, the original Rhapsody boilerplate is not going to be updated, I have a new toolkit that is much better but it's not quite ready for release, but very nearly.

    You should always place scripts in the Scripts folder.

    For the Rhapsody boilerplate (and the new version) you should include it as a git submodule, then it's really easy to get the latest changes with a simple git pull

  • [bug] Timestretch causing pops and white noise

    3
    2 Votes
    3 Posts
    315 Views
    d.healeyD

    @Christoph-Hart Still getting nasty sounds with time stretching.

    Here's an audio file rendered directly from HISE. You can hear the distortion at the beginning, this is just by enabling time-stretching for the sampler without changing the ratio.

    HiseExport.wav

  • CSS Label Hover Question

    2
    0 Votes
    2 Posts
    269 Views
    GabG

    @Casmat Seems to be either a bug or it needs a proper hover state like the button which I'm not sure but its the same thing for Scriptlabel and Focus don't seem to do anything.

    HiseSnippet 1208.3ocsV80aaaCDmJIpqVqcXAXe.DxdnNC0IR9OINtXXNwwYKnIoAycEaXOTPKQYwEJRAJpj3MLf8Ea.6izdeOrcTTwVNwcqvXU1R.7Nd28iGO963kRQ.IKSHQVNudZJAY8D6QS4p3AwXJGc5wHqOxNRHySPGMMEmkQBQVVq+0ZkV01.U77me0QXFlGPlKBgdifFPNilPUykdY+WRYrSvgjWSSpL618OMPvGHXhb.Hqa6gRwAWgmPt.qm1Z1nuAmEir9B6lMiB5540sUqNdXbqPLtsOo0Ajn8hB53cfW33Ndvu8PVOZXHUIjiTXEICYswQhvoihE2vMA3MzL5XFQOvGMBhrQ7IBVndIpkhFDSYgWdWFJCAd4x44q0M4qOy9bZHcl744sOsPg6bKpl.sVaQ3s9BvyuJ77p.uk.IqJPZCCj1zdTfjlplqQimO19TthHivv9TUnXlKZseec6ABXFb0NI3qHmHgAyrndaOum6Be19ENNvdUlx8Zrz8L7XBy28KcuyxID0.QRpfCCpukQ8VKZCaLaPVVEaBjDHIblH.yNSHt5Pd3IDBqt1JGmc20cXBlx18kjolvAeibLNYmLB.QFkSFolxHihIDHpN0b1IqXQwzy24WbbgmwBYHQ1ykCX6EERBfzJH3y8NR+yHaLT2MQJx4g8bURLOKEKALZTFA.tQDNgxl1y8YGJoX1ypnIi9yjdt64kdaEg2PnShU8fvyBMhK9DRyRYXvMQLR4z+o7LEMZZi.SZoma.Qm8MJwL5DdCphjjsnBE4VUiBs8bYjnRjpSIMhKCsuQVJNLjxmzy0y0eFFM9PuPoJp.7Alwb81oYlKAmQZHxKcXBVNgBp6dmgE1.DCIk4IFrE9C00kGyPUVLNTbCD+zaKdaBuxIiw0gJox+63CF7qNKre0KVbMQVtqs7vznS5sKKRMKiR6kDoVauvt9MwPxrH1NaYpzbLUq5ZpGTKZJ2f4EkyCzIJWA+Bgh7Jd8sAjVC7i68UEEsTc5pdofwHxkpVSLJ+2LrNOOYLQ9b3rDKmLahvg+EYTre2LJUI7Jq1pLQA+TNU8pTB+cQChJO4pYeJQELUUA2ymTx8bTtRI3HJPwTy1LvGU.4pMIPe2oGiU36bD3SHNoDohpWBVGStFZhXH0pYeLI6JkHE5GLihAZWYB6SKCawdXQTersY+Dc67.db+oyGD1+FZnJdlf948MmXlI429w95xKM04fQiLrOnwStqO0Sra2b+tM882uYWj9nYUEcOX+CZ0oiGR6gEsv+.nAlGJOMD1oFhChApMSpdtSZBf3Qat4l+UAPprdez6QZtxJ9zpq312aE+GSt+JFcQezC6m.cxDg4vQuEauouDPoBnBcgdJ59FZJkoUujv+a87deg3l1WRUAwKGiqsDLB0geHvX4MEdp8vnHRfZN.2v9ju+Cy0BPeKPcCr8miURJTOXeQdxHntJf.QmyILMCg0Z5ZKyXO8XcFXDgGVL3ugmRk95wVkJ8uSIJAGHEuMvv.nuKxiKj.XhWbusZ1mqG6tjC9IvUidaPvht5AF1bUMr0pZX6U0vNqpg6spFt+pZX2+aC02b8vbkHwbrAgN+xgED0VVC4XnBrnZE8O7uBewC

    Maybe the answer is in that

    https://docs.hise.dev/scripting/scripting-api/scriptlabel/index.html#setstylesheetclass

  • MIDI CC Factory Assignments?

    2
    0 Votes
    2 Posts
    202 Views
    d.healeyD

    @Chazrox Assign them via midi learn menu, then save the preset.

  • Math.randInt / Stop duplicate returns.

    Solved
    3
    0 Votes
    3 Posts
    287 Views
    ChazroxC

    @d-healey Sweet! I'll watch this now. Hope you're having a great day brother! Thanks 🙏

  • added scriptable FFT

    28
    0 Votes
    28 Posts
    3k Views
    OrvillainO

    Another thing to bear in mind - it seems to me that you cannot run an FFT over a typical array. It HAS to be a buffer.

  • Multiple start and end ranges in a single AudioLoopPlayer

    10
    0 Votes
    10 Posts
    712 Views
    griffinboyG

    @Orvillain

    I'll give you a peek, but this isn't meant for other people to try and use yet (it's not properly cleaned up or optimized).
    This is the polyphonic sampler node. We then use the Hise Midi processing scripts to target notes / voices and send event data to control the parameters in this node. Meaning that we end up with polyphonic control over the parameters on a voice / note basis. The c++ sampler allows for safe modulation of any parameter, which means we can simply update it from the Hise scripts without worrying.

    #pragma once #include <JuceHeader.h> #include <array> #include <vector> #include <cmath> #include <algorithm> #include <random> #include <limits> #include <new> #include <atomic> namespace project { #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #if defined(_MSC_VER) #define FORCE_INLINE __forceinline #else #define FORCE_INLINE inline __attribute__((always_inline)) #endif using namespace juce; using namespace hise; using namespace scriptnode; static constexpr int FIXED_SHIFT = 16; static constexpr int64_t FIXED_ONE = (int64_t)1 << FIXED_SHIFT; static constexpr int64_t FIXED_MASK = FIXED_ONE - 1; struct SampleSettings { double pitchOffsetCents = 0.0; float volumeMult = 1.0f; float panning = 0.0f; float playbackStartInSamples = 0.0f; float loopStartInSamples = 0.0f; float loopEndInSamples = 0.0f; bool loopMode = false; float xfadeLengthInSamples = 0.0f; }; struct SamplePlayback { const float* sourceL = nullptr; const float* sourceR = nullptr; bool active = false; float amplitude = 1.0f; SampleSettings settings; int64_t phaseAcc = 0; int64_t phaseInc = 0; float playbackStart = 0.0f; float loopStart = 0.0f; float loopEnd = 0.0f; SamplePlayback() noexcept = default; SamplePlayback(const std::array<const float*, 2>& src, float amp, const SampleSettings& s, int bufferLength, double baseDelta) { sourceL = src[0]; sourceR = src[1]; settings = s; amplitude = amp * s.volumeMult; active = true; float pbStart = s.playbackStartInSamples; if (pbStart < 0.f) pbStart = 0.f; if (pbStart > float(bufferLength - 1)) pbStart = float(bufferLength - 1); playbackStart = pbStart; float lStart = s.loopStartInSamples; float lEnd = s.loopEndInSamples; if (!s.loopMode) { lStart = pbStart; lEnd = s.loopEndInSamples; } else { if (lStart < 0.f) lStart = 0.f; if (lStart > float(bufferLength - 1)) lStart = float(bufferLength - 1); if (lEnd < 0.f) lEnd = 0.f; if (lEnd > float(bufferLength - 1)) lEnd = float(bufferLength - 1); } loopStart = lStart; loopEnd = lEnd; phaseAcc = (int64_t)std::llround(pbStart * FIXED_ONE); double centsFact = std::pow(2.0, (s.pitchOffsetCents / 1200.0)); double effectiveSpeed = baseDelta * centsFact; phaseInc = (int64_t)std::llround(effectiveSpeed * FIXED_ONE); } // Marked as inline to help reduce overhead in performance-critical paths. FORCE_INLINE void updateSettings(const SampleSettings& s, int bufferLength) { settings = s; float pbStart = s.playbackStartInSamples; if (pbStart < 0.f) pbStart = 0.f; if (pbStart > float(bufferLength - 1)) pbStart = float(bufferLength - 1); playbackStart = pbStart; float lStart = s.loopStartInSamples; float lEnd = s.loopEndInSamples; if (!s.loopMode) { lStart = pbStart; lEnd = s.loopEndInSamples; } else { if (lStart < 0.f) lStart = 0.f; if (lStart > float(bufferLength - 1)) lStart = float(bufferLength - 1); if (lEnd < 0.f) lEnd = 0.f; if (lEnd > float(bufferLength - 1)) lEnd = float(bufferLength - 1); } loopStart = lStart; loopEnd = lEnd; } // This synthesis function is critical in performance: it is FORCE_INLINE to encourage inlining // and precomputes frequently used values outside the inner loops. FORCE_INLINE int vectorSynthesize(float* outL, float* outR, int blockSize, const AudioBuffer<float>* preXfadeBuffer, float preXfadeLength) { if (!active) return 0; int processed = 0; const float invFixedOne = 1.f / FIXED_ONE; const float leftGain = 0.5f * (1.f - settings.panning); const float rightGain = 0.5f * (1.f + settings.panning); const float ampLeft = amplitude * leftGain; const float ampRight = amplitude * rightGain; const bool loopEnabled = settings.loopMode; const float X = settings.xfadeLengthInSamples; const float endSample = loopEnd; const float crossfadeStartF = loopEnabled ? (endSample - X) : 0.f; const float piOverTwo = float(M_PI * 0.5f); const int64_t fixedEnd = int64_t(endSample * FIXED_ONE); int64_t fixedLoopStart = 0; int64_t fixedCrossfadeStart = 0; int64_t fixedX = 0; if (loopEnabled) { fixedLoopStart = int64_t(loopStart * FIXED_ONE); fixedX = (int64_t)std::llround(X * FIXED_ONE); fixedCrossfadeStart = fixedEnd - fixedX; } while (processed < blockSize) { if (!loopEnabled) { if (phaseAcc >= fixedEnd) { active = false; break; } int64_t samplesToBoundary = (fixedEnd - phaseAcc + phaseInc - 1) / phaseInc; int n = (samplesToBoundary > (blockSize - processed)) ? (blockSize - processed) : int(samplesToBoundary); for (int i = 0; i < n; i++) { int idx = int(phaseAcc >> FIXED_SHIFT); float frac = float(phaseAcc & FIXED_MASK) * invFixedOne; float sampL = sourceL[idx] + frac * (sourceL[idx + 1] - sourceL[idx]); float sampR = sourceR[idx] + frac * (sourceR[idx + 1] - sourceR[idx]); outL[processed + i] += sampL * ampLeft; outR[processed + i] += sampR * ampRight; phaseAcc += phaseInc; } processed += n; } else { if (phaseAcc < fixedLoopStart) { int64_t samplesToBoundary = (fixedLoopStart - phaseAcc + phaseInc - 1) / phaseInc; int n = (samplesToBoundary > (blockSize - processed)) ? (blockSize - processed) : int(samplesToBoundary); for (int i = 0; i < n; i++) { int idx = int(phaseAcc >> FIXED_SHIFT); float frac = float(phaseAcc & FIXED_MASK) * invFixedOne; float sampL = sourceL[idx] + frac * (sourceL[idx + 1] - sourceL[idx]); float sampR = sourceR[idx] + frac * (sourceR[idx + 1] - sourceR[idx]); outL[processed + i] += sampL * ampLeft; outR[processed + i] += sampR * ampRight; phaseAcc += phaseInc; } processed += n; } else if (phaseAcc >= fixedEnd) { int64_t excess = phaseAcc - fixedEnd; phaseAcc = fixedLoopStart + fixedX + excess; continue; } else if (phaseAcc < fixedCrossfadeStart) { int64_t samplesToBoundary = (fixedCrossfadeStart - phaseAcc + phaseInc - 1) / phaseInc; int n = (samplesToBoundary > (blockSize - processed)) ? (blockSize - processed) : int(samplesToBoundary); for (int i = 0; i < n; i++) { int idx = int(phaseAcc >> FIXED_SHIFT); float frac = float(phaseAcc & FIXED_MASK) * invFixedOne; float sampL = sourceL[idx] + frac * (sourceL[idx + 1] - sourceL[idx]); float sampR = sourceR[idx] + frac * (sourceR[idx + 1] - sourceR[idx]); outL[processed + i] += sampL * ampLeft; outR[processed + i] += sampR * ampRight; phaseAcc += phaseInc; } processed += n; } else { int64_t samplesToBoundary = (fixedEnd - phaseAcc + phaseInc - 1) / phaseInc; int n = (samplesToBoundary > (blockSize - processed)) ? (blockSize - processed) : int(samplesToBoundary); if (preXfadeBuffer) { const float* xfadeL = preXfadeBuffer->getReadPointer(0); const float* xfadeR = preXfadeBuffer->getReadPointer(1); int xfadeBufferLen = preXfadeBuffer->getNumSamples(); for (int i = 0; i < n; i++) { int idxPhase = int(phaseAcc >> FIXED_SHIFT); float frac = float(phaseAcc & FIXED_MASK) * invFixedOne; float currentP = float(phaseAcc) * invFixedOne; float posInXfade = currentP - crossfadeStartF; int idx = int(posInXfade); float subFrac = posInXfade - idx; if (idx < 0) idx = 0; else if (idx >= xfadeBufferLen - 1) idx = xfadeBufferLen - 2; float sampL = xfadeL[idx] + subFrac * (xfadeL[idx + 1] - xfadeL[idx]); float sampR = xfadeR[idx] + subFrac * (xfadeR[idx + 1] - xfadeR[idx]); outL[processed + i] += sampL * ampLeft; outR[processed + i] += sampR * ampRight; phaseAcc += phaseInc; } } else { for (int i = 0; i < n; i++) { int idxPhase = int(phaseAcc >> FIXED_SHIFT); float frac = float(phaseAcc & FIXED_MASK) * invFixedOne; float currentP = float(phaseAcc) * invFixedOne; float alpha = (currentP - crossfadeStartF) / X; float crossAngle = alpha * piOverTwo; float tailGain = std::cos(crossAngle); float headGain = std::sin(crossAngle); float sampTailL = sourceL[idxPhase] + frac * (sourceL[idxPhase + 1] - sourceL[idxPhase]); float sampTailR = sourceR[idxPhase] + frac * (sourceR[idxPhase + 1] - sourceR[idxPhase]); float headPos = float(fixedLoopStart) * invFixedOne + (currentP - crossfadeStartF); int idxHead = int(headPos); float fracHead = headPos - idxHead; float sampHeadL = sourceL[idxHead] + fracHead * (sourceL[idxHead + 1] - sourceL[idxHead]); float sampHeadR = sourceR[idxHead] + fracHead * (sourceR[idxHead + 1] - sourceR[idxHead]); float mixL = tailGain * sampTailL + headGain * sampHeadL; float mixR = tailGain * sampTailR + headGain * sampHeadR; outL[processed + i] += mixL * ampLeft; outR[processed + i] += mixR * ampRight; phaseAcc += phaseInc; } } processed += n; } } } return processed; } }; struct Voice { int midiNote = 60; bool isActive = false; float velocity = 1.0f; SamplePlayback playback; void reset(int note, float vel, const std::array<const float*, 2>& sample, int bufferLength, double baseDelta, const SampleSettings& settings) { midiNote = note; velocity = vel; isActive = true; new (&playback) SamplePlayback(sample, velocity, settings, bufferLength, baseDelta); } }; template <int NV> struct Griffin_Sampler : public data::base { SNEX_NODE(Griffin_Sampler); struct MetadataClass { SN_NODE_ID("Griffin_Sampler"); }; static constexpr bool isModNode() { return false; } static constexpr bool isPolyphonic() { return NV > 1; } static constexpr bool hasTail() { return true; } 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; PolyData<Voice, NV> voices; ExternalData sampleData; AudioBuffer<float> sampleBuffer; std::array<const float*, 2> sample{ nullptr, nullptr }; std::array<float, 128> pitchRatios{}; double sampleRate = 44100.0; double sampleRateRatio = 1.0; float sampleStartPercent = 0.0f; float loopStartPercent = 0.0f; float loopEndPercent = 1.0f; float playbackStartOffsetInSamples = 0.0f; float loopStartOffsetInSamples = 0.0f; float loopEndOffsetInSamples = 0.0f; double globalPitchOffsetFactor = 1.0; float xfadeFraction = 0.0f; float xfadeLengthInSamples = 0.0f; bool loopMode = false; std::mt19937 randomGen; std::atomic<AudioBuffer<float>*> precomputedXfadeBuffer{ nullptr }; void setExternalData(const ExternalData& ed, int) { sampleData = ed; AudioSampleBuffer tempBuf = ed.toAudioSampleBuffer(); int numSamples = tempBuf.getNumSamples(); int numChannels = tempBuf.getNumChannels(); if (numSamples <= 0) { int fallbackLen = 8; int chs = (numChannels > 0 ? numChannels : 2); AudioSampleBuffer fallback(chs, fallbackLen); fallback.clear(); sampleBuffer.makeCopyOf(fallback, true); } else { sampleBuffer.makeCopyOf(tempBuf, true); } sample[0] = sampleBuffer.getReadPointer(0); if (numChannels > 1) sample[1] = sampleBuffer.getReadPointer(1); else sample[1] = sample[0]; updateDerivedParameters(); } void updateDerivedParameters() { int currentSampleLength = sampleBuffer.getNumSamples(); if (currentSampleLength < 1) currentSampleLength = 1; playbackStartOffsetInSamples = sampleStartPercent * float(currentSampleLength - 1); loopStartOffsetInSamples = loopStartPercent * float(currentSampleLength - 1); loopEndOffsetInSamples = loopEndPercent * float(currentSampleLength - 1); float regionLen = loopEndOffsetInSamples - loopStartOffsetInSamples; if (regionLen < 0.f) regionLen = 0.f; float maxXfade = regionLen * 0.5f; float desiredXfade = xfadeFraction * regionLen; if (desiredXfade > maxXfade) desiredXfade = maxXfade; xfadeLengthInSamples = desiredXfade; if (loopMode && xfadeLengthInSamples > 0.f && sampleBuffer.getNumSamples() > 0) { int xfadeSamples = std::max(1, (int)std::round(xfadeLengthInSamples)); auto* newXfadeBuffer = new AudioBuffer<float>(2, xfadeSamples); for (int ch = 0; ch < 2; ++ch) { float* dest = newXfadeBuffer->getWritePointer(ch); const float* src = sampleBuffer.getReadPointer(std::min(ch, sampleBuffer.getNumChannels() - 1)); for (int i = 0; i < xfadeSamples; i++) { float pos = float(i); float alpha = (xfadeSamples > 1 ? pos / float(xfadeSamples - 1) : 0.f); float tailGain = std::cos(alpha * (float(M_PI) * 0.5f)); float headGain = std::sin(alpha * (float(M_PI) * 0.5f)); float tailPos = loopEndOffsetInSamples - xfadeLengthInSamples + pos; float headPos = loopStartOffsetInSamples + pos; int tailIdx = int(tailPos); int headIdx = int(headPos); float tailFrac = tailPos - tailIdx; float headFrac = headPos - headIdx; int numSamples = sampleBuffer.getNumSamples(); int tailIdx1 = std::min(tailIdx + 1, numSamples - 1); int headIdx1 = std::min(headIdx + 1, numSamples - 1); float tailSample = src[tailIdx] + tailFrac * (src[tailIdx1] - src[tailIdx]); float headSample = src[headIdx] + headFrac * (src[headIdx1] - src[headIdx]); dest[i] = tailGain * tailSample + headGain * headSample; } } AudioBuffer<float>* oldBuffer = precomputedXfadeBuffer.exchange(newXfadeBuffer); if (oldBuffer) delete oldBuffer; } else { AudioBuffer<float>* oldBuffer = precomputedXfadeBuffer.exchange(nullptr); if (oldBuffer) delete oldBuffer; } } void reset() { for (auto& voice : voices) voice.isActive = false; } void prepare(PrepareSpecs specs) { sampleRate = specs.sampleRate; initPitchRatios(); updateDerivedParameters(); voices.prepare(specs); std::random_device rd; randomGen.seed(rd()); } void handleHiseEvent(HiseEvent& e) { if (e.isNoteOn()) { auto& voice = voices.get(); double baseDelta = pitchRatios[e.getNoteNumber()] * sampleRateRatio * globalPitchOffsetFactor; SampleSettings settings; settings.pitchOffsetCents = 0.0; settings.volumeMult = 1.0f; settings.panning = 0.0f; settings.playbackStartInSamples = playbackStartOffsetInSamples; settings.loopStartInSamples = loopStartOffsetInSamples; settings.loopEndInSamples = loopEndOffsetInSamples; settings.loopMode = loopMode; settings.xfadeLengthInSamples = xfadeLengthInSamples; voice.reset(e.getNoteNumber(), e.getFloatVelocity(), sample, sampleBuffer.getNumSamples(), baseDelta, settings); } } template <typename ProcessDataType> void process(ProcessDataType& data) { auto& fixData = data.template as<ProcessData<getFixChannelAmount()>>(); auto audioBlock = fixData.toAudioBlock(); auto* leftChannel = audioBlock.getChannelPointer(0); auto* rightChannel = audioBlock.getChannelPointer(1); int totalSamples = data.getNumSamples(); if (sampleBuffer.getNumSamples() == 0) { audioBlock.clear(); return; } // Pre-clear output buffers std::fill(leftChannel, leftChannel + totalSamples, 0.f); std::fill(rightChannel, rightChannel + totalSamples, 0.f); AudioBuffer<float>* currentXfadeBuffer = precomputedXfadeBuffer.load(); for (auto& voice : voices) { if (!voice.isActive) continue; int n = voice.playback.vectorSynthesize(leftChannel, rightChannel, totalSamples, currentXfadeBuffer, xfadeLengthInSamples); if (!voice.playback.active) voice.isActive = false; } // Lock external sample data for thread-safe UI update. { DataReadLock lock(sampleData); bool uiUpdated = false; for (auto& voice : voices) { if (voice.isActive) { // Convert fixed-point phase accumulator to a float sample position. float currentPos = float(voice.playback.phaseAcc) / float(FIXED_ONE); sampleData.setDisplayedValue(currentPos); uiUpdated = true; break; } } if (!uiUpdated) sampleData.setDisplayedValue(0.0); } } template <typename FrameDataType> void processFrame(FrameDataType&) {} template <int P> void setParameter(double v) { if constexpr (P == 0) { globalPitchOffsetFactor = std::pow(2.0, v / 12.0); for (auto& voice : voices) { if (voice.isActive) { double newBaseDelta = pitchRatios[voice.midiNote] * sampleRateRatio * globalPitchOffsetFactor; double centsFact = std::pow(2.0, (voice.playback.settings.pitchOffsetCents / 1200.0)); double effectiveSpeed = newBaseDelta * centsFact; voice.playback.phaseInc = (int64_t)std::llround(effectiveSpeed * FIXED_ONE); } } } else if constexpr (P == 1) { sampleStartPercent = (float)v; updateDerivedParameters(); } else if constexpr (P == 2) { loopStartPercent = (float)v; updateDerivedParameters(); } else if constexpr (P == 3) { loopEndPercent = (float)v; updateDerivedParameters(); } else if constexpr (P == 4) { xfadeFraction = (float)v; if (xfadeFraction < 0.f) xfadeFraction = 0.f; if (xfadeFraction > 1.f) xfadeFraction = 1.f; updateDerivedParameters(); } else if constexpr (P == 5) { loopMode = (v >= 0.5); updateDerivedParameters(); } if constexpr (P >= 1 && P <= 5) { for (auto& voice : voices) { if (voice.isActive) { SampleSettings newSettings; newSettings.pitchOffsetCents = voice.playback.settings.pitchOffsetCents; newSettings.volumeMult = voice.playback.settings.volumeMult; newSettings.panning = voice.playback.settings.panning; newSettings.playbackStartInSamples = playbackStartOffsetInSamples; newSettings.loopStartInSamples = loopStartOffsetInSamples; newSettings.loopEndInSamples = loopEndOffsetInSamples; newSettings.loopMode = loopMode; newSettings.xfadeLengthInSamples = xfadeLengthInSamples; voice.playback.updateSettings(newSettings, sampleBuffer.getNumSamples()); } } } } void initPitchRatios() { for (int i = 0; i < 128; ++i) pitchRatios[i] = std::pow(2.0f, float(i - 60) / 12.0f); } void createParameters(ParameterDataList& data) { { parameter::data pitchParam("Pitch (semitones)", { -48.0, 24.0, 0.01 }); registerCallback<0>(pitchParam); pitchParam.setDefaultValue(0.0); data.add(std::move(pitchParam)); } { parameter::data startParam("Playhead Start", { 0.0, 1.0, 0.00001 }); registerCallback<1>(startParam); startParam.setDefaultValue(0.0); data.add(std::move(startParam)); } { parameter::data loopStartParam("Loop Start", { 0.0, 1.0, 0.00001 }); registerCallback<2>(loopStartParam); loopStartParam.setDefaultValue(0.0); data.add(std::move(loopStartParam)); } { parameter::data loopEndParam("Sample End", { 0.0, 1.0, 0.00001 }); registerCallback<3>(loopEndParam); loopEndParam.setDefaultValue(1.0); data.add(std::move(loopEndParam)); } { parameter::data xfadeParam("Xfade Length", { 0.0, 1.0, 0.0001 }); registerCallback<4>(xfadeParam); xfadeParam.setDefaultValue(0.0); data.add(std::move(xfadeParam)); } { parameter::data loopModeParam("Loop Mode", { 0.0, 1.0, 1.0 }); registerCallback<5>(loopModeParam); loopModeParam.setDefaultValue(0.0); data.add(std::move(loopModeParam)); } } }; } // namespace project

30

Online

2.0k

Users

12.7k

Topics

110.1k

Posts