From 9a452b7c1bae65d14df1275b2b66770ec369c722 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 22 Oct 2025 16:58:27 +0000 Subject: [PATCH] Upload files to "Source" --- Source/ScopeComponent.h | 202 ++++++++++++++++++------------------ Source/ScopeDataCollector.h | 122 +++++++++++----------- 2 files changed, 162 insertions(+), 162 deletions(-) diff --git a/Source/ScopeComponent.h b/Source/ScopeComponent.h index 5d4ec4d..da731de 100644 --- a/Source/ScopeComponent.h +++ b/Source/ScopeComponent.h @@ -1,102 +1,102 @@ -#pragma once - -#include "AudioBufferQueue.h" - -//============================================================================== -template -class ScopeComponent : public juce::Component, - private juce::Timer -{ -public: - using Queue = AudioBufferQueue; - - //============================================================================== - ScopeComponent(Queue& queueToUse) - : audioBufferQueue(queueToUse) - { - sampleData.fill(SampleType(0)); - setFramesPerSecond(30); - } - - //============================================================================== - void setFramesPerSecond(int framesPerSecond) - { - jassert(framesPerSecond > 0 && framesPerSecond < 1000); - startTimerHz(framesPerSecond); - } - - //============================================================================== - void paint(juce::Graphics& g) override - { - g.fillAll(juce::Colours::black); - g.setColour(juce::Colours::white); - - auto area = getLocalBounds(); - auto h = (SampleType)area.getHeight(); - auto w = (SampleType)area.getWidth(); - - // Oscilloscope - auto scopeRect = juce::Rectangle{ SampleType(0), SampleType(0), w, h / 2 }; - plot(sampleData.data(), sampleData.size(), g, scopeRect, SampleType(1), h / 4); - - // Spectrum - auto spectrumRect = juce::Rectangle{ SampleType(0), h / 2, w, h / 2 }; - plot(spectrumData.data(), spectrumData.size() / 4, g, spectrumRect); - } - - //============================================================================== - void resized() override {} - -private: - //============================================================================== - Queue& audioBufferQueue; - std::array sampleData; - - juce::dsp::FFT fft{ Queue::order }; - using WindowFun = juce::dsp::WindowingFunction; - WindowFun windowFun{ (size_t)fft.getSize(), WindowFun::hann }; - std::array spectrumData; - - //============================================================================== - void timerCallback() override - { - audioBufferQueue.pop(sampleData.data()); - juce::FloatVectorOperations::copy(spectrumData.data(), sampleData.data(), (int)sampleData.size()); - - auto fftSize = (size_t)fft.getSize(); - - jassert(spectrumData.size() == 2 * fftSize); - windowFun.multiplyWithWindowingTable(spectrumData.data(), fftSize); - fft.performFrequencyOnlyForwardTransform(spectrumData.data()); - - static constexpr auto mindB = SampleType(-160); - static constexpr auto maxdB = SampleType(0); - - 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)); - - repaint(); - } - - //============================================================================== - static void plot(const SampleType* data, - size_t numSamples, - juce::Graphics& g, - juce::Rectangle rect, - SampleType scaler = SampleType(1), - SampleType offset = SampleType(0)) - { - auto w = rect.getWidth(); - auto h = rect.getHeight(); - auto right = rect.getRight(); - - auto center = rect.getBottom() - offset; - auto gain = h * scaler; - - 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)), - center - gain * data[i - 1], - juce::jmap(SampleType(i), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)), - center - gain * data[i] }); - } +#pragma once + +#include "AudioBufferQueue.h" + +//============================================================================== +template +class ScopeComponent : public juce::Component, + private juce::Timer +{ +public: + using Queue = AudioBufferQueue; + + //============================================================================== + ScopeComponent(Queue& queueToUse) + : audioBufferQueue(queueToUse) + { + sampleData.fill(SampleType(0)); + setFramesPerSecond(30); + } + + //============================================================================== + void setFramesPerSecond(int framesPerSecond) + { + jassert(framesPerSecond > 0 && framesPerSecond < 1000); + startTimerHz(framesPerSecond); + } + + //============================================================================== + void paint(juce::Graphics& g) override + { + g.fillAll(juce::Colours::black); + g.setColour(juce::Colours::white); + + auto area = getLocalBounds(); + auto h = (SampleType)area.getHeight(); + auto w = (SampleType)area.getWidth(); + + // Oscilloscope + auto scopeRect = juce::Rectangle{ SampleType(0), SampleType(0), w, h / 2 }; + plot(sampleData.data(), sampleData.size(), g, scopeRect, SampleType(1), h / 4); + + // Spectrum + auto spectrumRect = juce::Rectangle{ SampleType(0), h / 2, w, h / 2 }; + plot(spectrumData.data(), spectrumData.size() / 4, g, spectrumRect); + } + + //============================================================================== + void resized() override {} + +private: + //============================================================================== + Queue& audioBufferQueue; + std::array sampleData; + + juce::dsp::FFT fft{ Queue::order }; + using WindowFun = juce::dsp::WindowingFunction; + WindowFun windowFun{ (size_t)fft.getSize(), WindowFun::hann }; + std::array spectrumData; + + //============================================================================== + void timerCallback() override + { + audioBufferQueue.pop(sampleData.data()); + juce::FloatVectorOperations::copy(spectrumData.data(), sampleData.data(), (int)sampleData.size()); + + auto fftSize = (size_t)fft.getSize(); + + jassert(spectrumData.size() == 2 * fftSize); + windowFun.multiplyWithWindowingTable(spectrumData.data(), fftSize); + fft.performFrequencyOnlyForwardTransform(spectrumData.data()); + + static constexpr auto mindB = SampleType(-160); + static constexpr auto maxdB = SampleType(0); + + 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)); + + repaint(); + } + + //============================================================================== + static void plot(const SampleType* data, + size_t numSamples, + juce::Graphics& g, + juce::Rectangle rect, + SampleType scaler = SampleType(1), + SampleType offset = SampleType(0)) + { + auto w = rect.getWidth(); + auto h = rect.getHeight(); + auto right = rect.getRight(); + + auto center = rect.getBottom() - offset; + auto gain = h * scaler; + + 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)), + center - gain * data[i - 1], + juce::jmap(SampleType(i), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)), + center - gain * data[i] }); + } }; \ No newline at end of file diff --git a/Source/ScopeDataCollector.h b/Source/ScopeDataCollector.h index e5cd7c8..a271ebb 100644 --- a/Source/ScopeDataCollector.h +++ b/Source/ScopeDataCollector.h @@ -1,62 +1,62 @@ -#pragma once - -template -class ScopeDataCollector -{ -public: - //============================================================================== - ScopeDataCollector(AudioBufferQueue& queueToUse) - : audioBufferQueue(queueToUse) - { - } - - //============================================================================== - void process(const SampleType* data, size_t numSamples) - { - size_t index = 0; - - if (state == State::waitingForTrigger) - { - while (index++ < numSamples) - { - auto currentSample = *data++; - - if (currentSample >= triggerLevel && prevSample < triggerLevel) - { - numCollected = 0; - state = State::collecting; - break; - } - - prevSample = currentSample; - } - } - - if (state == State::collecting) - { - while (index++ < numSamples) - { - buffer[numCollected++] = *data++; - - if (numCollected == buffer.size()) - { - audioBufferQueue.push(buffer.data(), buffer.size()); - state = State::waitingForTrigger; - prevSample = SampleType(100); - break; - } - } - } - } - -private: - //============================================================================== - AudioBufferQueue& audioBufferQueue; - std::array::bufferSize> buffer; - size_t numCollected; - SampleType prevSample = SampleType(100); - - static constexpr auto triggerLevel = SampleType(0.05); - - enum class State { waitingForTrigger, collecting } state{ State::waitingForTrigger }; +#pragma once + +template +class ScopeDataCollector +{ +public: + //============================================================================== + ScopeDataCollector(AudioBufferQueue& queueToUse) + : audioBufferQueue(queueToUse) + { + } + + //============================================================================== + void process(const SampleType* data, size_t numSamples) + { + size_t index = 0; + + if (state == State::waitingForTrigger) + { + while (index++ < numSamples) + { + auto currentSample = *data++; + + if (currentSample >= triggerLevel && prevSample < triggerLevel) + { + numCollected = 0; + state = State::collecting; + break; + } + + prevSample = currentSample; + } + } + + if (state == State::collecting) + { + while (index++ < numSamples) + { + buffer[numCollected++] = *data++; + + if (numCollected == buffer.size()) + { + audioBufferQueue.push(buffer.data(), buffer.size()); + state = State::waitingForTrigger; + prevSample = SampleType(100); + break; + } + } + } + } + +private: + //============================================================================== + AudioBufferQueue& audioBufferQueue; + std::array::bufferSize> buffer; + size_t numCollected; + SampleType prevSample = SampleType(100); + + static constexpr auto triggerLevel = SampleType(0.05); + + enum class State { waitingForTrigger, collecting } state{ State::waitingForTrigger }; }; \ No newline at end of file