From 93ea3d922f3b0173785ccb342e4ae8224a7cb0fb Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 10 Sep 2018 11:46:47 +0100 Subject: [PATCH] Added class AudioProcessLoadMeasurer, and a new version of the method AudioDeviceManager::getAudioDeviceSetup() --- .../DemoRunner/Source/Demos/JUCEDemos.cpp | 3 +- .../buffers/juce_AudioProcessLoadMeasurer.cpp | 79 +++++++++++++++ .../buffers/juce_AudioProcessLoadMeasurer.h | 96 +++++++++++++++++++ .../juce_audio_basics/juce_audio_basics.cpp | 1 + modules/juce_audio_basics/juce_audio_basics.h | 1 + .../audio_io/juce_AudioDeviceManager.cpp | 54 ++++------- .../audio_io/juce_AudioDeviceManager.h | 38 ++++---- .../gui/juce_AudioAppComponent.cpp | 5 +- .../gui/juce_AudioDeviceSelectorComponent.cpp | 10 +- 9 files changed, 218 insertions(+), 69 deletions(-) create mode 100644 modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp create mode 100644 modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h diff --git a/examples/DemoRunner/Source/Demos/JUCEDemos.cpp b/examples/DemoRunner/Source/Demos/JUCEDemos.cpp index 29b7a4cf7a..e609221670 100644 --- a/examples/DemoRunner/Source/Demos/JUCEDemos.cpp +++ b/examples/DemoRunner/Source/Demos/JUCEDemos.cpp @@ -127,8 +127,7 @@ AudioDeviceManager& getSharedAudioDeviceManager (int numInputChannels, int numOu if (sharedAudioDeviceManager->getCurrentAudioDevice() != nullptr) { - AudioDeviceManager::AudioDeviceSetup setup; - sharedAudioDeviceManager->getAudioDeviceSetup (setup); + auto setup = sharedAudioDeviceManager->getAudioDeviceSetup(); auto numInputs = jmax (numInputChannels, setup.inputChannels.countNumberOfSetBits()); auto numOutputs = jmax (numOutputChannels, setup.outputChannels.countNumberOfSetBits()); diff --git a/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp b/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp new file mode 100644 index 0000000000..1795949351 --- /dev/null +++ b/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp @@ -0,0 +1,79 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() {} +AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() {} + +void AudioProcessLoadMeasurer::reset() +{ + reset (0, 0); +} + +void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize) +{ + cpuUsageMs = 0; + xruns = 0; + + if (sampleRate > 0.0 && blockSize > 0) + { + msPerBlock = 1000.0 * blockSize / sampleRate; + timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; + } + else + { + msPerBlock = 0; + timeToCpuScale = 0; + } +} + +void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds) +{ + const double filterAmount = 0.2; + cpuUsageMs += filterAmount * (milliseconds - cpuUsageMs); + + if (milliseconds > msPerBlock) + ++xruns; +} + +double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); } +double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); } + +int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; } + +AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p) + : owner (p), startTime (Time::getMillisecondCounterHiRes()) +{ +} + +AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer() +{ + owner.registerBlockRenderTime (Time::getMillisecondCounterHiRes() - startTime); +} + +} // namespace juce diff --git a/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h b/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h new file mode 100644 index 0000000000..53a3377cb1 --- /dev/null +++ b/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h @@ -0,0 +1,96 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +/** + Maintains an ongoing measurement of the proportion of time which is being + spent inside an audio callback. +*/ +class JUCE_API AudioProcessLoadMeasurer +{ +public: + /** */ + AudioProcessLoadMeasurer(); + + /** Destructor. */ + ~AudioProcessLoadMeasurer(); + + //============================================================================== + /** Resets the state. */ + void reset(); + + /** Resets the counter, in preparation for use with the given sample rate and block size. */ + void reset (double sampleRate, int blockSize); + + /** Returns the current load as a proportion 0 to 1.0 */ + double getLoadAsProportion() const; + + /** Returns the current load as a percentage 0 to 100.0 */ + double getLoadAsPercentage() const; + + /** Returns the number of over- (or under-) runs recorded since the state was reset. */ + int getXRunCount() const; + + //============================================================================== + /** This class measures the time between its construction and destruction and + adds it to an AudioProcessLoadMeasurer. + + e.g. + @code + { + AudioProcessLoadMeasurer::ScopedTimer timer (myProcessLoadMeasurer); + myCallback->doTheCallback(); + } + @endcode + */ + struct JUCE_API ScopedTimer + { + ScopedTimer (AudioProcessLoadMeasurer&); + ~ScopedTimer(); + + private: + AudioProcessLoadMeasurer& owner; + double startTime; + + JUCE_DECLARE_NON_COPYABLE (ScopedTimer) + }; + + /** Can be called manually to add the time of a callback to the stats. + Normally you probably would never call this - it's simpler and more robust to + use a ScopedTimer to measure the time using an RAII pattern. + */ + void registerBlockRenderTime (double millisecondsTaken); + +private: + double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0; + int xruns = 0; +}; + + +} // namespace juce diff --git a/modules/juce_audio_basics/juce_audio_basics.cpp b/modules/juce_audio_basics/juce_audio_basics.cpp index 56371d0d10..3b00dee363 100644 --- a/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/modules/juce_audio_basics/juce_audio_basics.cpp @@ -56,6 +56,7 @@ #include "buffers/juce_AudioDataConverters.cpp" #include "buffers/juce_FloatVectorOperations.cpp" #include "buffers/juce_AudioChannelSet.cpp" +#include "buffers/juce_AudioProcessLoadMeasurer.cpp" #include "effects/juce_IIRFilter.cpp" #include "effects/juce_LagrangeInterpolator.cpp" #include "effects/juce_CatmullRomInterpolator.cpp" diff --git a/modules/juce_audio_basics/juce_audio_basics.h b/modules/juce_audio_basics/juce_audio_basics.h index 3662bc3604..16dda906d0 100644 --- a/modules/juce_audio_basics/juce_audio_basics.h +++ b/modules/juce_audio_basics/juce_audio_basics.h @@ -84,6 +84,7 @@ #include "buffers/juce_FloatVectorOperations.h" #include "buffers/juce_AudioSampleBuffer.h" #include "buffers/juce_AudioChannelSet.h" +#include "buffers/juce_AudioProcessLoadMeasurer.h" #include "effects/juce_Decibels.h" #include "effects/juce_IIRFilter.h" #include "effects/juce_LagrangeInterpolator.h" diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index e2fb0b41a6..bbbcbbe623 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -23,14 +23,6 @@ namespace juce { -AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup() - : sampleRate (0), - bufferSize (0), - useDefaultInputChannels (true), - useDefaultOutputChannels (true) -{ -} - bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const { return outputDeviceName == other.outputDeviceName @@ -367,6 +359,11 @@ AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const return {}; } +AudioDeviceManager::AudioDeviceSetup AudioDeviceManager::getAudioDeviceSetup() const +{ + return currentSetup; +} + void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) const { setup = currentSetup; @@ -582,7 +579,7 @@ void AudioDeviceManager::closeAudioDevice() { stopDevice(); currentAudioDevice.reset(); - cpuUsageMs = 0; + loadMeasurer.reset(); } void AudioDeviceManager::restartLastAudioDevice() @@ -694,7 +691,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat if (callbacks.size() > 0) { - auto callbackStartTime = Time::getMillisecondCounterHiRes(); + AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer); tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); @@ -716,13 +713,6 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat dst[j] += src[j]; } } - - auto msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime; - const double filterAmount = 0.2; - cpuUsageMs += filterAmount * (msTaken - cpuUsageMs); - - if (msTaken > msPerBlock) - ++xruns; } else { @@ -748,17 +738,8 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) { - cpuUsageMs = 0; - xruns = 0; - - auto sampleRate = device->getCurrentSampleRate(); - auto blockSize = device->getCurrentBufferSizeSamples(); - - if (sampleRate > 0.0 && blockSize > 0) - { - msPerBlock = 1000.0 * blockSize / sampleRate; - timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; - } + loadMeasurer.reset (device->getCurrentSampleRate(), + device->getCurrentBufferSizeSamples()); { const ScopedLock sl (audioCallbackLock); @@ -772,13 +753,12 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device void AudioDeviceManager::audioDeviceStoppedInt() { - cpuUsageMs = 0; - timeToCpuScale = 0; - xruns = 0; sendChangeMessage(); const ScopedLock sl (audioCallbackLock); + loadMeasurer.reset(); + for (int i = callbacks.size(); --i >= 0;) callbacks.getUnchecked(i)->audioDeviceStopped(); } @@ -793,7 +773,7 @@ void AudioDeviceManager::audioDeviceErrorInt (const String& message) double AudioDeviceManager::getCpuUsage() const { - return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); + return loadMeasurer.getLoadAsProportion(); } //============================================================================== @@ -980,7 +960,7 @@ void AudioDeviceManager::playTestSound() auto phasePerSample = MathConstants::twoPi / (sampleRate / frequency); - auto* newSound = new AudioBuffer (1, soundLength); + std::unique_ptr> newSound (new AudioBuffer (1, soundLength)); for (int i = 0; i < soundLength; ++i) newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); @@ -988,15 +968,17 @@ void AudioDeviceManager::playTestSound() newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); - const ScopedLock sl (audioCallbackLock); - testSound.reset (newSound); + { + const ScopedLock sl (audioCallbackLock); + std::swap (testSound, newSound); + } } } int AudioDeviceManager::getXRunCount() const noexcept { auto deviceXRuns = (currentAudioDevice != nullptr ? currentAudioDevice->getXRunCount() : -1); - return jmax (0, deviceXRuns) + xruns; + return jmax (0, deviceXRuns) + loadMeasurer.getXRunCount(); } } // namespace juce diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index c8a262fbc3..378b26da4f 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -89,17 +89,6 @@ public: */ struct JUCE_API AudioDeviceSetup { - /** Creates an AudioDeviceSetup object. - - The default constructor sets all the member variables to indicate default values. - You can then fill-in any values you want to before passing the object to - AudioDeviceManager::initialise(). - */ - AudioDeviceSetup(); - - bool operator== (const AudioDeviceSetup& other) const; - bool operator!= (const AudioDeviceSetup& other) const; - /** The name of the audio device used for output. The name has to be one of the ones listed by the AudioDeviceManager's currently selected device type. @@ -119,13 +108,13 @@ public: A value of 0 indicates that you don't care what rate is used, and the device will choose a sensible rate for you. */ - double sampleRate; + double sampleRate = 0; /** The buffer size, in samples. This buffer size is used for both the input and output devices. A value of 0 indicates the default buffer size. */ - int bufferSize; + int bufferSize = 0; /** The set of active input channels. The bits that are set in this array indicate the channels of the @@ -138,7 +127,7 @@ public: should be ignored, and instead, the device's default channels should be used. */ - bool useDefaultInputChannels; + bool useDefaultInputChannels = true; /** The set of active output channels. The bits that are set in this array indicate the channels of the @@ -151,7 +140,10 @@ public: should be ignored, and instead, the device's default channels should be used. */ - bool useDefaultOutputChannels; + bool useDefaultOutputChannels = true; + + bool operator== (const AudioDeviceSetup&) const; + bool operator!= (const AudioDeviceSetup&) const; }; @@ -211,6 +203,13 @@ public: /** Returns the current device properties that are in use. @see setAudioDeviceSetup */ + AudioDeviceSetup getAudioDeviceSetup() const; + + /** Returns the current device properties that are in use. + This is an old method, kept around for compatibility, but you should prefer the new + version which returns the result rather than taking an out-parameter. + @see getAudioDeviceSetup() + */ void getAudioDeviceSetup (AudioDeviceSetup& result) const; /** Changes the current device or its settings. @@ -232,8 +231,7 @@ public: @see getAudioDeviceSetup */ - String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, - bool treatAsChosenDevice); + String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice); /** Returns the currently-active audio device. */ @@ -257,8 +255,7 @@ public: For a list of types, see getAvailableDeviceTypes(). */ - void setCurrentAudioDeviceType (const String& type, - bool treatAsChosenDevice); + void setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice); /** Closes the currently-open device. You can call restartLastAudioDevice() later to reopen it in the same state @@ -496,8 +493,7 @@ private: std::unique_ptr> testSound; int testSoundPosition = 0; - double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0; - int xruns = 0; + AudioProcessLoadMeasurer loadMeasurer; LevelMeter::Ptr inputLevelGetter { new LevelMeter() }, outputLevelGetter { new LevelMeter() }; diff --git a/modules/juce_audio_utils/gui/juce_AudioAppComponent.cpp b/modules/juce_audio_utils/gui/juce_AudioAppComponent.cpp index 31ccc2ae80..78a0909e18 100644 --- a/modules/juce_audio_utils/gui/juce_AudioAppComponent.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioAppComponent.cpp @@ -52,11 +52,10 @@ void AudioAppComponent::setAudioChannels (int numInputChannels, int numOutputCha if (usingCustomDeviceManager && xml == nullptr) { - AudioDeviceManager::AudioDeviceSetup setup; - deviceManager.getAudioDeviceSetup (setup); + auto setup = deviceManager.getAudioDeviceSetup(); if (setup.inputChannels.countNumberOfSetBits() != numInputChannels - || setup.outputChannels.countNumberOfSetBits() != numOutputChannels) + || setup.outputChannels.countNumberOfSetBits() != numOutputChannels) { setup.inputChannels.clear(); setup.outputChannels.clear(); diff --git a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index 1b1b292c8d..ee22972aca 100644 --- a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -330,8 +330,7 @@ public: void updateConfig (bool updateOutputDevice, bool updateInputDevice, bool updateSampleRate, bool updateBufferSize) { - AudioDeviceManager::AudioDeviceSetup config; - setup.manager->getAudioDeviceSetup (config); + auto config = setup.manager->getAudioDeviceSetup(); String error; if (updateOutputDevice || updateInputDevice) @@ -769,9 +768,7 @@ public: auto item = items[row]; bool enabled = false; - - AudioDeviceManager::AudioDeviceSetup config; - setup.manager->getAudioDeviceSetup (config); + auto config = setup.manager->getAudioDeviceSetup(); if (setup.useStereoPairs) { @@ -868,8 +865,7 @@ public: if (isPositiveAndBelow (row, items.size())) { - AudioDeviceManager::AudioDeviceSetup config; - setup.manager->getAudioDeviceSetup (config); + auto config = setup.manager->getAudioDeviceSetup(); if (setup.useStereoPairs) {