2 Copyright (C) 2006-2009 Paul Davis
3 Some portions Copyright (C) Sophia Poirier.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <pbd/transmitter.h>
28 #include <pbd/xml++.h>
29 #include <pbd/whitespace.h>
30 #include <pbd/pathscanner.h>
31 #include <pbd/localeguard.h>
33 #include <glibmm/thread.h>
34 #include <glibmm/fileutils.h>
35 #include <glibmm/miscutils.h>
37 #include <ardour/ardour.h>
38 #include <ardour/audioengine.h>
39 #include <ardour/io.h>
40 #include <ardour/audio_unit.h>
41 #include <ardour/session.h>
42 #include <ardour/tempo.h>
43 #include <ardour/utils.h>
45 #include <appleutility/CAAudioUnit.h>
46 #include <appleutility/CAAUParameter.h>
48 #include <CoreFoundation/CoreFoundation.h>
49 #include <CoreServices/CoreServices.h>
50 #include <AudioUnit/AudioUnit.h>
51 #include <AudioToolbox/AudioUnitUtilities.h>
57 using namespace ARDOUR
;
61 #define TRACE_API(fmt,...) fprintf (stderr, fmt, ## __VA_ARGS__)
63 #define TRACE_API(fmt,...)
66 #ifndef AU_STATE_SUPPORT
67 static bool seen_get_state_message
= false;
68 static bool seen_set_state_message
= false;
69 static bool seen_loading_message
= false;
70 static bool seen_saving_message
= false;
73 AUPluginInfo::CachedInfoMap
AUPluginInfo::cached_info
;
75 static string preset_search_path
= "/Library/Audio/Presets:/Network/Library/Audio/Presets";
76 static string preset_suffix
= ".aupreset";
77 static bool preset_search_path_initialized
= false;
78 static bool debug_io_config
= true;
81 _render_callback(void *userData
,
82 AudioUnitRenderActionFlags
*ioActionFlags
,
83 const AudioTimeStamp
*inTimeStamp
,
85 UInt32 inNumberFrames
,
86 AudioBufferList
* ioData
)
89 return ((AUPlugin
*)userData
)->render_callback (ioActionFlags
, inTimeStamp
, inBusNumber
, inNumberFrames
, ioData
);
95 _get_beat_and_tempo_callback (void* userData
,
96 Float64
* outCurrentBeat
,
97 Float64
* outCurrentTempo
)
100 return ((AUPlugin
*)userData
)->get_beat_and_tempo_callback (outCurrentBeat
, outCurrentTempo
);
107 _get_musical_time_location_callback (void * userData
,
108 UInt32
* outDeltaSampleOffsetToNextBeat
,
109 Float32
* outTimeSig_Numerator
,
110 UInt32
* outTimeSig_Denominator
,
111 Float64
* outCurrentMeasureDownBeat
)
114 return ((AUPlugin
*)userData
)->get_musical_time_location_callback (outDeltaSampleOffsetToNextBeat
,
115 outTimeSig_Numerator
,
116 outTimeSig_Denominator
,
117 outCurrentMeasureDownBeat
);
123 _get_transport_state_callback (void* userData
,
124 Boolean
* outIsPlaying
,
125 Boolean
* outTransportStateChanged
,
126 Float64
* outCurrentSampleInTimeLine
,
127 Boolean
* outIsCycling
,
128 Float64
* outCycleStartBeat
,
129 Float64
* outCycleEndBeat
)
132 return ((AUPlugin
*)userData
)->get_transport_state_callback (outIsPlaying
, outTransportStateChanged
,
133 outCurrentSampleInTimeLine
, outIsCycling
,
134 outCycleStartBeat
, outCycleEndBeat
);
141 save_property_list (CFPropertyListRef propertyList
, Glib::ustring path
)
147 // Convert the property list into XML data.
149 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
152 error
<< _("Could not create XML version of property list") << endmsg
;
156 // Write the XML data to the file.
158 fd
= open (path
.c_str(), O_WRONLY
|O_CREAT
|O_EXCL
, 0664);
160 if (errno
== EEXIST
) {
161 /* tell any UI's that this file already exists and ask them what to do */
162 bool overwrite
= Plugin::PresetFileExists(); // EMIT SIGNAL
164 fd
= open (path
.c_str(), O_WRONLY
, 0664);
170 error
<< string_compose (_("Cannot open preset file %1 (%2)"), path
, strerror (errno
)) << endmsg
;
175 size_t cnt
= CFDataGetLength (xmlData
);
177 if (write (fd
, CFDataGetBytePtr (xmlData
), cnt
) != (ssize_t
) cnt
) {
188 static CFPropertyListRef
189 load_property_list (Glib::ustring path
)
192 CFPropertyListRef propertyList
;
194 CFStringRef errorString
;
196 // Read the XML file.
198 if ((fd
= open (path
.c_str(), O_RDONLY
)) < 0) {
203 off_t len
= lseek (fd
, 0, SEEK_END
);
204 char* buf
= new char[len
];
205 lseek (fd
, 0, SEEK_SET
);
207 if (read (fd
, buf
, len
) != len
) {
215 xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) buf
, len
, kCFAllocatorNull
);
217 // Reconstitute the dictionary using the XML data.
219 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
221 kCFPropertyListImmutable
,
230 //-----------------------------------------------------------------------------
232 set_preset_name_in_plist (CFPropertyListRef plist
, string preset_name
)
237 CFStringRef pn
= CFStringCreateWithCString (kCFAllocatorDefault
, preset_name
.c_str(), kCFStringEncodingUTF8
);
239 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
240 CFDictionarySetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
), pn
);
246 //-----------------------------------------------------------------------------
248 get_preset_name_in_plist (CFPropertyListRef plist
)
256 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
257 const void *p
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
259 CFStringRef str
= (CFStringRef
) p
;
260 int len
= CFStringGetLength(str
);
262 char local_buffer
[len
];
263 if (CFStringGetCString (str
, local_buffer
, len
, kCFStringEncodingUTF8
)) {
271 //--------------------------------------------------------------------------
272 // general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
273 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
274 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
);
275 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
)
277 if ( (inComponentDescription1
== NULL
) || (inComponentDescription2
== NULL
) )
280 if ( (inComponentDescription1
->componentSubType
== inComponentDescription2
->componentSubType
)
281 && (inComponentDescription1
->componentManufacturer
== inComponentDescription2
->componentManufacturer
) )
283 // only sub-type and manufacturer IDs need to be equal
286 // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
287 else if (inComponentDescription1
->componentType
== inComponentDescription2
->componentType
)
294 //--------------------------------------------------------------------------
295 // general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
296 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
297 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
);
298 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
)
301 ComponentDescription desc
;
303 if ( (inComponent
== NULL
) || (inComponentDescription
== NULL
) )
306 // get the ComponentDescription of the input Component
307 status
= GetComponentInfo(inComponent
, &desc
, NULL
, NULL
, NULL
);
311 // check if the Component's ComponentDescription matches the input ComponentDescription
312 return ComponentDescriptionsMatch_General(&desc
, inComponentDescription
, inIgnoreType
);
315 //--------------------------------------------------------------------------
316 // determine if 2 ComponentDescriptions are basically equal
317 // (by that, I mean that the important identifying values are compared,
318 // but not the ComponentDescription flags)
319 Boolean
ComponentDescriptionsMatch(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
321 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, FALSE
);
324 //--------------------------------------------------------------------------
325 // determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
326 Boolean
ComponentDescriptionsMatch_Loose(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
328 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, TRUE
);
331 //--------------------------------------------------------------------------
332 // determine if a ComponentDescription basically matches that of a particular Component
333 Boolean
ComponentAndDescriptionMatch(Component inComponent
, const ComponentDescription
* inComponentDescription
)
335 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, FALSE
);
338 //--------------------------------------------------------------------------
339 // determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
340 Boolean
ComponentAndDescriptionMatch_Loosely(Component inComponent
, const ComponentDescription
* inComponentDescription
)
342 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, TRUE
);
346 AUPlugin::AUPlugin (AudioEngine
& engine
, Session
& session
, boost::shared_ptr
<CAComponent
> _comp
)
347 : Plugin (engine
, session
)
349 , unit (new CAAudioUnit
)
350 , initialized (false)
351 , _current_block_size (0)
352 , _requires_fixed_size_buffers (false)
356 , current_buffers (0)
357 , frames_processed (0)
358 , last_transport_rolling (false)
359 , last_transport_speed (0.0)
361 if (!preset_search_path_initialized
) {
362 Glib::ustring p
= Glib::get_home_dir();
363 p
+= "/Library/Audio/Presets:";
364 p
+= preset_search_path
;
365 preset_search_path
= p
;
366 preset_search_path_initialized
= true;
372 AUPlugin::AUPlugin (const AUPlugin
& other
)
374 , comp (other
.get_comp())
375 , unit (new CAAudioUnit
)
376 , initialized (false)
377 , _current_block_size (0)
378 , _requires_fixed_size_buffers (false)
382 , current_buffers (0)
383 , frames_processed (0)
389 AUPlugin::~AUPlugin ()
392 TRACE_API ("about to call uninitialize in plugin destructor\n");
393 unit
->Uninitialize ();
402 AUPlugin::discover_factory_presets ()
405 UInt32 dataSize
= sizeof (presets
);
408 TRACE_API ("get property FactoryPresets in global scope\n");
409 if ((err
= unit
->GetProperty (kAudioUnitProperty_FactoryPresets
, kAudioUnitScope_Global
, 0, (void*) &presets
, &dataSize
)) != 0) {
410 cerr
<< "cannot get factory preset info: " << err
<< endl
;
418 CFIndex cnt
= CFArrayGetCount (presets
);
420 for (CFIndex i
= 0; i
< cnt
; ++i
) {
421 AUPreset
* preset
= (AUPreset
*) CFArrayGetValueAtIndex (presets
, i
);
423 string name
= CFStringRefToStdString (preset
->presetName
);
424 factory_preset_map
[name
] = preset
->presetNumber
;
436 TRACE_API ("opening AudioUnit\n");
437 err
= CAAudioUnit::Open (*(comp
.get()), *unit
);
439 error
<< _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg
;
440 throw failed_constructor();
444 error
<< _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg
;
445 throw failed_constructor ();
448 AURenderCallbackStruct renderCallbackInfo
;
450 renderCallbackInfo
.inputProc
= _render_callback
;
451 renderCallbackInfo
.inputProcRefCon
= this;
453 TRACE_API ("set render callback in input scope\n");
454 if ((err
= unit
->SetProperty (kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
,
455 0, (void*) &renderCallbackInfo
, sizeof(renderCallbackInfo
))) != 0) {
456 cerr
<< "cannot install render callback (err = " << err
<< ')' << endl
;
457 throw failed_constructor();
460 /* tell the plugin about tempo/meter/transport callbacks in case it wants them */
462 HostCallbackInfo info
;
463 memset (&info
, 0, sizeof (HostCallbackInfo
));
464 info
.hostUserData
= this;
465 info
.beatAndTempoProc
= _get_beat_and_tempo_callback
;
466 info
.musicalTimeLocationProc
= _get_musical_time_location_callback
;
467 info
.transportStateProc
= _get_transport_state_callback
;
469 //ignore result of this - don't care if the property isn't supported
470 TRACE_API ("set host callbacks in global scope\n");
471 unit
->SetProperty (kAudioUnitProperty_HostCallbacks
,
472 kAudioUnitScope_Global
,
475 sizeof (HostCallbackInfo
));
477 TRACE_API ("count global elements\n");
478 unit
->GetElementCount (kAudioUnitScope_Global
, global_elements
);
479 TRACE_API ("count input elements\n");
480 unit
->GetElementCount (kAudioUnitScope_Input
, input_elements
);
481 TRACE_API ("count output elements\n");
482 unit
->GetElementCount (kAudioUnitScope_Output
, output_elements
);
484 /* these keep track of *configured* channel set up,
485 not potential set ups.
489 output_channels
= -1;
491 if (_set_block_size (_session
.get_block_size())) {
492 error
<< _("AUPlugin: cannot set processing block size") << endmsg
;
493 throw failed_constructor();
496 discover_parameters ();
497 discover_factory_presets ();
499 Plugin::setup_controls ();
503 AUPlugin::discover_parameters ()
505 /* discover writable parameters */
507 AudioUnitScope scopes
[] = {
508 kAudioUnitScope_Global
,
509 kAudioUnitScope_Output
,
510 kAudioUnitScope_Input
513 descriptors
.clear ();
515 for (uint32_t i
= 0; i
< sizeof (scopes
) / sizeof (scopes
[0]); ++i
) {
517 AUParamInfo
param_info (unit
->AU(), false, false, scopes
[i
]);
519 for (uint32_t i
= 0; i
< param_info
.NumParams(); ++i
) {
521 AUParameterDescriptor d
;
523 d
.id
= param_info
.ParamID (i
);
525 const CAAUParameter
* param
= param_info
.GetParamInfo (d
.id
);
526 const AudioUnitParameterInfo
& info (param
->ParamInfo());
528 const int len
= CFStringGetLength (param
->GetName());;
529 char local_buffer
[len
*2];
530 Boolean good
= CFStringGetCString(param
->GetName(),local_buffer
,len
*2,kCFStringEncodingMacRoman
);
534 d
.label
= local_buffer
;
537 d
.scope
= param_info
.GetScope ();
538 d
.element
= param_info
.GetElement ();
540 /* info.units to consider */
542 kAudioUnitParameterUnit_Generic = 0
543 kAudioUnitParameterUnit_Indexed = 1
544 kAudioUnitParameterUnit_Boolean = 2
545 kAudioUnitParameterUnit_Percent = 3
546 kAudioUnitParameterUnit_Seconds = 4
547 kAudioUnitParameterUnit_SampleFrames = 5
548 kAudioUnitParameterUnit_Phase = 6
549 kAudioUnitParameterUnit_Rate = 7
550 kAudioUnitParameterUnit_Hertz = 8
551 kAudioUnitParameterUnit_Cents = 9
552 kAudioUnitParameterUnit_RelativeSemiTones = 10
553 kAudioUnitParameterUnit_MIDINoteNumber = 11
554 kAudioUnitParameterUnit_MIDIController = 12
555 kAudioUnitParameterUnit_Decibels = 13
556 kAudioUnitParameterUnit_LinearGain = 14
557 kAudioUnitParameterUnit_Degrees = 15
558 kAudioUnitParameterUnit_EqualPowerCrossfade = 16
559 kAudioUnitParameterUnit_MixerFaderCurve1 = 17
560 kAudioUnitParameterUnit_Pan = 18
561 kAudioUnitParameterUnit_Meters = 19
562 kAudioUnitParameterUnit_AbsoluteCents = 20
563 kAudioUnitParameterUnit_Octaves = 21
564 kAudioUnitParameterUnit_BPM = 22
565 kAudioUnitParameterUnit_Beats = 23
566 kAudioUnitParameterUnit_Milliseconds = 24
567 kAudioUnitParameterUnit_Ratio = 25
570 /* info.flags to consider */
574 kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
575 kAudioUnitParameterFlag_HasClump = (1L << 20)
576 kAudioUnitParameterFlag_HasName = (1L << 21)
577 kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
578 kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
579 kAudioUnitParameterFlag_NonRealTime = (1L << 24)
580 kAudioUnitParameterFlag_CanRamp = (1L << 25)
581 kAudioUnitParameterFlag_ExpertMode = (1L << 26)
582 kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
583 kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
584 kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
585 kAudioUnitParameterFlag_IsReadable = (1L << 30)
586 kAudioUnitParameterFlag_IsWritable = (1L << 31)
589 d
.lower
= info
.minValue
;
590 d
.upper
= info
.maxValue
;
591 d
.default_value
= info
.defaultValue
;
593 d
.integer_step
= (info
.unit
& kAudioUnitParameterUnit_Indexed
);
594 d
.toggled
= (info
.unit
& kAudioUnitParameterUnit_Boolean
) ||
595 (d
.integer_step
&& ((d
.upper
- d
.lower
) == 1.0));
596 d
.sr_dependent
= (info
.unit
& kAudioUnitParameterUnit_SampleFrames
);
597 d
.automatable
= !d
.toggled
&&
598 !(info
.flags
& kAudioUnitParameterFlag_NonRealTime
) &&
599 (info
.flags
& kAudioUnitParameterFlag_IsWritable
);
601 d
.logarithmic
= (info
.flags
& kAudioUnitParameterFlag_DisplayLogarithmic
);
607 d
.min_unbound
= 0; // lower is bound
608 d
.max_unbound
= 0; // upper is bound
610 descriptors
.push_back (d
);
617 four_ints_to_four_byte_literal (unsigned char n
[4])
619 /* this is actually implementation dependent. sigh. this is what gcc
620 and quite a few others do.
622 return ((n
[0] << 24) + (n
[1] << 16) + (n
[2] << 8) + n
[3]);
626 AUPlugin::maybe_fix_broken_au_id (const std::string
& id
)
628 if (isdigit (id
[0])) {
632 /* ID format is xxxx-xxxx-xxxx
633 where x maybe \xNN or a printable character.
635 Split at the '-' and and process each part into an integer.
636 Then put it back together.
640 unsigned char nascent
[4];
641 const char* cstr
= id
.c_str();
642 const char* estr
= cstr
+ id
.size();
653 while (*cstr
&& next_int
< 4) {
657 if (estr
- cstr
< 3) {
659 /* too close to the end for \xNN parsing: treat as literal characters */
661 cerr
<< "Parse " << cstr
<< " as a literal \\" << endl
;
668 if (cstr
[1] == 'x' && isxdigit (cstr
[2]) && isxdigit (cstr
[3])) {
672 memcpy (short_buf
, &cstr
[2], 2);
673 nascent
[in
] = strtol (short_buf
, NULL
, 16);
679 /* treat as literal characters */
680 cerr
<< "Parse " << cstr
<< " as a literal \\" << endl
;
694 if (in
&& (in
% 4 == 0)) {
695 /* nascent is ready */
696 n
[next_int
] = four_ints_to_four_byte_literal (nascent
);
700 /* swallow space-hyphen-space */
714 s
<< n
[0] << '-' << n
[1] << '-' << n
[2];
723 AUPlugin::unique_id () const
725 return AUPluginInfo::stringify_descriptor (comp
->Desc());
729 AUPlugin::label () const
731 return _info
->name
.c_str();
735 AUPlugin::parameter_count () const
737 return descriptors
.size();
741 AUPlugin::default_value (uint32_t port
)
743 if (port
< descriptors
.size()) {
744 return descriptors
[port
].default_value
;
751 AUPlugin::latency () const
753 return unit
->Latency() * _session
.frame_rate();
757 AUPlugin::set_parameter (uint32_t which
, float val
)
759 if (which
< descriptors
.size()) {
760 const AUParameterDescriptor
& d (descriptors
[which
]);
761 TRACE_API ("set parameter %d in scope %d element %d to %f\n", d
.id
, d
.scope
, d
.element
, val
);
762 unit
->SetParameter (d
.id
, d
.scope
, d
.element
, val
);
764 /* tell the world what we did */
766 AudioUnitEvent theEvent
;
768 theEvent
.mEventType
= kAudioUnitEvent_ParameterValueChange
;
769 theEvent
.mArgument
.mParameter
.mAudioUnit
= unit
->AU();
770 theEvent
.mArgument
.mParameter
.mParameterID
= d
.id
;
771 theEvent
.mArgument
.mParameter
.mScope
= d
.scope
;
772 theEvent
.mArgument
.mParameter
.mElement
= d
.element
;
774 TRACE_API ("notify about parameter change\n");
775 AUEventListenerNotify (NULL
, NULL
, &theEvent
);
780 AUPlugin::get_parameter (uint32_t which
) const
783 if (which
< descriptors
.size()) {
784 const AUParameterDescriptor
& d (descriptors
[which
]);
785 TRACE_API ("get value of parameter %d in scope %d element %d\n", d
.id
, d
.scope
, d
.element
);
786 unit
->GetParameter(d
.id
, d
.scope
, d
.element
, val
);
792 AUPlugin::get_parameter_descriptor (uint32_t which
, ParameterDescriptor
& pd
) const
794 if (which
< descriptors
.size()) {
795 pd
= descriptors
[which
];
802 AUPlugin::nth_parameter (uint32_t which
, bool& ok
) const
804 if (which
< descriptors
.size()) {
813 AUPlugin::activate ()
817 TRACE_API ("call Initialize in activate()\n");
818 if ((err
= unit
->Initialize()) != noErr
) {
819 error
<< string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err
) << endmsg
;
821 frames_processed
= 0;
828 AUPlugin::deactivate ()
830 TRACE_API ("call Uninitialize in deactivate()\n");
831 unit
->Uninitialize ();
838 TRACE_API ("call Reset in flush()\n");
839 unit
->GlobalReset ();
843 AUPlugin::set_block_size (nframes_t nframes
)
845 bool was_initialized
= initialized
;
846 UInt32 numFrames
= nframes
;
853 TRACE_API ("set MaximumFramesPerSlice in global scope to %u\n", numFrames
);
854 if ((err
= unit
->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
,
855 0, &numFrames
, sizeof (numFrames
))) != noErr
) {
856 cerr
<< "cannot set max frames (err = " << err
<< ')' << endl
;
860 if (was_initialized
) {
864 _current_block_size
= nframes
;
870 AUPlugin::configure_io (int32_t in
, int32_t out
)
872 AudioStreamBasicDescription streamFormat
;
873 bool was_initialized
= initialized
;
876 //if we are already running with the requested i/o config, bail out here
877 if ( (in
==input_channels
) && (out
==output_channels
) ) {
884 streamFormat
.mSampleRate
= _session
.frame_rate();
885 streamFormat
.mFormatID
= kAudioFormatLinearPCM
;
886 streamFormat
.mFormatFlags
= kAudioFormatFlagIsFloat
|kAudioFormatFlagIsPacked
|kAudioFormatFlagIsNonInterleaved
;
888 #ifdef __LITTLE_ENDIAN__
891 streamFormat
.mFormatFlags
|= kAudioFormatFlagIsBigEndian
;
894 streamFormat
.mBitsPerChannel
= 32;
895 streamFormat
.mFramesPerPacket
= 1;
897 /* apple says that for non-interleaved data, these
898 values always refer to a single channel.
900 streamFormat
.mBytesPerPacket
= 4;
901 streamFormat
.mBytesPerFrame
= 4;
903 streamFormat
.mChannelsPerFrame
= in
;
905 if (set_input_format (streamFormat
) != 0) {
909 streamFormat
.mChannelsPerFrame
= out
;
911 if (set_output_format (streamFormat
) != 0) {
915 int ret
= Plugin::configure_io (in
, out
);
918 if (was_initialized
) {
927 AUPlugin::can_do (int32_t in
, int32_t& out
)
929 // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never
930 // attempt to multiply-instantiate plugins to meet io configurations.
932 int32_t plugcnt
= -1;
933 AUPluginInfoPtr pinfo
= boost::dynamic_pointer_cast
<AUPluginInfo
>(get_info());
935 vector
<pair
<int,int> >& io_configs
= pinfo
->cache
.io_configs
;
937 if (debug_io_config
) {
938 cerr
<< name() << " has " << io_configs
.size() << " IO Configurations\n";
941 //Ardour expects the plugin to tell it the output configuration
942 //but AU plugins can have multiple I/O configurations
943 //in most cases (since we don't allow special routing like sidechains in A2, we want to preserve the number of streams
944 //so first lets see if there's a configuration that keeps out==in
946 for (vector
<pair
<int,int> >::iterator i
= io_configs
.begin(); i
!= io_configs
.end(); ++i
) {
947 int32_t possible_in
= i
->first
;
948 int32_t possible_out
= i
->second
;
950 if (possible_in
== in
&& possible_out
== out
) {
951 cerr
<< "\tCHOSEN: in " << in
<< " out " << out
<< endl
;
956 /* now allow potentially "imprecise" matches */
958 for (vector
<pair
<int,int> >::iterator i
= io_configs
.begin(); i
!= io_configs
.end(); ++i
) {
960 int32_t possible_in
= i
->first
;
961 int32_t possible_out
= i
->second
;
963 if (debug_io_config
) {
964 cerr
<< "\tin " << possible_in
<< " out " << possible_out
<< endl
;
967 if (possible_out
== 0) {
968 warning
<< string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg
;
972 if (possible_in
== 0) {
974 /* instrument plugin, always legal but throws away inputs ...
977 if (possible_out
== -1) {
978 /* out much match in (UNLIKELY!!) */
981 } else if (possible_out
== -2) {
982 /* any configuration possible, pick matching */
985 } else if (possible_out
< -2) {
986 /* explicit variable number of outputs, pick maximum */
990 /* exact number of outputs */
996 if (possible_in
== -1) {
998 /* wildcard for input */
1000 if (possible_out
== -1) {
1001 /* out much match in */
1004 } else if (possible_out
== -2) {
1005 /* any configuration possible, pick matching */
1008 } else if (possible_out
< -2) {
1009 /* explicit variable number of outputs, pick maximum */
1010 out
= -possible_out
;
1013 /* exact number of outputs */
1019 if (possible_in
== -2) {
1021 if (possible_out
== -1) {
1022 /* any configuration possible, pick matching */
1025 } else if (possible_out
== -2) {
1026 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
1029 } else if (possible_out
< -2) {
1030 /* explicit variable number of outputs, pick maximum */
1031 out
= -possible_out
;
1034 /* exact number of outputs */
1040 if (possible_in
< -2) {
1042 /* explicit variable number of inputs */
1044 if (in
> -possible_in
) {
1045 /* request is too large */
1049 if (possible_out
== -1) {
1050 /* out must match in */
1053 } else if (possible_out
== -2) {
1054 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
1057 } else if (possible_out
< -2) {
1058 /* explicit variable number of outputs, pick maximum */
1059 out
= -possible_out
;
1062 /* exact number of outputs */
1068 if (possible_in
== in
) {
1070 /* exact number of inputs ... must match obviously */
1072 if (possible_out
== -1) {
1073 /* out must match in */
1076 } else if (possible_out
== -2) {
1077 /* any output configuration, pick matching */
1080 } else if (possible_out
< -2) {
1081 /* explicit variable number of outputs, pick maximum */
1082 out
= -possible_out
;
1085 /* exact number of outputs */
1097 if (debug_io_config
) {
1099 cerr
<< "\tCHOSEN: in " << in
<< " out " << out
<< " plugcnt will be " << plugcnt
<< endl
;
1101 cerr
<< "\tFAIL: no configs match requested in " << in
<< endl
;
1109 AUPlugin::set_input_format (AudioStreamBasicDescription
& fmt
)
1111 return set_stream_format (kAudioUnitScope_Input
, input_elements
, fmt
);
1115 AUPlugin::set_output_format (AudioStreamBasicDescription
& fmt
)
1117 if (set_stream_format (kAudioUnitScope_Output
, output_elements
, fmt
) != 0) {
1126 buffers
= (AudioBufferList
*) malloc (offsetof(AudioBufferList
, mBuffers
) +
1127 fmt
.mChannelsPerFrame
* sizeof(AudioBuffer
));
1129 Glib::Mutex::Lock
em (_session
.engine().process_lock());
1130 IO::MoreOutputs (fmt
.mChannelsPerFrame
);
1136 AUPlugin::set_stream_format (int scope
, uint32_t cnt
, AudioStreamBasicDescription
& fmt
)
1140 for (uint32_t i
= 0; i
< cnt
; ++i
) {
1141 TRACE_API ("set stream format for %s, scope = %d element %d\n",
1142 (scope
== kAudioUnitScope_Input
? "input" : "output"),
1144 if ((result
= unit
->SetFormat (scope
, i
, fmt
)) != 0) {
1145 error
<< string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
1146 (scope
== kAudioUnitScope_Input
? "input" : "output"), i
, result
) << endmsg
;
1151 if (scope
== kAudioUnitScope_Input
) {
1152 input_channels
= fmt
.mChannelsPerFrame
;
1154 output_channels
= fmt
.mChannelsPerFrame
;
1161 AUPlugin::input_streams() const
1163 if (input_channels
< 0) {
1164 warning
<< string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg
;
1167 return input_channels
;
1172 AUPlugin::output_streams() const
1174 if (output_channels
< 0) {
1175 warning
<< string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg
;
1178 return output_channels
;
1182 AUPlugin::render_callback(AudioUnitRenderActionFlags
*ioActionFlags
,
1183 const AudioTimeStamp
*inTimeStamp
,
1185 UInt32 inNumberFrames
,
1186 AudioBufferList
* ioData
)
1188 /* not much to do - the data is already in the buffers given to us in connect_and_run() */
1190 if (current_maxbuf
== 0) {
1191 error
<< _("AUPlugin: render callback called illegally!") << endmsg
;
1192 return kAudioUnitErr_CannotDoInCurrentContext
;
1194 uint32_t limit
= min ((uint32_t) ioData
->mNumberBuffers
, current_maxbuf
);
1195 for (uint32_t i
= 0; i
< limit
; ++i
) {
1196 ioData
->mBuffers
[i
].mNumberChannels
= 1;
1197 ioData
->mBuffers
[i
].mDataByteSize
= sizeof (Sample
) * inNumberFrames
;
1198 ioData
->mBuffers
[i
].mData
= (*current_buffers
)[i
] + cb_offset
+ current_offset
;
1199 // cerr << "chn " << i << " rendering from " << ioData->mBuffers[i].mData << endl;
1202 cb_offset
+= inNumberFrames
;
1208 AUPlugin::connect_and_run (vector
<Sample
*>& bufs
, uint32_t maxbuf
, int32_t& in
, int32_t& out
, nframes_t nframes
, nframes_t offset
)
1210 AudioUnitRenderActionFlags flags
= 0;
1214 if (requires_fixed_size_buffers() && (nframes
!= _current_block_size
)) {
1215 unit
->GlobalReset();
1218 current_buffers
= &bufs
;
1219 current_maxbuf
= maxbuf
;
1220 current_offset
= offset
;
1223 buffers
->mNumberBuffers
= min ((uint32_t) output_channels
, maxbuf
);
1225 for (uint32_t i
= 0; i
< buffers
->mNumberBuffers
; ++i
) {
1226 buffers
->mBuffers
[i
].mNumberChannels
= 1;
1227 buffers
->mBuffers
[i
].mDataByteSize
= nframes
* sizeof (Sample
);
1228 buffers
->mBuffers
[i
].mData
= 0;
1231 ts
.mSampleTime
= frames_processed
;
1232 ts
.mFlags
= kAudioTimeStampSampleTimeValid
;
1234 if ((err
= unit
->Render (&flags
, &ts
, 0, nframes
, buffers
)) == noErr
) {
1237 frames_processed
+= nframes
;
1239 uint32_t limit
= min ((uint32_t) buffers
->mNumberBuffers
, maxbuf
);
1242 for (i
= 0; i
< limit
; ++i
) {
1243 if (bufs
[i
] + offset
!= buffers
->mBuffers
[i
].mData
) {
1244 // cerr << "chn " << i << " rendered into " << bufs[i]+offset << endl;
1245 memcpy (bufs
[i
]+offset
, buffers
->mBuffers
[i
].mData
, nframes
* sizeof (Sample
));
1249 /* now silence any buffers that were passed in but the that the plugin
1250 did not fill/touch/use.
1253 for (;i
< maxbuf
; ++i
) {
1254 memset (bufs
[i
]+offset
, 0, nframes
* sizeof (Sample
));
1260 // cerr << name() << " render error " << err << endl;
1266 AUPlugin::get_beat_and_tempo_callback (Float64
* outCurrentBeat
,
1267 Float64
* outCurrentTempo
)
1269 TempoMap
& tmap (_session
.tempo_map());
1271 TRACE_API ("AU calls ardour beat&tempo callback\n");
1273 /* more than 1 meter or more than 1 tempo means that a simplistic computation
1274 (and interpretation) of a beat position will be incorrect. So refuse to
1278 if (tmap
.n_tempos() > 1 || tmap
.n_meters() > 1) {
1279 return kAudioUnitErr_CannotDoInCurrentContext
;
1283 TempoMap::Metric metric
= tmap
.metric_at (_session
.transport_frame() + current_offset
);
1284 tmap
.bbt_time_with_metric (_session
.transport_frame() + current_offset
, bbt
, metric
);
1286 if (outCurrentBeat
) {
1288 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1290 beat
+= bbt
.ticks
/ Meter::ticks_per_beat
;
1291 *outCurrentBeat
= beat
;
1294 if (outCurrentTempo
) {
1295 *outCurrentTempo
= floor (metric
.tempo().beats_per_minute());
1303 AUPlugin::get_musical_time_location_callback (UInt32
* outDeltaSampleOffsetToNextBeat
,
1304 Float32
* outTimeSig_Numerator
,
1305 UInt32
* outTimeSig_Denominator
,
1306 Float64
* outCurrentMeasureDownBeat
)
1308 TempoMap
& tmap (_session
.tempo_map());
1310 TRACE_API ("AU calls ardour music time location callback\n");
1312 /* more than 1 meter or more than 1 tempo means that a simplistic computation
1313 (and interpretation) of a beat position will be incorrect. So refuse to
1317 if (tmap
.n_tempos() > 1 || tmap
.n_meters() > 1) {
1318 return kAudioUnitErr_CannotDoInCurrentContext
;
1322 TempoMap::Metric metric
= tmap
.metric_at (_session
.transport_frame() + current_offset
);
1323 tmap
.bbt_time_with_metric (_session
.transport_frame() + current_offset
, bbt
, metric
);
1325 if (outDeltaSampleOffsetToNextBeat
) {
1326 if (bbt
.ticks
== 0) {
1328 *outDeltaSampleOffsetToNextBeat
= 0;
1330 *outDeltaSampleOffsetToNextBeat
= (UInt32
) floor (((Meter::ticks_per_beat
- bbt
.ticks
)/Meter::ticks_per_beat
) * // fraction of a beat to next beat
1331 metric
.tempo().frames_per_beat(_session
.frame_rate(), metric
.meter())); // frames per beat
1335 if (outTimeSig_Numerator
) {
1336 *outTimeSig_Numerator
= (UInt32
) lrintf (metric
.meter().beats_per_bar());
1338 if (outTimeSig_Denominator
) {
1339 *outTimeSig_Denominator
= (UInt32
) lrintf (metric
.meter().note_divisor());
1342 if (outCurrentMeasureDownBeat
) {
1344 /* beat for the start of the bar.
1346 2|1|0 -> 1 + beats_per_bar
1347 3|1|0 -> 1 + (2 * beats_per_bar)
1351 *outCurrentMeasureDownBeat
= 1 + metric
.meter().beats_per_bar() * (bbt
.bars
- 1);
1358 AUPlugin::get_transport_state_callback (Boolean
* outIsPlaying
,
1359 Boolean
* outTransportStateChanged
,
1360 Float64
* outCurrentSampleInTimeLine
,
1361 Boolean
* outIsCycling
,
1362 Float64
* outCycleStartBeat
,
1363 Float64
* outCycleEndBeat
)
1368 TRACE_API ("AU calls ardour transport state callback\n");
1370 rolling
= _session
.transport_rolling();
1371 speed
= _session
.transport_speed ();
1374 *outIsPlaying
= _session
.transport_rolling();
1377 if (outTransportStateChanged
) {
1378 if (rolling
!= last_transport_rolling
) {
1379 *outTransportStateChanged
= true;
1380 } else if (speed
!= last_transport_speed
) {
1381 *outTransportStateChanged
= true;
1383 *outTransportStateChanged
= false;
1387 if (outCurrentSampleInTimeLine
) {
1388 /* this assumes that the AU can only call this host callback from render context,
1389 where current_offset is valid.
1391 *outCurrentSampleInTimeLine
= _session
.transport_frame() + current_offset
;
1395 Location
* loc
= _session
.locations()->auto_loop_location();
1397 *outIsCycling
= (loc
&& _session
.transport_rolling() && _session
.get_play_loop());
1399 if (*outIsCycling
) {
1401 if (outCycleStartBeat
|| outCycleEndBeat
) {
1403 TempoMap
& tmap (_session
.tempo_map());
1405 /* more than 1 meter means that a simplistic computation (and interpretation) of
1406 a beat position will be incorrect. so refuse to offer the value.
1409 if (tmap
.n_meters() > 1) {
1410 return kAudioUnitErr_CannotDoInCurrentContext
;
1415 if (outCycleStartBeat
) {
1416 TempoMap::Metric metric
= tmap
.metric_at (loc
->start() + current_offset
);
1417 _session
.tempo_map().bbt_time_with_metric (loc
->start(), bbt
, metric
);
1420 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1422 beat
+= bbt
.ticks
/ Meter::ticks_per_beat
;
1424 *outCycleStartBeat
= beat
;
1427 if (outCycleEndBeat
) {
1428 TempoMap::Metric metric
= tmap
.metric_at (loc
->end() + current_offset
);
1429 _session
.tempo_map().bbt_time_with_metric (loc
->end(), bbt
, metric
);
1432 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1434 beat
+= bbt
.ticks
/ Meter::ticks_per_beat
;
1436 *outCycleEndBeat
= beat
;
1442 last_transport_rolling
= rolling
;
1443 last_transport_speed
= speed
;
1449 AUPlugin::automatable() const
1451 set
<uint32_t> automates
;
1453 for (uint32_t i
= 0; i
< descriptors
.size(); ++i
) {
1454 if (descriptors
[i
].automatable
) {
1455 automates
.insert (i
);
1463 AUPlugin::describe_parameter (uint32_t param
)
1465 return descriptors
[param
].label
;
1469 AUPlugin::print_parameter (uint32_t param
, char* buf
, uint32_t len
) const
1471 // NameValue stuff here
1475 AUPlugin::parameter_is_audio (uint32_t) const
1481 AUPlugin::parameter_is_control (uint32_t) const
1487 AUPlugin::parameter_is_input (uint32_t) const
1493 AUPlugin::parameter_is_output (uint32_t) const
1499 AUPlugin::get_state()
1501 LocaleGuard
lg (X_("POSIX"));
1502 XMLNode
*root
= new XMLNode (state_node_name());
1504 #ifdef AU_STATE_SUPPORT
1506 CFPropertyListRef propertyList
;
1508 TRACE_API ("get preset state\n");
1509 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1513 // Convert the property list into XML data.
1515 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
1518 error
<< _("Could not create XML version of property list") << endmsg
;
1522 /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
1523 our state node. GACK!
1528 if (t
.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData
), CFDataGetLength (xmlData
)))) {
1530 root
->add_child_copy (*t
.root());
1534 CFRelease (xmlData
);
1535 CFRelease (propertyList
);
1537 if (!seen_get_state_message
) {
1538 info
<< _("Saving AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1540 seen_get_state_message
= true;
1548 AUPlugin::set_state(const XMLNode
& node
)
1550 #ifdef AU_STATE_SUPPORT
1552 CFPropertyListRef propertyList
;
1553 LocaleGuard
lg (X_("POSIX"));
1555 if (node
.name() != state_node_name()) {
1556 error
<< _("Bad node sent to AUPlugin::set_state") << endmsg
;
1560 if (node
.children().empty()) {
1564 XMLNode
* top
= node
.children().front();
1565 XMLNode
* copy
= new XMLNode (*top
);
1570 const string
& xml
= t
.write_buffer ();
1571 CFDataRef xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) xml
.data(), xml
.length(), kCFAllocatorNull
);
1572 CFStringRef errorString
;
1574 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
1576 kCFPropertyListImmutable
,
1579 CFRelease (xmlData
);
1582 TRACE_API ("set preset\n");
1583 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1586 /* tell the world */
1588 AudioUnitParameter changedUnit
;
1589 changedUnit
.mAudioUnit
= unit
->AU();
1590 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1591 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1593 CFRelease (propertyList
);
1598 if (!seen_set_state_message
) {
1599 info
<< _("Restoring AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
1607 AUPlugin::load_preset (const string preset_label
)
1609 #ifdef AU_STATE_SUPPORT
1611 CFPropertyListRef propertyList
;
1613 UserPresetMap::iterator ux
;
1614 FactoryPresetMap::iterator fx
;
1616 /* look first in "user" presets */
1618 if ((ux
= user_preset_map
.find (preset_label
)) != user_preset_map
.end()) {
1620 if ((propertyList
= load_property_list (ux
->second
)) != 0) {
1621 TRACE_API ("set preset from user presets\n");
1622 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1625 /* tell the world */
1627 AudioUnitParameter changedUnit
;
1628 changedUnit
.mAudioUnit
= unit
->AU();
1629 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1630 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1632 CFRelease(propertyList
);
1635 } else if ((fx
= factory_preset_map
.find (preset_label
)) != factory_preset_map
.end()) {
1639 preset
.presetNumber
= fx
->second
;
1640 preset
.presetName
= CFStringCreateWithCString (kCFAllocatorDefault
, fx
->first
.c_str(), kCFStringEncodingUTF8
);
1642 TRACE_API ("set preset from factory presets\n");
1644 if (unit
->SetPresentPreset (preset
) == 0) {
1647 /* tell the world */
1649 AudioUnitParameter changedUnit
;
1650 changedUnit
.mAudioUnit
= unit
->AU();
1651 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1652 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1658 if (!seen_loading_message
) {
1659 info
<< _("Loading AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1661 seen_loading_message
= true;
1668 AUPlugin::save_preset (string preset_name
)
1670 #ifdef AU_STATE_SUPPORT
1671 CFPropertyListRef propertyList
;
1672 vector
<Glib::ustring
> v
;
1673 Glib::ustring user_preset_path
;
1676 std::string m
= maker();
1677 std::string n
= name();
1679 strip_whitespace_edges (m
);
1680 strip_whitespace_edges (n
);
1682 v
.push_back (Glib::get_home_dir());
1683 v
.push_back ("Library");
1684 v
.push_back ("Audio");
1685 v
.push_back ("Presets");
1689 user_preset_path
= Glib::build_filename (v
);
1691 if (g_mkdir_with_parents (user_preset_path
.c_str(), 0775) < 0) {
1692 error
<< string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path
) << endmsg
;
1696 TRACE_API ("get current preset\n");
1697 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1701 // add the actual preset name */
1703 v
.push_back (preset_name
+ preset_suffix
);
1707 user_preset_path
= Glib::build_filename (v
);
1709 set_preset_name_in_plist (propertyList
, preset_name
);
1711 if (save_property_list (propertyList
, user_preset_path
)) {
1712 error
<< string_compose (_("Saving plugin state to %1 failed"), user_preset_path
) << endmsg
;
1716 CFRelease(propertyList
);
1720 if (!seen_saving_message
) {
1721 info
<< _("Saving AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
1723 seen_saving_message
= true;
1729 //-----------------------------------------------------------------------------
1730 // this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
1732 GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary
, CFStringRef inDictionaryKey
, Boolean
* outSuccess
)
1734 CFNumberRef cfNumber
;
1735 SInt32 numberValue
= 0;
1736 Boolean dummySuccess
;
1738 if (outSuccess
== NULL
)
1739 outSuccess
= &dummySuccess
;
1740 if ( (inAUStateDictionary
== NULL
) || (inDictionaryKey
== NULL
) )
1742 *outSuccess
= FALSE
;
1746 cfNumber
= (CFNumberRef
) CFDictionaryGetValue(inAUStateDictionary
, inDictionaryKey
);
1747 if (cfNumber
== NULL
)
1749 *outSuccess
= FALSE
;
1752 *outSuccess
= CFNumberGetValue(cfNumber
, kCFNumberSInt32Type
, &numberValue
);
1760 GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData
, ComponentDescription
* outComponentDescription
)
1762 CFDictionaryRef auStateDictionary
;
1763 ComponentDescription tempDesc
= {0};
1764 SInt32 versionValue
;
1767 if ( (inAUStateData
== NULL
) || (outComponentDescription
== NULL
) )
1770 // the property list for AU state data must be of the dictionary type
1771 if (CFGetTypeID(inAUStateData
) != CFDictionaryGetTypeID()) {
1772 return kAudioUnitErr_InvalidPropertyValue
;
1775 auStateDictionary
= (CFDictionaryRef
)inAUStateData
;
1777 // first check to make sure that the version of the AU state data is one that we know understand
1778 // XXX should I really do this? later versions would probably still hold these ID keys, right?
1779 versionValue
= GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetVersionKey
), &gotValue
);
1782 return kAudioUnitErr_InvalidPropertyValue
;
1784 #define kCurrentSavedStateVersion 0
1785 if (versionValue
!= kCurrentSavedStateVersion
) {
1786 return kAudioUnitErr_InvalidPropertyValue
;
1789 // grab the ComponentDescription values from the AU state data
1790 tempDesc
.componentType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetTypeKey
), NULL
);
1791 tempDesc
.componentSubType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetSubtypeKey
), NULL
);
1792 tempDesc
.componentManufacturer
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetManufacturerKey
), NULL
);
1793 // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
1794 if ( (tempDesc
.componentType
== 0) || (tempDesc
.componentSubType
== 0) || (tempDesc
.componentManufacturer
== 0) )
1795 return kAudioUnitErr_InvalidPropertyValue
;
1797 *outComponentDescription
= tempDesc
;
1802 static bool au_preset_filter (const string
& str
, void* arg
)
1804 /* Not a dotfile, has a prefix before a period, suffix is aupreset */
1808 ret
= (str
[0] != '.' && str
.length() > 9 && str
.find (preset_suffix
) == (str
.length() - preset_suffix
.length()));
1812 /* check the preset file path name against this plugin
1813 ID. The idea is that all preset files for this plugin
1814 include "<manufacturer>/<plugin-name>" in their path.
1817 Plugin
* p
= (Plugin
*) arg
;
1818 string match
= p
->maker();
1822 ret
= str
.find (match
) != string::npos
;
1825 string m
= p
->maker ();
1826 string n
= p
->name ();
1827 strip_whitespace_edges (m
);
1828 strip_whitespace_edges (n
);
1833 ret
= str
.find (match
) != string::npos
;
1841 check_and_get_preset_name (Component component
, const string
& pathstr
, string
& preset_name
)
1844 CFPropertyListRef plist
;
1845 ComponentDescription presetDesc
;
1848 plist
= load_property_list (pathstr
);
1854 // get the ComponentDescription from the AU preset file
1856 status
= GetAUComponentDescriptionFromStateData(plist
, &presetDesc
);
1858 if (status
== noErr
) {
1859 if (ComponentAndDescriptionMatch_Loosely(component
, &presetDesc
)) {
1861 /* try to get the preset name from the property list */
1863 if (CFGetTypeID(plist
) == CFDictionaryGetTypeID()) {
1865 const void* psk
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
1869 const char* p
= CFStringGetCStringPtr ((CFStringRef
) psk
, kCFStringEncodingUTF8
);
1872 char buf
[PATH_MAX
+1];
1874 if (CFStringGetCString ((CFStringRef
)psk
, buf
, sizeof (buf
), kCFStringEncodingUTF8
)) {
1889 AUPlugin::current_preset() const
1893 #ifdef AU_STATE_SUPPORT
1894 CFPropertyListRef propertyList
;
1896 TRACE_API ("get current preset for current_preset()\n");
1897 if (unit
->GetAUPreset (propertyList
) == noErr
) {
1898 preset_name
= get_preset_name_in_plist (propertyList
);
1899 CFRelease(propertyList
);
1906 AUPlugin::get_presets ()
1908 vector
<string
> presets
;
1910 #ifdef AU_STATE_SUPPORT
1911 vector
<string
*>* preset_files
;
1912 PathScanner scanner
;
1914 user_preset_map
.clear ();
1916 preset_files
= scanner (preset_search_path
, au_preset_filter
, this, true, true, -1, true);
1918 if (!preset_files
) {
1922 for (vector
<string
*>::iterator x
= preset_files
->begin(); x
!= preset_files
->end(); ++x
) {
1924 string path
= *(*x
);
1927 /* make an initial guess at the preset name using the path */
1929 preset_name
= Glib::path_get_basename (path
);
1930 preset_name
= preset_name
.substr (0, preset_name
.find_last_of ('.'));
1932 /* check that this preset file really matches this plugin
1933 and potentially get the "real" preset name from
1937 if (check_and_get_preset_name (get_comp()->Comp(), path
, preset_name
)) {
1938 user_preset_map
[preset_name
] = path
;
1944 delete preset_files
;
1946 /* now fill the vector<string> with the names we have */
1948 for (UserPresetMap::iterator i
= user_preset_map
.begin(); i
!= user_preset_map
.end(); ++i
) {
1949 presets
.push_back (i
->first
);
1952 /* add factory presets */
1954 for (FactoryPresetMap::iterator i
= factory_preset_map
.begin(); i
!= factory_preset_map
.end(); ++i
) {
1955 presets
.push_back (i
->first
);
1964 AUPlugin::has_editor () const
1966 // even if the plugin doesn't have its own editor, the AU API can be used
1967 // to create one that looks native.
1971 AUPluginInfo::AUPluginInfo (boost::shared_ptr
<CAComponentDescription
> d
)
1974 type
= ARDOUR::AudioUnit
;
1977 AUPluginInfo::~AUPluginInfo ()
1979 type
= ARDOUR::AudioUnit
;
1983 AUPluginInfo::load (Session
& session
)
1988 TRACE_API ("load AU as a component\n");
1989 boost::shared_ptr
<CAComponent
> comp (new CAComponent(*descriptor
));
1991 if (!comp
->IsValid()) {
1992 error
<< ("AudioUnit: not a valid Component") << endmsg
;
1994 plugin
.reset (new AUPlugin (session
.engine(), session
, comp
));
1997 AUPluginInfo
*aup
= new AUPluginInfo (*this);
1998 plugin
->set_info (PluginInfoPtr (aup
));
1999 boost::dynamic_pointer_cast
<AUPlugin
> (plugin
)->set_fixed_size_buffers (aup
->creator
== "!UAD");
2003 catch (failed_constructor
&err
) {
2004 return PluginPtr ();
2009 AUPluginInfo::au_cache_path ()
2011 return Glib::build_filename (ARDOUR::get_user_ardour_path(), "au_cache");
2015 AUPluginInfo::discover ()
2019 if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS
)) {
2020 ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
2023 PluginInfoList plugs
;
2025 discover_fx (plugs
);
2026 discover_music (plugs
);
2027 discover_generators (plugs
);
2033 AUPluginInfo::discover_music (PluginInfoList
& plugs
)
2035 CAComponentDescription desc
;
2036 desc
.componentFlags
= 0;
2037 desc
.componentFlagsMask
= 0;
2038 desc
.componentSubType
= 0;
2039 desc
.componentManufacturer
= 0;
2040 desc
.componentType
= kAudioUnitType_MusicEffect
;
2042 discover_by_description (plugs
, desc
);
2046 AUPluginInfo::discover_fx (PluginInfoList
& plugs
)
2048 CAComponentDescription desc
;
2049 desc
.componentFlags
= 0;
2050 desc
.componentFlagsMask
= 0;
2051 desc
.componentSubType
= 0;
2052 desc
.componentManufacturer
= 0;
2053 desc
.componentType
= kAudioUnitType_Effect
;
2055 discover_by_description (plugs
, desc
);
2059 AUPluginInfo::discover_generators (PluginInfoList
& plugs
)
2061 CAComponentDescription desc
;
2062 desc
.componentFlags
= 0;
2063 desc
.componentFlagsMask
= 0;
2064 desc
.componentSubType
= 0;
2065 desc
.componentManufacturer
= 0;
2066 desc
.componentType
= kAudioUnitType_Generator
;
2068 discover_by_description (plugs
, desc
);
2072 AUPluginInfo::discover_by_description (PluginInfoList
& plugs
, CAComponentDescription
& desc
)
2076 comp
= FindNextComponent (NULL
, &desc
);
2078 while (comp
!= NULL
) {
2079 CAComponentDescription temp
;
2080 GetComponentInfo (comp
, &temp
, NULL
, NULL
, NULL
);
2082 AUPluginInfoPtr
info (new AUPluginInfo
2083 (boost::shared_ptr
<CAComponentDescription
> (new CAComponentDescription(temp
))));
2085 /* no panners, format converters or i/o AU's for our purposes
2088 switch (info
->descriptor
->Type()) {
2089 case kAudioUnitType_Panner
:
2090 case kAudioUnitType_OfflineEffect
:
2091 case kAudioUnitType_FormatConverter
:
2093 case kAudioUnitType_Output
:
2094 case kAudioUnitType_MusicDevice
:
2095 case kAudioUnitType_MusicEffect
:
2096 case kAudioUnitType_Effect
:
2097 case kAudioUnitType_Mixer
:
2098 case kAudioUnitType_Generator
:
2104 switch (info
->descriptor
->SubType()) {
2105 case kAudioUnitSubType_DefaultOutput
:
2106 case kAudioUnitSubType_SystemOutput
:
2107 case kAudioUnitSubType_GenericOutput
:
2108 case kAudioUnitSubType_AUConverter
:
2109 /* we don't want output units here */
2113 case kAudioUnitSubType_DLSSynth
:
2114 info
->category
= "DLS Synth";
2117 case kAudioUnitSubType_Varispeed
:
2118 info
->category
= "Varispeed";
2121 case kAudioUnitSubType_Delay
:
2122 info
->category
= "Delay";
2125 case kAudioUnitSubType_LowPassFilter
:
2126 info
->category
= "Low-pass Filter";
2129 case kAudioUnitSubType_HighPassFilter
:
2130 info
->category
= "High-pass Filter";
2133 case kAudioUnitSubType_BandPassFilter
:
2134 info
->category
= "Band-pass Filter";
2137 case kAudioUnitSubType_HighShelfFilter
:
2138 info
->category
= "High-shelf Filter";
2141 case kAudioUnitSubType_LowShelfFilter
:
2142 info
->category
= "Low-shelf Filter";
2145 case kAudioUnitSubType_ParametricEQ
:
2146 info
->category
= "Parametric EQ";
2149 case kAudioUnitSubType_GraphicEQ
:
2150 info
->category
= "Graphic EQ";
2153 case kAudioUnitSubType_PeakLimiter
:
2154 info
->category
= "Peak Limiter";
2157 case kAudioUnitSubType_DynamicsProcessor
:
2158 info
->category
= "Dynamics Processor";
2161 case kAudioUnitSubType_MultiBandCompressor
:
2162 info
->category
= "Multiband Compressor";
2165 case kAudioUnitSubType_MatrixReverb
:
2166 info
->category
= "Matrix Reverb";
2169 case kAudioUnitSubType_SampleDelay
:
2170 info
->category
= "Sample Delay";
2173 case kAudioUnitSubType_Pitch
:
2174 info
->category
= "Pitch";
2177 case kAudioUnitSubType_NetSend
:
2178 info
->category
= "Net Sender";
2181 case kAudioUnitSubType_3DMixer
:
2182 info
->category
= "3DMixer";
2185 case kAudioUnitSubType_MatrixMixer
:
2186 info
->category
= "MatrixMixer";
2189 case kAudioUnitSubType_ScheduledSoundPlayer
:
2190 info
->category
= "Scheduled Sound Player";
2194 case kAudioUnitSubType_AudioFilePlayer
:
2195 info
->category
= "Audio File Player";
2198 case kAudioUnitSubType_NetReceive
:
2199 info
->category
= "Net Receiver";
2203 info
->category
= "";
2206 AUPluginInfo::get_names (temp
, info
->name
, info
->creator
);
2208 info
->type
= ARDOUR::AudioUnit
;
2209 info
->unique_id
= stringify_descriptor (*info
->descriptor
);
2211 /* XXX not sure of the best way to handle plugin versioning yet
2214 CAComponent
cacomp (*info
->descriptor
);
2216 if (cacomp
.GetResourceVersion (info
->version
) != noErr
) {
2220 if (cached_io_configuration (info
->unique_id
, info
->version
, cacomp
, info
->cache
, info
->name
)) {
2222 /* here we have to map apple's wildcard system to a simple pair
2223 of values. in ::can_do() we use the whole system, but here
2224 we need a single pair of values. XXX probably means we should
2225 remove any use of these values.
2228 info
->n_inputs
= info
->cache
.io_configs
.front().first
;
2229 info
->n_outputs
= info
->cache
.io_configs
.front().second
;
2231 cerr
<< "detected AU: " << info
->name
.c_str() << " (" << info
->cache
.io_configs
.size() << " i/o configurations) - " << info
->unique_id
<< endl
;
2233 plugs
.push_back (info
);
2236 error
<< string_compose (_("Cannot get I/O configuration info for AU %1"), info
->name
) << endmsg
;
2239 comp
= FindNextComponent (comp
, &desc
);
2244 AUPluginInfo::cached_io_configuration (const std::string
& unique_id
,
2247 AUPluginCachedInfo
& cinfo
,
2248 const std::string
& name
)
2253 /* concatenate unique ID with version to provide a key for cached info lookup.
2254 this ensures we don't get stale information, or should if plugin developers
2255 follow Apple "guidelines".
2258 snprintf (buf
, sizeof (buf
), "%u", (uint32_t) version
);
2263 CachedInfoMap::iterator cim
= cached_info
.find (id
);
2265 if (cim
!= cached_info
.end()) {
2266 cinfo
= cim
->second
;
2271 AUChannelInfo
* channel_info
;
2275 ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name
));
2279 if (CAAudioUnit::Open (comp
, unit
) != noErr
) {
2285 warning
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endmsg
;
2286 cerr
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endl
;
2291 TRACE_API ("get AU channel info\n");
2292 if ((ret
= unit
.GetChannelInfo (&channel_info
, cnt
)) < 0) {
2297 /* no explicit info available */
2299 cinfo
.io_configs
.push_back (pair
<int,int> (-1, -1));
2303 /* store each configuration */
2305 for (uint32_t n
= 0; n
< cnt
; ++n
) {
2306 cinfo
.io_configs
.push_back (pair
<int,int> (channel_info
[n
].inChannels
,
2307 channel_info
[n
].outChannels
));
2310 free (channel_info
);
2313 add_cached_info (id
, cinfo
);
2314 save_cached_info ();
2320 AUPluginInfo::add_cached_info (const std::string
& id
, AUPluginCachedInfo
& cinfo
)
2322 cached_info
[id
] = cinfo
;
2325 #define AU_CACHE_VERSION "2.0"
2328 AUPluginInfo::save_cached_info ()
2332 node
= new XMLNode (X_("AudioUnitPluginCache"));
2333 node
->add_property( "version", AU_CACHE_VERSION
);
2335 for (map
<string
,AUPluginCachedInfo
>::iterator i
= cached_info
.begin(); i
!= cached_info
.end(); ++i
) {
2336 XMLNode
* parent
= new XMLNode (X_("plugin"));
2337 parent
->add_property ("id", i
->first
);
2338 node
->add_child_nocopy (*parent
);
2340 for (vector
<pair
<int, int> >::iterator j
= i
->second
.io_configs
.begin(); j
!= i
->second
.io_configs
.end(); ++j
) {
2342 XMLNode
* child
= new XMLNode (X_("io"));
2345 snprintf (buf
, sizeof (buf
), "%d", j
->first
);
2346 child
->add_property (X_("in"), buf
);
2347 snprintf (buf
, sizeof (buf
), "%d", j
->second
);
2348 child
->add_property (X_("out"), buf
);
2349 parent
->add_child_nocopy (*child
);
2354 Glib::ustring path
= au_cache_path ();
2357 tree
.set_root (node
);
2359 if (!tree
.write (path
)) {
2360 error
<< string_compose (_("could not save AU cache to %1"), path
) << endmsg
;
2361 unlink (path
.c_str());
2366 AUPluginInfo::load_cached_info ()
2368 Glib::ustring path
= au_cache_path ();
2371 if (!Glib::file_test (path
, Glib::FILE_TEST_EXISTS
)) {
2375 if ( !tree
.read (path
) ) {
2376 error
<< "au_cache is not a valid XML file. AU plugins will be re-scanned" << endmsg
;
2380 const XMLNode
* root (tree
.root());
2382 if (root
->name() != X_("AudioUnitPluginCache")) {
2386 //initial version has incorrectly stored i/o info, and/or garbage chars.
2387 const XMLProperty
* version
= root
->property(X_("version"));
2388 if (! ((version
!= NULL
) && (version
->value() == X_(AU_CACHE_VERSION
)))) {
2389 error
<< "au_cache is not correct version. AU plugins will be re-scanned" << endmsg
;
2393 cached_info
.clear ();
2395 const XMLNodeList children
= root
->children();
2397 for (XMLNodeConstIterator iter
= children
.begin(); iter
!= children
.end(); ++iter
) {
2399 const XMLNode
* child
= *iter
;
2401 if (child
->name() == X_("plugin")) {
2403 const XMLNode
* gchild
;
2404 const XMLNodeList gchildren
= child
->children();
2405 const XMLProperty
* prop
= child
->property (X_("id"));
2411 string id
= prop
->value();
2415 string::size_type slash
= id
.find_last_of ('/');
2417 if (slash
== string::npos
) {
2421 version
= id
.substr (slash
);
2422 id
= id
.substr (0, slash
);
2423 fixed
= AUPlugin::maybe_fix_broken_au_id (id
);
2425 if (fixed
.empty()) {
2426 error
<< string_compose (_("Your AudioUnit configuration cache contains an AU plugin whose ID cannot be understood - ignored (%1)"), id
) << endmsg
;
2433 AUPluginCachedInfo cinfo
;
2435 for (XMLNodeConstIterator giter
= gchildren
.begin(); giter
!= gchildren
.end(); giter
++) {
2439 if (gchild
->name() == X_("io")) {
2443 const XMLProperty
* iprop
;
2444 const XMLProperty
* oprop
;
2446 if (((iprop
= gchild
->property (X_("in"))) != 0) &&
2447 ((oprop
= gchild
->property (X_("out"))) != 0)) {
2448 in
= atoi (iprop
->value());
2449 out
= atoi (oprop
->value());
2451 cinfo
.io_configs
.push_back (pair
<int,int> (in
, out
));
2456 if (cinfo
.io_configs
.size()) {
2457 add_cached_info (id
, cinfo
);
2466 AUPluginInfo::get_names (CAComponentDescription
& comp_desc
, std::string
& name
, Glib::ustring
& maker
)
2468 CFStringRef itemName
= NULL
;
2470 // Marc Poirier-style item name
2471 CAComponent
auComponent (comp_desc
);
2472 if (auComponent
.IsValid()) {
2473 CAComponentDescription dummydesc
;
2474 Handle nameHandle
= NewHandle(sizeof(void*));
2475 if (nameHandle
!= NULL
) {
2476 OSErr err
= GetComponentInfo(auComponent
.Comp(), &dummydesc
, nameHandle
, NULL
, NULL
);
2478 ConstStr255Param nameString
= (ConstStr255Param
) (*nameHandle
);
2479 if (nameString
!= NULL
) {
2480 itemName
= CFStringCreateWithPascalString(kCFAllocatorDefault
, nameString
, CFStringGetSystemEncoding());
2483 DisposeHandle(nameHandle
);
2487 // if Marc-style fails, do the original way
2488 if (itemName
== NULL
) {
2489 CFStringRef compTypeString
= UTCreateStringForOSType(comp_desc
.componentType
);
2490 CFStringRef compSubTypeString
= UTCreateStringForOSType(comp_desc
.componentSubType
);
2491 CFStringRef compManufacturerString
= UTCreateStringForOSType(comp_desc
.componentManufacturer
);
2493 itemName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ - %@ - %@"),
2494 compTypeString
, compManufacturerString
, compSubTypeString
);
2496 if (compTypeString
!= NULL
)
2497 CFRelease(compTypeString
);
2498 if (compSubTypeString
!= NULL
)
2499 CFRelease(compSubTypeString
);
2500 if (compManufacturerString
!= NULL
)
2501 CFRelease(compManufacturerString
);
2504 string str
= CFStringRefToStdString(itemName
);
2505 string::size_type colon
= str
.find (':');
2508 name
= str
.substr (colon
+1);
2509 maker
= str
.substr (0, colon
);
2510 strip_whitespace_edges (maker
);
2511 strip_whitespace_edges (name
);
2515 strip_whitespace_edges (name
);
2520 AUPluginInfo::stringify_descriptor (const CAComponentDescription
& desc
)
2524 /* note: OSType is a compiler-implemenation-defined value,
2525 historically a 32 bit integer created with a multi-character
2526 constant such as 'abcd'. It is, fundamentally, an abomination.
2531 s
<< desc
.SubType();