JuceDemoPlugin crash on quit in Logic 8

I’m having a problem with a crash on quiting Logic 8.0.2 with my plugin loaded.

I can reproduce this with the JuceDemoPlugin using the current tip [1.53.8]. My steps are:
[list]
[]Launch Logic[/]
[]New project —> Empty Project[/]
[]Add a stereo track[/]
[]Add the Juce Demo Plugin as an insert on the track[/]
[]Quit Logic[/]
[]Don’t save[/][/list]

It crashes in the entry point stuff while Logic closes 9 times out of 10 (or so). The important bits seem to be:

[code]Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000041008020
Crashed Thread: 5

Application Specific Information:
objc_msgSend() selector name: filterBeingDeleted:
[/code]

Thread 5 Crashed: 0 libobjc.A.dylib 0x93e12eec objc_msgSend + 44 1 ...rialsoftware.JuceDemoPlugin 0x2a3f09cb ComponentBase::ComponentEntryDispatch(ComponentParameters*, ComponentBase*) + 67 2 ...rialsoftware.JuceDemoPlugin 0x2a3eeca2 AUBase::ComponentEntryDispatch(ComponentParameters*, AUBase*) + 1270 3 ...rialsoftware.JuceDemoPlugin 0x2a3e44e7 ComponentEntryPoint<JuceAU>::Dispatch(ComponentParameters*, JuceAU*) + 170 4 ...ple.CoreServices.CarbonCore 0x97416ce5 CallComponentDispatch + 29 5 ...ple.CoreServices.CarbonCore 0x9741706e CallComponentClose + 43 6 ...ple.CoreServices.CarbonCore 0x97416f97 CloseComponentInternal(ComponentInstanceRecord*) + 101 7 ...ple.CoreServices.CarbonCore 0x97416f18 CloseComponent + 46 8 com.apple.logic.pro 0x0040edf8 0x1000 + 4251128 9 com.apple.logic.pro 0x0010f258 0x1000 + 1106520 10 com.apple.logic.pro 0x000c0531 0x1000 + 783665 11 com.apple.logic.pro 0x00027fca 0x1000 + 159690 12 com.apple.logic.pro 0x00029283 0x1000 + 164483 13 com.apple.logic.pro 0x000299f7 0x1000 + 166391 14 com.apple.logic.pro 0x0008bbcd 0x1000 + 568269 15 com.apple.logic.pro 0x0008bd5a 0x1000 + 568666 16 com.apple.logic.pro 0x002b3483 0x1000 + 2827395 17 com.apple.logic.pro 0x002b3552 0x1000 + 2827602 18 libSystem.B.dylib 0x92ad61fb _sigtramp + 43 19 libSystem.B.dylib 0xffff0269 __spin_lock + 9 20 ...opellerheads.rewire.library 0x22ec9773 RWPUnregisterDeviceImp + 132049 21 ...opellerheads.rewire.library 0x22ec97cf RWPUnregisterDeviceImp + 132141 22 ...opellerheads.rewire.library 0x22ec94d3 RWPUnregisterDeviceImp + 131377 23 ...opellerheads.rewire.library 0x22eae7bb RWPUnregisterDeviceImp + 21529 24 ...opellerheads.rewire.library 0x22ec9a5c RWPUnregisterDeviceImp + 132794 25 libSystem.B.dylib 0x92a9d81d _pthread_start + 345 26 libSystem.B.dylib 0x92a9d6a2 thread_start + 34 ...

This happens in both Debug and Release builds.

When testing my own plugin it crashes in the same way, in the attempted obj-c filterBeingDeleted: message. Thinking it might be the exported symbols issue I’ve stripped the plugin as per the instructions. It still crashes but the report says it then crashes in the VST entry point stuff instead… in Logic! Hmm that sounds weird. (I am building the AU and VST versions.)

In the JuceDemoPlugin it crashes slightly differently when stripped:

Thread 5 Crashed: 0 libobjc.A.dylib 0x93e12ed7 objc_msgSend + 23 1 ...rialsoftware.JuceDemoPlugin 0x2943e9cb JuceDemoProjectAUEntry + 59618 2 ...rialsoftware.JuceDemoPlugin 0x2943cca2 JuceDemoProjectAUEntry + 52153 3 ...rialsoftware.JuceDemoPlugin 0x294324e7 JuceDemoProjectAUEntry + 9214 4 ...ple.CoreServices.CarbonCore 0x97416ce5 CallComponentDispatch + 29 5 ...ple.CoreServices.CarbonCore 0x9741706e CallComponentClose + 43 6 ...ple.CoreServices.CarbonCore 0x97416f97 CloseComponentInternal(ComponentInstanceRecord*) + 101 7 ...ple.CoreServices.CarbonCore 0x97416f18 CloseComponent + 46 ...

Attached to the debugger using the Debug build I get whole load of leaked object reports (so many it looks like it leaked everything! I can post the full list if necessary) then it crashes with EXC_BAD_ACCESS but in a different place, although this looks similar to steps 19-26 in the crash report above:

#0 0xffff0269 in __spin_lock #1 0x92a99d92 in pthread_mutex_trylock #2 0x230eb773 in RWPUnregisterDeviceImp #3 0x230eb7cf in RWPUnregisterDeviceImp #4 0x230eb4d3 in RWPUnregisterDeviceImp #5 0x230d07bb in RWPUnregisterDeviceImp #6 0x230eba5c in RWPUnregisterDeviceImp #7 0x92a9d81d in _pthread_start #8 0x92a9d6a2 in thread_start

It seems to be something to do with rewire but I’m not using that and can’t see if it can be disabled (I don’t think it can be).

Looks like it might be calling an obj-c object after unloading the DLL that contains it. I’ve seen and worked-around similar crap in VSTs, it’s normally caused when the host closes the plugin window, and then unloads the DLL, but somewhere in the system, that NSWindow still has an event or something hanging around referencing it, which then gets delivered after the DLL has gone, with obvious nasty results.

Haven’t time to investigate right now, this is probably quite a messy thing to debug, but will do so as soon as I can.

OK thanks. I see if I can find a workaround.

Come to think of it I haven’t seen it crash like this with the plugin editor window already closed when issuing the quit command so your suggestion sounds very likely.

Cheers,

I did a bit of hacking…

I wondered if the activePlugins and activeUIs arrays were getting out of sync and being accessed from different thread during the Logic quit.

Strangely this bodge doesn’t crash but will probably cause other problems:

http://www.cems.uwe.ac.uk/~mgrobins/juce_AU_Wrapper.mm

I did a typo in the JuceAU() constructor and enter() the CriticalSection twice rather than exit() after the array access. BUT changing the erroneous enter() to exit() makes it crash again. Perhaps this is a hint… or just some change to the binary that makes the crash less likely…

Hmm, I suspect that maybe it doesn’t crash because whatever thread was crashing is instead stuck, infinitely waiting for the lock to be freed, and logic probably just kills that thread when it shuts down.

I doubt if the array is the problem, it’s much more likely to be related to lingering UI objects. Do you know if it’s using a cocoa or carbon UI when it crashes? Might be worth trying a build without carbon enabled?

Yes I tried it with BUILD_AU_CARBON_UI set to 0 (is JucePluginCharacteristics.h a good place for that?). The crash is the same.

Maybe dealloc is not getting called in JuceUIViewClass when the app quits?

So, it never removes itself from the activeUIs array and when ~JuceAU() gets round to calling filterDeingDeleted: on the items in activeUIs they’re no longer valid?

If it is dealloc not getting called, how about registering for NSApplicationWillTerminateNotification:

juce_AU_Wrapper.mm

[code]/*
/*

This file is part of the JUCE library - "Jules’ Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.


JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.

JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.


To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.

==============================================================================
*/

#include <Cocoa/Cocoa.h>
#include <AudioUnit/AUCocoaUIView.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioUnitUtilities.h>
#include “AUMIDIEffectBase.h”
#include “MusicDeviceBase.h”
#include “…/juce_IncludeCharacteristics.h”

#if JucePlugin_Build_AU

/** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as
well as ones that can open a cocoa view. If this is enabled, you’ll need to also add the AUCarbonBase
files to your project.
*/
#ifndef BUILD_AU_CARBON_UI
#define BUILD_AU_CARBON_UI 1
#endif

#ifdef LP64
#undef BUILD_AU_CARBON_UI // (not possible in a 64-bit build)
#endif

#if BUILD_AU_CARBON_UI
#undef Button
#include "AUCarbonViewBase.h"
class JuceAUView;
#endif

#include “…/juce_PluginHeaders.h”
#include “…/juce_PluginHostType.h”

//==============================================================================
#define juceFilterObjectPropertyID 0x1a45ffe9
static Array<void*> activePlugins, activeUIs;

static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
static const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs);

#if JucePlugin_IsSynth
#define JuceAUBaseClass MusicDeviceBase
#else
#define JuceAUBaseClass AUMIDIEffectBase
#endif

//==============================================================================
/** Somewhere in the codebase of your plugin, you need to implement this function
and make it create an instance of the filter subclass that you’re building.
/
extern AudioProcessor
JUCE_CALLTYPE createPluginFilter();

//==============================================================================
#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d
#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d)
#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JucePlugin_AUExportPrefix)

#define JuceUICreationClass JucePlugin_AUCocoaViewClassName
#define JuceUIViewClass MakeObjCClassName(JuceUIViewClass)

class JuceAU;
class EditorCompHolder;

//==============================================================================
@interface JuceUICreationClass : NSObject
{
}

  • (JuceUICreationClass*) init;
  • (void) dealloc;
  • (unsigned) interfaceVersion;
  • (NSString *) description;
  • (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
    withSize: (NSSize) inPreferredSize;
    @end

//==============================================================================
@interface JuceUIViewClass : NSView
{
AudioProcessor* filter;
JuceAU* au;
EditorCompHolder* editorComp;
BOOL active;
}

  • (JuceUIViewClass*) initWithFilter: (AudioProcessor*) filter
    withAU: (JuceAU*) au
    withComponent: (AudioProcessorEditor*) editorComp;

  • (void) applicationWillTerminate:(NSNotification *)aNotification;

  • (void) willClose;

  • (void) dealloc;

  • (void) viewDidMoveToWindow;

  • (BOOL) mouseDownCanMoveWindow;

  • (void) filterBeingDeleted: (JuceAU*) au_;

  • (void) deleteEditor;

@end

//==============================================================================
class JuceAU : public JuceAUBaseClass,
public AudioProcessorListener,
public AudioPlayHead,
public ComponentListener
{
public:
//==============================================================================
JuceAU (AudioUnit component)
#if JucePlugin_IsSynth
: MusicDeviceBase (component, 0, 1),
#else
: AUMIDIEffectBase (component),
#endif
bufferSpace (2, 16),
prepared (false)
{
if (activePlugins.size() + activeUIs.size() == 0)
{
#if BUILD_AU_CARBON_UI
NSApplicationLoad();
#endif

        initialiseJuce_GUI();
    }

    juceFilter = createPluginFilter();
    jassert (juceFilter != 0);

    juceFilter->setPlayHead (this);
    juceFilter->addListener (this);

    Globals()->UseIndexedParameters (juceFilter->getNumParameters());

	
    activePlugins.add (this);
	
    
	zerostruct (auEvent);
    auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();
    auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
    auEvent.mArgument.mParameter.mElement = 0;
}

~JuceAU()
{		
    for (int i = activeUIs.size(); --i >= 0;)
		[((JuceUIViewClass*) activeUIs.getUnchecked(i)) filterBeingDeleted: this];
				
    juceFilter = 0;

    jassert (activePlugins.contains (this));
    activePlugins.removeValue (this);

    if (activePlugins.size() + activeUIs.size() == 0)
        shutdownJuce_GUI();
}

//==============================================================================
ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
                                 AudioUnitScope inScope,
                                 AudioUnitElement inElement,
                                 UInt32& outDataSize,
                                 Boolean& outWritable)
{
    if (inScope == kAudioUnitScope_Global)
    {
        if (inID == juceFilterObjectPropertyID)
        {
            outWritable = false;
            outDataSize = sizeof (void*) * 2;
            return noErr;
        }
        else if (inID == kAudioUnitProperty_OfflineRender)
        {
            outWritable = true;
            outDataSize = sizeof (UInt32);
            return noErr;
        }
        else if (inID == kMusicDeviceProperty_InstrumentCount)
        {
            outDataSize = sizeof (UInt32);
            outWritable = false;
            return noErr;
        }
        else if (inID == kAudioUnitProperty_CocoaUI)
        {
          #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
            // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
            if (PlatformUtilities::getOSXMinorVersionNumber() > 4)
          #endif
            {
                outDataSize = sizeof (AudioUnitCocoaViewInfo);
                outWritable = true;
                return noErr;
            }
        }
    }

    return JuceAUBaseClass::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}

ComponentResult GetProperty (AudioUnitPropertyID inID,
                             AudioUnitScope inScope,
                             AudioUnitElement inElement,
                             void* outData)
{
    if (inScope == kAudioUnitScope_Global)
    {
        if (inID == juceFilterObjectPropertyID)
        {
            ((void**) outData)[0] = (void*) static_cast <AudioProcessor*> (juceFilter);
            ((void**) outData)[1] = (void*) this;
            return noErr;
        }
        else if (inID == kAudioUnitProperty_OfflineRender)
        {
            *(UInt32*) outData = (juceFilter != 0 && juceFilter->isNonRealtime()) ? 1 : 0;
            return noErr;
        }
        else if (inID == kMusicDeviceProperty_InstrumentCount)
        {
            *(UInt32*) outData = 1;
            return noErr;
        }
        else if (inID == kAudioUnitProperty_CocoaUI)
        {
          #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
            // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
            if (PlatformUtilities::getOSXMinorVersionNumber() > 4)
          #endif
            {
                JUCE_AUTORELEASEPOOL

                AudioUnitCocoaViewInfo* info = (AudioUnitCocoaViewInfo*) outData;

                const File bundleFile (File::getSpecialLocation (File::currentApplicationFile));
                NSString* bundlePath = [NSString stringWithUTF8String: (const char*) bundleFile.getFullPathName().toUTF8()];
                NSBundle* b = [NSBundle bundleWithPath: bundlePath];

                info->mCocoaAUViewClass[0] = (CFStringRef) [[[JuceUICreationClass class] className] retain];
                info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [b bundlePath]] retain];

                return noErr;
            }
        }
    }

    return JuceAUBaseClass::GetProperty (inID, inScope, inElement, outData);
}

ComponentResult SetProperty (AudioUnitPropertyID inID,
                             AudioUnitScope inScope,
                             AudioUnitElement inElement,
                             const void* inData,
                             UInt32 inDataSize)
{
    if (inScope == kAudioUnitScope_Global && inID == kAudioUnitProperty_OfflineRender)
    {
        if (juceFilter != 0)
            juceFilter->setNonRealtime ((*(UInt32*) inData) != 0);

        return noErr;
    }

    return JuceAUBaseClass::SetProperty (inID, inScope, inElement, inData, inDataSize);
}

ComponentResult SaveState (CFPropertyListRef* outData)
{
    ComponentResult err = JuceAUBaseClass::SaveState (outData);

    if (err != noErr)
        return err;

    jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());

    CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;

    if (juceFilter != 0)
    {
        MemoryBlock state;
        juceFilter->getCurrentProgramStateInformation (state);

        if (state.getSize() > 0)
        {
            CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) state.getData(), state.getSize());
            CFDictionarySetValue (dict, CFSTR("jucePluginState"), ourState);
            CFRelease (ourState);
        }
    }

    return noErr;
}

ComponentResult RestoreState (CFPropertyListRef inData)
{
    ComponentResult err = JuceAUBaseClass::RestoreState (inData);

    if (err != noErr)
        return err;

    if (juceFilter != 0)
    {
        CFDictionaryRef dict = (CFDictionaryRef) inData;
        CFDataRef data = 0;

        if (CFDictionaryGetValueIfPresent (dict, CFSTR("jucePluginState"),
                                           (const void**) &data))
        {
            if (data != 0)
            {
                const int numBytes = (int) CFDataGetLength (data);
                const JUCE_NAMESPACE::uint8* const rawBytes = CFDataGetBytePtr (data);

                if (numBytes > 0)
                    juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);
            }
        }
    }

    return noErr;
}

UInt32 SupportedNumChannels (const AUChannelInfo** outInfo)
{
    // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
    // value in your JucePluginCharacteristics.h file..
    jassert (numChannelConfigs > 0);

    if (outInfo != 0)
    {
        *outInfo = channelInfo;

        for (int i = 0; i < numChannelConfigs; ++i)
        {
          #if JucePlugin_IsSynth
            channelInfo[i].inChannels = 0;
          #else
            channelInfo[i].inChannels = channelConfigs[i][0];
          #endif
            channelInfo[i].outChannels = channelConfigs[i][1];
        }
    }

    return numChannelConfigs;
}

//==============================================================================
ComponentResult GetParameterInfo (AudioUnitScope inScope,
                                  AudioUnitParameterID inParameterID,
                                  AudioUnitParameterInfo& outParameterInfo)
{
    const int index = (int) inParameterID;

    if (inScope == kAudioUnitScope_Global
         && juceFilter != 0
         && index < juceFilter->getNumParameters())
    {
        outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable
                                  | kAudioUnitParameterFlag_IsReadable
                                  | kAudioUnitParameterFlag_HasCFNameString;

        const String name (juceFilter->getParameterName (index));

        // set whether the param is automatable (unnamed parameters aren't allowed to be automated)
        if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
            outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;

        if (juceFilter->isMetaParameter (index))
            outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;

        AUBase::FillInParameterName (outParameterInfo,
                                     PlatformUtilities::juceStringToCFString (name),
                                     false);

        outParameterInfo.minValue = 0.0f;
        outParameterInfo.maxValue = 1.0f;
        outParameterInfo.defaultValue = 0.0f;
        outParameterInfo.unit = kAudioUnitParameterUnit_Generic;

        return noErr;
    }
    else
    {
        return kAudioUnitErr_InvalidParameter;
    }
}

ComponentResult GetParameter (AudioUnitParameterID inID,
                              AudioUnitScope inScope,
                              AudioUnitElement inElement,
                              Float32& outValue)
{
    if (inScope == kAudioUnitScope_Global && juceFilter != 0)
    {
        outValue = juceFilter->getParameter ((int) inID);
        return noErr;
    }

    return AUBase::GetParameter (inID, inScope, inElement, outValue);
}

ComponentResult SetParameter (AudioUnitParameterID inID,
                              AudioUnitScope inScope,
                              AudioUnitElement inElement,
                              Float32 inValue,
                              UInt32 inBufferOffsetInFrames)
{
    if (inScope == kAudioUnitScope_Global && juceFilter != 0)
    {
        juceFilter->setParameter ((int) inID, inValue);
        return noErr;
    }

    return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);
}

//==============================================================================
ComponentResult Version()                   { return JucePlugin_VersionCode; }
bool SupportsTail()                         { return true; }
Float64 GetTailTime()                       { return (JucePlugin_TailLengthSeconds); }
Float64 GetSampleRate()                     { return GetOutput(0)->GetStreamFormat().mSampleRate; }

Float64 GetLatency()
{
    jassert (GetSampleRate() > 0);

    if (GetSampleRate() <= 0)
        return 0.0;

    return juceFilter->getLatencySamples() / GetSampleRate();
}

//==============================================================================

#if BUILD_AU_CARBON_UI
int GetNumCustomUIComponents() { return 1; }

void GetUIComponentDescs (ComponentDescription* inDescArray)
{
    inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
    inDescArray[0].componentSubType = JucePlugin_AUSubType;
    inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;
    inDescArray[0].componentFlags = 0;
    inDescArray[0].componentFlagsMask = 0;
}

#endif

//==============================================================================
bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
{
    info.timeSigNumerator = 0;
    info.timeSigDenominator = 0;
    info.timeInSeconds = 0;
    info.editOriginTime = 0;
    info.ppqPositionOfLastBarStart = 0;
    info.isPlaying = false;
    info.isRecording = false;

    switch (lastSMPTETime.mType)
    {
        case kSMPTETimeType24:
            info.frameRate = AudioPlayHead::fps24;
            break;

        case kSMPTETimeType25:
            info.frameRate = AudioPlayHead::fps25;
            break;

        case kSMPTETimeType30Drop:
            info.frameRate = AudioPlayHead::fps30drop;
            break;

        case kSMPTETimeType30:
            info.frameRate = AudioPlayHead::fps30;
            break;

        case kSMPTETimeType2997:
            info.frameRate = AudioPlayHead::fps2997;
            break;

        case kSMPTETimeType2997Drop:
            info.frameRate = AudioPlayHead::fps2997drop;
            break;

        //case kSMPTETimeType60:
        //case kSMPTETimeType5994:
        default:
            info.frameRate = AudioPlayHead::fpsUnknown;
            break;
    }

    if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
    {
        info.ppqPosition = 0;
        info.bpm = 0;
    }

    UInt32 outDeltaSampleOffsetToNextBeat;
    double outCurrentMeasureDownBeat;
    float num;
    UInt32 den;

    if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
                                     &outCurrentMeasureDownBeat) == noErr)
    {
        info.timeSigNumerator = (int) num;
        info.timeSigDenominator = den;
        info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
    }

    double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;
    Boolean playing, playchanged, looping;

    if (CallHostTransportState (&playing,
                                &playchanged,
                                &outCurrentSampleInTimeLine,
                                &looping,
                                &outCycleStartBeat,
                                &outCycleEndBeat) == noErr)
    {
        info.isPlaying = playing;
        info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate();
    }

    return true;
}

void sendAUEvent (const AudioUnitEventType type, const int index)
{
    if (AUEventListenerNotify != 0)
    {
        auEvent.mEventType = type;
        auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;
        AUEventListenerNotify (0, 0, &auEvent);
    }
}

void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
{
    sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);
}

void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index)
{
    sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);
}

void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index)
{
    sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
}

void audioProcessorChanged (AudioProcessor*)
{
    // xxx is there an AU equivalent?
}

bool StreamFormatWritable (AudioUnitScope inScope, AudioUnitElement element)
{
    return ! IsInitialized();
}

// (these two slightly different versions are because the definition changed between 10.4 and 10.5)
ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID&, UInt32, const MusicDeviceNoteParams&) { return noErr; }
ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) { return noErr; }
ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32)   { return noErr; }

//==============================================================================
ComponentResult Initialize()
{
  #if ! JucePlugin_IsSynth
    const int numIns = GetInput(0) != 0 ? GetInput(0)->GetStreamFormat().mChannelsPerFrame : 0;
  #endif
    const int numOuts = GetOutput(0) != 0 ? GetOutput(0)->GetStreamFormat().mChannelsPerFrame : 0;

    bool isValidChannelConfig = false;

    for (int i = 0; i < numChannelConfigs; ++i)
      #if JucePlugin_IsSynth
        if (numOuts == channelConfigs[i][1])
      #else
        if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1])
      #endif
            isValidChannelConfig = true;

    if (! isValidChannelConfig)
        return kAudioUnitErr_FormatNotSupported;

    JuceAUBaseClass::Initialize();
    prepareToPlay();
    return noErr;
}

void Cleanup()
{
    JuceAUBaseClass::Cleanup();

    if (juceFilter != 0)
        juceFilter->releaseResources();

    bufferSpace.setSize (2, 16);
    midiEvents.clear();
    incomingEvents.clear();
    prepared = false;
}

ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement)
{
    if (! prepared)
        prepareToPlay();

    if (juceFilter != 0)
        juceFilter->reset();

    return JuceAUBaseClass::Reset (inScope, inElement);
}

void prepareToPlay()
{
    if (juceFilter != 0)
    {
      #if ! JucePlugin_IsSynth
        juceFilter->setPlayConfigDetails (GetInput(0)->GetStreamFormat().mChannelsPerFrame,
      #else
        juceFilter->setPlayConfigDetails (0,
      #endif
                                          GetOutput(0)->GetStreamFormat().mChannelsPerFrame,
                                          GetSampleRate(),
                                          GetMaxFramesPerSlice());

        bufferSpace.setSize (juceFilter->getNumInputChannels() + juceFilter->getNumOutputChannels(),
                             GetMaxFramesPerSlice() + 32);

        juceFilter->prepareToPlay (GetSampleRate(),
                                   GetMaxFramesPerSlice());

        midiEvents.ensureSize (2048);
        midiEvents.clear();
        incomingEvents.ensureSize (2048);
        incomingEvents.clear();

        channels.calloc (jmax (juceFilter->getNumInputChannels(),
                               juceFilter->getNumOutputChannels()) + 4);

        prepared = true;
    }
}

ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags,
                        const AudioTimeStamp& inTimeStamp,
                        UInt32 nFrames)
{
    lastSMPTETime = inTimeStamp.mSMPTETime;

  #if ! JucePlugin_IsSynth
    return JuceAUBaseClass::Render (ioActionFlags, inTimeStamp, nFrames);
  #else
    // synths can't have any inputs..
    AudioBufferList inBuffer;
    inBuffer.mNumberBuffers = 0;

    return ProcessBufferLists (ioActionFlags, inBuffer, GetOutput(0)->GetBufferList(), nFrames);
  #endif
}

OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags,
                             const AudioBufferList& inBuffer,
                             AudioBufferList& outBuffer,
                             UInt32 numSamples)
{
    if (juceFilter != 0)
    {
        jassert (prepared);

        int numOutChans = 0;
        int nextSpareBufferChan = 0;
        bool needToReinterleave = false;
        const int numIn = juceFilter->getNumInputChannels();
        const int numOut = juceFilter->getNumOutputChannels();

        unsigned int i;
        for (i = 0; i < outBuffer.mNumberBuffers; ++i)
        {
            AudioBuffer& buf = outBuffer.mBuffers[i];

            if (buf.mNumberChannels == 1)
            {
                channels [numOutChans++] = (float*) buf.mData;
            }
            else
            {
                needToReinterleave = true;

                for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numOutChans < numOut; ++subChan)
                    channels [numOutChans++] = bufferSpace.getSampleData (nextSpareBufferChan++);
            }

            if (numOutChans >= numOut)
                break;
        }

        int numInChans = 0;

        for (i = 0; i < inBuffer.mNumberBuffers; ++i)
        {
            const AudioBuffer& buf = inBuffer.mBuffers[i];

            if (buf.mNumberChannels == 1)
            {
                if (numInChans < numOutChans)
                    memcpy (channels [numInChans], (const float*) buf.mData, sizeof (float) * numSamples);
                else
                    channels [numInChans] = (float*) buf.mData;

                ++numInChans;
            }
            else
            {
                // need to de-interleave..
                for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numInChans < numIn; ++subChan)
                {
                    float* dest;

                    if (numInChans < numOutChans)
                    {
                        dest = channels [numInChans++];
                    }
                    else
                    {
                        dest = bufferSpace.getSampleData (nextSpareBufferChan++);
                        channels [numInChans++] = dest;
                    }

                    const float* src = ((const float*) buf.mData) + subChan;

                    for (int j = numSamples; --j >= 0;)
                    {
                        *dest++ = *src;
                        src += buf.mNumberChannels;
                    }
                }
            }

            if (numInChans >= numIn)
                break;
        }

        {
            const ScopedLock sl (incomingMidiLock);
            midiEvents.clear();
            incomingEvents.swapWith (midiEvents);
        }

        {
            AudioSampleBuffer buffer (channels, jmax (numIn, numOut), numSamples);

            const ScopedLock sl (juceFilter->getCallbackLock());

            if (juceFilter->isSuspended())
            {
                for (int i = 0; i < numOut; ++i)
                    zeromem (channels [i], sizeof (float) * numSamples);
            }
            else
            {
                juceFilter->processBlock (buffer, midiEvents);
            }
        }

        if (! midiEvents.isEmpty())
        {
           #if JucePlugin_ProducesMidiOutput
            const JUCE_NAMESPACE::uint8* midiEventData;
            int midiEventSize, midiEventPosition;
            MidiBuffer::Iterator i (midiEvents);

            while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
            {
                jassert (isPositiveAndBelow (midiEventPosition, (int) numSamples));



                //xxx
            }
           #else
            // if your plugin creates midi messages, you'll need to set
            // the JucePlugin_ProducesMidiOutput macro to 1 in your
            // JucePluginCharacteristics.h file
            //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
           #endif

            midiEvents.clear();
        }

        if (needToReinterleave)
        {
            nextSpareBufferChan = 0;

            for (i = 0; i < outBuffer.mNumberBuffers; ++i)
            {
                AudioBuffer& buf = outBuffer.mBuffers[i];

                if (buf.mNumberChannels > 1)
                {
                    for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan)
                    {
                        const float* src = bufferSpace.getSampleData (nextSpareBufferChan++);
                        float* dest = ((float*) buf.mData) + subChan;

                        for (int j = numSamples; --j >= 0;)
                        {
                            *dest = *src++;
                            dest += buf.mNumberChannels;
                        }
                    }
                }
            }
        }

      #if ! JucePlugin_SilenceInProducesSilenceOut
        ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
      #endif
    }

    return noErr;
}

protected:
OSStatus HandleMidiEvent (UInt8 nStatus,
UInt8 inChannel,
UInt8 inData1,
UInt8 inData2,
#if defined(MAC_OS_X_VERSION_10_5)
UInt32 inStartFrame)
#else
long inStartFrame)
#endif
{
#if JucePlugin_WantsMidiInput
const ScopedLock sl (incomingMidiLock);
JUCE_NAMESPACE::uint8 data [4];
data[0] = nStatus | inChannel;
data[1] = inData1;
data[2] = inData2;

    incomingEvents.addEvent (data, 3, inStartFrame);
  #endif

    return noErr;
}

OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength)
{
  #if JucePlugin_WantsMidiInput
    const ScopedLock sl (incomingMidiLock);
    incomingEvents.addEvent (inData, inLength, 0);
  #endif
    return noErr;
}

//==============================================================================
ComponentResult GetPresets (CFArrayRef* outData) const
{
    if (outData != 0)
    {
        const int numPrograms = juceFilter->getNumPrograms();
        presetsArray.ensureSize (sizeof (AUPreset) * numPrograms, true);
        AUPreset* const presets = (AUPreset*) presetsArray.getData();

        CFMutableArrayRef presetsArray = CFArrayCreateMutable (0, numPrograms, 0);

        for (int i = 0; i < numPrograms; ++i)
        {
            presets[i].presetNumber = i;
            presets[i].presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (i));

            CFArrayAppendValue (presetsArray, presets + i);
        }

        *outData = (CFArrayRef) presetsArray;
    }

    return noErr;
}

OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset)
{
    const int numPrograms = juceFilter->getNumPrograms();
    const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;

    if (chosenPresetNumber >= numPrograms)
        return kAudioUnitErr_InvalidProperty;

    AUPreset chosenPreset;
    chosenPreset.presetNumber = chosenPresetNumber;
    chosenPreset.presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (chosenPresetNumber));

    juceFilter->setCurrentProgram (chosenPresetNumber);
    SetAFactoryPresetAsCurrent (chosenPreset);

    return noErr;
}

void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
{
    //if (wasResized)
    {
        NSView* view = (NSView*) component.getWindowHandle();
        NSRect r = [[view superview] frame];
        r.origin.y = r.origin.y + r.size.height - component.getHeight();
        r.size.width = component.getWidth();
        r.size.height = component.getHeight();
        [[view superview] setFrame: r];
        [view setFrame: NSMakeRect (0, 0, component.getWidth(), component.getHeight())];
        [view setNeedsDisplay: YES];
    }
}

//==============================================================================

private:
ScopedPointer juceFilter;
AudioSampleBuffer bufferSpace;
HeapBlock <float*> channels;
MidiBuffer midiEvents, incomingEvents;
bool prepared;
SMPTETime lastSMPTETime;
AUChannelInfo channelInfo [numChannelConfigs];
AudioUnitEvent auEvent;
mutable MemoryBlock presetsArray;
CriticalSection incomingMidiLock;
};

//==============================================================================
class EditorCompHolder : public Component,
public ComponentListener
{
public:
EditorCompHolder (AudioProcessorEditor* const editor)
{
setSize (editor->getWidth(), editor->getHeight());
addAndMakeVisible (editor);
editor->addComponentListener (this);

   #if ! JucePlugin_EditorRequiresKeyboardFocus
    setWantsKeyboardFocus (false);
   #else
    setWantsKeyboardFocus (true);
   #endif
}

~EditorCompHolder()
{
    deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
                         // have been transferred to another parent which takes over ownership.
}

void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
{
    Component* editor = getChildComponent(0);

    if (editor != 0 && wasResized)
    {
        const int w = jmax (32, editor->getWidth());
        const int h = jmax (32, editor->getHeight());

        if (getWidth() != w || getHeight() != h)
            setSize (w, h);

        NSView* view = (NSView*) getWindowHandle();
        NSRect r = [[view superview] frame];
        r.size.width = component.getWidth();
        r.size.height = component.getHeight();
        [[view superview] setFrame: r];
        [view setFrame: NSMakeRect (0, 0, editor->getWidth(), editor->getHeight())];
        [view setNeedsDisplay: YES];
    }
}

};

//==============================================================================
@implementation JuceUIViewClass

  • (JuceUIViewClass*) initWithFilter: (AudioProcessor*) filter_
    withAU: (JuceAU*) au_
    withComponent: (AudioProcessorEditor*) editorComp_
    {
    active = YES;
    filter = filter_;
    au = au_;
    editorComp = new EditorCompHolder (editorComp_);

    [super initWithFrame: NSMakeRect (0, 0, editorComp_->getWidth(), editorComp_->getHeight())];
    [self setHidden: NO];
    [self setPostsFrameChangedNotifications: YES];

    [[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(applicationWillTerminate:)
    name:NSApplicationWillTerminateNotification
    object:nil];

    activeUIs.add (self);

    editorComp->addToDesktop (0, (void*) self);
    editorComp->setVisible (true);

    return self;
    }

  • (void)applicationWillTerminate:(NSNotification *)aNotification
    {
    [self willClose];
    }

  • (void) willClose
    {
    // there’s some kind of component currently modal, but the host
    // is trying to delete our plugin…
    jassert (Component::getCurrentlyModalComponent() == 0);

    active = NO;

    [self deleteEditor];

    jassert (activeUIs.contains (self));

    activeUIs.removeValue (self);

    if (activePlugins.size() + activeUIs.size() == 0)
    shutdownJuce_GUI();
    }

  • (void) dealloc
    {
    if(active)
    [self willClose];

    [super dealloc];
    }

  • (void) viewDidMoveToWindow
    {
    if ([self window] != 0)
    {
    [[self window] setAcceptsMouseMovedEvents: YES];

      if (editorComp != 0)
          [[self window] makeFirstResponder: (NSView*) editorComp->getWindowHandle()];
    

    }
    }

  • (BOOL) mouseDownCanMoveWindow
    {
    return NO;
    }

  • (void) deleteEditor
    {
    if (editorComp != 0)
    {
    if (editorComp->getChildComponent(0) != 0)
    if (activePlugins.contains ((void*) au)) // plugin may have been deleted before the UI
    filter->editorBeingDeleted ((AudioProcessorEditor*) editorComp->getChildComponent(0));

      deleteAndZero (editorComp);
    

    }

    editorComp = 0;
    }

  • (void) filterBeingDeleted: (JuceAU*) au_
    {
    if (au_ == au)
    [self deleteEditor];
    }

@end

//==============================================================================
@implementation JuceUICreationClass

  • (JuceUICreationClass*) init
    {
    return [super init];
    }

  • (void) dealloc
    {
    [super dealloc];
    }

  • (unsigned) interfaceVersion
    {
    return 0;
    }

  • (NSString*) description
    {
    return [NSString stringWithString: @JucePlugin_Name];
    }

  • (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
    withSize: (NSSize) inPreferredSize
    {
    void* pointers[2];
    UInt32 propertySize = sizeof (pointers);

    if (AudioUnitGetProperty (inAudioUnit,
    juceFilterObjectPropertyID,
    kAudioUnitScope_Global,
    0,
    pointers,
    &propertySize) != noErr)
    return 0;

    AudioProcessor* filter = (AudioProcessor*) pointers[0];
    JuceAU* au = (JuceAU*) pointers[1];

    if (filter == 0)
    return 0;

    AudioProcessorEditor* editorComp = filter->createEditorIfNeeded();

    if (editorComp == 0)
    return 0;

    return [[[JuceUIViewClass alloc] initWithFilter: filter
    withAU: au
    withComponent: editorComp] autorelease];
    }
    @end

#if BUILD_AU_CARBON_UI

//==============================================================================
class JuceAUView : public AUCarbonViewBase
{
AudioProcessor* juceFilter;
ScopedPointer windowComp;

public:
JuceAUView (AudioUnitCarbonView auview)
: AUCarbonViewBase (auview),
juceFilter (0)
{
}

~JuceAUView()
{
    deleteUI();
}

ComponentResult CreateUI (Float32 /*inXOffset*/, Float32 /*inYOffset*/)
{
    JUCE_AUTORELEASEPOOL

    if (juceFilter == 0)
    {
        void* pointers[2];
        UInt32 propertySize = sizeof (pointers);

        AudioUnitGetProperty (GetEditAudioUnit(),
                              juceFilterObjectPropertyID,
                              kAudioUnitScope_Global,
                              0,
                              pointers,
                              &propertySize);

        juceFilter = (AudioProcessor*) pointers[0];
    }

    if (juceFilter != 0)
    {
        deleteUI();

        AudioProcessorEditor* editorComp = juceFilter->createEditorIfNeeded();
        editorComp->setOpaque (true);
        windowComp = new ComponentInHIView (editorComp, mCarbonPane);
    }
    else
    {
        jassertfalse // can't get a pointer to our effect
    }

    return noErr;
}

AudioUnitCarbonViewEventListener getEventListener() const   { return mEventListener; }
void* getEventListenerUserData() const                      { return mEventListenerUserData; }

private:
void deleteUI()
{
if (windowComp != 0)
{
PopupMenu::dismissAllActiveMenus();

        // there's some kind of component currently modal, but the host
        // is trying to delete our plugin..
        jassert (Component::getCurrentlyModalComponent() == 0);

        if (windowComp != 0 && windowComp->getChildComponent(0) != 0)
            juceFilter->editorBeingDeleted ((AudioProcessorEditor*) windowComp->getChildComponent(0));

        windowComp = 0;
    }
}

//==============================================================================
// Uses a child NSWindow to sit in front of a HIView and display our component
class ComponentInHIView  : public Component,
                           public ComponentListener
{
public:
    //==============================================================================
    ComponentInHIView (Component* const editor_, HIViewRef parentHIView)
        : parentView (parentHIView),
          editor (editor_),
          recursive (false)
    {
        JUCE_AUTORELEASEPOOL

        jassert (editor != 0);
        addAndMakeVisible (editor);
        setOpaque (true);
        setVisible (true);
        setBroughtToFrontOnMouseClick (true);

        setSize (editor->getWidth(), editor->getHeight());
        SizeControl (parentHIView, editor->getWidth(), editor->getHeight());

        WindowRef windowRef = HIViewGetWindow (parentHIView);
        hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];

        [hostWindow retain];
        [hostWindow setCanHide: YES];
        [hostWindow setReleasedWhenClosed: YES];

        updateWindowPos();

       #if ! JucePlugin_EditorRequiresKeyboardFocus
        addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
        setWantsKeyboardFocus (false);
       #else
        addToDesktop (ComponentPeer::windowIsTemporary);
        setWantsKeyboardFocus (true);
      #endif

        setVisible (true);
        toFront (false);

        addSubWindow();

        NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
        [pluginWindow setNextResponder: hostWindow];

        // Adds a callback bodge to work around some problems with wrapped
        // carbon windows..
        const EventTypeSpec eventsToCatch[] = {
            { kEventClassWindow, kEventWindowShown },
            { kEventClassWindow, kEventWindowHidden }
        };

        InstallWindowEventHandler ((WindowRef) windowRef,
                                   NewEventHandlerUPP (windowVisibilityBodge),
                                   GetEventTypeCount (eventsToCatch), eventsToCatch,
                                   (void*) hostWindow, 0);

        editor->addComponentListener (this);
    }

    ~ComponentInHIView()
    {
        jassert (isParentOf (editor)); // you mustn't remove your editor from its parent!

        editor->removeComponentListener (this);

        JUCE_AUTORELEASEPOOL

        NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
        [hostWindow removeChildWindow: pluginWindow];
        removeFromDesktop();

        [hostWindow release];
        hostWindow = 0;

        editor = 0;
    }

    void updateWindowPos()
    {
        HIPoint f;
        f.x = f.y = 0;
        HIPointConvert (&f, kHICoordSpaceView, parentView, kHICoordSpaceScreenPixel, 0);
        setTopLeftPosition ((int) f.x, (int) f.y);
    }

    void addSubWindow()
    {
        NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
        [pluginWindow setExcludedFromWindowsMenu: YES];
        [pluginWindow setCanHide: YES];

        [hostWindow addChildWindow: pluginWindow
                           ordered: NSWindowAbove];
        [hostWindow orderFront: nil];
        [pluginWindow orderFront: nil];
    }

    void resized()
    {
        if (getChildComponent(0) != 0)
            getChildComponent(0)->setBounds (0, 0, getWidth(), getHeight());
    }

    void paint (Graphics& g) {}

    void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
    {
        if (! recursive)
        {
            recursive = true;

            if (editor != 0 && wasResized)
            {
                const int w = jmax (32, editor->getWidth());
                const int h = jmax (32, editor->getHeight());

                SizeControl (parentView, w, h);

                if (getWidth() != w || getHeight() != h)
                    setSize (w, h);

                editor->repaint();

                updateWindowPos();
                addSubWindow(); // (need this for AULab)
            }

            recursive = false;
        }
    }

    bool keyPressed (const KeyPress& kp)
    {
        if (! kp.getModifiers().isCommandDown())
        {
            // If we have an unused keypress, move the key-focus to a host window
            // and re-inject the event..
            static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event

            if (lastEventTime != [[NSApp currentEvent] timestamp])
            {
                lastEventTime = [[NSApp currentEvent] timestamp];
                [[hostWindow parentWindow] makeKeyWindow];
                [NSApp postEvent: [NSApp currentEvent] atStart: YES];
            }
        }

        return false;
    }

private:
    HIViewRef parentView;
    NSWindow* hostWindow;
    ScopedPointer<Component> editor;
    bool recursive;

    /* When you wrap a WindowRef as an NSWindow, it seems to bugger up the HideWindow
       function, so when the host tries (and fails) to hide the window, this catches
       the event and does the job properly.
    */
    static pascal OSStatus windowVisibilityBodge (EventHandlerCallRef, EventRef e, void* user)
    {
        NSWindow* hostWindow = (NSWindow*) user;

        switch (GetEventKind (e))
        {
        case kEventWindowShown:
            [hostWindow orderFront: nil];
            break;
        case kEventWindowHidden:
            [hostWindow orderOut: nil];
            break;
        }

        return eventNotHandledErr;
    }
};

};

#endif

//==============================================================================
#define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)
extern “C” attribute((visibility(“default”))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj);
extern “C” attribute((visibility(“default”))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj)
{
return ComponentEntryPoint::Dispatch(params, obj);
}

#define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)

JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)

#if BUILD_AU_CARBON_UI
JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)
#endif

#endif
[/code]

I’ve done a few launch/quit cycles in different plugins and it seems OK.

That’s really interesting - it might just avoid the problem by closing the plugin window earlier in the shutdown process than it would otherwise happen, so giving the NSWindows chance to get cleared away properly before the DLL goes away. It’s probably a good precautionary measure to add, even if not exactly the cause of the crash.

Oops, forgot to remove the UI class as an observer:

[code]- (void) willClose
{
// there’s some kind of component currently modal, but the host
// is trying to delete our plugin…
jassert (Component::getCurrentlyModalComponent() == 0);

[[NSNotificationCenter defaultCenter] removeObserver:self];

active = NO;

[self deleteEditor];

jassert (activeUIs.contains (self));

activeUIs.removeValue (self);

if (activePlugins.size() + activeUIs.size() == 0)
    shutdownJuce_GUI();

}
[/code]

And I did some logging and dealloc isn’t called when the app quits, - this might explain the mountain of leaked objects reported in the Debug build.

However, I found where rewire was installed and removed that and it seems to work now without these changes too. So perhaps this is an unnecessary precaution when the problem was more likely to be in another third-party lib.

Thanks. I’ve added something similar anyway, as it won’t do any harm - will check in later today…

Also got a crash here, quitting Ableton Live 6, while my plugin’s editor was open:

Thread 0 Crashed: Dispatch queue: com.apple.main-thread 0 libobjc.A.dylib 0x98583ed7 objc_msgSend + 23 1 ...echnologies.TransientShaper 0x157a0853 JuceAUView::~JuceAUView() + 55 2 ...echnologies.TransientShaper 0x157904c7 ComponentBase::ComponentEntryDispatch(ComponentParameters*, ComponentBase*) + 67 3 ...echnologies.TransientShaper 0x157a09cd ComponentEntryPoint<JuceAUView>::Dispatch(ComponentParameters*, JuceAUView*) + 167 4 ...ple.CoreServices.CarbonCore 0x97cdf7c9 CallComponentDispatch + 29 5 ...ple.CoreServices.CarbonCore 0x97cdfb52 CallComponentClose + 43 6 ...ple.CoreServices.CarbonCore 0x97cdfa7b CloseComponentInternal(ComponentInstanceRecord*) + 101 7 ...ple.CoreServices.CarbonCore 0x97cdf9fc CloseComponent + 46 8 com.ableton.live 0x00ec1cdd 0x1000 + 15469789

It really pisses me off because I wanted to release the plugin today. I had this crash a few times now. It happens not every time, but I’d say 1 time of 5.

Just curious if it was the filterBeingDeleted: message too?