From f3dd3e6b18be7d22ca31e5424c2a07922b44b74e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 10 Dec 2007 21:29:51 +0000 Subject: [PATCH] AU support, plus changes in Plugin to make unique_id a string git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2754 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/audio_unit.h | 59 +++- libs/ardour/ardour/ladspa_plugin.h | 2 +- libs/ardour/ardour/plugin.h | 16 +- libs/ardour/ardour/plugin_manager.h | 6 + libs/ardour/ardour/vst_plugin.h | 2 +- libs/ardour/audio_unit.cc | 521 ++++++++++++++++++++++++---- libs/ardour/audioengine.cc | 1 + libs/ardour/insert.cc | 87 +++-- libs/ardour/ladspa_plugin.cc | 9 + libs/ardour/plugin.cc | 84 ++++- libs/ardour/plugin_manager.cc | 37 +- libs/ardour/route.cc | 21 +- libs/ardour/vst_plugin.cc | 8 +- 13 files changed, 709 insertions(+), 144 deletions(-) diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index a3d6074722..7f51d39738 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -30,6 +30,8 @@ #include +#include + #include class CAComponent; @@ -48,10 +50,10 @@ class AUPlugin : public ARDOUR::Plugin AUPlugin (AudioEngine& engine, Session& session, CAComponent* comp); virtual ~AUPlugin (); - uint32_t unique_id () const; + std::string unique_id () const; const char * label () const; const char * name () const { return _info->name.c_str(); } - const char * maker () const; + const char * maker () const { return _info->creator.c_str(); } uint32_t parameter_count () const; float default_value (uint32_t port); nframes_t latency () const; @@ -84,34 +86,63 @@ class AUPlugin : public ARDOUR::Plugin bool has_editor () const; + bool fixed_io() const { return false; } + int32_t can_support_input_configuration (int32_t in); + int32_t compute_output_streams (int32_t nplugins); + uint32_t output_streams() const; + uint32_t input_streams() const; + CAAudioUnit* get_au () { return unit; } CAComponent* get_comp () { return comp; } - + + OSStatus render_callback(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); private: CAComponent* comp; - CAAudioUnit* unit; - - AudioBufferList* in_list; - AudioBufferList* out_list; + CAAudioUnit* unit; + + AudioStreamBasicDescription streamFormat; + bool initialized; + AudioBufferList* buffers; + + UInt32 global_elements; + UInt32 output_elements; + UInt32 input_elements; + + int set_output_format (); + int set_input_format (); + int set_stream_format (int scope, uint32_t cnt); + int _set_block_size (nframes_t nframes); std::vector > parameter_map; + uint32_t current_maxbuf; + nframes_t current_offset; + nframes_t cb_offset; + vector* current_buffers; + nframes_t frames_processed; }; - + typedef boost::shared_ptr AUPluginPtr; class AUPluginInfo : public PluginInfo { public: - AUPluginInfo () { }; + AUPluginInfo (CAComponentDescription*); ~AUPluginInfo (); - CAComponentDescription* desc; - - static PluginInfoList discover (); PluginPtr load (Session& session); + static PluginInfoList discover (); + static void get_names (CAComponentDescription&, std::string& name, Glib::ustring& maker); + static std::string stringify_descriptor (const CAComponentDescription&); + private: - static std::string get_name (CAComponentDescription&); - void setup_nchannels (CAComponentDescription&); + CAComponentDescription* descriptor; + static void discover_music (PluginInfoList&); + static void discover_fx (PluginInfoList&); + static void discover_by_description (PluginInfoList&, CAComponentDescription&); }; typedef boost::shared_ptr AUPluginInfoPtr; diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index 48228c0c83..3c795299cc 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -52,7 +52,7 @@ class LadspaPlugin : public ARDOUR::Plugin /* Plugin interface */ - uint32_t unique_id() const { return descriptor->UniqueID; } + std::string unique_id() const; const char * label() const { return descriptor->Label; } const char * name() const { return descriptor->Name; } const char * maker() const { return descriptor->Maker; } diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index e4b498c452..869a0f395c 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -67,11 +67,11 @@ class PluginInfo { string category; Glib::ustring creator; Glib::ustring path; - uint32_t n_inputs; - uint32_t n_outputs; + int32_t n_inputs; + int32_t n_outputs; ARDOUR::PluginType type; - long unique_id; + std::string unique_id; virtual PluginPtr load (Session& session) = 0; @@ -108,7 +108,7 @@ class Plugin : public PBD::StatefulDestructible bool max_unbound; }; - virtual uint32_t unique_id() const = 0; + virtual std::string unique_id() const = 0; virtual const char * label() const = 0; virtual const char * name() const = 0; virtual const char * maker() const = 0; @@ -142,6 +142,12 @@ class Plugin : public PBD::StatefulDestructible virtual bool has_editor() const = 0; sigc::signal ParameterChanged; + + virtual bool fixed_io() const { return true; } + virtual int32_t can_support_input_configuration (int32_t in); + virtual int32_t compute_output_streams (int32_t nplugins); + virtual uint32_t output_streams() const; + virtual uint32_t input_streams() const; PBD::Controllable *get_nth_control (uint32_t, bool do_not_create = false); void make_nth_control (uint32_t, const XMLNode&); @@ -186,7 +192,7 @@ class Plugin : public PBD::StatefulDestructible vector controls; }; -PluginPtr find_plugin(ARDOUR::Session&, string name, long unique_id, ARDOUR::PluginType); +PluginPtr find_plugin(ARDOUR::Session&, string unique_id, ARDOUR::PluginType); } // namespace ARDOUR diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h index 504fbf0625..64b871104b 100644 --- a/libs/ardour/ardour/plugin_manager.h +++ b/libs/ardour/ardour/plugin_manager.h @@ -40,6 +40,7 @@ class PluginManager { ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; } ARDOUR::PluginInfoList &ladspa_plugin_info () { return _ladspa_plugin_info; } + ARDOUR::PluginInfoList &au_plugin_info () { return _au_plugin_info; } void refresh (); @@ -51,6 +52,8 @@ class PluginManager { private: ARDOUR::PluginInfoList _vst_plugin_info; ARDOUR::PluginInfoList _ladspa_plugin_info; + ARDOUR::PluginInfoList _au_plugin_info; + std::map rdf_type; std::string ladspa_path; @@ -64,6 +67,9 @@ class PluginManager { void add_vst_presets (); void add_presets (std::string domain); + int au_discover (); + void au_refresh (); + int vst_discover_from_path (std::string path); int vst_discover (std::string path); diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index fb03a75bb8..d6363d696b 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -56,7 +56,7 @@ class VSTPlugin : public ARDOUR::Plugin /* Plugin interface */ - uint32_t unique_id() const; + std::string unique_id() const; const char * label() const; const char * name() const; const char * maker() const; diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index 6879123ff1..6f4f6b3313 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -18,10 +18,16 @@ */ +#include + #include #include +#include + +#include #include +#include #include #include #include @@ -37,13 +43,31 @@ using namespace std; using namespace PBD; using namespace ARDOUR; +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, 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); + if (err != noErr) { error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg; delete unit; @@ -51,7 +75,42 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, CAComponent* _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; + delete unit; + delete comp; + throw failed_constructor(); + } + + 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; + + if (_set_block_size (_session.get_block_size())) { + error << _("AUPlugin: cannot set processing block size") << endmsg; + delete unit; + delete comp; + throw failed_constructor(); + } } AUPlugin::~AUPlugin () @@ -60,43 +119,26 @@ AUPlugin::~AUPlugin () unit->Uninitialize (); delete unit; } + + if (buffers) { + free (buffers); + } if (comp) { delete comp; } - - if (in_list) { - delete in_list; - } - - if (out_list) { - delete out_list; - } } -AUPluginInfo::~AUPluginInfo () -{ - if (desc) { - delete desc; - } -} - -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 @@ -121,7 +163,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 (parameter_map[which].first, parameter_map[which].second, 0, val); } float @@ -129,7 +171,7 @@ 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; } @@ -149,19 +191,168 @@ AUPlugin::nth_parameter (uint32_t which, bool& ok) const 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 (nframes_t nframes) { + _set_block_size (nframes); +} + +int +AUPlugin::_set_block_size (nframes_t nframes) +{ + bool was_initialized = initialized; + UInt32 numFrames = nframes; + OSErr err; + + if (initialized) { + unit->Uninitialize (); + } + + set_input_format (); + set_output_format (); + + 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; +} + +int32_t +AUPlugin::can_support_input_configuration (int32_t in) +{ + streamFormat.mBytesPerPacket = 4 * in; + streamFormat.mBytesPerFrame = 4 * in; + streamFormat.mChannelsPerFrame = in; + + if (set_input_format () == 0) { + return 1; + } else { + return -1; + } +} + +int +AUPlugin::set_input_format () +{ + return set_stream_format (kAudioUnitScope_Input, input_elements); +} + +int +AUPlugin::set_output_format () +{ + return set_stream_format (kAudioUnitScope_Output, output_elements); +} + +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"), + (scope == kAudioUnitScope_Input ? "input" : "output"), i) << endmsg; + return -1; + } + } + + 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 (!initialized) { + warning << _("AUPlugin: output_streams() called without calling Initialize!") << endmsg; + return 1; + } + return streamFormat.mChannelsPerFrame; +} + + +uint32_t +AUPlugin::input_streams() const +{ + if (!initialized) { + warning << _("AUPlugin: input_streams() called without calling Initialize!") << 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 @@ -169,17 +360,37 @@ AUPlugin::connect_and_run (vector& bufs, uint32_t maxbuf, int32_t& in, { 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); - - return 0; + + 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 @@ -229,8 +440,8 @@ AUPlugin::parameter_is_output (uint32_t) const XMLNode& AUPlugin::get_state() { - XMLNode* root = new XMLNode (state_node_name()); - + XMLNode *root = new XMLNode (state_node_name()); + LocaleGuard lg (X_("POSIX")); return *root; } @@ -263,7 +474,22 @@ 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 (CAComponentDescription* d) + : descriptor (d) +{ + +} + +AUPluginInfo::~AUPluginInfo () +{ + if (descriptor) { + delete descriptor; + } } PluginPtr @@ -272,7 +498,7 @@ AUPluginInfo::load (Session& session) try { PluginPtr plugin; - CAComponent* comp = new CAComponent(*desc); + CAComponent* comp = new CAComponent(*descriptor); if (!comp->IsValid()) { error << ("AudioUnit: not a valid Component") << endmsg; @@ -294,6 +520,28 @@ 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; @@ -301,35 +549,145 @@ 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 plug(new AUPluginInfo); - plug->name = AUPluginInfo::get_name (temp); - plug->type = ARDOUR::AudioUnit; - plug->n_inputs = 0; - plug->n_outputs = 0; - // plug->setup_nchannels (temp); - plug->category = "AudioUnit"; - plug->desc = new CAComponentDescription(temp); - plugs.push_back(plug); + AUPluginInfoPtr info (new AUPluginInfo (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 */ + + 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; @@ -363,23 +721,36 @@ AUPluginInfo::get_name (CAComponentDescription& comp_desc) CFRelease(compManufacturerString); } - return CFStringRefToStdString(itemName); -} + string str = CFStringRefToStdString(itemName); + string::size_type colon = str.find (':'); -void -AUPluginInfo::setup_nchannels (CAComponentDescription& comp_desc) -{ - CAAudioUnit unit; - - CAAudioUnit::Open (comp_desc, unit); - - if (unit.SupportsNumChannels()) { - n_inputs = n_outputs = 0; + if (colon) { + name = str.substr (colon+1); + maker = str.substr (0, colon); + // strip_whitespace_edges (maker); + // strip_whitespace_edges (name); } else { - AUChannelInfo cinfo; - size_t info_size = sizeof(cinfo); - OSStatus err = AudioUnitGetProperty (unit.AU(), kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, - 0, &cinfo, &info_size); + 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(); +} diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index bba177074b..1b6756e6fa 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index 160c572ab1..c2408ec86c 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -75,7 +75,7 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr plug, Placemen init (); - { + if (_plugins[0]->fixed_io()) { Glib::Mutex::Lock em (_session.engine().process_lock()); IO::MoreOutputs (output_streams ()); } @@ -94,7 +94,7 @@ PluginInsert::PluginInsert (Session& s, const XMLNode& node) _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); - { + if (_plugins[0]->fixed_io()) { Glib::Mutex::Lock em (_session.engine().process_lock()); IO::MoreOutputs (output_streams()); } @@ -182,13 +182,24 @@ PluginInsert::auto_state_changed (uint32_t which) uint32_t PluginInsert::output_streams() const { - return _plugins[0]->get_info()->n_outputs * _plugins.size(); + int32_t out = _plugins[0]->get_info()->n_outputs; + + if (out < 0) { + return _plugins[0]->output_streams (); + } else { + return out * _plugins.size(); + } } uint32_t PluginInsert::input_streams() const { - return _plugins[0]->get_info()->n_inputs * _plugins.size(); + int32_t in = _plugins[0]->get_info()->n_inputs; + if (in < 0) { + return _plugins[0]->input_streams (); + } else { + return in * _plugins.size(); + } } uint32_t @@ -366,12 +377,17 @@ PluginInsert::run (vector& bufs, uint32_t nbufs, nframes_t nframes, nf uint32_t in = _plugins[0]->get_info()->n_inputs; uint32_t out = _plugins[0]->get_info()->n_outputs; - if (out > in) { - - /* not active, but something has make up for any channel count increase */ + if (in < 0 || out < 0) { - for (uint32_t n = out - in; n < out; ++n) { - memcpy (bufs[n], bufs[in - 1], sizeof (Sample) * nframes); + } else { + + if (out > in) { + + /* not active, but something has make up for any channel count increase */ + + for (uint32_t n = out - in; n < out; ++n) { + memcpy (bufs[n], bufs[in - 1], sizeof (Sample) * nframes); + } } } } @@ -528,7 +544,14 @@ PluginInsert::plugin_factory (boost::shared_ptr other) int32_t PluginInsert::compute_output_streams (int32_t cnt) const { - return _plugins[0]->get_info()->n_outputs * cnt; + int32_t outputs; + + if ((outputs = _plugins[0]->get_info()->n_outputs) < 0) { + // have to ask the plugin itself, because it has flexible I/O + return _plugins[0]->compute_output_streams (cnt); + } + + return outputs * cnt; } int32_t @@ -543,6 +566,12 @@ PluginInsert::can_support_input_configuration (int32_t in) const int32_t outputs = _plugins[0]->get_info()->n_outputs; int32_t inputs = _plugins[0]->get_info()->n_inputs; + if (outputs < 0 || inputs < 0) { + /* have to ask the plugin because its got reconfigurable I/O + */ + return _plugins[0]->can_support_input_configuration (in); + } + if (inputs == 0) { /* instrument plugin, always legal, but it throws @@ -591,13 +620,7 @@ PluginInsert::state (bool full) node->add_child_nocopy (Redirect::state (full)); node->add_property ("type", _plugins[0]->state_node_name()); - snprintf(buf, sizeof(buf), "%s", _plugins[0]->name()); - node->add_property("id", string(buf)); - if (_plugins[0]->state_node_name() == "ladspa") { - char buf[32]; - snprintf (buf, sizeof (buf), "%ld", _plugins[0]->get_info()->unique_id); - node->add_property("unique-id", string(buf)); - } + node->add_property("unique-id", _plugins[0]->unique_id()); node->add_property("count", string_compose("%1", _plugins.size())); node->add_child_nocopy (_plugins[0]->get_state()); @@ -641,7 +664,6 @@ PluginInsert::set_state(const XMLNode& node) XMLNodeIterator niter; XMLPropertyList plist; const XMLProperty *prop; - long unique = 0; ARDOUR::PluginType type; if ((prop = node.property ("type")) == 0) { @@ -653,6 +675,8 @@ PluginInsert::set_state(const XMLNode& node) type = ARDOUR::LADSPA; } else if (prop->value() == X_("vst")) { type = ARDOUR::VST; + } else if (prop->value() == X_("audiounit")) { + type = ARDOUR::AudioUnit; } else { error << string_compose (_("unknown plugin type %1 in plugin insert state"), prop->value()) @@ -661,22 +685,27 @@ PluginInsert::set_state(const XMLNode& node) } prop = node.property ("unique-id"); - if (prop != 0) { - unique = atol(prop->value().c_str()); - } + if (prop == 0) { +#ifdef VST_SUPPORT + /* older sessions contain VST plugins with only an "id" field. + */ + + if (type == ARDOUR::VST) { + if (prop = node.property ("id")) { + } + } +#endif + /* recheck */ - if ((prop = node.property ("id")) == 0) { - error << _("XML node describing insert is missing the `id' field") << endmsg; - return -1; + if (prop == 0) { + error << _("Plugin has no unique ID field") << endmsg; + return -1; + } } boost::shared_ptr plugin; - if (unique != 0) { - plugin = find_plugin (_session, "", unique, type); - } else { - plugin = find_plugin (_session, prop->value(), 0, type); - } + plugin = find_plugin (_session, prop->value(), type); if (plugin == 0) { error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n" diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 6391349ab5..a7aab441e0 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -157,6 +157,15 @@ LadspaPlugin::~LadspaPlugin () } } +string +LadspaPlugin::unique_id() const +{ + char buf[32]; + snprintf (buf, sizeof (buf), "%u", descriptor->UniqueID); + return string (buf); +} + + float LadspaPlugin::default_value (uint32_t port) { diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 7b956b564b..2b193ab0e6 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -42,6 +42,10 @@ #include #include +#ifdef HAVE_AUDIOUNITS +#include +#endif + #include #include "i18n.h" @@ -192,7 +196,21 @@ vector Plugin::get_presets() { vector labels; - lrdf_uris* set_uris = lrdf_get_setting_uris(unique_id()); + uint32_t id; + std::string unique (unique_id()); + + /* XXX problem: AU plugins don't have numeric ID's. + Solution: they have a different method of providing presets. + XXX sub-problem: implement it. + */ + + if (!isdigit (unique[0])) { + return labels; + } + + id = atol (unique.c_str()); + + lrdf_uris* set_uris = lrdf_get_setting_uris(id); if (set_uris) { for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) { @@ -234,6 +252,20 @@ Plugin::save_preset (string name, string domain) { lrdf_portvalue portvalues[parameter_count()]; lrdf_defaults defaults; + uint32_t id; + std::string unique (unique_id()); + + /* XXX problem: AU plugins don't have numeric ID's. + Solution: they have a different method of providing/saving presets. + XXX sub-problem: implement it. + */ + + if (!isdigit (unique[0])) { + return false; + } + + id = atol (unique.c_str()); + defaults.count = parameter_count(); defaults.items = portvalues; @@ -252,7 +284,7 @@ Plugin::save_preset (string name, string domain) string source(string_compose("file:%1/.%2/rdf/ardour-presets.n3", envvar, domain)); - free(lrdf_add_preset(source.c_str(), name.c_str(), unique_id(), &defaults)); + free(lrdf_add_preset(source.c_str(), name.c_str(), id, &defaults)); string path = string_compose("%1/.%2", envvar, domain); if (g_mkdir_with_parents (path.c_str(), 0775)) { @@ -275,7 +307,7 @@ Plugin::save_preset (string name, string domain) } PluginPtr -ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType type) +ARDOUR::find_plugin(Session& session, string identifier, PluginType type) { PluginManager *mgr = PluginManager::the_manager(); PluginInfoList plugs; @@ -288,14 +320,12 @@ ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType ty #ifdef VST_SUPPORT case ARDOUR::VST: plugs = mgr->vst_plugin_info(); - unique_id = 0; // VST plugins don't have a unique id. break; #endif #ifdef HAVE_AUDIOUNITS case ARDOUR::AudioUnit: - plugs = AUPluginInfo::discover (); - unique_id = 0; // Neither do AU. + plugs = mgr->au_plugin_info(); break; #endif @@ -304,13 +334,49 @@ ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType ty } PluginInfoList::iterator i; + for (i = plugs.begin(); i != plugs.end(); ++i) { - if ((name == "" || (*i)->name == name) && - (unique_id == 0 || (*i)->unique_id == unique_id)) { - return (*i)->load (session); + if (identifier == (*i)->unique_id){ + return (*i)->load (session); } } return PluginPtr ((Plugin*) 0); } +int32_t +Plugin::can_support_input_configuration (int32_t in) +{ + /* LADSPA & VST should not get here because they do not + return negative i/o counts. + */ + return -1; +} + +int32_t +Plugin::compute_output_streams (int32_t nplugins) +{ + /* LADSPA & VST should not get here because they do not + return negative i/o counts. + */ + return -1; +} + +uint32_t +Plugin::output_streams () const +{ + /* LADSPA & VST should not get here because they do not + return negative i/o counts. + */ + return 0; +} + +uint32_t +Plugin::input_streams () const +{ + /* LADSPA & VST should not get here because they do not + return negative i/o counts. + */ + return 0; +} + diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index bd636dcf20..253c245fb7 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -40,6 +40,10 @@ #include #endif +#ifdef HAVE_AUDIOUNITS +#include +#endif + #include #include @@ -109,6 +113,9 @@ PluginManager::refresh () vst_refresh (); } #endif // VST_SUPPORT +#ifdef HAVE_AUDIOUNITS + au_refresh (); +#endif } void @@ -123,6 +130,7 @@ PluginManager::ladspa_refresh () ladspa_discover_from_path (ladspa_path); } + int PluginManager::add_ladspa_directory (string path) { @@ -273,7 +281,10 @@ PluginManager::ladspa_discover (string path) info->n_inputs = 0; info->n_outputs = 0; info->type = ARDOUR::LADSPA; - info->unique_id = descriptor->UniqueID; + + char buf[32]; + snprintf (buf, sizeof (buf), "%u", descriptor->UniqueID); + info->unique_id = buf; for (uint32_t n=0; n < descriptor->PortCount; ++n) { if ( LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n]) ) { @@ -310,7 +321,7 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) lrdf_statement* matches1 = lrdf_matches (&pattern); if (!matches1) { - return _("Unknown"); + return _(""); } pattern.subject = matches1->object; @@ -322,7 +333,7 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) lrdf_free_statements(matches1); if (!matches2) { - return _("Unknown"); + return _(""); } string label = matches2->object; @@ -331,6 +342,22 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) return label; } +#ifdef HAVE_AUDIOUNITS +void +PluginManager::au_refresh () +{ + au_discover(); +} + +int +PluginManager::au_discover () +{ + _au_plugin_info = AUPluginInfo::discover(); + return 0; +} + +#endif + #ifdef VST_SUPPORT void @@ -389,6 +416,7 @@ int PluginManager::vst_discover (string path) { FSTInfo* finfo; + char buf[32]; if ((finfo = fst_get_info (const_cast (path.c_str()))) == 0) { warning << "Cannot get VST information from " << path << endmsg; @@ -411,6 +439,9 @@ PluginManager::vst_discover (string path) info->name = finfo->name; } + + snprintf (buf, sizeof (buf), "%d", finfo->uniqueID); + info->uniqueID = buf; info->category = "VST"; info->path = path; // need to set info->creator but FST doesn't provide it diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 26b38658b4..d6df1c2a2b 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1263,7 +1263,13 @@ Route::check_some_plugin_counts (list& iclist, int32_t required_inp } (*i).in = required_inputs; - (*i).out = (*i).insert->compute_output_streams ((*i).cnt); + + if (((*i).out = (*i).insert->compute_output_streams ((*i).cnt)) < 0) { + if (err_streams) { + *err_streams = required_inputs; + } + return -1; + } required_inputs = (*i).out; } @@ -1560,22 +1566,29 @@ Route::add_redirect_from_xml (const XMLNode& node) if ((prop = node.property ("type")) != 0) { boost::shared_ptr insert; + bool have_insert = false; - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "vst") { - + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "vst" || + prop->value() == "audiounit") { + insert.reset (new PluginInsert(_session, node)); + have_insert = true; } else if (prop->value() == "port") { insert.reset (new PortInsert (_session, node)); + have_insert = true; } else { error << string_compose(_("unknown Insert type \"%1\"; ignored"), prop->value()) << endmsg; } - add_redirect (insert, this); + if (have_insert) { + add_redirect (insert, this); + } } else { error << _("Insert XML node has no type property") << endmsg; diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index 9ec6994e3c..2211b028a7 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -141,7 +141,7 @@ VSTPlugin::get_state() { XMLNode *root = new XMLNode (state_node_name()); LocaleGuard lg (X_("POSIX")); - + if (_plugin->flags & effFlagsProgramChunks) { /* fetch the current chunk */ @@ -408,10 +408,12 @@ VSTPlugin::activate () _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f); } -uint32_t +string VSTPlugin::unique_id() const { - return _plugin->uniqueID; + char buf[32]; + snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID); + return string (buf); }