From 57ac702c3f01e9c6f8664d47043df0b029b74efd Mon Sep 17 00:00:00 2001 From: xenakios Date: Thu, 17 Jan 2019 18:30:40 +0200 Subject: [PATCH] Add JUCE DSP module to Projucer project. Added a sonogram view of the output audio. --- Source/PluginEditor.cpp | 20 +------ Source/PluginEditor.h | 119 ++++++++++++++++++++++++++++++++++++- Source/PluginProcessor.cpp | 5 ++ paulstretchplugin.jucer | 11 ++-- 4 files changed, 133 insertions(+), 22 deletions(-) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 3713cce..eb078ac 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -173,21 +173,6 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau }; m_spec_order_ed.ModuleOrderOrEnabledChangedCallback = [this]() { - /* - const auto& specord = processor.getStretchSource()->getSpectrumProcessOrder(); - for (int i = 0; i < specord.size(); ++i) - { - int grtofind = specord[i].m_index; - for (int j = 0; j < m_parcomps.size(); ++j) - { - int gid = m_parcomps[j]->m_group_id; - if (gid == grtofind) - { - m_parcomps[j]->setEnabled(specord[i].m_enabled); - } - } - } - */ processor.setDirty(); }; @@ -244,8 +229,9 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau m_wavefilter_tab.addTab("Waveform", Colours::white, m_wave_container, true); m_wavefilter_tab.addTab("Ratio mixer", Colours::white, &m_ratiomixeditor, false); m_wavefilter_tab.addTab("Free filter", Colours::white, &m_free_filter_component, false); - - addAndMakeVisible(&m_wavefilter_tab); + m_wavefilter_tab.addTab("Spectrum", Colours::white, &m_sonogram, false); + + addAndMakeVisible(&m_wavefilter_tab); setSize (1200, 320+14*25); startTimer(1, 100); startTimer(2, 1000); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8275f73..a50b32e 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -265,6 +265,122 @@ private: }; +class SimpleFFTComponent : public Component, + private Timer +{ +public: + SimpleFFTComponent() : + + forwardFFT(fftOrder), + spectrogramImage(Image::RGB, 512, 512, true) + { + setOpaque(true); + startTimerHz(60); + + } + + ~SimpleFFTComponent() + { + + } + + void addAudioBlock(const AudioBuffer& bufferToFill) + { + if (bufferToFill.getNumChannels() > 0) + { + const auto* channelData = bufferToFill.getReadPointer(0); + + for (auto i = 0; i < bufferToFill.getNumSamples(); ++i) + pushNextSampleIntoFifo(channelData[i]); + } + } + + void resized() override + { + spectrogramImage = Image(Image::RGB, getWidth(), getHeight(), true); + } + + void paint(Graphics& g) override + { + g.fillAll(Colours::black); + + g.setOpacity(1.0f); + g.drawImage(spectrogramImage, getLocalBounds().toFloat()); + } + + void timerCallback() override + { + if (nextFFTBlockReady) + { + drawNextLineOfSpectrogram(); + nextFFTBlockReady = false; + repaint(); + } + } + + void pushNextSampleIntoFifo(float sample) noexcept + { + // if the fifo contains enough data, set a flag to say + // that the next line should now be rendered.. + if (fifoIndex == fftSize) + { + if (!nextFFTBlockReady) + { + zeromem(fftData, sizeof(fftData)); + memcpy(fftData, fifo, sizeof(fifo)); + nextFFTBlockReady = true; + } + + fifoIndex = 0; + } + + fifo[fifoIndex++] = sample; + } + + void drawNextLineOfSpectrogram() + { + auto rightHandEdge = spectrogramImage.getWidth() - 1; + auto imageHeight = spectrogramImage.getHeight(); + + // first, shuffle our image leftwards by 1 pixel.. + spectrogramImage.moveImageSection(0, 0, 1, 0, rightHandEdge, imageHeight); + + // then render our FFT data.. + forwardFFT.performFrequencyOnlyForwardTransform(fftData); + + // find the range of values produced, so we can scale our rendering to + // show up the detail clearly + auto maxLevel = FloatVectorOperations::findMinAndMax(fftData, fftSize / 2); + + for (auto y = 1; y < imageHeight; ++y) + { + auto skewedProportionY = 1.0f - std::exp(std::log(y / (float)imageHeight) * 0.2f); + auto fftDataIndex = jlimit(0, fftSize / 2, (int)(skewedProportionY * fftSize / 2)); + auto level = jmap(fftData[fftDataIndex], 0.0f, jmax(maxLevel.getEnd(), 1e-5f), 0.0f, 1.0f); + + spectrogramImage.setPixelAt(rightHandEdge, y, Colour::fromHSV(level, 1.0f, level, 1.0f)); + } + } + + enum + { + fftOrder = 10, + fftSize = 1 << fftOrder + }; + +private: + dsp::FFT forwardFFT; + Image spectrogramImage; + + float fifo[fftSize]; + float fftData[8 * fftSize]; + int fifoIndex = 0; + bool nextFFTBlockReady = false; + + +}; + + class AudioFilePreviewComponent : public FilePreviewComponent { public: @@ -319,7 +435,7 @@ public: void chooseFile(); void showRenderDialog(); void executeModalMenuAction(int menuid, int actionid); - + SimpleFFTComponent m_sonogram; private: PaulstretchpluginAudioProcessor& processor; uptrvec m_parcomps; @@ -337,6 +453,7 @@ private: zoom_scrollbar m_zs; RatioMixerEditor m_ratiomixeditor{ 8 }; FreeFilterComponent m_free_filter_component; + MyTabComponent m_wavefilter_tab; Component* m_wave_container=nullptr; void showAbout(); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 83c81b6..c6624ad 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -811,6 +811,11 @@ void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, M { buffer.clear(); } + auto ed = dynamic_cast(getActiveEditor()); + if (ed != nullptr) + { + ed->m_sonogram.addAudioBlock(buffer); + } } //============================================================================== diff --git a/paulstretchplugin.jucer b/paulstretchplugin.jucer index f2e430a..b3f1f2b 100644 --- a/paulstretchplugin.jucer +++ b/paulstretchplugin.jucer @@ -11,7 +11,7 @@ pluginWantsMidiIn="0" pluginProducesMidiOut="0" pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU" aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2" - jucerVersion="5.3.2" headerPath=" " pluginFormats="buildVST,buildVST3,buildAU,buildStandalone"> + jucerVersion="5.4.1" headerPath=" " pluginFormats="buildVST,buildVST3,buildAU,buildStandalone"> - + + + extraDefs=""> + @@ -130,6 +131,7 @@ + @@ -144,6 +146,7 @@ +