Enabling Windows Audio's SRC

Some audio devices, like Microsoft’s LifeChat LX-3000 USB handset, have different input and output sample rates (41.1k & 48k Hz in its case), and JUCE with Windows Audio (WASAPI) doesn’t support that.

But, since Windows 8 it seems that Windows Audio supports SRC, if you ask it to. So I made a commit to enable it. Seems to work :slight_smile: Many thanks to PortAudio whose source I looked at to discover the right Win API chants for this.

Feel free to use it (note that our master branch is JUCE 5 based, and there’s also a juce6 branch available) and/or incorporate it into JUCE!

7 Likes

From a quick search in the forum it looks like this issue is much more common than I thought (left a note in a few relevant threads)

1 Like

Thanks for the patch! We’ve added this to develop in b01e927 with a few modifications -

  • The Audiosessiontypes.h and audioclient.h headers aren’t required as we can do local declarations for the bits we need (as we do already). This also means we don’t need to bump the WINNT define.

  • When querying supported sample rates, IsFormatSupported() can succeed with a nearest supported format so we need to check the sample rate of this if it is valid otherwise all sample rates will “succeed” even though they aren’t supported by the device.

  • AUDCLNT_STREAMOPTIONS_MATCH_FORMAT isn’t required for SRC so we can remove the property setting.

  • We need to add all supported input and output device sample rates to the sampleRates array.

Whilst poking around in the WASAPI code, we’ve also added support for low latency shared streams via the IAudioClient3 interface in 6195a5a.

6 Likes

I tested your patch and it doesn’t seem to work here with my setup. It might be worth-while to hear whether it works on some setups and doesn’t for others.

To reproduce: Using Windows 10 here with the Microsoft LifeChat LX-3000 USB headset (which only supports 48000 Hz for earphones and 44100 for mic), running the JUCE DemoRunner and going to its preferences to choose audio devices, with my fix it previously offered both rates for either its input or output or both together, and with the new fix it works like it did before (can only pick input with its rate, output with its rate, and can’t pick them together).

This appears to be the culprit:

It seems like it is indeed required. Bringing it back fixes the issue!

I know that the Windows API chants can be mysterious and that it may be difficult to find how to actually enable something and the code may have some spurious stuff left. So I understand that you suspected that some of my changes are not required. But I did make an effort to only keep the bare-minimum actually required API chants.

2 Likes

No, I don’t think it’s required and the reason for removing in the commit is because I think it’s actually incorrect. When using AUDCLNT_STREAMOPTIONS_MATCH_FORMAT you are asking the audio engine to change its mix format (the underlying stream used for shared mode) to match the proposed format in the call to Initialize(), not to perform sample rate conversion between streams. This can cause IsFormatSupported() to fail with AUDCLNT_E_UNSUPPORTED_FORMAT if you are trying to open a device with a sample rate that the mix format doesn’t actually support (I’ve seen this happen with a headset that only supports 8kHz).

I think what we should be doing instead is always using the requested sample rate if SRC is available and enabled, instead of using the one suggested by nearestFormat. Can you see if the following change to tryFormat() works for your headset?

auto supportsSRC = supportsSampleRateConversion (deviceMode);

if (hr == S_FALSE
    && nearestFormat != nullptr
    && (format.Format.nSamplesPerSec == nearestFormat->nSamplesPerSec
        || supportsSRC))
{
    copyWavFormat (format, nearestFormat);

    if (supportsSRC)
    {
        format.Format.nSamplesPerSec       = (DWORD) newSampleRate;
        format.Format.nAvgBytesPerSec      = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
    }

    hr = S_OK;
}

CoTaskMemFree (nearestFormat);
return hr == S_OK;
1 Like

Your new patch does work with my setup. Thanks!

Great. That’s on develop now:

There’s also the change to ComSmartPtr which you suggested, so thanks for that!

2 Likes

Awesome, thanks for such a quick response!