Set audio visualiser to 60fps and show current frequency

pull/170/head
James Ball 2023-07-09 21:30:33 +01:00
rodzic 061595b575
commit be64e7325e
7 zmienionych plików z 113 dodań i 7 usunięć

Wyświetl plik

@ -81,6 +81,9 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
addAndMakeVisible(visualiser);
audioProcessor.audioProducer.registerConsumer(consumer);
visualiserProcessor.startThread();
addAndMakeVisible(frequencyLabel);
pitchDetector.startThread();
}
MainComponent::~MainComponent() {
@ -119,6 +122,9 @@ void MainComponent::resized() {
row.removeFromLeft(rowPadding);
createFile.setBounds(row.removeFromLeft(buttonWidth));
bounds.removeFromTop(padding);
frequencyLabel.setBounds(bounds.removeFromTop(20));
bounds.removeFromTop(padding);
visualiser.setBounds(bounds);
}

Wyświetl plik

@ -5,6 +5,7 @@
#include "parser/FileParser.h"
#include "parser/FrameProducer.h"
#include "components/VisualiserComponent.h"
#include "audio/PitchDetector.h"
class OscirenderAudioProcessorEditor;
class MainComponent : public juce::GroupComponent {
@ -31,5 +32,16 @@ private:
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(2048);
VisualiserProcessor visualiserProcessor{consumer, visualiser};
juce::Label frequencyLabel;
PitchDetector pitchDetector{
audioProcessor,
[this](float frequency) {
// round to nearest integer
int roundedFrequency = static_cast<int>(frequency + 0.5f);
frequencyLabel.setText(juce::String(roundedFrequency) + "Hz", juce::dontSendNotification);
}
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
};

Wyświetl plik

@ -0,0 +1,51 @@
#include "PitchDetector.h"
#include "PitchDetector.h"
PitchDetector::PitchDetector(OscirenderAudioProcessor& p, std::function<void(float)> frequencyCallback) : juce::Thread("PitchDetector"), audioProcessor(p), frequencyCallback(frequencyCallback) {}
PitchDetector::~PitchDetector() {
audioProcessor.audioProducer.unregisterConsumer(consumer);
stopThread(1000);
}
void PitchDetector::run() {
audioProcessor.audioProducer.registerConsumer(consumer);
while (!threadShouldExit()) {
auto buffer = consumer->startProcessing();
// buffer is for 2 channels, so we need to only use one
for (int i = 0; i < fftSize; i++) {
fftData[i] = buffer->at(2 * i);
}
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);
consumer->finishedProcessing();
triggerAsyncUpdate();
}
}
void PitchDetector::handleAsyncUpdate() {
frequencyCallback(frequency);
}
float PitchDetector::frequencyFromIndex(int index) {
auto binWidth = audioProcessor.currentSampleRate / fftSize;
return index * binWidth;
}

Wyświetl plik

@ -0,0 +1,29 @@
#pragma once
#include <JuceHeader.h>
#include "../concurrency/BufferConsumer.h"
#include "../PluginProcessor.h"
class PitchDetector : public juce::Thread, public juce::AsyncUpdater {
public:
PitchDetector(OscirenderAudioProcessor& p, std::function<void(float)> frequencyCallback);
~PitchDetector();
void run() override;
void handleAsyncUpdate() override;
std::atomic<float> frequency = 0.0f;
private:
static constexpr int fftOrder = 15;
static constexpr int fftSize = 1 << fftOrder;
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(fftSize);
juce::dsp::FFT forwardFFT{fftOrder};
std::array<float, fftSize * 2> fftData;
OscirenderAudioProcessor& audioProcessor;
std::function<void(float)> frequencyCallback;
float frequencyFromIndex(int index);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchDetector)
};

Wyświetl plik

@ -2,6 +2,7 @@
VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcessor& p) : numChannels(numChannels), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p) {
setOpaque(true);
startTimerHz(60);
}
VisualiserComponent::~VisualiserComponent() {}
@ -35,6 +36,10 @@ void VisualiserComponent::paint(juce::Graphics& g) {
}
}
void VisualiserComponent::timerCallback() {
repaint();
}
void VisualiserComponent::paintChannel(juce::Graphics& g, juce::Rectangle<float> area, int channel) {
juce::Path path;

Wyświetl plik

@ -4,7 +4,7 @@
#include "../concurrency/BufferConsumer.h"
#include "../PluginProcessor.h"
class VisualiserComponent : public juce::Component {
class VisualiserComponent : public juce::Component, public juce::Timer {
public:
VisualiserComponent(int numChannels, OscirenderAudioProcessor& p);
~VisualiserComponent() override;
@ -14,6 +14,7 @@ public:
void paintChannel(juce::Graphics&, juce::Rectangle<float> bounds, int channel);
void paintXY(juce::Graphics&, juce::Rectangle<float> bounds);
void paint(juce::Graphics&) override;
void timerCallback() override;
private:
juce::SpinLock lock;
@ -26,7 +27,7 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent)
};
class VisualiserProcessor : public juce::AsyncUpdater, public juce::Thread {
class VisualiserProcessor : public juce::Thread {
public:
VisualiserProcessor(std::shared_ptr<BufferConsumer> consumer, VisualiserComponent& visualiser) : juce::Thread("VisualiserProcessor"), consumer(consumer), visualiser(visualiser) {}
~VisualiserProcessor() override {}
@ -37,14 +38,9 @@ public:
visualiser.setBuffer(*buffer);
consumer->finishedProcessing();
triggerAsyncUpdate();
}
}
void handleAsyncUpdate() override {
visualiser.repaint();
}
private:
std::shared_ptr<BufferConsumer> consumer;
VisualiserComponent& visualiser;

Wyświetl plik

@ -146,6 +146,9 @@
file="Source/audio/EffectApplication.h"/>
<FILE id="uhyh7T" name="LuaEffect.cpp" compile="1" resource="0" file="Source/audio/LuaEffect.cpp"/>
<FILE id="jqDcZq" name="LuaEffect.h" compile="0" resource="0" file="Source/audio/LuaEffect.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="PbbNqz" name="RotateEffect.cpp" compile="1" resource="0"
file="Source/audio/RotateEffect.cpp"/>
<FILE id="tUwNZV" name="RotateEffect.h" compile="0" resource="0" file="Source/audio/RotateEffect.h"/>
@ -274,6 +277,7 @@
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</LINUX_MAKE>
<VS2022 targetFolder="Builds/VisualStudio2022">
@ -295,6 +299,7 @@
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</VS2022>
<XCODE_MAC targetFolder="Builds/MacOSX">
@ -316,6 +321,7 @@
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</XCODE_MAC>
</EXPORTFORMATS>
@ -329,6 +335,7 @@
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>