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.

278 lines
11KB

  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. #pragma once
  19. #include <JuceHeader.h>
  20. #include <mutex>
  21. //==============================================================================
  22. class MainContentComponent : public AudioAppComponent,
  23. private Timer
  24. {
  25. public:
  26. //==============================================================================
  27. MainContentComponent()
  28. {
  29. setSize (400, 400);
  30. setAudioChannels (0, 2);
  31. initGui();
  32. Desktop::getInstance().setScreenSaverEnabled (false);
  33. startTimer (1000);
  34. }
  35. ~MainContentComponent() override
  36. {
  37. shutdownAudio();
  38. }
  39. //==============================================================================
  40. void prepareToPlay (int bufferSize, double sampleRate) override
  41. {
  42. currentSampleRate = sampleRate;
  43. allocateBuffers (static_cast<size_t> (bufferSize));
  44. printHeader();
  45. }
  46. void releaseResources() override
  47. {
  48. a.clear();
  49. b.clear();
  50. c.clear();
  51. currentSampleRate = 0.0;
  52. }
  53. //==============================================================================
  54. void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
  55. {
  56. const double startTimeMs = getPreciseTimeMs();
  57. AudioBuffer<float>& outputAudio = *bufferToFill.buffer;
  58. std::size_t bufferSize = (std::size_t) outputAudio.getNumSamples();
  59. initialiseBuffers (bufferToFill, bufferSize);
  60. for (int ch = 0; ch < outputAudio.getNumChannels(); ++ch)
  61. crunchSomeNumbers (outputAudio.getWritePointer (ch), bufferSize, numLoopIterationsPerCallback);
  62. std::lock_guard<std::mutex> lock (metricMutex);
  63. double endTimeMs = getPreciseTimeMs();
  64. addCallbackMetrics (startTimeMs, endTimeMs);
  65. }
  66. //==============================================================================
  67. void addCallbackMetrics (double startTimeMs, double endTimeMs)
  68. {
  69. double runtimeMs = endTimeMs - startTimeMs;
  70. audioCallbackRuntimeMs.addValue (runtimeMs);
  71. if (runtimeMs > getPhysicalTimeLimitMs())
  72. numCallbacksOverPhysicalTimeLimit++;
  73. if (lastCallbackStartTimeMs > 0.0)
  74. {
  75. double gapMs = startTimeMs - lastCallbackStartTimeMs;
  76. audioCallbackGapMs.addValue (gapMs);
  77. if (gapMs > 1.5 * getPhysicalTimeLimitMs())
  78. numLateCallbacks++;
  79. }
  80. lastCallbackStartTimeMs = startTimeMs;
  81. }
  82. //==============================================================================
  83. void paint (Graphics& g) override
  84. {
  85. g.fillAll (Colours::black);
  86. g.setFont (Font (16.0f));
  87. g.setColour (Colours::white);
  88. g.drawText ("loop iterations / audio callback",
  89. getLocalBounds().withY (loopIterationsSlider.getHeight()), Justification::centred, true);
  90. }
  91. //==============================================================================
  92. void resized() override
  93. {
  94. loopIterationsSlider.setBounds (getLocalBounds().withSizeKeepingCentre (proportionOfWidth (0.9f), 50));
  95. }
  96. private:
  97. //==============================================================================
  98. void initGui()
  99. {
  100. loopIterationsSlider.setSliderStyle (Slider::LinearBar);
  101. loopIterationsSlider.setRange (0, 30000, 250);
  102. loopIterationsSlider.setValue (15000);
  103. loopIterationsSlider.setColour (Slider::thumbColourId, Colours::white);
  104. loopIterationsSlider.setColour (Slider::textBoxTextColourId, Colours::grey);
  105. updateNumLoopIterationsPerCallback();
  106. addAndMakeVisible (loopIterationsSlider);
  107. }
  108. //==============================================================================
  109. void allocateBuffers (std::size_t bufferSize)
  110. {
  111. a.resize (bufferSize);
  112. b.resize (bufferSize);
  113. c.resize (bufferSize);
  114. }
  115. //==============================================================================
  116. void initialiseBuffers (const AudioSourceChannelInfo& bufferToFill, std::size_t bufferSize)
  117. {
  118. if (bufferSize != a.size())
  119. {
  120. jassertfalse;
  121. Logger::writeToLog ("WARNING: Unexpected buffer size received."
  122. "expected: " + String (a.size()) +
  123. ", actual: " + String (bufferSize));
  124. if (bufferSize > a.size())
  125. Logger::writeToLog ("WARNING: Need to allocate larger buffers on audio thread!");
  126. allocateBuffers (bufferSize);
  127. }
  128. bufferToFill.clearActiveBufferRegion();
  129. std::fill (a.begin(), a.end(), 0.09f);
  130. std::fill (b.begin(), b.end(), 0.1f );
  131. std::fill (c.begin(), c.end(), 0.11f);
  132. }
  133. //==============================================================================
  134. void crunchSomeNumbers (float* outBuffer, std::size_t bufferSize, int numIterations) noexcept
  135. {
  136. jassert (a.size() == bufferSize && b.size() == bufferSize && c.size() == bufferSize);
  137. for (int i = 0; i < numIterations; ++i)
  138. {
  139. FloatVectorOperations::multiply (c.data(), a.data(), b.data(), (int) bufferSize);
  140. FloatVectorOperations::addWithMultiply (outBuffer, b.data(), c.data(), (int) bufferSize);
  141. }
  142. }
  143. //==============================================================================
  144. void timerCallback() override
  145. {
  146. printAndResetPerformanceMetrics();
  147. }
  148. //==============================================================================
  149. void printHeader() const
  150. {
  151. Logger::writeToLog ("buffer size = " + String (a.size()) + " samples");
  152. Logger::writeToLog ("sample rate = " + String (currentSampleRate) + " Hz");
  153. Logger::writeToLog ("physical time limit / callback = " + String (getPhysicalTimeLimitMs() )+ " ms");
  154. Logger::writeToLog ("");
  155. Logger::writeToLog (" | callback exec time / physLimit | callback time gap / physLimit | callback counters ");
  156. Logger::writeToLog ("numLoops | avg min max stddev | avg min max stddev | called late >limit ");
  157. Logger::writeToLog ("----- | ----- ----- ----- ----- | ----- ----- ----- ----- | --- --- --- ");
  158. }
  159. //==============================================================================
  160. void printAndResetPerformanceMetrics()
  161. {
  162. std::unique_lock<std::mutex> lock (metricMutex);
  163. auto runtimeMetric = audioCallbackRuntimeMs;
  164. auto gapMetric = audioCallbackGapMs;
  165. auto late = numLateCallbacks;
  166. auto overLimit = numCallbacksOverPhysicalTimeLimit;
  167. resetPerformanceMetrics();
  168. updateNumLoopIterationsPerCallback();
  169. lock.unlock();
  170. Logger::writeToLog (String (numLoopIterationsPerCallback).paddedRight (' ', 8) + " | "
  171. + getPercentFormattedMetricString (runtimeMetric) + " | "
  172. + getPercentFormattedMetricString (gapMetric) + " | "
  173. + String (runtimeMetric.getCount()).paddedRight (' ', 8)
  174. + String (late).paddedRight (' ', 8)
  175. + String (overLimit).paddedRight (' ', 8) + " | ");
  176. }
  177. //==============================================================================
  178. String getPercentFormattedMetricString (const StatisticsAccumulator<double> metric) const
  179. {
  180. auto physTimeLimit = getPhysicalTimeLimitMs();
  181. return (String (100.0 * metric.getAverage() / physTimeLimit, 1) + "%").paddedRight (' ', 8)
  182. + (String (100.0 * metric.getMinValue() / physTimeLimit, 1) + "%").paddedRight (' ', 8)
  183. + (String (100.0 * metric.getMaxValue() / physTimeLimit, 1) + "%").paddedRight (' ', 8)
  184. + String (metric.getStandardDeviation(), 3).paddedRight (' ', 8);
  185. }
  186. //==============================================================================
  187. void resetPerformanceMetrics()
  188. {
  189. audioCallbackRuntimeMs.reset();
  190. audioCallbackGapMs.reset();
  191. numLateCallbacks = 0;
  192. numCallbacksOverPhysicalTimeLimit = 0;
  193. }
  194. //==============================================================================
  195. void updateNumLoopIterationsPerCallback()
  196. {
  197. numLoopIterationsPerCallback = (int) loopIterationsSlider.getValue();
  198. }
  199. //==============================================================================
  200. static double getPreciseTimeMs() noexcept
  201. {
  202. return 1000.0 * (double) Time::getHighResolutionTicks() / (double) Time::getHighResolutionTicksPerSecond();
  203. }
  204. //==============================================================================
  205. double getPhysicalTimeLimitMs() const noexcept
  206. {
  207. return 1000.0 * (double) a.size() / currentSampleRate;
  208. }
  209. //==============================================================================
  210. std::vector<float> a, b, c; // must always be of size == current bufferSize
  211. double currentSampleRate = 0.0;
  212. StatisticsAccumulator<double> audioCallbackRuntimeMs;
  213. StatisticsAccumulator<double> audioCallbackGapMs;
  214. double lastCallbackStartTimeMs = 0.0;
  215. int numLateCallbacks = 0;
  216. int numCallbacksOverPhysicalTimeLimit = 0;
  217. int numLoopIterationsPerCallback;
  218. Slider loopIterationsSlider;
  219. std::mutex metricMutex;
  220. //==============================================================================
  221. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
  222. };
  223. // (This function is called by the app startup code to create our main component)
  224. Component* createMainContentComponent() { return new MainContentComponent(); }