The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
6.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. struct AudioVisualiserComponent::ChannelInfo
  22. {
  23. ChannelInfo (AudioVisualiserComponent& o, int bufferSize) : owner (o)
  24. {
  25. setBufferSize (bufferSize);
  26. clear();
  27. }
  28. void clear() noexcept
  29. {
  30. levels.fill ({});
  31. value = {};
  32. subSample = 0;
  33. }
  34. void pushSamples (const float* inputSamples, int num) noexcept
  35. {
  36. for (int i = 0; i < num; ++i)
  37. pushSample (inputSamples[i]);
  38. }
  39. void pushSample (float newSample) noexcept
  40. {
  41. if (--subSample <= 0)
  42. {
  43. if (++nextSample == levels.size())
  44. nextSample = 0;
  45. levels.getReference (nextSample) = value;
  46. subSample = owner.getSamplesPerBlock();
  47. value = Range<float> (newSample, newSample);
  48. }
  49. else
  50. {
  51. value = value.getUnionWith (newSample);
  52. }
  53. }
  54. void setBufferSize (int newSize)
  55. {
  56. levels.removeRange (newSize, levels.size());
  57. levels.insertMultiple (-1, {}, newSize - levels.size());
  58. if (nextSample >= newSize)
  59. nextSample = 0;
  60. }
  61. AudioVisualiserComponent& owner;
  62. Array<Range<float>> levels;
  63. Range<float> value;
  64. std::atomic<int> nextSample { 0 }, subSample { 0 };
  65. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelInfo)
  66. };
  67. //==============================================================================
  68. AudioVisualiserComponent::AudioVisualiserComponent (int initialNumChannels)
  69. : numSamples (1024),
  70. inputSamplesPerBlock (256),
  71. backgroundColour (Colours::black),
  72. waveformColour (Colours::white)
  73. {
  74. setOpaque (true);
  75. setNumChannels (initialNumChannels);
  76. setRepaintRate (60);
  77. }
  78. AudioVisualiserComponent::~AudioVisualiserComponent()
  79. {
  80. }
  81. void AudioVisualiserComponent::setNumChannels (int numChannels)
  82. {
  83. channels.clear();
  84. for (int i = 0; i < numChannels; ++i)
  85. channels.add (new ChannelInfo (*this, numSamples));
  86. }
  87. void AudioVisualiserComponent::setBufferSize (int newNumSamples)
  88. {
  89. numSamples = newNumSamples;
  90. for (auto* c : channels)
  91. c->setBufferSize (newNumSamples);
  92. }
  93. void AudioVisualiserComponent::clear()
  94. {
  95. for (auto* c : channels)
  96. c->clear();
  97. }
  98. void AudioVisualiserComponent::pushBuffer (const float** d, int numChannels, int num)
  99. {
  100. numChannels = jmin (numChannels, channels.size());
  101. for (int i = 0; i < numChannels; ++i)
  102. channels.getUnchecked(i)->pushSamples (d[i], num);
  103. }
  104. void AudioVisualiserComponent::pushBuffer (const AudioBuffer<float>& buffer)
  105. {
  106. pushBuffer (buffer.getArrayOfReadPointers(),
  107. buffer.getNumChannels(),
  108. buffer.getNumSamples());
  109. }
  110. void AudioVisualiserComponent::pushBuffer (const AudioSourceChannelInfo& buffer)
  111. {
  112. auto numChannels = jmin (buffer.buffer->getNumChannels(), channels.size());
  113. for (int i = 0; i < numChannels; ++i)
  114. channels.getUnchecked(i)->pushSamples (buffer.buffer->getReadPointer (i, buffer.startSample),
  115. buffer.numSamples);
  116. }
  117. void AudioVisualiserComponent::pushSample (const float* d, int numChannels)
  118. {
  119. numChannels = jmin (numChannels, channels.size());
  120. for (int i = 0; i < numChannels; ++i)
  121. channels.getUnchecked(i)->pushSample (d[i]);
  122. }
  123. void AudioVisualiserComponent::setSamplesPerBlock (int newSamplesPerPixel) noexcept
  124. {
  125. inputSamplesPerBlock = newSamplesPerPixel;
  126. }
  127. void AudioVisualiserComponent::setRepaintRate (int frequencyInHz)
  128. {
  129. startTimerHz (frequencyInHz);
  130. }
  131. void AudioVisualiserComponent::timerCallback()
  132. {
  133. repaint();
  134. }
  135. void AudioVisualiserComponent::setColours (Colour bk, Colour fg) noexcept
  136. {
  137. backgroundColour = bk;
  138. waveformColour = fg;
  139. repaint();
  140. }
  141. void AudioVisualiserComponent::paint (Graphics& g)
  142. {
  143. g.fillAll (backgroundColour);
  144. auto r = getLocalBounds().toFloat();
  145. auto channelHeight = r.getHeight() / channels.size();
  146. g.setColour (waveformColour);
  147. for (auto* c : channels)
  148. paintChannel (g, r.removeFromTop (channelHeight),
  149. c->levels.begin(), c->levels.size(), c->nextSample);
  150. }
  151. void AudioVisualiserComponent::getChannelAsPath (Path& path, const Range<float>* levels,
  152. int numLevels, int nextSample)
  153. {
  154. path.preallocateSpace (4 * numLevels + 8);
  155. for (int i = 0; i < numLevels; ++i)
  156. {
  157. auto level = -(levels[(nextSample + i) % numLevels].getEnd());
  158. if (i == 0)
  159. path.startNewSubPath (0.0f, level);
  160. else
  161. path.lineTo ((float) i, level);
  162. }
  163. for (int i = numLevels; --i >= 0;)
  164. path.lineTo ((float) i, -(levels[(nextSample + i) % numLevels].getStart()));
  165. path.closeSubPath();
  166. }
  167. void AudioVisualiserComponent::paintChannel (Graphics& g, Rectangle<float> area,
  168. const Range<float>* levels, int numLevels, int nextSample)
  169. {
  170. Path p;
  171. getChannelAsPath (p, levels, numLevels, nextSample);
  172. g.fillPath (p, AffineTransform::fromTargetPoints (0.0f, -1.0f, area.getX(), area.getY(),
  173. 0.0f, 1.0f, area.getX(), area.getBottom(),
  174. (float) numLevels, -1.0f, area.getRight(), area.getY()));
  175. }
  176. } // namespace juce