From 8bb64a5ddc9acb937ee4ec5ae4fe3ca994aa37b1 Mon Sep 17 00:00:00 2001 From: hogliux Date: Thu, 21 Sep 2017 15:49:02 +0100 Subject: [PATCH] Added getXRunCount to AudioIODevice as a way to get Xrun counts from underlying hardware --- .../audio_io/juce_AudioIODevice.cpp | 1 + .../audio_io/juce_AudioIODevice.h | 13 +++++ .../native/juce_android_Audio.cpp | 9 ++++ .../native/juce_android_OpenSL.cpp | 49 ++++++++++++++----- .../native/juce_ios_Audio.cpp | 31 ++++++++++++ .../native/juce_ios_Audio.h | 2 + .../native/juce_linux_ALSA.cpp | 47 +++++++++++++++--- .../native/juce_linux_JackAudio.cpp | 16 ++++++ .../native/juce_mac_CoreAudio.cpp | 6 +++ .../native/juce_win32_ASIO.cpp | 12 ++++- .../native/juce_win32_DirectSound.cpp | 30 +++++++++++- .../native/juce_win32_WASAPI.cpp | 15 ++++-- 12 files changed, 208 insertions(+), 23 deletions(-) diff --git a/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp b/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp index 95ae0c8e41..935de58d07 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp @@ -33,6 +33,7 @@ AudioIODevice::~AudioIODevice() {} void AudioIODeviceCallback::audioDeviceError (const String&) {} bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; } bool AudioIODevice::hasControlPanel() const { return false; } +int AudioIODevice::getXRunCount() const noexcept { return -1; } bool AudioIODevice::showControlPanel() { diff --git a/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h b/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h index d1cb0d52f0..a1599f4d2b 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h +++ b/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h @@ -293,6 +293,19 @@ public: */ virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled); + //============================================================================== + /** Returns the number of under- or over runs reported by the OS since + playback/recording has started. + + This number may be different than determining the Xrun count manually (by + measuring the time spent in the audio callback) as the OS may be doing + some buffering internally - especially on mobile devices. + + Returns -1 if playback/recording has not started yet or if getting the underrun + count is not supported for this device (Android SDK 23 and lower). + */ + virtual int getXRunCount() const noexcept; + //============================================================================== protected: /** Creates a device, setting its name and type member variables. */ diff --git a/modules/juce_audio_devices/native/juce_android_Audio.cpp b/modules/juce_audio_devices/native/juce_android_Audio.cpp index 6ff8c32261..66634137f0 100644 --- a/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -33,6 +33,7 @@ namespace juce METHOD (release, "release", "()V") \ METHOD (flush, "flush", "()V") \ METHOD (write, "write", "([SII)I") \ + METHOD (getUnderrunCount, "getUnderrunCount", "()I") \ DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack"); #undef JNI_CLASS_MEMBERS @@ -281,6 +282,14 @@ public: String getLastError() override { return lastError; } bool isPlaying() override { return isRunning && callback != 0; } + int getXRunCount() const noexcept override + { + if (outputDevice != nullptr) + return getEnv()->CallIntMethod (outputDevice, AudioTrack.getUnderrunCount); + + return -1; + } + void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) diff --git a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index 65362b8565..9da627fe0d 100644 --- a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -291,12 +291,32 @@ public: nextBlock (0), numBlocksOut (0) {} + ~OpenSLQueueRunner() + { + if (config != nullptr && javaProxy != nullptr) + { + javaProxy.clear(); + (*config)->ReleaseJavaProxy (config, SL_ANDROID_JAVA_PROXY_ROUTING); + } + } + bool init() { runner = crtp().createPlayerOrRecorder(); if (runner == nullptr) return false; + // may return nullptr on some platforms - that's ok + config = SlRef::cast (runner); + if (config != nullptr) + { + jobject audioRoutingJni; + auto status = (*config)->AcquireJavaProxy (config, SL_ANDROID_JAVA_PROXY_ROUTING, &audioRoutingJni); + + if (status == SL_RESULT_SUCCESS && audioRoutingJni != 0) + javaProxy = GlobalRef (audioRoutingJni); + } + queue = SlRef::cast (runner); if (queue == nullptr) return false; @@ -347,6 +367,8 @@ public: SlRef runner; SlRef queue; + SlRef config; + GlobalRef javaProxy; int numChannels; @@ -379,12 +401,12 @@ public: SLDataSource source = {&queueLocator, &dataFormat}; SLDataSink sink = {&outputMix, nullptr}; - SLInterfaceID queueInterfaces[] = { &IntfIID::iid }; - SLboolean trueFlag = SL_BOOLEAN_TRUE; + SLInterfaceID queueInterfaces[] = { &IntfIID::iid, &IntfIID::iid }; + SLboolean interfaceRequired[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE}; SLObjectItf obj = nullptr; - SLresult status = (*Base::owner.engine)->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 1, queueInterfaces, &trueFlag); + SLresult status = (*Base::owner.engine)->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { if (obj != nullptr) @@ -437,15 +459,12 @@ public: SlRef recorder = SlRef::cast (SlObjectRef (obj)); - // may return nullptr on some platforms - that's ok - config = SlRef::cast (recorder); - return recorder; } bool setAudioPreprocessingEnabled (bool shouldEnable) { - if (config != nullptr) + if (Base::config != nullptr) { const bool supportsUnprocessed = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 25); const SLuint32 recordingPresetValue @@ -453,8 +472,8 @@ public: : (supportsUnprocessed ? SL_ANDROID_RECORDING_PRESET_UNPROCESSED : SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION)); - SLresult status = (*config)->SetConfiguration (config, SL_ANDROID_KEY_RECORDING_PRESET, - &recordingPresetValue, sizeof (recordingPresetValue)); + SLresult status = (*Base::config)->SetConfiguration (Base::config, SL_ANDROID_KEY_RECORDING_PRESET, + &recordingPresetValue, sizeof (recordingPresetValue)); return (status == SL_RESULT_SUCCESS); } @@ -463,8 +482,6 @@ public: } void setState (bool running) { (*Base::runner)->SetRecordState (Base::runner, running ? SL_RECORDSTATE_RECORDING : SL_RECORDSTATE_STOPPED); } - - SlRef config; }; //============================================================================== @@ -521,6 +538,7 @@ public: virtual void stop() { running = false; } virtual bool setAudioPreprocessingEnabled (bool shouldEnable) = 0; virtual bool supportsFloatingPoint() const noexcept = 0; + virtual int getXRunCount() const noexcept = 0; void setCallback (AudioIODeviceCallback* callbackToUse) { @@ -678,6 +696,14 @@ public: return true; } + int getXRunCount() const noexcept override + { + if (player != nullptr && player->javaProxy != nullptr) + return getEnv()->CallIntMethod (player->javaProxy, AudioTrack.getUnderrunCount); + + return -1; + } + bool supportsFloatingPoint() const noexcept override { return (BufferHelpers::isFloatingPoint != 0); } void doSomeWorkOnAudioThread() @@ -892,6 +918,7 @@ public: BigInteger getActiveInputChannels() const override { return activeInputChans; } String getLastError() override { return lastError; } bool isPlaying() override { return callback != nullptr; } + int getXRunCount() const noexcept override { return (session != nullptr ? session->getXRunCount() : -1); } int getDefaultBufferSize() override { diff --git a/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/modules/juce_audio_devices/native/juce_ios_Audio.cpp index c123849b06..432bb228bd 100644 --- a/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -313,6 +313,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, { close(); + firstHostTime = true; + lastNumFrames = 0; + xrun = 0; lastError.clear(); preferredBufferSize = bufferSize <= 0 ? defaultBufferSize : bufferSize; @@ -714,6 +717,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, { OSStatus err = noErr; + recordXruns (time, numFrames); + if (audioInputIsAvailable && numInputChannels > 0) err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data); @@ -799,6 +804,26 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, updateSampleRateAndAudioInput(); } + void recordXruns (const AudioTimeStamp* time, UInt32 numFrames) + { + if (time != nullptr && (time->mFlags & kAudioTimeStampSampleTimeValid) != 0) + { + if (! firstHostTime) + { + if ((time->mSampleTime - lastSampleTime) != lastNumFrames) + xrun++; + } + else + firstHostTime = false; + + lastSampleTime = time->mSampleTime; + } + else + firstHostTime = true; + + lastNumFrames = numFrames; + } + //============================================================================== static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) @@ -1063,6 +1088,11 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, float* outputChannels[3]; bool monoInputChannelNumber, monoOutputChannelNumber; + bool firstHostTime; + Float64 lastSampleTime; + unsigned int lastNumFrames; + int xrun; + JUCE_DECLARE_NON_COPYABLE (Pimpl) }; @@ -1108,6 +1138,7 @@ BigInteger iOSAudioIODevice::getActiveInputChannels() const { return pim int iOSAudioIODevice::getOutputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].outputLatency); } int iOSAudioIODevice::getInputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].inputLatency); } +int iOSAudioIODevice::getXRunCount() const noexcept { return pimpl->xrun; } void iOSAudioIODevice::setMidiMessageCollector (MidiMessageCollector* collector) { pimpl->messageCollector = collector; } AudioPlayHead* iOSAudioIODevice::getAudioPlayHead() const { return pimpl; } diff --git a/modules/juce_audio_devices/native/juce_ios_Audio.h b/modules/juce_audio_devices/native/juce_ios_Audio.h index f0b3733268..b06be4d97d 100644 --- a/modules/juce_audio_devices/native/juce_ios_Audio.h +++ b/modules/juce_audio_devices/native/juce_ios_Audio.h @@ -62,6 +62,8 @@ public: int getOutputLatencyInSamples() override; int getInputLatencyInSamples() override; + int getXRunCount() const noexcept override; + //============================================================================== void setMidiMessageCollector (MidiMessageCollector*); AudioPlayHead* getAudioPlayHead() const; diff --git a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index db984ebd44..7a521f468f 100644 --- a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -333,8 +333,14 @@ public: numDone = snd_pcm_writen (handle, (void**) data, (snd_pcm_uframes_t) numSamples); } - if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) numDone, 1 /* silent */))) - return false; + if (numDone < 0) + { + if (numDone == -(EPIPE)) + underrunCount++; + + if (JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) numDone, 1 /* silent */))) + return false; + } if (numDone < numSamples) JUCE_ALSA_LOG ("Did not write all samples: numDone: " << numDone << ", numSamples: " << numSamples); @@ -354,8 +360,15 @@ public: snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); - if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) - return false; + if (num < 0) + { + if (num == -(EPIPE)) + overrunCount++; + + if (JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) + return false; + } + if (num < numSamples) JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); @@ -367,8 +380,14 @@ public: { snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, (snd_pcm_uframes_t) numSamples); - if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) - return false; + if (num < 0) + { + if (num == -(EPIPE)) + overrunCount++; + + if (JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) + return false; + } if (num < numSamples) JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); @@ -384,6 +403,7 @@ public: snd_pcm_t* handle; String error; int bitDepth, numChannelsRunning, latency; + int underrunCount = 0, overrunCount = 0; private: //============================================================================== @@ -749,6 +769,19 @@ public: return 16; } + int getXRunCount() const noexcept + { + int result = 0; + + if (outputDevice != nullptr) + result += outputDevice->underrunCount; + + if (inputDevice != nullptr) + result += inputDevice->overrunCount; + + return result; + } + //============================================================================== String error; double sampleRate; @@ -908,6 +941,8 @@ public: int getOutputLatencyInSamples() override { return internal.outputLatency; } int getInputLatencyInSamples() override { return internal.inputLatency; } + int getXRunCount() const noexcept override { return internal.getXRunCount(); } + void start (AudioIODeviceCallback* callback) override { if (! isOpen_) diff --git a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index eb25bd7622..35302a3160 100644 --- a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -69,6 +69,7 @@ JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); +JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg)); #if JUCE_DEBUG #define JACK_LOGGING_ENABLED 1 @@ -258,9 +259,11 @@ public: lastError.clear(); close(); + xruns = 0; juce::jack_set_process_callback (client, processCallback, this); juce::jack_set_port_connect_callback (client, portConnectCallback, this); juce::jack_on_shutdown (client, shutdownCallback, this); + juce::jack_set_xrun_callback (client, xrunCallback, this); juce::jack_activate (client); deviceIsOpen = true; @@ -302,6 +305,8 @@ public: if (client != nullptr) { juce::jack_deactivate (client); + + juce::jack_set_xrun_callback (client, xrunCallback, nullptr); juce::jack_set_process_callback (client, processCallback, nullptr); juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); juce::jack_on_shutdown (client, shutdownCallback, nullptr); @@ -338,6 +343,7 @@ public: bool isPlaying() override { return callback != nullptr; } int getCurrentBitDepth() override { return 32; } String getLastError() override { return lastError; } + int getXRunCount() const noexcept override { return xruns; } BigInteger getActiveOutputChannels() const override { return activeOutputChannels; } BigInteger getActiveInputChannels() const override { return activeInputChannels; } @@ -408,6 +414,14 @@ private: return 0; } + static int xrunCallback (void* callbackArgument) + { + if (callbackArgument != nullptr) + ((JackAudioIODevice*) callbackArgument)->xruns++; + + return 0; + } + void updateActivePorts() { BigInteger newOutputChannels, newInputChannels; @@ -477,6 +491,8 @@ private: int totalNumberOfOutputChannels; Array inputPorts, outputPorts; BigInteger activeInputChannels, activeOutputChannels; + + int xruns; }; diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 40629e7a69..0bd8f3fbcb 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -796,6 +796,7 @@ public: int inputLatency = 0; int outputLatency = 0; int bitDepth = 32; + int xruns = 0; BigInteger activeInputChans, activeOutputChans; StringArray inChanNames, outChanNames; Array sampleRates; @@ -837,6 +838,9 @@ private: switch (pa->mSelector) { + case kAudioDeviceProcessorOverload: + intern->xruns++; + break; case kAudioDevicePropertyBufferSize: case kAudioDevicePropertyBufferFrameSize: case kAudioDevicePropertyNominalSampleRate: @@ -962,6 +966,7 @@ public: double getCurrentSampleRate() override { return internal->getSampleRate(); } int getCurrentBitDepth() override { return internal->bitDepth; } int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } + int getXRunCount() const noexcept override { return internal->xruns; } int getDefaultBufferSize() override { @@ -982,6 +987,7 @@ public: { isOpen_ = true; + internal->xruns = 0; if (bufferSizeSamples <= 0) bufferSizeSamples = getDefaultBufferSize(); diff --git a/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 54bc1e7b51..11645bb6c2 100644 --- a/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -409,6 +409,8 @@ public: Array getAvailableBufferSizes() override { return bufferSizes; } int getDefaultBufferSize() override { return preferredBufferSize; } + int getXRunCount() const noexcept override { return xruns; } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sr, int bufferSizeSamples) override @@ -464,6 +466,9 @@ public: err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); jassert (err == ASE_OK); + if (asioObject->future (kAsioCanReportOverload, nullptr) != ASE_OK) + xruns = -1; + inBuffers.calloc (totalNumInputChans + 8); outBuffers.calloc (totalNumOutputChans + 8); @@ -789,6 +794,7 @@ private: bool volatile littleEndian, postOutput, needToReset; bool volatile insideControlPanelModalLoop; bool volatile shouldUsePreferredSize; + int xruns = 0; //============================================================================== static String convertASIOString (char* const text, int length) @@ -1180,6 +1186,7 @@ private: totalNumOutputChans = 0; numActiveInputChans = 0; numActiveOutputChans = 0; + xruns = 0; currentCallback = nullptr; error.clear(); @@ -1349,7 +1356,7 @@ private: { case kAsioSelectorSupported: if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest - || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) + || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor || value == kAsioOverload) return 1; break; @@ -1360,7 +1367,8 @@ private: case kAsioEngineVersion: return 2; case kAsioSupportsTimeInfo: - case kAsioSupportsTimeCode: return 0; + case kAsioSupportsTimeCode: return 0; + case kAsioOverload: xruns++; return 1; } return 0; diff --git a/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 83b60ce691..10ea41abae 100644 --- a/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -266,6 +266,10 @@ public: pDirectSound = nullptr; pOutputBuffer = nullptr; writeOffset = 0; + xruns = 0; + + firstPlayTime = true; + lastPlayTime = 0; String error; HRESULT hr = E_NOINTERFACE; @@ -276,6 +280,7 @@ public: if (SUCCEEDED (hr)) { bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; + ticksPerBuffer = bytesPerBuffer * Time::getHighResolutionTicksPerSecond() / (sampleRate * (bitDepth >> 2)); totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15; const int numChannels = 2; @@ -397,6 +402,18 @@ public: return true; } + auto currentPlayTime = Time::getHighResolutionTicks (); + if (! firstPlayTime) + { + auto expectedBuffers = (currentPlayTime - lastPlayTime) / ticksPerBuffer; + + playCursor += static_cast (expectedBuffers * bytesPerBuffer); + } + else + firstPlayTime = false; + + lastPlayTime = currentPlayTime; + int playWriteGap = (int) (writeCursor - playCursor); if (playWriteGap < 0) playWriteGap += totalBytesPerBuffer; @@ -409,6 +426,9 @@ public: { writeOffset = writeCursor; bytesEmpty = totalBytesPerBuffer - playWriteGap; + + // buffer underflow + xruns++; } if (bytesEmpty >= bytesPerBuffer) @@ -480,7 +500,7 @@ public: } } - int bitDepth; + int bitDepth, xruns; bool doneFlag; private: @@ -496,6 +516,9 @@ private: int totalBytesPerBuffer, bytesPerBuffer; unsigned int lastPlayCursor; + bool firstPlayTime; + int64 lastPlayTime, ticksPerBuffer; + static inline int convertInputValues (const float l, const float r) noexcept { return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16 @@ -854,6 +877,11 @@ public: bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } String getLastError() override { return lastError; } + int getXRunCount () const noexcept override + { + return (outChans[0] != nullptr ? outChans[0]->xruns : -1); + } + //============================================================================== StringArray inChannels, outChannels; int outputDeviceIndex, inputDeviceIndex; diff --git a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 16ed54c5c0..d0380d7897 100644 --- a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -128,7 +128,12 @@ enum EDataFlow enum { - DEVICE_STATE_ACTIVE = 1, + DEVICE_STATE_ACTIVE = 1 +}; + +enum +{ + AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY = 1, AUDCLNT_BUFFERFLAGS_SILENT = 2 }; @@ -742,6 +747,7 @@ public: reservoirMask = nextPowerOfTwo (reservoirSize) - 1; reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true); reservoirReadPos = reservoirWritePos = 0; + xruns = 0; if (! check (client->Start())) return false; @@ -774,6 +780,9 @@ public: while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0) { + if ((flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) + xruns++; + int samplesLeft = (int) numSamplesAvailable; while (samplesLeft > 0) @@ -838,7 +847,7 @@ public: ComSmartPtr captureClient; MemoryBlock reservoir; - int reservoirSize, reservoirMask; + int reservoirSize, reservoirMask, xruns; volatile int reservoirReadPos, reservoirWritePos; ScopedPointer converter; @@ -1075,7 +1084,7 @@ public: BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } String getLastError() override { return lastError; } - + int getXRunCount () const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override