kopia lustrzana https://github.com/jameshball/osci-render
Harmonic dupe integer freq, kaleidoscope boundary fix
rodzic
e2646d6b66
commit
5ff9fde8f9
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue