/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { 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(); value = Range(); 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 (newSample, newSample); } else { value = value.getUnionWith (newSample); } } void setBufferSize (int newSize) { levels.removeRange (newSize, levels.size()); levels.insertMultiple (-1, Range(), newSize - levels.size()); if (nextSample >= newSize) nextSample = 0; } AudioVisualiserComponent& owner; Array> levels; Range 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 AudioBuffer& 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 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* 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 area, const Range* levels, int numLevels, int nextSample) { Path p; getChannelAsPath (p, levels, numLevels, nextSample); g.fillPath (p, AffineTransform::fromTargetPoints (0.0f, -1.0f, area.getX(), area.getY(), 0.0f, 1.0f, area.getX(), area.getBottom(), (float) numLevels, -1.0f, area.getRight(), area.getY())); } } // namespace juce