kopia lustrzana https://github.com/jameshball/osci-render
Merge branch 'develop' into cpow-effect
commit
48a9489e16
|
@ -1,5 +1,6 @@
|
|||
local SCALE = 1.0
|
||||
local ANIMATION_SPEED = 1.0
|
||||
local FOV_4D = math.rad(80)
|
||||
|
||||
local function rotate4D_XY(x, y, z, w, angle)
|
||||
return x * math.cos(angle) - y * math.sin(angle),
|
||||
|
@ -23,8 +24,9 @@ local function rotate4D_XW(x, y, z, w, angle)
|
|||
end
|
||||
|
||||
local function project4Dto3D(x, y, z, w)
|
||||
local w_factor = 1 / (3 - w)
|
||||
return x * w_factor, y * w_factor, z * w_factor
|
||||
local camera_w = -1 / math.sin(0.5 * FOV_4D)
|
||||
local scale = 1 / (w - camera_w) / math.tan(0.5 * FOV_4D)
|
||||
return x * scale, y * scale, z * scale
|
||||
end
|
||||
|
||||
local vertices = {
|
||||
|
@ -36,36 +38,37 @@ local vertices = {
|
|||
{-1, -1, 1, 1}, {1, -1, 1, 1}, {1, 1, 1, 1}, {-1, 1, 1, 1}
|
||||
}
|
||||
|
||||
local edges = {
|
||||
-- Inner cube edges
|
||||
{1, 2}, {2, 3}, {3, 4}, {4, 1},
|
||||
{5, 6}, {6, 7}, {7, 8}, {8, 5},
|
||||
{1, 5}, {2, 6}, {3, 7}, {4, 8},
|
||||
-- Outer cube edges
|
||||
{9, 10}, {10, 11}, {11, 12}, {12, 9},
|
||||
{13, 14}, {14, 15}, {15, 16}, {16, 13},
|
||||
{9, 13}, {10, 14}, {11, 15}, {12, 16},
|
||||
-- Connections between cubes
|
||||
{1, 9}, {2, 10}, {3, 11}, {4, 12},
|
||||
{5, 13}, {6, 14}, {7, 15}, {8, 16}
|
||||
-- Eulerian cycle through vertices
|
||||
local path = {
|
||||
1, 5, 8, 4, 12, 16, 15, 11,
|
||||
3, 7, 6, 14, 15, 7, 8, 16,
|
||||
13, 5, 6, 2, 10, 9, 13, 14,
|
||||
10, 11, 12, 9, 1, 2, 3, 4
|
||||
}
|
||||
|
||||
local NUM_EDGES = #edges
|
||||
local PATH_LENGTH = #path
|
||||
|
||||
local railroad_switch = NUM_EDGES * phase / (2 * math.pi)
|
||||
local current_edge = math.floor(railroad_switch) + 1
|
||||
local railroad_switch = PATH_LENGTH * phase / (2 * math.pi)
|
||||
local current_vertex = math.floor(railroad_switch) + 1
|
||||
local next_vertex = current_vertex % PATH_LENGTH + 1
|
||||
local edge_phase = railroad_switch % 1
|
||||
|
||||
local time = step/sample_rate * ANIMATION_SPEED
|
||||
|
||||
if current_edge <= NUM_EDGES then
|
||||
local v1 = vertices[edges[current_edge][1]]
|
||||
local v2 = vertices[edges[current_edge][2]]
|
||||
if current_vertex <= PATH_LENGTH then
|
||||
local v1 = vertices[path[current_vertex]]
|
||||
local v2 = vertices[path[next_vertex]]
|
||||
|
||||
local x = v1[1] + (v2[1] - v1[1]) * edge_phase
|
||||
local y = v1[2] + (v2[2] - v1[2]) * edge_phase
|
||||
local z = v1[3] + (v2[3] - v1[3]) * edge_phase
|
||||
local w = v1[4] + (v2[4] - v1[4]) * edge_phase
|
||||
|
||||
-- Normalize
|
||||
x = x * 0.5
|
||||
y = y * 0.5
|
||||
z = z * 0.5
|
||||
w = w * 0.5
|
||||
|
||||
local fold_angle = math.sin(time) * math.pi / 2
|
||||
|
||||
|
@ -82,4 +85,4 @@ if current_edge <= NUM_EDGES then
|
|||
return {x, y, z}
|
||||
end
|
||||
|
||||
return {0, 0}
|
||||
return {0, 0}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
local X_SIZE = 0.35
|
||||
local LOOPS = 30
|
||||
local CAP_WIDTH = 0.35
|
||||
local STEM_WIDTH = 0.05
|
||||
local Y_SIZE = 0.02
|
||||
local HEIGHT = 2.0
|
||||
local OFFSET_Y = -1.0
|
||||
local LOOPS = 50
|
||||
local SHROOMS = 3 -- can be 1-4
|
||||
|
||||
local MOVE_AMOUNT = 0.4
|
||||
|
@ -13,27 +12,34 @@ local SPREAD = 0.2
|
|||
local railroad_switch = SHROOMS * phase / (2 * math.pi)
|
||||
local drawing_phase = (phase * SHROOMS) % (2 * math.pi)
|
||||
|
||||
local x = X_SIZE * math.cos(LOOPS * drawing_phase)
|
||||
local y = Y_SIZE * math.sin(LOOPS * drawing_phase)
|
||||
local x = CAP_WIDTH * math.cos(LOOPS * drawing_phase)
|
||||
local z = CAP_WIDTH * math.sin(LOOPS * drawing_phase)
|
||||
|
||||
local sawtooth = (drawing_phase % (2 * math.pi)) / (2 * math.pi)
|
||||
y = y + (HEIGHT * sawtooth) + OFFSET_Y
|
||||
local y = HEIGHT * sawtooth + OFFSET_Y
|
||||
|
||||
local sine_mod = math.sin(2 * math.pi * sawtooth)
|
||||
|
||||
if sawtooth < 0.75 then
|
||||
sine_mod = 1
|
||||
x = x * (STEM_WIDTH / X_SIZE)
|
||||
sine_mod = STEM_WIDTH / CAP_WIDTH
|
||||
end
|
||||
|
||||
x = x * sine_mod
|
||||
z = z * sine_mod
|
||||
|
||||
local base_time = step/sample_rate * MOVE_FREQ
|
||||
local current_mushroom = math.floor(railroad_switch)
|
||||
local phase_offset = current_mushroom / SHROOMS
|
||||
|
||||
local wiggle = math.sin(2 * math.pi * (sawtooth + base_time + phase_offset)) * sawtooth
|
||||
local outer_drift = SPREAD * sawtooth * wiggle
|
||||
x = x + (MOVE_AMOUNT * wiggle + outer_drift)
|
||||
local wiggle_x = math.cos(2 * math.pi * (sawtooth + base_time + phase_offset)) * sawtooth
|
||||
local wiggle_z = math.sin(2 * math.pi * (sawtooth + base_time + phase_offset)) * sawtooth
|
||||
local outer_drift_x = SPREAD * sawtooth * wiggle_x
|
||||
local outer_drift_z = SPREAD * sawtooth * wiggle_z
|
||||
x = x + (MOVE_AMOUNT * wiggle_x + outer_drift_x)
|
||||
z = z + (MOVE_AMOUNT * wiggle_z + outer_drift_z)
|
||||
|
||||
return {x, y}
|
||||
-- normalize for default HEIGHT and OFFSET_Y
|
||||
local max_xz = math.abs(MOVE_AMOUNT) + math.abs(SPREAD)
|
||||
local scale = 1 / math.sqrt(1 + max_xz * max_xz)
|
||||
|
||||
return {x * scale, y * scale, z * scale}
|
||||
|
|
|
@ -34,7 +34,7 @@ dir = dir or 1
|
|||
t = t or 0
|
||||
-- This is the correct increment for t to use such
|
||||
-- that we hear the right frequency.
|
||||
increment = 2 * math.pi * frequency / sample_rate
|
||||
increment = 4 * math.pi * frequency / sample_rate
|
||||
|
||||
-- If we get to the end of the spiral, flip the
|
||||
-- direction to go back.
|
||||
|
@ -51,4 +51,4 @@ t = t + dir * increment
|
|||
return {
|
||||
0.1 * t * math.sin(spiral_length * t),
|
||||
0.1 * t * math.cos(spiral_length * t)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M240-160q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35Zm480 0q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35ZM480-560q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35Z"/></svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 363 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M40-440v-80h240v80H40Zm270-154-84-84 56-56 84 84-56 56Zm130-86v-240h80v240h-80Zm210 86-56-56 84-84 56 56-84 84Zm30 154v-80h240v80H680Zm-200 80q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35Zm198 134-84-84 56-56 84 84-56 56Zm-396 0-56-56 84-84 56 56-84 84ZM440-40v-240h80v240h-80Z"/></svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 428 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M120-380q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm0-160q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm120 340q-17 0-28.5-11.5T200-240q0-17 11.5-28.5T240-280q17 0 28.5 11.5T280-240q0 17-11.5 28.5T240-200Zm0-160q-17 0-28.5-11.5T200-400q0-17 11.5-28.5T240-440q17 0 28.5 11.5T280-400q0 17-11.5 28.5T240-360Zm0-160q-17 0-28.5-11.5T200-560q0-17 11.5-28.5T240-600q17 0 28.5 11.5T280-560q0 17-11.5 28.5T240-520Zm0-160q-17 0-28.5-11.5T200-720q0-17 11.5-28.5T240-760q17 0 28.5 11.5T280-720q0 17-11.5 28.5T240-680Zm160 340q-25 0-42.5-17.5T340-400q0-25 17.5-42.5T400-460q25 0 42.5 17.5T460-400q0 25-17.5 42.5T400-340Zm0-160q-25 0-42.5-17.5T340-560q0-25 17.5-42.5T400-620q25 0 42.5 17.5T460-560q0 25-17.5 42.5T400-500Zm0 300q-17 0-28.5-11.5T360-240q0-17 11.5-28.5T400-280q17 0 28.5 11.5T440-240q0 17-11.5 28.5T400-200Zm0-480q-17 0-28.5-11.5T360-720q0-17 11.5-28.5T400-760q17 0 28.5 11.5T440-720q0 17-11.5 28.5T400-680Zm0 580q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm0-720q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm160 480q-25 0-42.5-17.5T500-400q0-25 17.5-42.5T560-460q25 0 42.5 17.5T620-400q0 25-17.5 42.5T560-340Zm0-160q-25 0-42.5-17.5T500-560q0-25 17.5-42.5T560-620q25 0 42.5 17.5T620-560q0 25-17.5 42.5T560-500Zm0 300q-17 0-28.5-11.5T520-240q0-17 11.5-28.5T560-280q17 0 28.5 11.5T600-240q0 17-11.5 28.5T560-200Zm0-480q-17 0-28.5-11.5T520-720q0-17 11.5-28.5T560-760q17 0 28.5 11.5T600-720q0 17-11.5 28.5T560-680Zm0 580q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm0-720q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm160 620q-17 0-28.5-11.5T680-240q0-17 11.5-28.5T720-280q17 0 28.5 11.5T760-240q0 17-11.5 28.5T720-200Zm0-160q-17 0-28.5-11.5T680-400q0-17 11.5-28.5T720-440q17 0 28.5 11.5T760-400q0 17-11.5 28.5T720-360Zm0-160q-17 0-28.5-11.5T680-560q0-17 11.5-28.5T720-600q17 0 28.5 11.5T760-560q0 17-11.5 28.5T720-520Zm0-160q-17 0-28.5-11.5T680-720q0-17 11.5-28.5T720-760q17 0 28.5 11.5T760-720q0 17-11.5 28.5T720-680Zm120 300q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Zm0-160q-8 0-14-6t-6-14q0-8 6-14t14-6q8 0 14 6t6 14q0 8-6 14t-14 6Z"/></svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 2.2 KiB |
|
@ -12,11 +12,13 @@
|
|||
#include "audio/BitCrushEffect.h"
|
||||
#include "audio/BulgeEffect.h"
|
||||
#include "audio/TwistEffect.h"
|
||||
#include "audio/SpiralBitCrushEffect.h"
|
||||
#include "audio/DistortEffect.h"
|
||||
#include "audio/KaleidoscopeEffect.h"
|
||||
#include "audio/MultiplexEffect.h"
|
||||
#include "audio/SmoothEffect.h"
|
||||
#include "audio/WobbleEffect.h"
|
||||
#include "audio/DuplicatorEffect.h"
|
||||
#include "audio/DashedLineEffect.h"
|
||||
#include "audio/VectorCancellingEffect.h"
|
||||
#include "audio/ScaleEffect.h"
|
||||
|
@ -27,6 +29,7 @@
|
|||
#include "audio/BounceEffect.h"
|
||||
#include "audio/SkewEffect.h"
|
||||
#include "audio/RadialWrapEffect.h"
|
||||
#include "audio/GodRayEffect.h"
|
||||
#include "parser/FileParser.h"
|
||||
#include "parser/FrameProducer.h"
|
||||
|
||||
|
@ -50,6 +53,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
|
|||
toggleableEffects.push_back(DashedLineEffect(*this).build());
|
||||
toggleableEffects.push_back(TraceEffect(*this).build());
|
||||
toggleableEffects.push_back(WobbleEffect(*this).build());
|
||||
toggleableEffects.push_back(DuplicatorEffect(*this).build());
|
||||
|
||||
#if OSCI_PREMIUM
|
||||
toggleableEffects.push_back(MultiplexEffect(*this).build());
|
||||
|
@ -58,6 +62,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
|
|||
toggleableEffects.push_back(TwistEffect().build());
|
||||
toggleableEffects.push_back(SkewEffect().build());
|
||||
toggleableEffects.push_back(RadialWrapEffect().build());
|
||||
toggleableEffects.push_back(GodRayEffect().build());
|
||||
toggleableEffects.push_back(SpiralBitCrushEffect().build());
|
||||
#endif
|
||||
|
||||
auto scaleEffect = ScaleEffectApp().build();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
class DuplicatorEffect : public osci::EffectApplication {
|
||||
public:
|
||||
DuplicatorEffect(OscirenderAudioProcessor& p) : audioProcessor(p) {}
|
||||
|
||||
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 spread = juce::jlimit(0.0, 1.0, values[1].load());
|
||||
double angleOffset = values[2].load() * juce::MathConstants<double>::twoPi;
|
||||
|
||||
// Offset moves each time the input shape is drawn once
|
||||
double theta = std::floor(framePhase * copies) / copies * twoPi + angleOffset;
|
||||
osci::Point offset(std::cos(theta), std::sin(theta), 0.0);
|
||||
|
||||
double freqDivisor = std::ceil(copies - 1e-3);
|
||||
framePhase += audioProcessor.frequency / freqDivisor / sampleRate;
|
||||
framePhase = framePhase - std::floor(framePhase);
|
||||
|
||||
return (1 - spread) * input + spread * offset;
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
auto eff = std::make_shared<osci::Effect>(
|
||||
std::make_shared<DuplicatorEffect>(audioProcessor),
|
||||
std::vector<osci::EffectParameter*>{
|
||||
new osci::EffectParameter("Duplicator Copies",
|
||||
"Controls the number of copies of the input shape to draw. Splitting the shape into multiple copies creates audible harmony.",
|
||||
"duplicatorCopies", VERSION_HINT, 3.0, 1.0, 6.0),
|
||||
new osci::EffectParameter("Spread",
|
||||
"Controls the spread between copies of the input shape.",
|
||||
"duplicatorSpread", VERSION_HINT, 0.4, 0.0, 1.0),
|
||||
new osci::EffectParameter("Angle Offset",
|
||||
"Rotates the offsets between copies without rotating the input shape.",
|
||||
"duplicatorAngle", VERSION_HINT, 0.0, 0.0, 1.0, 0.0001, osci::LfoType::Sawtooth, 0.1)
|
||||
}
|
||||
);
|
||||
eff->setName("Duplicator");
|
||||
eff->setIcon(BinaryData::duplicator_svg);
|
||||
return eff;
|
||||
}
|
||||
|
||||
private:
|
||||
OscirenderAudioProcessor &audioProcessor;
|
||||
double framePhase = 0.0; // [0, 1]
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class GodRayEffect : public osci::EffectApplication {
|
||||
public:
|
||||
osci::Point apply(int index, osci::Point input, const std::vector<std::atomic<double>> &values, double sampleRate) override {
|
||||
double noiseAmp = juce::jmax(0.0, values[0].load());
|
||||
double bias = values[1];
|
||||
double biasExponent = std::pow(12.0, std::abs(bias));
|
||||
|
||||
double noise = (double)std::rand() / RAND_MAX;
|
||||
// Bias values toward 0 or 1 based on sign
|
||||
if (bias > 0.0) {
|
||||
noise = std::pow(noise, biasExponent);
|
||||
}
|
||||
else {
|
||||
noise = 1 - std::pow(1 - noise, biasExponent);
|
||||
}
|
||||
double scale = (1 - noiseAmp) + noise * noiseAmp;
|
||||
return input * scale;
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
auto eff = std::make_shared<osci::Effect>(
|
||||
std::make_shared<GodRayEffect>(),
|
||||
std::vector<osci::EffectParameter*>{
|
||||
new osci::EffectParameter("God Ray Strength",
|
||||
"Creates a god ray effect by adding noise. This slider controls the size of the rays. Looks best with higher sample rates.",
|
||||
"godRayStrength", VERSION_HINT, 0.5, 0.0, 1.0),
|
||||
new osci::EffectParameter("God Ray Position",
|
||||
"Controls whether the rays appear to be radiating inward or outward.",
|
||||
"godRayPosition", VERSION_HINT, 0.8, -1.0, 1.0)
|
||||
});
|
||||
eff->setIcon(BinaryData::god_ray_svg);
|
||||
return eff;
|
||||
}
|
||||
};
|
|
@ -22,18 +22,18 @@ public:
|
|||
|
||||
int fullSegments = (int)std::floor(segments);
|
||||
double fractionalPart = segments - fullSegments; // in [0,1)
|
||||
fullSegments = fractionalPart > 1e-3 ? 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-3) ? fractionalPart : 1.0;
|
||||
double wedgeAngle = baseWedgeAngle * partialScale;
|
||||
|
||||
// Normalize theta to [0,1) for compression
|
||||
|
|
|
@ -11,9 +11,9 @@ public:
|
|||
osci::Point apply(int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override {
|
||||
jassert(values.size() == 5);
|
||||
|
||||
double gridX = values[0].load() + 0.0001;
|
||||
double gridY = values[1].load() + 0.0001;
|
||||
double gridZ = values[2].load() + 0.0001;
|
||||
double gridX = values[0].load();
|
||||
double gridY = values[1].load();
|
||||
double gridZ = values[2].load();
|
||||
double interpolation = values[3].load();
|
||||
double gridDelay = values[4].load();
|
||||
|
||||
|
@ -24,7 +24,9 @@ public:
|
|||
buffer[head] = input;
|
||||
|
||||
osci::Point grid = osci::Point(gridX, gridY, gridZ);
|
||||
osci::Point gridFloor = osci::Point(std::floor(gridX), std::floor(gridY), std::floor(gridZ));
|
||||
osci::Point gridFloor = osci::Point(std::floor(gridX + 1e-3),
|
||||
std::floor(gridY + 1e-3),
|
||||
std::floor(gridZ + 1e-3));
|
||||
|
||||
gridFloor.x = std::max(gridFloor.x, 1.0);
|
||||
gridFloor.y = std::max(gridFloor.y, 1.0);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class SpiralBitCrushEffect : public osci::EffectApplication {
|
||||
public:
|
||||
osci::Point apply(int index, osci::Point input, const std::vector<std::atomic<double>> &values, double sampleRate) override {
|
||||
// Completing one revolution in input space traverses the hypotenuse of one "domain" in log-polar space
|
||||
double effectScale = juce::jlimit(0.0, 1.0, values[0].load());
|
||||
double domainX = juce::jmax(2.0, std::floor(values[1].load() + 0.001));
|
||||
double domainY = std::round(domainX * values[2].load());
|
||||
double zoom = values[3].load() * juce::MathConstants<double>::twoPi; // Use same scale as angle
|
||||
double rotation = values[4].load() * juce::MathConstants<double>::twoPi;
|
||||
|
||||
double domainHypot = std::hypot(domainX, domainY);
|
||||
double domainTheta = std::atan2(domainY, domainX);
|
||||
double scale = domainHypot / juce::MathConstants<double>::twoPi;
|
||||
osci::Point output(0);
|
||||
|
||||
// Round to log-polar grid
|
||||
if (input.x != 0 || input.y != 0) {
|
||||
// Convert input point to log-polar coordinates transformed based on domain and offset
|
||||
// Note 90 degree rotation: Theta is treated relative to -Y rather than +X
|
||||
double r = std::hypot(input.x, input.y);
|
||||
double logR = std::log(r);
|
||||
double theta = std::atan2(input.x, -input.y);
|
||||
osci::Point logPolarCoords(theta - rotation, logR - zoom);
|
||||
logPolarCoords.rotate(0, 0, domainTheta);
|
||||
logPolarCoords = logPolarCoords * scale;
|
||||
|
||||
// Round this point to the center of the log-polar cell the input lies in, convert back to Cartesian
|
||||
logPolarCoords.x = std::round(logPolarCoords.x);
|
||||
logPolarCoords.y = std::round(logPolarCoords.y);
|
||||
logPolarCoords = logPolarCoords / scale;
|
||||
logPolarCoords.rotate(0, 0, -domainTheta);
|
||||
double outR = std::exp(logPolarCoords.y + zoom);
|
||||
double outTheta = logPolarCoords.x + rotation;
|
||||
output.x = outR * std::sin(outTheta);
|
||||
output.y = outR * -std::cos(outTheta);
|
||||
}
|
||||
|
||||
// Round z in log space using same spacing as xy log-polar grid
|
||||
// Use same offset as xy's radial offset to be consistent with the appearance of zooming
|
||||
if (input.z != 0) {
|
||||
double signZ = input.z > 0 ? 1.0 : -1.0;
|
||||
double logZ = std::log(std::abs(input.z));
|
||||
logZ = (logZ - zoom) * scale;
|
||||
logZ = std::round(logZ);
|
||||
logZ = logZ / scale + zoom;
|
||||
output.z = signZ * std::exp(logZ);
|
||||
}
|
||||
return (1 - effectScale) * input + effectScale * output;
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
auto eff = std::make_shared<osci::Effect>(
|
||||
std::make_shared<SpiralBitCrushEffect>(),
|
||||
std::vector<osci::EffectParameter*>{
|
||||
new osci::EffectParameter("Spiral Bit Crush",
|
||||
"Constrains points to a spiral pattern.",
|
||||
"spiralBitCrush", VERSION_HINT, 0.4, 0.0, 1.0),
|
||||
new osci::EffectParameter("Spiral Density",
|
||||
"Controls the density of the spiral pattern.",
|
||||
"spiralBitCrushDensity", VERSION_HINT, 13.0, 3.0, 30.0, 1.0),
|
||||
new osci::EffectParameter("Spiral Twist",
|
||||
"Controls how much the spiral pattern twists.",
|
||||
"spiralBitCrushTwist", VERSION_HINT, 0.6, -1.0, 1.0),
|
||||
new osci::EffectParameter("Zoom",
|
||||
"Zooms the spiral pattern.",
|
||||
"spiralBitCrushZoom", VERSION_HINT, 0.0, 0.0, 1.0, 0.0001, osci::LfoType::Sawtooth, 0.1),
|
||||
new osci::EffectParameter("Rotation",
|
||||
"Rotates the spiral pattern.",
|
||||
"spiralBitCrushRotation", VERSION_HINT, 0.0, 0.0, 1.0, 0.0001, osci::LfoType::ReverseSawtooth, 0.02)
|
||||
}
|
||||
);
|
||||
eff->setIcon(BinaryData::spiral_bitcrush_svg);
|
||||
return eff;
|
||||
}
|
||||
};
|
|
@ -95,9 +95,11 @@
|
|||
<FILE id="Z2xdIc" name="dodecahedron.svg" compile="0" resource="1"
|
||||
file="Resources/svg/dodecahedron.svg"/>
|
||||
<FILE id="QkDLXs" name="donut.svg" compile="0" resource="1" file="Resources/svg/donut.svg"/>
|
||||
<FILE id="I5dGjs" name="duplicator.svg" compile="0" resource="1" file="Resources/svg/duplicator.svg"/>
|
||||
<FILE id="YwkQpy" name="fixed_rotate.svg" compile="0" resource="1"
|
||||
file="Resources/svg/fixed_rotate.svg"/>
|
||||
<FILE id="WIkl6l" name="fullscreen.svg" compile="0" resource="1" file="Resources/svg/fullscreen.svg"/>
|
||||
<FILE id="ledD1o" name="god_ray.svg" compile="0" resource="1" file="Resources/svg/god_ray.svg"/>
|
||||
<FILE id="VegGHd" name="graph.svg" compile="0" resource="1" file="Resources/svg/graph.svg"/>
|
||||
<FILE id="dYYqKr" name="gravity.svg" compile="0" resource="1" file="Resources/svg/gravity.svg"/>
|
||||
<FILE id="dYl3l5" name="greek.svg" compile="0" resource="1" file="Resources/svg/greek.svg"/>
|
||||
|
@ -141,6 +143,8 @@
|
|||
<FILE id="OiOWD6" name="skull.svg" compile="0" resource="1" file="Resources/svg/skull.svg"/>
|
||||
<FILE id="ZPUNcg" name="smoothing.svg" compile="0" resource="1" file="Resources/svg/smoothing.svg"/>
|
||||
<FILE id="XxYNOy" name="snowflake.svg" compile="0" resource="1" file="Resources/svg/snowflake.svg"/>
|
||||
<FILE id="iZMino" name="spiral_bitcrush.svg" compile="0" resource="1"
|
||||
file="Resources/svg/spiral_bitcrush.svg"/>
|
||||
<FILE id="yiDo4s" name="spout.svg" compile="0" resource="1" file="Resources/svg/spout.svg"/>
|
||||
<FILE id="EOWhGi" name="stop.svg" compile="0" resource="1" file="Resources/svg/stop.svg"/>
|
||||
<FILE id="Z3yYJU" name="swirl.svg" compile="0" resource="1" file="Resources/svg/swirl.svg"/>
|
||||
|
@ -168,7 +172,12 @@
|
|||
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
|
||||
<FILE id="jfRtxu" name="RadialWrapEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/RadialWrapEffect.h"/>
|
||||
<FILE id="de5H36" name="GodRayEffect.h" compile="0" resource="0" file="Source/audio/GodRayEffect.h"/>
|
||||
<FILE id="tU2pQl" name="SkewEffect.h" compile="0" resource="0" file="Source/audio/SkewEffect.h"/>
|
||||
<FILE id="yxWOsR" name="DuplicatorEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/DuplicatorEffect.h"/>
|
||||
<FILE id="GbAmhn" name="SpiralBitCrushEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/SpiralBitCrushEffect.h"/>
|
||||
<FILE id="HE3dFE" name="AudioRecorder.h" compile="0" resource="0" file="Source/audio/AudioRecorder.h"/>
|
||||
<FILE id="Bc8UeW" name="BitCrushEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/BitCrushEffect.h"/>
|
||||
|
@ -845,7 +854,7 @@
|
|||
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_devices" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_formats" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client" path="../../../JfUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_processors" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_utils" path="../../../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_core" path="../../../JUCE/modules"/>
|
||||
|
|
Ładowanie…
Reference in New Issue