Merge branch 'develop' into polygon-bitcrush

pull/322/head
Anthony Hall 2025-09-09 21:25:18 -07:00
commit ac331eb604
9 zmienionych plików z 108 dodań i 42 usunięć

Wyświetl plik

@ -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}

Wyświetl plik

@ -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}

Wyświetl plik

@ -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)
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -18,6 +18,7 @@
#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"
@ -50,6 +51,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());

Wyświetl plik

@ -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]
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -95,6 +95,7 @@
<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"/>
@ -167,6 +168,8 @@
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
<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="GJoA2i" name="PolygonBitCrushEffect.h" compile="0" resource="0"
file="Source/audio/PolygonBitCrushEffect.h"/>
<FILE id="HE3dFE" name="AudioRecorder.h" compile="0" resource="0" file="Source/audio/AudioRecorder.h"/>