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.

227 lines
6.5KB

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