Harmonic dupe integer freq, kaleidoscope boundary fix

pull/327/head
Anthony Hall 2025-09-07 21:11:29 -07:00
rodzic e2646d6b66
commit 5ff9fde8f9
2 zmienionych plików z 20 dodań i 11 usunięć

Wyświetl plik

@ -9,29 +9,38 @@ public:
osci::Point apply(int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override {
const double twoPi = juce::MathConstants<double>::twoPi;
double copies = juce::jmax(1.0, values[0].load());
double dist = juce::jlimit(0.0, 1.0, values[1].load());
double spread = juce::jlimit(0.0, 1.0, values[1].load());
double angleOffset = values[2].load() * juce::MathConstants<double>::twoPi;
// Ensure values extremely close to integer don't get rounded up
double fractionalPart = copies - std::floor(copies);
double ceilCopies = fractionalPart > 1e-4 ? std::ceil(copies) : std::floor(copies);
double theta = std::floor(framePhase * copies) / copies * twoPi + angleOffset;
osci::Point offset(std::cos(theta), std::sin(theta), 0.0);
framePhase += audioProcessor.frequency / copies / sampleRate;
framePhase += audioProcessor.frequency / ceilCopies / sampleRate;
framePhase = framePhase - std::floor(framePhase);
return (1 - dist) * input + dist * offset;
return (1 - spread) * input + spread * offset;
}
std::shared_ptr<osci::Effect> build() const override {
auto eff = std::make_shared<osci::Effect>(
std::make_shared<HarmonicDuplicatorEffect>(audioProcessor),
std::vector<osci::EffectParameter*>{
// TODO ID strings
new osci::EffectParameter("Copies", "Controls the number of copies of the input shape to draw.", "rdCopies", VERSION_HINT, 3.0, 1.0, 10.0),
new osci::EffectParameter("Distance", "Controls the distance between copies of the input shape.", "rdMix", VERSION_HINT, 0.5, 0.0, 1.0),
new osci::EffectParameter("Angle Offset", "Rotates the offsets between copies without rotating the input shape.", "rdAngle", VERSION_HINT, 0.0, 0.0, 1.0, 0.0001, osci::LfoType::Sawtooth, 1.0)
new osci::EffectParameter("Copies",
"Controls the number of copies of the input shape to draw. Splitting the shape into multiple copies creates audible harmony.",
"harmonicDuplicatorCopies", VERSION_HINT, 3.0, 1.0, 6.0),
new osci::EffectParameter("Spread",
"Controls the spread between copies of the input shape.",
"harmonicDuplicatorSpread", VERSION_HINT, 0.4, 0.0, 1.0),
new osci::EffectParameter("Angle Offset",
"Rotates the offsets between copies without rotating the input shape.",
"harmonicDuplicatorAngle", VERSION_HINT, 0.0, 0.0, 1.0, 0.0001, osci::LfoType::Sawtooth, 0.2)
}
);
eff->setName("Radial Duplicator");
eff->setName("Harmonic Duplicator");
eff->setIcon(BinaryData::kaleidoscope_svg);
return eff;
}

Wyświetl plik

@ -22,18 +22,18 @@ public:
int fullSegments = (int)std::floor(segments);
double fractionalPart = segments - fullSegments; // in [0,1)
fullSegments = fractionalPart > 1e-4 ? fullSegments : fullSegments - 1;
phase = nextPhase(audioProcessor.frequency / (fullSegments + 1), sampleRate) / (2.0 * std::numbers::pi);
// Use 'segments' for timing so partial segment gets proportionally shorter time.
double currentSegmentFloat = phase * segments; // [0, segments)
int currentSegmentIndex = (int)std::floor(currentSegmentFloat);
int maxIndex = fractionalPart > 1e-9 ? fullSegments : fullSegments - 1; // include partial index if exists
if (currentSegmentIndex > maxIndex) currentSegmentIndex = maxIndex; // safety
if (currentSegmentIndex > fullSegments) currentSegmentIndex = fullSegments; // safety
// Base full wedge angle (all full wedges) and size of partial wedge
double baseWedgeAngle = juce::MathConstants<double>::twoPi / segments; // size of a "unit" wedge
double partialScale = (currentSegmentIndex == fullSegments && fractionalPart > 1e-9) ? fractionalPart : 1.0;
double partialScale = (currentSegmentIndex == fullSegments && fractionalPart > 1e-4) ? fractionalPart : 1.0;
double wedgeAngle = baseWedgeAngle * partialScale;
// Normalize theta to [0,1) for compression