Make import GUI report if you are importing a file of a name that
[ardour.git] / libs / ardour / audio_unit.cc
index 25c8aeb53bd2d89c99e603214478f36f4ddcfa96..5e0114c49d708e1fd6c90b169d16a76a883eb6e8 100644 (file)
@@ -1,6 +1,5 @@
 /*
     Copyright (C) 2006 Paul Davis 
-       Written by Taybin Rutkin
        
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+#include <sstream>
+
 #include <pbd/transmitter.h>
 #include <pbd/xml++.h>
+#include <pbd/whitespace.h>
+
+#include <glibmm/thread.h>
 
 #include <ardour/audioengine.h>
+#include <ardour/io.h>
 #include <ardour/audio_unit.h>
 #include <ardour/session.h>
 #include <ardour/utils.h>
 
 #include <appleutility/CAAudioUnit.h>
+#include <appleutility/CAAUParameter.h>
 
 #include <CoreServices/CoreServices.h>
 #include <AudioUnit/AudioUnit.h>
@@ -37,82 +43,233 @@ using namespace std;
 using namespace PBD;
 using namespace ARDOUR;
 
-AUPlugin::AUPlugin (AudioEngine& engine, Session& session, CAComponent* _comp)
+static OSStatus 
+_render_callback(void *userData,
+                AudioUnitRenderActionFlags *ioActionFlags,
+                const AudioTimeStamp    *inTimeStamp,
+                UInt32       inBusNumber,
+                UInt32       inNumberFrames,
+                AudioBufferList*       ioData)
+{
+       return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+}
+
+AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
        :
        Plugin (engine, session),
        comp (_comp),
-       unit (new CAAudioUnit)
+       unit (new CAAudioUnit),
+       initialized (false),
+       buffers (0),
+       current_maxbuf (0),
+       current_offset (0),
+       current_buffers (0),
+       frames_processed (0)
 {                      
-       OSErr err = CAAudioUnit::Open (*comp, *unit);
+       OSErr err = CAAudioUnit::Open (*(comp.get()), *unit);
+
        if (err != noErr) {
                error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg;
-               delete unit;
-               delete comp;
                throw failed_constructor ();
        }
        
-       unit->Initialize ();
+       AURenderCallbackStruct renderCallbackInfo;
+
+       renderCallbackInfo.inputProc = _render_callback;
+       renderCallbackInfo.inputProcRefCon = this;
+
+       if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 
+                                        0, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) {
+               cerr << "cannot install render callback (err = " << err << ')' << endl;
+               throw failed_constructor();
+       }
+
+       unit->GetElementCount (kAudioUnitScope_Global, global_elements);
+       unit->GetElementCount (kAudioUnitScope_Input, input_elements);
+       unit->GetElementCount (kAudioUnitScope_Output, output_elements);
+
+       // set up the basic stream format. these fields do not change
+                           
+       streamFormat.mSampleRate = session.frame_rate();
+       streamFormat.mFormatID = kAudioFormatLinearPCM;
+       streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
+       streamFormat.mBitsPerChannel = 32;
+       streamFormat.mFramesPerPacket = 1;
+
+       // subject to later modification as we discover channel counts
+
+       streamFormat.mBytesPerPacket = 4;
+       streamFormat.mBytesPerFrame = 4;
+       streamFormat.mChannelsPerFrame = 1;
+
+       format_set = 0;
+
+       if (_set_block_size (_session.get_block_size())) {
+               error << _("AUPlugin: cannot set processing block size") << endmsg;
+               throw failed_constructor();
+       }
+
+       discover_parameters ();
+
+       Plugin::setup_controls ();
 }
 
 AUPlugin::~AUPlugin ()
 {
        if (unit) {
                unit->Uninitialize ();
-               delete unit;
-       }
-       
-       if (comp) {
-               delete comp;
-       }
-       
-       if (in_list) {
-               delete in_list;
        }
-       
-       if (out_list) {
-               delete out_list;
+
+       if (buffers) {
+               free (buffers);
        }
 }
 
-AUPluginInfo::~AUPluginInfo ()
+void
+AUPlugin::discover_parameters ()
 {
-       if (desc) {
-               delete desc;
+       /* discover writable parameters */
+       
+       cerr << "get param info, there are " << global_elements << " global elements\n";
+
+       AudioUnitScope scopes[] = { 
+               kAudioUnitScope_Global,
+               kAudioUnitScope_Output,
+               kAudioUnitScope_Input
+       };
+
+       descriptors.clear ();
+
+       for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
+
+               AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
+               
+               cerr << "discovered " << param_info.NumParams() << " parameters in scope " << i << endl;
+               
+               for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
+
+                       AUParameterDescriptor d;
+
+                       d.id = param_info.ParamID (i);
+
+                       const CAAUParameter* param = param_info.GetParamInfo (d.id);
+                       const AudioUnitParameterInfo& info (param->ParamInfo());
+
+                       const int len = CFStringGetLength (param->GetName());;
+                       char local_buffer[len*2];
+                       Boolean good = CFStringGetCString(param->GetName(),local_buffer,len*2,kCFStringEncodingMacRoman);
+                       if (!good) {
+                               d.label = "???";
+                       } else {
+                               d.label = local_buffer;
+                       }
+
+                       d.scope = param_info.GetScope ();
+                       d.element = param_info.GetElement ();
+
+                       /* info.units to consider */
+                       /*
+                         kAudioUnitParameterUnit_Generic             = 0
+                         kAudioUnitParameterUnit_Indexed             = 1
+                         kAudioUnitParameterUnit_Boolean             = 2
+                         kAudioUnitParameterUnit_Percent             = 3
+                         kAudioUnitParameterUnit_Seconds             = 4
+                         kAudioUnitParameterUnit_SampleFrames        = 5
+                         kAudioUnitParameterUnit_Phase               = 6
+                         kAudioUnitParameterUnit_Rate                = 7
+                         kAudioUnitParameterUnit_Hertz               = 8
+                         kAudioUnitParameterUnit_Cents               = 9
+                         kAudioUnitParameterUnit_RelativeSemiTones   = 10
+                         kAudioUnitParameterUnit_MIDINoteNumber      = 11
+                         kAudioUnitParameterUnit_MIDIController      = 12
+                         kAudioUnitParameterUnit_Decibels            = 13
+                         kAudioUnitParameterUnit_LinearGain          = 14
+                         kAudioUnitParameterUnit_Degrees             = 15
+                         kAudioUnitParameterUnit_EqualPowerCrossfade = 16
+                         kAudioUnitParameterUnit_MixerFaderCurve1    = 17
+                         kAudioUnitParameterUnit_Pan                 = 18
+                         kAudioUnitParameterUnit_Meters              = 19
+                         kAudioUnitParameterUnit_AbsoluteCents       = 20
+                         kAudioUnitParameterUnit_Octaves             = 21
+                         kAudioUnitParameterUnit_BPM                 = 22
+                         kAudioUnitParameterUnit_Beats               = 23
+                         kAudioUnitParameterUnit_Milliseconds        = 24
+                         kAudioUnitParameterUnit_Ratio               = 25
+                       */
+
+                       /* info.flags to consider */
+
+                       /*
+
+                         kAudioUnitParameterFlag_CFNameRelease       = (1L << 4)
+                         kAudioUnitParameterFlag_HasClump            = (1L << 20)
+                         kAudioUnitParameterFlag_HasName             = (1L << 21)
+                         kAudioUnitParameterFlag_DisplayLogarithmic  = (1L << 22)
+                         kAudioUnitParameterFlag_IsHighResolution    = (1L << 23)
+                         kAudioUnitParameterFlag_NonRealTime         = (1L << 24)
+                         kAudioUnitParameterFlag_CanRamp             = (1L << 25)
+                         kAudioUnitParameterFlag_ExpertMode          = (1L << 26)
+                         kAudioUnitParameterFlag_HasCFNameString     = (1L << 27)
+                         kAudioUnitParameterFlag_IsGlobalMeta        = (1L << 28)
+                         kAudioUnitParameterFlag_IsElementMeta       = (1L << 29)
+                         kAudioUnitParameterFlag_IsReadable          = (1L << 30)
+                         kAudioUnitParameterFlag_IsWritable          = (1L << 31)
+                       */
+
+                       d.integer_step = (info.unit & kAudioUnitParameterUnit_Indexed);
+                       d.toggled = (info.unit & kAudioUnitParameterUnit_Boolean);
+                       d.sr_dependent = (info.unit & kAudioUnitParameterUnit_SampleFrames);
+                       d.automatable = !d.toggled && 
+                               !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
+                               (info.flags & kAudioUnitParameterFlag_IsWritable);
+                       
+                       d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
+                       d.unit = info.unit;
+
+                       d.lower = info.minValue;
+                       d.upper = info.maxValue;
+                       d.default_value = info.defaultValue;
+                       d.step = 1.0;
+                       d.smallstep = 0.1;
+                       d.largestep = 10.0;
+                       d.min_unbound = 0; // lower is bound
+                       d.max_unbound = 0; // upper is bound
+
+                       descriptors.push_back (d);
+               }
        }
 }
 
-uint32_t
+
+string
 AUPlugin::unique_id () const
 {
-       return 0;
+       return AUPluginInfo::stringify_descriptor (comp->Desc());
 }
 
 const char *
 AUPlugin::label () const
 {
-       return "AUPlugin label";
-}
-
-const char *
-AUPlugin::maker () const
-{
-       return "AUplugin maker";
+       return _info->name.c_str();
 }
 
 uint32_t
 AUPlugin::parameter_count () const
 {
-       return 0;
+       return descriptors.size();
 }
 
 float
 AUPlugin::default_value (uint32_t port)
 {
-       // AudioUnits don't have default values.  Maybe presets though?
+       if (port < descriptors.size()) {
+               return descriptors[port].default_value;
+       }
+
        return 0;
 }
 
-jack_nframes_t
+nframes_t
 AUPlugin::latency () const
 {
        return unit->Latency ();
@@ -121,7 +278,7 @@ AUPlugin::latency () const
 void
 AUPlugin::set_parameter (uint32_t which, float val)
 {
-       unit->SetParameter (parameter_map[which].first, parameter_map[which].second, 0, val);
+       // unit->SetParameter (id, 0, val);
 }
 
 float
@@ -129,89 +286,267 @@ AUPlugin::get_parameter (uint32_t which) const
 {
        float outValue = 0.0;
        
-       unit->GetParameter(parameter_map[which].first, parameter_map[which].second, 0, outValue);
+       // unit->GetParameter(parameter_map[which].first, parameter_map[which].second, 0, outValue);
        
        return outValue;
 }
 
 int
-AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const
+AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const
 {
+       if (which < descriptors.size()) {
+               pd = descriptors[which];
+               return 0;
+       } 
        return -1;
 }
 
 uint32_t
 AUPlugin::nth_parameter (uint32_t which, bool& ok) const
 {
+       if (which < descriptors.size()) {
+               ok = true;
+               return which;
+       }
+       ok = false;
        return 0;
 }
 
 void
 AUPlugin::activate ()
 {
-       unit->GlobalReset ();
+       if (!initialized) {
+               OSErr err;
+               if ((err = unit->Initialize()) != noErr) {
+                       error << string_compose (_("AUPlugin: cannot initialize plugin (err = %1)"), err) << endmsg;
+               } else {
+                       frames_processed = 0;
+                       initialized = true;
+               }
+       }
 }
 
 void
 AUPlugin::deactivate ()
 {
-       // not needed.  GlobalReset () takes care of it.
+       unit->GlobalReset ();
 }
 
 void
-AUPlugin::set_block_size (jack_nframes_t nframes)
+AUPlugin::set_block_size (nframes_t nframes)
 {
-       
+       _set_block_size (nframes);
 }
 
 int
-AUPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, jack_nframes_t nframes, jack_nframes_t offset)
+AUPlugin::_set_block_size (nframes_t nframes)
 {
-       AudioUnitRenderActionFlags flags = 0;
-       AudioTimeStamp ts;
-       
-       AudioBufferList abl;
-       abl.mNumberBuffers = 1;
-       abl.mBuffers[0].mNumberChannels = 1;
-       abl.mBuffers[0].mDataByteSize = nframes * sizeof(Sample);
-       abl.mBuffers[0].mData = &bufs[0];
-       
-       
-       unit->Render (&flags, &ts, 0, 0, &abl);
-       
+       bool was_initialized = initialized;
+       UInt32 numFrames = nframes;
+       OSErr err;
+
+       if (initialized) {
+               unit->Uninitialize ();
+       }
+
+       if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 
+                                     0, &numFrames, sizeof (numFrames))) != noErr) {
+               cerr << "cannot set max frames (err = " << err << ')' << endl;
+               return -1;
+       }
+
+       if (was_initialized) {
+               activate ();
+       }
+
        return 0;
 }
 
-set<uint32_t>
-AUPlugin::automatable() const
+int32_t 
+AUPlugin::can_support_input_configuration (int32_t in)
+{      
+       streamFormat.mChannelsPerFrame = in;
+       /* apple says that for non-interleaved data, these
+          values always refer to a single channel.
+       */
+       streamFormat.mBytesPerPacket = 4;
+       streamFormat.mBytesPerFrame = 4;
+
+       if (set_input_format () == 0) {
+               return 1;
+       } else {
+               return -1;
+       }
+}
+
+int
+AUPlugin::set_input_format ()
 {
-       set<uint32_t> automates;
-       
-       return automates;
+       return set_stream_format (kAudioUnitScope_Input, input_elements);
 }
 
-void
-AUPlugin::store_state (ARDOUR::PluginState&)
+int
+AUPlugin::set_output_format ()
 {
-       
+       return set_stream_format (kAudioUnitScope_Output, output_elements);
 }
 
-void
-AUPlugin::restore_state (ARDOUR::PluginState&)
+int
+AUPlugin::set_stream_format (int scope, uint32_t cnt)
 {
+       OSErr result;
+
+       for (uint32_t i = 0; i < cnt; ++i) {
+               if ((result = unit->SetFormat (scope, i, streamFormat)) != 0) {
+                       error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
+                                                (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg;
+                       return -1;
+               }
+       }
+
+       if (scope == kAudioUnitScope_Input) {
+               format_set |= 0x1;
+       } else {
+               format_set |= 0x2;
+       }
+
+       return 0;
+}
+
+int32_t 
+AUPlugin::compute_output_streams (int32_t nplugins)
+{
+       /* we will never replicate AU plugins - either they can do the I/O we need
+          or not. thus, we can ignore nplugins entirely.
+       */
        
+       if (set_output_format() == 0) {
+
+               if (buffers) {
+                       free (buffers);
+                       buffers = 0;
+               }
+
+               buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) + 
+                                                     streamFormat.mChannelsPerFrame * sizeof(AudioBuffer));
+
+               Glib::Mutex::Lock em (_session.engine().process_lock());
+               IO::MoreOutputs (streamFormat.mChannelsPerFrame);
+
+               return streamFormat.mChannelsPerFrame;
+       } else {
+               return -1;
+       }
+}
+
+uint32_t
+AUPlugin::output_streams() const
+{
+       if (!(format_set & 0x2)) {
+               warning << _("AUPlugin: output_streams() called without any format set!") << endmsg;
+               return 1;
+       }
+       return streamFormat.mChannelsPerFrame;
+}
+
+
+uint32_t
+AUPlugin::input_streams() const
+{
+       if (!(format_set & 0x1)) {
+               warning << _("AUPlugin: input_streams() called without any format set!") << endmsg;
+               return 1;
+       }
+       return streamFormat.mChannelsPerFrame;
+}
+
+OSStatus 
+AUPlugin::render_callback(AudioUnitRenderActionFlags *ioActionFlags,
+                         const AudioTimeStamp    *inTimeStamp,
+                         UInt32       inBusNumber,
+                         UInt32       inNumberFrames,
+                         AudioBufferList*       ioData)
+{
+       /* not much to do - the data is already in the buffers given to us in connect_and_run() */
+
+       if (current_maxbuf == 0) {
+               error << _("AUPlugin: render callback called illegally!") << endmsg;
+               return kAudioUnitErr_CannotDoInCurrentContext;
+       }
+
+       for (uint32_t i = 0; i < current_maxbuf; ++i) {
+               ioData->mBuffers[i].mNumberChannels = 1;
+               ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
+               ioData->mBuffers[i].mData = (*current_buffers)[i] + cb_offset + current_offset;
+       }
+
+       cb_offset += inNumberFrames;
+
+       return noErr;
+}
+
+int
+AUPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset)
+{
+       AudioUnitRenderActionFlags flags = 0;
+       AudioTimeStamp ts;
+
+       current_buffers = &bufs;
+       current_maxbuf = maxbuf;
+       current_offset = offset;
+       cb_offset = 0;
+
+       buffers->mNumberBuffers = maxbuf;
+
+       for (uint32_t i = 0; i < maxbuf; ++i) {
+               buffers->mBuffers[i].mNumberChannels = 1;
+               buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
+               buffers->mBuffers[i].mData = 0;
+       }
+
+       ts.mSampleTime = frames_processed;
+       ts.mFlags = kAudioTimeStampSampleTimeValid;
+
+       if (unit->Render (&flags, &ts, 0, nframes, buffers) == noErr) {
+
+               current_maxbuf = 0;
+               frames_processed += nframes;
+               
+               for (uint32_t i = 0; i < maxbuf; ++i) {
+                       if (bufs[i] + offset != buffers->mBuffers[i].mData) {
+                               memcpy (bufs[i]+offset, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
+                       }
+               }
+               return 0;
+       }
+
+       return -1;
+}
+
+set<uint32_t>
+AUPlugin::automatable() const
+{
+       set<uint32_t> automates;
+
+       for (uint32_t i = 0; i < descriptors.size(); ++i) {
+               if (descriptors[i].automatable) {
+                       automates.insert (i);
+               }
+       }
+
+       return automates;
 }
 
 string
-AUPlugin::describe_parameter (uint32_t)
+AUPlugin::describe_parameter (uint32_t param)
 {
-       return "";
+       return descriptors[param].label;
 }
 
 void
-AUPlugin::print_parameter (uint32_t, char*, uint32_t len) const
+AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const
 {
-       
+       // NameValue stuff here
 }
 
 bool
@@ -223,7 +558,7 @@ AUPlugin::parameter_is_audio (uint32_t) const
 bool
 AUPlugin::parameter_is_control (uint32_t) const
 {
-       return false;
+       return true;
 }
 
 bool
@@ -241,8 +576,8 @@ AUPlugin::parameter_is_output (uint32_t) const
 XMLNode&
 AUPlugin::get_state()
 {
-       XMLNoderoot = new XMLNode (state_node_name());
-       
+       XMLNode *root = new XMLNode (state_node_name());
+       LocaleGuard lg (X_("POSIX"));
        return *root;
 }
 
@@ -275,7 +610,19 @@ AUPlugin::get_presets ()
 bool
 AUPlugin::has_editor () const
 {
-       return false;
+       // even if the plugin doesn't have its own editor, the AU API can be used
+       // to create one that looks native.
+       return true;
+}
+
+AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
+       : descriptor (d)
+{
+
+}
+
+AUPluginInfo::~AUPluginInfo ()
+{
 }
 
 PluginPtr
@@ -284,7 +631,7 @@ AUPluginInfo::load (Session& session)
        try {
                PluginPtr plugin;
 
-               CAComponent* comp = new CAComponent(*desc);
+               boost::shared_ptr<CAComponent> comp (new CAComponent(*descriptor));
                
                if (!comp->IsValid()) {
                        error << ("AudioUnit: not a valid Component") << endmsg;
@@ -292,7 +639,7 @@ AUPluginInfo::load (Session& session)
                        plugin.reset (new AUPlugin (session.engine(), session, comp));
                }
                
-               plugin->set_info(PluginInfoPtr(new AUPluginInfo(*this)));
+               plugin->set_info (PluginInfoPtr (new AUPluginInfo (*this)));
                return plugin;
        }
 
@@ -305,7 +652,29 @@ PluginInfoList
 AUPluginInfo::discover ()
 {
        PluginInfoList plugs;
+       
+       discover_fx (plugs);
+       discover_music (plugs);
+
+       return plugs;
+}
 
+void
+AUPluginInfo::discover_music (PluginInfoList& plugs)
+{
+       CAComponentDescription desc;
+       desc.componentFlags = 0;
+       desc.componentFlagsMask = 0;
+       desc.componentSubType = 0;
+       desc.componentManufacturer = 0;
+       desc.componentType = kAudioUnitType_MusicEffect;
+
+       discover_by_description (plugs, desc);
+}
+
+void
+AUPluginInfo::discover_fx (PluginInfoList& plugs)
+{
        CAComponentDescription desc;
        desc.componentFlags = 0;
        desc.componentFlagsMask = 0;
@@ -313,34 +682,146 @@ AUPluginInfo::discover ()
        desc.componentManufacturer = 0;
        desc.componentType = kAudioUnitType_Effect;
 
+       discover_by_description (plugs, desc);
+}
+
+void
+AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
+{
        Component comp = 0;
 
        comp = FindNextComponent (NULL, &desc);
+
        while (comp != NULL) {
                CAComponentDescription temp;
                GetComponentInfo (comp, &temp, NULL, NULL, NULL);
+
+               AUPluginInfoPtr info (new AUPluginInfo 
+                                     (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
+
+               /* no panners, format converters or i/o AU's for our purposes
+                */
+
+               switch (info->descriptor->Type()) {
+               case kAudioUnitType_Panner:
+               case kAudioUnitType_OfflineEffect:
+               case kAudioUnitType_FormatConverter:
+                       continue;
+               default:
+                       break;
+               }
+
+               switch (info->descriptor->SubType()) {
+               case kAudioUnitSubType_DefaultOutput:
+               case kAudioUnitSubType_SystemOutput:
+               case kAudioUnitSubType_GenericOutput:
+               case kAudioUnitSubType_AUConverter:
+                       continue;
+                       break;
+
+               case kAudioUnitSubType_DLSSynth:
+                       info->category = "DLSSynth";
+                       break;
+
+               case kAudioUnitType_MusicEffect:
+                       info->category = "MusicEffect";
+                       break;
+
+               case kAudioUnitSubType_Varispeed:
+                       info->category = "Varispeed";
+                       break;
+
+               case kAudioUnitSubType_Delay:
+                       info->category = "Delay";
+                       break;
+
+               case kAudioUnitSubType_LowPassFilter:
+                       info->category = "LowPassFilter";
+                       break;
+
+               case kAudioUnitSubType_HighPassFilter:
+                       info->category = "HighPassFilter";
+                       break;
+
+               case kAudioUnitSubType_BandPassFilter:
+                       info->category = "BandPassFilter";
+                       break;
+
+               case kAudioUnitSubType_HighShelfFilter:
+                       info->category = "HighShelfFilter";
+                       break;
+
+               case kAudioUnitSubType_LowShelfFilter:
+                       info->category = "LowShelfFilter";
+                       break;
+
+               case kAudioUnitSubType_ParametricEQ:
+                       info->category = "ParametricEQ";
+                       break;
+
+               case kAudioUnitSubType_GraphicEQ:
+                       info->category = "GraphicEQ";
+                       break;
+
+               case kAudioUnitSubType_PeakLimiter:
+                       info->category = "PeakLimiter";
+                       break;
+
+               case kAudioUnitSubType_DynamicsProcessor:
+                       info->category = "DynamicsProcessor";
+                       break;
+
+               case kAudioUnitSubType_MultiBandCompressor:
+                       info->category = "MultiBandCompressor";
+                       break;
+
+               case kAudioUnitSubType_MatrixReverb:
+                       info->category = "MatrixReverb";
+                       break;
+
+               case kAudioUnitType_Mixer:
+                       info->category = "Mixer";
+                       break;
+
+               case kAudioUnitSubType_StereoMixer:
+                       info->category = "StereoMixer";
+                       break;
+
+               case kAudioUnitSubType_3DMixer:
+                       info->category = "3DMixer";
+                       break;
+
+               case kAudioUnitSubType_MatrixMixer:
+                       info->category = "MatrixMixer";
+                       break;
+
+               default:
+                       info->category = "";
+               }
+
+               AUPluginInfo::get_names (temp, info->name, info->creator);
+
+               info->type = ARDOUR::AudioUnit;
+               info->unique_id = stringify_descriptor (*info->descriptor);
+
+               /* mark the plugin as having flexible i/o */
                
-               AUPluginInfoPtr plug(new AUPluginInfo);
-               plug->name = AUPluginInfo::get_name (temp);
-               plug->type = PluginInfo::AudioUnit;
-               plug->n_inputs = 0;
-               plug->n_outputs = 0;
-               plug->category = "AudioUnit";
-               plug->desc = new CAComponentDescription(temp);
-
-               plugs.push_back(plug);
+               info->n_inputs = -1;
+               info->n_outputs = -1;
+
+
+               plugs.push_back (info);
                
                comp = FindNextComponent (comp, &desc);
        }
-
-       return plugs;
 }
 
-string
-AUPluginInfo::get_name (CAComponentDescription& comp_desc)
+void
+AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker)
 {
        CFStringRef itemName = NULL;
-       // Marc Poirier -style item name
+
+       // Marc Poirier-style item name
        CAComponent auComponent (comp_desc);
        if (auComponent.IsValid()) {
                CAComponentDescription dummydesc;
@@ -374,5 +855,36 @@ AUPluginInfo::get_name (CAComponentDescription& comp_desc)
                        CFRelease(compManufacturerString);
        }
        
-       return CFStringRefToStdString(itemName);
+       string str = CFStringRefToStdString(itemName);
+       string::size_type colon = str.find (':');
+
+       if (colon) {
+               name = str.substr (colon+1);
+               maker = str.substr (0, colon);
+               // strip_whitespace_edges (maker);
+               // strip_whitespace_edges (name);
+       } else {
+               name = str;
+               maker = "unknown";
+       }
+}
+
+// from CAComponentDescription.cpp (in libs/appleutility in ardour source)
+extern char *StringForOSType (OSType t, char *writeLocation);
+
+std::string
+AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
+{
+       char str[24];
+       stringstream s;
+
+       s << StringForOSType (desc.Type(), str);
+       s << " - ";
+               
+       s << StringForOSType (desc.SubType(), str);
+       s << " - ";
+               
+       s << StringForOSType (desc.Manu(), str);
+
+       return s.str();
 }