1acb5633cd050d74ffb5c860d0c18e859431723e
[ardour.git] / libs / ardour / audio_unit.cc
1 /*
2     Copyright (C) 2006-2009 Paul Davis
3     Some portions Copyright (C) Sophia Poirier.
4
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.
9
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.
14
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.
18
19 */
20
21 #include <sstream>
22 #include <fstream>
23 #include <errno.h>
24 #include <string.h>
25 #include <math.h>
26 #include <ctype.h>
27
28 #include "pbd/gstdio_compat.h"
29 #include "pbd/transmitter.h"
30 #include "pbd/xml++.h"
31 #include "pbd/convert.h"
32 #include "pbd/whitespace.h"
33 #include "pbd/file_utils.h"
34 #include "pbd/locale_guard.h"
35
36 #include <glibmm/threads.h>
37 #include <glibmm/fileutils.h>
38 #include <glibmm/miscutils.h>
39
40 #include "ardour/ardour.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/audio_buffer.h"
43 #include "ardour/debug.h"
44 #include "ardour/midi_buffer.h"
45 #include "ardour/filesystem_paths.h"
46 #include "ardour/io.h"
47 #include "ardour/audio_unit.h"
48 #include "ardour/route.h"
49 #include "ardour/session.h"
50 #include "ardour/tempo.h"
51 #include "ardour/utils.h"
52
53 #include "CAAudioUnit.h"
54 #include "CAAUParameter.h"
55
56 #include <CoreFoundation/CoreFoundation.h>
57 #include <CoreServices/CoreServices.h>
58 #include <AudioUnit/AudioUnit.h>
59 #include <AudioToolbox/AudioUnitUtilities.h>
60 #ifdef WITH_CARBON
61 #include <Carbon/Carbon.h>
62 #endif
63
64 #ifdef COREAUDIO105
65 #define ArdourComponent Component
66 #define ArdourDescription ComponentDescription
67 #define ArdourFindNext FindNextComponent
68 #else
69 #define ArdourComponent AudioComponent
70 #define ArdourDescription AudioComponentDescription
71 #define ArdourFindNext AudioComponentFindNext
72 #endif
73
74 #include "i18n.h"
75
76 using namespace std;
77 using namespace PBD;
78 using namespace ARDOUR;
79
80 AUPluginInfo::CachedInfoMap AUPluginInfo::cached_info;
81
82 static string preset_search_path = "/Library/Audio/Presets:/Network/Library/Audio/Presets";
83 static string preset_suffix = ".aupreset";
84 static bool preset_search_path_initialized = false;
85 FILE * AUPluginInfo::_crashlog_fd = NULL;
86 bool AUPluginInfo::_scan_only = true;
87
88
89 static void au_blacklist (std::string id)
90 {
91         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
92         FILE * blacklist_fd = NULL;
93         if (! (blacklist_fd = fopen(fn.c_str(), "a"))) {
94                 PBD::error << "Cannot append to AU blacklist for '"<< id <<"'\n";
95                 return;
96         }
97         assert(id.find("\n") == string::npos);
98         fprintf(blacklist_fd, "%s\n", id.c_str());
99         ::fclose(blacklist_fd);
100 }
101
102 static void au_unblacklist (std::string id)
103 {
104         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
105         if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
106                 PBD::warning << "Expected Blacklist file does not exist.\n";
107                 return;
108         }
109
110         std::string bl;
111         {
112                 std::ifstream ifs(fn.c_str());
113                 bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
114         }
115
116         ::g_unlink (fn.c_str());
117
118         assert (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS));
119         assert(id.find("\n") == string::npos);
120
121         id += "\n"; // add separator
122         const size_t rpl = bl.find(id);
123         if (rpl != string::npos) {
124                 bl.replace(rpl, id.size(), "");
125         }
126         if (bl.empty()) {
127                 return;
128         }
129
130         FILE * blacklist_fd = NULL;
131         if (! (blacklist_fd = fopen(fn.c_str(), "w"))) {
132                 PBD::error << "Cannot open AU blacklist.\n";
133                 return;
134         }
135         fprintf(blacklist_fd, "%s", bl.c_str());
136         ::fclose(blacklist_fd);
137 }
138
139 static bool is_blacklisted (std::string id)
140 {
141         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
142         if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
143                 return false;
144         }
145         std::string bl;
146         std::ifstream ifs(fn.c_str());
147         bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
148
149         assert(id.find("\n") == string::npos);
150
151         id += "\n"; // add separator
152         const size_t rpl = bl.find(id);
153         if (rpl != string::npos) {
154                 return true;
155         }
156         return false;
157 }
158
159
160
161 static OSStatus
162 _render_callback(void *userData,
163                  AudioUnitRenderActionFlags *ioActionFlags,
164                  const AudioTimeStamp    *inTimeStamp,
165                  UInt32       inBusNumber,
166                  UInt32       inNumberFrames,
167                  AudioBufferList*       ioData)
168 {
169         if (userData) {
170                 return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
171         }
172         return paramErr;
173 }
174
175 static OSStatus
176 _get_beat_and_tempo_callback (void*    userData,
177                               Float64* outCurrentBeat,
178                               Float64* outCurrentTempo)
179 {
180         if (userData) {
181                 return ((AUPlugin*)userData)->get_beat_and_tempo_callback (outCurrentBeat, outCurrentTempo);
182         }
183
184         return paramErr;
185 }
186
187 static OSStatus
188 _get_musical_time_location_callback (void *     userData,
189                                      UInt32 *   outDeltaSampleOffsetToNextBeat,
190                                      Float32 *  outTimeSig_Numerator,
191                                      UInt32 *   outTimeSig_Denominator,
192                                      Float64 *  outCurrentMeasureDownBeat)
193 {
194         if (userData) {
195                 return ((AUPlugin*)userData)->get_musical_time_location_callback (outDeltaSampleOffsetToNextBeat,
196                                                                                   outTimeSig_Numerator,
197                                                                                   outTimeSig_Denominator,
198                                                                                   outCurrentMeasureDownBeat);
199         }
200         return paramErr;
201 }
202
203 static OSStatus
204 _get_transport_state_callback (void*     userData,
205                                Boolean*  outIsPlaying,
206                                Boolean*  outTransportStateChanged,
207                                Float64*  outCurrentSampleInTimeLine,
208                                Boolean*  outIsCycling,
209                                Float64*  outCycleStartBeat,
210                                Float64*  outCycleEndBeat)
211 {
212         if (userData) {
213                 return ((AUPlugin*)userData)->get_transport_state_callback (
214                         outIsPlaying, outTransportStateChanged,
215                         outCurrentSampleInTimeLine, outIsCycling,
216                         outCycleStartBeat, outCycleEndBeat);
217         }
218         return paramErr;
219 }
220
221
222 static int
223 save_property_list (CFPropertyListRef propertyList, Glib::ustring path)
224
225 {
226         CFDataRef xmlData;
227         int fd;
228
229         // Convert the property list into XML data.
230
231         xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
232
233         if (!xmlData) {
234                 error << _("Could not create XML version of property list") << endmsg;
235                 return -1;
236         }
237
238         // Write the XML data to the file.
239
240         fd = open (path.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0664);
241         while (fd < 0) {
242                 if (errno == EEXIST) {
243                         error << string_compose (_("Preset file %1 exists; not overwriting"),
244                                                  path) << endmsg;
245                 } else {
246                         error << string_compose (_("Cannot open preset file %1 (%2)"),
247                                                  path, strerror (errno)) << endmsg;
248                 }
249                 CFRelease (xmlData);
250                 return -1;
251         }
252
253         size_t cnt = CFDataGetLength (xmlData);
254
255         if (write (fd, CFDataGetBytePtr (xmlData), cnt) != (ssize_t) cnt) {
256                 CFRelease (xmlData);
257                 close (fd);
258                 return -1;
259         }
260
261         close (fd);
262         return 0;
263 }
264
265
266 static CFPropertyListRef
267 load_property_list (Glib::ustring path)
268 {
269         int fd;
270         CFPropertyListRef propertyList = 0;
271         CFDataRef         xmlData;
272         CFStringRef       errorString;
273
274         // Read the XML file.
275
276         if ((fd = open (path.c_str(), O_RDONLY)) < 0) {
277                 return propertyList;
278
279         }
280
281         off_t len = lseek (fd, 0, SEEK_END);
282         char* buf = new char[len];
283         lseek (fd, 0, SEEK_SET);
284
285         if (read (fd, buf, len) != len) {
286                 delete [] buf;
287                 close (fd);
288                 return propertyList;
289         }
290
291         close (fd);
292
293         xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) buf, len, kCFAllocatorNull);
294
295         // Reconstitute the dictionary using the XML data.
296
297         propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
298                                                         xmlData,
299                                                         kCFPropertyListImmutable,
300                                                         &errorString);
301
302         CFRelease (xmlData);
303         delete [] buf;
304
305         return propertyList;
306 }
307
308 //-----------------------------------------------------------------------------
309 static void
310 set_preset_name_in_plist (CFPropertyListRef plist, string preset_name)
311 {
312         if (!plist) {
313                 return;
314         }
315         CFStringRef pn = CFStringCreateWithCString (kCFAllocatorDefault, preset_name.c_str(), kCFStringEncodingUTF8);
316
317         if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
318                 CFDictionarySetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey), pn);
319         }
320
321         CFRelease (pn);
322 }
323
324 //-----------------------------------------------------------------------------
325 static std::string
326 get_preset_name_in_plist (CFPropertyListRef plist)
327 {
328         std::string ret;
329
330         if (!plist) {
331                 return ret;
332         }
333
334         if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
335                 const void *p = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
336                 if (p) {
337                         CFStringRef str = (CFStringRef) p;
338                         int len = CFStringGetLength(str);
339                         len =  (len * 2) + 1;
340                         char local_buffer[len];
341                         if (CFStringGetCString (str, local_buffer, len, kCFStringEncodingUTF8)) {
342                                 ret = local_buffer;
343                         }
344                 }
345         }
346         return ret;
347 }
348
349 //--------------------------------------------------------------------------
350 // general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
351 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
352 Boolean ComponentDescriptionsMatch_General(const ArdourDescription * inComponentDescription1, const ArdourDescription * inComponentDescription2, Boolean inIgnoreType);
353 Boolean ComponentDescriptionsMatch_General(const ArdourDescription * inComponentDescription1, const ArdourDescription * inComponentDescription2, Boolean inIgnoreType)
354 {
355         if ( (inComponentDescription1 == NULL) || (inComponentDescription2 == NULL) )
356                 return FALSE;
357
358         if ( (inComponentDescription1->componentSubType == inComponentDescription2->componentSubType)
359                         && (inComponentDescription1->componentManufacturer == inComponentDescription2->componentManufacturer) )
360         {
361                 // only sub-type and manufacturer IDs need to be equal
362                 if (inIgnoreType)
363                         return TRUE;
364                 // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
365                 else if (inComponentDescription1->componentType == inComponentDescription2->componentType)
366                         return TRUE;
367         }
368
369         return FALSE;
370 }
371
372 //--------------------------------------------------------------------------
373 // general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
374 // if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
375 Boolean ComponentAndDescriptionMatch_General(ArdourComponent inComponent, const ArdourDescription * inComponentDescription, Boolean inIgnoreType);
376 Boolean ComponentAndDescriptionMatch_General(ArdourComponent inComponent, const ArdourDescription * inComponentDescription, Boolean inIgnoreType)
377 {
378         OSErr status;
379         ArdourDescription desc;
380
381         if ( (inComponent == NULL) || (inComponentDescription == NULL) )
382                 return FALSE;
383
384         // get the ComponentDescription of the input Component
385 #ifdef COREAUDIO105
386         status = GetComponentInfo(inComponent, &desc, NULL, NULL, NULL);
387 #else
388         status = AudioComponentGetDescription (inComponent, &desc);
389 #endif
390         if (status != noErr)
391                 return FALSE;
392
393         // check if the Component's ComponentDescription matches the input ComponentDescription
394         return ComponentDescriptionsMatch_General(&desc, inComponentDescription, inIgnoreType);
395 }
396
397 //--------------------------------------------------------------------------
398 // determine if 2 ComponentDescriptions are basically equal
399 // (by that, I mean that the important identifying values are compared,
400 // but not the ComponentDescription flags)
401 Boolean ComponentDescriptionsMatch(const ArdourDescription * inComponentDescription1, const ArdourDescription * inComponentDescription2)
402 {
403         return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, FALSE);
404 }
405
406 //--------------------------------------------------------------------------
407 // determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
408 Boolean ComponentDescriptionsMatch_Loose(const ArdourDescription * inComponentDescription1, const ArdourDescription * inComponentDescription2)
409 {
410         return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, TRUE);
411 }
412
413 //--------------------------------------------------------------------------
414 // determine if a ComponentDescription basically matches that of a particular Component
415 Boolean ComponentAndDescriptionMatch(ArdourComponent inComponent, const ArdourDescription * inComponentDescription)
416 {
417         return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, FALSE);
418 }
419
420 //--------------------------------------------------------------------------
421 // determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
422 Boolean ComponentAndDescriptionMatch_Loosely(ArdourComponent inComponent, const ArdourDescription * inComponentDescription)
423 {
424         return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, TRUE);
425 }
426
427
428 AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
429         : Plugin (engine, session)
430         , comp (_comp)
431         , unit (new CAAudioUnit)
432         , initialized (false)
433         , _current_block_size (0)
434         , _requires_fixed_size_buffers (false)
435         , buffers (0)
436         , variable_inputs (false)
437         , variable_outputs (false)
438         , configured_input_busses (0)
439         , configured_output_busses (0)
440         , bus_inputs (0)
441         , bus_outputs (0)
442         , input_maxbuf (0)
443         , input_offset (0)
444         , input_buffers (0)
445         , input_map (0)
446         , frames_processed (0)
447         , audio_input_cnt (0)
448         , _parameter_listener (0)
449         , _parameter_listener_arg (0)
450         , last_transport_rolling (false)
451         , last_transport_speed (0.0)
452 {
453         if (!preset_search_path_initialized) {
454                 Glib::ustring p = Glib::get_home_dir();
455                 p += "/Library/Audio/Presets:";
456                 p += preset_search_path;
457                 preset_search_path = p;
458                 preset_search_path_initialized = true;
459                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset Path: %1\n", preset_search_path));
460         }
461
462         init ();
463 }
464
465
466 AUPlugin::AUPlugin (const AUPlugin& other)
467         : Plugin (other)
468         , comp (other.get_comp())
469         , unit (new CAAudioUnit)
470         , initialized (false)
471         , _current_block_size (0)
472         , _last_nframes (0)
473         , _requires_fixed_size_buffers (false)
474         , buffers (0)
475         , variable_inputs (false)
476         , variable_outputs (false)
477         , configured_input_busses (0)
478         , configured_output_busses (0)
479         , bus_inputs (0)
480         , bus_outputs (0)
481         , input_maxbuf (0)
482         , input_offset (0)
483         , input_buffers (0)
484         , input_map (0)
485         , frames_processed (0)
486         , _parameter_listener (0)
487         , _parameter_listener_arg (0)
488
489 {
490         init ();
491         for (size_t i = 0; i < descriptors.size(); ++i) {
492                 set_parameter (i, other.get_parameter (i));
493         }
494 }
495
496 AUPlugin::~AUPlugin ()
497 {
498         if (_parameter_listener) {
499                 AUListenerDispose (_parameter_listener);
500                 _parameter_listener = 0;
501         }
502
503         if (unit) {
504                 DEBUG_TRACE (DEBUG::AudioUnits, "about to call uninitialize in plugin destructor\n");
505                 unit->Uninitialize ();
506         }
507
508         free (buffers);
509         free (bus_inputs);
510         free (bus_outputs);
511 }
512
513 void
514 AUPlugin::discover_factory_presets ()
515 {
516         CFArrayRef presets;
517         UInt32 dataSize;
518         Boolean isWritable;
519         OSStatus err;
520
521         if ((err = unit->GetPropertyInfo (kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &dataSize, &isWritable)) != 0) {
522                 DEBUG_TRACE (DEBUG::AudioUnits, "no factory presets for AU\n");
523                 return;
524         }
525
526         assert (dataSize == sizeof (presets));
527
528         if ((err = unit->GetProperty (kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, (void*) &presets, &dataSize)) != 0) {
529                 error << string_compose (_("cannot get factory preset info: errcode %1"), err) << endmsg;
530                 return;
531         }
532
533         if (!presets) {
534                 return;
535         }
536
537         CFIndex cnt = CFArrayGetCount (presets);
538
539         for (CFIndex i = 0; i < cnt; ++i) {
540                 AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (presets, i);
541
542                 string name = CFStringRefToStdString (preset->presetName);
543                 factory_preset_map[name] = preset->presetNumber;
544                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Factory Preset: %1 > %2\n", name, preset->presetNumber));
545         }
546
547         CFRelease (presets);
548 }
549
550 void
551 AUPlugin::init ()
552 {
553         OSErr err;
554         CFStringRef itemName;
555
556         /* these keep track of *configured* channel set up,
557            not potential set ups.
558         */
559
560         input_channels = -1;
561         output_channels = -1;
562         {
563                 CAComponentDescription temp;
564 #ifdef COREAUDIO105
565                 GetComponentInfo (comp.get()->Comp(), &temp, NULL, NULL, NULL);
566 #else
567                 AudioComponentGetDescription (comp.get()->Comp(), &temp);
568 #endif
569                 CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
570                 CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
571                 CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
572                 itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
573                                 compTypeString, compManufacturerString, compSubTypeString);
574                 if (compTypeString != NULL) CFRelease(compTypeString);
575                 if (compSubTypeString != NULL) CFRelease(compSubTypeString);
576                 if (compManufacturerString != NULL) CFRelease(compManufacturerString);
577         }
578
579         au_blacklist(CFStringRefToStdString(itemName));
580
581         try {
582                 DEBUG_TRACE (DEBUG::AudioUnits, "opening AudioUnit\n");
583                 err = CAAudioUnit::Open (*(comp.get()), *unit);
584         } catch (...) {
585                 error << _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg;
586                 throw failed_constructor();
587         }
588
589         if (err != noErr) {
590                 error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg;
591                 throw failed_constructor ();
592         }
593
594         DEBUG_TRACE (DEBUG::AudioUnits, "count global elements\n");
595         unit->GetElementCount (kAudioUnitScope_Global, global_elements);
596         DEBUG_TRACE (DEBUG::AudioUnits, "count input elements\n");
597         unit->GetElementCount (kAudioUnitScope_Input, input_elements);
598         DEBUG_TRACE (DEBUG::AudioUnits, "count output elements\n");
599         unit->GetElementCount (kAudioUnitScope_Output, output_elements);
600
601         bus_inputs = (uint32_t*) calloc (input_elements, sizeof(uint32_t));
602         bus_outputs = (uint32_t*) calloc (output_elements, sizeof(uint32_t));
603
604         for (size_t i = 0; i < output_elements; ++i) {
605                 AudioUnitReset (unit->AU(), kAudioUnitScope_Output, i);
606                 AudioStreamBasicDescription fmt;
607                 UInt32 sz = sizeof(AudioStreamBasicDescription);
608                 err = AudioUnitGetProperty(unit->AU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, i, &fmt, &sz);
609                 if (err == noErr) {
610                         bus_outputs[i] = fmt.mChannelsPerFrame;
611                 }
612                 CFStringRef name;
613                 sz = sizeof (CFStringRef);
614                 if (AudioUnitGetProperty (unit->AU(), kAudioUnitProperty_ElementName, kAudioUnitScope_Output,
615                                         i, &name, &sz) == noErr
616                                 && sz > 0) {
617                         _bus_name_out.push_back (CFStringRefToStdString (name));
618                         CFRelease(name);
619                 } else {
620                         _bus_name_out.push_back (string_compose ("Audio-Bus %1", i));
621                 }
622         }
623
624         for (size_t i = 0; i < input_elements; ++i) {
625                 AudioUnitReset (unit->AU(), kAudioUnitScope_Input, i);
626                 AudioStreamBasicDescription fmt;
627                 UInt32 sz = sizeof(AudioStreamBasicDescription);
628                 err = AudioUnitGetProperty(unit->AU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &fmt, &sz);
629                 if (err == noErr) {
630                         bus_inputs[i] = fmt.mChannelsPerFrame;
631                 }
632                 CFStringRef name;
633                 sz = sizeof (CFStringRef);
634                 if (AudioUnitGetProperty (unit->AU(), kAudioUnitProperty_ElementName, kAudioUnitScope_Input,
635                                         i, &name, &sz) == noErr
636                                 && sz > 0) {
637                         _bus_name_in.push_back (CFStringRefToStdString (name));
638                         CFRelease(name);
639                 } else {
640                         _bus_name_in.push_back (string_compose ("Audio-Bus %1", i));
641                 }
642         }
643
644         for (size_t i = 0; i < input_elements; ++i) {
645                 /* setup render callback: the plugin calls this to get input data */
646                 AURenderCallbackStruct renderCallbackInfo;
647                 renderCallbackInfo.inputProc = _render_callback;
648                 renderCallbackInfo.inputProcRefCon = this;
649                 DEBUG_TRACE (DEBUG::AudioUnits, "set render callback in input scope\n");
650                 if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
651                                               i, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) {
652                         error << string_compose (_("cannot install render callback (err = %1)"), err) << endmsg;
653                         throw failed_constructor();
654                 }
655         }
656
657         /* tell the plugin about tempo/meter/transport callbacks in case it wants them */
658
659         HostCallbackInfo info;
660         memset (&info, 0, sizeof (HostCallbackInfo));
661         info.hostUserData = this;
662         info.beatAndTempoProc = _get_beat_and_tempo_callback;
663         info.musicalTimeLocationProc = _get_musical_time_location_callback;
664         info.transportStateProc = _get_transport_state_callback;
665
666         //ignore result of this - don't care if the property isn't supported
667         DEBUG_TRACE (DEBUG::AudioUnits, "set host callbacks in global scope\n");
668         unit->SetProperty (kAudioUnitProperty_HostCallbacks,
669                            kAudioUnitScope_Global,
670                            0, //elementID
671                            &info,
672                            sizeof (HostCallbackInfo));
673
674         if (set_block_size (_session.get_block_size())) {
675                 error << _("AUPlugin: cannot set processing block size") << endmsg;
676                 throw failed_constructor();
677         }
678
679         create_parameter_listener (AUPlugin::_parameter_change_listener, this, 0.05);
680         discover_parameters ();
681         discover_factory_presets ();
682
683         // Plugin::setup_controls ();
684
685         au_unblacklist(CFStringRefToStdString(itemName));
686         if (itemName != NULL) CFRelease(itemName);
687 }
688
689 void
690 AUPlugin::discover_parameters ()
691 {
692         /* discover writable parameters */
693
694         AudioUnitScope scopes[] = {
695                 kAudioUnitScope_Global,
696                 kAudioUnitScope_Output,
697                 kAudioUnitScope_Input
698         };
699
700         descriptors.clear ();
701
702         for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
703
704                 AUParamInfo param_info (unit->AU(), false, /* include read only */ true, scopes[i]);
705
706                 for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
707
708                         AUParameterDescriptor d;
709
710                         d.id = param_info.ParamID (i);
711
712                         const CAAUParameter* param = param_info.GetParamInfo (d.id);
713                         const AudioUnitParameterInfo& info (param->ParamInfo());
714
715                         const int len = CFStringGetLength (param->GetName());
716                         char local_buffer[len*2];
717                         Boolean good = CFStringGetCString (param->GetName(), local_buffer ,len*2 , kCFStringEncodingUTF8);
718                         if (!good) {
719                                 d.label = "???";
720                         } else {
721                                 d.label = local_buffer;
722                         }
723
724                         d.scope = param_info.GetScope ();
725                         d.element = param_info.GetElement ();
726
727                         /* info.units to consider */
728                         /*
729                           kAudioUnitParameterUnit_Generic             = 0
730                           kAudioUnitParameterUnit_Indexed             = 1
731                           kAudioUnitParameterUnit_Boolean             = 2
732                           kAudioUnitParameterUnit_Percent             = 3
733                           kAudioUnitParameterUnit_Seconds             = 4
734                           kAudioUnitParameterUnit_SampleFrames        = 5
735                           kAudioUnitParameterUnit_Phase               = 6
736                           kAudioUnitParameterUnit_Rate                = 7
737                           kAudioUnitParameterUnit_Hertz               = 8
738                           kAudioUnitParameterUnit_Cents               = 9
739                           kAudioUnitParameterUnit_RelativeSemiTones   = 10
740                           kAudioUnitParameterUnit_MIDINoteNumber      = 11
741                           kAudioUnitParameterUnit_MIDIController      = 12
742                           kAudioUnitParameterUnit_Decibels            = 13
743                           kAudioUnitParameterUnit_LinearGain          = 14
744                           kAudioUnitParameterUnit_Degrees             = 15
745                           kAudioUnitParameterUnit_EqualPowerCrossfade = 16
746                           kAudioUnitParameterUnit_MixerFaderCurve1    = 17
747                           kAudioUnitParameterUnit_Pan                 = 18
748                           kAudioUnitParameterUnit_Meters              = 19
749                           kAudioUnitParameterUnit_AbsoluteCents       = 20
750                           kAudioUnitParameterUnit_Octaves             = 21
751                           kAudioUnitParameterUnit_BPM                 = 22
752                           kAudioUnitParameterUnit_Beats               = 23
753                           kAudioUnitParameterUnit_Milliseconds        = 24
754                           kAudioUnitParameterUnit_Ratio               = 25
755                         */
756
757                         /* info.flags to consider */
758
759                         /*
760
761                           kAudioUnitParameterFlag_CFNameRelease       = (1L << 4)
762                           kAudioUnitParameterFlag_HasClump            = (1L << 20)
763                           kAudioUnitParameterFlag_HasName             = (1L << 21)
764                           kAudioUnitParameterFlag_DisplayLogarithmic  = (1L << 22)
765                           kAudioUnitParameterFlag_IsHighResolution    = (1L << 23)
766                           kAudioUnitParameterFlag_NonRealTime         = (1L << 24)
767                           kAudioUnitParameterFlag_CanRamp             = (1L << 25)
768                           kAudioUnitParameterFlag_ExpertMode          = (1L << 26)
769                           kAudioUnitParameterFlag_HasCFNameString     = (1L << 27)
770                           kAudioUnitParameterFlag_IsGlobalMeta        = (1L << 28)
771                           kAudioUnitParameterFlag_IsElementMeta       = (1L << 29)
772                           kAudioUnitParameterFlag_IsReadable          = (1L << 30)
773                           kAudioUnitParameterFlag_IsWritable          = (1L << 31)
774                         */
775
776                         d.lower = info.minValue;
777                         d.upper = info.maxValue;
778                         d.normal = info.defaultValue;
779
780                         d.integer_step = (info.unit == kAudioUnitParameterUnit_Indexed);
781                         d.toggled = (info.unit == kAudioUnitParameterUnit_Boolean) ||
782                                 (d.integer_step && ((d.upper - d.lower) == 1.0));
783                         d.sr_dependent = (info.unit == kAudioUnitParameterUnit_SampleFrames);
784                         d.automatable = /* !d.toggled && -- ardour can automate toggles, can AU ? */
785                                 !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
786                                 (info.flags & kAudioUnitParameterFlag_IsWritable);
787
788                         d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
789                         d.au_unit = info.unit;
790                         switch (info.unit) {
791                         case kAudioUnitParameterUnit_Decibels:
792                                 d.unit = ParameterDescriptor::DB;
793                                 break;
794                         case kAudioUnitParameterUnit_MIDINoteNumber:
795                                 d.unit = ParameterDescriptor::MIDI_NOTE;
796                                 break;
797                         case kAudioUnitParameterUnit_Hertz:
798                                 d.unit = ParameterDescriptor::HZ;
799                                 break;
800                         }
801
802                         d.min_unbound = 0; // lower is bound
803                         d.max_unbound = 0; // upper is bound
804                         d.update_steps();
805
806                         descriptors.push_back (d);
807
808                         uint32_t last_param = descriptors.size() - 1;
809                         parameter_map.insert (pair<uint32_t,uint32_t> (d.id, last_param));
810                         listen_to_parameter (last_param);
811                 }
812         }
813 }
814
815
816 static unsigned int
817 four_ints_to_four_byte_literal (unsigned char n[4])
818 {
819         /* this is actually implementation dependent. sigh. this is what gcc
820            and quite a few others do.
821          */
822         return ((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + n[3]);
823 }
824
825 std::string
826 AUPlugin::maybe_fix_broken_au_id (const std::string& id)
827 {
828         if (isdigit (id[0])) {
829                 return id;
830         }
831
832         /* ID format is xxxx-xxxx-xxxx
833            where x maybe \xNN or a printable character.
834
835            Split at the '-' and and process each part into an integer.
836            Then put it back together.
837         */
838
839
840         unsigned char nascent[4];
841         const char* cstr = id.c_str();
842         const char* estr = cstr + id.size();
843         uint32_t n[3];
844         int in;
845         int next_int;
846         char short_buf[3];
847         stringstream s;
848
849         in = 0;
850         next_int = 0;
851         short_buf[2] = '\0';
852
853         while (*cstr && next_int < 4) {
854
855                 if (*cstr == '\\') {
856
857                         if (estr - cstr < 3) {
858
859                                 /* too close to the end for \xNN parsing: treat as literal characters */
860
861                                 nascent[in] = *cstr;
862                                 ++cstr;
863                                 ++in;
864
865                         } else {
866
867                                 if (cstr[1] == 'x' && isxdigit (cstr[2]) && isxdigit (cstr[3])) {
868
869                                         /* parse \xNN */
870
871                                         memcpy (short_buf, &cstr[2], 2);
872                                         nascent[in] = strtol (short_buf, NULL, 16);
873                                         cstr += 4;
874                                         ++in;
875
876                                 } else {
877
878                                         /* treat as literal characters */
879                                         nascent[in] = *cstr;
880                                         ++cstr;
881                                         ++in;
882                                 }
883                         }
884
885                 } else {
886
887                         nascent[in] = *cstr;
888                         ++cstr;
889                         ++in;
890                 }
891
892                 if (in && (in % 4 == 0)) {
893                         /* nascent is ready */
894                         n[next_int] = four_ints_to_four_byte_literal (nascent);
895                         in = 0;
896                         next_int++;
897
898                         /* swallow space-hyphen-space */
899
900                         if (next_int < 3) {
901                                 ++cstr;
902                                 ++cstr;
903                                 ++cstr;
904                         }
905                 }
906         }
907
908         if (next_int != 3) {
909                 goto err;
910         }
911
912         s << n[0] << '-' << n[1] << '-' << n[2];
913
914         return s.str();
915
916 err:
917         return string();
918 }
919
920 string
921 AUPlugin::unique_id () const
922 {
923         return AUPluginInfo::stringify_descriptor (comp->Desc());
924 }
925
926 const char *
927 AUPlugin::label () const
928 {
929         return _info->name.c_str();
930 }
931
932 uint32_t
933 AUPlugin::parameter_count () const
934 {
935         return descriptors.size();
936 }
937
938 float
939 AUPlugin::default_value (uint32_t port)
940 {
941         if (port < descriptors.size()) {
942                 return descriptors[port].normal;
943         }
944
945         return 0;
946 }
947
948 framecnt_t
949 AUPlugin::signal_latency () const
950 {
951         return unit->Latency() * _session.frame_rate();
952 }
953
954 void
955 AUPlugin::set_parameter (uint32_t which, float val)
956 {
957         if (which >= descriptors.size()) {
958                 return;
959         }
960
961         if (get_parameter(which) == val) {
962                 return;
963         }
964
965         const AUParameterDescriptor& d (descriptors[which]);
966         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set parameter %1 in scope %2 element %3 to %4\n", d.id, d.scope, d.element, val));
967         unit->SetParameter (d.id, d.scope, d.element, val);
968
969         /* tell the world what we did */
970
971         AudioUnitEvent theEvent;
972
973         theEvent.mEventType = kAudioUnitEvent_ParameterValueChange;
974         theEvent.mArgument.mParameter.mAudioUnit = unit->AU();
975         theEvent.mArgument.mParameter.mParameterID = d.id;
976         theEvent.mArgument.mParameter.mScope = d.scope;
977         theEvent.mArgument.mParameter.mElement = d.element;
978
979         DEBUG_TRACE (DEBUG::AudioUnits, "notify about parameter change\n");
980         /* Note the 1st argument, which means "Don't notify us about a change we made ourselves" */
981         AUEventListenerNotify (_parameter_listener, NULL, &theEvent);
982
983         Plugin::set_parameter (which, val);
984 }
985
986 float
987 AUPlugin::get_parameter (uint32_t which) const
988 {
989         float val = 0.0;
990         if (which < descriptors.size()) {
991                 const AUParameterDescriptor& d (descriptors[which]);
992                 // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("get value of parameter %1 in scope %2 element %3\n", d.id, d.scope, d.element));
993                 unit->GetParameter(d.id, d.scope, d.element, val);
994         }
995         return val;
996 }
997
998 int
999 AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const
1000 {
1001         if (which < descriptors.size()) {
1002                 pd = descriptors[which];
1003                 return 0;
1004         }
1005         return -1;
1006 }
1007
1008 uint32_t
1009 AUPlugin::nth_parameter (uint32_t which, bool& ok) const
1010 {
1011         if (which < descriptors.size()) {
1012                 ok = true;
1013                 return which;
1014         }
1015         ok = false;
1016         return 0;
1017 }
1018
1019 void
1020 AUPlugin::activate ()
1021 {
1022         if (!initialized) {
1023                 OSErr err;
1024                 DEBUG_TRACE (DEBUG::AudioUnits, "call Initialize in activate()\n");
1025                 if ((err = unit->Initialize()) != noErr) {
1026                         error << string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err) << endmsg;
1027                 } else {
1028                         frames_processed = 0;
1029                         initialized = true;
1030                 }
1031         }
1032 }
1033
1034 void
1035 AUPlugin::deactivate ()
1036 {
1037         DEBUG_TRACE (DEBUG::AudioUnits, "call Uninitialize in deactivate()\n");
1038         unit->Uninitialize ();
1039         initialized = false;
1040 }
1041
1042 void
1043 AUPlugin::flush ()
1044 {
1045         DEBUG_TRACE (DEBUG::AudioUnits, "call Reset in flush()\n");
1046         unit->GlobalReset ();
1047 }
1048
1049 bool
1050 AUPlugin::requires_fixed_size_buffers() const
1051 {
1052         return _requires_fixed_size_buffers;
1053 }
1054
1055
1056 int
1057 AUPlugin::set_block_size (pframes_t nframes)
1058 {
1059         bool was_initialized = initialized;
1060         UInt32 numFrames = nframes;
1061         OSErr err;
1062
1063         if (initialized) {
1064                 deactivate ();
1065         }
1066
1067         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set MaximumFramesPerSlice in global scope to %1\n", numFrames));
1068         if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1069                                       0, &numFrames, sizeof (numFrames))) != noErr) {
1070                 error << string_compose (_("AU: cannot set max frames (err = %1)"), err) << endmsg;
1071                 return -1;
1072         }
1073
1074         if (was_initialized) {
1075                 activate ();
1076         }
1077
1078         _current_block_size = nframes;
1079
1080         return 0;
1081 }
1082
1083 bool
1084 AUPlugin::configure_io (ChanCount in, ChanCount out)
1085 {
1086         AudioStreamBasicDescription streamFormat;
1087         bool was_initialized = initialized;
1088         int32_t audio_out = out.n_audio();
1089         if (audio_input_cnt > 0) {
1090                 in.set (DataType::AUDIO, audio_input_cnt);
1091         }
1092         const int32_t audio_in = in.n_audio();
1093
1094         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("configure %1 for %2 in %3 out\n", name(), in, out));
1095
1096         if (initialized) {
1097                 //if we are already running with the requested i/o config, bail out here
1098                 if ( (audio_in==input_channels) && (audio_out==output_channels) ) {
1099                         return true;
1100                 } else {
1101                         deactivate ();
1102                 }
1103         }
1104
1105         streamFormat.mSampleRate = _session.frame_rate();
1106         streamFormat.mFormatID = kAudioFormatLinearPCM;
1107         streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
1108
1109 #ifdef __LITTLE_ENDIAN__
1110         /* relax */
1111 #else
1112         streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
1113 #endif
1114
1115         streamFormat.mBitsPerChannel = 32;
1116         streamFormat.mFramesPerPacket = 1;
1117
1118         /* apple says that for non-interleaved data, these
1119          * values always refer to a single channel.
1120          */
1121         streamFormat.mBytesPerPacket = 4;
1122         streamFormat.mBytesPerFrame = 4;
1123
1124         configured_input_busses = 0;
1125         configured_output_busses = 0;
1126         /* reset busses */
1127         for (size_t i = 0; i < output_elements; ++i) {
1128                 AudioUnitReset (unit->AU(), kAudioUnitScope_Output, i);
1129         }
1130         for (size_t i = 0; i < input_elements; ++i) {
1131                 AudioUnitReset (unit->AU(), kAudioUnitScope_Input, i);
1132         }
1133
1134         /* now assign the channels to available busses */
1135         uint32_t used_in = 0;
1136         uint32_t used_out = 0;
1137
1138         if (variable_inputs) {
1139                 // we only ever use the first bus
1140                 if (input_elements > 1) {
1141                         warning << string_compose (_("AU %1 has multiple input busses and variable port count."), name()) << endmsg;
1142                 }
1143                 streamFormat.mChannelsPerFrame = audio_in;
1144                 if (set_stream_format (kAudioUnitScope_Input, 0, streamFormat) != 0) {
1145                         return false;
1146                 }
1147                 configured_input_busses = 1;
1148                 used_in = audio_in;
1149         } else {
1150                 configured_input_busses = 0;
1151                 uint32_t remain = audio_in;
1152                 for (uint32_t bus = 0; remain > 0 && bus < input_elements; ++bus) {
1153                         uint32_t cnt = std::min (remain, bus_inputs[bus]);
1154                         if (cnt == 0) { continue; }
1155                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 configure input bus: %2 chn: %3", name(), bus, cnt));
1156
1157                         streamFormat.mChannelsPerFrame = cnt;
1158                         if (set_stream_format (kAudioUnitScope_Input, bus, streamFormat) != 0) {
1159                                 return false;
1160                         }
1161                         used_in += cnt;
1162                         ++configured_input_busses;
1163                         remain -= cnt;
1164                 }
1165         }
1166
1167         if (variable_outputs) {
1168                 if (output_elements > 1) {
1169                         warning << string_compose (_("AU %1 has multiple output busses and variable port count."), name()) << endmsg;
1170                 }
1171
1172                 streamFormat.mChannelsPerFrame = audio_out;
1173                 if (set_stream_format (kAudioUnitScope_Output, 0, streamFormat) != 0) {
1174                         return false;
1175                 }
1176                 configured_output_busses = 1;
1177                 used_out = audio_out;
1178         } else {
1179                 uint32_t remain = audio_out;
1180                 configured_output_busses = 0;
1181                 for (uint32_t bus = 0; remain > 0 && bus < output_elements; ++bus) {
1182                         uint32_t cnt = std::min (remain, bus_outputs[bus]);
1183                         if (cnt == 0) { continue; }
1184                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 configure output bus: %2 chn: %3", name(), bus, cnt));
1185                         streamFormat.mChannelsPerFrame = cnt;
1186                         if (set_stream_format (kAudioUnitScope_Output, bus, streamFormat) != 0) {
1187                                 return false;
1188                         }
1189                         used_out += cnt;
1190                         remain -= cnt;
1191                         ++configured_output_busses;
1192                 }
1193         }
1194
1195         free (buffers);
1196         buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) +
1197                                               used_out * sizeof(::AudioBuffer));
1198
1199         input_channels = used_in;
1200         output_channels = used_out;
1201         /* reset plugin info to show currently configured state */
1202
1203         _info->n_inputs = in;
1204         _info->n_outputs = out;
1205
1206         if (was_initialized) {
1207                 activate ();
1208         }
1209
1210         return true;
1211 }
1212
1213 ChanCount
1214 AUPlugin::input_streams() const
1215 {
1216         ChanCount c;
1217         if (input_channels < 0) {
1218                 // force PluginIoReConfigure -- see also commit msg e38eb06
1219                 c.set (DataType::AUDIO, 0);
1220                 c.set (DataType::MIDI, 0);
1221         } else {
1222                 c.set (DataType::AUDIO, input_channels);
1223                 c.set (DataType::MIDI, _has_midi_input ? 1 : 0);
1224         }
1225         return c;
1226 }
1227
1228
1229 ChanCount
1230 AUPlugin::output_streams() const
1231 {
1232         ChanCount c;
1233         if (output_channels < 0) {
1234                 // force PluginIoReConfigure - see also commit msg e38eb06
1235                 c.set (DataType::AUDIO, 0);
1236                 c.set (DataType::MIDI, 0);
1237         } else {
1238                 c.set (DataType::AUDIO, output_channels);
1239                 c.set (DataType::MIDI, _has_midi_output ? 1 : 0);
1240         }
1241         return c;
1242 }
1243
1244 bool
1245 AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out, ChanCount* imprecise)
1246 {
1247         _output_configs.clear ();
1248         const int32_t audio_in = in.n_audio();
1249         AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
1250
1251         /* lets check MIDI first */
1252
1253         if (in.n_midi() > 0 && !_has_midi_input && !imprecise) {
1254                 return false;
1255         }
1256
1257         vector<pair<int,int> >& io_configs = pinfo->cache.io_configs;
1258
1259         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 has %2 IO configurations, looking for %3 in, %4 out\n",
1260                                                         name(), io_configs.size(), in, out));
1261
1262 #if 0
1263         printf ("AU I/O Configs %s %d\n", name().c_str(), io_configs.size());
1264         for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
1265                 printf ("- I/O  %d / %d\n", i->first, i->second);
1266         }
1267 #endif
1268
1269         // preferred setting (provided by plugin_insert)
1270         const int preferred_out = out.n_audio ();
1271         bool found = false;
1272         bool exact_match = false;
1273
1274         /* kAudioUnitProperty_SupportedNumChannels
1275          * https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html#//apple_ref/doc/uid/TP40003278-CH12-SW20
1276          *
1277          * - both fields are -1
1278          *   e.g. inChannels = -1 outChannels = -1
1279          *    This is the default case. Any number of input and output channels, as long as the numbers match
1280          *
1281          * - one field is -1, the other field is positive
1282          *   e.g. inChannels = -1 outChannels = 2
1283          *    Any number of input channels, exactly two output channels
1284          *
1285          * - one field is -1, the other field is -2
1286          *   e.g. inChannels = -1 outChannels = -2
1287          *    Any number of input channels, any number of output channels
1288          *
1289          * - both fields have non-negative values
1290          *   e.g. inChannels = 2 outChannels = 6
1291          *    Exactly two input channels, exactly six output channels
1292          *   e.g. inChannels = 0 outChannels = 2
1293          *    No input channels, exactly two output channels (such as for an instrument unit with stereo output)
1294          *
1295          * - both fields have negative values, neither of which is â€“1 or â€“2
1296          *   e.g. inChannels = -4 outChannels = -8
1297          *    Up to four input channels and up to eight output channels
1298          */
1299
1300         for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
1301
1302                 int32_t possible_in = i->first;
1303                 int32_t possible_out = i->second;
1304
1305                 if ((possible_in == audio_in) && (possible_out == preferred_out)) {
1306                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: %1 in %2 out to match in %3 out %4\n",
1307                                                 possible_in, possible_out,
1308                                                 in, out));
1309
1310                         // exact match
1311                         _output_configs.insert (preferred_out);
1312                         exact_match = true;
1313                         found = true;
1314                         break;
1315                 }
1316         }
1317
1318         /* now allow potentially "imprecise" matches */
1319         int32_t audio_out = -1;
1320         float penalty = 9999;
1321         int used_possible_in = 0;
1322 #if defined (__clang__)
1323 #       pragma clang diagnostic push
1324 #       pragma clang diagnostic ignored "-Wtautological-compare"
1325 #endif
1326
1327 #define FOUNDCFG(nch) {                            \
1328   float p = fabsf ((float)(nch) - preferred_out);  \
1329   _output_configs.insert (nch);                    \
1330   if ((nch) > preferred_out) { p *= 1.1; }         \
1331   if (p < penalty) {                               \
1332     used_possible_in = possible_in;                \
1333     audio_out = (nch);                             \
1334     penalty = p;                                   \
1335     found = true;                                  \
1336     variable_inputs = possible_in < 0;             \
1337     variable_outputs = possible_out < 0;           \
1338   }                                                \
1339 }
1340
1341 #define ANYTHINGGOES                               \
1342   _output_configs.insert (0);
1343
1344 #define UPTO(nch) {                                \
1345   for (int n = 1; n < nch; ++n) {                  \
1346     _output_configs.insert (n);                    \
1347   }                                                \
1348 }
1349
1350         for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
1351
1352                 int32_t possible_in = i->first;
1353                 int32_t possible_out = i->second;
1354
1355                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tpossible in %1 possible out %2\n", possible_in, possible_out));
1356
1357                 if (possible_out == 0) {
1358                         warning << string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg;
1359                         /* XXX surely this is just a send? (e.g. AUNetSend) */
1360                         continue;
1361                 }
1362
1363                 if (possible_in == 0) {
1364                         /* no inputs, generators & instruments */
1365                         if (possible_out == -1) {
1366                                 /* any configuration possible, provide stereo output */
1367                                 FOUNDCFG (preferred_out);
1368                                 ANYTHINGGOES;
1369                         } else if (possible_out == -2) {
1370                                 /* invalid, should be (0, -1) */
1371                                 FOUNDCFG (preferred_out);
1372                                 ANYTHINGGOES;
1373                         } else if (possible_out < -2) {
1374                                 /* variable number of outputs up to -N, */
1375                                 FOUNDCFG (min (-possible_out, preferred_out));
1376                                 UPTO (-possible_out);
1377                         } else {
1378                                 /* exact number of outputs */
1379                                 FOUNDCFG (possible_out);
1380                         }
1381                 }
1382
1383                 if (possible_in == -1) {
1384                         /* wildcard for input */
1385                         if (possible_out == -1) {
1386                                 /* out must match in */
1387                                 FOUNDCFG (audio_in);
1388                         } else if (possible_out == -2) {
1389                                 /* any configuration possible, pick matching */
1390                                 FOUNDCFG (preferred_out);
1391                                 ANYTHINGGOES;
1392                         } else if (possible_out < -2) {
1393                                 /* explicitly variable number of outputs, pick maximum */
1394                                 FOUNDCFG (max (-possible_out, preferred_out));
1395                                 /* and try min, too, in case the penalty is lower */
1396                                 FOUNDCFG (min (-possible_out, preferred_out));
1397                                 UPTO (-possible_out)
1398                         } else {
1399                                 /* exact number of outputs */
1400                                 FOUNDCFG (possible_out);
1401                         }
1402                 }
1403
1404                 if (possible_in == -2) {
1405                         if (possible_out == -1) {
1406                                 /* any configuration possible, pick matching */
1407                                 FOUNDCFG (preferred_out);
1408                                 ANYTHINGGOES;
1409                         } else if (possible_out == -2) {
1410                                 /* invalid. interpret as (-1, -1) */
1411                                 FOUNDCFG (preferred_out);
1412                                 ANYTHINGGOES;
1413                         } else if (possible_out < -2) {
1414                                 /* invalid,  interpret as (<-2, <-2)
1415                                  * variable number of outputs up to -N, */
1416                                 FOUNDCFG (min (-possible_out, preferred_out));
1417                                 UPTO (-possible_out)
1418                         } else {
1419                                 /* exact number of outputs */
1420                                 FOUNDCFG (possible_out);
1421                         }
1422                 }
1423
1424                 if (possible_in < -2) {
1425                         /* explicit variable number of inputs */
1426                         if (audio_in > -possible_in && imprecise != NULL) {
1427                                 // hide inputs ports
1428                                 imprecise->set (DataType::AUDIO, -possible_in);
1429                         }
1430
1431                         if (audio_in > -possible_in && imprecise == NULL) {
1432                                 /* request is too large */
1433                         } else if (possible_out == -1) {
1434                                 /* any output configuration possible */
1435                                 FOUNDCFG (preferred_out);
1436                                 ANYTHINGGOES;
1437                         } else if (possible_out == -2) {
1438                                 /* invalid. interpret as (<-2, -1) */
1439                                 FOUNDCFG (preferred_out);
1440                                 ANYTHINGGOES;
1441                         } else if (possible_out < -2) {
1442                                 /* variable number of outputs up to -N, */
1443                                 FOUNDCFG (min (-possible_out, preferred_out));
1444                                 UPTO (-possible_out)
1445                         } else {
1446                                 /* exact number of outputs */
1447                                 FOUNDCFG (possible_out);
1448                         }
1449                 }
1450
1451                 if (possible_in && (possible_in == audio_in)) {
1452                         /* exact number of inputs ... must match obviously */
1453                         if (possible_out == -1) {
1454                                 /* any output configuration possible */
1455                                 FOUNDCFG (preferred_out);
1456                                 ANYTHINGGOES;
1457                         } else if (possible_out == -2) {
1458                                 /* plugins shouldn't really use (>0,-2), interpret as (>0,-1) */
1459                                 FOUNDCFG (preferred_out);
1460                                 ANYTHINGGOES;
1461                         } else if (possible_out < -2) {
1462                                 /* > 0, < -2 is not specified
1463                                  * interpret as up to -N */
1464                                 FOUNDCFG (min (-possible_out, preferred_out));
1465                                 UPTO (-possible_out)
1466                         } else {
1467                                 /* exact number of outputs */
1468                                 FOUNDCFG (possible_out);
1469                         }
1470                 }
1471         }
1472
1473         if (!found && imprecise) {
1474                 /* try harder */
1475                 for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
1476                         int32_t possible_in = i->first;
1477                         int32_t possible_out = i->second;
1478
1479                         assert (possible_in > 0); // all other cases will have been matched above
1480                         assert (possible_out !=0 || possible_in !=0); // already handled above
1481
1482                         imprecise->set (DataType::AUDIO, possible_in);
1483                         if (possible_out == -1 || possible_out == -2) {
1484                                 FOUNDCFG (2);
1485                         } else if (possible_out < -2) {
1486                                 /* explicitly variable number of outputs, pick maximum */
1487                                 FOUNDCFG (min (-possible_out, preferred_out));
1488                         } else {
1489                                 /* exact number of outputs */
1490                                 FOUNDCFG (possible_out);
1491                         }
1492                         // ideally we'll also find the closest, best matching
1493                         // input configuration with minimal output penalty...
1494                 }
1495         }
1496
1497         if (!found) {
1498                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tFAIL: no io configs match %1\n", in));
1499                 return false;
1500         }
1501
1502         if (exact_match) {
1503                 out.set (DataType::MIDI, 0); // currently always zero
1504                 out.set (DataType::AUDIO, preferred_out);
1505         } else {
1506                 if (used_possible_in < -2 && audio_in == 0) {
1507                         // input-port count cannot be zero, use as many ports
1508                         // as outputs, but at most abs(possible_in)
1509                         audio_input_cnt = max (1, min (audio_out, -used_possible_in));
1510                 }
1511                 out.set (DataType::MIDI, 0); /// XXX
1512                 out.set (DataType::AUDIO, audio_out);
1513         }
1514         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: in %1 out %2\n", in, out));
1515
1516 #if defined (__clang__)
1517 #       pragma clang diagnostic pop
1518 #endif
1519         return true;
1520 }
1521
1522 int
1523 AUPlugin::set_stream_format (int scope, uint32_t bus, AudioStreamBasicDescription& fmt)
1524 {
1525         OSErr result;
1526
1527         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set stream format for %1, scope = %2 element %3\n",
1528                                 (scope == kAudioUnitScope_Input ? "input" : "output"),
1529                                 scope, bus));
1530         if ((result = unit->SetFormat (scope, bus, fmt)) != 0) {
1531                 error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
1532                                 (scope == kAudioUnitScope_Input ? "input" : "output"), bus, result) << endmsg;
1533                 return -1;
1534         }
1535         return 0;
1536 }
1537
1538 OSStatus
1539 AUPlugin::render_callback(AudioUnitRenderActionFlags*,
1540                           const AudioTimeStamp*,
1541                           UInt32 bus,
1542                           UInt32 inNumberFrames,
1543                           AudioBufferList* ioData)
1544 {
1545         /* not much to do with audio - the data is already in the buffers given to us in connect_and_run() */
1546         cerr << string_compose ("%1: render callback, frames %2 bus %3 bufs %4\n",
1547                         name(), inNumberFrames, bus, ioData->mNumberBuffers);
1548
1549         // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: render callback, frames %2 bus %3 bufs %4\n",
1550         // name(), inNumberFrames, bus, ioData->mNumberBuffers));
1551
1552         if (input_maxbuf == 0) {
1553                 DEBUG_TRACE (DEBUG::AudioUnits, "AUPlugin: render callback called illegally!");
1554                 error << _("AUPlugin: render callback called illegally!") << endmsg;
1555                 return kAudioUnitErr_CannotDoInCurrentContext;
1556         }
1557
1558         assert (bus < input_elements);
1559         uint32_t busoff = 0;
1560         for (uint32_t i = 0; i < bus; ++i) {
1561                 busoff += bus_inputs[i];
1562         }
1563
1564         uint32_t limit = min ((uint32_t) ioData->mNumberBuffers, input_maxbuf);
1565
1566         ChanCount bufs_count (DataType::AUDIO, 1);
1567         BufferSet& silent_bufs = _session.get_silent_buffers(bufs_count);
1568
1569         /* apply bus offsets */
1570
1571         for (uint32_t i = 0; i < limit; ++i) {
1572                 ioData->mBuffers[i].mNumberChannels = 1;
1573                 ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
1574
1575                 bool valid = false;
1576                 uint32_t idx = input_map->get (DataType::AUDIO, i + busoff, &valid);
1577                 if (valid) {
1578                         ioData->mBuffers[i].mData = input_buffers->get_audio (idx).data (cb_offset + input_offset);
1579                 } else {
1580                         ioData->mBuffers[i].mData = silent_bufs.get_audio(0).data (cb_offset + input_offset);
1581                 }
1582         }
1583 #if 0 // TODO  per bus
1584         cb_offset += inNumberFrames;
1585 #endif
1586         return noErr;
1587 }
1588
1589 int
1590 AUPlugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_map, pframes_t nframes, framecnt_t offset)
1591 {
1592         Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset);
1593
1594         AudioUnitRenderActionFlags flags = 0;
1595         AudioTimeStamp ts;
1596         OSErr err;
1597
1598         if (requires_fixed_size_buffers() && (nframes != _last_nframes)) {
1599                 unit->GlobalReset();
1600                 _last_nframes = nframes;
1601         }
1602
1603         /* test if we can run in-place; only compare audio buffers */
1604         bool inplace = true; // configured_output_busses == 1;
1605         ChanMapping::Mappings inmap (in_map.mappings ());
1606         ChanMapping::Mappings outmap (out_map.mappings ());
1607         assert (outmap[DataType::AUDIO].size () > 0);
1608         if (inmap[DataType::AUDIO].size() > 0 && inmap != outmap) {
1609                 inplace = false;
1610         }
1611
1612         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 in %2 out %3 MIDI %4 bufs %5 (available %6) Inplace: %7\n",
1613                                 name(), input_channels, output_channels, _has_midi_input,
1614                                 bufs.count(), bufs.available(), inplace));
1615
1616         /* the apparent number of buffers matches our input configuration, but we know that the bufferset
1617          * has the capacity to handle our outputs.
1618          */
1619
1620         assert (bufs.available() >= ChanCount (DataType::AUDIO, output_channels));
1621
1622         input_buffers = &bufs;
1623         input_map = &in_map;
1624         input_maxbuf = bufs.count().n_audio(); // number of input audio buffers
1625         input_offset = offset;
1626         cb_offset = 0;
1627
1628         ChanCount bufs_count (DataType::AUDIO, 1);
1629         BufferSet& scratch_bufs = _session.get_scratch_buffers(bufs_count);
1630
1631         if (_has_midi_input) {
1632                 uint32_t nmidi = bufs.count().n_midi();
1633                 for (uint32_t i = 0; i < nmidi; ++i) {
1634                         /* one MIDI port/buffer only */
1635                         MidiBuffer& m = bufs.get_midi (i);
1636                         for (MidiBuffer::iterator i = m.begin(); i != m.end(); ++i) {
1637                                 Evoral::MIDIEvent<framepos_t> ev (*i);
1638                                 if (ev.is_channel_event()) {
1639                                         const uint8_t* b = ev.buffer();
1640                                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: MIDI event %2\n", name(), ev));
1641                                         unit->MIDIEvent (b[0], b[1], b[2], ev.time());
1642                                 }
1643                                 /* XXX need to handle sysex and other message types */
1644                         }
1645                 }
1646         }
1647
1648         bool ok = true;
1649         uint32_t busoff = 0;
1650         uint32_t remain = output_channels;
1651         for (uint32_t bus = 0; remain > 0 && bus < configured_output_busses; ++bus) {
1652                 uint32_t cnt = std::min (remain, bus_outputs[bus]);
1653                 assert (cnt > 0);
1654
1655                 buffers->mNumberBuffers = cnt;
1656
1657                 for (uint32_t i = 0; i < cnt; ++i) {
1658                         buffers->mBuffers[i].mNumberChannels = 1;
1659                         buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
1660                         /* setting this to 0 indicates to the AU that it can provide buffers here
1661                          * if necessary. if it can process in-place, it will use the buffers provided
1662                          * as input by ::render_callback() above.
1663                          *
1664                          * a non-null values tells the plugin to render into the buffer pointed
1665                          * at by the value.
1666                          */
1667                         if (inplace) {
1668                                 buffers->mBuffers[i].mData = 0;
1669                         } else {
1670                                 bool valid = false;
1671                                 uint32_t idx = out_map.get (DataType::AUDIO, i + busoff, &valid);
1672                                 if (valid) {
1673                                         buffers->mBuffers[i].mData = bufs.get_audio (idx).data (offset);
1674                                 } else {
1675                                         buffers->mBuffers[i].mData = scratch_bufs.get_audio(0).data(offset);
1676                                 }
1677                         }
1678                 }
1679
1680                 /* does this really mean anything ?  */
1681                 ts.mSampleTime = frames_processed;
1682                 ts.mFlags = kAudioTimeStampSampleTimeValid;
1683
1684                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 render flags=%2 time=%3 nframes=%4 bus=%5 buffers=%6\n",
1685                                         name(), flags, frames_processed, nframes, bus, buffers->mNumberBuffers));
1686
1687                 if ((err = unit->Render (&flags, &ts, bus, nframes, buffers)) == noErr) {
1688
1689                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 rendered %2 buffers of %3\n",
1690                                                 name(), buffers->mNumberBuffers, output_channels));
1691
1692                         uint32_t limit = std::min ((uint32_t) buffers->mNumberBuffers, cnt);
1693                         for (uint32_t i = 0; i < limit; ++i) {
1694                                 bool valid = false;
1695                                 uint32_t idx = out_map.get (DataType::AUDIO, i + busoff, &valid);
1696                                 if (!valid) continue;
1697                                 Sample* expected_buffer_address = bufs.get_audio (idx).data (offset);
1698                                 if (expected_buffer_address != buffers->mBuffers[i].mData) {
1699                                         /* plugin provided its own buffer for output so copy it back to where we want it */
1700                                         memcpy (expected_buffer_address, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
1701                                 }
1702                         }
1703                 } else {
1704                         error << string_compose (_("AU: render error for %1, bus %2 status = %2"), name(), bus, err) << endmsg;
1705                         ok = false;
1706                         break;
1707                 }
1708
1709                 remain -= cnt;
1710                 busoff += bus_outputs[bus];
1711         }
1712 #if 0
1713         /* now silence any buffers that were passed in but the that the plugin
1714          * did not fill/touch/use.
1715          */
1716         for (;i < output_channels; ++i) {
1717                 bool valid = false;
1718                 uint32_t idx = out_map.get (DataType::AUDIO, i, &valid);
1719                 if (!valid) continue;
1720                 memset (bufs.get_audio (idx).data (offset), 0, nframes * sizeof (Sample));
1721         }
1722 #endif
1723
1724         input_maxbuf = 0;
1725
1726         if (ok) {
1727                 frames_processed += nframes;
1728                 return 0;
1729         }
1730         return -1;
1731 }
1732
1733 OSStatus
1734 AUPlugin::get_beat_and_tempo_callback (Float64* outCurrentBeat,
1735                                        Float64* outCurrentTempo)
1736 {
1737         TempoMap& tmap (_session.tempo_map());
1738
1739         DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour beat&tempo callback\n");
1740
1741         /* more than 1 meter or more than 1 tempo means that a simplistic computation
1742            (and interpretation) of a beat position will be incorrect. So refuse to
1743            offer the value.
1744         */
1745
1746         if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
1747                 return kAudioUnitErr_CannotDoInCurrentContext;
1748         }
1749
1750         Timecode::BBT_Time bbt;
1751         TempoMetric metric = tmap.metric_at (_session.transport_frame() + input_offset);
1752         tmap.bbt_time (_session.transport_frame() + input_offset, bbt);
1753
1754         if (outCurrentBeat) {
1755                 float beat;
1756                 beat = metric.meter().divisions_per_bar() * bbt.bars;
1757                 beat += bbt.beats;
1758                 beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
1759                 *outCurrentBeat = beat;
1760         }
1761
1762         if (outCurrentTempo) {
1763                 *outCurrentTempo = floor (metric.tempo().beats_per_minute());
1764         }
1765
1766         return noErr;
1767
1768 }
1769
1770 OSStatus
1771 AUPlugin::get_musical_time_location_callback (UInt32*   outDeltaSampleOffsetToNextBeat,
1772                                               Float32*  outTimeSig_Numerator,
1773                                               UInt32*   outTimeSig_Denominator,
1774                                               Float64*  outCurrentMeasureDownBeat)
1775 {
1776         TempoMap& tmap (_session.tempo_map());
1777
1778         DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour music time location callback\n");
1779
1780         /* more than 1 meter or more than 1 tempo means that a simplistic computation
1781            (and interpretation) of a beat position will be incorrect. So refuse to
1782            offer the value.
1783         */
1784
1785         if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
1786                 return kAudioUnitErr_CannotDoInCurrentContext;
1787         }
1788
1789         Timecode::BBT_Time bbt;
1790         TempoMetric metric = tmap.metric_at (_session.transport_frame() + input_offset);
1791         tmap.bbt_time (_session.transport_frame() + input_offset, bbt);
1792
1793         if (outDeltaSampleOffsetToNextBeat) {
1794                 if (bbt.ticks == 0) {
1795                         /* on the beat */
1796                         *outDeltaSampleOffsetToNextBeat = 0;
1797                 } else {
1798                         *outDeltaSampleOffsetToNextBeat = (UInt32)
1799                                 floor (((Timecode::BBT_Time::ticks_per_beat - bbt.ticks)/Timecode::BBT_Time::ticks_per_beat) * // fraction of a beat to next beat
1800                                        metric.tempo().frames_per_beat (_session.frame_rate())); // frames per beat
1801                 }
1802         }
1803
1804         if (outTimeSig_Numerator) {
1805                 *outTimeSig_Numerator = (UInt32) lrintf (metric.meter().divisions_per_bar());
1806         }
1807         if (outTimeSig_Denominator) {
1808                 *outTimeSig_Denominator = (UInt32) lrintf (metric.meter().note_divisor());
1809         }
1810
1811         if (outCurrentMeasureDownBeat) {
1812
1813                 /* beat for the start of the bar.
1814                    1|1|0 -> 1
1815                    2|1|0 -> 1 + divisions_per_bar
1816                    3|1|0 -> 1 + (2 * divisions_per_bar)
1817                    etc.
1818                 */
1819
1820                 *outCurrentMeasureDownBeat = 1 + metric.meter().divisions_per_bar() * (bbt.bars - 1);
1821         }
1822
1823         return noErr;
1824 }
1825
1826 OSStatus
1827 AUPlugin::get_transport_state_callback (Boolean*  outIsPlaying,
1828                                         Boolean*  outTransportStateChanged,
1829                                         Float64*  outCurrentSampleInTimeLine,
1830                                         Boolean*  outIsCycling,
1831                                         Float64*  outCycleStartBeat,
1832                                         Float64*  outCycleEndBeat)
1833 {
1834         bool rolling;
1835         float speed;
1836
1837         DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour transport state callback\n");
1838
1839         rolling = _session.transport_rolling();
1840         speed = _session.transport_speed ();
1841
1842         if (outIsPlaying) {
1843                 *outIsPlaying = _session.transport_rolling();
1844         }
1845
1846         if (outTransportStateChanged) {
1847                 if (rolling != last_transport_rolling) {
1848                         *outTransportStateChanged = true;
1849                 } else if (speed != last_transport_speed) {
1850                         *outTransportStateChanged = true;
1851                 } else {
1852                         *outTransportStateChanged = false;
1853                 }
1854         }
1855
1856         if (outCurrentSampleInTimeLine) {
1857                 /* this assumes that the AU can only call this host callback from render context,
1858                    where input_offset is valid.
1859                 */
1860                 *outCurrentSampleInTimeLine = _session.transport_frame() + input_offset;
1861         }
1862
1863         if (outIsCycling) {
1864                 Location* loc = _session.locations()->auto_loop_location();
1865
1866                 *outIsCycling = (loc && _session.transport_rolling() && _session.get_play_loop());
1867
1868                 if (*outIsCycling) {
1869
1870                         if (outCycleStartBeat || outCycleEndBeat) {
1871
1872                                 TempoMap& tmap (_session.tempo_map());
1873
1874                                 /* more than 1 meter means that a simplistic computation (and interpretation) of
1875                                    a beat position will be incorrect. so refuse to offer the value.
1876                                 */
1877
1878                                 if (tmap.n_meters() > 1) {
1879                                         return kAudioUnitErr_CannotDoInCurrentContext;
1880                                 }
1881
1882                                 Timecode::BBT_Time bbt;
1883
1884                                 if (outCycleStartBeat) {
1885                                         TempoMetric metric = tmap.metric_at (loc->start() + input_offset);
1886                                         _session.tempo_map().bbt_time (loc->start(), bbt);
1887
1888                                         float beat;
1889                                         beat = metric.meter().divisions_per_bar() * bbt.bars;
1890                                         beat += bbt.beats;
1891                                         beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
1892
1893                                         *outCycleStartBeat = beat;
1894                                 }
1895
1896                                 if (outCycleEndBeat) {
1897                                         TempoMetric metric = tmap.metric_at (loc->end() + input_offset);
1898                                         _session.tempo_map().bbt_time (loc->end(), bbt);
1899
1900                                         float beat;
1901                                         beat = metric.meter().divisions_per_bar() * bbt.bars;
1902                                         beat += bbt.beats;
1903                                         beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
1904
1905                                         *outCycleEndBeat = beat;
1906                                 }
1907                         }
1908                 }
1909         }
1910
1911         last_transport_rolling = rolling;
1912         last_transport_speed = speed;
1913
1914         return noErr;
1915 }
1916
1917 set<Evoral::Parameter>
1918 AUPlugin::automatable() const
1919 {
1920         set<Evoral::Parameter> automates;
1921
1922         for (uint32_t i = 0; i < descriptors.size(); ++i) {
1923                 if (descriptors[i].automatable) {
1924                         automates.insert (automates.end(), Evoral::Parameter (PluginAutomation, 0, i));
1925                 }
1926         }
1927
1928         return automates;
1929 }
1930
1931 Plugin::IOPortDescription
1932 AUPlugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const
1933 {
1934         std::stringstream ss;
1935         switch (dt) {
1936                 case DataType::AUDIO:
1937                         break;
1938                 case DataType::MIDI:
1939                         ss << _("Midi");
1940                         break;
1941                 default:
1942                         ss << _("?");
1943                         break;
1944         }
1945
1946         if (dt == DataType::AUDIO) {
1947                 if (input) {
1948                         uint32_t pid = id;
1949                         for (uint32_t bus = 0; bus < input_elements; ++bus) {
1950                                 if (pid < bus_inputs[bus]) {
1951                                         id = pid;
1952                                         ss << _bus_name_in[bus];
1953                                         break;
1954                                 }
1955                                 pid -= bus_inputs[bus];
1956                         }
1957                 }
1958                 else {
1959                         uint32_t pid = id;
1960                         for (uint32_t bus = 0; bus < output_elements; ++bus) {
1961                                 if (pid < bus_outputs[bus]) {
1962                                         id = pid;
1963                                         ss << _bus_name_out[bus];
1964                                         break;
1965                                 }
1966                                 pid -= bus_outputs[bus];
1967                         }
1968                 }
1969         }
1970
1971         if (input) {
1972                 ss << " " << _("In") << " ";
1973         } else {
1974                 ss << " " << _("Out") << " ";
1975         }
1976
1977         ss << (id + 1);
1978
1979         Plugin::IOPortDescription iod (ss.str());
1980         return iod;
1981 }
1982
1983 string
1984 AUPlugin::describe_parameter (Evoral::Parameter param)
1985 {
1986         if (param.type() == PluginAutomation && param.id() < parameter_count()) {
1987                 return descriptors[param.id()].label;
1988         } else {
1989                 return "??";
1990         }
1991 }
1992
1993 void
1994 AUPlugin::print_parameter (uint32_t /*param*/, char* /*buf*/, uint32_t /*len*/) const
1995 {
1996         // NameValue stuff here
1997 }
1998
1999 bool
2000 AUPlugin::parameter_is_audio (uint32_t) const
2001 {
2002         return false;
2003 }
2004
2005 bool
2006 AUPlugin::parameter_is_control (uint32_t param) const
2007 {
2008         assert(param < descriptors.size());
2009         if (descriptors[param].automatable) {
2010                 /* corrently ardour expects all controls to be automatable
2011                  * IOW ardour GUI elements mandate an Evoral::Parameter
2012                  * for all input+control ports.
2013                  */
2014                 return true;
2015         }
2016         return false;
2017 }
2018
2019 bool
2020 AUPlugin::parameter_is_input (uint32_t param) const
2021 {
2022         /* AU params that are both readable and writeable,
2023          * are listed in kAudioUnitScope_Global
2024          */
2025         return (descriptors[param].scope == kAudioUnitScope_Input || descriptors[param].scope == kAudioUnitScope_Global);
2026 }
2027
2028 bool
2029 AUPlugin::parameter_is_output (uint32_t param) const
2030 {
2031         assert(param < descriptors.size());
2032         // TODO check if ardour properly handles ports
2033         // that report is_input + is_output == true
2034         // -> add || descriptors[param].scope == kAudioUnitScope_Global
2035         return (descriptors[param].scope == kAudioUnitScope_Output);
2036 }
2037
2038 void
2039 AUPlugin::add_state (XMLNode* root) const
2040 {
2041         LocaleGuard lg (X_("C"));
2042         CFDataRef xmlData;
2043         CFPropertyListRef propertyList;
2044
2045         DEBUG_TRACE (DEBUG::AudioUnits, "get preset state\n");
2046         if (unit->GetAUPreset (propertyList) != noErr) {
2047                 return;
2048         }
2049
2050         // Convert the property list into XML data.
2051
2052         xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
2053
2054         if (!xmlData) {
2055                 error << _("Could not create XML version of property list") << endmsg;
2056                 return;
2057         }
2058
2059         /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
2060            our state node. GACK!
2061         */
2062
2063         XMLTree t;
2064
2065         if (t.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData), CFDataGetLength (xmlData)))) {
2066                 if (t.root()) {
2067                         root->add_child_copy (*t.root());
2068                 }
2069         }
2070
2071         CFRelease (xmlData);
2072         CFRelease (propertyList);
2073 }
2074
2075 int
2076 AUPlugin::set_state(const XMLNode& node, int version)
2077 {
2078         int ret = -1;
2079         CFPropertyListRef propertyList;
2080         LocaleGuard lg (X_("C"));
2081
2082         if (node.name() != state_node_name()) {
2083                 error << _("Bad node sent to AUPlugin::set_state") << endmsg;
2084                 return -1;
2085         }
2086
2087 #ifndef NO_PLUGIN_STATE
2088         if (node.children().empty()) {
2089                 return -1;
2090         }
2091
2092         XMLNode* top = node.children().front();
2093         XMLNode* copy = new XMLNode (*top);
2094
2095         XMLTree t;
2096         t.set_root (copy);
2097
2098         const string& xml = t.write_buffer ();
2099         CFDataRef xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) xml.data(), xml.length(), kCFAllocatorNull);
2100         CFStringRef errorString;
2101
2102         propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
2103                                                         xmlData,
2104                                                         kCFPropertyListImmutable,
2105                                                         &errorString);
2106
2107         CFRelease (xmlData);
2108
2109         if (propertyList) {
2110                 DEBUG_TRACE (DEBUG::AudioUnits, "set preset\n");
2111                 if (unit->SetAUPreset (propertyList) == noErr) {
2112                         ret = 0;
2113
2114                         /* tell the world */
2115
2116                         AudioUnitParameter changedUnit;
2117                         changedUnit.mAudioUnit = unit->AU();
2118                         changedUnit.mParameterID = kAUParameterListener_AnyParameter;
2119                         AUParameterListenerNotify (NULL, NULL, &changedUnit);
2120                 }
2121                 CFRelease (propertyList);
2122         }
2123 #endif
2124
2125         Plugin::set_state (node, version);
2126         return ret;
2127 }
2128
2129 bool
2130 AUPlugin::load_preset (PresetRecord r)
2131 {
2132         Plugin::load_preset (r);
2133
2134         bool ret = false;
2135         CFPropertyListRef propertyList;
2136         Glib::ustring path;
2137         UserPresetMap::iterator ux;
2138         FactoryPresetMap::iterator fx;
2139
2140         /* look first in "user" presets */
2141
2142         if ((ux = user_preset_map.find (r.label)) != user_preset_map.end()) {
2143
2144                 if ((propertyList = load_property_list (ux->second)) != 0) {
2145                         DEBUG_TRACE (DEBUG::AudioUnits, "set preset from user presets\n");
2146                         if (unit->SetAUPreset (propertyList) == noErr) {
2147                                 ret = true;
2148
2149                                 /* tell the world */
2150
2151                                 AudioUnitParameter changedUnit;
2152                                 changedUnit.mAudioUnit = unit->AU();
2153                                 changedUnit.mParameterID = kAUParameterListener_AnyParameter;
2154                                 AUParameterListenerNotify (NULL, NULL, &changedUnit);
2155                         }
2156                         CFRelease(propertyList);
2157                 }
2158
2159         } else if ((fx = factory_preset_map.find (r.label)) != factory_preset_map.end()) {
2160
2161                 AUPreset preset;
2162
2163                 preset.presetNumber = fx->second;
2164                 preset.presetName = CFStringCreateWithCString (kCFAllocatorDefault, fx->first.c_str(), kCFStringEncodingUTF8);
2165
2166                 DEBUG_TRACE (DEBUG::AudioUnits, "set preset from factory presets\n");
2167
2168                 if (unit->SetPresentPreset (preset) == 0) {
2169                         ret = true;
2170
2171                         /* tell the world */
2172
2173                         AudioUnitParameter changedUnit;
2174                         changedUnit.mAudioUnit = unit->AU();
2175                         changedUnit.mParameterID = kAUParameterListener_AnyParameter;
2176                         AUParameterListenerNotify (NULL, NULL, &changedUnit);
2177                 }
2178         }
2179
2180         return ret;
2181 }
2182
2183 void
2184 AUPlugin::do_remove_preset (std::string)
2185 {
2186 }
2187
2188 string
2189 AUPlugin::do_save_preset (string preset_name)
2190 {
2191         CFPropertyListRef propertyList;
2192         vector<Glib::ustring> v;
2193         Glib::ustring user_preset_path;
2194
2195         std::string m = maker();
2196         std::string n = name();
2197
2198         strip_whitespace_edges (m);
2199         strip_whitespace_edges (n);
2200
2201         v.push_back (Glib::get_home_dir());
2202         v.push_back ("Library");
2203         v.push_back ("Audio");
2204         v.push_back ("Presets");
2205         v.push_back (m);
2206         v.push_back (n);
2207
2208         user_preset_path = Glib::build_filename (v);
2209
2210         if (g_mkdir_with_parents (user_preset_path.c_str(), 0775) < 0) {
2211                 error << string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path) << endmsg;
2212                 return string();
2213         }
2214
2215         DEBUG_TRACE (DEBUG::AudioUnits, "get current preset\n");
2216         if (unit->GetAUPreset (propertyList) != noErr) {
2217                 return string();
2218         }
2219
2220         // add the actual preset name */
2221
2222         v.push_back (preset_name + preset_suffix);
2223
2224         // rebuild
2225
2226         user_preset_path = Glib::build_filename (v);
2227
2228         set_preset_name_in_plist (propertyList, preset_name);
2229
2230         if (save_property_list (propertyList, user_preset_path)) {
2231                 error << string_compose (_("Saving plugin state to %1 failed"), user_preset_path) << endmsg;
2232                 return string();
2233         }
2234
2235         CFRelease(propertyList);
2236
2237         user_preset_map[preset_name] = user_preset_path;;
2238
2239         DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Saving Preset to %1\n", user_preset_path));
2240
2241         return string ("file:///") + user_preset_path;
2242 }
2243
2244 //-----------------------------------------------------------------------------
2245 // this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
2246 static SInt32
2247 GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary, CFStringRef inDictionaryKey, Boolean * outSuccess)
2248 {
2249         CFNumberRef cfNumber;
2250         SInt32 numberValue = 0;
2251         Boolean dummySuccess;
2252
2253         if (outSuccess == NULL)
2254                 outSuccess = &dummySuccess;
2255         if ( (inAUStateDictionary == NULL) || (inDictionaryKey == NULL) )
2256         {
2257                 *outSuccess = FALSE;
2258                 return 0;
2259         }
2260
2261         cfNumber = (CFNumberRef) CFDictionaryGetValue(inAUStateDictionary, inDictionaryKey);
2262         if (cfNumber == NULL)
2263         {
2264                 *outSuccess = FALSE;
2265                 return 0;
2266         }
2267         *outSuccess = CFNumberGetValue(cfNumber, kCFNumberSInt32Type, &numberValue);
2268         if (*outSuccess)
2269                 return numberValue;
2270         else
2271                 return 0;
2272 }
2273
2274 static OSStatus
2275 GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData, ArdourDescription * outComponentDescription)
2276 {
2277         CFDictionaryRef auStateDictionary;
2278         ArdourDescription tempDesc = {0,0,0,0,0};
2279         SInt32 versionValue;
2280         Boolean gotValue;
2281
2282         if ( (inAUStateData == NULL) || (outComponentDescription == NULL) )
2283                 return paramErr;
2284
2285         // the property list for AU state data must be of the dictionary type
2286         if (CFGetTypeID(inAUStateData) != CFDictionaryGetTypeID()) {
2287                 return kAudioUnitErr_InvalidPropertyValue;
2288         }
2289
2290         auStateDictionary = (CFDictionaryRef)inAUStateData;
2291
2292         // first check to make sure that the version of the AU state data is one that we know understand
2293         // XXX should I really do this?  later versions would probably still hold these ID keys, right?
2294         versionValue = GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetVersionKey), &gotValue);
2295
2296         if (!gotValue) {
2297                 return kAudioUnitErr_InvalidPropertyValue;
2298         }
2299 #define kCurrentSavedStateVersion 0
2300         if (versionValue != kCurrentSavedStateVersion) {
2301                 return kAudioUnitErr_InvalidPropertyValue;
2302         }
2303
2304         // grab the ComponentDescription values from the AU state data
2305         tempDesc.componentType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetTypeKey), NULL);
2306         tempDesc.componentSubType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetSubtypeKey), NULL);
2307         tempDesc.componentManufacturer = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetManufacturerKey), NULL);
2308         // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
2309         if ( (tempDesc.componentType == 0) || (tempDesc.componentSubType == 0) || (tempDesc.componentManufacturer == 0) )
2310                 return kAudioUnitErr_InvalidPropertyValue;
2311
2312         *outComponentDescription = tempDesc;
2313         return noErr;
2314 }
2315
2316
2317 static bool au_preset_filter (const string& str, void* arg)
2318 {
2319         /* Not a dotfile, has a prefix before a period, suffix is aupreset */
2320
2321         bool ret;
2322
2323         ret = (str[0] != '.' && str.length() > 9 && str.find (preset_suffix) == (str.length() - preset_suffix.length()));
2324
2325         if (ret && arg) {
2326
2327                 /* check the preset file path name against this plugin
2328                    ID. The idea is that all preset files for this plugin
2329                    include "<manufacturer>/<plugin-name>" in their path.
2330                 */
2331
2332                 AUPluginInfo* p = (AUPluginInfo *) arg;
2333                 string match = p->creator;
2334                 match += '/';
2335                 match += p->name;
2336
2337                 ret = str.find (match) != string::npos;
2338
2339                 if (ret == false) {
2340                         string m = p->creator;
2341                         string n = p->name;
2342                         strip_whitespace_edges (m);
2343                         strip_whitespace_edges (n);
2344                         match = m;
2345                         match += '/';
2346                         match += n;
2347
2348                         ret = str.find (match) != string::npos;
2349                 }
2350         }
2351
2352         return ret;
2353 }
2354
2355 static bool
2356 check_and_get_preset_name (ArdourComponent component, const string& pathstr, string& preset_name)
2357 {
2358         OSStatus status;
2359         CFPropertyListRef plist;
2360         ArdourDescription presetDesc;
2361         bool ret = false;
2362
2363         plist = load_property_list (pathstr);
2364
2365         if (!plist) {
2366                 return ret;
2367         }
2368
2369         // get the ComponentDescription from the AU preset file
2370
2371         status = GetAUComponentDescriptionFromStateData(plist, &presetDesc);
2372
2373         if (status == noErr) {
2374                 if (ComponentAndDescriptionMatch_Loosely(component, &presetDesc)) {
2375
2376                         /* try to get the preset name from the property list */
2377
2378                         if (CFGetTypeID(plist) == CFDictionaryGetTypeID()) {
2379
2380                                 const void* psk = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
2381
2382                                 if (psk) {
2383
2384                                         const char* p = CFStringGetCStringPtr ((CFStringRef) psk, kCFStringEncodingUTF8);
2385
2386                                         if (!p) {
2387                                                 char buf[PATH_MAX+1];
2388
2389                                                 if (CFStringGetCString ((CFStringRef)psk, buf, sizeof (buf), kCFStringEncodingUTF8)) {
2390                                                         preset_name = buf;
2391                                                 }
2392                                         }
2393                                 }
2394                         }
2395                 }
2396         }
2397
2398         CFRelease (plist);
2399
2400         return true;
2401 }
2402
2403
2404 static void
2405 #ifdef COREAUDIO105
2406 get_names (CAComponentDescription& comp_desc, std::string& name, std::string& maker)
2407 #else
2408 get_names (ArdourComponent& comp, std::string& name, std::string& maker)
2409 #endif
2410 {
2411         CFStringRef itemName = NULL;
2412         // Marc Poirier-style item name
2413 #ifdef COREAUDIO105
2414         CAComponent auComponent (comp_desc);
2415         if (auComponent.IsValid()) {
2416                 CAComponentDescription dummydesc;
2417                 Handle nameHandle = NewHandle(sizeof(void*));
2418                 if (nameHandle != NULL) {
2419                         OSErr err = GetComponentInfo(auComponent.Comp(), &dummydesc, nameHandle, NULL, NULL);
2420                         if (err == noErr) {
2421                                 ConstStr255Param nameString = (ConstStr255Param) (*nameHandle);
2422                                 if (nameString != NULL) {
2423                                         itemName = CFStringCreateWithPascalString(kCFAllocatorDefault, nameString, CFStringGetSystemEncoding());
2424                                 }
2425                         }
2426                         DisposeHandle(nameHandle);
2427                 }
2428         }
2429 #else
2430         assert (comp);
2431         AudioComponentCopyName (comp, &itemName);
2432 #endif
2433
2434         // if Marc-style fails, do the original way
2435         if (itemName == NULL) {
2436 #ifndef COREAUDIO105
2437                 CAComponentDescription comp_desc;
2438                 AudioComponentGetDescription (comp, &comp_desc);
2439 #endif
2440                 CFStringRef compTypeString = UTCreateStringForOSType(comp_desc.componentType);
2441                 CFStringRef compSubTypeString = UTCreateStringForOSType(comp_desc.componentSubType);
2442                 CFStringRef compManufacturerString = UTCreateStringForOSType(comp_desc.componentManufacturer);
2443
2444                 itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
2445                                 compTypeString, compManufacturerString, compSubTypeString);
2446
2447                 if (compTypeString != NULL)
2448                         CFRelease(compTypeString);
2449                 if (compSubTypeString != NULL)
2450                         CFRelease(compSubTypeString);
2451                 if (compManufacturerString != NULL)
2452                         CFRelease(compManufacturerString);
2453         }
2454
2455         string str = CFStringRefToStdString(itemName);
2456         string::size_type colon = str.find (':');
2457
2458         if (colon) {
2459                 name = str.substr (colon+1);
2460                 maker = str.substr (0, colon);
2461                 strip_whitespace_edges (maker);
2462                 strip_whitespace_edges (name);
2463         } else {
2464                 name = str;
2465                 maker = "unknown";
2466                 strip_whitespace_edges (name);
2467         }
2468 }
2469
2470 std::string
2471 AUPlugin::current_preset() const
2472 {
2473         string preset_name;
2474
2475         CFPropertyListRef propertyList;
2476
2477         DEBUG_TRACE (DEBUG::AudioUnits, "get current preset for current_preset()\n");
2478         if (unit->GetAUPreset (propertyList) == noErr) {
2479                 preset_name = get_preset_name_in_plist (propertyList);
2480                 CFRelease(propertyList);
2481         }
2482
2483         return preset_name;
2484 }
2485
2486 void
2487 AUPlugin::find_presets ()
2488 {
2489         vector<string> preset_files;
2490
2491         user_preset_map.clear ();
2492
2493         PluginInfoPtr nfo = get_info();
2494         find_files_matching_filter (preset_files, preset_search_path, au_preset_filter,
2495                         boost::dynamic_pointer_cast<AUPluginInfo> (nfo).get(),
2496                         true, true, true);
2497
2498         if (preset_files.empty()) {
2499                 DEBUG_TRACE (DEBUG::AudioUnits, "AU No Preset Files found for given plugin.\n");
2500         }
2501
2502         for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
2503
2504                 string path = *x;
2505                 string preset_name;
2506
2507                 /* make an initial guess at the preset name using the path */
2508
2509                 preset_name = Glib::path_get_basename (path);
2510                 preset_name = preset_name.substr (0, preset_name.find_last_of ('.'));
2511
2512                 /* check that this preset file really matches this plugin
2513                    and potentially get the "real" preset name from
2514                    within the file.
2515                 */
2516
2517                 if (check_and_get_preset_name (get_comp()->Comp(), path, preset_name)) {
2518                         user_preset_map[preset_name] = path;
2519                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset File: %1 > %2\n", preset_name, path));
2520                 } else {
2521                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU INVALID Preset: %1 > %2\n", preset_name, path));
2522                 }
2523
2524         }
2525
2526         /* now fill the vector<string> with the names we have */
2527
2528         for (UserPresetMap::iterator i = user_preset_map.begin(); i != user_preset_map.end(); ++i) {
2529                 _presets.insert (make_pair (i->second, Plugin::PresetRecord (i->second, i->first)));
2530                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding User Preset: %1 > %2\n", i->first, i->second));
2531         }
2532
2533         /* add factory presets */
2534
2535         for (FactoryPresetMap::iterator i = factory_preset_map.begin(); i != factory_preset_map.end(); ++i) {
2536                 /* XXX: dubious */
2537                 string const uri = string_compose ("%1", _presets.size ());
2538                 _presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first, false)));
2539                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding Factory Preset: %1 > %2\n", i->first, i->second));
2540         }
2541 }
2542
2543 bool
2544 AUPlugin::has_editor () const
2545 {
2546         // even if the plugin doesn't have its own editor, the AU API can be used
2547         // to create one that looks native.
2548         return true;
2549 }
2550
2551 AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
2552         : descriptor (d)
2553         , version (0)
2554 {
2555         type = ARDOUR::AudioUnit;
2556 }
2557
2558 AUPluginInfo::~AUPluginInfo ()
2559 {
2560         type = ARDOUR::AudioUnit;
2561 }
2562
2563 PluginPtr
2564 AUPluginInfo::load (Session& session)
2565 {
2566         try {
2567                 PluginPtr plugin;
2568
2569                 DEBUG_TRACE (DEBUG::AudioUnits, "load AU as a component\n");
2570                 boost::shared_ptr<CAComponent> comp (new CAComponent(*descriptor));
2571
2572                 if (!comp->IsValid()) {
2573                         error << ("AudioUnit: not a valid Component") << endmsg;
2574                 } else {
2575                         plugin.reset (new AUPlugin (session.engine(), session, comp));
2576                 }
2577
2578                 AUPluginInfo *aup = new AUPluginInfo (*this);
2579                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("plugin info for %1 = %2\n", this, aup));
2580                 plugin->set_info (PluginInfoPtr (aup));
2581                 boost::dynamic_pointer_cast<AUPlugin> (plugin)->set_fixed_size_buffers (aup->creator == "Universal Audio");
2582                 return plugin;
2583         }
2584
2585         catch (failed_constructor &err) {
2586                 DEBUG_TRACE (DEBUG::AudioUnits, "failed to load component/plugin\n");
2587                 return PluginPtr ();
2588         }
2589 }
2590
2591 std::vector<Plugin::PresetRecord>
2592 AUPluginInfo::get_presets (bool user_only) const
2593 {
2594         std::vector<Plugin::PresetRecord> p;
2595         boost::shared_ptr<CAComponent> comp;
2596 #ifndef NO_PLUGIN_STATE
2597         try {
2598                 comp = boost::shared_ptr<CAComponent>(new CAComponent(*descriptor));
2599                 if (!comp->IsValid()) {
2600                         throw failed_constructor();
2601                 }
2602         } catch (failed_constructor& err) {
2603                 return p;
2604         }
2605
2606         // user presets
2607
2608         if (!preset_search_path_initialized) {
2609                 Glib::ustring p = Glib::get_home_dir();
2610                 p += "/Library/Audio/Presets:";
2611                 p += preset_search_path;
2612                 preset_search_path = p;
2613                 preset_search_path_initialized = true;
2614                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset Path: %1\n", preset_search_path));
2615         }
2616
2617         vector<string> preset_files;
2618         find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, const_cast<AUPluginInfo*>(this), true, true, true);
2619
2620         for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
2621                 string path = *x;
2622                 string preset_name;
2623                 preset_name = Glib::path_get_basename (path);
2624                 preset_name = preset_name.substr (0, preset_name.find_last_of ('.'));
2625                 if (check_and_get_preset_name (comp.get()->Comp(), path, preset_name)) {
2626                         p.push_back (Plugin::PresetRecord (path, preset_name));
2627                 }
2628         }
2629
2630         if (user_only) {
2631                 return p;
2632         }
2633
2634         // factory presets
2635
2636         CFArrayRef presets;
2637         UInt32 dataSize;
2638         Boolean isWritable;
2639
2640         boost::shared_ptr<CAAudioUnit> unit (new CAAudioUnit);
2641         if (noErr != CAAudioUnit::Open (*(comp.get()), *unit)) {
2642                 return p;
2643         }
2644         if (noErr != unit->GetPropertyInfo (kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &dataSize, &isWritable)) {
2645                 unit->Uninitialize ();
2646                 return p;
2647         }
2648         if (noErr != unit->GetProperty (kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, (void*) &presets, &dataSize)) {
2649                 unit->Uninitialize ();
2650                 return p;
2651         }
2652         if (!presets) {
2653                 unit->Uninitialize ();
2654                 return p;
2655         }
2656
2657         CFIndex cnt = CFArrayGetCount (presets);
2658         for (CFIndex i = 0; i < cnt; ++i) {
2659                 AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (presets, i);
2660                 string const uri = string_compose ("%1", i);
2661                 string name = CFStringRefToStdString (preset->presetName);
2662                 p.push_back (Plugin::PresetRecord (uri, name, false));
2663         }
2664         CFRelease (presets);
2665         unit->Uninitialize ();
2666
2667 #endif // NO_PLUGIN_STATE
2668         return p;
2669 }
2670
2671 Glib::ustring
2672 AUPluginInfo::au_cache_path ()
2673 {
2674         return Glib::build_filename (ARDOUR::user_cache_directory(), "au_cache");
2675 }
2676
2677 PluginInfoList*
2678 AUPluginInfo::discover (bool scan_only)
2679 {
2680         XMLTree tree;
2681
2682         /* AU require a CAComponentDescription pointer provided by the OS.
2683          * Ardour only caches port and i/o config. It can't just 'scan' without
2684          * 'discovering' (like we do for VST).
2685          *
2686          * "Scan Only" means
2687          * "Iterate over all plugins. skip the ones where there's no io-cache".
2688          */
2689         _scan_only = scan_only;
2690
2691         if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) {
2692                 ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
2693         }
2694         // create crash log file
2695         au_start_crashlog ();
2696
2697         PluginInfoList* plugs = new PluginInfoList;
2698
2699         discover_fx (*plugs);
2700         discover_music (*plugs);
2701         discover_generators (*plugs);
2702         discover_instruments (*plugs);
2703
2704         // all fine if we get here
2705         au_remove_crashlog ();
2706
2707         DEBUG_TRACE (DEBUG::PluginManager, string_compose ("AU: discovered %1 plugins\n", plugs->size()));
2708
2709         return plugs;
2710 }
2711
2712 void
2713 AUPluginInfo::discover_music (PluginInfoList& plugs)
2714 {
2715         CAComponentDescription desc;
2716         desc.componentFlags = 0;
2717         desc.componentFlagsMask = 0;
2718         desc.componentSubType = 0;
2719         desc.componentManufacturer = 0;
2720         desc.componentType = kAudioUnitType_MusicEffect;
2721
2722         discover_by_description (plugs, desc);
2723 }
2724
2725 void
2726 AUPluginInfo::discover_fx (PluginInfoList& plugs)
2727 {
2728         CAComponentDescription desc;
2729         desc.componentFlags = 0;
2730         desc.componentFlagsMask = 0;
2731         desc.componentSubType = 0;
2732         desc.componentManufacturer = 0;
2733         desc.componentType = kAudioUnitType_Effect;
2734
2735         discover_by_description (plugs, desc);
2736 }
2737
2738 void
2739 AUPluginInfo::discover_generators (PluginInfoList& plugs)
2740 {
2741         CAComponentDescription desc;
2742         desc.componentFlags = 0;
2743         desc.componentFlagsMask = 0;
2744         desc.componentSubType = 0;
2745         desc.componentManufacturer = 0;
2746         desc.componentType = kAudioUnitType_Generator;
2747
2748         discover_by_description (plugs, desc);
2749 }
2750
2751 void
2752 AUPluginInfo::discover_instruments (PluginInfoList& plugs)
2753 {
2754         CAComponentDescription desc;
2755         desc.componentFlags = 0;
2756         desc.componentFlagsMask = 0;
2757         desc.componentSubType = 0;
2758         desc.componentManufacturer = 0;
2759         desc.componentType = kAudioUnitType_MusicDevice;
2760
2761         discover_by_description (plugs, desc);
2762 }
2763
2764
2765 bool
2766 AUPluginInfo::au_get_crashlog (std::string &msg)
2767 {
2768         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
2769         if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
2770                 return false;
2771         }
2772         std::ifstream ifs(fn.c_str());
2773         msg.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
2774         au_remove_crashlog ();
2775         return true;
2776 }
2777
2778 void
2779 AUPluginInfo::au_start_crashlog ()
2780 {
2781         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
2782         assert(!_crashlog_fd);
2783         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Creating AU Log: %1\n", fn));
2784         if (!(_crashlog_fd = fopen(fn.c_str(), "w"))) {
2785                 PBD::error << "Cannot create AU error-log" << fn << "\n";
2786                 cerr << "Cannot create AU error-log" << fn << "\n";
2787         }
2788 }
2789
2790 void
2791 AUPluginInfo::au_remove_crashlog ()
2792 {
2793         if (_crashlog_fd) {
2794                 ::fclose(_crashlog_fd);
2795                 _crashlog_fd = NULL;
2796         }
2797         string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
2798         ::g_unlink(fn.c_str());
2799         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Remove AU Log: %1\n", fn));
2800 }
2801
2802
2803 void
2804 AUPluginInfo::au_crashlog (std::string msg)
2805 {
2806         if (!_crashlog_fd) {
2807                 fprintf(stderr, "AU: %s\n", msg.c_str());
2808         } else {
2809                 fprintf(_crashlog_fd, "AU: %s\n", msg.c_str());
2810                 ::fflush(_crashlog_fd);
2811         }
2812 }
2813
2814 void
2815 AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
2816 {
2817         ArdourComponent comp = 0;
2818         au_crashlog(string_compose("Start AU discovery for Type: %1", (int)desc.componentType));
2819
2820         comp = ArdourFindNext (NULL, &desc);
2821
2822         while (comp != NULL) {
2823                 CAComponentDescription temp;
2824 #ifdef COREAUDIO105
2825                 GetComponentInfo (comp, &temp, NULL, NULL, NULL);
2826 #else
2827                 AudioComponentGetDescription (comp, &temp);
2828 #endif
2829                 CFStringRef itemName = NULL;
2830
2831                 {
2832                         if (itemName != NULL) CFRelease(itemName);
2833                         CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
2834                         CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
2835                         CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
2836                         itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
2837                                         compTypeString, compManufacturerString, compSubTypeString);
2838                         au_crashlog(string_compose("Scanning ID: %1", CFStringRefToStdString(itemName)));
2839                         if (compTypeString != NULL)
2840                                 CFRelease(compTypeString);
2841                         if (compSubTypeString != NULL)
2842                                 CFRelease(compSubTypeString);
2843                         if (compManufacturerString != NULL)
2844                                 CFRelease(compManufacturerString);
2845                 }
2846
2847                 if (is_blacklisted(CFStringRefToStdString(itemName))) {
2848                         info << string_compose (_("Skipped blacklisted AU plugin %1 "), CFStringRefToStdString(itemName)) << endmsg;
2849                         comp = ArdourFindNext (comp, &desc);
2850                         continue;
2851                 }
2852
2853                 bool has_midi_in = false;
2854
2855                 AUPluginInfoPtr info (new AUPluginInfo
2856                                       (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
2857
2858                 /* although apple designed the subtype field to be a "category" indicator,
2859                    its really turned into a plugin ID field for a given manufacturer. Hence
2860                    there are no categories for AudioUnits. However, to keep the plugins
2861                    showing up under "categories", we'll use the "type" as a high level
2862                    selector.
2863
2864                    NOTE: no panners, format converters or i/o AU's for our purposes
2865                  */
2866
2867                 switch (info->descriptor->Type()) {
2868                 case kAudioUnitType_Panner:
2869                 case kAudioUnitType_OfflineEffect:
2870                 case kAudioUnitType_FormatConverter:
2871                         comp = ArdourFindNext (comp, &desc);
2872                         continue;
2873
2874                 case kAudioUnitType_Output:
2875                         info->category = _("AudioUnit Outputs");
2876                         break;
2877                 case kAudioUnitType_MusicDevice:
2878                         info->category = _("AudioUnit Instruments");
2879                         has_midi_in = true;
2880                         break;
2881                 case kAudioUnitType_MusicEffect:
2882                         info->category = _("AudioUnit MusicEffects");
2883                         has_midi_in = true;
2884                         break;
2885                 case kAudioUnitType_Effect:
2886                         info->category = _("AudioUnit Effects");
2887                         break;
2888                 case kAudioUnitType_Mixer:
2889                         info->category = _("AudioUnit Mixers");
2890                         break;
2891                 case kAudioUnitType_Generator:
2892                         info->category = _("AudioUnit Generators");
2893                         break;
2894                 default:
2895                         info->category = _("AudioUnit (Unknown)");
2896                         break;
2897                 }
2898
2899                 au_blacklist(CFStringRefToStdString(itemName));
2900 #ifdef COREAUDIO105
2901                 get_names (temp, info->name, info->creator);
2902 #else
2903                 get_names (comp, info->name, info->creator);
2904 #endif
2905                 ARDOUR::PluginScanMessage(_("AU"), info->name, false);
2906                 au_crashlog(string_compose("Plugin: %1", info->name));
2907
2908                 info->type = ARDOUR::AudioUnit;
2909                 info->unique_id = stringify_descriptor (*info->descriptor);
2910
2911                 /* XXX not sure of the best way to handle plugin versioning yet */
2912
2913                 CAComponent cacomp (*info->descriptor);
2914
2915 #ifdef COREAUDIO105
2916                 if (cacomp.GetResourceVersion (info->version) != noErr)
2917 #else
2918                 if (cacomp.GetVersion (info->version) != noErr)
2919 #endif
2920                 {
2921                         info->version = 0;
2922                 }
2923
2924                 const int rv = cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name);
2925
2926                 if (rv == 0) {
2927                         /* here we have to map apple's wildcard system to a simple pair
2928                            of values. in ::can_do() we use the whole system, but here
2929                            we need a single pair of values. XXX probably means we should
2930                            remove any use of these values.
2931
2932                            for now, if the plugin provides a wildcard, treat it as 1. we really
2933                            don't care much, because whether we can handle an i/o configuration
2934                            depends upon ::can_support_io_configuration(), not these counts.
2935
2936                            they exist because other parts of ardour try to present i/o configuration
2937                            info to the user, which should perhaps be revisited.
2938                         */
2939
2940                         int32_t possible_in = info->cache.io_configs.front().first;
2941                         int32_t possible_out = info->cache.io_configs.front().second;
2942
2943                         if (possible_in > 0) {
2944                                 info->n_inputs.set (DataType::AUDIO, possible_in);
2945                         } else {
2946                                 info->n_inputs.set (DataType::AUDIO, 1);
2947                         }
2948
2949                         info->n_inputs.set (DataType::MIDI, has_midi_in ? 1 : 0);
2950
2951                         if (possible_out > 0) {
2952                                 info->n_outputs.set (DataType::AUDIO, possible_out);
2953                         } else {
2954                                 info->n_outputs.set (DataType::AUDIO, 1);
2955                         }
2956
2957                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("detected AU %1 with %2 i/o configurations - %3\n",
2958                                                                         info->name.c_str(), info->cache.io_configs.size(), info->unique_id));
2959
2960                         plugs.push_back (info);
2961
2962                 }
2963                 else if (rv == -1) {
2964                         error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg;
2965                 }
2966
2967                 au_unblacklist(CFStringRefToStdString(itemName));
2968                 au_crashlog("Success.");
2969                 comp = ArdourFindNext (comp, &desc);
2970                 if (itemName != NULL) CFRelease(itemName); itemName = NULL;
2971         }
2972         au_crashlog(string_compose("End AU discovery for Type: %1", (int)desc.componentType));
2973 }
2974
2975 int
2976 AUPluginInfo::cached_io_configuration (const std::string& unique_id,
2977                                        UInt32 version,
2978                                        CAComponent& comp,
2979                                        AUPluginCachedInfo& cinfo,
2980                                        const std::string& name)
2981 {
2982         std::string id;
2983         char buf[32];
2984
2985         /* concatenate unique ID with version to provide a key for cached info lookup.
2986            this ensures we don't get stale information, or should if plugin developers
2987            follow Apple "guidelines".
2988          */
2989
2990         snprintf (buf, sizeof (buf), "%u", (uint32_t) version);
2991         id = unique_id;
2992         id += '/';
2993         id += buf;
2994
2995         CachedInfoMap::iterator cim = cached_info.find (id);
2996
2997         if (cim != cached_info.end()) {
2998                 cinfo = cim->second;
2999                 return 0;
3000         }
3001
3002         if (_scan_only) {
3003                 PBD::info << string_compose (_("Skipping AU %1 (not indexed. Discover new plugins to add)"), name) << endmsg;
3004                 return 1;
3005         }
3006
3007         CAAudioUnit unit;
3008         AUChannelInfo* channel_info;
3009         UInt32 cnt;
3010         int ret;
3011
3012         ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name));
3013
3014         try {
3015
3016                 if (CAAudioUnit::Open (comp, unit) != noErr) {
3017                         return -1;
3018                 }
3019
3020         } catch (...) {
3021
3022                 warning << string_compose (_("Could not load AU plugin %1 - ignored"), name) << endmsg;
3023                 return -1;
3024
3025         }
3026
3027         DEBUG_TRACE (DEBUG::AudioUnits, "get AU channel info\n");
3028         if ((ret = unit.GetChannelInfo (&channel_info, cnt)) < 0) {
3029                 return -1;
3030         }
3031
3032         if (ret > 0) {
3033
3034                 /* no explicit info available, so default to 1in/1out */
3035
3036                 /* XXX this is wrong. we should be indicating wildcard values */
3037
3038                 cinfo.io_configs.push_back (pair<int,int> (-1, -1));
3039
3040         } else {
3041                 /* store each configuration */
3042                 if (comp.Desc().IsGenerator() || comp.Desc().IsMusicDevice()) {
3043                         // incrementally add busses
3044                         int in = 0;
3045                         int out = 0;
3046                         for (uint32_t n = 0; n < cnt; ++n) {
3047                                 in += channel_info[n].inChannels;
3048                                 out += channel_info[n].outChannels;
3049                                 cinfo.io_configs.push_back (pair<int,int> (in, out));
3050                         }
3051                 } else {
3052                         for (uint32_t n = 0; n < cnt; ++n) {
3053                                 cinfo.io_configs.push_back (pair<int,int> (channel_info[n].inChannels,
3054                                                         channel_info[n].outChannels));
3055                         }
3056                 }
3057
3058                 free (channel_info);
3059         }
3060
3061         add_cached_info (id, cinfo);
3062         save_cached_info ();
3063
3064         return 0;
3065 }
3066
3067 void
3068 AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo)
3069 {
3070         cached_info[id] = cinfo;
3071 }
3072
3073 #define AU_CACHE_VERSION "2.0"
3074
3075 void
3076 AUPluginInfo::save_cached_info ()
3077 {
3078         XMLNode* node;
3079
3080         node = new XMLNode (X_("AudioUnitPluginCache"));
3081         node->add_property( "version", AU_CACHE_VERSION );
3082
3083         for (map<string,AUPluginCachedInfo>::iterator i = cached_info.begin(); i != cached_info.end(); ++i) {
3084                 XMLNode* parent = new XMLNode (X_("plugin"));
3085                 parent->add_property ("id", i->first);
3086                 node->add_child_nocopy (*parent);
3087
3088                 for (vector<pair<int, int> >::iterator j = i->second.io_configs.begin(); j != i->second.io_configs.end(); ++j) {
3089
3090                         XMLNode* child = new XMLNode (X_("io"));
3091                         char buf[32];
3092
3093                         snprintf (buf, sizeof (buf), "%d", j->first);
3094                         child->add_property (X_("in"), buf);
3095                         snprintf (buf, sizeof (buf), "%d", j->second);
3096                         child->add_property (X_("out"), buf);
3097                         parent->add_child_nocopy (*child);
3098                 }
3099
3100         }
3101
3102         Glib::ustring path = au_cache_path ();
3103         XMLTree tree;
3104
3105         tree.set_root (node);
3106
3107         if (!tree.write (path)) {
3108                 error << string_compose (_("could not save AU cache to %1"), path) << endmsg;
3109                 g_unlink (path.c_str());
3110         }
3111 }
3112
3113 int
3114 AUPluginInfo::load_cached_info ()
3115 {
3116         Glib::ustring path = au_cache_path ();
3117         XMLTree tree;
3118
3119         if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
3120                 return 0;
3121         }
3122
3123         if ( !tree.read (path) ) {
3124                 error << "au_cache is not a valid XML file.  AU plugins will be re-scanned" << endmsg;
3125                 return -1;
3126         }
3127
3128         const XMLNode* root (tree.root());
3129
3130         if (root->name() != X_("AudioUnitPluginCache")) {
3131                 return -1;
3132         }
3133
3134         //initial version has incorrectly stored i/o info, and/or garbage chars.
3135         const XMLProperty* version = root->property(X_("version"));
3136         if (! ((version != NULL) && (version->value() == X_(AU_CACHE_VERSION)))) {
3137                 error << "au_cache is not correct version.  AU plugins will be re-scanned" << endmsg;
3138                 return -1;
3139         }
3140
3141         cached_info.clear ();
3142
3143         const XMLNodeList children = root->children();
3144
3145         for (XMLNodeConstIterator iter = children.begin(); iter != children.end(); ++iter) {
3146
3147                 const XMLNode* child = *iter;
3148
3149                 if (child->name() == X_("plugin")) {
3150
3151                         const XMLNode* gchild;
3152                         const XMLNodeList gchildren = child->children();
3153                         const XMLProperty* prop = child->property (X_("id"));
3154
3155                         if (!prop) {
3156                                 continue;
3157                         }
3158
3159                         string id = prop->value();
3160                         string fixed;
3161                         string version;
3162
3163                         string::size_type slash = id.find_last_of ('/');
3164
3165                         if (slash == string::npos) {
3166                                 continue;
3167                         }
3168
3169                         version = id.substr (slash);
3170                         id = id.substr (0, slash);
3171                         fixed = AUPlugin::maybe_fix_broken_au_id (id);
3172
3173                         if (fixed.empty()) {
3174                                 error << string_compose (_("Your AudioUnit configuration cache contains an AU plugin whose ID cannot be understood - ignored (%1)"), id) << endmsg;
3175                                 continue;
3176                         }
3177
3178                         id = fixed;
3179                         id += version;
3180
3181                         AUPluginCachedInfo cinfo;
3182
3183                         for (XMLNodeConstIterator giter = gchildren.begin(); giter != gchildren.end(); giter++) {
3184
3185                                 gchild = *giter;
3186
3187                                 if (gchild->name() == X_("io")) {
3188
3189                                         int in;
3190                                         int out;
3191                                         const XMLProperty* iprop;
3192                                         const XMLProperty* oprop;
3193
3194                                         if (((iprop = gchild->property (X_("in"))) != 0) &&
3195                                             ((oprop = gchild->property (X_("out"))) != 0)) {
3196                                                 in = atoi (iprop->value());
3197                                                 out = atoi (oprop->value());
3198
3199                                                 cinfo.io_configs.push_back (pair<int,int> (in, out));
3200                                         }
3201                                 }
3202                         }
3203
3204                         if (cinfo.io_configs.size()) {
3205                                 add_cached_info (id, cinfo);
3206                         }
3207                 }
3208         }
3209
3210         return 0;
3211 }
3212
3213
3214 std::string
3215 AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
3216 {
3217         stringstream s;
3218
3219         /* note: OSType is a compiler-implemenation-defined value,
3220            historically a 32 bit integer created with a multi-character
3221            constant such as 'abcd'. It is, fundamentally, an abomination.
3222         */
3223
3224         s << desc.Type();
3225         s << '-';
3226         s << desc.SubType();
3227         s << '-';
3228         s << desc.Manu();
3229
3230         return s.str();
3231 }
3232
3233 bool
3234 AUPluginInfo::needs_midi_input () const
3235 {
3236         return is_effect_with_midi_input () || is_instrument ();
3237 }
3238
3239 bool
3240 AUPluginInfo::is_effect () const
3241 {
3242         return is_effect_without_midi_input() || is_effect_with_midi_input();
3243 }
3244
3245 bool
3246 AUPluginInfo::is_effect_without_midi_input () const
3247 {
3248         return descriptor->IsAUFX();
3249 }
3250
3251 bool
3252 AUPluginInfo::is_effect_with_midi_input () const
3253 {
3254         return descriptor->IsAUFM();
3255 }
3256
3257 bool
3258 AUPluginInfo::is_instrument () const
3259 {
3260         return descriptor->IsMusicDevice();
3261 }
3262
3263 void
3264 AUPlugin::set_info (PluginInfoPtr info)
3265 {
3266         Plugin::set_info (info);
3267
3268         AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
3269         _has_midi_input = pinfo->needs_midi_input ();
3270         _has_midi_output = false;
3271 }
3272
3273 int
3274 AUPlugin::create_parameter_listener (AUEventListenerProc cb, void* arg, float interval_secs)
3275 {
3276 #ifdef WITH_CARBON
3277         CFRunLoopRef run_loop = (CFRunLoopRef) GetCFRunLoopFromEventLoop(GetCurrentEventLoop());
3278 #else
3279         CFRunLoopRef run_loop = CFRunLoopGetCurrent();
3280 #endif
3281         CFStringRef  loop_mode = kCFRunLoopDefaultMode;
3282
3283         if (AUEventListenerCreate (cb, arg, run_loop, loop_mode, interval_secs, interval_secs, &_parameter_listener) != noErr) {
3284                 return -1;
3285         }
3286
3287         _parameter_listener_arg = arg;
3288
3289         return 0;
3290 }
3291
3292 int
3293 AUPlugin::listen_to_parameter (uint32_t param_id)
3294 {
3295         AudioUnitEvent      event;
3296
3297         if (!_parameter_listener || param_id >= descriptors.size()) {
3298                 return -2;
3299         }
3300
3301         event.mEventType = kAudioUnitEvent_ParameterValueChange;
3302         event.mArgument.mParameter.mAudioUnit = unit->AU();
3303         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3304         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3305         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3306
3307         if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3308                 return -1;
3309         }
3310
3311         event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
3312         event.mArgument.mParameter.mAudioUnit = unit->AU();
3313         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3314         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3315         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3316
3317         if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3318                 return -1;
3319         }
3320
3321         event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
3322         event.mArgument.mParameter.mAudioUnit = unit->AU();
3323         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3324         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3325         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3326
3327         if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3328                 return -1;
3329         }
3330
3331         return 0;
3332 }
3333
3334 int
3335 AUPlugin::end_listen_to_parameter (uint32_t param_id)
3336 {
3337         AudioUnitEvent      event;
3338
3339         if (!_parameter_listener || param_id >= descriptors.size()) {
3340                 return -2;
3341         }
3342
3343         event.mEventType = kAudioUnitEvent_ParameterValueChange;
3344         event.mArgument.mParameter.mAudioUnit = unit->AU();
3345         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3346         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3347         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3348
3349         if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3350                 return -1;
3351         }
3352
3353         event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
3354         event.mArgument.mParameter.mAudioUnit = unit->AU();
3355         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3356         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3357         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3358
3359         if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3360                 return -1;
3361         }
3362
3363         event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
3364         event.mArgument.mParameter.mAudioUnit = unit->AU();
3365         event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
3366         event.mArgument.mParameter.mScope = descriptors[param_id].scope;
3367         event.mArgument.mParameter.mElement = descriptors[param_id].element;
3368
3369         if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
3370                 return -1;
3371         }
3372
3373         return 0;
3374 }
3375
3376 void
3377 AUPlugin::_parameter_change_listener (void* arg, void* src, const AudioUnitEvent* event, UInt64 host_time, Float32 new_value)
3378 {
3379         ((AUPlugin*) arg)->parameter_change_listener (arg, src, event, host_time, new_value);
3380 }
3381
3382 void
3383 AUPlugin::parameter_change_listener (void* /*arg*/, void* src, const AudioUnitEvent* event, UInt64 /*host_time*/, Float32 new_value)
3384 {
3385         ParameterMap::iterator i;
3386
3387         if ((i = parameter_map.find (event->mArgument.mParameter.mParameterID)) == parameter_map.end()) {
3388                 return;
3389         }
3390
3391         switch (event->mEventType) {
3392         case kAudioUnitEvent_BeginParameterChangeGesture:
3393                 StartTouch (i->second);
3394                 break;
3395         case kAudioUnitEvent_EndParameterChangeGesture:
3396                 EndTouch (i->second);
3397                 break;
3398         case kAudioUnitEvent_ParameterValueChange:
3399                 /* whenever we change a parameter, we request that we are NOT notified of the change, so anytime we arrive here, it
3400                    means that something else (i.e. the plugin GUI) made the change.
3401                 */
3402                 ParameterChangedExternally (i->second, new_value);
3403                 break;
3404         default:
3405                 break;
3406         }
3407 }