kopia lustrzana https://github.com/jameshball/osci-render
Fix crash with trace, improve and fix wobble effect, remove pitch detector, make changing slider values with small increments more reliable
rodzic
8f67d26199
commit
f36d52c2ae
|
@ -143,7 +143,7 @@ void CommonPluginEditor::saveProjectAs() {
|
|||
void CommonPluginEditor::updateTitle() {
|
||||
juce::String title = appName;
|
||||
if (!audioProcessor.currentProjectFile.isEmpty()) {
|
||||
appName += " - " + audioProcessor.currentProjectFile;
|
||||
title += " - " + audioProcessor.currentProjectFile;
|
||||
}
|
||||
getTopLevelComponent()->setName(title);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p, OscirenderAudioP
|
|||
frequency.slider.setValue(audioProcessor.frequencyEffect->getValue(), juce::dontSendNotification);
|
||||
|
||||
frequency.slider.onValueChange = [this] {
|
||||
audioProcessor.frequencyEffect->setValue(frequency.slider.getValue());
|
||||
audioProcessor.frequencyEffect->parameters[0]->setUnnormalisedValueNotifyingHost(frequency.slider.getValue());
|
||||
};
|
||||
|
||||
/*addBtn.setButtonText("Add Item...");
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "parser/FileParser.h"
|
||||
#include "parser/FrameProducer.h"
|
||||
#include "visualiser/VisualiserComponent.h"
|
||||
#include "audio/PitchDetector.h"
|
||||
#include "UGen/ugen_JuceEnvelopeComponent.h"
|
||||
#include "components/SvgButton.h"
|
||||
|
||||
|
|
|
@ -103,10 +103,15 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
|
|||
std::make_shared<SmoothEffect>(),
|
||||
new EffectParameter("Smoothing", "This works as a low-pass frequency filter that removes high frequencies, making the image look smoother, and audio sound less harsh.", "smoothing", VERSION_HINT, 0.75, 0.0, 1.0)
|
||||
));
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
std::shared_ptr<Effect> wobble = std::make_shared<Effect>(
|
||||
wobbleEffect,
|
||||
new EffectParameter("Wobble", "Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble.", "wobble", VERSION_HINT, 0.3, 0.0, 1.0)
|
||||
));
|
||||
std::vector<EffectParameter*>{
|
||||
new EffectParameter("Wobble Amount", "Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble.", "wobble", VERSION_HINT, 0.3, 0.0, 1.0),
|
||||
new EffectParameter("Wobble Phase", "Controls the phase of the wobble.", "wobblePhase", VERSION_HINT, 0.0, -1.0, 1.0),
|
||||
}
|
||||
);
|
||||
wobble->getParameter("wobblePhase")->lfo->setUnnormalisedValueNotifyingHost((int) LfoType::Sawtooth);
|
||||
toggleableEffects.push_back(wobble);
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
delayEffect,
|
||||
std::vector<EffectParameter*>{
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "audio/SampleRateManager.h"
|
||||
#include <numbers>
|
||||
#include "audio/DelayEffect.h"
|
||||
#include "audio/PitchDetector.h"
|
||||
#include "audio/WobbleEffect.h"
|
||||
#include "audio/PerspectiveEffect.h"
|
||||
#include "obj/ObjectServer.h"
|
||||
|
@ -111,7 +110,7 @@ public:
|
|||
|
||||
BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false, "Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider.");
|
||||
BooleanParameter* inputEnabled = new BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false, "Enable to use input audio, instead of the generated audio.");
|
||||
std::atomic<float> frequency = 220.0f;
|
||||
std::atomic<double> frequency = 220.0;
|
||||
|
||||
juce::SpinLock parsersLock;
|
||||
std::vector<std::shared_ptr<FileParser>> parsers;
|
||||
|
@ -177,8 +176,7 @@ public:
|
|||
|
||||
double animationTime = 0.f;
|
||||
|
||||
PitchDetector pitchDetector{*this};
|
||||
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(pitchDetector);
|
||||
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(*this);
|
||||
|
||||
juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain);
|
||||
|
||||
|
@ -208,7 +206,7 @@ public:
|
|||
void notifyErrorListeners(int lineNumber, juce::String id, juce::String error);
|
||||
private:
|
||||
|
||||
bool prevMidiEnabled = !midiEnabled->getBoolValue();
|
||||
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
|
||||
|
||||
juce::SpinLock audioThreadCallbackLock;
|
||||
std::function<void(const juce::AudioBuffer<float>&)> audioThreadCallback;
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
#include "PitchDetector.h"
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : AudioBackgroundThread("PitchDetector", audioProcessor.threadManager), audioProcessor(audioProcessor) {}
|
||||
|
||||
void PitchDetector::runTask(const std::vector<OsciPoint>& points) {
|
||||
// buffer is for 2 channels, so we need to only use one
|
||||
for (int i = 0; i < fftSize; i++) {
|
||||
fftData[i] = points[i].x;
|
||||
}
|
||||
|
||||
forwardFFT.performFrequencyOnlyForwardTransform(fftData.data());
|
||||
|
||||
// get frequency of the peak
|
||||
int maxIndex = 0;
|
||||
for (int i = 0; i < fftSize / 2; ++i) {
|
||||
if (frequencyFromIndex(i) < 20 || frequencyFromIndex(i) > 20000) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto current = fftData[i];
|
||||
if (current > fftData[maxIndex]) {
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
frequency = frequencyFromIndex(maxIndex);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
int PitchDetector::prepareTask(double sampleRate, int samplesPerBlock) {
|
||||
this->sampleRate = sampleRate;
|
||||
return fftSize;
|
||||
}
|
||||
|
||||
void PitchDetector::stopTask() {}
|
||||
|
||||
void PitchDetector::handleAsyncUpdate() {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
for (auto& callback : callbacks) {
|
||||
callback(frequency);
|
||||
}
|
||||
}
|
||||
|
||||
int PitchDetector::addCallback(std::function<void(float)> callback) {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
callbacks.push_back(callback);
|
||||
return callbacks.size() - 1;
|
||||
}
|
||||
|
||||
void PitchDetector::removeCallback(int index) {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
callbacks.erase(callbacks.begin() + index);
|
||||
}
|
||||
|
||||
float PitchDetector::frequencyFromIndex(int index) {
|
||||
auto binWidth = sampleRate / fftSize;
|
||||
return index * binWidth;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
#include "../concurrency/AudioBackgroundThread.h"
|
||||
|
||||
class OscirenderAudioProcessor;
|
||||
class PitchDetector : public AudioBackgroundThread, public juce::AsyncUpdater {
|
||||
public:
|
||||
PitchDetector(OscirenderAudioProcessor& audioProcessor);
|
||||
|
||||
int prepareTask(double sampleRate, int samplesPerBlock) override;
|
||||
void runTask(const std::vector<OsciPoint>& points) override;
|
||||
void stopTask() override;
|
||||
void handleAsyncUpdate() override;
|
||||
int addCallback(std::function<void(float)> callback);
|
||||
void removeCallback(int index);
|
||||
|
||||
std::atomic<float> frequency = 0.0f;
|
||||
|
||||
private:
|
||||
static constexpr int fftOrder = 15;
|
||||
static constexpr int fftSize = 1 << fftOrder;
|
||||
|
||||
juce::dsp::FFT forwardFFT{fftOrder};
|
||||
std::array<float, fftSize * 2> fftData;
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
std::vector<std::function<void(float)>> callbacks;
|
||||
juce::SpinLock lock;
|
||||
float sampleRate = 192000.0f;
|
||||
|
||||
float frequencyFromIndex(int index);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchDetector)
|
||||
};
|
|
@ -83,7 +83,7 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star
|
|||
if (audioProcessor.midiEnabled->getBoolValue()) {
|
||||
actualFrequency = frequency * pitchWheelAdjustment;
|
||||
} else {
|
||||
actualFrequency = audioProcessor.frequency;
|
||||
actualFrequency = audioProcessor.frequency.load();
|
||||
}
|
||||
|
||||
for (auto sample = startSample; sample < startSample + numSamples; ++sample) {
|
||||
|
@ -168,12 +168,13 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star
|
|||
}
|
||||
|
||||
if (!renderingSample && frameDrawn >= drawnFrameLength) {
|
||||
double currentShapeLength = frame[currentShape]->len;
|
||||
if (sound.load() != nullptr && currentlyPlaying) {
|
||||
frameLength = sound.load()->updateFrame(frame);
|
||||
}
|
||||
frameDrawn -= drawnFrameLength;
|
||||
if (traceEnabled) {
|
||||
shapeDrawn = juce::jlimit(0.0, frame[currentShape]->len, frameDrawn);
|
||||
shapeDrawn = juce::jlimit(0.0, currentShapeLength, frameDrawn);
|
||||
}
|
||||
currentShape = 0;
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#include "WobbleEffect.h"
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
WobbleEffect::WobbleEffect(PitchDetector& pitchDetector) : pitchDetector(pitchDetector) {}
|
||||
WobbleEffect::WobbleEffect(OscirenderAudioProcessor& p) : audioProcessor(p) {}
|
||||
|
||||
WobbleEffect::~WobbleEffect() {}
|
||||
|
||||
OsciPoint WobbleEffect::apply(int index, OsciPoint 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);
|
||||
double delta = 0.5 * values[0] * std::sin(theta);
|
||||
double wobblePhase = values[1] * std::numbers::pi;
|
||||
double theta = nextPhase(audioProcessor.frequency, sampleRate) + wobblePhase;
|
||||
double delta = 0.5 * values[0] * std::sin(theta);
|
||||
|
||||
return input + delta;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#pragma once
|
||||
#include "EffectApplication.h"
|
||||
#include "../shape/OsciPoint.h"
|
||||
#include "PitchDetector.h"
|
||||
|
||||
class OscirenderAudioProcessor;
|
||||
class WobbleEffect : public EffectApplication {
|
||||
public:
|
||||
WobbleEffect(PitchDetector& pitchDetector);
|
||||
WobbleEffect(OscirenderAudioProcessor& p);
|
||||
~WobbleEffect();
|
||||
|
||||
OsciPoint apply(int index, OsciPoint input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
PitchDetector& pitchDetector;
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
double smoothedFrequency = 0;
|
||||
};
|
||||
|
|
|
@ -54,6 +54,17 @@ EffectComponent::EffectComponent(Effect& effect, int index) : effect(effect), in
|
|||
|
||||
EffectComponent::EffectComponent(Effect& effect) : EffectComponent(effect, 0) {}
|
||||
|
||||
void EffectComponent::setSliderValueIfChanged(FloatParameter* parameter, juce::Slider& slider) {
|
||||
juce::String newSliderValue = juce::String(parameter->getValueUnnormalised(), 3);
|
||||
juce::String oldSliderValue = juce::String((float) slider.getValue(), 3);
|
||||
|
||||
// only set the slider value if the parameter value is different so that we prefer the more
|
||||
// precise slider value.
|
||||
if (newSliderValue != oldSliderValue) {
|
||||
slider.setValue(parameter->getValueUnnormalised(), juce::dontSendNotification);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectComponent::setupComponent() {
|
||||
EffectParameter* parameter = effect.parameters[index];
|
||||
|
||||
|
@ -64,7 +75,7 @@ void EffectComponent::setupComponent() {
|
|||
label.setInterceptsMouseClicks(false, false);
|
||||
|
||||
slider.setRange(parameter->min, parameter->max, parameter->step);
|
||||
slider.setValue(parameter->getValueUnnormalised(), juce::dontSendNotification);
|
||||
setSliderValueIfChanged(parameter, slider);
|
||||
slider.setDoubleClickReturnValue(true, parameter->defaultValue);
|
||||
|
||||
lfoEnabled = parameter->lfo != nullptr && parameter->lfoRate != nullptr;
|
||||
|
@ -86,6 +97,7 @@ void EffectComponent::setupComponent() {
|
|||
};
|
||||
|
||||
lfoSlider.setRange(parameter->lfoRate->min, parameter->lfoRate->max, parameter->lfoRate->step);
|
||||
setSliderValueIfChanged(parameter->lfoRate, lfoSlider);
|
||||
lfoSlider.setValue(parameter->lfoRate->getValueUnnormalised(), juce::dontSendNotification);
|
||||
lfoSlider.setSkewFactorFromMidPoint(parameter->lfoRate->min + 0.1 * (parameter->lfoRate->max - parameter->lfoRate->min));
|
||||
lfoSlider.setDoubleClickReturnValue(true, 1.0);
|
||||
|
|
|
@ -86,6 +86,7 @@ private:
|
|||
const int TEXT_WIDTH = 120;
|
||||
const int SMALL_TEXT_WIDTH = 60;
|
||||
|
||||
void setSliderValueIfChanged(FloatParameter* parameter, juce::Slider& slider);
|
||||
void setupComponent();
|
||||
bool lfoEnabled = true;
|
||||
bool sidechainEnabled = true;
|
||||
|
|
|
@ -23,7 +23,7 @@ private:
|
|||
|
||||
AudioBackgroundThreadManager& manager;
|
||||
std::unique_ptr<BufferConsumer> consumer = nullptr;
|
||||
bool shouldBeRunning = false;
|
||||
std::atomic<bool> shouldBeRunning = false;
|
||||
std::atomic<bool> isPrepared = false;
|
||||
std::atomic<bool> deleting = false;
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@ void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) {
|
|||
}
|
||||
}
|
||||
|
||||
__attribute__((no_sanitize("thread")))
|
||||
void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
|
|
@ -185,7 +185,7 @@ private:
|
|||
std::vector<float> smoothedXSamples;
|
||||
std::vector<float> smoothedYSamples;
|
||||
std::vector<float> smoothedZSamples;
|
||||
int sampleBufferCount = 0;
|
||||
std::atomic<int> sampleBufferCount = 0;
|
||||
int prevSampleBufferCount = 0;
|
||||
long lastTriggerPosition = 0;
|
||||
|
||||
|
|
|
@ -110,9 +110,6 @@
|
|||
file="Source/audio/PerspectiveEffect.cpp"/>
|
||||
<FILE id="h0dMim" name="PerspectiveEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/PerspectiveEffect.h"/>
|
||||
<FILE id="t2bsR8" name="PitchDetector.cpp" compile="1" resource="0"
|
||||
file="Source/audio/PitchDetector.cpp"/>
|
||||
<FILE id="rQC2gX" name="PitchDetector.h" compile="0" resource="0" file="Source/audio/PitchDetector.h"/>
|
||||
<FILE id="t5g8pf" name="PublicSynthesiser.h" compile="0" resource="0"
|
||||
file="Source/audio/PublicSynthesiser.h"/>
|
||||
<FILE id="Q5kjpU" name="SampleRateManager.h" compile="0" resource="0"
|
||||
|
|
Ładowanie…
Reference in New Issue