| @@ -1,171 +0,0 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE examples. | |||
| Copyright (c) 2017 - ROLI Ltd. | |||
| 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, vs2017 | |||
| type: Component | |||
| mainClass: SimpleFFTDemo | |||
| useLocalCopy: 1 | |||
| END_JUCE_PIP_METADATA | |||
| *******************************************************************************/ | |||
| #pragma once | |||
| //============================================================================== | |||
| class SimpleFFTDemo : public AudioAppComponent, | |||
| private Timer | |||
| { | |||
| public: | |||
| SimpleFFTDemo() | |||
| : forwardFFT (fftOrder), | |||
| spectrogramImage (Image::RGB, 512, 512, true) | |||
| { | |||
| setOpaque (true); | |||
| setAudioChannels (2, 0); // we want a couple of input channels but no outputs | |||
| startTimerHz (60); | |||
| setSize (700, 500); | |||
| } | |||
| ~SimpleFFTDemo() | |||
| { | |||
| 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) | |||
| { | |||
| auto* channelData = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); | |||
| for (auto i = 0; i < bufferToFill.numSamples; ++i) | |||
| pushNextSampleIntoFifo (channelData[i]); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| 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 [2 * fftSize]; | |||
| int fifoIndex = 0; | |||
| bool nextFFTBlockReady = false; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleFFTDemo) | |||
| }; | |||