2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "pbd/transmitter.h"
25 #include "pbd/xml++.h"
26 #include "pbd/whitespace.h"
27 #include "pbd/pathscanner.h"
29 #include <glibmm/thread.h>
30 #include <glibmm/fileutils.h>
31 #include <glibmm/miscutils.h>
33 #include "ardour/ardour.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/io.h"
36 #include "ardour/audio_unit.h"
37 #include "ardour/session.h"
38 #include "ardour/utils.h"
40 #include <appleutility/CAAudioUnit.h>
41 #include <appleutility/CAAUParameter.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <CoreServices/CoreServices.h>
50 using namespace ARDOUR;
52 #ifndef AU_STATE_SUPPORT
53 static bool seen_get_state_message = false;
54 static bool seen_set_state_message = false;
55 static bool seen_loading_message = false;
56 static bool seen_saving_message = false;
59 AUPluginInfo::CachedInfoMap AUPluginInfo::cached_info;
61 static string preset_search_path = "/Library/Audio/Presets:/Network/Library/Audio/Presets";
62 static string preset_suffix = ".aupreset";
63 static bool preset_search_path_initialized = false;
66 _render_callback(void *userData,
67 AudioUnitRenderActionFlags *ioActionFlags,
68 const AudioTimeStamp *inTimeStamp,
70 UInt32 inNumberFrames,
71 AudioBufferList* ioData)
73 return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
77 save_property_list (CFPropertyListRef propertyList, Glib::ustring path)
83 // Convert the property list into XML data.
85 xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
88 error << _("Could not create XML version of property list") << endmsg;
92 // Write the XML data to the file.
94 fd = open (path.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0664);
96 if (errno == EEXIST) {
97 /* tell any UI's that this file already exists and ask them what to do */
98 bool overwrite = Plugin::PresetFileExists(); // EMIT SIGNAL
100 fd = open (path.c_str(), O_WRONLY, 0664);
106 error << string_compose (_("Cannot open preset file %1 (%2)"), path, strerror (errno)) << endmsg;
111 size_t cnt = CFDataGetLength (xmlData);
113 if (write (fd, CFDataGetBytePtr (xmlData), cnt) != cnt) {
124 static CFPropertyListRef
125 load_property_list (Glib::ustring path)
128 CFPropertyListRef propertyList;
130 CFStringRef errorString;
132 // Read the XML file.
134 if ((fd = open (path.c_str(), O_RDONLY)) < 0) {
139 off_t len = lseek (fd, 0, SEEK_END);
140 char* buf = new char[len];
141 lseek (fd, 0, SEEK_SET);
143 if (read (fd, buf, len) != len) {
151 xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) buf, len, kCFAllocatorNull);
153 // Reconstitute the dictionary using the XML data.
155 propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
157 kCFPropertyListImmutable,
166 //-----------------------------------------------------------------------------
168 set_preset_name_in_plist (CFPropertyListRef plist, string preset_name)
173 CFStringRef pn = CFStringCreateWithCString (kCFAllocatorDefault, preset_name.c_str(), kCFStringEncodingUTF8);
175 if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
176 CFDictionarySetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey), pn);
182 //-----------------------------------------------------------------------------
184 get_preset_name_in_plist (CFPropertyListRef plist)
192 if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
193 const void *p = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
195 CFStringRef str = (CFStringRef) p;
196 int len = CFStringGetLength(str);
198 char local_buffer[len];
199 if (CFStringGetCString (str, local_buffer, len, kCFStringEncodingUTF8)) {
207 //--------------------------------------------------------------------------
208 // general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
209 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
210 Boolean ComponentDescriptionsMatch_General(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2, Boolean inIgnoreType);
211 Boolean ComponentDescriptionsMatch_General(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2, Boolean inIgnoreType)
213 if ( (inComponentDescription1 == NULL) || (inComponentDescription2 == NULL) )
216 if ( (inComponentDescription1->componentSubType == inComponentDescription2->componentSubType)
217 && (inComponentDescription1->componentManufacturer == inComponentDescription2->componentManufacturer) )
219 // only sub-type and manufacturer IDs need to be equal
222 // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
223 else if (inComponentDescription1->componentType == inComponentDescription2->componentType)
230 //--------------------------------------------------------------------------
231 // general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
232 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
233 Boolean ComponentAndDescriptionMatch_General(Component inComponent, const ComponentDescription * inComponentDescription, Boolean inIgnoreType);
234 Boolean ComponentAndDescriptionMatch_General(Component inComponent, const ComponentDescription * inComponentDescription, Boolean inIgnoreType)
237 ComponentDescription desc;
239 if ( (inComponent == NULL) || (inComponentDescription == NULL) )
242 // get the ComponentDescription of the input Component
243 status = GetComponentInfo(inComponent, &desc, NULL, NULL, NULL);
247 // check if the Component's ComponentDescription matches the input ComponentDescription
248 return ComponentDescriptionsMatch_General(&desc, inComponentDescription, inIgnoreType);
251 //--------------------------------------------------------------------------
252 // determine if 2 ComponentDescriptions are basically equal
253 // (by that, I mean that the important identifying values are compared,
254 // but not the ComponentDescription flags)
255 Boolean ComponentDescriptionsMatch(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2)
257 return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, FALSE);
260 //--------------------------------------------------------------------------
261 // determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
262 Boolean ComponentDescriptionsMatch_Loose(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2)
264 return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, TRUE);
267 //--------------------------------------------------------------------------
268 // determine if a ComponentDescription basically matches that of a particular Component
269 Boolean ComponentAndDescriptionMatch(Component inComponent, const ComponentDescription * inComponentDescription)
271 return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, FALSE);
274 //--------------------------------------------------------------------------
275 // determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
276 Boolean ComponentAndDescriptionMatch_Loosely(Component inComponent, const ComponentDescription * inComponentDescription)
278 return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, TRUE);
282 AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
283 : Plugin (engine, session),
285 unit (new CAAudioUnit),
293 if (!preset_search_path_initialized) {
294 Glib::ustring p = Glib::get_home_dir();
295 p += "/Library/Audio/Presets:";
296 p += preset_search_path;
297 preset_search_path = p;
298 preset_search_path_initialized = true;
304 AUPlugin::AUPlugin (const AUPlugin& other)
306 , comp (other.get_comp())
307 , unit (new CAAudioUnit)
308 , initialized (false)
312 , current_buffers (0)
313 , frames_processed (0)
319 AUPlugin::~AUPlugin ()
322 unit->Uninitialize ();
337 err = CAAudioUnit::Open (*(comp.get()), *unit);
339 error << _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg;
340 throw failed_constructor();
344 error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg;
345 throw failed_constructor ();
348 AURenderCallbackStruct renderCallbackInfo;
350 renderCallbackInfo.inputProc = _render_callback;
351 renderCallbackInfo.inputProcRefCon = this;
353 if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
354 0, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) {
355 cerr << "cannot install render callback (err = " << err << ')' << endl;
356 throw failed_constructor();
359 unit->GetElementCount (kAudioUnitScope_Global, global_elements);
360 unit->GetElementCount (kAudioUnitScope_Input, input_elements);
361 unit->GetElementCount (kAudioUnitScope_Output, output_elements);
363 /* these keep track of *configured* channel set up,
364 not potential set ups.
368 output_channels = -1;
370 if (_set_block_size (_session.get_block_size())) {
371 error << _("AUPlugin: cannot set processing block size") << endmsg;
372 throw failed_constructor();
375 discover_parameters ();
377 Plugin::setup_controls ();
381 AUPlugin::discover_parameters ()
383 /* discover writable parameters */
385 AudioUnitScope scopes[] = {
386 kAudioUnitScope_Global,
387 kAudioUnitScope_Output,
388 kAudioUnitScope_Input
391 descriptors.clear ();
393 for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
395 AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
397 for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
399 AUParameterDescriptor d;
401 d.id = param_info.ParamID (i);
403 const CAAUParameter* param = param_info.GetParamInfo (d.id);
404 const AudioUnitParameterInfo& info (param->ParamInfo());
406 const int len = CFStringGetLength (param->GetName());;
407 char local_buffer[len*2];
408 Boolean good = CFStringGetCString(param->GetName(),local_buffer,len*2,kCFStringEncodingMacRoman);
412 d.label = local_buffer;
415 d.scope = param_info.GetScope ();
416 d.element = param_info.GetElement ();
418 /* info.units to consider */
420 kAudioUnitParameterUnit_Generic = 0
421 kAudioUnitParameterUnit_Indexed = 1
422 kAudioUnitParameterUnit_Boolean = 2
423 kAudioUnitParameterUnit_Percent = 3
424 kAudioUnitParameterUnit_Seconds = 4
425 kAudioUnitParameterUnit_SampleFrames = 5
426 kAudioUnitParameterUnit_Phase = 6
427 kAudioUnitParameterUnit_Rate = 7
428 kAudioUnitParameterUnit_Hertz = 8
429 kAudioUnitParameterUnit_Cents = 9
430 kAudioUnitParameterUnit_RelativeSemiTones = 10
431 kAudioUnitParameterUnit_MIDINoteNumber = 11
432 kAudioUnitParameterUnit_MIDIController = 12
433 kAudioUnitParameterUnit_Decibels = 13
434 kAudioUnitParameterUnit_LinearGain = 14
435 kAudioUnitParameterUnit_Degrees = 15
436 kAudioUnitParameterUnit_EqualPowerCrossfade = 16
437 kAudioUnitParameterUnit_MixerFaderCurve1 = 17
438 kAudioUnitParameterUnit_Pan = 18
439 kAudioUnitParameterUnit_Meters = 19
440 kAudioUnitParameterUnit_AbsoluteCents = 20
441 kAudioUnitParameterUnit_Octaves = 21
442 kAudioUnitParameterUnit_BPM = 22
443 kAudioUnitParameterUnit_Beats = 23
444 kAudioUnitParameterUnit_Milliseconds = 24
445 kAudioUnitParameterUnit_Ratio = 25
448 /* info.flags to consider */
452 kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
453 kAudioUnitParameterFlag_HasClump = (1L << 20)
454 kAudioUnitParameterFlag_HasName = (1L << 21)
455 kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
456 kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
457 kAudioUnitParameterFlag_NonRealTime = (1L << 24)
458 kAudioUnitParameterFlag_CanRamp = (1L << 25)
459 kAudioUnitParameterFlag_ExpertMode = (1L << 26)
460 kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
461 kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
462 kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
463 kAudioUnitParameterFlag_IsReadable = (1L << 30)
464 kAudioUnitParameterFlag_IsWritable = (1L << 31)
467 d.lower = info.minValue;
468 d.upper = info.maxValue;
469 d.default_value = info.defaultValue;
471 d.integer_step = (info.unit & kAudioUnitParameterUnit_Indexed);
472 d.toggled = (info.unit & kAudioUnitParameterUnit_Boolean) ||
473 (d.integer_step && ((d.upper - d.lower) == 1.0));
474 d.sr_dependent = (info.unit & kAudioUnitParameterUnit_SampleFrames);
475 d.automatable = !d.toggled &&
476 !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
477 (info.flags & kAudioUnitParameterFlag_IsWritable);
479 d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
485 d.min_unbound = 0; // lower is bound
486 d.max_unbound = 0; // upper is bound
488 descriptors.push_back (d);
495 AUPlugin::unique_id () const
497 return AUPluginInfo::stringify_descriptor (comp->Desc());
501 AUPlugin::label () const
503 return _info->name.c_str();
507 AUPlugin::parameter_count () const
509 return descriptors.size();
513 AUPlugin::default_value (uint32_t port)
515 if (port < descriptors.size()) {
516 return descriptors[port].default_value;
523 AUPlugin::latency () const
525 return unit->Latency() * _session.frame_rate();
529 AUPlugin::set_parameter (uint32_t which, float val)
531 if (which < descriptors.size()) {
532 const AUParameterDescriptor& d (descriptors[which]);
533 unit->SetParameter (d.id, d.scope, d.element, val);
538 AUPlugin::get_parameter (uint32_t which) const
541 if (which < descriptors.size()) {
542 const AUParameterDescriptor& d (descriptors[which]);
543 unit->GetParameter(d.id, d.scope, d.element, val);
549 AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const
551 if (which < descriptors.size()) {
552 pd = descriptors[which];
559 AUPlugin::nth_parameter (uint32_t which, bool& ok) const
561 if (which < descriptors.size()) {
570 AUPlugin::activate ()
574 if ((err = unit->Initialize()) != noErr) {
575 error << string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err) << endmsg;
577 frames_processed = 0;
584 AUPlugin::deactivate ()
586 unit->GlobalReset ();
590 AUPlugin::set_block_size (nframes_t nframes)
592 _set_block_size (nframes);
596 AUPlugin::_set_block_size (nframes_t nframes)
598 bool was_initialized = initialized;
599 UInt32 numFrames = nframes;
603 unit->Uninitialize ();
607 if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
608 0, &numFrames, sizeof (numFrames))) != noErr) {
609 cerr << "cannot set max frames (err = " << err << ')' << endl;
613 if (was_initialized) {
621 AUPlugin::configure_io (int32_t in, int32_t out)
623 AudioStreamBasicDescription streamFormat;
625 streamFormat.mSampleRate = _session.frame_rate();
626 streamFormat.mFormatID = kAudioFormatLinearPCM;
627 streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
629 #ifdef __LITTLE_ENDIAN__
632 streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
635 streamFormat.mBitsPerChannel = 32;
636 streamFormat.mFramesPerPacket = 1;
638 /* apple says that for non-interleaved data, these
639 values always refer to a single channel.
641 streamFormat.mBytesPerPacket = 4;
642 streamFormat.mBytesPerFrame = 4;
644 streamFormat.mChannelsPerFrame = in;
646 if (set_input_format (streamFormat) != 0) {
650 streamFormat.mChannelsPerFrame = out;
652 if (set_output_format (streamFormat) != 0) {
656 return Plugin::configure_io (in, out);
660 AUPlugin::can_do (int32_t in, int32_t& out)
662 // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never
663 // attempt to multiply-instantiate plugins to meet io configurations.
665 int32_t plugcnt = -1;
666 AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
670 vector<pair<int,int> >& io_configs = pinfo->cache.io_configs;
672 for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
674 int32_t possible_in = i->first;
675 int32_t possible_out = i->second;
677 if (possible_out == 0) {
678 warning << string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg;
682 if (possible_in == 0) {
684 /* instrument plugin, always legal but throws away inputs ...
687 if (possible_out == -1) {
688 /* out much match in (UNLIKELY!!) */
691 } else if (possible_out == -2) {
692 /* any configuration possible, pick matching */
695 } else if (possible_out < -2) {
696 /* explicit variable number of outputs, pick maximum */
700 /* exact number of outputs */
706 if (possible_in == -1) {
708 /* wildcard for input */
710 if (possible_out == -1) {
711 /* out much match in */
714 } else if (possible_out == -2) {
715 /* any configuration possible, pick matching */
718 } else if (possible_out < -2) {
719 /* explicit variable number of outputs, pick maximum */
723 /* exact number of outputs */
729 if (possible_in == -2) {
731 if (possible_out == -1) {
732 /* any configuration possible, pick matching */
735 } else if (possible_out == -2) {
736 error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
739 } else if (possible_out < -2) {
740 /* explicit variable number of outputs, pick maximum */
744 /* exact number of outputs */
750 if (possible_in < -2) {
752 /* explicit variable number of inputs */
754 if (in > -possible_in) {
755 /* request is too large */
759 if (possible_out == -1) {
760 /* out must match in */
763 } else if (possible_out == -2) {
764 error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
767 } else if (possible_out < -2) {
768 /* explicit variable number of outputs, pick maximum */
772 /* exact number of outputs */
778 if (possible_in == in) {
780 /* exact number of inputs ... must match obviously */
782 if (possible_out == -1) {
783 /* out must match in */
786 } else if (possible_out == -2) {
787 /* any output configuration, pick matching */
790 } else if (possible_out < -2) {
791 /* explicit variable number of outputs, pick maximum */
795 /* exact number of outputs */
811 AUPlugin::set_input_format (AudioStreamBasicDescription& fmt)
813 return set_stream_format (kAudioUnitScope_Input, input_elements, fmt);
817 AUPlugin::set_output_format (AudioStreamBasicDescription& fmt)
819 if (set_stream_format (kAudioUnitScope_Output, output_elements, fmt) != 0) {
828 buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) +
829 fmt.mChannelsPerFrame * sizeof(AudioBuffer));
831 Glib::Mutex::Lock em (_session.engine().process_lock());
832 IO::MoreOutputs (fmt.mChannelsPerFrame);
838 AUPlugin::set_stream_format (int scope, uint32_t cnt, AudioStreamBasicDescription& fmt)
842 for (uint32_t i = 0; i < cnt; ++i) {
843 if ((result = unit->SetFormat (scope, i, fmt)) != 0) {
844 error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
845 (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg;
850 if (scope == kAudioUnitScope_Input) {
851 input_channels = fmt.mChannelsPerFrame;
853 output_channels = fmt.mChannelsPerFrame;
860 AUPlugin::input_streams() const
862 if (input_channels < 0) {
863 warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg;
866 return input_channels;
871 AUPlugin::output_streams() const
873 if (output_channels < 0) {
874 warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg;
877 return output_channels;
881 AUPlugin::render_callback(AudioUnitRenderActionFlags *ioActionFlags,
882 const AudioTimeStamp *inTimeStamp,
884 UInt32 inNumberFrames,
885 AudioBufferList* ioData)
887 /* not much to do - the data is already in the buffers given to us in connect_and_run() */
889 if (current_maxbuf == 0) {
890 error << _("AUPlugin: render callback called illegally!") << endmsg;
891 return kAudioUnitErr_CannotDoInCurrentContext;
894 for (uint32_t i = 0; i < current_maxbuf; ++i) {
895 ioData->mBuffers[i].mNumberChannels = 1;
896 ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
897 ioData->mBuffers[i].mData = (*current_buffers)[i] + cb_offset + current_offset;
900 cb_offset += inNumberFrames;
906 AUPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset)
908 AudioUnitRenderActionFlags flags = 0;
911 current_buffers = &bufs;
912 current_maxbuf = maxbuf;
913 current_offset = offset;
916 buffers->mNumberBuffers = maxbuf;
918 for (uint32_t i = 0; i < maxbuf; ++i) {
919 buffers->mBuffers[i].mNumberChannels = 1;
920 buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
921 buffers->mBuffers[i].mData = 0;
924 ts.mSampleTime = frames_processed;
925 ts.mFlags = kAudioTimeStampSampleTimeValid;
927 if (unit->Render (&flags, &ts, 0, nframes, buffers) == noErr) {
930 frames_processed += nframes;
932 for (uint32_t i = 0; i < maxbuf; ++i) {
933 if (bufs[i] + offset != buffers->mBuffers[i].mData) {
934 memcpy (bufs[i]+offset, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
944 AUPlugin::automatable() const
946 set<uint32_t> automates;
948 for (uint32_t i = 0; i < descriptors.size(); ++i) {
949 if (descriptors[i].automatable) {
950 automates.insert (i);
958 AUPlugin::describe_parameter (uint32_t param)
960 return descriptors[param].label;
964 AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const
966 // NameValue stuff here
970 AUPlugin::parameter_is_audio (uint32_t) const
976 AUPlugin::parameter_is_control (uint32_t) const
982 AUPlugin::parameter_is_input (uint32_t) const
988 AUPlugin::parameter_is_output (uint32_t) const
994 AUPlugin::get_state()
996 LocaleGuard lg (X_("POSIX"));
997 XMLNode *root = new XMLNode (state_node_name());
999 #ifdef AU_STATE_SUPPORT
1001 CFPropertyListRef propertyList;
1003 if (unit->GetAUPreset (propertyList) != noErr) {
1007 // Convert the property list into XML data.
1009 xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
1012 error << _("Could not create XML version of property list") << endmsg;
1016 /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
1017 our state node. GACK!
1022 if (t.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData), CFDataGetLength (xmlData)))) {
1024 root->add_child_copy (*t.root());
1028 CFRelease (xmlData);
1029 CFRelease (propertyList);
1031 if (!seen_get_state_message) {
1032 info << _("Saving AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1034 seen_get_state_message = true;
1042 AUPlugin::set_state(const XMLNode& node)
1044 #ifdef AU_STATE_SUPPORT
1046 CFPropertyListRef propertyList;
1047 LocaleGuard lg (X_("POSIX"));
1049 if (node.name() != state_node_name()) {
1050 error << _("Bad node sent to AUPlugin::set_state") << endmsg;
1054 if (node.children().empty()) {
1058 XMLNode* top = node.children().front();
1059 XMLNode* copy = new XMLNode (*top);
1064 const string& xml = t.write_buffer ();
1065 CFDataRef xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) xml.data(), xml.length(), kCFAllocatorNull);
1066 CFStringRef errorString;
1068 propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
1070 kCFPropertyListImmutable,
1073 CFRelease (xmlData);
1076 if (unit->SetAUPreset (propertyList) == noErr) {
1079 CFRelease (propertyList);
1084 if (!seen_set_state_message) {
1085 info << _("Restoring AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1093 AUPlugin::load_preset (const string preset_label)
1095 #ifdef AU_STATE_SUPPORT
1097 CFPropertyListRef propertyList;
1099 PresetMap::iterator x = preset_map.find (preset_label);
1101 if (x == preset_map.end()) {
1105 if ((propertyList = load_property_list (x->second)) != 0) {
1106 if (unit->SetAUPreset (propertyList) == noErr) {
1109 CFRelease(propertyList);
1114 if (!seen_loading_message) {
1115 info << _("Loading AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1117 seen_loading_message = true;
1124 AUPlugin::save_preset (string preset_name)
1126 #ifdef AU_STATE_SUPPORT
1127 CFPropertyListRef propertyList;
1128 vector<Glib::ustring> v;
1129 Glib::ustring user_preset_path;
1132 std::string m = maker();
1133 std::string n = name();
1135 strip_whitespace_edges (m);
1136 strip_whitespace_edges (n);
1138 v.push_back (Glib::get_home_dir());
1139 v.push_back ("Library");
1140 v.push_back ("Audio");
1141 v.push_back ("Presets");
1145 user_preset_path = Glib::build_filename (v);
1147 if (g_mkdir_with_parents (user_preset_path.c_str(), 0775) < 0) {
1148 error << string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path) << endmsg;
1152 if (unit->GetAUPreset (propertyList) != noErr) {
1156 // add the actual preset name */
1158 v.push_back (preset_name + preset_suffix);
1162 user_preset_path = Glib::build_filename (v);
1164 set_preset_name_in_plist (propertyList, preset_name);
1166 if (save_property_list (propertyList, user_preset_path)) {
1167 error << string_compose (_("Saving plugin state to %1 failed"), user_preset_path) << endmsg;
1171 CFRelease(propertyList);
1175 if (!seen_saving_message) {
1176 info << _("Saving AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1178 seen_saving_message = true;
1184 //-----------------------------------------------------------------------------
1185 // this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
1187 GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary, CFStringRef inDictionaryKey, Boolean * outSuccess)
1189 CFNumberRef cfNumber;
1190 SInt32 numberValue = 0;
1191 Boolean dummySuccess;
1193 if (outSuccess == NULL)
1194 outSuccess = &dummySuccess;
1195 if ( (inAUStateDictionary == NULL) || (inDictionaryKey == NULL) )
1197 *outSuccess = FALSE;
1201 cfNumber = (CFNumberRef) CFDictionaryGetValue(inAUStateDictionary, inDictionaryKey);
1202 if (cfNumber == NULL)
1204 *outSuccess = FALSE;
1207 *outSuccess = CFNumberGetValue(cfNumber, kCFNumberSInt32Type, &numberValue);
1215 GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData, ComponentDescription * outComponentDescription)
1217 CFDictionaryRef auStateDictionary;
1218 ComponentDescription tempDesc = {0};
1219 SInt32 versionValue;
1222 if ( (inAUStateData == NULL) || (outComponentDescription == NULL) )
1225 // the property list for AU state data must be of the dictionary type
1226 if (CFGetTypeID(inAUStateData) != CFDictionaryGetTypeID()) {
1227 return kAudioUnitErr_InvalidPropertyValue;
1230 auStateDictionary = (CFDictionaryRef)inAUStateData;
1232 // first check to make sure that the version of the AU state data is one that we know understand
1233 // XXX should I really do this? later versions would probably still hold these ID keys, right?
1234 versionValue = GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetVersionKey), &gotValue);
1237 return kAudioUnitErr_InvalidPropertyValue;
1239 #define kCurrentSavedStateVersion 0
1240 if (versionValue != kCurrentSavedStateVersion) {
1241 return kAudioUnitErr_InvalidPropertyValue;
1244 // grab the ComponentDescription values from the AU state data
1245 tempDesc.componentType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetTypeKey), NULL);
1246 tempDesc.componentSubType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetSubtypeKey), NULL);
1247 tempDesc.componentManufacturer = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetManufacturerKey), NULL);
1248 // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
1249 if ( (tempDesc.componentType == 0) || (tempDesc.componentSubType == 0) || (tempDesc.componentManufacturer == 0) )
1250 return kAudioUnitErr_InvalidPropertyValue;
1252 *outComponentDescription = tempDesc;
1257 static bool au_preset_filter (const string& str, void* arg)
1259 /* Not a dotfile, has a prefix before a period, suffix is aupreset */
1263 ret = (str[0] != '.' && str.length() > 9 && str.find (preset_suffix) == (str.length() - preset_suffix.length()));
1267 /* check the preset file path name against this plugin
1268 ID. The idea is that all preset files for this plugin
1269 include "<manufacturer>/<plugin-name>" in their path.
1272 Plugin* p = (Plugin *) arg;
1273 string match = p->maker();
1277 ret = str.find (match) != string::npos;
1280 string m = p->maker ();
1281 string n = p->name ();
1282 strip_whitespace_edges (m);
1283 strip_whitespace_edges (n);
1288 ret = str.find (match) != string::npos;
1296 check_and_get_preset_name (Component component, const string& pathstr, string& preset_name)
1299 CFPropertyListRef plist;
1300 ComponentDescription presetDesc;
1303 plist = load_property_list (pathstr);
1309 // get the ComponentDescription from the AU preset file
1311 status = GetAUComponentDescriptionFromStateData(plist, &presetDesc);
1313 if (status == noErr) {
1314 if (ComponentAndDescriptionMatch_Loosely(component, &presetDesc)) {
1316 /* try to get the preset name from the property list */
1318 if (CFGetTypeID(plist) == CFDictionaryGetTypeID()) {
1320 const void* psk = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
1324 const char* p = CFStringGetCStringPtr ((CFStringRef) psk, kCFStringEncodingUTF8);
1327 char buf[PATH_MAX+1];
1329 if (CFStringGetCString ((CFStringRef)psk, buf, sizeof (buf), kCFStringEncodingUTF8)) {
1344 AUPlugin::current_preset() const
1348 #ifdef AU_STATE_SUPPORT
1349 CFPropertyListRef propertyList;
1351 if (unit->GetAUPreset (propertyList) == noErr) {
1352 preset_name = get_preset_name_in_plist (propertyList);
1353 CFRelease(propertyList);
1360 AUPlugin::get_presets ()
1362 vector<string*>* preset_files;
1363 vector<string> presets;
1364 PathScanner scanner;
1366 preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
1368 if (!preset_files) {
1372 for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
1374 string path = *(*x);
1377 /* make an initial guess at the preset name using the path */
1379 preset_name = Glib::path_get_basename (path);
1380 preset_name = preset_name.substr (0, preset_name.find_last_of ('.'));
1382 /* check that this preset file really matches this plugin
1383 and potentially get the "real" preset name from
1387 if (check_and_get_preset_name (get_comp()->Comp(), path, preset_name)) {
1388 presets.push_back (preset_name);
1389 preset_map[preset_name] = path;
1395 delete preset_files;
1401 AUPlugin::has_editor () const
1403 // even if the plugin doesn't have its own editor, the AU API can be used
1404 // to create one that looks native.
1408 AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
1414 AUPluginInfo::~AUPluginInfo ()
1419 AUPluginInfo::load (Session& session)
1424 boost::shared_ptr<CAComponent> comp (new CAComponent(*descriptor));
1426 if (!comp->IsValid()) {
1427 error << ("AudioUnit: not a valid Component") << endmsg;
1429 plugin.reset (new AUPlugin (session.engine(), session, comp));
1432 plugin->set_info (PluginInfoPtr (new AUPluginInfo (*this)));
1436 catch (failed_constructor &err) {
1437 return PluginPtr ();
1442 AUPluginInfo::au_cache_path ()
1444 return Glib::build_filename (ARDOUR::get_user_ardour_path(), "au_cache");
1448 AUPluginInfo::discover ()
1452 if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) {
1453 ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
1456 PluginInfoList plugs;
1458 discover_fx (plugs);
1459 discover_music (plugs);
1460 discover_generators (plugs);
1466 AUPluginInfo::discover_music (PluginInfoList& plugs)
1468 CAComponentDescription desc;
1469 desc.componentFlags = 0;
1470 desc.componentFlagsMask = 0;
1471 desc.componentSubType = 0;
1472 desc.componentManufacturer = 0;
1473 desc.componentType = kAudioUnitType_MusicEffect;
1475 discover_by_description (plugs, desc);
1479 AUPluginInfo::discover_fx (PluginInfoList& plugs)
1481 CAComponentDescription desc;
1482 desc.componentFlags = 0;
1483 desc.componentFlagsMask = 0;
1484 desc.componentSubType = 0;
1485 desc.componentManufacturer = 0;
1486 desc.componentType = kAudioUnitType_Effect;
1488 discover_by_description (plugs, desc);
1492 AUPluginInfo::discover_generators (PluginInfoList& plugs)
1494 CAComponentDescription desc;
1495 desc.componentFlags = 0;
1496 desc.componentFlagsMask = 0;
1497 desc.componentSubType = 0;
1498 desc.componentManufacturer = 0;
1499 desc.componentType = kAudioUnitType_Generator;
1501 discover_by_description (plugs, desc);
1505 AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
1509 comp = FindNextComponent (NULL, &desc);
1511 while (comp != NULL) {
1512 CAComponentDescription temp;
1513 GetComponentInfo (comp, &temp, NULL, NULL, NULL);
1515 AUPluginInfoPtr info (new AUPluginInfo
1516 (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
1518 /* no panners, format converters or i/o AU's for our purposes
1521 switch (info->descriptor->Type()) {
1522 case kAudioUnitType_Panner:
1523 case kAudioUnitType_OfflineEffect:
1524 case kAudioUnitType_FormatConverter:
1526 case kAudioUnitType_Output:
1527 case kAudioUnitType_MusicDevice:
1528 case kAudioUnitType_MusicEffect:
1529 case kAudioUnitType_Effect:
1530 case kAudioUnitType_Mixer:
1531 case kAudioUnitType_Generator:
1537 switch (info->descriptor->SubType()) {
1538 case kAudioUnitSubType_DefaultOutput:
1539 case kAudioUnitSubType_SystemOutput:
1540 case kAudioUnitSubType_GenericOutput:
1541 case kAudioUnitSubType_AUConverter:
1542 /* we don't want output units here */
1546 case kAudioUnitSubType_DLSSynth:
1547 info->category = "DLS Synth";
1550 case kAudioUnitSubType_Varispeed:
1551 info->category = "Varispeed";
1554 case kAudioUnitSubType_Delay:
1555 info->category = "Delay";
1558 case kAudioUnitSubType_LowPassFilter:
1559 info->category = "Low-pass Filter";
1562 case kAudioUnitSubType_HighPassFilter:
1563 info->category = "High-pass Filter";
1566 case kAudioUnitSubType_BandPassFilter:
1567 info->category = "Band-pass Filter";
1570 case kAudioUnitSubType_HighShelfFilter:
1571 info->category = "High-shelf Filter";
1574 case kAudioUnitSubType_LowShelfFilter:
1575 info->category = "Low-shelf Filter";
1578 case kAudioUnitSubType_ParametricEQ:
1579 info->category = "Parametric EQ";
1582 case kAudioUnitSubType_GraphicEQ:
1583 info->category = "Graphic EQ";
1586 case kAudioUnitSubType_PeakLimiter:
1587 info->category = "Peak Limiter";
1590 case kAudioUnitSubType_DynamicsProcessor:
1591 info->category = "Dynamics Processor";
1594 case kAudioUnitSubType_MultiBandCompressor:
1595 info->category = "Multiband Compressor";
1598 case kAudioUnitSubType_MatrixReverb:
1599 info->category = "Matrix Reverb";
1602 case kAudioUnitSubType_SampleDelay:
1603 info->category = "Sample Delay";
1606 case kAudioUnitSubType_Pitch:
1607 info->category = "Pitch";
1610 case kAudioUnitSubType_NetSend:
1611 info->category = "Net Sender";
1614 case kAudioUnitSubType_3DMixer:
1615 info->category = "3DMixer";
1618 case kAudioUnitSubType_MatrixMixer:
1619 info->category = "MatrixMixer";
1622 case kAudioUnitSubType_ScheduledSoundPlayer:
1623 info->category = "Scheduled Sound Player";
1627 case kAudioUnitSubType_AudioFilePlayer:
1628 info->category = "Audio File Player";
1631 case kAudioUnitSubType_NetReceive:
1632 info->category = "Net Receiver";
1636 info->category = "";
1639 AUPluginInfo::get_names (temp, info->name, info->creator);
1641 info->type = ARDOUR::AudioUnit;
1642 info->unique_id = stringify_descriptor (*info->descriptor);
1644 /* XXX not sure of the best way to handle plugin versioning yet
1647 CAComponent cacomp (*info->descriptor);
1649 if (cacomp.GetResourceVersion (info->version) != noErr) {
1653 if (cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name)) {
1655 /* here we have to map apple's wildcard system to a simple pair
1656 of values. in ::can_do() we use the whole system, but here
1657 we need a single pair of values. XXX probably means we should
1658 remove any use of these values.
1661 info->n_inputs = info->cache.io_configs.front().first;
1662 info->n_outputs = info->cache.io_configs.front().second;
1664 if (info->cache.io_configs.size() > 1) {
1665 cerr << "ODD: variable IO config for " << info->unique_id << endl;
1668 plugs.push_back (info);
1671 error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg;
1674 comp = FindNextComponent (comp, &desc);
1679 AUPluginInfo::cached_io_configuration (const std::string& unique_id,
1682 AUPluginCachedInfo& cinfo,
1683 const std::string& name)
1688 /* concatenate unique ID with version to provide a key for cached info lookup.
1689 this ensures we don't get stale information, or should if plugin developers
1690 follow Apple "guidelines".
1693 snprintf (buf, sizeof (buf), "%u", version);
1698 CachedInfoMap::iterator cim = cached_info.find (id);
1700 if (cim != cached_info.end()) {
1701 cinfo = cim->second;
1706 AUChannelInfo* channel_info;
1710 ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name));
1714 if (CAAudioUnit::Open (comp, unit) != noErr) {
1720 warning << string_compose (_("Could not load AU plugin %1 - ignored"), name) << endmsg;
1721 cerr << string_compose (_("Could not load AU plugin %1 - ignored"), name) << endl;
1726 if ((ret = unit.GetChannelInfo (&channel_info, cnt)) < 0) {
1731 /* no explicit info available */
1733 cinfo.io_configs.push_back (pair<int,int> (-1, -1));
1737 /* store each configuration */
1739 for (uint32_t n = 0; n < cnt; ++n) {
1740 cinfo.io_configs.push_back (pair<int,int> (channel_info[n].inChannels,
1741 channel_info[n].outChannels));
1744 free (channel_info);
1747 add_cached_info (id, cinfo);
1748 save_cached_info ();
1754 AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo)
1756 cached_info[id] = cinfo;
1760 AUPluginInfo::save_cached_info ()
1764 node = new XMLNode (X_("AudioUnitPluginCache"));
1766 for (map<string,AUPluginCachedInfo>::iterator i = cached_info.begin(); i != cached_info.end(); ++i) {
1767 XMLNode* parent = new XMLNode (X_("plugin"));
1768 parent->add_property ("id", i->first);
1769 node->add_child_nocopy (*parent);
1771 for (vector<pair<int, int> >::iterator j = i->second.io_configs.begin(); j != i->second.io_configs.end(); ++j) {
1773 XMLNode* child = new XMLNode (X_("io"));
1776 snprintf (buf, sizeof (buf), "%d", j->first);
1777 child->add_property (X_("in"), buf);
1778 snprintf (buf, sizeof (buf), "%d", j->second);
1779 child->add_property (X_("out"), buf);
1780 parent->add_child_nocopy (*child);
1785 Glib::ustring path = au_cache_path ();
1788 tree.set_root (node);
1790 if (!tree.write (path)) {
1791 error << string_compose (_("could not save AU cache to %1"), path) << endmsg;
1792 unlink (path.c_str());
1797 AUPluginInfo::load_cached_info ()
1799 Glib::ustring path = au_cache_path ();
1802 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1807 const XMLNode* root (tree.root());
1809 if (root->name() != X_("AudioUnitPluginCache")) {
1813 cached_info.clear ();
1815 const XMLNodeList children = root->children();
1817 for (XMLNodeConstIterator iter = children.begin(); iter != children.end(); ++iter) {
1819 const XMLNode* child = *iter;
1821 if (child->name() == X_("plugin")) {
1823 const XMLNode* gchild;
1824 const XMLNodeList gchildren = child->children();
1825 const XMLProperty* prop = child->property (X_("id"));
1831 std::string id = prop->value();
1832 AUPluginCachedInfo cinfo;
1834 for (XMLNodeConstIterator giter = gchildren.begin(); giter != gchildren.end(); giter++) {
1838 if (gchild->name() == X_("io")) {
1842 const XMLProperty* iprop;
1843 const XMLProperty* oprop;
1845 if (((iprop = gchild->property (X_("in"))) != 0) &&
1846 ((oprop = gchild->property (X_("out"))) != 0)) {
1847 in = atoi (iprop->value());
1848 out = atoi (iprop->value());
1850 cinfo.io_configs.push_back (pair<int,int> (in, out));
1855 if (cinfo.io_configs.size()) {
1856 add_cached_info (id, cinfo);
1865 AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker)
1867 CFStringRef itemName = NULL;
1869 // Marc Poirier-style item name
1870 CAComponent auComponent (comp_desc);
1871 if (auComponent.IsValid()) {
1872 CAComponentDescription dummydesc;
1873 Handle nameHandle = NewHandle(sizeof(void*));
1874 if (nameHandle != NULL) {
1875 OSErr err = GetComponentInfo(auComponent.Comp(), &dummydesc, nameHandle, NULL, NULL);
1877 ConstStr255Param nameString = (ConstStr255Param) (*nameHandle);
1878 if (nameString != NULL) {
1879 itemName = CFStringCreateWithPascalString(kCFAllocatorDefault, nameString, CFStringGetSystemEncoding());
1882 DisposeHandle(nameHandle);
1886 // if Marc-style fails, do the original way
1887 if (itemName == NULL) {
1888 CFStringRef compTypeString = UTCreateStringForOSType(comp_desc.componentType);
1889 CFStringRef compSubTypeString = UTCreateStringForOSType(comp_desc.componentSubType);
1890 CFStringRef compManufacturerString = UTCreateStringForOSType(comp_desc.componentManufacturer);
1892 itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
1893 compTypeString, compManufacturerString, compSubTypeString);
1895 if (compTypeString != NULL)
1896 CFRelease(compTypeString);
1897 if (compSubTypeString != NULL)
1898 CFRelease(compSubTypeString);
1899 if (compManufacturerString != NULL)
1900 CFRelease(compManufacturerString);
1903 string str = CFStringRefToStdString(itemName);
1904 string::size_type colon = str.find (':');
1907 name = str.substr (colon+1);
1908 maker = str.substr (0, colon);
1909 // strip_whitespace_edges (maker);
1910 // strip_whitespace_edges (name);
1917 // from CAComponentDescription.cpp (in libs/appleutility in ardour source)
1918 extern char *StringForOSType (OSType t, char *writeLocation);
1921 AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
1926 s << StringForOSType (desc.Type(), str);
1929 s << StringForOSType (desc.SubType(), str);
1932 s << StringForOSType (desc.Manu(), str);