| 29 | } |
| 30 | |
| 31 | void LfoEngine::updateShape(const LfoData& shapeData) |
| 32 | { |
| 33 | const auto& points = shapeData.points; |
| 34 | const auto& curvatures = shapeData.curvatures; |
| 35 | |
| 36 | if (points.size() < 2 || curvatures.size() < (points.size() - 1)) |
| 37 | { |
| 38 | jassertfalse; |
| 39 | return; |
| 40 | } |
| 41 | |
| 42 | const auto numPointsInTable = wavetable.getNumPoints(); |
| 43 | |
| 44 | // Step 1: Create a temporary, mutable array to build the waveform. |
| 45 | juce::Array<float> tempTable; |
| 46 | tempTable.resize(numPointsInTable); |
| 47 | |
| 48 | // Step 2: Generate the raw, unsmoothed shape into the temporary array. |
| 49 | for (int i = 0; i < numPointsInTable; ++i) |
| 50 | { |
| 51 | const float phase = (float) i / (float) (numPointsInTable > 1 ? numPointsInTable - 1 : 1); |
| 52 | |
| 53 | float sampleValue = 0.5f; // Default to middle value |
| 54 | |
| 55 | if (points.size() >= 2) |
| 56 | { |
| 57 | // Find the correct segment for the current phase |
| 58 | for (size_t p = 0; p < points.size() - 1; ++p) |
| 59 | { |
| 60 | const auto& p1 = points[p]; |
| 61 | const auto& p2 = points[p + 1]; |
| 62 | |
| 63 | if (phase >= p1.x && phase <= p2.x) |
| 64 | { |
| 65 | const float segmentWidth = p2.x - p1.x; |
| 66 | |
| 67 | // Fallback to linear interpolation if curvatures data is missing or segment is zero-width |
| 68 | if (p >= curvatures.size() || std::abs(segmentWidth) < 1e-9f) |
| 69 | { |
| 70 | sampleValue = (std::abs(segmentWidth) < 1e-9f) |
| 71 | ? p1.y |
| 72 | : p1.y + (p2.y - p1.y) * ((phase - p1.x) / segmentWidth); |
| 73 | } |
| 74 | else // Apply curvature |
| 75 | { |
| 76 | const float curvature = curvatures[p]; |
| 77 | const float tx = (phase - p1.x) / segmentWidth; |
| 78 | const float absExp = std::pow(4.0f, std::abs(curvature)); |
| 79 | float ty; |
| 80 | |
| 81 | if (curvature >= 0.0f) |
| 82 | ty = std::pow(tx, absExp); |
| 83 | else |
| 84 | ty = 1.0f - std::pow(juce::jmax(0.0f, 1.0f - tx), absExp); |
| 85 | |
| 86 | sampleValue = p1.y + (p2.y - p1.y) * ty; |
| 87 | } |
| 88 | break; // Exit segment search once found |
no test coverage detected