|
- /*
- ==============================================================================
-
- This file is part of the JUCE examples.
- Copyright (c) 2022 - Raw Material Software Limited
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
- WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
- PURPOSE, ARE DISCLAIMED.
-
- ==============================================================================
- */
-
- /*******************************************************************************
- The block below describes the properties of this PIP. A PIP is a short snippet
- of code that can be read by the Projucer and used to generate a JUCE project.
-
- BEGIN_JUCE_PIP_METADATA
-
- name: SimpleFFTDemo
- version: 1.0.0
- vendor: JUCE
- website: http://juce.com
- description: Simple FFT application.
-
- dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
- juce_audio_processors, juce_audio_utils, juce_core,
- juce_data_structures, juce_dsp, juce_events, juce_graphics,
- juce_gui_basics, juce_gui_extra
- exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
-
- moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
-
- type: Component
- mainClass: SimpleFFTDemo
-
- useLocalCopy: 1
-
- END_JUCE_PIP_METADATA
-
- *******************************************************************************/
-
- #pragma once
-
-
- //==============================================================================
- class SimpleFFTDemo final : public AudioAppComponent,
- private Timer
- {
- public:
- SimpleFFTDemo() :
- #ifdef JUCE_DEMO_RUNNER
- AudioAppComponent (getSharedAudioDeviceManager (1, 0)),
- #endif
- forwardFFT (fftOrder),
- spectrogramImage (Image::RGB, 512, 512, true)
- {
- setOpaque (true);
-
- #ifndef JUCE_DEMO_RUNNER
- RuntimePermissions::request (RuntimePermissions::recordAudio,
- [this] (bool granted)
- {
- int numInputChannels = granted ? 2 : 0;
- setAudioChannels (numInputChannels, 2);
- });
- #else
- setAudioChannels (2, 2);
- #endif
-
- startTimerHz (60);
- setSize (700, 500);
- }
-
- ~SimpleFFTDemo() override
- {
- shutdownAudio();
- }
-
- //==============================================================================
- void prepareToPlay (int /*samplesPerBlockExpected*/, double /*newSampleRate*/) override
- {
- // (nothing to do here)
- }
-
- void releaseResources() override
- {
- // (nothing to do here)
- }
-
- void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
- {
- if (bufferToFill.buffer->getNumChannels() > 0)
- {
- const auto* channelData = bufferToFill.buffer->getReadPointer (0, bufferToFill.startSample);
-
- for (auto i = 0; i < bufferToFill.numSamples; ++i)
- pushNextSampleIntoFifo (channelData[i]);
-
- bufferToFill.clearActiveBufferRegion();
- }
- }
-
- //==============================================================================
- 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 ((float) y / (float) imageHeight) * 0.2f);
- auto fftDataIndex = jlimit (0, fftSize / 2, (int) (skewedProportionY * (int) 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 [2 * fftSize];
- int fifoIndex = 0;
- bool nextFFTBlockReady = false;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleFFTDemo)
- };
|