Divide by zero in ResamplingAudioSource


#1

This might have gone unnoticed, but this code:

         if (bufferSize < sampsNeeded + 8)
         {
             bufferPos %= bufferSize;

Can leads to (extremely rare) divide-by-zero exception being generated.
What happens, is that the main thread has just finished setting the callback:

#0  0x00007f89f4ba2474 in __pthread_mutex_unlock_usercnt (mutex=0x21a7c58, decr=<value optimized out>) at pthread_mutex_unlock.c:52
#1  0x00000000006bb7ea in juce::CriticalSection::exit (this=0x21a7c58) at ../../src/native/common/juce_posix_SharedCode.h:59
#2  0x0000000000429687 in ~ScopedLock (this=0x7fff1744b2d0, __in_chrg=<value optimized out>) at ../../include/../../Libraries/Juce/src/containers/../utilities/../events/../threads/juce_ScopedLock.h:78
#3  0x00000000006d41ff in juce::ALSAThread::setCallback (this=0x21a7b30, newCallback=0x21c06f8) at ../../src/native/linux/juce_linux_Audio.cpp:511
#4  0x00000000006d5074 in juce::ALSAAudioIODevice::start (this=0x2122530, callback=0x21c06f8) at ../../src/native/linux/juce_linux_Audio.cpp:788

And the alsa thread called :

#0  0x0000000000586f90 in juce::ResamplingAudioSource::getNextAudioBlock (this=0x21c0520, info=...) at ../../src/audio/audio_sources/juce_ResamplingAudioSource.cpp:112
#1  0x000000000058604d in juce::AudioSourcePlayer::audioDeviceIOCallback (this=0x21c06f8, inputChannelData=0x21a8288, totalNumInputChannels=0, outputChannelData=0x21a7e80, totalNumOutputChannels=6,
    numSamples=8192) at ../../src/audio/audio_sources/juce_AudioSourcePlayer.cpp:157
#2  0x00000000006d4335 in juce::ALSAThread::run (this=0x21a7b30) at ../../src/native/linux/juce_linux_Audio.cpp:540

Since the prepareToPlay wasn’t called yet (it’s called after setCallback returns, but before start returns), the Resampling’s audio source is called to fill some buffer while it’s not “prepared”, thus leading bufferSize = 0.
This workaround might work if you change the start function to read instead:

    void start (AudioIODeviceCallback* callback)
    {
        if (! isOpen_)
            callback = 0;

        if (callback != 0)
            callback->audioDeviceAboutToStart (this);

        internal->setCallback (callback);

        isStarted = (callback != 0);
    }

Please confirm that “internal” can’t be changed from the outside (which should be ok, unless someone is calling stop() inside the audioDeviceAboutToStart, but he will see the previous callback which is IMHO what was expected)


#2

Good catch - that’s definitely an ALSA problem rather than anything wrong with the resampling source, the sources should always be able to assume that getNextAudioBlock will never be called before they are prepared.

Your fix looks good to me, that’s the way it’s done in the other audio API wrappers, I’ll get it sorted out.