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/convert.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/pathscanner.h"
32 #include "pbd/locale_guard.h"
34 #include <glibmm/thread.h>
35 #include <glibmm/fileutils.h>
36 #include <glibmm/miscutils.h>
38 #include "ardour/ardour.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/audio_buffer.h"
41 #include "ardour/midi_buffer.h"
42 #include "ardour/filesystem_paths.h"
43 #include "ardour/io.h"
44 #include "ardour/audio_unit.h"
45 #include "ardour/session.h"
46 #include "ardour/tempo.h"
47 #include "ardour/utils.h"
49 #include "appleutility/CAAudioUnit.h"
50 #include "appleutility/CAAUParameter.h"
52 #include <CoreFoundation/CoreFoundation.h>
53 #include <CoreServices/CoreServices.h>
54 #include <AudioUnit/AudioUnit.h>
55 #include <AudioToolbox/AudioUnitUtilities.h>
61 using namespace ARDOUR
;
63 //#define TRACE_AU_API
65 #define TRACE_API(fmt,...) fprintf (stderr, fmt, ## __VA_ARGS__)
67 #define TRACE_API(fmt,...)
70 #ifndef AU_STATE_SUPPORT
71 static bool seen_get_state_message
= false;
72 static bool seen_set_state_message
= false;
73 static bool seen_loading_message
= false;
74 static bool seen_saving_message
= false;
77 AUPluginInfo::CachedInfoMap
AUPluginInfo::cached_info
;
79 static string preset_search_path
= "/Library/Audio/Presets:/Network/Library/Audio/Presets";
80 static string preset_suffix
= ".aupreset";
81 static bool preset_search_path_initialized
= false;
82 static bool debug_io_config
= true;
85 _render_callback(void *userData
,
86 AudioUnitRenderActionFlags
*ioActionFlags
,
87 const AudioTimeStamp
*inTimeStamp
,
89 UInt32 inNumberFrames
,
90 AudioBufferList
* ioData
)
93 return ((AUPlugin
*)userData
)->render_callback (ioActionFlags
, inTimeStamp
, inBusNumber
, inNumberFrames
, ioData
);
99 _get_beat_and_tempo_callback (void* userData
,
100 Float64
* outCurrentBeat
,
101 Float64
* outCurrentTempo
)
104 return ((AUPlugin
*)userData
)->get_beat_and_tempo_callback (outCurrentBeat
, outCurrentTempo
);
111 _get_musical_time_location_callback (void * userData
,
112 UInt32
* outDeltaSampleOffsetToNextBeat
,
113 Float32
* outTimeSig_Numerator
,
114 UInt32
* outTimeSig_Denominator
,
115 Float64
* outCurrentMeasureDownBeat
)
118 return ((AUPlugin
*)userData
)->get_musical_time_location_callback (outDeltaSampleOffsetToNextBeat
,
119 outTimeSig_Numerator
,
120 outTimeSig_Denominator
,
121 outCurrentMeasureDownBeat
);
127 _get_transport_state_callback (void* userData
,
128 Boolean
* outIsPlaying
,
129 Boolean
* outTransportStateChanged
,
130 Float64
* outCurrentSampleInTimeLine
,
131 Boolean
* outIsCycling
,
132 Float64
* outCycleStartBeat
,
133 Float64
* outCycleEndBeat
)
136 return ((AUPlugin
*)userData
)->get_transport_state_callback (
137 outIsPlaying
, outTransportStateChanged
,
138 outCurrentSampleInTimeLine
, outIsCycling
,
139 outCycleStartBeat
, outCycleEndBeat
);
146 save_property_list (CFPropertyListRef propertyList
, Glib::ustring path
)
152 // Convert the property list into XML data.
154 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
157 error
<< _("Could not create XML version of property list") << endmsg
;
161 // Write the XML data to the file.
163 fd
= open (path
.c_str(), O_WRONLY
|O_CREAT
|O_EXCL
, 0664);
165 if (errno
== EEXIST
) {
166 error
<< string_compose (_("Preset file %1 exists; not overwriting"),
169 error
<< string_compose (_("Cannot open preset file %1 (%2)"),
170 path
, strerror (errno
)) << endmsg
;
176 size_t cnt
= CFDataGetLength (xmlData
);
178 if (write (fd
, CFDataGetBytePtr (xmlData
), cnt
) != (ssize_t
) cnt
) {
189 static CFPropertyListRef
190 load_property_list (Glib::ustring path
)
193 CFPropertyListRef propertyList
= 0;
195 CFStringRef errorString
;
197 // Read the XML file.
199 if ((fd
= open (path
.c_str(), O_RDONLY
)) < 0) {
204 off_t len
= lseek (fd
, 0, SEEK_END
);
205 char* buf
= new char[len
];
206 lseek (fd
, 0, SEEK_SET
);
208 if (read (fd
, buf
, len
) != len
) {
216 xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) buf
, len
, kCFAllocatorNull
);
218 // Reconstitute the dictionary using the XML data.
220 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
222 kCFPropertyListImmutable
,
231 //-----------------------------------------------------------------------------
233 set_preset_name_in_plist (CFPropertyListRef plist
, string preset_name
)
238 CFStringRef pn
= CFStringCreateWithCString (kCFAllocatorDefault
, preset_name
.c_str(), kCFStringEncodingUTF8
);
240 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
241 CFDictionarySetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
), pn
);
247 //-----------------------------------------------------------------------------
249 get_preset_name_in_plist (CFPropertyListRef plist
)
257 if (CFGetTypeID (plist
) == CFDictionaryGetTypeID()) {
258 const void *p
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
260 CFStringRef str
= (CFStringRef
) p
;
261 int len
= CFStringGetLength(str
);
263 char local_buffer
[len
];
264 if (CFStringGetCString (str
, local_buffer
, len
, kCFStringEncodingUTF8
)) {
272 //--------------------------------------------------------------------------
273 // general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
274 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
275 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
);
276 Boolean
ComponentDescriptionsMatch_General(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
, Boolean inIgnoreType
)
278 if ( (inComponentDescription1
== NULL
) || (inComponentDescription2
== NULL
) )
281 if ( (inComponentDescription1
->componentSubType
== inComponentDescription2
->componentSubType
)
282 && (inComponentDescription1
->componentManufacturer
== inComponentDescription2
->componentManufacturer
) )
284 // only sub-type and manufacturer IDs need to be equal
287 // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
288 else if (inComponentDescription1
->componentType
== inComponentDescription2
->componentType
)
295 //--------------------------------------------------------------------------
296 // general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
297 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
298 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
);
299 Boolean
ComponentAndDescriptionMatch_General(Component inComponent
, const ComponentDescription
* inComponentDescription
, Boolean inIgnoreType
)
302 ComponentDescription desc
;
304 if ( (inComponent
== NULL
) || (inComponentDescription
== NULL
) )
307 // get the ComponentDescription of the input Component
308 status
= GetComponentInfo(inComponent
, &desc
, NULL
, NULL
, NULL
);
312 // check if the Component's ComponentDescription matches the input ComponentDescription
313 return ComponentDescriptionsMatch_General(&desc
, inComponentDescription
, inIgnoreType
);
316 //--------------------------------------------------------------------------
317 // determine if 2 ComponentDescriptions are basically equal
318 // (by that, I mean that the important identifying values are compared,
319 // but not the ComponentDescription flags)
320 Boolean
ComponentDescriptionsMatch(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
322 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, FALSE
);
325 //--------------------------------------------------------------------------
326 // determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
327 Boolean
ComponentDescriptionsMatch_Loose(const ComponentDescription
* inComponentDescription1
, const ComponentDescription
* inComponentDescription2
)
329 return ComponentDescriptionsMatch_General(inComponentDescription1
, inComponentDescription2
, TRUE
);
332 //--------------------------------------------------------------------------
333 // determine if a ComponentDescription basically matches that of a particular Component
334 Boolean
ComponentAndDescriptionMatch(Component inComponent
, const ComponentDescription
* inComponentDescription
)
336 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, FALSE
);
339 //--------------------------------------------------------------------------
340 // determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
341 Boolean
ComponentAndDescriptionMatch_Loosely(Component inComponent
, const ComponentDescription
* inComponentDescription
)
343 return ComponentAndDescriptionMatch_General(inComponent
, inComponentDescription
, TRUE
);
347 AUPlugin::AUPlugin (AudioEngine
& engine
, Session
& session
, boost::shared_ptr
<CAComponent
> _comp
)
348 : Plugin (engine
, session
)
350 , unit (new CAAudioUnit
)
351 , initialized (false)
352 , _current_block_size (0)
353 , _requires_fixed_size_buffers (false)
357 , current_buffers (0)
358 , frames_processed (0)
359 , last_transport_rolling (false)
360 , last_transport_speed (0.0)
362 if (!preset_search_path_initialized
) {
363 Glib::ustring p
= Glib::get_home_dir();
364 p
+= "/Library/Audio/Presets:";
365 p
+= preset_search_path
;
366 preset_search_path
= p
;
367 preset_search_path_initialized
= true;
374 AUPlugin::AUPlugin (const AUPlugin
& other
)
376 , comp (other
.get_comp())
377 , unit (new CAAudioUnit
)
378 , initialized (false)
379 , _current_block_size (0)
381 , _requires_fixed_size_buffers (false)
385 , current_buffers (0)
386 , frames_processed (0)
392 AUPlugin::~AUPlugin ()
395 TRACE_API ("about to call uninitialize in plugin destructor\n");
396 unit
->Uninitialize ();
405 AUPlugin::discover_factory_presets ()
408 UInt32 dataSize
= sizeof (presets
);
411 TRACE_API ("get property FactoryPresets in global scope\n");
412 if ((err
= unit
->GetProperty (kAudioUnitProperty_FactoryPresets
, kAudioUnitScope_Global
, 0, (void*) &presets
, &dataSize
)) != 0) {
413 cerr
<< "cannot get factory preset info: " << err
<< endl
;
421 CFIndex cnt
= CFArrayGetCount (presets
);
423 for (CFIndex i
= 0; i
< cnt
; ++i
) {
424 AUPreset
* preset
= (AUPreset
*) CFArrayGetValueAtIndex (presets
, i
);
426 string name
= CFStringRefToStdString (preset
->presetName
);
427 factory_preset_map
[name
] = preset
->presetNumber
;
439 TRACE_API ("opening AudioUnit\n");
440 err
= CAAudioUnit::Open (*(comp
.get()), *unit
);
442 error
<< _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg
;
443 throw failed_constructor();
447 error
<< _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg
;
448 throw failed_constructor ();
451 TRACE_API ("count global elements\n");
452 unit
->GetElementCount (kAudioUnitScope_Global
, global_elements
);
453 TRACE_API ("count input elements\n");
454 unit
->GetElementCount (kAudioUnitScope_Input
, input_elements
);
455 TRACE_API ("count output elements\n");
456 unit
->GetElementCount (kAudioUnitScope_Output
, output_elements
);
458 if (input_elements
> 0) {
459 AURenderCallbackStruct renderCallbackInfo
;
461 renderCallbackInfo
.inputProc
= _render_callback
;
462 renderCallbackInfo
.inputProcRefCon
= this;
464 TRACE_API ("set render callback in input scope\n");
465 if ((err
= unit
->SetProperty (kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
,
466 0, (void*) &renderCallbackInfo
, sizeof(renderCallbackInfo
))) != 0) {
467 cerr
<< "cannot install render callback (err = " << err
<< ')' << endl
;
468 throw failed_constructor();
472 /* tell the plugin about tempo/meter/transport callbacks in case it wants them */
474 HostCallbackInfo info
;
475 memset (&info
, 0, sizeof (HostCallbackInfo
));
476 info
.hostUserData
= this;
477 info
.beatAndTempoProc
= _get_beat_and_tempo_callback
;
478 info
.musicalTimeLocationProc
= _get_musical_time_location_callback
;
479 info
.transportStateProc
= _get_transport_state_callback
;
481 //ignore result of this - don't care if the property isn't supported
482 TRACE_API ("set host callbacks in global scope\n");
483 unit
->SetProperty (kAudioUnitProperty_HostCallbacks
,
484 kAudioUnitScope_Global
,
487 sizeof (HostCallbackInfo
));
489 /* these keep track of *configured* channel set up,
490 not potential set ups.
494 output_channels
= -1;
496 if (set_block_size (_session
.get_block_size())) {
497 error
<< _("AUPlugin: cannot set processing block size") << endmsg
;
498 throw failed_constructor();
501 AUPluginInfoPtr pinfo
= boost::dynamic_pointer_cast
<AUPluginInfo
>(get_info());
502 _has_midi_input
= pinfo
->needs_midi_input ();
503 _has_midi_output
= false;
505 discover_parameters ();
506 discover_factory_presets ();
508 // Plugin::setup_controls ();
512 AUPlugin::discover_parameters ()
514 /* discover writable parameters */
516 AudioUnitScope scopes
[] = {
517 kAudioUnitScope_Global
,
518 kAudioUnitScope_Output
,
519 kAudioUnitScope_Input
522 descriptors
.clear ();
524 for (uint32_t i
= 0; i
< sizeof (scopes
) / sizeof (scopes
[0]); ++i
) {
526 AUParamInfo
param_info (unit
->AU(), false, false, scopes
[i
]);
528 for (uint32_t i
= 0; i
< param_info
.NumParams(); ++i
) {
530 AUParameterDescriptor d
;
532 d
.id
= param_info
.ParamID (i
);
534 const CAAUParameter
* param
= param_info
.GetParamInfo (d
.id
);
535 const AudioUnitParameterInfo
& info (param
->ParamInfo());
537 const int len
= CFStringGetLength (param
->GetName());;
538 char local_buffer
[len
*2];
539 Boolean good
= CFStringGetCString(param
->GetName(),local_buffer
,len
*2,kCFStringEncodingMacRoman
);
543 d
.label
= local_buffer
;
546 d
.scope
= param_info
.GetScope ();
547 d
.element
= param_info
.GetElement ();
549 /* info.units to consider */
551 kAudioUnitParameterUnit_Generic = 0
552 kAudioUnitParameterUnit_Indexed = 1
553 kAudioUnitParameterUnit_Boolean = 2
554 kAudioUnitParameterUnit_Percent = 3
555 kAudioUnitParameterUnit_Seconds = 4
556 kAudioUnitParameterUnit_SampleFrames = 5
557 kAudioUnitParameterUnit_Phase = 6
558 kAudioUnitParameterUnit_Rate = 7
559 kAudioUnitParameterUnit_Hertz = 8
560 kAudioUnitParameterUnit_Cents = 9
561 kAudioUnitParameterUnit_RelativeSemiTones = 10
562 kAudioUnitParameterUnit_MIDINoteNumber = 11
563 kAudioUnitParameterUnit_MIDIController = 12
564 kAudioUnitParameterUnit_Decibels = 13
565 kAudioUnitParameterUnit_LinearGain = 14
566 kAudioUnitParameterUnit_Degrees = 15
567 kAudioUnitParameterUnit_EqualPowerCrossfade = 16
568 kAudioUnitParameterUnit_MixerFaderCurve1 = 17
569 kAudioUnitParameterUnit_Pan = 18
570 kAudioUnitParameterUnit_Meters = 19
571 kAudioUnitParameterUnit_AbsoluteCents = 20
572 kAudioUnitParameterUnit_Octaves = 21
573 kAudioUnitParameterUnit_BPM = 22
574 kAudioUnitParameterUnit_Beats = 23
575 kAudioUnitParameterUnit_Milliseconds = 24
576 kAudioUnitParameterUnit_Ratio = 25
579 /* info.flags to consider */
583 kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
584 kAudioUnitParameterFlag_HasClump = (1L << 20)
585 kAudioUnitParameterFlag_HasName = (1L << 21)
586 kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
587 kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
588 kAudioUnitParameterFlag_NonRealTime = (1L << 24)
589 kAudioUnitParameterFlag_CanRamp = (1L << 25)
590 kAudioUnitParameterFlag_ExpertMode = (1L << 26)
591 kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
592 kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
593 kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
594 kAudioUnitParameterFlag_IsReadable = (1L << 30)
595 kAudioUnitParameterFlag_IsWritable = (1L << 31)
598 d
.lower
= info
.minValue
;
599 d
.upper
= info
.maxValue
;
600 d
.default_value
= info
.defaultValue
;
602 d
.integer_step
= (info
.unit
== kAudioUnitParameterUnit_Indexed
);
603 d
.toggled
= (info
.unit
== kAudioUnitParameterUnit_Boolean
) ||
604 (d
.integer_step
&& ((d
.upper
- d
.lower
) == 1.0));
605 d
.sr_dependent
= (info
.unit
== kAudioUnitParameterUnit_SampleFrames
);
606 d
.automatable
= !d
.toggled
&&
607 !(info
.flags
& kAudioUnitParameterFlag_NonRealTime
) &&
608 (info
.flags
& kAudioUnitParameterFlag_IsWritable
);
610 d
.logarithmic
= (info
.flags
& kAudioUnitParameterFlag_DisplayLogarithmic
);
616 d
.min_unbound
= 0; // lower is bound
617 d
.max_unbound
= 0; // upper is bound
619 descriptors
.push_back (d
);
626 four_ints_to_four_byte_literal (unsigned char n
[4])
628 /* this is actually implementation dependent. sigh. this is what gcc
629 and quite a few others do.
631 return ((n
[0] << 24) + (n
[1] << 16) + (n
[2] << 8) + n
[3]);
635 AUPlugin::maybe_fix_broken_au_id (const std::string
& id
)
637 if (isdigit (id
[0])) {
641 /* ID format is xxxx-xxxx-xxxx
642 where x maybe \xNN or a printable character.
644 Split at the '-' and and process each part into an integer.
645 Then put it back together.
649 unsigned char nascent
[4];
650 const char* cstr
= id
.c_str();
651 const char* estr
= cstr
+ id
.size();
662 while (*cstr
&& next_int
< 4) {
666 if (estr
- cstr
< 3) {
668 /* too close to the end for \xNN parsing: treat as literal characters */
670 cerr
<< "Parse " << cstr
<< " as a literal \\" << endl
;
677 if (cstr
[1] == 'x' && isxdigit (cstr
[2]) && isxdigit (cstr
[3])) {
681 memcpy (short_buf
, &cstr
[2], 2);
682 nascent
[in
] = strtol (short_buf
, NULL
, 16);
688 /* treat as literal characters */
689 cerr
<< "Parse " << cstr
<< " as a literal \\" << endl
;
703 if (in
&& (in
% 4 == 0)) {
704 /* nascent is ready */
705 n
[next_int
] = four_ints_to_four_byte_literal (nascent
);
709 /* swallow space-hyphen-space */
723 s
<< n
[0] << '-' << n
[1] << '-' << n
[2];
732 AUPlugin::unique_id () const
734 return AUPluginInfo::stringify_descriptor (comp
->Desc());
738 AUPlugin::label () const
740 return _info
->name
.c_str();
744 AUPlugin::parameter_count () const
746 return descriptors
.size();
750 AUPlugin::default_value (uint32_t port
)
752 if (port
< descriptors
.size()) {
753 return descriptors
[port
].default_value
;
760 AUPlugin::signal_latency () const
762 return unit
->Latency() * _session
.frame_rate();
766 AUPlugin::set_parameter (uint32_t which
, float val
)
768 if (which
>= descriptors
.size()) {
772 const AUParameterDescriptor
& d (descriptors
[which
]);
773 TRACE_API ("set parameter %d in scope %d element %d to %f\n", d
.id
, d
.scope
, d
.element
, val
);
774 unit
->SetParameter (d
.id
, d
.scope
, d
.element
, val
);
776 /* tell the world what we did */
778 AudioUnitEvent theEvent
;
780 theEvent
.mEventType
= kAudioUnitEvent_ParameterValueChange
;
781 theEvent
.mArgument
.mParameter
.mAudioUnit
= unit
->AU();
782 theEvent
.mArgument
.mParameter
.mParameterID
= d
.id
;
783 theEvent
.mArgument
.mParameter
.mScope
= d
.scope
;
784 theEvent
.mArgument
.mParameter
.mElement
= d
.element
;
786 TRACE_API ("notify about parameter change\n");
787 AUEventListenerNotify (NULL
, NULL
, &theEvent
);
789 Plugin::set_parameter (which
, val
);
793 AUPlugin::get_parameter (uint32_t which
) const
796 if (which
< descriptors
.size()) {
797 const AUParameterDescriptor
& d (descriptors
[which
]);
798 TRACE_API ("get value of parameter %d in scope %d element %d\n", d
.id
, d
.scope
, d
.element
);
799 unit
->GetParameter(d
.id
, d
.scope
, d
.element
, val
);
805 AUPlugin::get_parameter_descriptor (uint32_t which
, ParameterDescriptor
& pd
) const
807 if (which
< descriptors
.size()) {
808 pd
= descriptors
[which
];
815 AUPlugin::nth_parameter (uint32_t which
, bool& ok
) const
817 if (which
< descriptors
.size()) {
826 AUPlugin::activate ()
830 TRACE_API ("call Initialize in activate()\n");
831 if ((err
= unit
->Initialize()) != noErr
) {
832 error
<< string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err
) << endmsg
;
834 frames_processed
= 0;
841 AUPlugin::deactivate ()
843 TRACE_API ("call Uninitialize in deactivate()\n");
844 unit
->Uninitialize ();
851 TRACE_API ("call Reset in flush()\n");
852 unit
->GlobalReset ();
856 AUPlugin::requires_fixed_size_buffers() const
858 return _requires_fixed_size_buffers
;
863 AUPlugin::set_block_size (pframes_t nframes
)
865 bool was_initialized
= initialized
;
866 UInt32 numFrames
= nframes
;
873 TRACE_API ("set MaximumFramesPerSlice in global scope to %u\n", numFrames
);
874 if ((err
= unit
->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
,
875 0, &numFrames
, sizeof (numFrames
))) != noErr
) {
876 cerr
<< "cannot set max frames (err = " << err
<< ')' << endl
;
880 if (was_initialized
) {
884 _current_block_size
= nframes
;
890 AUPlugin::configure_io (ChanCount in
, ChanCount out
)
892 AudioStreamBasicDescription streamFormat
;
893 bool was_initialized
= initialized
;
894 int32_t audio_in
= in
.n_audio();
895 int32_t audio_out
= out
.n_audio();
898 //if we are already running with the requested i/o config, bail out here
899 if ( (audio_in
==input_channels
) && (audio_out
==output_channels
) ) {
906 streamFormat
.mSampleRate
= _session
.frame_rate();
907 streamFormat
.mFormatID
= kAudioFormatLinearPCM
;
908 streamFormat
.mFormatFlags
= kAudioFormatFlagIsFloat
|kAudioFormatFlagIsPacked
|kAudioFormatFlagIsNonInterleaved
;
910 #ifdef __LITTLE_ENDIAN__
913 streamFormat
.mFormatFlags
|= kAudioFormatFlagIsBigEndian
;
916 streamFormat
.mBitsPerChannel
= 32;
917 streamFormat
.mFramesPerPacket
= 1;
919 /* apple says that for non-interleaved data, these
920 values always refer to a single channel.
922 streamFormat
.mBytesPerPacket
= 4;
923 streamFormat
.mBytesPerFrame
= 4;
925 streamFormat
.mChannelsPerFrame
= audio_in
;
927 if (set_input_format (streamFormat
) != 0) {
931 streamFormat
.mChannelsPerFrame
= audio_out
;
933 if (set_output_format (streamFormat
) != 0) {
937 if (was_initialized
) {
945 AUPlugin::can_support_io_configuration (const ChanCount
& in
, ChanCount
& out
) const
947 // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never
948 // attempt to multiply-instantiate plugins to meet io configurations.
950 int32_t audio_in
= in
.n_audio();
952 int32_t plugcnt
= -1;
953 AUPluginInfoPtr pinfo
= boost::dynamic_pointer_cast
<AUPluginInfo
>(get_info());
955 /* lets check MIDI first */
957 if (in
.n_midi() > 0) {
958 if (!_has_midi_input
) {
963 vector
<pair
<int,int> >& io_configs
= pinfo
->cache
.io_configs
;
965 if (debug_io_config
) {
966 cerr
<< name() << " has " << io_configs
.size() << " IO Configurations\n";
969 //Ardour expects the plugin to tell it the output configuration
970 //but AU plugins can have multiple I/O configurations
971 //in most cases (since we don't allow special routing like sidechains in A2, we want to preserve the number of streams
972 //so first lets see if there's a configuration that keeps out==in
974 audio_out
= audio_in
;
976 for (vector
<pair
<int,int> >::iterator i
= io_configs
.begin(); i
!= io_configs
.end(); ++i
) {
977 int32_t possible_in
= i
->first
;
978 int32_t possible_out
= i
->second
;
980 if (possible_in
== audio_in
&& possible_out
== audio_out
) {
981 cerr
<< "\tCHOSEN: in " << in
<< " out " << out
<< endl
;
986 /* now allow potentially "imprecise" matches */
988 for (vector
<pair
<int,int> >::iterator i
= io_configs
.begin(); i
!= io_configs
.end(); ++i
) {
990 int32_t possible_in
= i
->first
;
991 int32_t possible_out
= i
->second
;
993 if (debug_io_config
) {
994 cerr
<< "\tin " << possible_in
<< " out " << possible_out
<< endl
;
997 if (possible_out
== 0) {
998 warning
<< string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg
;
1002 if (possible_in
== 0) {
1004 /* instrument plugin, always legal but throws away inputs ...
1007 if (possible_out
== -1) {
1008 /* out much match in (UNLIKELY!!) */
1009 audio_out
= audio_in
;
1011 } else if (possible_out
== -2) {
1012 /* any configuration possible, pick matching */
1013 audio_out
= audio_in
;
1015 } else if (possible_out
< -2) {
1016 /* explicit variable number of outputs, pick maximum */
1017 audio_out
= -possible_out
;
1020 /* exact number of outputs */
1021 audio_out
= possible_out
;
1026 if (possible_in
== -1) {
1028 /* wildcard for input */
1030 if (possible_out
== -1) {
1031 /* out much match in */
1032 audio_out
= audio_in
;
1034 } else if (possible_out
== -2) {
1035 /* any configuration possible, pick matching */
1036 audio_out
= audio_in
;
1038 } else if (possible_out
< -2) {
1039 /* explicit variable number of outputs, pick maximum */
1040 audio_out
= -possible_out
;
1043 /* exact number of outputs */
1044 audio_out
= possible_out
;
1049 if (possible_in
== -2) {
1051 if (possible_out
== -1) {
1052 /* any configuration possible, pick matching */
1053 audio_out
= audio_in
;
1055 } else if (possible_out
== -2) {
1056 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
1059 } else if (possible_out
< -2) {
1060 /* explicit variable number of outputs, pick maximum */
1061 audio_out
= -possible_out
;
1064 /* exact number of outputs */
1065 audio_out
= possible_out
;
1070 if (possible_in
< -2) {
1072 /* explicit variable number of inputs */
1074 if (audio_in
> -possible_in
) {
1075 /* request is too large */
1079 if (possible_out
== -1) {
1080 /* out must match in */
1081 audio_out
= audio_in
;
1083 } else if (possible_out
== -2) {
1084 error
<< string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
1087 } else if (possible_out
< -2) {
1088 /* explicit variable number of outputs, pick maximum */
1089 audio_out
= -possible_out
;
1092 /* exact number of outputs */
1093 audio_out
= possible_out
;
1098 if (possible_in
== audio_in
) {
1100 /* exact number of inputs ... must match obviously */
1102 if (possible_out
== -1) {
1103 /* out must match in */
1104 audio_out
= audio_in
;
1106 } else if (possible_out
== -2) {
1107 /* any output configuration, pick matching */
1108 audio_out
= audio_in
;
1110 } else if (possible_out
< -2) {
1111 /* explicit variable number of outputs, pick maximum */
1112 audio_out
= -possible_out
;
1115 /* exact number of outputs */
1116 audio_out
= possible_out
;
1127 if (debug_io_config
) {
1129 cerr
<< "\tCHOSEN: in " << in
<< " out " << out
<< " plugcnt will be " << plugcnt
<< endl
;
1131 cerr
<< "\tFAIL: no configs match requested in " << in
<< endl
;
1135 out
.set (DataType::MIDI
, 0);
1136 out
.set (DataType::AUDIO
, audio_out
);
1142 AUPlugin::set_input_format (AudioStreamBasicDescription
& fmt
)
1144 return set_stream_format (kAudioUnitScope_Input
, input_elements
, fmt
);
1148 AUPlugin::set_output_format (AudioStreamBasicDescription
& fmt
)
1150 if (set_stream_format (kAudioUnitScope_Output
, output_elements
, fmt
) != 0) {
1159 buffers
= (AudioBufferList
*) malloc (offsetof(AudioBufferList
, mBuffers
) +
1160 fmt
.mChannelsPerFrame
* sizeof(::AudioBuffer
));
1162 Glib::Mutex::Lock
em (_session
.engine().process_lock());
1163 IO::PortCountChanged (ChanCount(DataType::AUDIO
, fmt
.mChannelsPerFrame
));
1169 AUPlugin::set_stream_format (int scope
, uint32_t cnt
, AudioStreamBasicDescription
& fmt
)
1173 for (uint32_t i
= 0; i
< cnt
; ++i
) {
1174 TRACE_API ("set stream format for %s, scope = %d element %d\n",
1175 (scope
== kAudioUnitScope_Input
? "input" : "output"),
1177 if ((result
= unit
->SetFormat (scope
, i
, fmt
)) != 0) {
1178 error
<< string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
1179 (scope
== kAudioUnitScope_Input
? "input" : "output"), i
, result
) << endmsg
;
1184 if (scope
== kAudioUnitScope_Input
) {
1185 input_channels
= fmt
.mChannelsPerFrame
;
1187 output_channels
= fmt
.mChannelsPerFrame
;
1194 AUPlugin::render_callback(AudioUnitRenderActionFlags
*,
1195 const AudioTimeStamp
*,
1197 UInt32 inNumberFrames
,
1198 AudioBufferList
* ioData
)
1200 if (_has_midi_input
) {
1201 assert (current_buffers
->count().n_midi() > 0);
1203 /* deliver the first (and assumed only) MIDI buffer's data
1207 MidiBuffer
& mb (current_buffers
->get_midi(0));
1209 for (MidiBuffer::iterator i
= mb
.begin(); i
!= mb
.end(); ++i
) {
1210 Evoral::MIDIEvent
<MidiBuffer::TimeType
> ev
= *i
;
1211 switch (ev
.type()) {
1212 case MIDI_CMD_NOTE_ON
:
1213 case MIDI_CMD_NOTE_OFF
:
1214 case MIDI_CMD_CONTROL
:
1215 case MIDI_CMD_BENDER
:
1216 case MIDI_CMD_PGM_CHANGE
:
1217 case MIDI_CMD_CHANNEL_PRESSURE
:
1219 const uint8_t* b
= ev
.buffer();
1220 unit
->MIDIEvent (b
[0], b
[1], b
[2], ev
.time());
1225 /* plugins do not get other stuff by default */
1231 /* not much to do with audio - the data is already in the buffers given to us in connect_and_run() */
1233 if (current_maxbuf
== 0) {
1234 error
<< _("AUPlugin: render callback called illegally!") << endmsg
;
1235 return kAudioUnitErr_CannotDoInCurrentContext
;
1237 uint32_t limit
= min ((uint32_t) ioData
->mNumberBuffers
, current_maxbuf
);
1239 for (uint32_t i
= 0; i
< limit
; ++i
) {
1240 ioData
->mBuffers
[i
].mNumberChannels
= 1;
1241 ioData
->mBuffers
[i
].mDataByteSize
= sizeof (Sample
) * inNumberFrames
;
1243 /* we don't use the channel mapping because audiounits are
1244 never replicated. one plugin instance uses all channels/buffers
1245 passed to PluginInsert::connect_and_run()
1248 ioData
->mBuffers
[i
].mData
= current_buffers
->get_audio (i
).data (cb_offset
+ current_offset
);
1251 cb_offset
+= inNumberFrames
;
1257 AUPlugin::connect_and_run (BufferSet
& bufs
, ChanMapping in_map
, ChanMapping out_map
, pframes_t nframes
, framecnt_t offset
)
1259 Plugin::connect_and_run (bufs
, in_map
, out_map
, nframes
, offset
);
1261 AudioUnitRenderActionFlags flags
= 0;
1264 uint32_t maxbuf
= bufs
.count().n_audio();
1266 if (requires_fixed_size_buffers() && (nframes
!= _last_nframes
)) {
1267 unit
->GlobalReset();
1268 _last_nframes
= nframes
;
1271 current_buffers
= &bufs
;
1272 current_maxbuf
= maxbuf
;
1273 current_offset
= offset
;
1276 buffers
->mNumberBuffers
= min ((uint32_t) output_channels
, maxbuf
);
1278 for (uint32_t i
= 0; i
< buffers
->mNumberBuffers
; ++i
) {
1279 buffers
->mBuffers
[i
].mNumberChannels
= 1;
1280 buffers
->mBuffers
[i
].mDataByteSize
= nframes
* sizeof (Sample
);
1281 buffers
->mBuffers
[i
].mData
= 0;
1284 ts
.mSampleTime
= frames_processed
;
1285 ts
.mFlags
= kAudioTimeStampSampleTimeValid
;
1287 if ((err
= unit
->Render (&flags
, &ts
, 0, nframes
, buffers
)) == noErr
) {
1290 frames_processed
+= nframes
;
1292 uint32_t limit
= min ((uint32_t) buffers
->mNumberBuffers
, maxbuf
);
1295 for (i
= 0; i
< limit
; ++i
) {
1296 Sample
* expected_buffer_address
= bufs
.get_audio (i
).data (offset
);
1297 if (expected_buffer_address
!= buffers
->mBuffers
[i
].mData
) {
1298 // cerr << "chn " << i << " rendered into " << bufs[i]+offset << endl;
1299 memcpy (expected_buffer_address
, buffers
->mBuffers
[i
].mData
, nframes
* sizeof (Sample
));
1303 /* now silence any buffers that were passed in but the that the plugin
1304 did not fill/touch/use.
1307 for (;i
< maxbuf
; ++i
) {
1308 Sample
* buffer_address
= bufs
.get_audio (i
).data (offset
);
1309 memset (buffer_address
, 0, nframes
* sizeof (Sample
));
1315 cerr
<< name() << " render status " << err
<< endl
;
1320 AUPlugin::get_beat_and_tempo_callback (Float64
* outCurrentBeat
,
1321 Float64
* outCurrentTempo
)
1323 TempoMap
& tmap (_session
.tempo_map());
1325 TRACE_API ("AU calls ardour beat&tempo callback\n");
1327 /* more than 1 meter or more than 1 tempo means that a simplistic computation
1328 (and interpretation) of a beat position will be incorrect. So refuse to
1332 if (tmap
.n_tempos() > 1 || tmap
.n_meters() > 1) {
1333 return kAudioUnitErr_CannotDoInCurrentContext
;
1336 Timecode::BBT_Time bbt
;
1337 TempoMetric metric
= tmap
.metric_at (_session
.transport_frame() + current_offset
);
1338 tmap
.bbt_time_with_metric (_session
.transport_frame() + current_offset
, bbt
, metric
);
1340 if (outCurrentBeat
) {
1342 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1344 beat
+= bbt
.ticks
/ BBT_Time::ticks_per_beat
;
1345 *outCurrentBeat
= beat
;
1348 if (outCurrentTempo
) {
1349 *outCurrentTempo
= floor (metric
.tempo().beats_per_minute());
1357 AUPlugin::get_musical_time_location_callback (UInt32
* outDeltaSampleOffsetToNextBeat
,
1358 Float32
* outTimeSig_Numerator
,
1359 UInt32
* outTimeSig_Denominator
,
1360 Float64
* outCurrentMeasureDownBeat
)
1362 TempoMap
& tmap (_session
.tempo_map());
1364 TRACE_API ("AU calls ardour music time location callback\n");
1366 /* more than 1 meter or more than 1 tempo means that a simplistic computation
1367 (and interpretation) of a beat position will be incorrect. So refuse to
1371 if (tmap
.n_tempos() > 1 || tmap
.n_meters() > 1) {
1372 return kAudioUnitErr_CannotDoInCurrentContext
;
1375 Timecode::BBT_Time bbt
;
1376 TempoMetric metric
= tmap
.metric_at (_session
.transport_frame() + current_offset
);
1377 tmap
.bbt_time_with_metric (_session
.transport_frame() + current_offset
, bbt
, metric
);
1379 if (outDeltaSampleOffsetToNextBeat
) {
1380 if (bbt
.ticks
== 0) {
1382 *outDeltaSampleOffsetToNextBeat
= 0;
1384 *outDeltaSampleOffsetToNextBeat
= (UInt32
) floor (((BBT_Time::ticks_per_beat
- bbt
.ticks
)/BBT_Time::ticks_per_beat
) * // fraction of a beat to next beat
1385 metric
.tempo().frames_per_beat(_session
.frame_rate(), metric
.meter())); // frames per beat
1389 if (outTimeSig_Numerator
) {
1390 *outTimeSig_Numerator
= (UInt32
) lrintf (metric
.meter().beats_per_bar());
1392 if (outTimeSig_Denominator
) {
1393 *outTimeSig_Denominator
= (UInt32
) lrintf (metric
.meter().note_divisor());
1396 if (outCurrentMeasureDownBeat
) {
1398 /* beat for the start of the bar.
1400 2|1|0 -> 1 + beats_per_bar
1401 3|1|0 -> 1 + (2 * beats_per_bar)
1405 *outCurrentMeasureDownBeat
= 1 + metric
.meter().beats_per_bar() * (bbt
.bars
- 1);
1412 AUPlugin::get_transport_state_callback (Boolean
* outIsPlaying
,
1413 Boolean
* outTransportStateChanged
,
1414 Float64
* outCurrentSampleInTimeLine
,
1415 Boolean
* outIsCycling
,
1416 Float64
* outCycleStartBeat
,
1417 Float64
* outCycleEndBeat
)
1422 TRACE_API ("AU calls ardour transport state callback\n");
1424 rolling
= _session
.transport_rolling();
1425 speed
= _session
.transport_speed ();
1428 *outIsPlaying
= _session
.transport_rolling();
1431 if (outTransportStateChanged
) {
1432 if (rolling
!= last_transport_rolling
) {
1433 *outTransportStateChanged
= true;
1434 } else if (speed
!= last_transport_speed
) {
1435 *outTransportStateChanged
= true;
1437 *outTransportStateChanged
= false;
1441 if (outCurrentSampleInTimeLine
) {
1442 /* this assumes that the AU can only call this host callback from render context,
1443 where current_offset is valid.
1445 *outCurrentSampleInTimeLine
= _session
.transport_frame() + current_offset
;
1449 Location
* loc
= _session
.locations()->auto_loop_location();
1451 *outIsCycling
= (loc
&& _session
.transport_rolling() && _session
.get_play_loop());
1453 if (*outIsCycling
) {
1455 if (outCycleStartBeat
|| outCycleEndBeat
) {
1457 TempoMap
& tmap (_session
.tempo_map());
1459 /* more than 1 meter means that a simplistic computation (and interpretation) of
1460 a beat position will be incorrect. so refuse to offer the value.
1463 if (tmap
.n_meters() > 1) {
1464 return kAudioUnitErr_CannotDoInCurrentContext
;
1467 Timecode::BBT_Time bbt
;
1469 if (outCycleStartBeat
) {
1470 TempoMetric metric
= tmap
.metric_at (loc
->start() + current_offset
);
1471 _session
.tempo_map().bbt_time_with_metric (loc
->start(), bbt
, metric
);
1474 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1476 beat
+= bbt
.ticks
/ BBT_Time::ticks_per_beat
;
1478 *outCycleStartBeat
= beat
;
1481 if (outCycleEndBeat
) {
1482 TempoMetric metric
= tmap
.metric_at (loc
->end() + current_offset
);
1483 _session
.tempo_map().bbt_time_with_metric (loc
->end(), bbt
, metric
);
1486 beat
= metric
.meter().beats_per_bar() * bbt
.bars
;
1488 beat
+= bbt
.ticks
/ BBT_Time::ticks_per_beat
;
1490 *outCycleEndBeat
= beat
;
1496 last_transport_rolling
= rolling
;
1497 last_transport_speed
= speed
;
1502 set
<Evoral::Parameter
>
1503 AUPlugin::automatable() const
1505 set
<Evoral::Parameter
> automates
;
1507 for (uint32_t i
= 0; i
< descriptors
.size(); ++i
) {
1508 if (descriptors
[i
].automatable
) {
1509 automates
.insert (automates
.end(), Evoral::Parameter (PluginAutomation
, 0, i
));
1517 AUPlugin::describe_parameter (Evoral::Parameter param
)
1519 if (param
.type() == PluginAutomation
&& param
.id() < parameter_count()) {
1520 return descriptors
[param
.id()].label
;
1527 AUPlugin::print_parameter (uint32_t /*param*/, char* /*buf*/, uint32_t /*len*/) const
1529 // NameValue stuff here
1533 AUPlugin::parameter_is_audio (uint32_t) const
1539 AUPlugin::parameter_is_control (uint32_t) const
1545 AUPlugin::parameter_is_input (uint32_t) const
1551 AUPlugin::parameter_is_output (uint32_t) const
1557 AUPlugin::add_state (XMLNode
* root
)
1559 LocaleGuard
lg (X_("POSIX"));
1561 #ifdef AU_STATE_SUPPORT
1563 CFPropertyListRef propertyList
;
1565 TRACE_API ("get preset state\n");
1566 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1570 // Convert the property list into XML data.
1572 xmlData
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, propertyList
);
1575 error
<< _("Could not create XML version of property list") << endmsg
;
1579 /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
1580 our state node. GACK!
1585 if (t
.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData
), CFDataGetLength (xmlData
)))) {
1587 root
->add_child_copy (*t
.root());
1591 CFRelease (xmlData
);
1592 CFRelease (propertyList
);
1594 if (!seen_get_state_message
) {
1595 info
<< string_compose (_("Saving AudioUnit settings is not supported in this build of %1. Consider paying for a newer version"),
1598 seen_get_state_message
= true;
1604 AUPlugin::set_state(const XMLNode
& node
, int version
)
1606 #ifdef AU_STATE_SUPPORT
1608 CFPropertyListRef propertyList
;
1609 LocaleGuard
lg (X_("POSIX"));
1611 if (node
.name() != state_node_name()) {
1612 error
<< _("Bad node sent to AUPlugin::set_state") << endmsg
;
1616 if (node
.children().empty()) {
1620 XMLNode
* top
= node
.children().front();
1621 XMLNode
* copy
= new XMLNode (*top
);
1626 const string
& xml
= t
.write_buffer ();
1627 CFDataRef xmlData
= CFDataCreateWithBytesNoCopy (kCFAllocatorDefault
, (UInt8
*) xml
.data(), xml
.length(), kCFAllocatorNull
);
1628 CFStringRef errorString
;
1630 propertyList
= CFPropertyListCreateFromXMLData( kCFAllocatorDefault
,
1632 kCFPropertyListImmutable
,
1635 CFRelease (xmlData
);
1638 TRACE_API ("set preset\n");
1639 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1642 /* tell the world */
1644 AudioUnitParameter changedUnit
;
1645 changedUnit
.mAudioUnit
= unit
->AU();
1646 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1647 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1649 CFRelease (propertyList
);
1652 Plugin::set_state (node
, version
);
1655 if (!seen_set_state_message
) {
1656 info
<< string_compose (_("Restoring AudioUnit settings is not supported in this build of %1. Consider paying for a newer version"),
1660 return Plugin::set_state (node
, version
);
1665 AUPlugin::load_preset (PluginRecord r
)
1667 Plugin::load_preset (r
);
1669 #ifdef AU_STATE_SUPPORT
1671 CFPropertyListRef propertyList
;
1673 UserPresetMap::iterator ux
;
1674 FactoryPresetMap::iterator fx
;
1676 /* look first in "user" presets */
1678 if ((ux
= user_preset_map
.find (preset_label
)) != user_preset_map
.end()) {
1680 if ((propertyList
= load_property_list (ux
->second
)) != 0) {
1681 TRACE_API ("set preset from user presets\n");
1682 if (unit
->SetAUPreset (propertyList
) == noErr
) {
1685 /* tell the world */
1687 AudioUnitParameter changedUnit
;
1688 changedUnit
.mAudioUnit
= unit
->AU();
1689 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1690 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1692 CFRelease(propertyList
);
1695 } else if ((fx
= factory_preset_map
.find (preset_label
)) != factory_preset_map
.end()) {
1699 preset
.presetNumber
= fx
->second
;
1700 preset
.presetName
= CFStringCreateWithCString (kCFAllocatorDefault
, fx
->first
.c_str(), kCFStringEncodingUTF8
);
1702 TRACE_API ("set preset from factory presets\n");
1704 if (unit
->SetPresentPreset (preset
) == 0) {
1707 /* tell the world */
1709 AudioUnitParameter changedUnit
;
1710 changedUnit
.mAudioUnit
= unit
->AU();
1711 changedUnit
.mParameterID
= kAUParameterListener_AnyParameter
;
1712 AUParameterListenerNotify (NULL
, NULL
, &changedUnit
);
1718 if (!seen_loading_message
) {
1719 info
<< string_compose (_("Loading AudioUnit presets is not supported in this build of %1. Consider paying for a newer version"),
1722 seen_loading_message
= true;
1729 AUPlugin::save_preset (string preset_name
)
1731 #ifdef AU_STATE_SUPPORT
1732 CFPropertyListRef propertyList
;
1733 vector
<Glib::ustring
> v
;
1734 Glib::ustring user_preset_path
;
1737 std::string m
= maker();
1738 std::string n
= name();
1740 strip_whitespace_edges (m
);
1741 strip_whitespace_edges (n
);
1743 v
.push_back (Glib::get_home_dir());
1744 v
.push_back ("Library");
1745 v
.push_back ("Audio");
1746 v
.push_back ("Presets");
1750 user_preset_path
= Glib::build_filename (v
);
1752 if (g_mkdir_with_parents (user_preset_path
.c_str(), 0775) < 0) {
1753 error
<< string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path
) << endmsg
;
1757 TRACE_API ("get current preset\n");
1758 if (unit
->GetAUPreset (propertyList
) != noErr
) {
1762 // add the actual preset name */
1764 v
.push_back (preset_name
+ preset_suffix
);
1768 user_preset_path
= Glib::build_filename (v
);
1770 set_preset_name_in_plist (propertyList
, preset_name
);
1772 if (save_property_list (propertyList
, user_preset_path
)) {
1773 error
<< string_compose (_("Saving plugin state to %1 failed"), user_preset_path
) << endmsg
;
1777 CFRelease(propertyList
);
1781 if (!seen_saving_message
) {
1782 info
<< string_compose (_("Saving AudioUnit presets is not supported in this build of %1. Consider paying for a newer version"),
1785 seen_saving_message
= true;
1791 //-----------------------------------------------------------------------------
1792 // this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
1794 GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary
, CFStringRef inDictionaryKey
, Boolean
* outSuccess
)
1796 CFNumberRef cfNumber
;
1797 SInt32 numberValue
= 0;
1798 Boolean dummySuccess
;
1800 if (outSuccess
== NULL
)
1801 outSuccess
= &dummySuccess
;
1802 if ( (inAUStateDictionary
== NULL
) || (inDictionaryKey
== NULL
) )
1804 *outSuccess
= FALSE
;
1808 cfNumber
= (CFNumberRef
) CFDictionaryGetValue(inAUStateDictionary
, inDictionaryKey
);
1809 if (cfNumber
== NULL
)
1811 *outSuccess
= FALSE
;
1814 *outSuccess
= CFNumberGetValue(cfNumber
, kCFNumberSInt32Type
, &numberValue
);
1822 GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData
, ComponentDescription
* outComponentDescription
)
1824 CFDictionaryRef auStateDictionary
;
1825 ComponentDescription tempDesc
= {0,0,0,0,0};
1826 SInt32 versionValue
;
1829 if ( (inAUStateData
== NULL
) || (outComponentDescription
== NULL
) )
1832 // the property list for AU state data must be of the dictionary type
1833 if (CFGetTypeID(inAUStateData
) != CFDictionaryGetTypeID()) {
1834 return kAudioUnitErr_InvalidPropertyValue
;
1837 auStateDictionary
= (CFDictionaryRef
)inAUStateData
;
1839 // first check to make sure that the version of the AU state data is one that we know understand
1840 // XXX should I really do this? later versions would probably still hold these ID keys, right?
1841 versionValue
= GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetVersionKey
), &gotValue
);
1844 return kAudioUnitErr_InvalidPropertyValue
;
1846 #define kCurrentSavedStateVersion 0
1847 if (versionValue
!= kCurrentSavedStateVersion
) {
1848 return kAudioUnitErr_InvalidPropertyValue
;
1851 // grab the ComponentDescription values from the AU state data
1852 tempDesc
.componentType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetTypeKey
), NULL
);
1853 tempDesc
.componentSubType
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetSubtypeKey
), NULL
);
1854 tempDesc
.componentManufacturer
= (OSType
) GetDictionarySInt32Value(auStateDictionary
, CFSTR(kAUPresetManufacturerKey
), NULL
);
1855 // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
1856 if ( (tempDesc
.componentType
== 0) || (tempDesc
.componentSubType
== 0) || (tempDesc
.componentManufacturer
== 0) )
1857 return kAudioUnitErr_InvalidPropertyValue
;
1859 *outComponentDescription
= tempDesc
;
1864 static bool au_preset_filter (const string
& str
, void* arg
)
1866 /* Not a dotfile, has a prefix before a period, suffix is aupreset */
1870 ret
= (str
[0] != '.' && str
.length() > 9 && str
.find (preset_suffix
) == (str
.length() - preset_suffix
.length()));
1874 /* check the preset file path name against this plugin
1875 ID. The idea is that all preset files for this plugin
1876 include "<manufacturer>/<plugin-name>" in their path.
1879 Plugin
* p
= (Plugin
*) arg
;
1880 string match
= p
->maker();
1884 ret
= str
.find (match
) != string::npos
;
1887 string m
= p
->maker ();
1888 string n
= p
->name ();
1889 strip_whitespace_edges (m
);
1890 strip_whitespace_edges (n
);
1895 ret
= str
.find (match
) != string::npos
;
1903 check_and_get_preset_name (Component component
, const string
& pathstr
, string
& preset_name
)
1906 CFPropertyListRef plist
;
1907 ComponentDescription presetDesc
;
1910 plist
= load_property_list (pathstr
);
1916 // get the ComponentDescription from the AU preset file
1918 status
= GetAUComponentDescriptionFromStateData(plist
, &presetDesc
);
1920 if (status
== noErr
) {
1921 if (ComponentAndDescriptionMatch_Loosely(component
, &presetDesc
)) {
1923 /* try to get the preset name from the property list */
1925 if (CFGetTypeID(plist
) == CFDictionaryGetTypeID()) {
1927 const void* psk
= CFDictionaryGetValue ((CFMutableDictionaryRef
)plist
, CFSTR(kAUPresetNameKey
));
1931 const char* p
= CFStringGetCStringPtr ((CFStringRef
) psk
, kCFStringEncodingUTF8
);
1934 char buf
[PATH_MAX
+1];
1936 if (CFStringGetCString ((CFStringRef
)psk
, buf
, sizeof (buf
), kCFStringEncodingUTF8
)) {
1951 AUPlugin::current_preset() const
1955 #ifdef AU_STATE_SUPPORT
1956 CFPropertyListRef propertyList
;
1958 TRACE_API ("get current preset for current_preset()\n");
1959 if (unit
->GetAUPreset (propertyList
) == noErr
) {
1960 preset_name
= get_preset_name_in_plist (propertyList
);
1961 CFRelease(propertyList
);
1968 AUPlugin::find_presets ()
1970 #ifdef AU_STATE_SUPPORT
1971 vector
<string
*>* preset_files
;
1972 PathScanner scanner
;
1974 user_preset_map
.clear ();
1976 preset_files
= scanner (preset_search_path
, au_preset_filter
, this, true, true, -1, true);
1978 if (!preset_files
) {
1982 for (vector
<string
*>::iterator x
= preset_files
->begin(); x
!= preset_files
->end(); ++x
) {
1984 string path
= *(*x
);
1987 /* make an initial guess at the preset name using the path */
1989 preset_name
= Glib::path_get_basename (path
);
1990 preset_name
= preset_name
.substr (0, preset_name
.find_last_of ('.'));
1992 /* check that this preset file really matches this plugin
1993 and potentially get the "real" preset name from
1997 if (check_and_get_preset_name (get_comp()->Comp(), path
, preset_name
)) {
1998 user_preset_map
[preset_name
] = path
;
2004 delete preset_files
;
2006 /* now fill the vector<string> with the names we have */
2008 for (UserPresetMap::iterator i
= user_preset_map
.begin(); i
!= user_preset_map
.end(); ++i
) {
2009 _presets
.insert (i
->second
, Plugin::PresetRecord (i
->second
, i
->first
));
2012 /* add factory presets */
2014 for (FactoryPresetMap::iterator i
= factory_preset_map
.begin(); i
!= factory_preset_map
.end(); ++i
) {
2016 string
const uri
= string_compose ("%1", _presets
.size ());
2017 _presets
.push_back (uri
, Plugin::PresetRecord (uri
, i
->first
));
2024 AUPlugin::has_editor () const
2026 // even if the plugin doesn't have its own editor, the AU API can be used
2027 // to create one that looks native.
2031 AUPluginInfo::AUPluginInfo (boost::shared_ptr
<CAComponentDescription
> d
)
2034 type
= ARDOUR::AudioUnit
;
2037 AUPluginInfo::~AUPluginInfo ()
2039 type
= ARDOUR::AudioUnit
;
2043 AUPluginInfo::load (Session
& session
)
2048 TRACE_API ("load AU as a component\n");
2049 boost::shared_ptr
<CAComponent
> comp (new CAComponent(*descriptor
));
2051 if (!comp
->IsValid()) {
2052 error
<< ("AudioUnit: not a valid Component") << endmsg
;
2054 plugin
.reset (new AUPlugin (session
.engine(), session
, comp
));
2057 AUPluginInfo
*aup
= new AUPluginInfo (*this);
2058 plugin
->set_info (PluginInfoPtr (aup
));
2059 boost::dynamic_pointer_cast
<AUPlugin
> (plugin
)->set_fixed_size_buffers (aup
->creator
== "Universal Audio");
2063 catch (failed_constructor
&err
) {
2064 return PluginPtr ();
2069 AUPluginInfo::au_cache_path ()
2071 return Glib::build_filename (ARDOUR::user_config_directory().to_string(), "au_cache");
2075 AUPluginInfo::discover ()
2079 if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS
)) {
2080 ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
2083 PluginInfoList
* plugs
= new PluginInfoList
;
2085 discover_fx (*plugs
);
2086 discover_music (*plugs
);
2087 discover_generators (*plugs
);
2088 discover_instruments (*plugs
);
2094 AUPluginInfo::discover_music (PluginInfoList
& plugs
)
2096 CAComponentDescription desc
;
2097 desc
.componentFlags
= 0;
2098 desc
.componentFlagsMask
= 0;
2099 desc
.componentSubType
= 0;
2100 desc
.componentManufacturer
= 0;
2101 desc
.componentType
= kAudioUnitType_MusicEffect
;
2103 discover_by_description (plugs
, desc
);
2107 AUPluginInfo::discover_fx (PluginInfoList
& plugs
)
2109 CAComponentDescription desc
;
2110 desc
.componentFlags
= 0;
2111 desc
.componentFlagsMask
= 0;
2112 desc
.componentSubType
= 0;
2113 desc
.componentManufacturer
= 0;
2114 desc
.componentType
= kAudioUnitType_Effect
;
2116 discover_by_description (plugs
, desc
);
2120 AUPluginInfo::discover_generators (PluginInfoList
& plugs
)
2122 CAComponentDescription desc
;
2123 desc
.componentFlags
= 0;
2124 desc
.componentFlagsMask
= 0;
2125 desc
.componentSubType
= 0;
2126 desc
.componentManufacturer
= 0;
2127 desc
.componentType
= kAudioUnitType_Generator
;
2129 discover_by_description (plugs
, desc
);
2133 AUPluginInfo::discover_instruments (PluginInfoList
& plugs
)
2135 CAComponentDescription desc
;
2136 desc
.componentFlags
= 0;
2137 desc
.componentFlagsMask
= 0;
2138 desc
.componentSubType
= 0;
2139 desc
.componentManufacturer
= 0;
2140 desc
.componentType
= kAudioUnitType_MusicDevice
;
2142 discover_by_description (plugs
, desc
);
2146 AUPluginInfo::discover_by_description (PluginInfoList
& plugs
, CAComponentDescription
& desc
)
2150 comp
= FindNextComponent (NULL
, &desc
);
2152 while (comp
!= NULL
) {
2153 CAComponentDescription temp
;
2154 GetComponentInfo (comp
, &temp
, NULL
, NULL
, NULL
);
2156 AUPluginInfoPtr
info (new AUPluginInfo
2157 (boost::shared_ptr
<CAComponentDescription
> (new CAComponentDescription(temp
))));
2159 /* although apple designed the subtype field to be a "category" indicator,
2160 its really turned into a plugin ID field for a given manufacturer. Hence
2161 there are no categories for AudioUnits. However, to keep the plugins
2162 showing up under "categories", we'll use the "type" as a high level
2165 NOTE: no panners, format converters or i/o AU's for our purposes
2168 switch (info
->descriptor
->Type()) {
2169 case kAudioUnitType_Panner
:
2170 case kAudioUnitType_OfflineEffect
:
2171 case kAudioUnitType_FormatConverter
:
2174 case kAudioUnitType_Output
:
2175 info
->category
= _("AudioUnit Outputs");
2177 case kAudioUnitType_MusicDevice
:
2178 info
->category
= _("AudioUnit Instruments");
2180 case kAudioUnitType_MusicEffect
:
2181 info
->category
= _("AudioUnit MusicEffects");
2183 case kAudioUnitType_Effect
:
2184 info
->category
= _("AudioUnit Effects");
2186 case kAudioUnitType_Mixer
:
2187 info
->category
= _("AudioUnit Mixers");
2189 case kAudioUnitType_Generator
:
2190 info
->category
= _("AudioUnit Generators");
2193 info
->category
= _("AudioUnit (Unknown)");
2197 AUPluginInfo::get_names (temp
, info
->name
, info
->creator
);
2199 info
->type
= ARDOUR::AudioUnit
;
2200 info
->unique_id
= stringify_descriptor (*info
->descriptor
);
2202 /* XXX not sure of the best way to handle plugin versioning yet
2205 CAComponent
cacomp (*info
->descriptor
);
2207 if (cacomp
.GetResourceVersion (info
->version
) != noErr
) {
2211 if (cached_io_configuration (info
->unique_id
, info
->version
, cacomp
, info
->cache
, info
->name
)) {
2213 /* here we have to map apple's wildcard system to a simple pair
2214 of values. in ::can_do() we use the whole system, but here
2215 we need a single pair of values. XXX probably means we should
2216 remove any use of these values.
2219 info
->n_inputs
.set (DataType::AUDIO
, info
->cache
.io_configs
.front().first
);
2220 info
->n_outputs
.set (DataType::AUDIO
, info
->cache
.io_configs
.front().second
);
2222 cerr
<< "detected AU: " << info
->name
.c_str() << " (" << info
->cache
.io_configs
.size() << " i/o configurations) - " << info
->unique_id
<< endl
;
2224 plugs
.push_back (info
);
2227 error
<< string_compose (_("Cannot get I/O configuration info for AU %1"), info
->name
) << endmsg
;
2230 comp
= FindNextComponent (comp
, &desc
);
2235 AUPluginInfo::cached_io_configuration (const std::string
& unique_id
,
2238 AUPluginCachedInfo
& cinfo
,
2239 const std::string
& name
)
2244 /* concatenate unique ID with version to provide a key for cached info lookup.
2245 this ensures we don't get stale information, or should if plugin developers
2246 follow Apple "guidelines".
2249 snprintf (buf
, sizeof (buf
), "%u", (uint32_t) version
);
2254 CachedInfoMap::iterator cim
= cached_info
.find (id
);
2256 if (cim
!= cached_info
.end()) {
2257 cinfo
= cim
->second
;
2262 AUChannelInfo
* channel_info
;
2266 ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name
));
2270 if (CAAudioUnit::Open (comp
, unit
) != noErr
) {
2276 warning
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endmsg
;
2277 cerr
<< string_compose (_("Could not load AU plugin %1 - ignored"), name
) << endl
;
2282 TRACE_API ("get AU channel info\n");
2283 if ((ret
= unit
.GetChannelInfo (&channel_info
, cnt
)) < 0) {
2288 /* no explicit info available */
2290 cinfo
.io_configs
.push_back (pair
<int,int> (-1, -1));
2294 /* store each configuration */
2296 for (uint32_t n
= 0; n
< cnt
; ++n
) {
2297 cinfo
.io_configs
.push_back (pair
<int,int> (channel_info
[n
].inChannels
,
2298 channel_info
[n
].outChannels
));
2301 free (channel_info
);
2304 add_cached_info (id
, cinfo
);
2305 save_cached_info ();
2311 AUPluginInfo::add_cached_info (const std::string
& id
, AUPluginCachedInfo
& cinfo
)
2313 cached_info
[id
] = cinfo
;
2316 #define AU_CACHE_VERSION "2.0"
2319 AUPluginInfo::save_cached_info ()
2323 node
= new XMLNode (X_("AudioUnitPluginCache"));
2324 node
->add_property( "version", AU_CACHE_VERSION
);
2326 for (map
<string
,AUPluginCachedInfo
>::iterator i
= cached_info
.begin(); i
!= cached_info
.end(); ++i
) {
2327 XMLNode
* parent
= new XMLNode (X_("plugin"));
2328 parent
->add_property ("id", i
->first
);
2329 node
->add_child_nocopy (*parent
);
2331 for (vector
<pair
<int, int> >::iterator j
= i
->second
.io_configs
.begin(); j
!= i
->second
.io_configs
.end(); ++j
) {
2333 XMLNode
* child
= new XMLNode (X_("io"));
2336 snprintf (buf
, sizeof (buf
), "%d", j
->first
);
2337 child
->add_property (X_("in"), buf
);
2338 snprintf (buf
, sizeof (buf
), "%d", j
->second
);
2339 child
->add_property (X_("out"), buf
);
2340 parent
->add_child_nocopy (*child
);
2345 Glib::ustring path
= au_cache_path ();
2348 tree
.set_root (node
);
2350 if (!tree
.write (path
)) {
2351 error
<< string_compose (_("could not save AU cache to %1"), path
) << endmsg
;
2352 unlink (path
.c_str());
2357 AUPluginInfo::load_cached_info ()
2359 Glib::ustring path
= au_cache_path ();
2362 if (!Glib::file_test (path
, Glib::FILE_TEST_EXISTS
)) {
2366 if ( !tree
.read (path
) ) {
2367 error
<< "au_cache is not a valid XML file. AU plugins will be re-scanned" << endmsg
;
2371 const XMLNode
* root (tree
.root());
2373 if (root
->name() != X_("AudioUnitPluginCache")) {
2377 //initial version has incorrectly stored i/o info, and/or garbage chars.
2378 const XMLProperty
* version
= root
->property(X_("version"));
2379 if (! ((version
!= NULL
) && (version
->value() == X_(AU_CACHE_VERSION
)))) {
2380 error
<< "au_cache is not correct version. AU plugins will be re-scanned" << endmsg
;
2384 cached_info
.clear ();
2386 const XMLNodeList children
= root
->children();
2388 for (XMLNodeConstIterator iter
= children
.begin(); iter
!= children
.end(); ++iter
) {
2390 const XMLNode
* child
= *iter
;
2392 if (child
->name() == X_("plugin")) {
2394 const XMLNode
* gchild
;
2395 const XMLNodeList gchildren
= child
->children();
2396 const XMLProperty
* prop
= child
->property (X_("id"));
2402 string id
= prop
->value();
2406 string::size_type slash
= id
.find_last_of ('/');
2408 if (slash
== string::npos
) {
2412 version
= id
.substr (slash
);
2413 id
= id
.substr (0, slash
);
2414 fixed
= AUPlugin::maybe_fix_broken_au_id (id
);
2416 if (fixed
.empty()) {
2417 error
<< string_compose (_("Your AudioUnit configuration cache contains an AU plugin whose ID cannot be understood - ignored (%1)"), id
) << endmsg
;
2424 AUPluginCachedInfo cinfo
;
2426 for (XMLNodeConstIterator giter
= gchildren
.begin(); giter
!= gchildren
.end(); giter
++) {
2430 if (gchild
->name() == X_("io")) {
2434 const XMLProperty
* iprop
;
2435 const XMLProperty
* oprop
;
2437 if (((iprop
= gchild
->property (X_("in"))) != 0) &&
2438 ((oprop
= gchild
->property (X_("out"))) != 0)) {
2439 in
= atoi (iprop
->value());
2440 out
= atoi (oprop
->value());
2442 cinfo
.io_configs
.push_back (pair
<int,int> (in
, out
));
2447 if (cinfo
.io_configs
.size()) {
2448 add_cached_info (id
, cinfo
);
2457 AUPluginInfo::get_names (CAComponentDescription
& comp_desc
, std::string
& name
, std::string
& maker
)
2459 CFStringRef itemName
= NULL
;
2461 // Marc Poirier-style item name
2462 CAComponent
auComponent (comp_desc
);
2463 if (auComponent
.IsValid()) {
2464 CAComponentDescription dummydesc
;
2465 Handle nameHandle
= NewHandle(sizeof(void*));
2466 if (nameHandle
!= NULL
) {
2467 OSErr err
= GetComponentInfo(auComponent
.Comp(), &dummydesc
, nameHandle
, NULL
, NULL
);
2469 ConstStr255Param nameString
= (ConstStr255Param
) (*nameHandle
);
2470 if (nameString
!= NULL
) {
2471 itemName
= CFStringCreateWithPascalString(kCFAllocatorDefault
, nameString
, CFStringGetSystemEncoding());
2474 DisposeHandle(nameHandle
);
2478 // if Marc-style fails, do the original way
2479 if (itemName
== NULL
) {
2480 CFStringRef compTypeString
= UTCreateStringForOSType(comp_desc
.componentType
);
2481 CFStringRef compSubTypeString
= UTCreateStringForOSType(comp_desc
.componentSubType
);
2482 CFStringRef compManufacturerString
= UTCreateStringForOSType(comp_desc
.componentManufacturer
);
2484 itemName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ - %@ - %@"),
2485 compTypeString
, compManufacturerString
, compSubTypeString
);
2487 if (compTypeString
!= NULL
)
2488 CFRelease(compTypeString
);
2489 if (compSubTypeString
!= NULL
)
2490 CFRelease(compSubTypeString
);
2491 if (compManufacturerString
!= NULL
)
2492 CFRelease(compManufacturerString
);
2495 string str
= CFStringRefToStdString(itemName
);
2496 string::size_type colon
= str
.find (':');
2499 name
= str
.substr (colon
+1);
2500 maker
= str
.substr (0, colon
);
2501 strip_whitespace_edges (maker
);
2502 strip_whitespace_edges (name
);
2506 strip_whitespace_edges (name
);
2511 AUPluginInfo::stringify_descriptor (const CAComponentDescription
& desc
)
2515 /* note: OSType is a compiler-implemenation-defined value,
2516 historically a 32 bit integer created with a multi-character
2517 constant such as 'abcd'. It is, fundamentally, an abomination.
2522 s
<< desc
.SubType();
2530 AUPluginInfo::needs_midi_input ()
2532 return is_effect_with_midi_input () || is_instrument ();
2536 AUPluginInfo::is_effect () const
2538 return is_effect_without_midi_input() || is_effect_with_midi_input();
2542 AUPluginInfo::is_effect_without_midi_input () const
2544 return descriptor
->IsAUFX();
2548 AUPluginInfo::is_effect_with_midi_input () const
2550 return descriptor
->IsAUFM();
2554 AUPluginInfo::is_instrument () const
2556 return descriptor
->IsMusicDevice();