kopia lustrzana https://github.com/jameshball/osci-render
Set audio visualiser to 60fps and show current frequency
rodzic
061595b575
commit
be64e7325e
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Ładowanie…
Reference in New Issue