kopia lustrzana https://github.com/jameshball/osci-render
Add experimental kaleidoscope and bounce effects
rodzic
600d69e568
commit
2facd66ef8
|
@ -12,6 +12,7 @@
|
|||
#include "audio/BitCrushEffect.h"
|
||||
#include "audio/BulgeEffect.h"
|
||||
#include "audio/DistortEffect.h"
|
||||
#include "audio/KaleidoscopeEffect.h"
|
||||
#include "audio/MultiplexEffect.h"
|
||||
#include "audio/SmoothEffect.h"
|
||||
#include "audio/VectorCancellingEffect.h"
|
||||
|
@ -20,6 +21,7 @@
|
|||
#include "audio/TranslateEffect.h"
|
||||
#include "audio/RippleEffect.h"
|
||||
#include "audio/SwirlEffect.h"
|
||||
#include "audio/PhysicsBounceEffect.h"
|
||||
#include "parser/FileParser.h"
|
||||
#include "parser/FrameProducer.h"
|
||||
|
||||
|
@ -35,6 +37,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
|
|||
|
||||
toggleableEffects.push_back(BulgeEffect().build());
|
||||
toggleableEffects.push_back(MultiplexEffect().build());
|
||||
toggleableEffects.push_back(KaleidoscopeEffect().build());
|
||||
toggleableEffects.push_back(PhysicsBounceEffect().build());
|
||||
toggleableEffects.push_back(VectorCancellingEffect().build());
|
||||
{
|
||||
auto scaleEffect = ScaleEffectApp().build();
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// KaleidoscopeEffect.h
|
||||
// Repeats and mirrors the input around the origin to create a kaleidoscope pattern.
|
||||
// The effect supports a floating point number of segments, allowing smooth morphing
|
||||
// between different symmetry counts.
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class KaleidoscopeEffect : public osci::EffectApplication {
|
||||
public:
|
||||
osci::Point apply(int /*index*/, osci::Point input, const std::vector<std::atomic<double>>& values, double /*sampleRate*/) override {
|
||||
// values[0] = segments (can be fractional)
|
||||
// values[1] = phase (0-1) selecting which segment is currently being drawn
|
||||
double segments = juce::jlimit(1.0, 256.0, values[0].load());
|
||||
double phase = values.size() > 1 ? values[1].load() : 0.0;
|
||||
|
||||
// Polar conversion
|
||||
double r = std::sqrt(input.x * input.x + input.y * input.y);
|
||||
if (r < 1e-12) return input;
|
||||
double theta = std::atan2(input.y, input.x);
|
||||
|
||||
int fullSegments = (int)std::floor(segments);
|
||||
double fractionalPart = segments - fullSegments; // in [0,1)
|
||||
|
||||
// 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
|
||||
|
||||
// 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 wedgeAngle = baseWedgeAngle * partialScale;
|
||||
|
||||
// Normalize theta to [0,1) for compression
|
||||
double thetaNorm = (theta + juce::MathConstants<double>::pi) / juce::MathConstants<double>::twoPi; // 0..1
|
||||
|
||||
// Offset for this segment: each preceding full segment occupies baseWedgeAngle
|
||||
double segmentOffset = 0.0;
|
||||
if (currentSegmentIndex < fullSegments) {
|
||||
segmentOffset = currentSegmentIndex * baseWedgeAngle;
|
||||
} else { // partial segment
|
||||
segmentOffset = fullSegments * baseWedgeAngle;
|
||||
}
|
||||
// Map entire original angle range into [segmentOffset, segmentOffset + wedgeAngle) so edges line up exactly.
|
||||
double finalTheta = segmentOffset + thetaNorm * wedgeAngle - juce::MathConstants<double>::pi; // constant 180° rotation
|
||||
|
||||
double newX = r * std::cos(finalTheta);
|
||||
double newY = r * std::sin(finalTheta);
|
||||
return osci::Point(newX, newY, input.z);
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
auto eff = std::make_shared<osci::Effect>(
|
||||
std::make_shared<KaleidoscopeEffect>(),
|
||||
std::vector<osci::EffectParameter*>{
|
||||
new osci::EffectParameter(
|
||||
"Kaleidoscope Segments",
|
||||
"Controls how many times the image is rotationally repeated around the centre. Fractional values smoothly morph the repetition.",
|
||||
"kaleidoscopeSegments",
|
||||
VERSION_HINT,
|
||||
6.0, // default
|
||||
1.0, // min
|
||||
32.0 // max
|
||||
),
|
||||
new osci::EffectParameter(
|
||||
"Kaleidoscope Phase",
|
||||
"Selects which kaleidoscope segment is currently being drawn (time-multiplexed). Animate to sweep around the circle.",
|
||||
"kaleidoscopePhase",
|
||||
VERSION_HINT,
|
||||
0.0, // default
|
||||
0.0, // min
|
||||
1.0, // max
|
||||
0.0001f, // step
|
||||
osci::LfoType::Sawtooth,
|
||||
0.5f // LFO frequency (Hz) – slow, visible rotation
|
||||
),
|
||||
}
|
||||
);
|
||||
eff->setName("Kaleidoscope");
|
||||
// Reuse an existing icon for now (repeat). A dedicated kaleidoscope icon can be added later.
|
||||
eff->setIcon(BinaryData::repeat_svg);
|
||||
return eff;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
// PhysicsBounceEffect.h (Simplified DVD-style 2D bounce)
|
||||
// Scales the original shape, then translates it within [-1,1] x [-1,1] using
|
||||
// constant-velocity motion that bounces off the edges. Z coordinate is unchanged.
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class PhysicsBounceEffect : public osci::EffectApplication {
|
||||
public:
|
||||
osci::Point apply(int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override {
|
||||
// values[0] = size (0.05..1.0)
|
||||
// values[1] = speed (0..2)
|
||||
// values[2] = angle (0..1 -> 0..2π)
|
||||
double size = juce::jlimit(0.05, 1.0, values[0].load());
|
||||
double speed = juce::jlimit(0.0, 2.0, values[1].load());
|
||||
double angle = values[2].load() * juce::MathConstants<double>::twoPi;
|
||||
|
||||
// Base direction from user
|
||||
double dirX = std::cos(angle);
|
||||
double dirY = std::sin(angle);
|
||||
if (flipX) dirX = -dirX;
|
||||
if (flipY) dirY = -dirY;
|
||||
|
||||
double dt = 1.0 / sampleRate;
|
||||
position.x += dirX * speed * dt;
|
||||
position.y += dirY * speed * dt;
|
||||
|
||||
double maxP = 1.0 - size;
|
||||
double minP = -1.0 + size;
|
||||
if (position.x > maxP) { position.x = maxP; flipX = !flipX; }
|
||||
else if (position.x < minP) { position.x = minP; flipX = !flipX; }
|
||||
if (position.y > maxP) { position.y = maxP; flipY = !flipY; }
|
||||
else if (position.y < minP) { position.y = minP; flipY = !flipY; }
|
||||
|
||||
osci::Point scaled = input * size;
|
||||
osci::Point out = scaled + osci::Point(position.x, position.y, 0.0);
|
||||
out.z = input.z; // preserve original Z
|
||||
return out;
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
auto eff = std::make_shared<osci::Effect>(
|
||||
std::make_shared<PhysicsBounceEffect>(),
|
||||
std::vector<osci::EffectParameter*>{
|
||||
new osci::EffectParameter("Bounce Size", "Size (scale) of the bouncing object.", "bounceSize", VERSION_HINT, 0.3, 0.05, 1.0),
|
||||
new osci::EffectParameter("Bounce Speed", "Speed of motion.", "bounceSpeed", VERSION_HINT, 0.6, 0.0, 2.0),
|
||||
new osci::EffectParameter("Bounce Angle", "Direction of travel (0..1 -> 0..360°).", "bounceAngle", VERSION_HINT, 0.125, 0.0, 1.0, 0.0001f, osci::LfoType::Sawtooth, 0.05f),
|
||||
}
|
||||
);
|
||||
eff->setName("Bounce");
|
||||
eff->setIcon(BinaryData::random_svg);
|
||||
return eff;
|
||||
}
|
||||
|
||||
private:
|
||||
osci::Point position { 0.0, 0.0, 0.0 };
|
||||
bool flipX = false;
|
||||
bool flipY = false;
|
||||
};
|
|
@ -322,7 +322,7 @@ public:
|
|||
"Ambient Light",
|
||||
"Controls how much ambient light is added to the oscilloscope display.",
|
||||
"ambient",
|
||||
VERSION_HINT, 0.7, 0.0, 5.0
|
||||
VERSION_HINT, 0.0, 0.0, 5.0
|
||||
)
|
||||
);
|
||||
std::shared_ptr<osci::Effect> smoothEffect = SmoothEffect("visualiser", 0.0f).build();
|
||||
|
|
|
@ -110,6 +110,10 @@
|
|||
<FILE id="ux2dO2" name="DistortEffect.h" compile="0" resource="0" file="Source/audio/DistortEffect.h"/>
|
||||
<FILE id="SWC0tN" name="MultiplexEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/MultiplexEffect.h"/>
|
||||
<FILE id="kAlEiD" name="KaleidoscopeEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/KaleidoscopeEffect.h"/>
|
||||
<FILE id="bNcEfx" name="PhysicsBounceEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/PhysicsBounceEffect.h"/>
|
||||
<FILE id="h0dMim" name="PerspectiveEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/PerspectiveEffect.h"/>
|
||||
<FILE id="t5g8pf" name="PublicSynthesiser.h" compile="0" resource="0"
|
||||
|
|
Ładowanie…
Reference in New Issue