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.

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