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.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. struct AudioVisualiserComponent::ChannelInfo
  21. {
  22. ChannelInfo (AudioVisualiserComponent& o, int bufferSize) : owner (o)
  23. {
  24. setBufferSize (bufferSize);
  25. clear();
  26. }
  27. void clear() noexcept
  28. {
  29. levels.fill ({});
  30. value = {};
  31. subSample = 0;
  32. }
  33. void pushSamples (const float* inputSamples, int num) noexcept
  34. {
  35. for (int i = 0; i < num; ++i)
  36. pushSample (inputSamples[i]);
  37. }
  38. void pushSample (float newSample) noexcept
  39. {
  40. if (--subSample <= 0)
  41. {
  42. if (++nextSample == levels.size())
  43. nextSample = 0;
  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, {}, newSize - levels.size());
  57. if (nextSample >= newSize)
  58. nextSample = 0;
  59. }
  60. AudioVisualiserComponent& owner;
  61. Array<Range<float>> levels;
  62. Range<float> value;
  63. std::atomic<int> nextSample { 0 }, subSample { 0 };
  64. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelInfo)
  65. };
  66. //==============================================================================
  67. AudioVisualiserComponent::AudioVisualiserComponent (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 (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 (auto* c : channels)
  90. c->setBufferSize (newNumSamples);
  91. }
  92. void AudioVisualiserComponent::clear()
  93. {
  94. for (auto* c : channels)
  95. c->clear();
  96. }
  97. void AudioVisualiserComponent::pushBuffer (const float* const* 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 AudioBuffer<float>& buffer)
  104. {
  105. pushBuffer (buffer.getArrayOfReadPointers(),
  106. buffer.getNumChannels(),
  107. buffer.getNumSamples());
  108. }
  109. void AudioVisualiserComponent::pushBuffer (const AudioSourceChannelInfo& buffer)
  110. {
  111. auto 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. auto r = getLocalBounds().toFloat();
  144. auto channelHeight = r.getHeight() / (float) channels.size();
  145. g.setColour (waveformColour);
  146. for (auto* c : channels)
  147. paintChannel (g, r.removeFromTop (channelHeight),
  148. c->levels.begin(), c->levels.size(), c->nextSample);
  149. }
  150. void AudioVisualiserComponent::getChannelAsPath (Path& path, const Range<float>* levels,
  151. int numLevels, int nextSample)
  152. {
  153. path.preallocateSpace (4 * numLevels + 8);
  154. for (int i = 0; i < numLevels; ++i)
  155. {
  156. auto 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. }
  175. } // namespace juce