#pragma once #include // ============================================================ // Hann window lookup table (1024 points, computed once) // ============================================================ struct GrainWindow { static constexpr int tableSize = 1024; float table[tableSize]; GrainWindow() { for (int i = 0; i < tableSize; ++i) { float phase = (float) i / (float) (tableSize - 1); table[i] = 0.5f * (1.0f - std::cos (juce::MathConstants::twoPi * phase)); } } float getValue (float phase) const { float index = juce::jlimit (0.0f, 1.0f, phase) * (float) (tableSize - 1); int i0 = (int) index; int i1 = std::min (i0 + 1, tableSize - 1); float frac = index - (float) i0; return table[i0] + frac * (table[i1] - table[i0]); } }; // ============================================================ // Single grain // ============================================================ struct Grain { int startSample = 0; // where in source buffer int lengthSamples = 0; // grain duration in samples float pitchRatio = 1.0f; // playback speed double readPosition = 0.0; // current fractional read position int samplesElapsed = 0; // output samples generated float panPosition = 0.0f; // -1 left, +1 right float volume = 1.0f; bool reverse = false; bool active = false; };