ASIOAudioIODevice::getCurrentSampleRate() returning wrong value

ASIOAudioIODevice::getCurrentSampleRate() does sometimes return a wrong value. When the samplerate of an audio device that is open in a JUCE application is changed by a different application, this change will not be reflected in the currentSampleRate variable that is returned by this function.

Maybe in juce_win32_ASIO.cpp it should be changed like this?

double getCurrentSampleRate() override
{
  currentSampleRate = getSampleRate();
  return currentSampleRate;
}

 

Updating a value inside a function that is supposed to already know the correct value is something I'd be very suspicious of..

Looking at the code, it looks to me that maybe it just needs to ask the driver for the sample rate later on in the process - e.g. does your driver work if you add this at line 564?

            readLatencies();

            currentSampleRate = getSampleRate(); // <-- new line

            asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity);
            deviceIsOpen = true;

That does not help. As the ASIOAudioIODevice is not reset or re-opened, there is no call to ASIOAudioIODevice::open().

When the samplerate is changed e.g. from Cubase, only the callback function AudioDeviceManager::audioDeviceListChanged() is triggered. This will call currentAudioDevice->getCurrentSampleRate(), which returns the old value.

My ASIO device is an RME Babyface.

 

Regards,

Gregor

 

Don't understand this.. You say that audioDeviceListChanged() is called, so the device must call sendASIODeviceChangeToListeners(), which is only called immediately after a call to open(). So I don't see how it can not be calling open() ?

The call to audioDeviceListChanged() is originated from DeviceChangeDetector::timerCallback() which will call WASAPIAudioIODeviceType::systemDeviceChanged(). 

So the message is more or less an indirect "something has changed" message. It is not coming from the ASIO device itself, which might be a problem of the driver. The problem only occured with the RME Babyface and not with other interfaces.

Anyway in this case it happens that the samplerate that is returned from getCurrentSampleRate() is wrong which could be avoided as suggested above.


 

Your suggested fix isn't enough because when the sample rate changes, the device must be restarted, so that any clients get an audioDeviceAboutToStart callback. If getSampleRate just suddenly starts returning a new value, then any synths or audio playback wouldn't pick this change up, would continue playing at the wrong speed.

So I was trying to look for a way to force a device reset. On your device, surely the sampleRateChangedCallback() callback gets called? That should trigger a call to resetRequest, and then open(), etc.. ?

In my application it is sufficient to reset some internal stuff when the samplerate was changed. I did not need to restart the device. But I agree that this might not be sufficient as a general sulution.

Unfortunately sampleRateChangedCallback() also does not get called in this case. I just updated the RME driver but the problem remains. Maybe I should contact them, too.

 

If the driver is changing its rate without calling that method then yes, it definitely sounds like their bug!

Does it not call any of the ASIO callbacks when this happens? e.g. asioMessagesCallback with a kAsioResetRequest or kAsioResyncRequest or something? I can't see how any apps could handle this unless it does something to tell it about this..

I ran into a similar problem with an RME MadiFace USB. Here is what I have found:

[For simplicity, I’m using Lo to group 44.1 and 48KHz, and Hi to group 88.2 and 96K]

  • When RME’s SampleRate is changed between a “Lo” group and “Hi” group - for example from 44.1 to 96, or from 48 to 96, 44.1 to 88.2, etc… the ASIO driver sends the latencies changed callback (kAsioLatenciesChanged).

  • When RME’s SampleRate is changed within the Lo or Hi groups (44.1 -> 48, or 96 -> 88.2, etc.) the RME driver does not send any callbacks, neither through the asioMessage callaback, nor though the sampleRateDidChange callback).

What’s interesting is that Cubase 7 is able to detect this change even when I see no callbacks from the RME driver.

On the other hand, other DAWs such as Reaper are not able to detect this change either.

I’m suspecting that Cubase is doing one of two things to trigger their SR change handling logic:

  • Polling the getCurrentSampleRate() from a timer.
  • Calculating the actual SampleRate (based on the OS timer) when it gets the bufferSwitch/bufferSwitchTimeInfo callbacks.

Thanks.
Devendra.