Debugging with Reaper



  • I have gotten a couple crash reports with one of my plugins within Reaper, so I'm trying to sort out where the issue is happening. Maybe someone has been down this road before.

    When I launch the plugin, I'm getting a breakpoint before it loads at

    jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
    
        //==============================================================================
        /** Reset to a new sample rate and ramp length.
            @param sampleRate           The sample rate
            @param rampLengthInSeconds  The duration of the ramp in seconds
        */
        void reset (double sampleRate, double rampLengthInSeconds) noexcept
        {
            jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
            reset ((int) std::floor (rampLengthInSeconds * sampleRate));
        }
    

    The details in Visual Studio say

    rampLengthInSeconds   0.020000000000000000	double
    sampleRate  -1.0000000000000000	double
    
    this	0x000000000d2ffe30 {step=0.000000000 stepsToTarget=0 }	juce::SmoothedValue<float,juce::ValueSmoothingTypes::Linear> *
    

    I'm assuming a sample rate of -1 is causing the problem here?

    Hitting Continue gets me to the next break at

    jassert (newNumSamples >= 0);
    
        //==============================================================================
        /** Changes the buffer's size or number of channels.
    
            This can expand or contract the buffer's length, and add or remove channels.
    
            If keepExistingContent is true, it will try to preserve as much of the
            old data as it can in the new buffer.
    
            If clearExtraSpace is true, then any extra channels or space that is
            allocated will be also be cleared. If false, then this space is left
            uninitialised.
    
            If avoidReallocating is true, then changing the buffer's size won't reduce the
            amount of memory that is currently allocated (but it will still increase it if
            the new size is bigger than the amount it currently has). If this is false, then
            a new allocation will be done so that the buffer uses takes up the minimum amount
            of memory that it needs.
    
            Note that if keepExistingContent and avoidReallocating are both true, then it will
            only avoid reallocating if neither the channel count or length in samples increase.
    
            If the required memory can't be allocated, this will throw a std::bad_alloc exception.
        */
        void setSize (int newNumChannels,
                      int newNumSamples,
                      bool keepExistingContent = false,
                      bool clearExtraSpace = false,
                      bool avoidReallocating = false)
        {
            jassert (newNumChannels >= 0);
            jassert (newNumSamples >= 0);
    

    Visual studio details say:

    		avoidReallocating	false	bool
    		newNumChannels	2	int
    		newNumSamples	-1	int
    		this	0x000000000d2ffd08 {numChannels=0 size=0 allocatedBytes=0 ...}	juce::AudioBuffer<float> *
    

    After continuing, the plugin finally loads.

    Clicking on the sound card settings dialog will throw an exception:

    Exception thrown at 0x00007FFB549BFEBA (ntdll.dll) in reaper.exe: 0xC0000008: An invalid handle was specified.
    

    details

    		avoidReallocating	false	bool
    		newNumChannels	2	int
    		newNumSamples	-1	int
    		this	0x00000000689f16a8 {numChannels=0 size=0 allocatedBytes=0 ...}	juce::AudioBuffer<float> *
    

    Changing the buffer size for my ASIO device to 1024 causes a break at :

    jassert(ok);
    

    in

    bool LockHelpers::noMessageThreadBeyondInitialisation(const MainController* mc)
    {
    	bool ok = !isMessageThreadBeyondInitialisation(mc);
    
    	// If you hit this assertion, it means you have called a function
    	// from the message thread that you are not supposed to do
    	jassert(ok);
    
    	return ok;
    

    If I hit Continue a few times, it will pass the break point.

    After playing for a minute, I get a break at:

    jassert(numThisTime % HISE_EVENT_RASTER == 0);
    
    else if (buffer.getNumSamples() > HISE_MAX_PROCESSING_BLOCKSIZE)
    	{
    		int numChannels = buffer.getNumChannels();
    		int numToDo = buffer.getNumSamples();
    
    		auto ptrs = (float**)alloca(numChannels * sizeof(float*) * numChannels);
    		
    		memcpy(ptrs, buffer.getArrayOfWritePointers(), sizeof(float*) * numChannels);
    
    		int start = 0;
    
    		while (numToDo > 0)
    		{
    			auto numThisTime = jmin(numToDo, HISE_MAX_PROCESSING_BLOCKSIZE);
    
    			jassert(numThisTime % HISE_EVENT_RASTER == 0);
    
    			AudioSampleBuffer chunk(ptrs, numChannels, numThisTime);
    			
    			delayedMidiBuffer.clear();
    			delayedMidiBuffer.addEvents(midiMessages, start, numThisTime, -start);
    			
    			start += numThisTime;
    			numToDo -= numThisTime;
    
    			for (int i = 0; i < numChannels; i++)
    				ptrs[i] += numThisTime;
    
    			mc->processBlockCommon(chunk, delayedMidiBuffer);
    		}
    	}
    

    details:

    numThisTime	100	int
    numToDo	100	int
    
    this	0x000000004e164608 {pimpl={object=0x000000004db56760 {hostType {type=Reaper (25) } leakDetector861={...} } } ...}	hise::DelayedRenderer *
    
    pimpl	{object=0x000000004db56760 {hostType={type=Reaper (25) } leakDetector861={...} } }	juce::ScopedPointer<hise::DelayedRenderer::Pimpl>
    
    mc	0x000000004e164230 {deactivatedBecauseOfMemoryLimitation=false updater={suspendState=true parent={deactivatedBecauseOfMemoryLimitation=...} } ...}	hise::MainController * {hise::FrontendProcessor}
    

    If anyone has suggestions, I'm all ears.



  • Does it happen with the standalone version or just the plugin?



  • @d-healey I haven't tested standalone yet. I'm only getting reports from Reaper users about this one.
    Just to be sure, I should probably fire up Cubase and PT to check. 😼



  • The big issue I'm trying to track down is a Runtime error.. but I can't make it happen on my rig.

    runtime.jpg



  • In Cubase, I get some additional breakpoints:

    jassert (isPositiveAndBelow (sampleIndex, size));
    
        /** Returns a writeable pointer to one of the buffer's channels.
            For speed, this doesn't check whether the channel number or index are out of range,
            so be careful when using it!
            Note that if you're not planning on writing to the data, you should
            use getReadPointer instead.
        */
        Type* getWritePointer (int channelNumber, int sampleIndex) noexcept
        {
            jassert (isPositiveAndBelow (channelNumber, numChannels));
            jassert (isPositiveAndBelow (sampleIndex, size));
            isClear = false;
            return channels[channelNumber] + sampleIndex;
        }
    

    details

    -		this	0x000000002e92f110 {numChannels=2 size=0 allocatedBytes=0 ...}	juce::AudioBuffer<float> *
    
    		allocatedBytes	0	unsigned __int64
    +		channels	0x000000002e92f130 {0x000000005844f0f0 {-4.85710463e-17}}	float * *
    +		allocatedData	{data=0x0000000000000000 <NULL> }	juce::HeapBlock<char,1>
    +		preallocatedChannelSpace	0x000000002e92f130 {0x000000005844f0f0 {-4.85710463e-17}, 0x000000005844f8f0 {-4.85710463e-17}, 0x0000000000000000 {...}, ...}	float *[32]
    		isClear	false	bool
    		leakDetector1130	{...}	juce::LeakedObjectDetector<juce::AudioBuffer<float> >
    
    

    and

    jassert(sizeMatches && channelMatches);
    
    struct PointerWatcher
    {
    #if JUCE_DEBUG
    	PointerWatcher(ProcessData& data) :
    		dataToWatch(data),
    		c(data.numChannels),
    		size(data.size)
    	{
    		memcpy(reinterpret_cast<void*>(d), data.data, data.numChannels * sizeof(float*));
    	}
    
    	~PointerWatcher()
    	{
    		bool sizeMatches = dataToWatch.size = size;
    		bool channelMatches = dataToWatch.numChannels = c;
    
    		// You've managed to change the channel amount or block size during processing
    		jassert(sizeMatches && channelMatches);
    
    		for (int i = 0; i < c; i++)
    		{
    			if (dataToWatch.data[i] != d[i])
    			{
    				// You've managed to change the pointers during processing.
    				jassertfalse;
    			}
    		}
    	}
    

    Is it an issue with setting the channel amount at initialization?



  • Test it with standalone before anything else. Using a DAW may complicate the issue if the problem has nothing to do with the DAW, and using two DAWs could be even less helpful. If there is no problem with the standalone then we look at the plugin.



  • Here's what I'm seeing on a standalone:

    Initialising Standalone Wrapper
    Creating Device Manager
    Create Main Processor
    Loading embedded image data
    Loading embedded impulse responses
    Loading embedded other data
    Creating Frontend Processor
    Create Shared Cache Pool for AudioFilePool
    Create Shared Cache Pool for ImagePool
    Create Shared Cache Pool for SampleMapPool
    Create Shared Cache Pool for MidiFilePool
    Initialising MainController
    Checking license
    Load images
    Load embedded audio files
    Load samplemaps
    Load Midi Files
    Restoring main container
    Compiling all scripts
    JUCE Assertion failure in juce_smoothedvalue.h:248
    Test_Debug x64.exe has triggered a breakpoint.
    
    JUCE Assertion failure in juce_audiosamplebuffer.h:347
    Test_Debug x64.exe has triggered a breakpoint.
    
    Adding plugin parameters
    Restoring global settings
    Setting disk mode
    Samples are validated. Skipping reference check
    Loading samples
    Initialise Audio Driver...
    Audio Driver Default Initialisation
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\dsound.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\MMDevAPI.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\devobj.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\wdmaud.drv'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\ksuser.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\avrt.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\AudioSes.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\msacm32.drv'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\msacm32.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\midimap.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\ResourcePolicyClient.dll'. Symbols loaded.
    Error initialising with default settings: The input and output devices don't share a common sample rate!
    OK
    Creating Interface
    Creating Root Panel
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\d2d1.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\DWrite.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\d3d10warp.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\mscms.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\userenv.dll'. Symbols loaded.
    'Test_Debug x64.exe' (Win32): Loaded 'C:\Windows\System32\coloradapterclient.dll'. Symbols loaded.
    JUCE Assertion failure in juce_smoothedvalue.h:248
    Test_Debug x64.exe has triggered a breakpoint.
    
    Resizing interface
    Change scale factor
    The thread 0x4174 has exited with code 0 (0x0).
    The thread 0x4fe0 has exited with code 0 (0x0).
    The thread 0x4f5c has exited with code 0 (0x0).
    The thread 0x271c has exited with code 0 (0x0).
    

    It couldn't initialize the audio driver, but I'm not sure if that's an issue or not. It did, however, have the same 2 SampleRate breakpoints as exibited in the plugin version.

    JUCE Assertion failure in juce_smoothedvalue.h:248
    JUCE Assertion failure in juce_audiosamplebuffer.h:347
    


  • I don't know if this is part of the issue
    Error initialising with default settings: The input and output devices don't share a common sample rate!

    Is it a control on your interface that is triggering the failure in juce_smoothedvalue?



  • @d-healey said in Debugging with Reaper:

    Is it a control on your interface that is triggering the failure in juce_smoothedvalue?

    I'm dissecting the code now.



  • Making some progress. I've found the cause of the following breakpoint

    bool LockHelpers::noMessageThreadBeyondInitialisation(const MainController* mc)
    {
    	bool ok = !isMessageThreadBeyondInitialisation(mc);
    
    	// If you hit this assertion, it means you have called a function
    	// from the message thread that you are not supposed to do
    	jassert(ok);
    
    	return ok;
    
    }
    

    Here's a minimized snippet of the problem code:

    HiseSnippet 1034.3oc6WE0aaaCDlxIbsVaFaAn+.D7SJnYoxINoEqnctw1oyXMIF0YEaOUPKQawEJRAI515EDf8qZ+t1+fsiRxVzJNEAdnuM8fct66tiej7tOqLLQ5SSSkIHq5WNOlhr9F7n4BUX2PBSfFzCY8UXEMUgNYdLIMkFfrr150ZLq5aixd96e7DBmH7oktPn2IY9z2vhXpRuC67yLN+TR.8RVjQzs6LvWJ5J4xY.O1B6ghI9WQlROmnCqFFHQ+.lRlLRQ.xfr19DYv7QgxOJxi+crT1XNUazBMBJTt6Sk7.Mi0dQcCY7fgK1uoHnJCK28aku6eD9LV.ao+xSguKCvoLCyyCqZeN50xjdd2e5YYPusyo2N3Q9IrXUIhlaeMdfPQSlPfq.SZkGKplpFtqDhPn1OhbE8zDvXYFtG34smSKOucedC6F1S4xwDtyOISUiHQwb5agsDf.WPoJmOPRbJc+FxXJ24ENKJ9TppqLJVJ.C2lUhqYd8aX+jmziNgZVGGc6Ph4ZjEQlW3HMQAqQewTlftueBERHC4hw+N0W4pKakv2OU+YlsawFSWz.cc7xXwsRflGdWBmOF58bmLS3qXRg6tMrutgc8rbCTOtEjdc1DGWsiW3bD.6.OWm+k9ox1VWZ2lJ5mTM2q3HwQC9CNMcdbkiYMQWTlaukjw46nEQcSC6a184KHpiTbtTQu.Xr8010suw1oJzjIqESe6kH4bcoWCbwhd2I5JlEMllrGbwwmQWFHzyt5PA9tGJLmY8yalLBTJFHXpKhoh6ZTAUzAB+0uLnGQQzSOE9f3hoIJllBV8ne.jkxmkpi6QSuBNVyhsnsEY8.UFZihIsrKQDKHa1a0qVzmJkvFzY9Ri+7Uc9HKPEVh9acBorogFRguritkPq2rROQ6m444ABk2ZvGjejAy3D0pZRZg3B.3NYkge8.tHkolaJT+EQn59R2cvCYJ+v0y2Zqguvs3WZ9VH62.2exDPMojraiO8W2TM90+SPd2SMd6hNOX80D4awulZnQrNAdq+BWNWFmPiIIzKkC4j4toKSbOmwbo+UiX+gdFMWBYU4mRU1olqHH3TYzONmymnqmqeHQHn7zMQg.u5Q5CteGoqpG.urf4NtTlvjkkAujTktVSGxakyTLwzyHpDFLkuy4yhFAubhOsawtE3t0C0BO41dZacC6HpHHy3efmBvVZaqBvVU.OXgsF7fJfGZBdXEv1lfsq.djI3QU.O1D73JfO0Dbow+QE1++.8VGnQD+D468yaF0RAOLyCzRJxdq253yz1NsPYSMPdXu88PQvag9dees.02ChaqOmC1fbNbCxo8FjyQaPNGuA47zMHmm8YyQ+O.7pYJYT9OdANF1OaTvxpuf.BUYZxn+Ef8sq2H
    

    The code uses a scriptFX module to collect the Host sample rate and passes that to a global variable. I'm using a deferred callback to set the text of a label to the global variable.

    Here's a thread where we originally discussed this method:
    https://forum.hise.audio/topic/2054/convolution-gain-louder-at-higher-sample-rates/18



  • Does the problem occur if you don't defer the callback?

    Oh never mind, I just looked at the thread you linked to. Christoph suggested deferring the on init callback. Have you tried that?



  • @d-healey said in Debugging with Reaper:

    Christoph suggested deferring the on init callback. Have you tried that?

    I believe that's what I'm doing. onInit looks like this:

    Content.makeFrontInterface(200, 100);
    
    global HostSampleRate;
    const var SampleRateLabel = Content.getComponent("SampleRateLabel");
    
    
    //Defer SampleRate Timer
    const var DeferTimerStart = Engine.createTimerObject();
    DeferTimerStart.startTimer(100);
    var dt = 0;
    
    DeferTimerStart.setTimerCallback(function()
    {
    	dt = dt+1;
    	if (dt == 5)
        {
            SampleRateLabel.set("text","Sample Rate: " + HostSampleRate);
            DeferTimerStart.stopTimer();
        }
    });
    


  • Synth.deferCallbacks(true); is what you need. It will defer all callbacks in that script (which is what you generally want in an interface script).



  • just posting some notes here for when I forget what I did 2 weeks ago 🙃

    Separate MIDI processing logic from Interface scripts
    Although both things can be achieved with the same module (the Script Processor), it can be considered as good practice to keep these two things separated. The reason is (apart from the usual encapsulation benefits) a huge performance increase if you defer the interface script.
    
    Deferring a script means running all callbacks that are triggered by MIDI messages or the timer on the message thread instead of the audio thread. It comes with some trade-offs (you can't change the midi message with a deferred callback or rely on the accuracy of the timer), but since it is not running on the audio thread, you can do (almost) whatever you want in any of the callbacks without having to risk a drop out (in the worst case, the interface will get laggy).
    
    Deferring a script is simple: just call
    
    Synth.deferCallbacks(true);
    and from then, the callbacks will stay away from the audio thread.
    
    These are some things that heavily rely on the deferred callback feature:
    
    setting text values of labels according to MIDI input
    using the timer for animation purposes
    heavy calculations from MIDI input
    




  • @d-healey Synth.deferCallbacks(true); worked like a charm!

    Next up: While playing audio in a loop, I'm getting a breakpoint at:

    JUCE Assertion failure in customdatacontainers.h:933
    
    	/** Adds an element to the queue. If it fails because the queue is full, it throws an assertion and return false. */
    	bool push(const ElementType& newElement)
    	{
    		const bool ok = queue.try_enqueue(std::move(newElement));
    		jassert_skip_unit_test(ok);
    		return ok;
    	}
    


  • Looks like something to do with arrays perhaps...?



  • I forgot to mention... Standalone loads properly after deferring the callback. I have now moved on to vst3/Reaper testing

    @Christoph-Hart It throws this error continuously while audio is playing.



  • @d-healey The main header reads:

    /** A wrapper around moodycamels ReaderWriterQueue with more JUCE like interface and some assertions. */
    

    I'm thinking it's not array data.



  • @dustbro First time I've heard of moodycamels! Something to do with this I think - https://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++


Log in to reply
 

7
Online

703
Users

2.8k
Topics

23.7k
Posts