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.

221 lines
6.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. struct AudioVisualiserComponent::ChannelInfo
  18. {
  19. ChannelInfo (AudioVisualiserComponent& o, int bufferSize)
  20. : owner (o), nextSample (0), subSample (0)
  21. {
  22. setBufferSize (bufferSize);
  23. clear();
  24. }
  25. void clear() noexcept
  26. {
  27. for (int i = 0; i < levels.size(); ++i)
  28. levels.getReference(i) = Range<float>();
  29. value = Range<float>();
  30. subSample = 0;
  31. }
  32. void pushSamples (const float* inputSamples, const int num) noexcept
  33. {
  34. for (int i = 0; i < num; ++i)
  35. pushSample (inputSamples[i]);
  36. }
  37. void pushSample (const float newSample) noexcept
  38. {
  39. if (--subSample <= 0)
  40. {
  41. nextSample %= levels.size();
  42. levels.getReference (nextSample++) = value;
  43. subSample = owner.getSamplesPerBlock();
  44. value = Range<float> (newSample, newSample);
  45. }
  46. else
  47. {
  48. value = value.getUnionWith (newSample);
  49. }
  50. }
  51. void setBufferSize (int newSize)
  52. {
  53. levels.removeRange (newSize, levels.size());
  54. levels.insertMultiple (-1, Range<float>(), newSize - levels.size());
  55. if (nextSample >= newSize)
  56. nextSample = 0;
  57. }
  58. AudioVisualiserComponent& owner;
  59. Array<Range<float> > levels;
  60. Range<float> value;
  61. int nextSample, subSample;
  62. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelInfo)
  63. };
  64. //==============================================================================
  65. AudioVisualiserComponent::AudioVisualiserComponent (const int initialNumChannels)
  66. : numSamples (1024),
  67. inputSamplesPerBlock (256),
  68. backgroundColour (Colours::black),
  69. waveformColour (Colours::white)
  70. {
  71. setOpaque (true);
  72. setNumChannels (initialNumChannels);
  73. setRepaintRate (60);
  74. }
  75. AudioVisualiserComponent::~AudioVisualiserComponent()
  76. {
  77. }
  78. void AudioVisualiserComponent::setNumChannels (const int numChannels)
  79. {
  80. channels.clear();
  81. for (int i = 0; i < numChannels; ++i)
  82. channels.add (new ChannelInfo (*this, numSamples));
  83. }
  84. void AudioVisualiserComponent::setBufferSize (int newNumSamples)
  85. {
  86. numSamples = newNumSamples;
  87. for (int i = 0; i < channels.size(); ++i)
  88. channels.getUnchecked(i)->setBufferSize (newNumSamples);
  89. }
  90. void AudioVisualiserComponent::clear()
  91. {
  92. for (int i = 0; i < channels.size(); ++i)
  93. channels.getUnchecked(i)->clear();
  94. }
  95. void AudioVisualiserComponent::pushBuffer (const float** d, int numChannels, int num)
  96. {
  97. numChannels = jmin (numChannels, channels.size());
  98. for (int i = 0; i < numChannels; ++i)
  99. channels.getUnchecked(i)->pushSamples (d[i], num);
  100. }
  101. void AudioVisualiserComponent::pushBuffer (const AudioSampleBuffer& buffer)
  102. {
  103. pushBuffer (buffer.getArrayOfReadPointers(),
  104. buffer.getNumChannels(),
  105. buffer.getNumSamples());
  106. }
  107. void AudioVisualiserComponent::pushBuffer (const AudioSourceChannelInfo& buffer)
  108. {
  109. const int numChannels = jmin (buffer.buffer->getNumChannels(), channels.size());
  110. for (int i = 0; i < numChannels; ++i)
  111. channels.getUnchecked(i)->pushSamples (buffer.buffer->getReadPointer (i, buffer.startSample),
  112. buffer.numSamples);
  113. }
  114. void AudioVisualiserComponent::pushSample (const float* d, int numChannels)
  115. {
  116. numChannels = jmin (numChannels, channels.size());
  117. for (int i = 0; i < numChannels; ++i)
  118. channels.getUnchecked(i)->pushSample (d[i]);
  119. }
  120. void AudioVisualiserComponent::setSamplesPerBlock (int newSamplesPerPixel) noexcept
  121. {
  122. inputSamplesPerBlock = newSamplesPerPixel;
  123. }
  124. void AudioVisualiserComponent::setRepaintRate (int frequencyInHz)
  125. {
  126. startTimerHz (frequencyInHz);
  127. }
  128. void AudioVisualiserComponent::timerCallback()
  129. {
  130. repaint();
  131. }
  132. void AudioVisualiserComponent::setColours (Colour bk, Colour fg) noexcept
  133. {
  134. backgroundColour = bk;
  135. waveformColour = fg;
  136. repaint();
  137. }
  138. void AudioVisualiserComponent::paint (Graphics& g)
  139. {
  140. g.fillAll (backgroundColour);
  141. Rectangle<float> r (getLocalBounds().toFloat());
  142. const float channelHeight = r.getHeight() / channels.size();
  143. g.setColour (waveformColour);
  144. for (int i = 0; i < channels.size(); ++i)
  145. {
  146. const ChannelInfo& c = *channels.getUnchecked(i);
  147. paintChannel (g, r.removeFromTop (channelHeight),
  148. c.levels.begin(), c.levels.size(), c.nextSample);
  149. }
  150. }
  151. void AudioVisualiserComponent::getChannelAsPath (Path& path, const Range<float>* levels, int numLevels, int nextSample)
  152. {
  153. path.preallocateSpace (4 * numLevels + 8);
  154. for (int i = 0; i < numLevels; ++i)
  155. {
  156. const float level = -(levels[(nextSample + i) % numLevels].getEnd());
  157. if (i == 0)
  158. path.startNewSubPath (0.0f, level);
  159. else
  160. path.lineTo ((float) i, level);
  161. }
  162. for (int i = numLevels; --i >= 0;)
  163. path.lineTo ((float) i, -(levels[(nextSample + i) % numLevels].getStart()));
  164. path.closeSubPath();
  165. }
  166. void AudioVisualiserComponent::paintChannel (Graphics& g, Rectangle<float> area,
  167. const Range<float>* levels, int numLevels, int nextSample)
  168. {
  169. Path p;
  170. getChannelAsPath (p, levels, numLevels, nextSample);
  171. g.fillPath (p, AffineTransform::fromTargetPoints (0.0f, -1.0f, area.getX(), area.getY(),
  172. 0.0f, 1.0f, area.getX(), area.getBottom(),
  173. (float) numLevels, -1.0f, area.getRight(), area.getY()));
  174. }