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.

279 lines
11KB

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