DirectSound crash with mono input

I can get this crash to happen using the JUCE Demo Plug-in Standalone… select DirectSound and select a mono input source (I’m testing using Parallels Desktop 13 and Windows 10 Pro)… and you’ll get a crash in AudioProcessorPlayer::audioDeviceIOCallback() in line 132 at memcpy()

Cheers,

Rail

Another oddity I ran into… if I pressed the Test button for DirectSound the tone would continue to play unless I toggled the device…

in AudioDeviceManager::audioDeviceIOCallbackInt() I had to add:

        if (testSoundPosition >= testSound->getNumSamples())
            {
            testSound.reset();
            
            for (int i = 0; i < numOutputChannels; ++i)
                zeromem (outputChannelData[i], sizeof(float) * (size_t) numSamples);
            }

to stop it.

Rail

Thanks Rail, but that second change doesn’t make much sense… The test sound data gets added to the contents of the buffer, which must by that point in the function have already been given valid (or zero) data.

Perhaps the real problem is that you have a callback which is failing to clear the buffer when it’s not using it?

If it were a callback issue… surely I’d be seeing the same issue with the other drivers (ASIO and Windows Audio) and I don’t ever see this issue on MacOS… but with DirectSound it’s consistent… it’ll play the 440 Hz tone for 1 second but then it’ll keep playing the tone at a reduced (probably 3dB) level until I toggle devices.

Right, but your callback is required to clear the channel buffers that it’s given. If it was doing that, then adding the zeromem stuff that you suggested would make no difference because your callback would already have cleared those buffers!

I think maybe it’s not a problem with DSound, but rather that some of the other platforms hide the problem by clearing the buffers before passing them to the callback, so that if you forget to do that yourself, you get away with it.

Okay, so in a Standalone app then… where would it be making the callback to the device’s buffer? I have my main processor’s prepareToPlay() and processBlock()… and in the latter I do:

void CMyAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const int iNumSamples = buffer.getNumSamples();
    
    buffer.clear();

    :

    m_pMainGraph->processBlock (buffer, midiMessages);  // for my AudioProcessorGraph

    :
}

Cheers,

Rail

Well, by “callback” in this context I’m talking about the AudioIODeviceCallback that gets added to the AudioDeviceManager. Which class are you using as the bridge between that and your AudioProcessor?

I’m basically using the JUCE Standalone code… I don’t believe I changed anything in my copy of the code which would affect that… but I’ll switch JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP off and check it out.

Rail

Hmm, looking at the AudioProcessorPlayer code, I can’t see any way it’d fail to clear the buffers as long as your AudioProcessor clears them. Odd.

Yeah, I’ll debug to see if I can see what’s going on.

Cheers,

Rail

Well I get the same issue using the stock Standalone code.

Looking at the AudioProcessor buffer shows us the issue… which is probably related to the original post here… the AudioDeviceManager::audioDeviceIOCallbackInt() is showing 2 channels… while the AudioProcessor buffer is only 1 channel.

The only change I made to the related code is to stop the crash in the original post… in AudioProcessorPlayer::audioDeviceIOCallback()

        if (numInputChannels > 1)   // <<<--- Added this to stop a crash when numInputChannels == 1
        {
            for (int i = 0; i < numInputChannels; ++i)
            {
                channels[totalNumChans] = outputChannelData[i];
                memcpy (channels[totalNumChans], inputChannelData[i], sizeof (float) * (size_t) numSamples);
                ++totalNumChans;
            }
        }

what I should have done is:

    else
    {
		int iBaseChannel = numInputChannels;

        if (numInputChannels > 1)
			{
			for (int i = 0; i < numInputChannels; ++i)
				{
					channels[totalNumChans] = outputChannelData[i];
					memcpy (channels[totalNumChans], inputChannelData[i], sizeof (float) * (size_t) numSamples);
					++totalNumChans;
				}
			}
		else if (numInputChannels == 1)
			{
			iBaseChannel = 0;
			}

        for (int i = iBaseChannel; i < numOutputChannels; ++i)
        {
            channels[totalNumChans] = outputChannelData[i];
            zeromem (channels[totalNumChans], sizeof (float) * (size_t) numSamples);
            ++totalNumChans;
        }
    }

Rail

It looks like JUCE’s DirectSound interface assumes stereo pairs of channels, which obviously causes trouble with external audio devices. I’ll investigate this more thoroughly when I’m back from a short Xmas break - I’ve not got access to any hardware until Jan.

Thanks Tom

My Kludge is working as a temporary fix for now.

A simple no hardware test is in parallels to set the DirectSound input to the computer microphone.

Best wishes!

Rail

DirectSound and “Microphone (Parallels Audio Controller)” is working for me without any issues, but that’s because if I dig into the Windows 10 sound settings I can only choose from stereo input stream formats. Do you know where can I change this? The VM host is a Macbook.