mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added a new class AudioVisualiserComponent, and updated the demo audio pages to use this.
This commit is contained in:
parent
560b314111
commit
57d4889f91
5 changed files with 369 additions and 70 deletions
|
|
@ -32,18 +32,14 @@
|
||||||
/* This component scrolls a continuous waveform showing the audio that's
|
/* This component scrolls a continuous waveform showing the audio that's
|
||||||
coming into whatever audio inputs this object is connected to.
|
coming into whatever audio inputs this object is connected to.
|
||||||
*/
|
*/
|
||||||
class LiveScrollingAudioDisplay : public Component,
|
class LiveScrollingAudioDisplay : public AudioVisualiserComponent,
|
||||||
public AudioIODeviceCallback,
|
public AudioIODeviceCallback
|
||||||
private Timer
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LiveScrollingAudioDisplay()
|
LiveScrollingAudioDisplay() : AudioVisualiserComponent (1)
|
||||||
: nextSample (0), subSample (0), accumulator (0)
|
|
||||||
{
|
{
|
||||||
setOpaque (true);
|
setSamplesPerBlock (256);
|
||||||
clear();
|
setBufferSize (1024);
|
||||||
|
|
||||||
startTimerHz (75); // use a timer to keep repainting this component
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -66,72 +62,18 @@ public:
|
||||||
float inputSample = 0;
|
float inputSample = 0;
|
||||||
|
|
||||||
for (int chan = 0; chan < numInputChannels; ++chan)
|
for (int chan = 0; chan < numInputChannels; ++chan)
|
||||||
if (inputChannelData[chan] != nullptr)
|
if (const float* inputChannel = inputChannelData[chan])
|
||||||
inputSample += std::abs (inputChannelData[chan][i]); // find the sum of all the channels
|
inputSample += inputChannel[i]; // find the sum of all the channels
|
||||||
|
|
||||||
pushSample (10.0f * inputSample); // boost the level to make it more easily visible.
|
inputSample *= 10.0f; // boost the level to make it more easily visible.
|
||||||
|
|
||||||
|
pushSample (&inputSample, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to clear the output buffers before returning, in case they're full of junk..
|
// We need to clear the output buffers before returning, in case they're full of junk..
|
||||||
for (int j = 0; j < numOutputChannels; ++j)
|
for (int j = 0; j < numOutputChannels; ++j)
|
||||||
if (outputChannelData[j] != nullptr)
|
if (float* outputChannel = outputChannelData[j])
|
||||||
zeromem (outputChannelData[j], sizeof (float) * (size_t) numSamples);
|
zeromem (outputChannel, sizeof (float) * (size_t) numSamples);
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float samples[1024];
|
|
||||||
int nextSample, subSample;
|
|
||||||
float accumulator;
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
zeromem (samples, sizeof (samples));
|
|
||||||
accumulator = 0;
|
|
||||||
subSample = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void paint (Graphics& g) override
|
|
||||||
{
|
|
||||||
g.fillAll (Colours::black);
|
|
||||||
|
|
||||||
const float midY = getHeight() * 0.5f;
|
|
||||||
int samplesAgo = (nextSample + numElementsInArray (samples) - 1);
|
|
||||||
|
|
||||||
RectangleList<float> waveform;
|
|
||||||
waveform.ensureStorageAllocated ((int) numElementsInArray (samples));
|
|
||||||
|
|
||||||
for (int x = jmin (getWidth(), (int) numElementsInArray (samples)); --x >= 0;)
|
|
||||||
{
|
|
||||||
const float sampleSize = midY * samples [samplesAgo-- % numElementsInArray (samples)];
|
|
||||||
waveform.addWithoutMerging (Rectangle<float> ((float) x, midY - sampleSize, 1.0f, sampleSize * 2.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setColour (Colours::lightgreen);
|
|
||||||
g.fillRectList (waveform);
|
|
||||||
}
|
|
||||||
|
|
||||||
void timerCallback() override
|
|
||||||
{
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushSample (const float newSample)
|
|
||||||
{
|
|
||||||
accumulator += newSample;
|
|
||||||
|
|
||||||
if (subSample == 0)
|
|
||||||
{
|
|
||||||
const int inputSamplesPerPixel = 200;
|
|
||||||
|
|
||||||
samples[nextSample] = accumulator / inputSamplesPerPixel;
|
|
||||||
nextSample = (nextSample + 1) % numElementsInArray (samples);
|
|
||||||
subSample = inputSamplesPerPixel;
|
|
||||||
accumulator = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
--subSample;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay);
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay);
|
||||||
|
|
|
||||||
220
modules/juce_audio_utils/gui/juce_AudioVisualiserComponent.cpp
Normal file
220
modules/juce_audio_utils/gui/juce_AudioVisualiserComponent.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This file is part of the JUCE library.
|
||||||
|
Copyright (c) 2015 - ROLI Ltd.
|
||||||
|
|
||||||
|
Permission is granted to use this software under the terms of either:
|
||||||
|
a) the GPL v2 (or any later version)
|
||||||
|
b) the Affero GPL v3
|
||||||
|
|
||||||
|
Details of these licenses can be found at: www.gnu.org/licenses
|
||||||
|
|
||||||
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
To release a closed-source product which uses JUCE, commercial licenses are
|
||||||
|
available: visit www.juce.com for more information.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct AudioVisualiserComponent::ChannelInfo
|
||||||
|
{
|
||||||
|
ChannelInfo (AudioVisualiserComponent& o, int bufferSize)
|
||||||
|
: owner (o), nextSample (0), subSample (0)
|
||||||
|
{
|
||||||
|
setBufferSize (bufferSize);
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() noexcept
|
||||||
|
{
|
||||||
|
for (int i = 0; i < levels.size(); ++i)
|
||||||
|
levels.getReference(i) = Range<float>();
|
||||||
|
|
||||||
|
value = Range<float>();
|
||||||
|
subSample = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushSamples (const float* inputSamples, const int num) noexcept
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
pushSample (inputSamples[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushSample (const float newSample) noexcept
|
||||||
|
{
|
||||||
|
if (--subSample <= 0)
|
||||||
|
{
|
||||||
|
nextSample %= levels.size();
|
||||||
|
levels.getReference (nextSample++) = value;
|
||||||
|
subSample = owner.getSamplesPerBlock();
|
||||||
|
value = Range<float> (newSample, newSample);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = value.getUnionWith (newSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBufferSize (int newSize)
|
||||||
|
{
|
||||||
|
levels.removeRange (newSize, levels.size());
|
||||||
|
levels.insertMultiple (-1, Range<float>(), newSize - levels.size());
|
||||||
|
|
||||||
|
if (nextSample >= newSize)
|
||||||
|
nextSample = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioVisualiserComponent& owner;
|
||||||
|
Array<Range<float> > levels;
|
||||||
|
Range<float> value;
|
||||||
|
int nextSample, subSample;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
AudioVisualiserComponent::AudioVisualiserComponent (const int initialNumChannels)
|
||||||
|
: numSamples (1024),
|
||||||
|
inputSamplesPerBlock (256),
|
||||||
|
backgroundColour (Colours::black),
|
||||||
|
waveformColour (Colours::white)
|
||||||
|
{
|
||||||
|
setOpaque (true);
|
||||||
|
setNumChannels (initialNumChannels);
|
||||||
|
setRepaintRate (60);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioVisualiserComponent::~AudioVisualiserComponent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::setNumChannels (const int numChannels)
|
||||||
|
{
|
||||||
|
channels.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < numChannels; ++i)
|
||||||
|
channels.add (new ChannelInfo (*this, numSamples));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::setBufferSize (int newNumSamples)
|
||||||
|
{
|
||||||
|
numSamples = newNumSamples;
|
||||||
|
|
||||||
|
for (int i = 0; i < channels.size(); ++i)
|
||||||
|
channels.getUnchecked(i)->setBufferSize (newNumSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < channels.size(); ++i)
|
||||||
|
channels.getUnchecked(i)->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::pushBuffer (const float** d, int numChannels, int num)
|
||||||
|
{
|
||||||
|
numChannels = jmin (numChannels, channels.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < numChannels; ++i)
|
||||||
|
channels.getUnchecked(i)->pushSamples (d[i], num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::pushBuffer (const AudioSampleBuffer& buffer)
|
||||||
|
{
|
||||||
|
pushBuffer (buffer.getArrayOfReadPointers(),
|
||||||
|
buffer.getNumChannels(),
|
||||||
|
buffer.getNumSamples());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::pushBuffer (const AudioSourceChannelInfo& buffer)
|
||||||
|
{
|
||||||
|
const int numChannels = jmin (buffer.buffer->getNumChannels(), channels.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < numChannels; ++i)
|
||||||
|
channels.getUnchecked(i)->pushSamples (buffer.buffer->getReadPointer (i, buffer.startSample),
|
||||||
|
buffer.numSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::pushSample (const float* d, int numChannels)
|
||||||
|
{
|
||||||
|
numChannels = jmin (numChannels, channels.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < numChannels; ++i)
|
||||||
|
channels.getUnchecked(i)->pushSample (d[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::setSamplesPerBlock (int newSamplesPerPixel) noexcept
|
||||||
|
{
|
||||||
|
inputSamplesPerBlock = newSamplesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::setRepaintRate (int frequencyInHz)
|
||||||
|
{
|
||||||
|
startTimerHz (frequencyInHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::timerCallback()
|
||||||
|
{
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::setColours (Colour bk, Colour fg) noexcept
|
||||||
|
{
|
||||||
|
backgroundColour = bk;
|
||||||
|
waveformColour = fg;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::paint (Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll (backgroundColour);
|
||||||
|
|
||||||
|
Rectangle<float> r (getLocalBounds().toFloat());
|
||||||
|
const float channelHeight = r.getHeight() / channels.size();
|
||||||
|
|
||||||
|
g.setColour (waveformColour);
|
||||||
|
|
||||||
|
for (int i = 0; i < channels.size(); ++i)
|
||||||
|
{
|
||||||
|
const ChannelInfo& c = *channels.getUnchecked(i);
|
||||||
|
|
||||||
|
paintChannel (g, r.removeFromTop (channelHeight),
|
||||||
|
c.levels.begin(), c.levels.size(), c.nextSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::getChannelAsPath (Path& path, const Range<float>* levels, int numLevels, int nextSample)
|
||||||
|
{
|
||||||
|
path.preallocateSpace (4 * numLevels + 8);
|
||||||
|
|
||||||
|
for (int i = 0; i < numLevels; ++i)
|
||||||
|
{
|
||||||
|
const float level = -(levels[(nextSample + i) % numLevels].getEnd());
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
path.startNewSubPath (0.0f, level);
|
||||||
|
else
|
||||||
|
path.lineTo ((float) i, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = numLevels; --i >= 0;)
|
||||||
|
path.lineTo ((float) i, -(levels[(nextSample + i) % numLevels].getStart()));
|
||||||
|
|
||||||
|
path.closeSubPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioVisualiserComponent::paintChannel (Graphics& g, Rectangle<float> bounds,
|
||||||
|
const Range<float>* levels, int numLevels, int nextSample)
|
||||||
|
{
|
||||||
|
Path p;
|
||||||
|
getChannelAsPath (p, levels, numLevels, nextSample);
|
||||||
|
|
||||||
|
g.fillPath (p, AffineTransform::fromTargetPoints (0.0f, -1.0f, bounds.getX(), bounds.getY(),
|
||||||
|
0.0f, 1.0f, bounds.getX(), bounds.getBottom(),
|
||||||
|
numLevels, -1.0f, bounds.getRight(), bounds.getY()));
|
||||||
|
}
|
||||||
135
modules/juce_audio_utils/gui/juce_AudioVisualiserComponent.h
Normal file
135
modules/juce_audio_utils/gui/juce_AudioVisualiserComponent.h
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This file is part of the JUCE library.
|
||||||
|
Copyright (c) 2015 - ROLI Ltd.
|
||||||
|
|
||||||
|
Permission is granted to use this software under the terms of either:
|
||||||
|
a) the GPL v2 (or any later version)
|
||||||
|
b) the Affero GPL v3
|
||||||
|
|
||||||
|
Details of these licenses can be found at: www.gnu.org/licenses
|
||||||
|
|
||||||
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
To release a closed-source product which uses JUCE, commercial licenses are
|
||||||
|
available: visit www.juce.com for more information.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JUCE_AUDIOVISUALISER_H_INCLUDED
|
||||||
|
#define JUCE_AUDIOVISUALISER_H_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
A simple component that can be used to show a scrolling waveform of audio data.
|
||||||
|
|
||||||
|
This is a handy way to get a quick visualisation of some audio data. Just create
|
||||||
|
one of these, set its size and oversampling rate, and then feed it with incoming
|
||||||
|
data by calling one of its pushBuffer() or pushSample() methods.
|
||||||
|
|
||||||
|
You can override its paint method for more customised views, but it's only designed
|
||||||
|
as a quick-and-dirty class for simple tasks, so please don't send us feature requests
|
||||||
|
for fancy additional features that you'd like it to support! If you're building a
|
||||||
|
real-world app that requires more powerful waveform display, you'll probably want to
|
||||||
|
create your own component instead.
|
||||||
|
*/
|
||||||
|
class AudioVisualiserComponent : public Component,
|
||||||
|
private Timer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Creates a visualiser with the given number of channels. */
|
||||||
|
AudioVisualiserComponent (int initialNumChannels);
|
||||||
|
|
||||||
|
/** Destructor. */
|
||||||
|
~AudioVisualiserComponent();
|
||||||
|
|
||||||
|
/** Changes the number of channels that the visualiser stores. */
|
||||||
|
void setNumChannels (int numChannels);
|
||||||
|
|
||||||
|
/** Changes the number of samples that the visualiser keeps in its history.
|
||||||
|
Note that this value refers to the number of averaged sample blocks, and each
|
||||||
|
block is calculated as the peak of a number of incoming audio samples. To set
|
||||||
|
the number of incoming samples per block, use setSamplesPerBlock().
|
||||||
|
*/
|
||||||
|
void setBufferSize (int bufferSize);
|
||||||
|
|
||||||
|
/** */
|
||||||
|
void setSamplesPerBlock (int newNumInputSamplesPerBlock) noexcept;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
int getSamplesPerBlock() const noexcept { return inputSamplesPerBlock; }
|
||||||
|
|
||||||
|
/** Clears the contents of the buffers. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/** Pushes a buffer of channels data.
|
||||||
|
The number of channels provided here is expected to match the number of channels
|
||||||
|
that this AudioVisualiserComponent has been told to use.
|
||||||
|
*/
|
||||||
|
void pushBuffer (const AudioSampleBuffer& bufferToPush);
|
||||||
|
|
||||||
|
/** Pushes a buffer of channels data.
|
||||||
|
The number of channels provided here is expected to match the number of channels
|
||||||
|
that this AudioVisualiserComponent has been told to use.
|
||||||
|
*/
|
||||||
|
void pushBuffer (const AudioSourceChannelInfo& bufferToPush);
|
||||||
|
|
||||||
|
/** Pushes a buffer of channels data.
|
||||||
|
The number of channels provided here is expected to match the number of channels
|
||||||
|
that this AudioVisualiserComponent has been told to use.
|
||||||
|
*/
|
||||||
|
void pushBuffer (const float** channelData, int numChannels, int numSamples);
|
||||||
|
|
||||||
|
/** Pushes a single sample (per channel).
|
||||||
|
The number of channels provided here is expected to match the number of channels
|
||||||
|
that this AudioVisualiserComponent has been told to use.
|
||||||
|
*/
|
||||||
|
void pushSample (const float* samplesForEachChannel, int numChannels);
|
||||||
|
|
||||||
|
/** Sets the colours used to paint the */
|
||||||
|
void setColours (Colour backgroundColour, Colour waveformColour) noexcept;
|
||||||
|
|
||||||
|
/** Sets the frequency at which the component repaints itself. */
|
||||||
|
void setRepaintRate (int frequencyInHz);
|
||||||
|
|
||||||
|
/** Draws a channel of audio data in the given bounds.
|
||||||
|
The default implementation just calls getChannelAsPath() and fits this into the given
|
||||||
|
area. You may want to override this to draw things differently.
|
||||||
|
*/
|
||||||
|
virtual void paintChannel (Graphics&, Rectangle<float> bounds,
|
||||||
|
const Range<float>* levels, int numLevels, int nextSample);
|
||||||
|
|
||||||
|
/** Creates a path which contains the waveform shape of a given set of range data.
|
||||||
|
The path is normalised so that -1 and +1 are its upper and lower bounds, and it
|
||||||
|
goes from 0 to numLevels on the X axis.
|
||||||
|
*/
|
||||||
|
void getChannelAsPath (Path& result, const Range<float>* levels, int numLevels, int nextSample);
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
/** @internal */
|
||||||
|
void paint (Graphics&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ChannelInfo;
|
||||||
|
friend struct ChannelInfo;
|
||||||
|
friend struct ContainerDeletePolicy<ChannelInfo>;
|
||||||
|
|
||||||
|
OwnedArray<ChannelInfo> channels;
|
||||||
|
int numSamples, inputSamplesPerBlock;
|
||||||
|
float interpolation;
|
||||||
|
Colour backgroundColour, waveformColour;
|
||||||
|
|
||||||
|
void timerCallback() override;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioVisualiserComponent);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // JUCE_AUDIOVISUALISER_H_INCLUDED
|
||||||
|
|
@ -44,6 +44,7 @@ namespace juce
|
||||||
#include "gui/juce_AudioDeviceSelectorComponent.cpp"
|
#include "gui/juce_AudioDeviceSelectorComponent.cpp"
|
||||||
#include "gui/juce_AudioThumbnail.cpp"
|
#include "gui/juce_AudioThumbnail.cpp"
|
||||||
#include "gui/juce_AudioThumbnailCache.cpp"
|
#include "gui/juce_AudioThumbnailCache.cpp"
|
||||||
|
#include "gui/juce_AudioVisualiserComponent.cpp"
|
||||||
#include "gui/juce_MidiKeyboardComponent.cpp"
|
#include "gui/juce_MidiKeyboardComponent.cpp"
|
||||||
#include "gui/juce_AudioAppComponent.cpp"
|
#include "gui/juce_AudioAppComponent.cpp"
|
||||||
#include "players/juce_AudioProcessorPlayer.cpp"
|
#include "players/juce_AudioProcessorPlayer.cpp"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ namespace juce
|
||||||
#include "gui/juce_AudioThumbnailBase.h"
|
#include "gui/juce_AudioThumbnailBase.h"
|
||||||
#include "gui/juce_AudioThumbnail.h"
|
#include "gui/juce_AudioThumbnail.h"
|
||||||
#include "gui/juce_AudioThumbnailCache.h"
|
#include "gui/juce_AudioThumbnailCache.h"
|
||||||
|
#include "gui/juce_AudioVisualiserComponent.h"
|
||||||
#include "gui/juce_MidiKeyboardComponent.h"
|
#include "gui/juce_MidiKeyboardComponent.h"
|
||||||
#include "gui/juce_AudioAppComponent.h"
|
#include "gui/juce_AudioAppComponent.h"
|
||||||
#include "players/juce_AudioProcessorPlayer.h"
|
#include "players/juce_AudioProcessorPlayer.h"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue