kopia lustrzana https://github.com/jameshball/osci-render
Fix several threading bugs, and crash when changing sample rate
rodzic
2556f6c68f
commit
6194b039f3
|
@ -143,12 +143,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
resized();
|
||||
repaint();
|
||||
});
|
||||
addAndMakeVisible(openOscilloscope);
|
||||
|
||||
openOscilloscope.onClick = [this] {
|
||||
// TODO: Log if this fails
|
||||
juce::URL("https://james.ball.sh/oscilloscope").launchInDefaultBrowser();
|
||||
};
|
||||
|
||||
addAndMakeVisible(frequencyLabel);
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ private:
|
|||
juce::ComboBox fileType;
|
||||
juce::TextButton createFile{"Create File"};
|
||||
|
||||
juce::TextButton openOscilloscope{"Open Oscilloscope"};
|
||||
|
||||
juce::Label frequencyLabel;
|
||||
int callbackIndex = -1;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
new EffectParameter("Vector Cancelling", "Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image.", "vectorCancelling", VERSION_HINT, 0.1111111, 0.0, 1.0)
|
||||
));
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
return input * Point(values[0], values[1], values[2]);
|
||||
}, std::vector<EffectParameter*>{
|
||||
new EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -5.0, 5.0),
|
||||
|
@ -54,7 +54,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
}
|
||||
));
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
int flip = index % 2 == 0 ? 1 : -1;
|
||||
Point jitter = Point(flip * values[0], flip * values[1], flip * values[2]);
|
||||
return input + jitter;
|
||||
|
@ -65,7 +65,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
}
|
||||
));
|
||||
auto rippleEffect = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double phase = values[1] * std::numbers::pi;
|
||||
double distance = 100 * values[2] * (input.x * input.x + input.y * input.y);
|
||||
input.z += values[0] * std::sin(phase + distance);
|
||||
|
@ -79,7 +79,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
rippleEffect->getParameter("ripplePhase")->lfo->setUnnormalisedValueNotifyingHost((int) LfoType::Sawtooth);
|
||||
toggleableEffects.push_back(rippleEffect);
|
||||
auto rotateEffect = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
input.rotate(values[0] * std::numbers::pi, values[1] * std::numbers::pi, values[2] * std::numbers::pi);
|
||||
return input;
|
||||
}, std::vector<EffectParameter*>{
|
||||
|
@ -92,7 +92,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
rotateEffect->getParameter("rotateY")->lfoRate->setUnnormalisedValueNotifyingHost(0.2);
|
||||
toggleableEffects.push_back(rotateEffect);
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
return input + Point(values[0], values[1], values[2]);
|
||||
}, std::vector<EffectParameter*>{
|
||||
new EffectParameter("Translate X", "Moves the object horizontally.", "translateX", VERSION_HINT, 0.0, -1.0, 1.0),
|
||||
|
@ -101,7 +101,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
|
|||
}
|
||||
));
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double length = 10 * values[0] * input.magnitude();
|
||||
double newX = input.x * std::cos(length) - input.y * std::sin(length);
|
||||
double newY = input.x * std::sin(length) + input.y * std::cos(length);
|
||||
|
@ -331,7 +331,7 @@ void OscirenderAudioProcessor::addLuaSlider() {
|
|||
}
|
||||
|
||||
luaEffects.push_back(std::make_shared<Effect>(
|
||||
[this, sliderIndex](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this, sliderIndex](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
luaValues[sliderIndex] = values[0];
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
|
@ -668,6 +668,8 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
|
|||
animationTime = playTimeSeconds;
|
||||
}
|
||||
|
||||
juce::SpinLock::ScopedLockType lock1(parsersLock);
|
||||
juce::SpinLock::ScopedLockType lock2(effectsLock);
|
||||
if (currentFile >= 0 && sounds[currentFile]->parser->isAnimatable) {
|
||||
int animFrame = (int)(animationTime * animationRate->getValueUnnormalised() + animationOffset->getValueUnnormalised());
|
||||
auto lineArt = sounds[currentFile]->parser->getLineArt();
|
||||
|
|
|
@ -85,8 +85,8 @@ public:
|
|||
double luaValues[26] = { 0.0 };
|
||||
|
||||
std::shared_ptr<Effect> frequencyEffect = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
frequency = values[0];
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
frequency = values[0].load();
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
"Frequency",
|
||||
|
@ -97,8 +97,8 @@ public:
|
|||
);
|
||||
|
||||
std::shared_ptr<Effect> volumeEffect = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
volume = values[0];
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
volume = values[0].load();
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
"Volume",
|
||||
|
@ -109,8 +109,8 @@ public:
|
|||
);
|
||||
|
||||
std::shared_ptr<Effect> thresholdEffect = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
threshold = values[0];
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
threshold = values[0].load();
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
"Threshold",
|
||||
|
@ -243,7 +243,7 @@ public:
|
|||
|
||||
BooleanParameter* invertImage = new BooleanParameter("Invert Image", "invertImage", VERSION_HINT, false, "Inverts the image so that dark pixels become light, and vice versa.");
|
||||
std::shared_ptr<Effect> imageThreshold = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
"Image Threshold",
|
||||
|
@ -253,7 +253,7 @@ public:
|
|||
)
|
||||
);
|
||||
std::shared_ptr<Effect> imageStride = std::make_shared<Effect>(
|
||||
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
return input;
|
||||
}, new EffectParameter(
|
||||
"Image Stride",
|
||||
|
|
|
@ -88,7 +88,7 @@ void SettingsComponent::update() {
|
|||
}
|
||||
|
||||
void SettingsComponent::mouseMove(const juce::MouseEvent& event) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < 1; i++) {
|
||||
if (toggleComponents[i]->getBounds().removeFromTop(pluginEditor.CLOSED_PREF_SIZE).contains(event.getPosition())) {
|
||||
setMouseCursor(juce::MouseCursor::PointingHandCursor);
|
||||
return;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
BitCrushEffect::BitCrushEffect() {}
|
||||
|
||||
// algorithm from https://www.kvraudio.com/forum/viewtopic.php?t=163880
|
||||
Point BitCrushEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
Point BitCrushEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double value = values[0];
|
||||
// change rage of value from 0-1 to 0.0-0.78
|
||||
double rangedValue = value * 0.78;
|
||||
|
|
|
@ -6,5 +6,5 @@ class BitCrushEffect : public EffectApplication {
|
|||
public:
|
||||
BitCrushEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ BulgeEffect::BulgeEffect() {}
|
|||
|
||||
BulgeEffect::~BulgeEffect() {}
|
||||
|
||||
Point BulgeEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
Point BulgeEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double value = values[0];
|
||||
double translatedBulge = -value + 1;
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@ public:
|
|||
BulgeEffect();
|
||||
~BulgeEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>&, double sampleRate) override;
|
||||
};
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>&, double sampleRate) override;
|
||||
};
|
||||
|
|
|
@ -13,8 +13,8 @@ CustomEffect::~CustomEffect() {
|
|||
parser->close(L);
|
||||
}
|
||||
|
||||
Point CustomEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
auto effectScale = values[0];
|
||||
Point CustomEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
auto effectScale = values[0].load();
|
||||
|
||||
auto x = input.x;
|
||||
auto y = input.y;
|
||||
|
|
|
@ -13,7 +13,7 @@ public:
|
|||
static const juce::String UNIQUE_ID;
|
||||
static const juce::String FILE_NAME;
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
void updateCode(const juce::String& newCode);
|
||||
|
||||
juce::String getCode();
|
||||
|
|
|
@ -4,15 +4,15 @@ DashedLineEffect::DashedLineEffect() {}
|
|||
|
||||
DashedLineEffect::~DashedLineEffect() {}
|
||||
|
||||
Point DashedLineEffect::apply(int index, Point vector, const std::vector<double>& values, double sampleRate) {
|
||||
Point DashedLineEffect::apply(int index, Point vector, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
// dash length in seconds
|
||||
double dashLength = values[0] / 400;
|
||||
int dashLengthSamples = (int)(dashLength * sampleRate);
|
||||
dashLengthSamples = juce::jmin(dashLengthSamples, MAX_BUFFER);
|
||||
|
||||
if (dashIndex >= dashLengthSamples) {
|
||||
dashIndex = 0;
|
||||
bufferIndex = 0;
|
||||
if (dashIndex >= dashLengthSamples) {
|
||||
dashIndex = 0;
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
buffer[bufferIndex] = vector;
|
||||
|
@ -20,8 +20,8 @@ Point DashedLineEffect::apply(int index, Point vector, const std::vector<double>
|
|||
|
||||
vector = buffer[dashIndex];
|
||||
|
||||
if (index % 2 == 0) {
|
||||
dashIndex++;
|
||||
if (index % 2 == 0) {
|
||||
dashIndex++;
|
||||
}
|
||||
|
||||
return vector;
|
||||
|
|
|
@ -7,11 +7,11 @@ public:
|
|||
DashedLineEffect();
|
||||
~DashedLineEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
const static int MAX_BUFFER = 192000;
|
||||
std::vector<Point> buffer = std::vector<Point>(MAX_BUFFER);
|
||||
int dashIndex = 0;
|
||||
int bufferIndex = 0;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ DelayEffect::DelayEffect() {}
|
|||
|
||||
DelayEffect::~DelayEffect() {}
|
||||
|
||||
Point DelayEffect::apply(int index, Point vector, const std::vector<double>& values, double sampleRate) {
|
||||
Point DelayEffect::apply(int index, Point vector, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double decay = values[0];
|
||||
double decayLength = values[1];
|
||||
int delayBufferLength = (int)(sampleRate * decayLength);
|
||||
|
|
|
@ -7,7 +7,7 @@ public:
|
|||
DelayEffect();
|
||||
~DelayEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
const static int MAX_DELAY = 192000 * 10;
|
||||
|
@ -15,4 +15,4 @@ private:
|
|||
int head = 0;
|
||||
int position = 0;
|
||||
int samplesSinceLastDelay = 0;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,13 +4,13 @@ DistortEffect::DistortEffect(bool vertical) : vertical(vertical) {}
|
|||
|
||||
DistortEffect::~DistortEffect() {}
|
||||
|
||||
Point DistortEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
Point DistortEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double value = values[0];
|
||||
int vertical = (int)this->vertical;
|
||||
if (index % 2 == 0) {
|
||||
input.translate((1 - vertical) * value, vertical * value, 0);
|
||||
} else {
|
||||
input.translate((1 - vertical) * -value, vertical * -value, 0);
|
||||
} else {
|
||||
input.translate((1 - vertical) * -value, vertical * -value, 0);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ public:
|
|||
DistortEffect(bool vertical);
|
||||
~DistortEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
private:
|
||||
bool vertical;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ Effect::Effect(std::shared_ptr<EffectApplication> effectApplication, const std::
|
|||
effectApplication(effectApplication),
|
||||
parameters(parameters),
|
||||
enabled(nullptr),
|
||||
actualValues(std::vector<double>(parameters.size(), 0.0)) {}
|
||||
actualValues(std::vector<std::atomic<double>>(parameters.size())) {}
|
||||
|
||||
Effect::Effect(std::shared_ptr<EffectApplication> effectApplication, EffectParameter* parameter) : Effect(effectApplication, std::vector<EffectParameter*>{parameter}) {}
|
||||
|
||||
|
@ -13,13 +13,13 @@ Effect::Effect(EffectApplicationType application, const std::vector<EffectParame
|
|||
application(application),
|
||||
parameters(parameters),
|
||||
enabled(nullptr),
|
||||
actualValues(std::vector<double>(parameters.size(), 0.0)) {}
|
||||
actualValues(std::vector<std::atomic<double>>(parameters.size())) {}
|
||||
|
||||
Effect::Effect(EffectApplicationType application, EffectParameter* parameter) : Effect(application, std::vector<EffectParameter*>{parameter}) {}
|
||||
|
||||
Effect::Effect(const std::vector<EffectParameter*>& parameters) : Effect([](int index, Point input, const std::vector<double>& values, double sampleRate) {return input;}, parameters) {}
|
||||
Effect::Effect(const std::vector<EffectParameter*>& parameters) : Effect([](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {return input;}, parameters) {}
|
||||
|
||||
Effect::Effect(EffectParameter* parameter) : Effect([](int index, Point input, const std::vector<double>& values, double sampleRate) {return input;}, parameter) {}
|
||||
Effect::Effect(EffectParameter* parameter) : Effect([](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {return input;}, parameter) {}
|
||||
|
||||
Point Effect::apply(int index, Point input, double volume) {
|
||||
animateValues(volume);
|
||||
|
@ -105,12 +105,10 @@ double Effect::getValue() {
|
|||
return getValue(0);
|
||||
}
|
||||
|
||||
// Not thread safe! Should only be called from the audio thread
|
||||
double Effect::getActualValue(int index) {
|
||||
return actualValues[index];
|
||||
}
|
||||
|
||||
// Not thread safe! Should only be called from the audio thread
|
||||
double Effect::getActualValue() {
|
||||
return actualValues[0];
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "EffectParameter.h"
|
||||
#include "BooleanParameter.h"
|
||||
|
||||
typedef std::function<Point(int index, Point input, const std::vector<double>& values, double sampleRate)> EffectApplicationType;
|
||||
typedef std::function<Point(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate)> EffectApplicationType;
|
||||
|
||||
class Effect {
|
||||
public:
|
||||
|
@ -43,7 +43,7 @@ public:
|
|||
private:
|
||||
|
||||
juce::SpinLock listenerLock;
|
||||
std::vector<double> actualValues;
|
||||
std::vector<std::atomic<double>> actualValues;
|
||||
int precedence = -1;
|
||||
int sampleRate = 192000;
|
||||
EffectApplicationType application;
|
||||
|
|
|
@ -6,10 +6,10 @@ class EffectApplication {
|
|||
public:
|
||||
EffectApplication() {};
|
||||
|
||||
virtual Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) = 0;
|
||||
virtual Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) = 0;
|
||||
|
||||
void resetPhase();
|
||||
double nextPhase(double frequency, double sampleRate);
|
||||
private:
|
||||
double phase = 0.0;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,9 +7,9 @@ PerspectiveEffect::PerspectiveEffect() {}
|
|||
|
||||
PerspectiveEffect::~PerspectiveEffect() {}
|
||||
|
||||
Point PerspectiveEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
auto effectScale = values[0];
|
||||
auto focalLength = juce::jmax(values[1], 0.001);
|
||||
Point PerspectiveEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
auto effectScale = values[0].load();
|
||||
auto focalLength = juce::jmax(values[1].load(), 0.001);
|
||||
|
||||
Vec3 origin = Vec3(0, 0, -focalLength);
|
||||
camera.setPosition(origin);
|
||||
|
|
|
@ -9,7 +9,7 @@ public:
|
|||
PerspectiveEffect();
|
||||
~PerspectiveEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -6,13 +6,19 @@ PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : juce::T
|
|||
}
|
||||
|
||||
PitchDetector::~PitchDetector() {
|
||||
audioProcessor.consumerStop(consumer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
audioProcessor.consumerStop(consumer);
|
||||
}
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
void PitchDetector::run() {
|
||||
while (!threadShouldExit()) {
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
}
|
||||
audioProcessor.consumerRead(consumer);
|
||||
|
||||
// buffer is for 2 channels, so we need to only use one
|
||||
|
|
|
@ -20,6 +20,7 @@ private:
|
|||
static constexpr int fftOrder = 15;
|
||||
static constexpr int fftSize = 1 << fftOrder;
|
||||
|
||||
juce::CriticalSection consumerLock;
|
||||
std::shared_ptr<BufferConsumer> consumer;
|
||||
std::vector<float> buffer = std::vector<float>(2 * fftSize);
|
||||
juce::dsp::FFT forwardFFT{fftOrder};
|
||||
|
|
|
@ -4,8 +4,8 @@ SmoothEffect::SmoothEffect() {}
|
|||
|
||||
SmoothEffect::~SmoothEffect() {}
|
||||
|
||||
Point SmoothEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
double weight = juce::jmax(values[0], 0.00001);
|
||||
Point SmoothEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double weight = juce::jmax(values[0].load(), 0.00001);
|
||||
weight *= 0.95;
|
||||
double strength = 10;
|
||||
weight = std::log(strength * weight + 1) / std::log(strength + 1);
|
||||
|
|
|
@ -7,7 +7,7 @@ public:
|
|||
SmoothEffect();
|
||||
~SmoothEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
private:
|
||||
Point avg;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ VectorCancellingEffect::VectorCancellingEffect() {}
|
|||
|
||||
VectorCancellingEffect::~VectorCancellingEffect() {}
|
||||
|
||||
Point VectorCancellingEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
Point VectorCancellingEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double value = values[0];
|
||||
if (value < 0.001) {
|
||||
return input;
|
||||
|
|
|
@ -7,8 +7,8 @@ public:
|
|||
VectorCancellingEffect();
|
||||
~VectorCancellingEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
private:
|
||||
int lastIndex = 0;
|
||||
double nextInvert = 0;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ WobbleEffect::WobbleEffect(PitchDetector& pitchDetector) : pitchDetector(pitchDe
|
|||
|
||||
WobbleEffect::~WobbleEffect() {}
|
||||
|
||||
Point WobbleEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
Point WobbleEffect::apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
// TODO: this doesn't consider sample rate
|
||||
smoothedFrequency = smoothedFrequency * 0.99995 + pitchDetector.frequency * 0.00005;
|
||||
double theta = nextPhase(smoothedFrequency, sampleRate);
|
||||
|
|
|
@ -8,9 +8,9 @@ public:
|
|||
WobbleEffect(PitchDetector& pitchDetector);
|
||||
~WobbleEffect();
|
||||
|
||||
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;
|
||||
Point apply(int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
PitchDetector& pitchDetector;
|
||||
double smoothedFrequency = 0;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
|
||||
EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect) : DraggableListBoxItem(lb, data, rn),
|
||||
effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) {
|
||||
if (effect.enabled == nullptr) {
|
||||
DBG("Effect enabled is null");
|
||||
}
|
||||
|
||||
auto parameters = effect.parameters;
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(audioProcessor, effect, i);
|
||||
|
|
|
@ -78,9 +78,9 @@ public:
|
|||
|
||||
void parameterValueChanged(int parameterIndex, float newValue) override {
|
||||
juce::WeakReference<SwitchButton> weakThis = this;
|
||||
juce::MessageManager::callAsync([weakThis, this]() {
|
||||
juce::MessageManager::callAsync([weakThis]() {
|
||||
if (weakThis != nullptr) {
|
||||
setToggleState(parameter->getBoolValue(), juce::NotificationType::dontSendNotification);
|
||||
weakThis->setToggleState(weakThis->parameter->getBoolValue(), juce::NotificationType::dontSendNotification);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "../LookAndFeel.h"
|
||||
#include "VisualiserComponent.h"
|
||||
|
||||
VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) {
|
||||
setVisualiserType(oldVisualiser);
|
||||
|
||||
VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) {
|
||||
resetBuffer();
|
||||
if (!oldVisualiser) {
|
||||
initialiseBrowser();
|
||||
}
|
||||
startTimerHz(60);
|
||||
startThread();
|
||||
|
||||
|
@ -49,7 +50,10 @@ VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, Visualiser
|
|||
}
|
||||
|
||||
VisualiserComponent::~VisualiserComponent() {
|
||||
audioProcessor.consumerStop(consumer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
audioProcessor.consumerStop(consumer);
|
||||
}
|
||||
stopThread(1000);
|
||||
masterReference.clear();
|
||||
}
|
||||
|
@ -127,16 +131,16 @@ void VisualiserComponent::run() {
|
|||
resetBuffer();
|
||||
}
|
||||
|
||||
consumer = audioProcessor.consumerRegister(tempBuffer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
consumer = audioProcessor.consumerRegister(tempBuffer);
|
||||
}
|
||||
audioProcessor.consumerRead(consumer);
|
||||
|
||||
setBuffer(tempBuffer);
|
||||
juce::WeakReference<VisualiserComponent> visualiser(this);
|
||||
if (!oldVisualiser) {
|
||||
juce::MessageManager::callAsync([this, visualiser] () {
|
||||
if (visualiser != nullptr && browser != nullptr) {
|
||||
browser->emitEventIfBrowserIsVisible("audioUpdated", juce::Base64::toBase64(buffer.data(), buffer.size() * sizeof(float)));
|
||||
}
|
||||
});
|
||||
audioUpdated = true;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +151,10 @@ void VisualiserComponent::setPaused(bool paused) {
|
|||
startTimerHz(60);
|
||||
startThread();
|
||||
} else {
|
||||
audioProcessor.consumerStop(consumer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
audioProcessor.consumerStop(consumer);
|
||||
}
|
||||
stopTimer();
|
||||
stopThread(1000);
|
||||
}
|
||||
|
@ -220,7 +227,11 @@ void VisualiserComponent::setVisualiserType(bool oldVisualiser) {
|
|||
child->setVisualiserType(oldVisualiser);
|
||||
}
|
||||
if (oldVisualiser) {
|
||||
browser.reset();
|
||||
oldBrowser = std::move(browser);
|
||||
if (oldBrowser != nullptr) {
|
||||
removeChildComponent(oldBrowser.get());
|
||||
oldBrowser->goToURL("about:blank");
|
||||
}
|
||||
if (closeSettings != nullptr) {
|
||||
closeSettings();
|
||||
}
|
||||
|
@ -260,6 +271,12 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area
|
|||
}
|
||||
|
||||
void VisualiserComponent::initialiseBrowser() {
|
||||
oldBrowser = std::move(browser);
|
||||
if (oldBrowser != nullptr) {
|
||||
removeChildComponent(oldBrowser.get());
|
||||
oldBrowser->goToURL("about:blank");
|
||||
}
|
||||
|
||||
browser = std::make_unique<juce::WebBrowserComponent>(
|
||||
juce::WebBrowserComponent::Options()
|
||||
.withNativeIntegrationEnabled()
|
||||
|
@ -315,9 +332,20 @@ void VisualiserComponent::resetBuffer() {
|
|||
sampleRate = (int) audioProcessor.currentSampleRate;
|
||||
tempBuffer = std::vector<float>(2 * sampleRate * BUFFER_LENGTH_SECS);
|
||||
if (!oldVisualiser && isShowing()) {
|
||||
juce::MessageManager::callAsync([this] () {
|
||||
initialiseBrowser();
|
||||
});
|
||||
restartBrowser = true;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualiserComponent::handleAsyncUpdate() {
|
||||
if (restartBrowser) {
|
||||
initialiseBrowser();
|
||||
restartBrowser = false;
|
||||
}
|
||||
if (audioUpdated && browser != nullptr) {
|
||||
juce::CriticalSection::ScopedLockType scope(lock);
|
||||
browser->emitEventIfBrowserIsVisible("audioUpdated", juce::Base64::toBase64(buffer.data(), buffer.size() * sizeof(float)));
|
||||
audioUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,7 +367,7 @@ void VisualiserComponent::resized() {
|
|||
}
|
||||
|
||||
void VisualiserComponent::childChanged() {
|
||||
if (!oldVisualiser) {
|
||||
if (!oldVisualiser && browser != nullptr) {
|
||||
browser->emitEventIfBrowserIsVisible("childPresent", child != nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ enum class FullScreenMode {
|
|||
};
|
||||
|
||||
class VisualiserWindow;
|
||||
class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread, public juce::MouseListener, public juce::SettableTooltipClient {
|
||||
class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread, public juce::MouseListener, public juce::SettableTooltipClient, public juce::AsyncUpdater {
|
||||
public:
|
||||
VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false);
|
||||
~VisualiserComponent() override;
|
||||
|
@ -41,6 +41,7 @@ public:
|
|||
bool keyPressed(const juce::KeyPress& key) override;
|
||||
void setFullScreen(bool fullScreen);
|
||||
void setVisualiserType(bool oldVisualiser);
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
VisualiserComponent* parent = nullptr;
|
||||
VisualiserComponent* child = nullptr;
|
||||
|
@ -52,12 +53,13 @@ private:
|
|||
const double BUFFER_LENGTH_SECS = 0.02;
|
||||
const double DEFAULT_SAMPLE_RATE = 192000.0;
|
||||
|
||||
|
||||
std::atomic<bool> restartBrowser = false;
|
||||
std::atomic<bool> audioUpdated = false;
|
||||
std::atomic<int> timerId;
|
||||
std::atomic<int> lastMouseX;
|
||||
std::atomic<int> lastMouseY;
|
||||
|
||||
bool oldVisualiser;
|
||||
std::atomic<bool> oldVisualiser;
|
||||
|
||||
juce::CriticalSection lock;
|
||||
std::vector<float> buffer;
|
||||
|
@ -75,6 +77,7 @@ private:
|
|||
std::vector<float> tempBuffer;
|
||||
int precision = 4;
|
||||
|
||||
juce::CriticalSection consumerLock;
|
||||
std::shared_ptr<BufferConsumer> consumer;
|
||||
|
||||
std::function<void(FullScreenMode)> fullScreenCallback;
|
||||
|
@ -110,14 +113,15 @@ private:
|
|||
};
|
||||
|
||||
std::unique_ptr<juce::WebBrowserComponent> browser = nullptr;
|
||||
// keeping this around for memory management reasons
|
||||
std::unique_ptr<juce::WebBrowserComponent> oldBrowser = nullptr;
|
||||
|
||||
void initialiseBrowser();
|
||||
void resetBuffer();
|
||||
void popoutWindow();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent)
|
||||
juce::WeakReference<VisualiserComponent>::Master masterReference;
|
||||
friend class juce::WeakReference<VisualiserComponent>;
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE(VisualiserComponent)
|
||||
};
|
||||
|
||||
class VisualiserWindow : public juce::DocumentWindow {
|
||||
|
|
|
@ -49,7 +49,10 @@ VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p
|
|||
}
|
||||
|
||||
VolumeComponent::~VolumeComponent() {
|
||||
audioProcessor.consumerStop(consumer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(consumerLock);
|
||||
audioProcessor.consumerStop(consumer);
|
||||
}
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
|
@ -103,7 +106,10 @@ void VolumeComponent::run() {
|
|||
continue;
|
||||
}
|
||||
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(consumerLock);
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
}
|
||||
audioProcessor.consumerRead(consumer);
|
||||
|
||||
float leftVolume = 0;
|
||||
|
|
|
@ -91,6 +91,7 @@ private:
|
|||
std::unique_ptr<juce::Drawable> volumeIcon;
|
||||
std::unique_ptr<juce::Drawable> thresholdIcon;
|
||||
|
||||
juce::CriticalSection consumerLock;
|
||||
std::shared_ptr<BufferConsumer> consumer;
|
||||
|
||||
void resetBuffer();
|
||||
|
|
|
@ -684,7 +684,7 @@
|
|||
bigIcon="pSc1mq" applicationCategory="public.app-category.music">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
|
||||
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render"/>
|
||||
<CONFIGURATION name="Release" targetName="osci-render"/>
|
||||
</CONFIGURATIONS>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
|
||||
|
|
Ładowanie…
Reference in New Issue