Upload files to "Source"

This commit is contained in:
ed
2025-10-22 16:58:27 +00:00
parent 1c23e3a1d0
commit 9a452b7c1b
2 changed files with 162 additions and 162 deletions

View File

@@ -1,102 +1,102 @@
#pragma once #pragma once
#include "AudioBufferQueue.h" #include "AudioBufferQueue.h"
//============================================================================== //==============================================================================
template <typename SampleType> template <typename SampleType>
class ScopeComponent : public juce::Component, class ScopeComponent : public juce::Component,
private juce::Timer private juce::Timer
{ {
public: public:
using Queue = AudioBufferQueue<SampleType>; using Queue = AudioBufferQueue<SampleType>;
//============================================================================== //==============================================================================
ScopeComponent(Queue& queueToUse) ScopeComponent(Queue& queueToUse)
: audioBufferQueue(queueToUse) : audioBufferQueue(queueToUse)
{ {
sampleData.fill(SampleType(0)); sampleData.fill(SampleType(0));
setFramesPerSecond(30); setFramesPerSecond(30);
} }
//============================================================================== //==============================================================================
void setFramesPerSecond(int framesPerSecond) void setFramesPerSecond(int framesPerSecond)
{ {
jassert(framesPerSecond > 0 && framesPerSecond < 1000); jassert(framesPerSecond > 0 && framesPerSecond < 1000);
startTimerHz(framesPerSecond); startTimerHz(framesPerSecond);
} }
//============================================================================== //==============================================================================
void paint(juce::Graphics& g) override void paint(juce::Graphics& g) override
{ {
g.fillAll(juce::Colours::black); g.fillAll(juce::Colours::black);
g.setColour(juce::Colours::white); g.setColour(juce::Colours::white);
auto area = getLocalBounds(); auto area = getLocalBounds();
auto h = (SampleType)area.getHeight(); auto h = (SampleType)area.getHeight();
auto w = (SampleType)area.getWidth(); auto w = (SampleType)area.getWidth();
// Oscilloscope // Oscilloscope
auto scopeRect = juce::Rectangle<SampleType>{ SampleType(0), SampleType(0), w, h / 2 }; auto scopeRect = juce::Rectangle<SampleType>{ SampleType(0), SampleType(0), w, h / 2 };
plot(sampleData.data(), sampleData.size(), g, scopeRect, SampleType(1), h / 4); plot(sampleData.data(), sampleData.size(), g, scopeRect, SampleType(1), h / 4);
// Spectrum // Spectrum
auto spectrumRect = juce::Rectangle<SampleType>{ SampleType(0), h / 2, w, h / 2 }; auto spectrumRect = juce::Rectangle<SampleType>{ SampleType(0), h / 2, w, h / 2 };
plot(spectrumData.data(), spectrumData.size() / 4, g, spectrumRect); plot(spectrumData.data(), spectrumData.size() / 4, g, spectrumRect);
} }
//============================================================================== //==============================================================================
void resized() override {} void resized() override {}
private: private:
//============================================================================== //==============================================================================
Queue& audioBufferQueue; Queue& audioBufferQueue;
std::array<SampleType, Queue::bufferSize> sampleData; std::array<SampleType, Queue::bufferSize> sampleData;
juce::dsp::FFT fft{ Queue::order }; juce::dsp::FFT fft{ Queue::order };
using WindowFun = juce::dsp::WindowingFunction<SampleType>; using WindowFun = juce::dsp::WindowingFunction<SampleType>;
WindowFun windowFun{ (size_t)fft.getSize(), WindowFun::hann }; WindowFun windowFun{ (size_t)fft.getSize(), WindowFun::hann };
std::array<SampleType, 2 * Queue::bufferSize> spectrumData; std::array<SampleType, 2 * Queue::bufferSize> spectrumData;
//============================================================================== //==============================================================================
void timerCallback() override void timerCallback() override
{ {
audioBufferQueue.pop(sampleData.data()); audioBufferQueue.pop(sampleData.data());
juce::FloatVectorOperations::copy(spectrumData.data(), sampleData.data(), (int)sampleData.size()); juce::FloatVectorOperations::copy(spectrumData.data(), sampleData.data(), (int)sampleData.size());
auto fftSize = (size_t)fft.getSize(); auto fftSize = (size_t)fft.getSize();
jassert(spectrumData.size() == 2 * fftSize); jassert(spectrumData.size() == 2 * fftSize);
windowFun.multiplyWithWindowingTable(spectrumData.data(), fftSize); windowFun.multiplyWithWindowingTable(spectrumData.data(), fftSize);
fft.performFrequencyOnlyForwardTransform(spectrumData.data()); fft.performFrequencyOnlyForwardTransform(spectrumData.data());
static constexpr auto mindB = SampleType(-160); static constexpr auto mindB = SampleType(-160);
static constexpr auto maxdB = SampleType(0); static constexpr auto maxdB = SampleType(0);
for (auto& s : spectrumData) for (auto& s : spectrumData)
s = juce::jmap(juce::jlimit(mindB, maxdB, juce::Decibels::gainToDecibels(s) - juce::Decibels::gainToDecibels(SampleType(fftSize))), mindB, maxdB, SampleType(0), SampleType(1)); s = juce::jmap(juce::jlimit(mindB, maxdB, juce::Decibels::gainToDecibels(s) - juce::Decibels::gainToDecibels(SampleType(fftSize))), mindB, maxdB, SampleType(0), SampleType(1));
repaint(); repaint();
} }
//============================================================================== //==============================================================================
static void plot(const SampleType* data, static void plot(const SampleType* data,
size_t numSamples, size_t numSamples,
juce::Graphics& g, juce::Graphics& g,
juce::Rectangle<SampleType> rect, juce::Rectangle<SampleType> rect,
SampleType scaler = SampleType(1), SampleType scaler = SampleType(1),
SampleType offset = SampleType(0)) SampleType offset = SampleType(0))
{ {
auto w = rect.getWidth(); auto w = rect.getWidth();
auto h = rect.getHeight(); auto h = rect.getHeight();
auto right = rect.getRight(); auto right = rect.getRight();
auto center = rect.getBottom() - offset; auto center = rect.getBottom() - offset;
auto gain = h * scaler; auto gain = h * scaler;
for (size_t i = 1; i < numSamples; ++i) for (size_t i = 1; i < numSamples; ++i)
g.drawLine({ juce::jmap(SampleType(i - 1), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)), g.drawLine({ juce::jmap(SampleType(i - 1), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)),
center - gain * data[i - 1], center - gain * data[i - 1],
juce::jmap(SampleType(i), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)), juce::jmap(SampleType(i), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)),
center - gain * data[i] }); center - gain * data[i] });
} }
}; };

View File

@@ -1,62 +1,62 @@
#pragma once #pragma once
template <typename SampleType> template <typename SampleType>
class ScopeDataCollector class ScopeDataCollector
{ {
public: public:
//============================================================================== //==============================================================================
ScopeDataCollector(AudioBufferQueue<SampleType>& queueToUse) ScopeDataCollector(AudioBufferQueue<SampleType>& queueToUse)
: audioBufferQueue(queueToUse) : audioBufferQueue(queueToUse)
{ {
} }
//============================================================================== //==============================================================================
void process(const SampleType* data, size_t numSamples) void process(const SampleType* data, size_t numSamples)
{ {
size_t index = 0; size_t index = 0;
if (state == State::waitingForTrigger) if (state == State::waitingForTrigger)
{ {
while (index++ < numSamples) while (index++ < numSamples)
{ {
auto currentSample = *data++; auto currentSample = *data++;
if (currentSample >= triggerLevel && prevSample < triggerLevel) if (currentSample >= triggerLevel && prevSample < triggerLevel)
{ {
numCollected = 0; numCollected = 0;
state = State::collecting; state = State::collecting;
break; break;
} }
prevSample = currentSample; prevSample = currentSample;
} }
} }
if (state == State::collecting) if (state == State::collecting)
{ {
while (index++ < numSamples) while (index++ < numSamples)
{ {
buffer[numCollected++] = *data++; buffer[numCollected++] = *data++;
if (numCollected == buffer.size()) if (numCollected == buffer.size())
{ {
audioBufferQueue.push(buffer.data(), buffer.size()); audioBufferQueue.push(buffer.data(), buffer.size());
state = State::waitingForTrigger; state = State::waitingForTrigger;
prevSample = SampleType(100); prevSample = SampleType(100);
break; break;
} }
} }
} }
} }
private: private:
//============================================================================== //==============================================================================
AudioBufferQueue<SampleType>& audioBufferQueue; AudioBufferQueue<SampleType>& audioBufferQueue;
std::array<SampleType, AudioBufferQueue<SampleType>::bufferSize> buffer; std::array<SampleType, AudioBufferQueue<SampleType>::bufferSize> buffer;
size_t numCollected; size_t numCollected;
SampleType prevSample = SampleType(100); SampleType prevSample = SampleType(100);
static constexpr auto triggerLevel = SampleType(0.05); static constexpr auto triggerLevel = SampleType(0.05);
enum class State { waitingForTrigger, collecting } state{ State::waitingForTrigger }; enum class State { waitingForTrigger, collecting } state{ State::waitingForTrigger };
}; };