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>
45 #include <AudioUnit/AudioUnit.h>
51 using namespace ARDOUR
;
53 #ifndef AU_STATE_SUPPORT
54 static bool seen_get_state_message
= false;
55 static bool seen_set_state_message
= false;
56 static bool seen_loading_message
= false;
57 static bool seen_saving_message
= false;
60 AUPluginInfo::CachedInfoMap
AUPluginInfo::cached_info
;
62 static string preset_search_path
= "/Library/Audio/Presets:/Network/Library/Audio/Presets";
63 static string preset_suffix
= ".aupreset";
64 static bool preset_search_path_initialized
= false;
67 _render_callback(void *userData
,
68 AudioUnitRenderActionFlags
*ioActionFlags
,
69 const AudioTimeStamp
*inTimeStamp
,
71 UInt32 inNumberFrames
,
72 AudioBufferList
* ioData
)
74 return ((AUPlugin
*)userData
)->render_callback (ioActionFlags
, inTimeStamp
, inBusNumber
, inNumberFrames
, ioData
);
78 save_property_list (CFPropertyListRef propertyList
, Glib::ustring path
)
84 // Convert the property list into XML data.
86 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
89 error
<< _("Could not create XML version of property list") << endmsg
;
93 // Write the XML data to the file.
95 fd
= open (path
.c_str(), O_WRONLY
|O_CREAT
|O_EXCL
, 0664);
97 if (errno
== EEXIST
) {
98 /* tell any UI's that this file already exists and ask them what to do */
99 bool overwrite
= Plugin::PresetFileExists(); // EMIT SIGNAL
101 fd
= open (path
.c_str(), O_WRONLY
, 0664);
107 error
<< string_compose (_("Cannot open preset file %1 (%2)"), path
, strerror (errno
)) << endmsg
;
112 size_t cnt
= CFDataGetLength (xmlData
);
114 if (write (fd
, CFDataGetBytePtr (xmlData
), cnt
) != (ssize_t
) cnt
) {
125 static CFPropertyListRef
126 load_property_list (Glib::ustring path
)
129 CFPropertyListRef propertyList
;
131 CFStringRef errorString
;
133 // Read the XML file.
135 if ((fd
= open (path
.c_str(), O_RDONLY
)) < 0) {
140 off_t len
= lseek (fd
, 0, SEEK_END
);
141 char* buf
= new char[len
];
142 lseek (fd
, 0, SEEK_SET
);
144 if (read (fd
, buf
, len
) != len
) {
152 xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) buf
, len
, kCFAllocatorNull
);
154 // Reconstitute the dictionary using the XML data.
156 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
158 kCFPropertyListImmutable
,
167 //-----------------------------------------------------------------------------
169 set_preset_name_in_plist (CFPropertyListRef plist
, string preset_name
)
174 CFStringRef pn
= CFStringCreateWithCString (kCFAllocatorDefault
, preset_name
.c_str(), kCFStringEncodingUTF8
);
176 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
177 CFDictionarySetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
), pn
);
183 //-----------------------------------------------------------------------------
185 get_preset_name_in_plist (CFPropertyListRef plist
)
193 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
194 const void *p
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
196 CFStringRef str
= (CFStringRef
) p
;
197 int len
= CFStringGetLength(str
);
199 char local_buffer
[len
];
200 if (CFStringGetCString (str
, local_buffer
, len
, kCFStringEncodingUTF8
)) {
208 //--------------------------------------------------------------------------
209 // general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
210 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
211 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
);
212 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
)
214 if ( (inComponentDescription1
== NULL
) || (inComponentDescription2
== NULL
) )
217 if ( (inComponentDescription1
->componentSubType
== inComponentDescription2
->componentSubType
)
218 && (inComponentDescription1
->componentManufacturer
== inComponentDescription2
->componentManufacturer
) )
220 // only sub-type and manufacturer IDs need to be equal
223 // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
224 else if (inComponentDescription1
->componentType
== inComponentDescription2
->componentType
)
231 //--------------------------------------------------------------------------
232 // general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
233 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
234 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
);
235 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
)
238 ComponentDescription desc
;
240 if ( (inComponent
== NULL
) || (inComponentDescription
== NULL
) )
243 // get the ComponentDescription of the input Component
244 status
= GetComponentInfo(inComponent
, &desc
, NULL
, NULL
, NULL
);
248 // check if the Component's ComponentDescription matches the input ComponentDescription
249 return ComponentDescriptionsMatch_General(&desc
, inComponentDescription
, inIgnoreType
);
252 //--------------------------------------------------------------------------
253 // determine if 2 ComponentDescriptions are basically equal
254 // (by that, I mean that the important identifying values are compared,
255 // but not the ComponentDescription flags)
256 Boolean
ComponentDescriptionsMatch(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
258 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, FALSE
);
261 //--------------------------------------------------------------------------
262 // determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
263 Boolean
ComponentDescriptionsMatch_Loose(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
265 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, TRUE
);
268 //--------------------------------------------------------------------------
269 // determine if a ComponentDescription basically matches that of a particular Component
270 Boolean
ComponentAndDescriptionMatch(Component inComponent
, const ComponentDescription
* inComponentDescription
)
272 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, FALSE
);
275 //--------------------------------------------------------------------------
276 // determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
277 Boolean
ComponentAndDescriptionMatch_Loosely(Component inComponent
, const ComponentDescription
* inComponentDescription
)
279 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, TRUE
);
283 AUPlugin::AUPlugin (AudioEngine
& engine
, Session
& session
, boost::shared_ptr
<CAComponent
> _comp
)
284 : Plugin (engine
, session
),
286 unit (new CAAudioUnit
),
294 if (!preset_search_path_initialized
) {
295 Glib::ustring p
= Glib::get_home_dir();
296 p
+= "/Library/Audio/Presets:";
297 p
+= preset_search_path
;
298 preset_search_path
= p
;
299 preset_search_path_initialized
= true;
305 AUPlugin::AUPlugin (const AUPlugin
& other
)
307 , comp (other
.get_comp())
308 , unit (new CAAudioUnit
)
309 , initialized (false)
313 , current_buffers (0)
314 , frames_processed (0)
320 AUPlugin::~AUPlugin ()
323 unit
->Uninitialize ();
332 AUPlugin::discover_factory_presets ()
336 OSStatus err
= unit
->GetPropertyInfo (kAudioUnitProperty_FactoryPresets
,
337 kAudioUnitScope_Global
, 0,
339 if (err
|| !dataSize
) {
344 dataSize
= sizeof (presets
);
346 if ((err
= unit
->GetProperty (kAudioUnitProperty_FactoryPresets
, kAudioUnitScope_Global
, 0, (void*) &presets
, &dataSize
)) != 0) {
347 cerr
<< "cannot get factory preset info: " << err
<< endl
;
351 CFIndex cnt
= CFArrayGetCount (presets
);
353 for (CFIndex i
= 0; i
< cnt
; ++i
) {
354 AUPreset
* preset
= (AUPreset
*) CFArrayGetValueAtIndex (presets
, i
);
356 string name
= CFStringRefToStdString (preset
->presetName
);
357 factory_preset_map
[name
] = preset
->presetNumber
;
367 err
= CAAudioUnit::Open (*(comp
.get()), *unit
);
369 error
<< _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg
;
370 throw failed_constructor();
374 error
<< _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg
;
375 throw failed_constructor ();
378 AURenderCallbackStruct renderCallbackInfo
;
380 renderCallbackInfo
.inputProc
= _render_callback
;
381 renderCallbackInfo
.inputProcRefCon
= this;
383 if ((err
= unit
->SetProperty (kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
,
384 0, (void*) &renderCallbackInfo
, sizeof(renderCallbackInfo
))) != 0) {
385 cerr
<< "cannot install render callback (err = " << err
<< ')' << endl
;
386 throw failed_constructor();
389 unit
->GetElementCount (kAudioUnitScope_Global
, global_elements
);
390 unit
->GetElementCount (kAudioUnitScope_Input
, input_elements
);
391 unit
->GetElementCount (kAudioUnitScope_Output
, output_elements
);
393 /* these keep track of *configured* channel set up,
394 not potential set ups.
398 output_channels
= -1;
400 if (_set_block_size (_session
.get_block_size())) {
401 error
<< _("AUPlugin: cannot set processing block size") << endmsg
;
402 throw failed_constructor();
405 discover_parameters ();
406 discover_factory_presets ();
408 Plugin::setup_controls ();
412 AUPlugin::discover_parameters ()
414 /* discover writable parameters */
416 AudioUnitScope scopes
[] = {
417 kAudioUnitScope_Global
,
418 kAudioUnitScope_Output
,
419 kAudioUnitScope_Input
422 descriptors
.clear ();
424 for (uint32_t i
= 0; i
< sizeof (scopes
) / sizeof (scopes
[0]); ++i
) {
426 AUParamInfo
param_info (unit
->AU(), false, false, scopes
[i
]);
428 for (uint32_t i
= 0; i
< param_info
.NumParams(); ++i
) {
430 AUParameterDescriptor d
;
432 d
.id
= param_info
.ParamID (i
);
434 const CAAUParameter
* param
= param_info
.GetParamInfo (d
.id
);
435 const AudioUnitParameterInfo
& info (param
->ParamInfo());
437 const int len
= CFStringGetLength (param
->GetName());;
438 char local_buffer
[len
*2];
439 Boolean good
= CFStringGetCString(param
->GetName(),local_buffer
,len
*2,kCFStringEncodingMacRoman
);
443 d
.label
= local_buffer
;
446 d
.scope
= param_info
.GetScope ();
447 d
.element
= param_info
.GetElement ();
449 /* info.units to consider */
451 kAudioUnitParameterUnit_Generic = 0
452 kAudioUnitParameterUnit_Indexed = 1
453 kAudioUnitParameterUnit_Boolean = 2
454 kAudioUnitParameterUnit_Percent = 3
455 kAudioUnitParameterUnit_Seconds = 4
456 kAudioUnitParameterUnit_SampleFrames = 5
457 kAudioUnitParameterUnit_Phase = 6
458 kAudioUnitParameterUnit_Rate = 7
459 kAudioUnitParameterUnit_Hertz = 8
460 kAudioUnitParameterUnit_Cents = 9
461 kAudioUnitParameterUnit_RelativeSemiTones = 10
462 kAudioUnitParameterUnit_MIDINoteNumber = 11
463 kAudioUnitParameterUnit_MIDIController = 12
464 kAudioUnitParameterUnit_Decibels = 13
465 kAudioUnitParameterUnit_LinearGain = 14
466 kAudioUnitParameterUnit_Degrees = 15
467 kAudioUnitParameterUnit_EqualPowerCrossfade = 16
468 kAudioUnitParameterUnit_MixerFaderCurve1 = 17
469 kAudioUnitParameterUnit_Pan = 18
470 kAudioUnitParameterUnit_Meters = 19
471 kAudioUnitParameterUnit_AbsoluteCents = 20
472 kAudioUnitParameterUnit_Octaves = 21
473 kAudioUnitParameterUnit_BPM = 22
474 kAudioUnitParameterUnit_Beats = 23
475 kAudioUnitParameterUnit_Milliseconds = 24
476 kAudioUnitParameterUnit_Ratio = 25
479 /* info.flags to consider */
483 kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
484 kAudioUnitParameterFlag_HasClump = (1L << 20)
485 kAudioUnitParameterFlag_HasName = (1L << 21)
486 kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
487 kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
488 kAudioUnitParameterFlag_NonRealTime = (1L << 24)
489 kAudioUnitParameterFlag_CanRamp = (1L << 25)
490 kAudioUnitParameterFlag_ExpertMode = (1L << 26)
491 kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
492 kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
493 kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
494 kAudioUnitParameterFlag_IsReadable = (1L << 30)
495 kAudioUnitParameterFlag_IsWritable = (1L << 31)
498 d
.lower
= info
.minValue
;
499 d
.upper
= info
.maxValue
;
500 d
.default_value
= info
.defaultValue
;
502 d
.integer_step
= (info
.unit
& kAudioUnitParameterUnit_Indexed
);
503 d
.toggled
= (info
.unit
& kAudioUnitParameterUnit_Boolean
) ||
504 (d
.integer_step
&& ((d
.upper
- d
.lower
) == 1.0));
505 d
.sr_dependent
= (info
.unit
& kAudioUnitParameterUnit_SampleFrames
);
506 d
.automatable
= !d
.toggled
&&
507 !(info
.flags
& kAudioUnitParameterFlag_NonRealTime
) &&
508 (info
.flags
& kAudioUnitParameterFlag_IsWritable
);
510 d
.logarithmic
= (info
.flags
& kAudioUnitParameterFlag_DisplayLogarithmic
);
516 d
.min_unbound
= 0; // lower is bound
517 d
.max_unbound
= 0; // upper is bound
519 descriptors
.push_back (d
);
526 AUPlugin::unique_id () const
528 return AUPluginInfo::stringify_descriptor (comp
->Desc());
532 AUPlugin::label () const
534 return _info
->name
.c_str();
538 AUPlugin::parameter_count () const
540 return descriptors
.size();
544 AUPlugin::default_value (uint32_t port
)
546 if (port
< descriptors
.size()) {
547 return descriptors
[port
].default_value
;
554 AUPlugin::latency () const
556 return unit
->Latency() * _session
.frame_rate();
560 AUPlugin::set_parameter (uint32_t which
, float val
)
562 if (which
< descriptors
.size()) {
563 const AUParameterDescriptor
& d (descriptors
[which
]);
564 unit
->SetParameter (d
.id
, d
.scope
, d
.element
, val
);
569 AUPlugin::get_parameter (uint32_t which
) const
572 if (which
< descriptors
.size()) {
573 const AUParameterDescriptor
& d (descriptors
[which
]);
574 unit
->GetParameter(d
.id
, d
.scope
, d
.element
, val
);
580 AUPlugin::get_parameter_descriptor (uint32_t which
, ParameterDescriptor
& pd
) const
582 if (which
< descriptors
.size()) {
583 pd
= descriptors
[which
];
590 AUPlugin::nth_parameter (uint32_t which
, bool& ok
) const
592 if (which
< descriptors
.size()) {
601 AUPlugin::activate ()
605 if ((err
= unit
->Initialize()) != noErr
) {
606 error
<< string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err
) << endmsg
;
608 frames_processed
= 0;
615 AUPlugin::deactivate ()
617 unit
->GlobalReset ();
621 AUPlugin::set_block_size (nframes_t nframes
)
623 _set_block_size (nframes
);
627 AUPlugin::_set_block_size (nframes_t nframes
)
629 bool was_initialized
= initialized
;
630 UInt32 numFrames
= nframes
;
634 unit
->Uninitialize ();
638 if ((err
= unit
->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
,
639 0, &numFrames
, sizeof (numFrames
))) != noErr
) {
640 cerr
<< "cannot set max frames (err = " << err
<< ')' << endl
;
644 if (was_initialized
) {
652 AUPlugin::configure_io (int32_t in
, int32_t out
)
654 AudioStreamBasicDescription streamFormat
;
656 streamFormat
.mSampleRate
= _session
.frame_rate();
657 streamFormat
.mFormatID
= kAudioFormatLinearPCM
;
658 streamFormat
.mFormatFlags
= kAudioFormatFlagIsFloat
|kAudioFormatFlagIsPacked
|kAudioFormatFlagIsNonInterleaved
;
660 #ifdef __LITTLE_ENDIAN__
663 streamFormat
.mFormatFlags
|= kAudioFormatFlagIsBigEndian
;
666 streamFormat
.mBitsPerChannel
= 32;
667 streamFormat
.mFramesPerPacket
= 1;
669 /* apple says that for non-interleaved data, these
670 values always refer to a single channel.
672 streamFormat
.mBytesPerPacket
= 4;
673 streamFormat
.mBytesPerFrame
= 4;
675 streamFormat
.mChannelsPerFrame
= in
;
677 if (set_input_format (streamFormat
) != 0) {
681 streamFormat
.mChannelsPerFrame
= out
;
683 if (set_output_format (streamFormat
) != 0) {
687 return Plugin::configure_io (in
, out
);
691 AUPlugin::can_do (int32_t in
, int32_t& out
)
693 // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never
694 // attempt to multiply-instantiate plugins to meet io configurations.
696 int32_t plugcnt
= -1;
697 AUPluginInfoPtr pinfo
= boost::dynamic_pointer_cast
<AUPluginInfo
>(get_info());
701 vector
<pair
<int,int> >& io_configs
= pinfo
->cache
.io_configs
;
703 for (vector
<pair
<int,int> >::iterator i
= io_configs
.begin(); i
!= io_configs
.end(); ++i
) {
705 int32_t possible_in
= i
->first
;
706 int32_t possible_out
= i
->second
;
708 if (possible_out
== 0) {
709 warning
<< string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg
;
713 if (possible_in
== 0) {
715 /* instrument plugin, always legal but throws away inputs ...
718 if (possible_out
== -1) {
719 /* out much match in (UNLIKELY!!) */
722 } else if (possible_out
== -2) {
723 /* any configuration possible, pick matching */
726 } else if (possible_out
< -2) {
727 /* explicit variable number of outputs, pick maximum */
731 /* exact number of outputs */
737 if (possible_in
== -1) {
739 /* wildcard for input */
741 if (possible_out
== -1) {
742 /* out much match in */
745 } else if (possible_out
== -2) {
746 /* any configuration possible, pick matching */
749 } else if (possible_out
< -2) {
750 /* explicit variable number of outputs, pick maximum */
754 /* exact number of outputs */
760 if (possible_in
== -2) {
762 if (possible_out
== -1) {
763 /* any configuration possible, pick matching */
766 } else if (possible_out
== -2) {
767 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
770 } else if (possible_out
< -2) {
771 /* explicit variable number of outputs, pick maximum */
775 /* exact number of outputs */
781 if (possible_in
< -2) {
783 /* explicit variable number of inputs */
785 if (in
> -possible_in
) {
786 /* request is too large */
790 if (possible_out
== -1) {
791 /* out must match in */
794 } else if (possible_out
== -2) {
795 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
798 } else if (possible_out
< -2) {
799 /* explicit variable number of outputs, pick maximum */
803 /* exact number of outputs */
809 if (possible_in
== in
) {
811 /* exact number of inputs ... must match obviously */
813 if (possible_out
== -1) {
814 /* out must match in */
817 } else if (possible_out
== -2) {
818 /* any output configuration, pick matching */
821 } else if (possible_out
< -2) {
822 /* explicit variable number of outputs, pick maximum */
826 /* exact number of outputs */
842 AUPlugin::set_input_format (AudioStreamBasicDescription
& fmt
)
844 return set_stream_format (kAudioUnitScope_Input
, input_elements
, fmt
);
848 AUPlugin::set_output_format (AudioStreamBasicDescription
& fmt
)
850 if (set_stream_format (kAudioUnitScope_Output
, output_elements
, fmt
) != 0) {
859 buffers
= (AudioBufferList
*) malloc (offsetof(AudioBufferList
, mBuffers
) +
860 fmt
.mChannelsPerFrame
* sizeof(AudioBuffer
));
862 Glib::Mutex::Lock
em (_session
.engine().process_lock());
863 IO::MoreOutputs (fmt
.mChannelsPerFrame
);
869 AUPlugin::set_stream_format (int scope
, uint32_t cnt
, AudioStreamBasicDescription
& fmt
)
873 for (uint32_t i
= 0; i
< cnt
; ++i
) {
874 if ((result
= unit
->SetFormat (scope
, i
, fmt
)) != 0) {
875 error
<< string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
876 (scope
== kAudioUnitScope_Input
? "input" : "output"), i
, result
) << endmsg
;
881 if (scope
== kAudioUnitScope_Input
) {
882 input_channels
= fmt
.mChannelsPerFrame
;
884 output_channels
= fmt
.mChannelsPerFrame
;
891 AUPlugin::input_streams() const
893 if (input_channels
< 0) {
894 warning
<< string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg
;
897 return input_channels
;
902 AUPlugin::output_streams() const
904 if (output_channels
< 0) {
905 warning
<< string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg
;
908 return output_channels
;
912 AUPlugin::render_callback(AudioUnitRenderActionFlags
*ioActionFlags
,
913 const AudioTimeStamp
*inTimeStamp
,
915 UInt32 inNumberFrames
,
916 AudioBufferList
* ioData
)
918 /* not much to do - the data is already in the buffers given to us in connect_and_run() */
920 if (current_maxbuf
== 0) {
921 error
<< _("AUPlugin: render callback called illegally!") << endmsg
;
922 return kAudioUnitErr_CannotDoInCurrentContext
;
925 for (uint32_t i
= 0; i
< current_maxbuf
; ++i
) {
926 ioData
->mBuffers
[i
].mNumberChannels
= 1;
927 ioData
->mBuffers
[i
].mDataByteSize
= sizeof (Sample
) * inNumberFrames
;
928 ioData
->mBuffers
[i
].mData
= (*current_buffers
)[i
] + cb_offset
+ current_offset
;
931 cb_offset
+= inNumberFrames
;
937 AUPlugin::connect_and_run (vector
<Sample
*>& bufs
, uint32_t maxbuf
, int32_t& in
, int32_t& out
, nframes_t nframes
, nframes_t offset
)
939 AudioUnitRenderActionFlags flags
= 0;
942 current_buffers
= &bufs
;
943 current_maxbuf
= maxbuf
;
944 current_offset
= offset
;
947 buffers
->mNumberBuffers
= maxbuf
;
949 for (uint32_t i
= 0; i
< maxbuf
; ++i
) {
950 buffers
->mBuffers
[i
].mNumberChannels
= 1;
951 buffers
->mBuffers
[i
].mDataByteSize
= nframes
* sizeof (Sample
);
952 buffers
->mBuffers
[i
].mData
= 0;
955 ts
.mSampleTime
= frames_processed
;
956 ts
.mFlags
= kAudioTimeStampSampleTimeValid
;
958 if (unit
->Render (&flags
, &ts
, 0, nframes
, buffers
) == noErr
) {
961 frames_processed
+= nframes
;
963 for (uint32_t i
= 0; i
< maxbuf
; ++i
) {
964 if (bufs
[i
] + offset
!= buffers
->mBuffers
[i
].mData
) {
965 memcpy (bufs
[i
]+offset
, buffers
->mBuffers
[i
].mData
, nframes
* sizeof (Sample
));
975 AUPlugin::automatable() const
977 set
<uint32_t> automates
;
979 for (uint32_t i
= 0; i
< descriptors
.size(); ++i
) {
980 if (descriptors
[i
].automatable
) {
981 automates
.insert (i
);
989 AUPlugin::describe_parameter (uint32_t param
)
991 return descriptors
[param
].label
;
995 AUPlugin::print_parameter (uint32_t param
, char* buf
, uint32_t len
) const
997 // NameValue stuff here
1001 AUPlugin::parameter_is_audio (uint32_t) const
1007 AUPlugin::parameter_is_control (uint32_t) const
1013 AUPlugin::parameter_is_input (uint32_t) const
1019 AUPlugin::parameter_is_output (uint32_t) const
1025 AUPlugin::get_state()
1027 LocaleGuard
lg (X_("POSIX"));
1028 XMLNode
*root
= new XMLNode (state_node_name());
1030 #ifdef AU_STATE_SUPPORT
1032 CFPropertyListRef propertyList
;
1034 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1038 // Convert the property list into XML data.
1040 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
1043 error
<< _("Could not create XML version of property list") << endmsg
;
1047 /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
1048 our state node. GACK!
1053 if (t
.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData
), CFDataGetLength (xmlData
)))) {
1055 root
->add_child_copy (*t
.root());
1059 CFRelease (xmlData
);
1060 CFRelease (propertyList
);
1062 if (!seen_get_state_message
) {
1063 info
<< _("Saving AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1065 seen_get_state_message
= true;
1073 AUPlugin::set_state(const XMLNode
& node
)
1075 #ifdef AU_STATE_SUPPORT
1077 CFPropertyListRef propertyList
;
1078 LocaleGuard
lg (X_("POSIX"));
1080 if (node
.name() != state_node_name()) {
1081 error
<< _("Bad node sent to AUPlugin::set_state") << endmsg
;
1085 if (node
.children().empty()) {
1089 XMLNode
* top
= node
.children().front();
1090 XMLNode
* copy
= new XMLNode (*top
);
1095 const string
& xml
= t
.write_buffer ();
1096 CFDataRef xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) xml
.data(), xml
.length(), kCFAllocatorNull
);
1097 CFStringRef errorString
;
1099 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
1101 kCFPropertyListImmutable
,
1104 CFRelease (xmlData
);
1107 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1110 CFRelease (propertyList
);
1115 if (!seen_set_state_message
) {
1116 info
<< _("Restoring AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1124 AUPlugin::load_preset (const string preset_label
)
1126 #ifdef AU_STATE_SUPPORT
1128 CFPropertyListRef propertyList
;
1130 UserPresetMap::iterator ux
;
1131 FactoryPresetMap::iterator fx
;
1133 /* look first in "user" presets */
1135 if ((ux
= user_preset_map
.find (preset_label
)) != user_preset_map
.end()) {
1137 if ((propertyList
= load_property_list (ux
->second
)) != 0) {
1138 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1141 CFRelease(propertyList
);
1144 } else if ((fx
= factory_preset_map
.find (preset_label
)) != factory_preset_map
.end()) {
1148 preset
.presetNumber
= fx
->second
;
1149 preset
.presetName
= CFStringCreateWithCString (kCFAllocatorDefault
, fx
->first
.c_str(), kCFStringEncodingUTF8
);
1151 cerr
<< "Setting factory preset " << fx
->second
<< endl
;
1153 if (unit
->SetPresentPreset (preset
) == 0) {
1161 if (!seen_loading_message
) {
1162 info
<< _("Loading AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1164 seen_loading_message
= true;
1171 AUPlugin::save_preset (string preset_name
)
1173 #ifdef AU_STATE_SUPPORT
1174 CFPropertyListRef propertyList
;
1175 vector
<Glib::ustring
> v
;
1176 Glib::ustring user_preset_path
;
1179 std::string m
= maker();
1180 std::string n
= name();
1182 strip_whitespace_edges (m
);
1183 strip_whitespace_edges (n
);
1185 v
.push_back (Glib::get_home_dir());
1186 v
.push_back ("Library");
1187 v
.push_back ("Audio");
1188 v
.push_back ("Presets");
1192 user_preset_path
= Glib::build_filename (v
);
1194 if (g_mkdir_with_parents (user_preset_path
.c_str(), 0775) < 0) {
1195 error
<< string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path
) << endmsg
;
1199 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1203 // add the actual preset name */
1205 v
.push_back (preset_name
+ preset_suffix
);
1209 user_preset_path
= Glib::build_filename (v
);
1211 set_preset_name_in_plist (propertyList
, preset_name
);
1213 if (save_property_list (propertyList
, user_preset_path
)) {
1214 error
<< string_compose (_("Saving plugin state to %1 failed"), user_preset_path
) << endmsg
;
1218 CFRelease(propertyList
);
1222 if (!seen_saving_message
) {
1223 info
<< _("Saving AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1225 seen_saving_message
= true;
1231 //-----------------------------------------------------------------------------
1232 // this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
1234 GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary
, CFStringRef inDictionaryKey
, Boolean
* outSuccess
)
1236 CFNumberRef cfNumber
;
1237 SInt32 numberValue
= 0;
1238 Boolean dummySuccess
;
1240 if (outSuccess
== NULL
)
1241 outSuccess
= &dummySuccess
;
1242 if ( (inAUStateDictionary
== NULL
) || (inDictionaryKey
== NULL
) )
1244 *outSuccess
= FALSE
;
1248 cfNumber
= (CFNumberRef
) CFDictionaryGetValue(inAUStateDictionary
, inDictionaryKey
);
1249 if (cfNumber
== NULL
)
1251 *outSuccess
= FALSE
;
1254 *outSuccess
= CFNumberGetValue(cfNumber
, kCFNumberSInt32Type
, &numberValue
);
1262 GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData
, ComponentDescription
* outComponentDescription
)
1264 CFDictionaryRef auStateDictionary
;
1265 ComponentDescription tempDesc
= {0};
1266 SInt32 versionValue
;
1269 if ( (inAUStateData
== NULL
) || (outComponentDescription
== NULL
) )
1272 // the property list for AU state data must be of the dictionary type
1273 if (CFGetTypeID(inAUStateData
) != CFDictionaryGetTypeID()) {
1274 return kAudioUnitErr_InvalidPropertyValue
;
1277 auStateDictionary
= (CFDictionaryRef
)inAUStateData
;
1279 // first check to make sure that the version of the AU state data is one that we know understand
1280 // XXX should I really do this? later versions would probably still hold these ID keys, right?
1281 versionValue
= GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetVersionKey
), &gotValue
);
1284 return kAudioUnitErr_InvalidPropertyValue
;
1286 #define kCurrentSavedStateVersion 0
1287 if (versionValue
!= kCurrentSavedStateVersion
) {
1288 return kAudioUnitErr_InvalidPropertyValue
;
1291 // grab the ComponentDescription values from the AU state data
1292 tempDesc
.componentType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetTypeKey
), NULL
);
1293 tempDesc
.componentSubType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetSubtypeKey
), NULL
);
1294 tempDesc
.componentManufacturer
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetManufacturerKey
), NULL
);
1295 // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
1296 if ( (tempDesc
.componentType
== 0) || (tempDesc
.componentSubType
== 0) || (tempDesc
.componentManufacturer
== 0) )
1297 return kAudioUnitErr_InvalidPropertyValue
;
1299 *outComponentDescription
= tempDesc
;
1304 static bool au_preset_filter (const string
& str
, void* arg
)
1306 /* Not a dotfile, has a prefix before a period, suffix is aupreset */
1310 ret
= (str
[0] != '.' && str
.length() > 9 && str
.find (preset_suffix
) == (str
.length() - preset_suffix
.length()));
1314 /* check the preset file path name against this plugin
1315 ID. The idea is that all preset files for this plugin
1316 include "<manufacturer>/<plugin-name>" in their path.
1319 Plugin
* p
= (Plugin
*) arg
;
1320 string match
= p
->maker();
1324 ret
= str
.find (match
) != string::npos
;
1327 string m
= p
->maker ();
1328 string n
= p
->name ();
1329 strip_whitespace_edges (m
);
1330 strip_whitespace_edges (n
);
1335 ret
= str
.find (match
) != string::npos
;
1343 check_and_get_preset_name (Component component
, const string
& pathstr
, string
& preset_name
)
1346 CFPropertyListRef plist
;
1347 ComponentDescription presetDesc
;
1350 plist
= load_property_list (pathstr
);
1356 // get the ComponentDescription from the AU preset file
1358 status
= GetAUComponentDescriptionFromStateData(plist
, &presetDesc
);
1360 if (status
== noErr
) {
1361 if (ComponentAndDescriptionMatch_Loosely(component
, &presetDesc
)) {
1363 /* try to get the preset name from the property list */
1365 if (CFGetTypeID(plist
) == CFDictionaryGetTypeID()) {
1367 const void* psk
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
1371 const char* p
= CFStringGetCStringPtr ((CFStringRef
) psk
, kCFStringEncodingUTF8
);
1374 char buf
[PATH_MAX
+1];
1376 if (CFStringGetCString ((CFStringRef
)psk
, buf
, sizeof (buf
), kCFStringEncodingUTF8
)) {
1391 AUPlugin::current_preset() const
1395 #ifdef AU_STATE_SUPPORT
1396 CFPropertyListRef propertyList
;
1398 if (unit
->GetAUPreset (propertyList
) == noErr
) {
1399 preset_name
= get_preset_name_in_plist (propertyList
);
1400 CFRelease(propertyList
);
1407 AUPlugin::get_presets ()
1409 vector
<string
> presets
;
1411 #ifdef AU_STATE_SUPPORT
1412 vector
<string
*>* preset_files
;
1413 PathScanner scanner
;
1415 user_preset_map
.clear ();
1417 preset_files
= scanner (preset_search_path
, au_preset_filter
, this, true, true, -1, true);
1419 if (!preset_files
) {
1423 for (vector
<string
*>::iterator x
= preset_files
->begin(); x
!= preset_files
->end(); ++x
) {
1425 string path
= *(*x
);
1428 /* make an initial guess at the preset name using the path */
1430 preset_name
= Glib::path_get_basename (path
);
1431 preset_name
= preset_name
.substr (0, preset_name
.find_last_of ('.'));
1433 /* check that this preset file really matches this plugin
1434 and potentially get the "real" preset name from
1438 if (check_and_get_preset_name (get_comp()->Comp(), path
, preset_name
)) {
1439 user_preset_map
[preset_name
] = path
;
1445 delete preset_files
;
1447 /* now fill the vector<string> with the names we have */
1449 for (UserPresetMap::iterator i
= user_preset_map
.begin(); i
!= user_preset_map
.end(); ++i
) {
1450 presets
.push_back (i
->first
);
1453 /* add factory presets */
1455 for (FactoryPresetMap::iterator i
= factory_preset_map
.begin(); i
!= factory_preset_map
.end(); ++i
) {
1456 presets
.push_back (i
->first
);
1465 AUPlugin::has_editor () const
1467 // even if the plugin doesn't have its own editor, the AU API can be used
1468 // to create one that looks native.
1472 AUPluginInfo::AUPluginInfo (boost::shared_ptr
<CAComponentDescription
> d
)
1478 AUPluginInfo::~AUPluginInfo ()
1483 AUPluginInfo::load (Session
& session
)
1488 boost::shared_ptr
<CAComponent
> comp (new CAComponent(*descriptor
));
1490 if (!comp
->IsValid()) {
1491 error
<< ("AudioUnit: not a valid Component") << endmsg
;
1493 plugin
.reset (new AUPlugin (session
.engine(), session
, comp
));
1496 plugin
->set_info (PluginInfoPtr (new AUPluginInfo (*this)));
1500 catch (failed_constructor
&err
) {
1501 return PluginPtr ();
1506 AUPluginInfo::au_cache_path ()
1508 return Glib::build_filename (ARDOUR::get_user_ardour_path(), "au_cache");
1512 AUPluginInfo::discover ()
1516 if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS
)) {
1517 ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
1520 PluginInfoList plugs
;
1522 discover_fx (plugs
);
1523 discover_music (plugs
);
1524 discover_generators (plugs
);
1530 AUPluginInfo::discover_music (PluginInfoList
& plugs
)
1532 CAComponentDescription desc
;
1533 desc
.componentFlags
= 0;
1534 desc
.componentFlagsMask
= 0;
1535 desc
.componentSubType
= 0;
1536 desc
.componentManufacturer
= 0;
1537 desc
.componentType
= kAudioUnitType_MusicEffect
;
1539 discover_by_description (plugs
, desc
);
1543 AUPluginInfo::discover_fx (PluginInfoList
& plugs
)
1545 CAComponentDescription desc
;
1546 desc
.componentFlags
= 0;
1547 desc
.componentFlagsMask
= 0;
1548 desc
.componentSubType
= 0;
1549 desc
.componentManufacturer
= 0;
1550 desc
.componentType
= kAudioUnitType_Effect
;
1552 discover_by_description (plugs
, desc
);
1556 AUPluginInfo::discover_generators (PluginInfoList
& plugs
)
1558 CAComponentDescription desc
;
1559 desc
.componentFlags
= 0;
1560 desc
.componentFlagsMask
= 0;
1561 desc
.componentSubType
= 0;
1562 desc
.componentManufacturer
= 0;
1563 desc
.componentType
= kAudioUnitType_Generator
;
1565 discover_by_description (plugs
, desc
);
1569 AUPluginInfo::discover_by_description (PluginInfoList
& plugs
, CAComponentDescription
& desc
)
1573 comp
= FindNextComponent (NULL
, &desc
);
1575 while (comp
!= NULL
) {
1576 CAComponentDescription temp
;
1577 GetComponentInfo (comp
, &temp
, NULL
, NULL
, NULL
);
1579 AUPluginInfoPtr
info (new AUPluginInfo
1580 (boost::shared_ptr
<CAComponentDescription
> (new CAComponentDescription(temp
))));
1582 /* no panners, format converters or i/o AU's for our purposes
1585 switch (info
->descriptor
->Type()) {
1586 case kAudioUnitType_Panner
:
1587 case kAudioUnitType_OfflineEffect
:
1588 case kAudioUnitType_FormatConverter
:
1590 case kAudioUnitType_Output
:
1591 case kAudioUnitType_MusicDevice
:
1592 case kAudioUnitType_MusicEffect
:
1593 case kAudioUnitType_Effect
:
1594 case kAudioUnitType_Mixer
:
1595 case kAudioUnitType_Generator
:
1601 switch (info
->descriptor
->SubType()) {
1602 case kAudioUnitSubType_DefaultOutput
:
1603 case kAudioUnitSubType_SystemOutput
:
1604 case kAudioUnitSubType_GenericOutput
:
1605 case kAudioUnitSubType_AUConverter
:
1606 /* we don't want output units here */
1610 case kAudioUnitSubType_DLSSynth
:
1611 info
->category
= "DLS Synth";
1614 case kAudioUnitSubType_Varispeed
:
1615 info
->category
= "Varispeed";
1618 case kAudioUnitSubType_Delay
:
1619 info
->category
= "Delay";
1622 case kAudioUnitSubType_LowPassFilter
:
1623 info
->category
= "Low-pass Filter";
1626 case kAudioUnitSubType_HighPassFilter
:
1627 info
->category
= "High-pass Filter";
1630 case kAudioUnitSubType_BandPassFilter
:
1631 info
->category
= "Band-pass Filter";
1634 case kAudioUnitSubType_HighShelfFilter
:
1635 info
->category
= "High-shelf Filter";
1638 case kAudioUnitSubType_LowShelfFilter
:
1639 info
->category
= "Low-shelf Filter";
1642 case kAudioUnitSubType_ParametricEQ
:
1643 info
->category
= "Parametric EQ";
1646 case kAudioUnitSubType_GraphicEQ
:
1647 info
->category
= "Graphic EQ";
1650 case kAudioUnitSubType_PeakLimiter
:
1651 info
->category
= "Peak Limiter";
1654 case kAudioUnitSubType_DynamicsProcessor
:
1655 info
->category
= "Dynamics Processor";
1658 case kAudioUnitSubType_MultiBandCompressor
:
1659 info
->category
= "Multiband Compressor";
1662 case kAudioUnitSubType_MatrixReverb
:
1663 info
->category
= "Matrix Reverb";
1666 case kAudioUnitSubType_SampleDelay
:
1667 info
->category
= "Sample Delay";
1670 case kAudioUnitSubType_Pitch
:
1671 info
->category
= "Pitch";
1674 case kAudioUnitSubType_NetSend
:
1675 info
->category
= "Net Sender";
1678 case kAudioUnitSubType_3DMixer
:
1679 info
->category
= "3DMixer";
1682 case kAudioUnitSubType_MatrixMixer
:
1683 info
->category
= "MatrixMixer";
1686 case kAudioUnitSubType_ScheduledSoundPlayer
:
1687 info
->category
= "Scheduled Sound Player";
1691 case kAudioUnitSubType_AudioFilePlayer
:
1692 info
->category
= "Audio File Player";
1695 case kAudioUnitSubType_NetReceive
:
1696 info
->category
= "Net Receiver";
1700 info
->category
= "";
1703 AUPluginInfo::get_names (temp
, info
->name
, info
->creator
);
1705 info
->type
= ARDOUR::AudioUnit
;
1706 info
->unique_id
= stringify_descriptor (*info
->descriptor
);
1708 /* XXX not sure of the best way to handle plugin versioning yet
1711 CAComponent
cacomp (*info
->descriptor
);
1713 if (cacomp
.GetResourceVersion (info
->version
) != noErr
) {
1717 if (cached_io_configuration (info
->unique_id
, info
->version
, cacomp
, info
->cache
, info
->name
)) {
1719 /* here we have to map apple's wildcard system to a simple pair
1720 of values. in ::can_do() we use the whole system, but here
1721 we need a single pair of values. XXX probably means we should
1722 remove any use of these values.
1725 info
->n_inputs
= info
->cache
.io_configs
.front().first
;
1726 info
->n_outputs
= info
->cache
.io_configs
.front().second
;
1728 if (info
->cache
.io_configs
.size() > 1) {
1729 cerr
<< "ODD: variable IO config for " << info
->unique_id
<< endl
;
1732 plugs
.push_back (info
);
1735 error
<< string_compose (_("Cannot get I/O configuration info for AU %1"), info
->name
) << endmsg
;
1738 comp
= FindNextComponent (comp
, &desc
);
1743 AUPluginInfo::cached_io_configuration (const std::string
& unique_id
,
1746 AUPluginCachedInfo
& cinfo
,
1747 const std::string
& name
)
1752 /* concatenate unique ID with version to provide a key for cached info lookup.
1753 this ensures we don't get stale information, or should if plugin developers
1754 follow Apple "guidelines".
1757 snprintf (buf
, sizeof (buf
), "%u", version
);
1762 CachedInfoMap::iterator cim
= cached_info
.find (id
);
1764 if (cim
!= cached_info
.end()) {
1765 cinfo
= cim
->second
;
1770 AUChannelInfo
* channel_info
;
1774 ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name
));
1778 if (CAAudioUnit::Open (comp
, unit
) != noErr
) {
1784 warning
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endmsg
;
1785 cerr
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endl
;
1790 if ((ret
= unit
.GetChannelInfo (&channel_info
, cnt
)) < 0) {
1795 /* no explicit info available */
1797 cinfo
.io_configs
.push_back (pair
<int,int> (-1, -1));
1801 /* store each configuration */
1803 for (uint32_t n
= 0; n
< cnt
; ++n
) {
1804 cinfo
.io_configs
.push_back (pair
<int,int> (channel_info
[n
].inChannels
,
1805 channel_info
[n
].outChannels
));
1808 free (channel_info
);
1811 add_cached_info (id
, cinfo
);
1812 save_cached_info ();
1818 AUPluginInfo::add_cached_info (const std::string
& id
, AUPluginCachedInfo
& cinfo
)
1820 cached_info
[id
] = cinfo
;
1824 AUPluginInfo::save_cached_info ()
1828 node
= new XMLNode (X_("AudioUnitPluginCache"));
1830 for (map
<string
,AUPluginCachedInfo
>::iterator i
= cached_info
.begin(); i
!= cached_info
.end(); ++i
) {
1831 XMLNode
* parent
= new XMLNode (X_("plugin"));
1832 parent
->add_property ("id", i
->first
);
1833 node
->add_child_nocopy (*parent
);
1835 for (vector
<pair
<int, int> >::iterator j
= i
->second
.io_configs
.begin(); j
!= i
->second
.io_configs
.end(); ++j
) {
1837 XMLNode
* child
= new XMLNode (X_("io"));
1840 snprintf (buf
, sizeof (buf
), "%d", j
->first
);
1841 child
->add_property (X_("in"), buf
);
1842 snprintf (buf
, sizeof (buf
), "%d", j
->second
);
1843 child
->add_property (X_("out"), buf
);
1844 parent
->add_child_nocopy (*child
);
1849 Glib::ustring path
= au_cache_path ();
1852 tree
.set_root (node
);
1854 if (!tree
.write (path
)) {
1855 error
<< string_compose (_("could not save AU cache to %1"), path
) << endmsg
;
1856 unlink (path
.c_str());
1861 AUPluginInfo::load_cached_info ()
1863 Glib::ustring path
= au_cache_path ();
1866 if (!Glib::file_test (path
, Glib::FILE_TEST_EXISTS
)) {
1871 const XMLNode
* root (tree
.root());
1873 if (root
->name() != X_("AudioUnitPluginCache")) {
1877 cached_info
.clear ();
1879 const XMLNodeList children
= root
->children();
1881 for (XMLNodeConstIterator iter
= children
.begin(); iter
!= children
.end(); ++iter
) {
1883 const XMLNode
* child
= *iter
;
1885 if (child
->name() == X_("plugin")) {
1887 const XMLNode
* gchild
;
1888 const XMLNodeList gchildren
= child
->children();
1889 const XMLProperty
* prop
= child
->property (X_("id"));
1895 std::string id
= prop
->value();
1896 AUPluginCachedInfo cinfo
;
1898 for (XMLNodeConstIterator giter
= gchildren
.begin(); giter
!= gchildren
.end(); giter
++) {
1902 if (gchild
->name() == X_("io")) {
1906 const XMLProperty
* iprop
;
1907 const XMLProperty
* oprop
;
1909 if (((iprop
= gchild
->property (X_("in"))) != 0) &&
1910 ((oprop
= gchild
->property (X_("out"))) != 0)) {
1911 in
= atoi (iprop
->value());
1912 out
= atoi (iprop
->value());
1914 cinfo
.io_configs
.push_back (pair
<int,int> (in
, out
));
1919 if (cinfo
.io_configs
.size()) {
1920 add_cached_info (id
, cinfo
);
1929 AUPluginInfo::get_names (CAComponentDescription
& comp_desc
, std::string
& name
, Glib::ustring
& maker
)
1931 CFStringRef itemName
= NULL
;
1933 // Marc Poirier-style item name
1934 CAComponent
auComponent (comp_desc
);
1935 if (auComponent
.IsValid()) {
1936 CAComponentDescription dummydesc
;
1937 Handle nameHandle
= NewHandle(sizeof(void*));
1938 if (nameHandle
!= NULL
) {
1939 OSErr err
= GetComponentInfo(auComponent
.Comp(), &dummydesc
, nameHandle
, NULL
, NULL
);
1941 ConstStr255Param nameString
= (ConstStr255Param
) (*nameHandle
);
1942 if (nameString
!= NULL
) {
1943 itemName
= CFStringCreateWithPascalString(kCFAllocatorDefault
, nameString
, CFStringGetSystemEncoding());
1946 DisposeHandle(nameHandle
);
1950 // if Marc-style fails, do the original way
1951 if (itemName
== NULL
) {
1952 CFStringRef compTypeString
= UTCreateStringForOSType(comp_desc
.componentType
);
1953 CFStringRef compSubTypeString
= UTCreateStringForOSType(comp_desc
.componentSubType
);
1954 CFStringRef compManufacturerString
= UTCreateStringForOSType(comp_desc
.componentManufacturer
);
1956 itemName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ - %@ - %@"),
1957 compTypeString
, compManufacturerString
, compSubTypeString
);
1959 if (compTypeString
!= NULL
)
1960 CFRelease(compTypeString
);
1961 if (compSubTypeString
!= NULL
)
1962 CFRelease(compSubTypeString
);
1963 if (compManufacturerString
!= NULL
)
1964 CFRelease(compManufacturerString
);
1967 string str
= CFStringRefToStdString(itemName
);
1968 string::size_type colon
= str
.find (':');
1971 name
= str
.substr (colon
+1);
1972 maker
= str
.substr (0, colon
);
1973 // strip_whitespace_edges (maker);
1974 // strip_whitespace_edges (name);
1981 // from CAComponentDescription.cpp (in libs/appleutility in ardour source)
1982 extern char *StringForOSType (OSType t
, char *writeLocation
);
1985 AUPluginInfo::stringify_descriptor (const CAComponentDescription
& desc
)
1990 s
<< StringForOSType (desc
.Type(), str
);
1993 s
<< StringForOSType (desc
.SubType(), str
);
1996 s
<< StringForOSType (desc
.Manu(), str
);