Hello all. I have written an ASIO output driver in C++ for low-latency audio playback in my software.
And while it works great for my Audigy Rx drivers, as well as with ASIO4ALL, it is crashing with many other drivers, including:
- Yamaha Steinberg CI1
- C-Media (CM6631A)
- FL Studio (not a real hardware driver)
The drivers appear to be crashing internally whenever I try to stop/release them, even when I do nothing in response to the callback messages. This takes down my entire application, even though ASIO is running in its own separate thread. I’ve tried adding lots of Sleep() commands in case the driver couldn’t keep up, but to no avail.
Can anyone please help me with how to avoid this? I strongly believe they are vendor driver bugs (a driver should never crash, no matter what inputs you give it, it should return error codes instead), however I may well be using the API wrong. Although I’ve followed host\sample\hostsample.cpp as closely as possible. Other closed-source ASIO applications seem to work okay, but I can’t analyze what they’re doing differently for obvious reasons.
Stack trace example:
I am using the direct COM interface, since this is a C++ project.
Initialization code:
//_active.classID retrieved from choosing one of HKLM\SOFTWARE\ASIO\*\CLSID entries
CLSID classID;
if(CLSIDFromString((LPOLESTR)_active.classID, (LPCLSID)&classID) != S_OK) return false;
if(CoCreateInstance(classID, 0, CLSCTX_INPROC_SERVER, classID, (void**)&_asio) != S_OK) return false;
if(!_asio->init((void*)_context)) return false; //_context is a valid, visible window handle (HWND)
if(_asio->getSampleRate(&_active.sampleRate) != ASE_OK) return false;
if(_asio->getChannels(&_active.inputChannels, &_active.outputChannels) != ASE_OK) return false;
if(_asio->getBufferSize(
&_active.minimumBufferSize, &_active.maximumBufferSize,
&_active.preferredBufferSize, &_active.granularity
) != ASE_OK) return false;
_frequency = _active.sampleRate;
_latency = _latency < _active.minimumBufferSize ? _active.minimumBufferSize : _latency;
_latency = _latency > _active.maximumBufferSize ? _active.maximumBufferSize : _latency;
for(auto n : range(_channels)) {
_channel[n].isInput = false;
_channel[n].channelNum = n;
_channel[n].buffers[0] = nullptr;
_channel[n].buffers[1] = nullptr;
}
ASIOCallbacks callbacks;
callbacks.bufferSwitch = &AudioASIO::_bufferSwitch;
callbacks.sampleRateDidChange = &AudioASIO::_sampleRateDidChange;
callbacks.asioMessage = &AudioASIO::_asioMessage;
callbacks.bufferSwitchTimeInfo = &AudioASIO::_bufferSwitchTimeInfo;
if(_asio->createBuffers(_channel, _channels, _latency, &callbacks) != ASE_OK) return false;
if(_asio->getLatencies(&_active.inputLatency, &_active.outputLatency) != ASE_OK) return false;
ASIOChannelInfo channelInformation = {};
channelInformation.channel = 0;
channelInformation.isInput = false;
if(_asio->getChannelInfo(&channelInformation) != ASE_OK) return false;
_sampleFormat = channelInformation.type;
if(_asio->start() != ASE_OK) return false;
return true;
Termination code:
_asio->stop(); //crashes here
_asio->disposeBuffers(); //if I don't call stop, it crashes here
_asio->Release(); //if I don't call disposeBuffers, it crashes here