2026-09-16 17:47:45 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <juce_audio_processors/juce_audio_processors.h>
|
|
|
|
|
#include <juce_dsp/juce_dsp.h>
|
|
|
|
|
#include <atomic>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
struct StereoSample
|
|
|
|
|
{
|
|
|
|
|
float mid = 0.0f;
|
|
|
|
|
float side = 0.0f;
|
|
|
|
|
float age = 0.0f;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct StereoMetrics
|
|
|
|
|
{
|
|
|
|
|
float correlation = 0.0f; // -1 to +1
|
|
|
|
|
float stereoWidth = 0.0f; // dB
|
|
|
|
|
float midLevel = 0.0f; // dB
|
|
|
|
|
float sideLevel = 0.0f; // dB
|
|
|
|
|
float balance = 0.0f; // dB (L-R)
|
|
|
|
|
float phaseAngle = 0.0f; // degrees
|
|
|
|
|
float crestFactor = 0.0f; // dB
|
|
|
|
|
float peakL = 0.0f; // dB
|
|
|
|
|
float peakR = 0.0f; // dB
|
|
|
|
|
float rmsL = 0.0f; // dB
|
|
|
|
|
float rmsR = 0.0f; // dB
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TerionProcessor final : public juce::AudioProcessor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static constexpr int kMaxPoints = 8192;
|
|
|
|
|
|
|
|
|
|
TerionProcessor();
|
|
|
|
|
~TerionProcessor() override;
|
|
|
|
|
|
|
|
|
|
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
|
|
|
|
void releaseResources() override;
|
|
|
|
|
void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
|
|
|
|
|
|
|
|
|
juce::AudioProcessorEditor* createEditor() override;
|
|
|
|
|
bool hasEditor() const override { return true; }
|
|
|
|
|
|
|
|
|
|
const juce::String getName() const override { return "Terion"; }
|
|
|
|
|
bool acceptsMidi() const override { return false; }
|
|
|
|
|
bool producesMidi() const override { return false; }
|
|
|
|
|
bool isMidiEffect() const override { return false; }
|
|
|
|
|
double getTailLengthSeconds() const override { return 0.0; }
|
|
|
|
|
|
|
|
|
|
int getNumPrograms() override { return 1; }
|
|
|
|
|
int getCurrentProgram() override { return 0; }
|
|
|
|
|
void setCurrentProgram(int) override {}
|
|
|
|
|
const juce::String getProgramName(int) override { return {}; }
|
|
|
|
|
void changeProgramName(int, const juce::String&) override {}
|
|
|
|
|
|
|
|
|
|
void getStateInformation(juce::MemoryBlock& destData) override;
|
|
|
|
|
void setStateInformation(const void* data, int sizeInBytes) override;
|
|
|
|
|
|
|
|
|
|
const std::vector<StereoSample>& getPoints() const { return points; }
|
|
|
|
|
const StereoMetrics& getMetrics() const { return metrics; }
|
|
|
|
|
|
|
|
|
|
juce::AudioProcessorValueTreeState apvts;
|
|
|
|
|
|
|
|
|
|
enum class ParameterID
|
|
|
|
|
{
|
|
|
|
|
fadeTime = 1,
|
|
|
|
|
sensitivity = 2,
|
|
|
|
|
dotSize = 3,
|
|
|
|
|
smoothing = 4,
|
2026-09-16 18:38:35 +02:00
|
|
|
freeze = 5,
|
|
|
|
|
mono = 6
|
2026-09-16 17:47:45 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::vector<StereoSample> points;
|
|
|
|
|
StereoMetrics metrics;
|
|
|
|
|
int writePos = 0;
|
|
|
|
|
bool filled = false;
|
|
|
|
|
|
|
|
|
|
// Smoothing filters
|
|
|
|
|
float midSmoothed = 0.0f;
|
|
|
|
|
float sideSmoothed = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Metrics calculation state
|
|
|
|
|
float sumMid = 0.0f;
|
|
|
|
|
float sumSide = 0.0f;
|
|
|
|
|
float sumMidSide = 0.0f;
|
|
|
|
|
float sumMid2 = 0.0f;
|
|
|
|
|
float sumSide2 = 0.0f;
|
|
|
|
|
float peakMid = 0.0f;
|
|
|
|
|
float peakSide = 0.0f;
|
|
|
|
|
int metricsCount = 0;
|
|
|
|
|
|
|
|
|
|
void addPoint(float mid, float side, float age);
|
|
|
|
|
void updateMetrics(float mid, float side, float left, float right);
|
|
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TerionProcessor)
|
|
|
|
|
};
|