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.

223 lines
6.6KB

  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. struct AudioVisualiserComponent::ChannelInfo
  20. {
  21. ChannelInfo (AudioVisualiserComponent& o, int bufferSize)
  22. : owner (o), nextSample (0), subSample (0)
  23. {
  24. setBufferSize (bufferSize);
  25. clear();
  26. }
  27. void clear() noexcept
  28. {
  29. for (int i = 0; i < levels.size(); ++i)
  30. levels.getReference(i) = Range<float>();
  31. value = Range<float>();
  32. subSample = 0;
  33. }
  34. void pushSamples (const float* inputSamples, const int num) noexcept
  35. {
  36. for (int i = 0; i < num; ++i)
  37. pushSample (inputSamples[i]);
  38. }
  39. void pushSample (const float newSample) noexcept
  40. {
  41. if (--subSample <= 0)
  42. {
  43. nextSample %= levels.size();
  44. levels.getReference (nextSample++) = value;
  45. subSample = owner.getSamplesPerBlock();
  46. value = Range<float> (newSample, newSample);
  47. }
  48. else
  49. {
  50. value = value.getUnionWith (newSample);
  51. }
  52. }
  53. void setBufferSize (int newSize)
  54. {
  55. levels.removeRange (newSize, levels.size());
  56. levels.insertMultiple (-1, Range<float>(), newSize - levels.size());
  57. if (nextSample >= newSize)
  58. nextSample = 0;
  59. }
  60. AudioVisualiserComponent& owner;
  61. Array<Range<float> > levels;
  62. Range<float> value;
  63. int nextSample, subSample;
  64. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelInfo)
  65. };
  66. //==============================================================================
  67. AudioVisualiserComponent::AudioVisualiserComponent (const int initialNumChannels)
  68. : numSamples (1024),
  69. inputSamplesPerBlock (256),
  70. backgroundColour (Colours::black),
  71. waveformColour (Colours::white)
  72. {
  73. setOpaque (true);
  74. setNumChannels (initialNumChannels);
  75. setRepaintRate (60);
  76. }
  77. AudioVisualiserComponent::~AudioVisualiserComponent()
  78. {
  79. }
  80. void AudioVisualiserComponent::setNumChannels (const int numChannels)
  81. {
  82. channels.clear();
  83. for (int i = 0; i < numChannels; ++i)
  84. channels.add (new ChannelInfo (*this, numSamples));
  85. }
  86. void AudioVisualiserComponent::setBufferSize (int newNumSamples)
  87. {
  88. numSamples = newNumSamples;
  89. for (int i = 0; i < channels.size(); ++i)
  90. channels.getUnchecked(i)->setBufferSize (newNumSamples);
  91. }
  92. void AudioVisualiserComponent::clear()
  93. {
  94. for (int i = 0; i < channels.size(); ++i)
  95. channels.getUnchecked(i)->clear();
  96. }
  97. void AudioVisualiserComponent::pushBuffer (const float** d, int numChannels, int num)
  98. {
  99. numChannels = jmin (numChannels, channels.size());
  100. for (int i = 0; i < numChannels; ++i)
  101. channels.getUnchecked(i)->pushSamples (d[i], num);
  102. }
  103. void AudioVisualiserComponent::pushBuffer (const AudioSampleBuffer& buffer)
  104. {
  105. pushBuffer (buffer.getArrayOfReadPointers(),
  106. buffer.getNumChannels(),
  107. buffer.getNumSamples());
  108. }
  109. void AudioVisualiserComponent::pushBuffer (const AudioSourceChannelInfo& buffer)
  110. {
  111. const int numChannels = jmin (buffer.buffer->getNumChannels(), channels.size());
  112. for (int i = 0; i < numChannels; ++i)
  113. channels.getUnchecked(i)->pushSamples (buffer.buffer->getReadPointer (i, buffer.startSample),
  114. buffer.numSamples);
  115. }
  116. void AudioVisualiserComponent::pushSample (const float* d, int numChannels)
  117. {
  118. numChannels = jmin (numChannels, channels.size());
  119. for (int i = 0; i < numChannels; ++i)
  120. channels.getUnchecked(i)->pushSample (d[i]);
  121. }
  122. void AudioVisualiserComponent::setSamplesPerBlock (int newSamplesPerPixel) noexcept
  123. {
  124. inputSamplesPerBlock = newSamplesPerPixel;
  125. }
  126. void AudioVisualiserComponent::setRepaintRate (int frequencyInHz)
  127. {
  128. startTimerHz (frequencyInHz);
  129. }
  130. void AudioVisualiserComponent::timerCallback()
  131. {
  132. repaint();
  133. }
  134. void AudioVisualiserComponent::setColours (Colour bk, Colour fg) noexcept
  135. {
  136. backgroundColour = bk;
  137. waveformColour = fg;
  138. repaint();
  139. }
  140. void AudioVisualiserComponent::paint (Graphics& g)
  141. {
  142. g.fillAll (backgroundColour);
  143. Rectangle<float> r (getLocalBounds().toFloat());
  144. const float channelHeight = r.getHeight() / channels.size();
  145. g.setColour (waveformColour);
  146. for (int i = 0; i < channels.size(); ++i)
  147. {
  148. const ChannelInfo& c = *channels.getUnchecked(i);
  149. paintChannel (g, r.removeFromTop (channelHeight),
  150. c.levels.begin(), c.levels.size(), c.nextSample);
  151. }
  152. }
  153. void AudioVisualiserComponent::getChannelAsPath (Path& path, const Range<float>* levels, int numLevels, int nextSample)
  154. {
  155. path.preallocateSpace (4 * numLevels + 8);
  156. for (int i = 0; i < numLevels; ++i)
  157. {
  158. const float level = -(levels[(nextSample + i) % numLevels].getEnd());
  159. if (i == 0)
  160. path.startNewSubPath (0.0f, level);
  161. else
  162. path.lineTo ((float) i, level);
  163. }
  164. for (int i = numLevels; --i >= 0;)
  165. path.lineTo ((float) i, -(levels[(nextSample + i) % numLevels].getStart()));
  166. path.closeSubPath();
  167. }
  168. void AudioVisualiserComponent::paintChannel (Graphics& g, Rectangle<float> area,
  169. const Range<float>* levels, int numLevels, int nextSample)
  170. {
  171. Path p;
  172. getChannelAsPath (p, levels, numLevels, nextSample);
  173. g.fillPath (p, AffineTransform::fromTargetPoints (0.0f, -1.0f, area.getX(), area.getY(),
  174. 0.0f, 1.0f, area.getX(), area.getBottom(),
  175. (float) numLevels, -1.0f, area.getRight(), area.getY()));
  176. }