Bug using Convolution with optimisation

I’ve been trying to use the new Convolution module, which was working fine until I turned on compiler optimisations. The plugin crashes in the Convolution::processSamples method.

The same happens with the DSP Demo if you build it in release mode. Happens everytime, but runs fine in debug build.

Here’s a stack trace from the crash using the DSP Demo:

#0 0x00000001000d8149 in juce::dsp::ConvolutionEngine::initializeConvolutionEngine(juce::dsp::ConvolutionEngine::ProcessingInformation&, int) ()
#1 0x00000001000d737c in juce::dsp::Convolution::Pimpl::initializeConvolutionEngines() ()
#2 0x00000001000d9b52 in juce::dsp::Convolution::Pimpl::processFifo() ()
#3 0x00000001000d5e0f in juce::dsp::Convolution::Pimpl::processSamples(juce::dsp::AudioBlock const&, juce::dsp::AudioBlock&) ()
#4 0x00000001000d59e9 in juce::dsp::Convolution::processSamples(juce::dsp::AudioBlock const&, juce::dsp::AudioBlock&, bool) ()
#5 0x0000000100002953 in DSPDemo::getNextAudioBlock(juce::AudioSourceChannelInfo const&) ()
#6 0x000000010001e193 in juce::AudioSourcePlayer::audioDeviceIOCallback(float const**, int, float**, int, int) ()
#7 0x000000010001d0db in juce::AudioDeviceManager::audioDeviceIOCallbackInt(float const**, int, float**, int, int) ()
#8 0x0000000100022845 in juce::CoreAudioClasses::CoreAudioInternal::audioCallback(AudioBufferList const*, AudioBufferList*) ()
#9 0x00000001000226e3 in juce::CoreAudioClasses::CoreAudioInternal::audioIOProc(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*) ()
#10 0x00007fff9319ad8d in HALC_ProxyIOContext::IOWorkLoop() ()
#11 0x00007fff93199667 in HALC_ProxyIOContext::IOThreadEntry(void*) ()
#12 0x00007fff9319938b in HALB_IOThread::Entry(void*) ()
#13 0x00000001007c2c50 in _pthread_body ()
#14 0x00000001007c2b9c in _pthread_start ()
#15 0x00000001007c2385 in thread_start ()

I’ll investigate this issue. I have been able to reproduce it with macOS 10.12 with the last XCode on the DSPDemo app, in Release mode at startup, if the optimization option in the Projucer has been set to something other than O0.

If I enable debugging in the Release exporter, I can see that the crash is located in the line 83 just when I try to access to the variable blockSize to set its new value, with a “EXC_BAD_ACCESS”. I don’t get how this is related with optimization however…

Looks like one or more uninitialised members, possibly causing memory corruption?

For example, this appears to be the only place that wantsStereo is set and in the demo it doesn’t seem to get called:

        case ChangeRequest::changeStereo:
            bool newWantsStereo = requestParameters[n];

            if (currentInfo.wantsStereo != newWantsStereo)
                changeLevel = jmax (1, changeLevel);

            currentInfo.wantsStereo = newWantsStereo;

and wantsStereo is uninitialised:

        double sampleRate = 0;
        bool wantsStereo;
        bool wantsTrimming;
        size_t impulseResponseSize;

That one is super simple being only a bool but I spotted it being randomly set to true or false in initializeConvolutionEngines()

Although unrelated, there is also quite a bit of memory allocation in the audio callback (resizing buffers, allocating an FFT object etc).

1 Like

The change with the optional normalization, the optional trim fix, and a fix for that specific issue is on the develop branch :wink:

Thanks for the report and for the tips to solve that issue !

By the way, about the memory allocation in the audio callback, it is supposed to happen only once at startup, and never after thanks to the use of threads.

I am having the same crash during AU validation, so how did you fix it ? I have set the same Projucer parameters as in the DSP Demo Plugin example, which validates fine.

In my case, I am preparing the audio material in an AudioSampleBuffer and assign it to a DSP block (before processing in convolution engine) , which is where the AU validation gets the crash. For intance, If I assign the processblock buffer to the block, it works fine.

The same code was working fine before, so I don’t understand what am I doing wrong.

BTW these anomalies happen only during the Logic AU validation. The same AU component works OK on Reaper , Live etc. I am testing with the latest Logic, OSX and Juce. I have noticed that at second or third try the AU validation passes.

Hello again,
I am getting this Logic AU validation crash; where the plugin works just fine as AU/VST on other DAWs.

The DSP process is exactly as in the demo example. I have started to get this after the last Logic update, OSX update etc. So I don’t know which one is the reason. You claim on this discussion that there is a solution , where is it ???

juce::dsp::ConvolutionEngine::initializeConvolutionEngine(juce::dsp::ConvolutionEngine::ProcessingInformation&, int) + 77
1 xxxxxxxxxxxxx 0x000000011106e47c juce::dsp::Convolution::Pimpl::initializeConvolutionEngines() + 124
2 xxxxxxxxxxxxx 0x0000000111070d90 juce::dsp::Convolution::Pimpl::processFifo() + 2864
3 xxxxxxxxxxxxx 0x000000011106cf0f juce::dsp::Convolution::Pimpl::processSamples(juce::dsp::AudioBlock const&,

Which version of JUCE did you get? The latest from the develop branch?

The latest official release.

would it be ok? to pick the dsp module at the develop branch and put it inside the official Juce modules folder ?

Mixing bits of code from different versions is a recipe for nasty problems. Why wouldn’t you just use the whole develop branch?

1 Like

Ok, I did that. No compile errors, neither validation. Just the convolution has stopped working, hence no convolution process, no convolved sound.

I have downloaded again a fresh Juce environment from juce.com. But this one has offered 20 C++ based Juce_VST_Wrapper errors, nothing related to the source code.

Being quite pissed off, I went through my backups applied the same code on a Juce backup from october, and now everything works. Logic validates instantly, and the convolution works.

I know these things could be performing a bit different in small demo examples rather then in 40000lines of code. But this was such a waste of time and effort, just again the SOURCE is the same. No one to blame,never forget to backup regularly !

I’m struggling a bit to understand what you mean about the errors you’re seeing in juce_VST_Wrapper, but here’s one thought, in case it helps: Often when we hear about people getting a new version of juce and hitting mysterious compile errors or subtle runtime errors that nobody else gets, the mistake turns out to be that they’re mixing up more than one juce folder. So e.g. they’re still including headers from their old juce folder but are pulling the source from a newer one, so are linking out-of-sync bits of juce code.

yes, you are right. However, on this last situation I was keeping just one JUCE folder, it is quite easy to mix the file folders , source locations etc. while using ProJucer. Always checking just in case with Find in Finder option where the relevant file is in reality. Maybe I still have a hidden Juce folder somewhere on the harddisk. But for the topic the issue with the convolution is still not clear to me, and I admit that the latest Logic or OSX update has difficulties, inconsistencies which never happened to me. The AU validation error caused by the convolution code was happening while checking the 512 frame test always. And often it happens that Logic validates the AU on a second attempt, is this rational ?

Speaking of leftover folders or old versions, I made it a habit each time I do a git checkout or git pull:

  • Projucer resave (can be done in a script as well: Projucer --resave MyProject.jucer
  • XCode build clean: CMD+SHIFT+K
  • XCode build: CMD+B

This rules out most of ghost effects.

I don’t know any details about the auval problem, sorry…

1 Like

I am typing now my detailed report on the convolution issue on a new topic !

I’m having some strange issue with the Convolution in the auvaltool as well, looking really hard to see if it is from my code, but looks quite nice and simple. Maybe might be something worth investigating:


Input Format: AudioStreamBasicDescription:  1 ch,  44100 Hz, 'lpcm' (0x00000029) 32-bit little- 
endian float, deinterleaved
Output Format: AudioStreamBasicDescription:  2 ch,  44100 Hz, 'lpcm' (0x00000029) 32-bit 
little-endian float, deinterleaved
Render Test at 512 frames
Slicing Render Test at 64 frames

Render Test at 64 frames, sample rate: 22050 Hz
Render Test at 137 frames, sample rate: 96000 Hz
Render Test at 4096 frames, sample rate: 48000 Hz
JUCE Assertion failure in juce_AudioBlock.h:337
JUCE Assertion failure in juce_AudioSampleBuffer.h:881
JUCE Assertion failure in juce_AudioSampleBuffer.h:881
JUCE Assertion failure in juce_AudioBlock.h:337
JUCE Assertion failure in juce_AudioSampleBuffer.h:881
JUCE Assertion failure in juce_AudioSampleBuffer.h:881
JUCE Assertion failure in juce_AudioBlock.h:337
JUCE Assertion failure in juce_AudioSampleBuffer.h:881
JUCE Assertion failure in juce_AudioSampleBuffer.h:275
Segmentation fault: 11

It goes quite right in the 2 first render tests, but then it hits an assertion. This is the stack when it hits the assertion:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
  * frame #0: 0x0000000105302ae9 Plugin`juce::dsp::AudioBlock<float>::getSubBlock(this=0x00007ffeefbfd4d0, newOffset=0, newLength=4096) const at juce_AudioBlock.h:337
frame #1: 0x00000001052e6d97 Plugin`juce::dsp::Convolution::Pimpl::processSamples(this=0x000000010386fe00, input=0x00007ffeefbfd8e8, output=0x00007ffeefbfd8e8) at juce_Convolution.cpp:714
frame #2: 0x00000001052e667d Plugin`juce::dsp::Convolution::processSamples(this=0x000000010386f008, input=0x00007ffeefbfd8e8, output=0x00007ffeefbfd8e8, isBypassed=false) at juce_Convolution.cpp:1208
frame #3: 0x000000010567d3ad Plugin`void juce::dsp::Convolution::process<juce::dsp::ProcessContextReplacing<float> >(this=0x000000010386f008, context=0x00007ffeefbfd8d8) at juce_Convolution.h:79
frame #4: 0x000000010567d16d Plugin`ss::LinearPhaseFilter::process(this=0x000000010386f000, context=0x00007ffeefbfd8d8) at LinearPhase.cpp:71
frame #5: 0x00000001056805a0 Plugin`ss::LinearPhaseCrossover::process(this=0x000000010386c2b0, context=0x00007ffeefbfdda0) at LinearPhaseCrossover.cpp:79
frame #6: 0x0000000105058e79 Plugin`PluginProcessor::processBlock(this=0x000000010386c180, buffer=0x0000000103820b58) at PluginProcessor.cpp:93
frame #7: 0x0000000105048695 Plugin`PluginAudioProcessor::processBlock(this=0x000000010386be00, buffer=0x0000000103820b58, midiMessages=0x0000000103820c90) at PluginProcessor.cpp:153
frame #8: 0x000000010501f8c8 Plugin`JuceAU::processBlock(this=0x0000000103820800, buffer=0x0000000103820b58, midiBuffer=0x0000000103820c90) at juce_AU_Wrapper.mm:1762
frame #9: 0x00000001050094f6 Plugin`JuceAU::Render(this=0x0000000103820800, ioActionFlags=0x00007ffeefbfe6ec, inTimeStamp=0x00007ffeefbfe7a0, nFrames=4096) at juce_AU_Wrapper.mm:1305
frame #10: 0x0000000105008e3d Plugin`AUBase::RenderBus(this=0x0000000103820800, ioActionFlags=0x00007ffeefbfe6ec, inTimeStamp=0x00007ffeefbfe7a0, inBusNumber=0, inNumberFrames=4096) at AUBase.h:332
frame #11: 0x000000010502a292 Plugin`AUBase::DoRenderBus(this=0x0000000103820800, ioActionFlags=0x00007ffeefbfe6ec, inTimeStamp=0x00007ffeefbfe7a0, inBusNumber=0, theOutput=0x0000000100216c90, inNumberFrames=4096, ioData=0x00000001006020b0) at AUBase.h:816
frame #12: 0x000000010502988b Plugin`AUBase::DoRender(this=0x0000000103820800, ioActionFlags=0x00007ffeefbfe6ec, inTimeStamp=0x00007ffeefbfe7a0, inBusNumber=0, inFramesToProcess=4096, ioData=0x00000001006020b0) at AUBase.cpp:1503
frame #13: 0x0000000105024bbe Plugin`CMgr_AudioUnitBaseRender(This=0x0000000103820800, ioActionFlags=0x00007ffeefbfe6ec, inTimeStamp=0x00007ffeefbfe7a0, inBusNumber=0, inNumberFrames=4096, ioData=0x00000001006020b0) at AUDispatch.cpp:433
frame #14: 0x000000010000ae25 autool`___lldb_unnamed_symbol59$$autool + 1011
frame #15: 0x000000010000c27a autool`___lldb_unnamed_symbol63$$autool + 2532
frame #16: 0x00000001000109f5 autool`___lldb_unnamed_symbol121$$autool + 1731
frame #17: 0x0000000100002169 autool`___lldb_unnamed_symbol3$$autool + 3191
frame #18: 0x000000010000141d autool`___lldb_unnamed_symbol2$$autool + 384
frame #19: 0x00007fff6c8b5015 libdyld.dylib`start + 1

Investigating myself, I’ve came to the conclusion that the “interpolationBuffer” member variable isn’t resized to 4096 samples, even though the “prepare()” method is called accordingly with the sample rate and buffer size changes.

This happens only on auvaltool. On standalone, Logic and Plugin Host tool, the assertion doesn’t get hit.

I’ll have a look next week, thanks for the report !

The last fix in the develop branch should solve this issue. Thanks @Fabian for having pushed the change !

Thanks @IvanC and @fabian for such a quick response and fix, you guys are rockstars!!

I was literally about to crack over this one… hahaha

Since we’re here, I had to change the Convolution IR switching LinearSmoothedValue by hand, bc it wasn’t smooth enough for my plugin. Would be nice to have a public method to do that! :slight_smile: