Fix several threading bugs, and crash when changing sample rate

pull/254/head
James H Ball 2024-08-26 18:09:29 +01:00 zatwierdzone przez James H Ball
rodzic 2556f6c68f
commit 6194b039f3
37 zmienionych plików z 139 dodań i 105 usunięć

Wyświetl plik

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

Wyświetl plik

@ -38,8 +38,6 @@ private:
juce::ComboBox fileType;
juce::TextButton createFile{"Create File"};
juce::TextButton openOscilloscope{"Open Oscilloscope"};
juce::Label frequencyLabel;
int callbackIndex = -1;

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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