Bug: Windows Audio processes less samples then the samplerate when not selecting any outputs

Hi,

When using AudioDeviceManager with input-only configuration and a non-default buffer size, the audio callback does not receive the expected number of samples per second, despite the device reporting a sample rate of 44100 Hz.

Details

Expected Behavior:

At 44100 Hz sample rate, the audio callback should receive 44100 samples per second.

Actual Behavior:

The actual number of samples received per second is significantly less than the reported sample rate, proportional to the selected buffer size

Preconditions
  • No output channels enabled
  • Windows Audio device type
  • Buffer size is smaller than the device’s default buffer size
Environment:
  • OS: Windows
  • Latest JUCE

Minimal Reproducible Example

#include <juce_audio_devices/juce_audio_devices.h>

class AudioExample : private juce::AudioIODeviceCallback, private juce::Timer
{
public:
    AudioExample ()
    {
        deviceManager.initialiseWithDefaultDevices (1, 0);

        deviceManager.addAudioCallback (this);

        // after 10 seconds, change the buffer size to a lower buffersize
        juce::Timer::callAfterDelay (10000, [&]
        {
            auto setup = deviceManager.getAudioDeviceSetup();
            setup.bufferSize = 224;
            setup.inputChannels = 1;
            setup.outputChannels = 0;

            if (const auto err = deviceManager.setAudioDeviceSetup(setup, true); err.isNotEmpty())
            {
                DBG (err);
                jassertfalse;
            }
        });

        startTimerHz (1);
    }

    ~AudioExample() override
    {
        deviceManager.removeAudioCallback (this);
    }

private:
    juce::AudioDeviceManager deviceManager;

    void audioDeviceIOCallbackWithContext (const float* const* inputChannelData,
                                           int numInputChannels,
                                           float* const* outputChannelData,
                                           int numOutputChannels,
                                           int numSamples,
                                           const juce::AudioIODeviceCallbackContext&) override
    {
        juce::ignoreUnused (inputChannelData, numInputChannels, outputChannelData, numOutputChannels);
        sampleCount += numSamples;
    }

    void audioDeviceAboutToStart (juce::AudioIODevice* audioDevice) override
    {
        DBG("Started device " + audioDevice->getName() + " (" + audioDevice->getTypeName() + ") with sampleRate and buffer size: "
            << audioDevice->getCurrentSampleRate() << ", " << audioDevice->getCurrentBufferSizeSamples());
    }

    void audioDeviceStopped() override
    {
        DBG ("Stopped");
    }

    void audioDeviceError (const juce::String& errorMessage) override
    {
        DBG (errorMessage);
        jassertfalse;
    }

    std::atomic<int> sampleCount { 0 };
    void timerCallback() override
    {
        // when the buffesize is changed to 224, we should see around 44100 samples processed per second
        // but on average we receive around 22400 samples per second
        DBG("Samples processed in one second: " << sampleCount.exchange(0));
    }
};

int main()
{
    const juce::ScopedJuceInitialiser_GUI _guiInitializer;
    const auto mm = juce::MessageManager::getInstance();

    AudioExample example;

    mm->runDispatchLoop();

    return 0;
}

See logs:

Started device Analog (1+2) (RME Fireface UCX II) (Windows Audio) with sampleRate and buffer size: 44100, 441
Samples processed in one second: 44100
Samples processed in one second: 44100
Samples processed in one second: 44100
Samples processed in one second: 44100
Samples processed in one second: 44541
Samples processed in one second: 44100
Samples processed in one second: 44100
Samples processed in one second: 44100
Samples processed in one second: 44100
Stopped
The thread 'JUCE v8.0.10: WASAPI' (37296) has exited with code 0 (0x0).
'AudioBugExample.exe' (Win32): Loaded 'C:\Windows\System32\avrt.dll'. Symbol loading disabled by Include/Exclude setting.
'AudioBugExample.exe' (Win32): Unloaded 'C:\Windows\System32\avrt.dll'
Started device Analog (1+2) (RME Fireface UCX II) (Windows Audio) with sampleRate and buffer size: 44100, 224
Samples processed in one second: 43659
Samples processed in one second: 22400
Samples processed in one second: 22624
Samples processed in one second: 22400

CMake used:

cmake_minimum_required(VERSION 3.15)

set(CMAKE_CXX_STANDARD 23)

project(AudioBugExample VERSION 0.0.1)

file(
    DOWNLOAD
    https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/CPM.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake
)
include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)

CPMAddPackage(
    NAME JUCE
    GITHUB_REPOSITORY juce-framework/JUCE
    GIT_TAG master
)

juce_add_console_app(AudioBugExample)
target_sources(AudioBugExample PRIVATE AudioBugExample.cpp)
target_link_libraries(AudioBugExample
        PRIVATE
        juce::juce_audio_utils
        PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags
)

Thanks for reporting. We’ve pushed a fix for this issue here:

Please note that, in shared mode, changing the audio block size does not seem to be possible. Therefore, the actual block size may not end up matching the requested block size.

1 Like

Amazing thanks!