Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASIO: sampleRateChanged callback and kAsioResetRequest (asioMessages callback) #235

Open
renaud-sc opened this issue Mar 12, 2020 · 4 comments

Comments

@renaud-sc
Copy link

renaud-sc commented Mar 12, 2020

I encountered a problem with a Motu 16A. The ASIO sampleRateChanged callback (line 3654 in RtAudio.cpp) is called even if the sample rate is not changed (and even if the card is in internal sync). Currently this callback simply stop the stream.

There is a comment in that function:

The ASIO documentation says that this usually only happens during
external sync. Audio processing is not stopped by the driver,
actual sample rate might not have even changed, maybe only the
sample rate status of an AES/EBU or S/PDIF digital input at the
audio device.

I have modified that function as follow and it works fine, the steam starts and audio is perfectly fine.

static void sampleRateChanged( ASIOSampleRate sRate )
{
  RtApi *object = (RtApi *) asioCallbackInfo->object;
  try {
    if(object->getStreamSampleRate() != sRate)
    {
      object->stopStream();
      std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
    }
  }
  catch ( RtAudioError &exception ) {
    std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
  }
}

First question: what do you think about this change ? Are you aware of situations where this would not work ?

Second question: let's say the sample rate did actually changed then I assume it make sense to stop the steam. But there is no obvious way for the user application to be aware that something happened and why. This is somewhere related to #195 and #212. So I was thinking of adding object->error(RtAudioError::DRIVER_ERROR); (or any other type of error), so that at the end the user provided errorCallback gets called.

Third question: a similar situation is when a kAsioResetRequest is received by asioMessages (this happen for example when you unplug a USB sound card), actually RtAudio does nothing ! I believe it would make sense to stop the stream there too AND to call the error callback. This is also related to #194.

Fourth question: What about thread safety ? How safe is it to call getStreamSampleRate, stopStream and error in the sampleRateChanged and asioMessages (provided that the user errorCallback would be thread safe, but that it the responsibility of the user) ?

I am going to try to get ASIO working with #212 and the above proposals to see what it does...

Thanks for this wonderful library !

@garyscavone
Copy link
Contributor

I added code in the newer 6.0 version that could hopefully correctly deal with this. The code is supposed to close an open stream automatically if either the sample rate changes or a kAsioResetRequest is received (and issue an error message through the errorCallback). The problem is that I cannot test it because I don't have an ASIO device that I can unplug. And the ASIO system (or at least my driver) does not allow me to change the sample rate once a stream is running. I also tried to open a stream and change the sample rate before the stream is started but that did not trigger a call to the sampleRateChanged() callback. Finally, I tried it with Asio4All and a USB headset that I could unplug but no kAsioResetRequest was triggered.

@renaud-sc
Copy link
Author

Hi Gary,

I have tested it using an SSL 2+. Changing the sample rate or the buffer size from the device control application (installed with the device driver) generate a kAsioResetRequest message. Unfortunately it crashes in the driver code somwhere. I was not able to spot exactly where and why. I wonder if it's really safe to call closeStream in the asioMessages callback. Perhaps it's descructing objects that are still in use.

Because we wanted to support more than one ASIO device, we now use our own implementation. We have a thread that manage all ASIO initialisation/deinitialisation and we push a "reset" command on a queue from the message callback and process that message in our own thread. I haven't see any crash so far. Perhaps that should be the approach used by RtAudio as well ?

@garyscavone
Copy link
Contributor

Thanks for this report! Yes, probably a better approach would be to raise a flag when kAsioResetRequest is called and then handle that condition from within the RtAudio :: callbackEvent() function, as we already do to stop a stream after draining. I'll hunt around for a device with an ASIO driver that I might be able to use for testing.

@garyscavone
Copy link
Contributor

I found a USB device that I could test with and I think I fixed the problem. It turns out that the callbackEvent() function is no longer called once a kAsioResetRequest is received. Thus, I slightly modified the already existing asioStopStream() function and I spawn a thread from within asioMessages() to call that function when we receive a reset request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants