How to clear output buffers

I’m getting a loud feedback noise when opening our plugin in logic x.

We have 16 stereo output channels and one input and a side chain:

BusesProperties()
                     .withOutput ("Output 1", AudioChannelSet::stereo(), true)
                     .withOutput ("Output 2", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 3", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 4", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 5", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 6", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 7", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 8", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 9", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 10", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 11", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 12", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 13", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 14", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 15", AudioChannelSet::stereo(), false)
                     .withOutput ("Output 16", AudioChannelSet::stereo(), false)
                     .withInput("Input", AudioChannelSet::stereo(), true)
                     .withInput  ("Sidechain", AudioChannelSet::stereo()))

I found out that when i clear the output buffers like this i don’t get the feedback noise anymore:

auto busCount = getBusCount (false);
for (int i = 0; i < busCount; ++i)
{
    getBusBuffer(buffer, false, i).clear();
}

But it looks like I delete also the side chain signal when doing this. I don’t get any input signal anymore.

How can I zero all outputs before processing and also make sure that the sidechain input still works?

Where / when are you calling clear()?

A similar issue was reported recently:

The way that AudioUnits handle their channel remapping was tweaked a bit, but this can cause issues for plugins that were expecting unused input channels to be empty.

The boilerplate plugin project contains some code to clear unused output channels:

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

Do you have something like this in your processBlock?

I clear before i start processing. I also cleared the inputs with the code above.

Using this code and also clearing the side chain after getting the values removes the feedback noise.
But it looks like the side chain is routed to the main input channel instead of the side chain channel. I can hear the side chain in output 1, but I would expect it on output 2 (busIndex 1).

I’m using the following code to get the side chain channel bus:

AudioSampleBuffer sideChainInput  = getBusBuffer(buffer, true, 1);
float *sideChainL = sideChainInput.getWritePointer(0);
float *sideChainR = sideChainInput.getWritePointer(1);

I don’t have this problem with the VST3 build. Our plugin is an instrument (Aui). Is there a difference in how i set up a side chain for an instrument for logic x?

Edit: it looks like logic always takes the input as a sidechain (at least for instruments). The side chain also shows up when you only have one input bus. I will mix them together as a workaround before processing the input.

Hope there is no AU cache involved… otherwise, these results are maybe useless.

Are you explicitly copying the sidechain input to the output anywhere?

Your code to get the sidechain input looks correct, but in order to play it on a particular output, I think you’d also need to call getBusBuffer (buffer, false, busIndex) and then copy the channel contents across.

You could also try calling getChannelIndexInProcessBlockBuffer to see how the channels of input and output busses correspond to channels in the processBlock buffer.

No, i don’t see any code that does this. It looks like that logic takes the first input bus as a sidechain for instrument plugins (Auv2i) and the second bus (sidechain) contains only garbage.

That sounds like it could be a bug in the wrapper. I’ll need to investigate here to make sure.

That would be great. It looks like I can’t just mix them together because of the garbage the sidechain bus contains. I need to exclude the sidechain for the AUi with an ifdef in the worst case. Hope for another solution.

Here also my bus layout supported implementation:


bool TalCore::isBusesLayoutSupported (const BusesLayout& layout) const
{
    for (const auto& bus : layout.outputBuses)
        if (bus != AudioChannelSet::stereo())
            return false;

    for (const auto& bus : layout.inputBuses)
        if (bus != AudioChannelSet::stereo())
            return false;

    return true;
}

I found something related:

I’ve debugged this a bit, and it looks like Logic indeed provides a maximum of one input bus to instrument plugins. In testing, only the first input bus was able to successfully get input from the host.

However, remaining input buses should have their input cleared, so I was surprised to see garbage data on any of the input bus channels. This turned out to be a bug in the wrapper - I’ve got a fix on the way for this. With that change in place, I think you’ll be able to mix the inputs properly as you suggested earlier.

1 Like

Thanks for looking at this.

The garbage was the audio from the previous process call and it summed up. Setting the input channels to zero after reading the values fixed the problem and I’m now able to mix the two channels. This way I shouldn’t need any wrapper-specific code.

It works for me as it is for the moment.

Maybe this is related:

Looks like we always need to increment the plugin version when testing bus settings in logic to be sure that the settings are not cached.

Sorry for the long delay in getting this merged. I think this change should improve the buffer-clearing behaviour for AU plugins:

Thanks for the fix. I will try that.

The AU crashes when i start Logic X with latest development code:


Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x00007fff73c9633a __pthread_kill + 10
1   libsystem_pthread.dylib       	0x00007fff73d52e60 pthread_kill + 430
2   libsystem_c.dylib             	0x00007fff73c1d808 abort + 120
3   libsystem_malloc.dylib        	0x00007fff73d1350b malloc_vreport + 548
4   libsystem_malloc.dylib        	0x00007fff73d24d27 malloc_zone_error + 183
5   libsystem_malloc.dylib        	0x00007fff73d0b081 tiny_free_list_remove_ptr + 690
6   libsystem_malloc.dylib        	0x00007fff73d0a279 tiny_free_no_lock + 1018
7   libsystem_malloc.dylib        	0x00007fff73d09d8b free_tiny + 459
8   ch.toguaudioline.xxxxx      	0x00000001057684a5 AUBase::DeallocateIOBuffers() + 117
9   ch.toguaudioline.xxxxx      	0x00000001057688e4 AUBase::DoCleanup() + 36
10  ch.toguaudioline.xxxxx      	0x000000010575559b AUMethodUninitialize(void*) + 59
11  auvaltool                     	0x000000010191c80e 0x101911000 + 47118
12  auvaltool                     	0x0000000101920e79 0x101911000 + 65145
13  auvaltool                     	0x00000001019129ac 0x101911000 + 6572
14  auvaltool                     	0x0000000101911ccf 0x101911000 + 3279
15  libdyld.dylib                 	0x00007fff73b4ecc9 start + 1

It’s the AU version that crashes when the validator closes the plugin.

Which versions of Logic and macOS cause the problem? Are you running on Intel or Arm, and with Rosetta enabled or disabled?

I tried modifying the AudioPluginExample to use your implementation of isBusesLayoutSupported, added a sidechain, and then ensured that it was a synth plugin with midi in and out. I’m able to load this successfully in Logic 10.6.3 on macOS 10.15.7 (Intel).

Did you also create an AU instrument with that setting?

I don’t have the latest version of logic 10.6.1. But I was also able to reproduce a crash in reaper. Also, the stack trace does look different:

  1. Open a new AUi plugin instance.
  2. Delete the instance.
  3. Open another new instance
    → crash

Sometimes it crashes when I create the first AU instance. Other plugin formats don’t have the problem.

Crashed Thread:        0  reaper  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Application Specific Information:
Performing @selector(swellOnControlDoubleClick:) from sender REAPERSwell_listview 0x7ffa0709ba00
abort() called
REAPER(18035,0x10f50adc0) malloc: Incorrect checksum for freed object 0x7ffa07d8c000: probably modified after being freed.
Corrupt value: 0x0
 
Thread 0 Crashed:: reaper  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x00007fff73c9633a __pthread_kill + 10
1   libsystem_pthread.dylib       	0x00007fff73d52e60 pthread_kill + 430
2   libsystem_c.dylib             	0x00007fff73c1d808 abort + 120
3   libsystem_malloc.dylib        	0x00007fff73d1350b malloc_vreport + 548
4   libsystem_malloc.dylib        	0x00007fff73d24d27 malloc_zone_error + 183
5   libsystem_malloc.dylib        	0x00007fff73d0bced small_free_list_remove_ptr_no_clear + 1243
6   libsystem_malloc.dylib        	0x00007fff73d06a7f small_malloc_from_free_list + 366
7   libsystem_malloc.dylib        	0x00007fff73d062d8 small_malloc_should_clear + 279
8   libsystem_malloc.dylib        	0x00007fff73d060fc szone_malloc_should_clear + 120
9   libsystem_malloc.dylib        	0x00007fff73d06f59 malloc_zone_calloc + 99
10  com.apple.CoreGraphics        	0x00007fff39ee0757 CGSImageDataHandleCreate + 83
11  com.apple.CoreGraphics        	0x00007fff39edeabe img_data_lock + 6526
12  com.apple.CoreGraphics        	0x00007fff39ed966d CGSImageDataLock + 1433
13  com.apple.CoreGraphics        	0x00007fff39ed9096 RIPImageDataInitializeShared + 177
14  com.apple.CoreGraphics        	0x00007fff39ed8d53 RIPImageCacheGetRetained + 722
15  com.apple.CoreGraphics        	0x00007fff39ed8831 ripc_AcquireRIPImageData + 385
16  com.apple.CoreGraphics        	0x00007fff39ed7562 ripc_DrawImage + 1187
17  com.apple.AppKit              	0x00007fff3700952f __backing_store_DrawImage_block_invoke + 55
18  com.apple.AppKit              	0x00007fff3700844b backing_store_delegate + 893
19  com.apple.AppKit              	0x00007fff37047c91 backing_store_DrawImage + 509
20  com.apple.CoreGraphics        	0x00007fff39ed67c3 CGContextDrawImageWithOptions + 454
21  com.apple.CoreGraphics        	0x00007fff39ef527c CGContextDrawImages + 728
22  com.apple.coreui              	0x00007fff535303d7 DrawNinePartImageWithOperation + 5538
23  com.apple.coreui              	0x00007fff5352ed02 DrawNinePartElementFromRenditionWithOperation + 428

It does not crash anymore when I roll back the commit above in the clearChannels() method.

Ah, I wonder whether the buffers passed to the audio callback are smaller than the prepared size, so that the zeromem call is writing past the end of the allocated region.

Still haven’t had much luck reproducing the issue yet, but that’s my best guess at the moment.

I still haven’t been able to trigger the issue. Please could you try applying this patch on develop and check whether it resolves the issue for you?

channels.patch (4.0 KB)