2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <pbd/transmitter.h>
23 #include <pbd/xml++.h>
24 #include <pbd/whitespace.h>
26 #include <glibmm/thread.h>
28 #include <ardour/audioengine.h>
29 #include <ardour/io.h>
30 #include <ardour/audio_unit.h>
31 #include <ardour/session.h>
32 #include <ardour/utils.h>
34 #include <appleutility/CAAudioUnit.h>
35 #include <appleutility/CAAUParameter.h>
37 #include <CoreServices/CoreServices.h>
38 #include <AudioUnit/AudioUnit.h>
44 using namespace ARDOUR;
47 _render_callback(void *userData,
48 AudioUnitRenderActionFlags *ioActionFlags,
49 const AudioTimeStamp *inTimeStamp,
51 UInt32 inNumberFrames,
52 AudioBufferList* ioData)
54 return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
57 AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
59 Plugin (engine, session),
61 unit (new CAAudioUnit),
69 OSErr err = CAAudioUnit::Open (*(comp.get()), *unit);
72 error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg;
73 throw failed_constructor ();
76 AURenderCallbackStruct renderCallbackInfo;
78 renderCallbackInfo.inputProc = _render_callback;
79 renderCallbackInfo.inputProcRefCon = this;
81 if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
82 0, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) {
83 cerr << "cannot install render callback (err = " << err << ')' << endl;
84 throw failed_constructor();
87 unit->GetElementCount (kAudioUnitScope_Global, global_elements);
88 unit->GetElementCount (kAudioUnitScope_Input, input_elements);
89 unit->GetElementCount (kAudioUnitScope_Output, output_elements);
91 // set up the basic stream format. these fields do not change
93 streamFormat.mSampleRate = session.frame_rate();
94 streamFormat.mFormatID = kAudioFormatLinearPCM;
95 streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
96 streamFormat.mBitsPerChannel = 32;
97 streamFormat.mFramesPerPacket = 1;
99 // subject to later modification as we discover channel counts
101 streamFormat.mBytesPerPacket = 4;
102 streamFormat.mBytesPerFrame = 4;
103 streamFormat.mChannelsPerFrame = 1;
107 if (_set_block_size (_session.get_block_size())) {
108 error << _("AUPlugin: cannot set processing block size") << endmsg;
109 throw failed_constructor();
112 discover_parameters ();
114 Plugin::setup_controls ();
117 AUPlugin::~AUPlugin ()
120 unit->Uninitialize ();
129 AUPlugin::discover_parameters ()
131 /* discover writable parameters */
133 cerr << "get param info, there are " << global_elements << " global elements\n";
135 AudioUnitScope scopes[] = {
136 kAudioUnitScope_Global,
137 kAudioUnitScope_Output,
138 kAudioUnitScope_Input
141 descriptors.clear ();
143 for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
145 AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
147 cerr << "discovered " << param_info.NumParams() << " parameters in scope " << i << endl;
149 for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
151 AUParameterDescriptor d;
153 d.id = param_info.ParamID (i);
155 const CAAUParameter* param = param_info.GetParamInfo (d.id);
156 const AudioUnitParameterInfo& info (param->ParamInfo());
158 const int len = CFStringGetLength (param->GetName());;
159 char local_buffer[len*2];
160 Boolean good = CFStringGetCString(param->GetName(),local_buffer,len*2,kCFStringEncodingMacRoman);
164 d.label = local_buffer;
167 d.scope = param_info.GetScope ();
168 d.element = param_info.GetElement ();
170 /* info.units to consider */
172 kAudioUnitParameterUnit_Generic = 0
173 kAudioUnitParameterUnit_Indexed = 1
174 kAudioUnitParameterUnit_Boolean = 2
175 kAudioUnitParameterUnit_Percent = 3
176 kAudioUnitParameterUnit_Seconds = 4
177 kAudioUnitParameterUnit_SampleFrames = 5
178 kAudioUnitParameterUnit_Phase = 6
179 kAudioUnitParameterUnit_Rate = 7
180 kAudioUnitParameterUnit_Hertz = 8
181 kAudioUnitParameterUnit_Cents = 9
182 kAudioUnitParameterUnit_RelativeSemiTones = 10
183 kAudioUnitParameterUnit_MIDINoteNumber = 11
184 kAudioUnitParameterUnit_MIDIController = 12
185 kAudioUnitParameterUnit_Decibels = 13
186 kAudioUnitParameterUnit_LinearGain = 14
187 kAudioUnitParameterUnit_Degrees = 15
188 kAudioUnitParameterUnit_EqualPowerCrossfade = 16
189 kAudioUnitParameterUnit_MixerFaderCurve1 = 17
190 kAudioUnitParameterUnit_Pan = 18
191 kAudioUnitParameterUnit_Meters = 19
192 kAudioUnitParameterUnit_AbsoluteCents = 20
193 kAudioUnitParameterUnit_Octaves = 21
194 kAudioUnitParameterUnit_BPM = 22
195 kAudioUnitParameterUnit_Beats = 23
196 kAudioUnitParameterUnit_Milliseconds = 24
197 kAudioUnitParameterUnit_Ratio = 25
200 /* info.flags to consider */
204 kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
205 kAudioUnitParameterFlag_HasClump = (1L << 20)
206 kAudioUnitParameterFlag_HasName = (1L << 21)
207 kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
208 kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
209 kAudioUnitParameterFlag_NonRealTime = (1L << 24)
210 kAudioUnitParameterFlag_CanRamp = (1L << 25)
211 kAudioUnitParameterFlag_ExpertMode = (1L << 26)
212 kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
213 kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
214 kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
215 kAudioUnitParameterFlag_IsReadable = (1L << 30)
216 kAudioUnitParameterFlag_IsWritable = (1L << 31)
219 d.lower = info.minValue;
220 d.upper = info.maxValue;
221 d.default_value = info.defaultValue;
223 d.integer_step = (info.unit & kAudioUnitParameterUnit_Indexed);
224 d.toggled = (info.unit & kAudioUnitParameterUnit_Boolean) ||
225 (d.integer_step && ((d.upper - d.lower) == 1.0));
226 d.sr_dependent = (info.unit & kAudioUnitParameterUnit_SampleFrames);
227 d.automatable = !d.toggled &&
228 !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
229 (info.flags & kAudioUnitParameterFlag_IsWritable);
231 d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
237 d.min_unbound = 0; // lower is bound
238 d.max_unbound = 0; // upper is bound
240 descriptors.push_back (d);
247 AUPlugin::unique_id () const
249 return AUPluginInfo::stringify_descriptor (comp->Desc());
253 AUPlugin::label () const
255 return _info->name.c_str();
259 AUPlugin::parameter_count () const
261 return descriptors.size();
265 AUPlugin::default_value (uint32_t port)
267 if (port < descriptors.size()) {
268 return descriptors[port].default_value;
275 AUPlugin::signal_latency () const
278 return _user_latency;
281 return unit->Latency ();
285 AUPlugin::set_parameter (uint32_t which, float val)
287 if (which < descriptors.size()) {
288 const AUParameterDescriptor& d (descriptors[which]);
289 unit->SetParameter (d.id, d.scope, d.element, val);
294 AUPlugin::get_parameter (uint32_t which) const
297 if (which < descriptors.size()) {
298 const AUParameterDescriptor& d (descriptors[which]);
299 unit->GetParameter(d.id, d.scope, d.element, val);
305 AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const
307 if (which < descriptors.size()) {
308 pd = descriptors[which];
315 AUPlugin::nth_parameter (uint32_t which, bool& ok) const
317 if (which < descriptors.size()) {
326 AUPlugin::activate ()
330 if ((err = unit->Initialize()) != noErr) {
331 error << string_compose (_("AUPlugin: cannot initialize plugin (err = %1)"), err) << endmsg;
333 frames_processed = 0;
340 AUPlugin::deactivate ()
342 unit->GlobalReset ();
346 AUPlugin::set_block_size (nframes_t nframes)
348 _set_block_size (nframes);
352 AUPlugin::_set_block_size (nframes_t nframes)
354 bool was_initialized = initialized;
355 UInt32 numFrames = nframes;
359 unit->Uninitialize ();
362 if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
363 0, &numFrames, sizeof (numFrames))) != noErr) {
364 cerr << "cannot set max frames (err = " << err << ')' << endl;
368 if (was_initialized) {
376 AUPlugin::can_support_input_configuration (int32_t in)
378 streamFormat.mChannelsPerFrame = in;
379 /* apple says that for non-interleaved data, these
380 values always refer to a single channel.
382 streamFormat.mBytesPerPacket = 4;
383 streamFormat.mBytesPerFrame = 4;
385 if (set_input_format () == 0) {
393 AUPlugin::set_input_format ()
395 return set_stream_format (kAudioUnitScope_Input, input_elements);
399 AUPlugin::set_output_format ()
401 return set_stream_format (kAudioUnitScope_Output, output_elements);
405 AUPlugin::set_stream_format (int scope, uint32_t cnt)
409 for (uint32_t i = 0; i < cnt; ++i) {
410 if ((result = unit->SetFormat (scope, i, streamFormat)) != 0) {
411 error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
412 (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg;
417 if (scope == kAudioUnitScope_Input) {
427 AUPlugin::compute_output_streams (int32_t nplugins)
429 /* we will never replicate AU plugins - either they can do the I/O we need
430 or not. thus, we can ignore nplugins entirely.
433 if (set_output_format() == 0) {
440 buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) +
441 streamFormat.mChannelsPerFrame * sizeof(AudioBuffer));
443 Glib::Mutex::Lock em (_session.engine().process_lock());
444 IO::MoreOutputs (streamFormat.mChannelsPerFrame);
446 return streamFormat.mChannelsPerFrame;
453 AUPlugin::output_streams() const
455 if (!(format_set & 0x2)) {
456 warning << _("AUPlugin: output_streams() called without any format set!") << endmsg;
459 return streamFormat.mChannelsPerFrame;
464 AUPlugin::input_streams() const
466 if (!(format_set & 0x1)) {
467 warning << _("AUPlugin: input_streams() called without any format set!") << endmsg;
470 return streamFormat.mChannelsPerFrame;
474 AUPlugin::render_callback(AudioUnitRenderActionFlags *ioActionFlags,
475 const AudioTimeStamp *inTimeStamp,
477 UInt32 inNumberFrames,
478 AudioBufferList* ioData)
480 /* not much to do - the data is already in the buffers given to us in connect_and_run() */
482 if (current_maxbuf == 0) {
483 error << _("AUPlugin: render callback called illegally!") << endmsg;
484 return kAudioUnitErr_CannotDoInCurrentContext;
487 for (uint32_t i = 0; i < current_maxbuf; ++i) {
488 ioData->mBuffers[i].mNumberChannels = 1;
489 ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
490 ioData->mBuffers[i].mData = (*current_buffers)[i] + cb_offset + current_offset;
493 cb_offset += inNumberFrames;
499 AUPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset)
501 AudioUnitRenderActionFlags flags = 0;
504 current_buffers = &bufs;
505 current_maxbuf = maxbuf;
506 current_offset = offset;
509 buffers->mNumberBuffers = maxbuf;
511 for (uint32_t i = 0; i < maxbuf; ++i) {
512 buffers->mBuffers[i].mNumberChannels = 1;
513 buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
514 buffers->mBuffers[i].mData = 0;
517 ts.mSampleTime = frames_processed;
518 ts.mFlags = kAudioTimeStampSampleTimeValid;
520 if (unit->Render (&flags, &ts, 0, nframes, buffers) == noErr) {
523 frames_processed += nframes;
525 for (uint32_t i = 0; i < maxbuf; ++i) {
526 if (bufs[i] + offset != buffers->mBuffers[i].mData) {
527 memcpy (bufs[i]+offset, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
537 AUPlugin::automatable() const
539 set<uint32_t> automates;
541 for (uint32_t i = 0; i < descriptors.size(); ++i) {
542 if (descriptors[i].automatable) {
543 automates.insert (i);
551 AUPlugin::describe_parameter (uint32_t param)
553 return descriptors[param].label;
557 AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const
559 // NameValue stuff here
563 AUPlugin::parameter_is_audio (uint32_t) const
569 AUPlugin::parameter_is_control (uint32_t) const
575 AUPlugin::parameter_is_input (uint32_t) const
581 AUPlugin::parameter_is_output (uint32_t) const
587 AUPlugin::get_state()
589 XMLNode *root = new XMLNode (state_node_name());
590 LocaleGuard lg (X_("POSIX"));
595 AUPlugin::set_state(const XMLNode& node)
601 AUPlugin::save_preset (string name)
607 AUPlugin::load_preset (const string preset_label)
613 AUPlugin::get_presets ()
615 vector<string> presets;
621 AUPlugin::has_editor () const
623 // even if the plugin doesn't have its own editor, the AU API can be used
624 // to create one that looks native.
628 AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
634 AUPluginInfo::~AUPluginInfo ()
639 AUPluginInfo::load (Session& session)
644 boost::shared_ptr<CAComponent> comp (new CAComponent(*descriptor));
646 if (!comp->IsValid()) {
647 error << ("AudioUnit: not a valid Component") << endmsg;
649 plugin.reset (new AUPlugin (session.engine(), session, comp));
652 plugin->set_info (PluginInfoPtr (new AUPluginInfo (*this)));
656 catch (failed_constructor &err) {
657 return PluginPtr ((Plugin*) 0);
662 AUPluginInfo::discover ()
664 PluginInfoList plugs;
667 discover_music (plugs);
673 AUPluginInfo::discover_music (PluginInfoList& plugs)
675 CAComponentDescription desc;
676 desc.componentFlags = 0;
677 desc.componentFlagsMask = 0;
678 desc.componentSubType = 0;
679 desc.componentManufacturer = 0;
680 desc.componentType = kAudioUnitType_MusicEffect;
682 discover_by_description (plugs, desc);
686 AUPluginInfo::discover_fx (PluginInfoList& plugs)
688 CAComponentDescription desc;
689 desc.componentFlags = 0;
690 desc.componentFlagsMask = 0;
691 desc.componentSubType = 0;
692 desc.componentManufacturer = 0;
693 desc.componentType = kAudioUnitType_Effect;
695 discover_by_description (plugs, desc);
699 AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
703 comp = FindNextComponent (NULL, &desc);
705 while (comp != NULL) {
706 CAComponentDescription temp;
707 GetComponentInfo (comp, &temp, NULL, NULL, NULL);
709 AUPluginInfoPtr info (new AUPluginInfo
710 (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
712 /* no panners, format converters or i/o AU's for our purposes
715 switch (info->descriptor->Type()) {
716 case kAudioUnitType_Panner:
717 case kAudioUnitType_OfflineEffect:
718 case kAudioUnitType_FormatConverter:
724 switch (info->descriptor->SubType()) {
725 case kAudioUnitSubType_DefaultOutput:
726 case kAudioUnitSubType_SystemOutput:
727 case kAudioUnitSubType_GenericOutput:
728 case kAudioUnitSubType_AUConverter:
732 case kAudioUnitSubType_DLSSynth:
733 info->category = "DLSSynth";
736 case kAudioUnitType_MusicEffect:
737 info->category = "MusicEffect";
740 case kAudioUnitSubType_Varispeed:
741 info->category = "Varispeed";
744 case kAudioUnitSubType_Delay:
745 info->category = "Delay";
748 case kAudioUnitSubType_LowPassFilter:
749 info->category = "LowPassFilter";
752 case kAudioUnitSubType_HighPassFilter:
753 info->category = "HighPassFilter";
756 case kAudioUnitSubType_BandPassFilter:
757 info->category = "BandPassFilter";
760 case kAudioUnitSubType_HighShelfFilter:
761 info->category = "HighShelfFilter";
764 case kAudioUnitSubType_LowShelfFilter:
765 info->category = "LowShelfFilter";
768 case kAudioUnitSubType_ParametricEQ:
769 info->category = "ParametricEQ";
772 case kAudioUnitSubType_GraphicEQ:
773 info->category = "GraphicEQ";
776 case kAudioUnitSubType_PeakLimiter:
777 info->category = "PeakLimiter";
780 case kAudioUnitSubType_DynamicsProcessor:
781 info->category = "DynamicsProcessor";
784 case kAudioUnitSubType_MultiBandCompressor:
785 info->category = "MultiBandCompressor";
788 case kAudioUnitSubType_MatrixReverb:
789 info->category = "MatrixReverb";
792 case kAudioUnitType_Mixer:
793 info->category = "Mixer";
796 case kAudioUnitSubType_StereoMixer:
797 info->category = "StereoMixer";
800 case kAudioUnitSubType_3DMixer:
801 info->category = "3DMixer";
804 case kAudioUnitSubType_MatrixMixer:
805 info->category = "MatrixMixer";
812 AUPluginInfo::get_names (temp, info->name, info->creator);
814 info->type = ARDOUR::AudioUnit;
815 info->unique_id = stringify_descriptor (*info->descriptor);
817 /* mark the plugin as having flexible i/o */
820 info->n_outputs = -1;
823 plugs.push_back (info);
825 comp = FindNextComponent (comp, &desc);
830 AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker)
832 CFStringRef itemName = NULL;
834 // Marc Poirier-style item name
835 CAComponent auComponent (comp_desc);
836 if (auComponent.IsValid()) {
837 CAComponentDescription dummydesc;
838 Handle nameHandle = NewHandle(sizeof(void*));
839 if (nameHandle != NULL) {
840 OSErr err = GetComponentInfo(auComponent.Comp(), &dummydesc, nameHandle, NULL, NULL);
842 ConstStr255Param nameString = (ConstStr255Param) (*nameHandle);
843 if (nameString != NULL) {
844 itemName = CFStringCreateWithPascalString(kCFAllocatorDefault, nameString, CFStringGetSystemEncoding());
847 DisposeHandle(nameHandle);
851 // if Marc-style fails, do the original way
852 if (itemName == NULL) {
853 CFStringRef compTypeString = UTCreateStringForOSType(comp_desc.componentType);
854 CFStringRef compSubTypeString = UTCreateStringForOSType(comp_desc.componentSubType);
855 CFStringRef compManufacturerString = UTCreateStringForOSType(comp_desc.componentManufacturer);
857 itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
858 compTypeString, compManufacturerString, compSubTypeString);
860 if (compTypeString != NULL)
861 CFRelease(compTypeString);
862 if (compSubTypeString != NULL)
863 CFRelease(compSubTypeString);
864 if (compManufacturerString != NULL)
865 CFRelease(compManufacturerString);
868 string str = CFStringRefToStdString(itemName);
869 string::size_type colon = str.find (':');
872 name = str.substr (colon+1);
873 maker = str.substr (0, colon);
874 // strip_whitespace_edges (maker);
875 // strip_whitespace_edges (name);
882 // from CAComponentDescription.cpp (in libs/appleutility in ardour source)
883 extern char *StringForOSType (OSType t, char *writeLocation);
886 AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
891 s << StringForOSType (desc.Type(), str);
894 s << StringForOSType (desc.SubType(), str);
897 s << StringForOSType (desc.Manu(), str);