+AUPlugin::requires_fixed_size_buffers() const
+{
+ return _requires_fixed_size_buffers;
+}
+
+
+int
+AUPlugin::set_block_size (pframes_t nframes)
+{
+ bool was_initialized = initialized;
+ UInt32 numFrames = nframes;
+ OSErr err;
+
+ if (initialized) {
+ deactivate ();
+ }
+
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set MaximumFramesPerSlice in global scope to %1\n", numFrames));
+ if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
+ 0, &numFrames, sizeof (numFrames))) != noErr) {
+ cerr << "cannot set max frames (err = " << err << ')' << endl;
+ return -1;
+ }
+
+ if (was_initialized) {
+ activate ();
+ }
+
+ _current_block_size = nframes;
+
+ return 0;
+}
+
+bool
+AUPlugin::configure_io (ChanCount in, ChanCount out)
+{
+ AudioStreamBasicDescription streamFormat;
+ bool was_initialized = initialized;
+ int32_t audio_in = in.n_audio();
+ int32_t audio_out = out.n_audio();
+
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("configure %1 for %2 in %3 out\n", name(), in, out));
+
+ if (initialized) {
+ //if we are already running with the requested i/o config, bail out here
+ if ( (audio_in==input_channels) && (audio_out==output_channels) ) {
+ return 0;
+ } else {
+ deactivate ();
+ }
+ }
+
+ streamFormat.mSampleRate = _session.frame_rate();
+ streamFormat.mFormatID = kAudioFormatLinearPCM;
+ streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
+
+#ifdef __LITTLE_ENDIAN__
+ /* relax */
+#else
+ streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+#endif
+
+ streamFormat.mBitsPerChannel = 32;
+ streamFormat.mFramesPerPacket = 1;
+
+ /* apple says that for non-interleaved data, these
+ values always refer to a single channel.
+ */
+ streamFormat.mBytesPerPacket = 4;
+ streamFormat.mBytesPerFrame = 4;
+
+ streamFormat.mChannelsPerFrame = audio_in;
+
+ if (set_input_format (streamFormat) != 0) {
+ return -1;
+ }
+
+ streamFormat.mChannelsPerFrame = audio_out;
+
+ if (set_output_format (streamFormat) != 0) {
+ return -1;
+ }
+
+ /* reset plugin info to show currently configured state */
+
+ _info->n_inputs = in;
+ _info->n_outputs = out;
+
+ if (was_initialized) {
+ activate ();
+ }
+
+ return 0;
+}
+
+ChanCount
+AUPlugin::input_streams() const
+{
+ ChanCount c;
+
+ c.set (DataType::AUDIO, 1);
+ c.set (DataType::MIDI, 0);
+
+ if (input_channels < 0) {
+ warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg;
+ } else {
+ c.set (DataType::AUDIO, input_channels);
+ c.set (DataType::MIDI, _has_midi_input ? 1 : 0);
+ }
+
+ return c;
+}
+
+
+ChanCount
+AUPlugin::output_streams() const
+{
+ ChanCount c;
+
+ c.set (DataType::AUDIO, 1);
+ c.set (DataType::MIDI, 0);
+
+ if (output_channels < 0) {
+ warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg;
+ } else {
+ c.set (DataType::AUDIO, output_channels);
+ c.set (DataType::MIDI, _has_midi_output ? 1 : 0);
+ }
+
+ return c;
+}
+
+bool
+AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) const
+{
+ // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never
+ // attempt to multiply-instantiate plugins to meet io configurations.
+
+ int32_t audio_in = in.n_audio();
+ int32_t audio_out;
+ int32_t plugcnt = -1;
+ AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
+
+ /* lets check MIDI first */
+
+ if (in.n_midi() > 0) {
+ if (!_has_midi_input) {
+ return false;
+ }
+ }
+
+ vector<pair<int,int> >& io_configs = pinfo->cache.io_configs;
+
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 has %2 IO configurations\n", name(), io_configs.size()));
+
+ //Ardour expects the plugin to tell it the output configuration
+ //but AU plugins can have multiple I/O configurations
+ //in most cases (since we don't allow special routing like sidechains in A2, we want to preserve the number of streams
+ //so first lets see if there's a configuration that keeps out==in
+
+ audio_out = audio_in;
+
+ for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
+ int32_t possible_in = i->first;
+ int32_t possible_out = i->second;
+
+ if (possible_in == audio_in && possible_out == audio_out) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: in %1 out %2\n", in, out));
+ return 1;
+ }
+ }
+
+ /* now allow potentially "imprecise" matches */
+ audio_out = -1;
+ for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
+
+ int32_t possible_in = i->first;
+ int32_t possible_out = i->second;
+
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tpossible in %1 possible out %2\n", possible_in, possible_out));
+
+ if (possible_out == 0) {
+ warning << string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg;
+ continue;
+ }
+
+ if (possible_in == 0) {
+
+ /* instrument plugin, always legal but throws away inputs ...
+ */
+
+ if (possible_out == -1) {
+ /* out much match in (UNLIKELY!!) */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out == -2) {
+ /* any configuration possible, pick matching */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out < -2) {
+ /* explicit variable number of outputs, pick maximum */
+ audio_out = -possible_out;
+ plugcnt = 1;
+ } else {
+ /* exact number of outputs */
+ audio_out = possible_out;
+ plugcnt = 1;
+ }
+ }
+
+ if (possible_in == -1) {
+
+ /* wildcard for input */
+
+ if (possible_out == -1) {
+ /* out much match in */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out == -2) {
+ /* any configuration possible, pick matching */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out < -2) {
+ /* explicit variable number of outputs, pick maximum */
+ audio_out = -possible_out;
+ plugcnt = 1;
+ } else {
+ /* exact number of outputs */
+ audio_out = possible_out;
+ plugcnt = 1;
+ }
+ }
+
+ if (possible_in == -2) {
+
+ if (possible_out == -1) {
+ /* any configuration possible, pick matching */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out == -2) {
+ error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
+ << endmsg;
+ plugcnt = -1;
+ } else if (possible_out < -2) {
+ /* explicit variable number of outputs, pick maximum */
+ audio_out = -possible_out;
+ plugcnt = 1;
+ } else {
+ /* exact number of outputs */
+ audio_out = possible_out;
+ plugcnt = 1;
+ }
+ }
+
+ if (possible_in < -2) {
+
+ /* explicit variable number of inputs */
+
+ if (audio_in > -possible_in) {
+ /* request is too large */
+ plugcnt = -1;
+ }
+
+ if (possible_out == -1) {
+ /* out must match in */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out == -2) {
+ error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name())
+ << endmsg;
+ plugcnt = -1;
+ } else if (possible_out < -2) {
+ /* explicit variable number of outputs, pick maximum */
+ audio_out = -possible_out;
+ plugcnt = 1;
+ } else {
+ /* exact number of outputs */
+ audio_out = possible_out;
+ plugcnt = 1;
+ }
+ }
+
+ if (possible_in == audio_in) {
+
+ /* exact number of inputs ... must match obviously */
+
+ if (possible_out == -1) {
+ /* out must match in */
+ audio_out = audio_in;
+ plugcnt = 1;
+ } else if (possible_out == -2) {
+ /* any output configuration, pick matching */
+ audio_out = audio_in;
+ plugcnt = -1;
+ } else if (possible_out < -2) {
+ /* explicit variable number of outputs, pick maximum */
+ audio_out = -possible_out;
+ plugcnt = 1;
+ } else {
+ /* exact number of outputs */
+ audio_out = possible_out;
+ plugcnt = 1;
+ }
+ }
+
+ if (plugcnt == 1) {
+ break;
+ }
+
+ }
+
+ if (plugcnt > 0) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: in %1 out %2 plugcnt %3\n", in, out, plugcnt));
+ } else {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\FAIL: no io configs match %1\n", in));
+ }
+
+ out.set (DataType::MIDI, 0);
+ out.set (DataType::AUDIO, audio_out);
+
+ return plugcnt;
+}
+
+int
+AUPlugin::set_input_format (AudioStreamBasicDescription& fmt)
+{
+ return set_stream_format (kAudioUnitScope_Input, input_elements, fmt);
+}
+
+int
+AUPlugin::set_output_format (AudioStreamBasicDescription& fmt)
+{
+ if (set_stream_format (kAudioUnitScope_Output, output_elements, fmt) != 0) {
+ return -1;
+ }
+
+ if (buffers) {
+ free (buffers);
+ buffers = 0;
+ }
+
+ buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) +
+ fmt.mChannelsPerFrame * sizeof(::AudioBuffer));
+
+ return 0;
+}
+
+int
+AUPlugin::set_stream_format (int scope, uint32_t cnt, AudioStreamBasicDescription& fmt)
+{
+ OSErr result;
+
+ for (uint32_t i = 0; i < cnt; ++i) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set stream format for %1, scope = %2 element %3\n",
+ (scope == kAudioUnitScope_Input ? "input" : "output"),
+ scope, cnt));
+ if ((result = unit->SetFormat (scope, i, fmt)) != 0) {
+ error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
+ (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg;
+ return -1;
+ }
+ }
+
+ if (scope == kAudioUnitScope_Input) {
+ input_channels = fmt.mChannelsPerFrame;
+ } else {
+ output_channels = fmt.mChannelsPerFrame;
+ }
+
+ return 0;
+}
+
+OSStatus
+AUPlugin::render_callback(AudioUnitRenderActionFlags*,
+ const AudioTimeStamp*,
+ UInt32,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData)
+{
+ if (_has_midi_input) {
+ assert (current_buffers->count().n_midi() > 0);
+
+ /* deliver the first (and assumed only) MIDI buffer's data
+ to the plugin
+ */
+
+ MidiBuffer& mb (current_buffers->get_midi(0));
+
+ for (MidiBuffer::iterator i = mb.begin(); i != mb.end(); ++i) {
+ Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *i;
+ switch (ev.type()) {
+ case MIDI_CMD_NOTE_ON:
+ case MIDI_CMD_NOTE_OFF:
+ case MIDI_CMD_CONTROL:
+ case MIDI_CMD_BENDER:
+ case MIDI_CMD_PGM_CHANGE:
+ case MIDI_CMD_CHANNEL_PRESSURE:
+ {
+ const uint8_t* b = ev.buffer();
+ unit->MIDIEvent (b[0], b[1], b[2], ev.time());
+ break;
+ }
+
+ default:
+ /* plugins do not get other stuff by default */
+ break;
+ };
+ }
+ }
+
+ /* not much to do with audio - the data is already in the buffers given to us in connect_and_run() */
+
+ if (current_maxbuf == 0) {
+ error << _("AUPlugin: render callback called illegally!") << endmsg;
+ return kAudioUnitErr_CannotDoInCurrentContext;
+ }
+ uint32_t limit = min ((uint32_t) ioData->mNumberBuffers, current_maxbuf);
+
+ for (uint32_t i = 0; i < limit; ++i) {
+ ioData->mBuffers[i].mNumberChannels = 1;
+ ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
+
+ /* we don't use the channel mapping because audiounits are
+ never replicated. one plugin instance uses all channels/buffers
+ passed to PluginInsert::connect_and_run()
+ */
+
+ ioData->mBuffers[i].mData = current_buffers->get_audio (i).data (cb_offset + current_offset);
+ }
+
+ cb_offset += inNumberFrames;
+
+ return noErr;
+}
+
+int
+AUPlugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_map, pframes_t nframes, framecnt_t offset)
+{
+ Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset);
+
+ AudioUnitRenderActionFlags flags = 0;
+ AudioTimeStamp ts;
+ OSErr err;
+ uint32_t maxbuf = bufs.count().n_audio();
+
+ if (requires_fixed_size_buffers() && (nframes != _last_nframes)) {
+ unit->GlobalReset();
+ _last_nframes = nframes;
+ }
+
+ current_buffers = &bufs;
+ current_maxbuf = maxbuf;
+ current_offset = offset;
+ cb_offset = 0;
+
+ buffers->mNumberBuffers = min ((uint32_t) output_channels, maxbuf);
+
+ for (uint32_t i = 0; i < buffers->mNumberBuffers; ++i) {
+ buffers->mBuffers[i].mNumberChannels = 1;
+ buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
+ buffers->mBuffers[i].mData = 0;
+ }
+
+ if (_has_midi_input) {
+
+ for (uint32_t i = 0; i < bufs.count().n_midi(); ++i) {
+
+ /* one MIDI port/buffer only */
+
+ MidiBuffer& m = bufs.get_midi (i);
+
+ for (MidiBuffer::iterator i = m.begin(); i != m.end(); ++i) {
+ Evoral::MIDIEvent<framepos_t> ev (*i);
+
+ if (ev.is_channel_event()) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: MIDI event %2\n",
+ name(), ev));
+ unit->MIDIEvent (ev.type() << 8 | ev.channel(),
+ ev.buffer()[1],
+ ev.buffer()[2],
+ ev.time());
+ }
+
+ /* XXX need to handle sysex and other message types */
+ }
+ }
+ }
+
+ ts.mSampleTime = frames_processed;
+ ts.mFlags = kAudioTimeStampSampleTimeValid;
+
+ if ((err = unit->Render (&flags, &ts, 0, nframes, buffers)) == noErr) {
+
+ current_maxbuf = 0;
+ frames_processed += nframes;
+
+ uint32_t limit = min ((uint32_t) buffers->mNumberBuffers, maxbuf);
+ uint32_t i;
+
+ for (i = 0; i < limit; ++i) {
+ Sample* expected_buffer_address= bufs.get_audio (i).data (offset);
+ if (expected_buffer_address != buffers->mBuffers[i].mData) {
+ // cerr << "chn " << i << " rendered into " << bufs[i]+offset << endl;
+ memcpy (expected_buffer_address, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
+ }
+ }
+
+ /* now silence any buffers that were passed in but the that the plugin
+ did not fill/touch/use.
+ */
+
+ for (;i < maxbuf; ++i) {
+ Sample* buffer_address= bufs.get_audio (i).data (offset);
+ memset (buffer_address, 0, nframes * sizeof (Sample));
+ }
+
+ return 0;
+ }
+
+ cerr << name() << " render status " << err << endl;
+ return -1;
+}
+
+OSStatus
+AUPlugin::get_beat_and_tempo_callback (Float64* outCurrentBeat,
+ Float64* outCurrentTempo)
+{
+ TempoMap& tmap (_session.tempo_map());
+
+ DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour beat&tempo callback\n");
+
+ /* more than 1 meter or more than 1 tempo means that a simplistic computation
+ (and interpretation) of a beat position will be incorrect. So refuse to
+ offer the value.
+ */
+
+ if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
+ return kAudioUnitErr_CannotDoInCurrentContext;
+ }
+
+ Timecode::BBT_Time bbt;
+ TempoMetric metric = tmap.metric_at (_session.transport_frame() + current_offset);
+ tmap.bbt_time_with_metric (_session.transport_frame() + current_offset, bbt, metric);
+
+ if (outCurrentBeat) {
+ float beat;
+ beat = metric.meter().beats_per_bar() * bbt.bars;
+ beat += bbt.beats;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
+ *outCurrentBeat = beat;
+ }
+
+ if (outCurrentTempo) {
+ *outCurrentTempo = floor (metric.tempo().beats_per_minute());
+ }
+
+ return noErr;
+
+}
+
+OSStatus
+AUPlugin::get_musical_time_location_callback (UInt32* outDeltaSampleOffsetToNextBeat,
+ Float32* outTimeSig_Numerator,
+ UInt32* outTimeSig_Denominator,
+ Float64* outCurrentMeasureDownBeat)
+{
+ TempoMap& tmap (_session.tempo_map());
+
+ DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour music time location callback\n");
+
+ /* more than 1 meter or more than 1 tempo means that a simplistic computation
+ (and interpretation) of a beat position will be incorrect. So refuse to
+ offer the value.
+ */
+
+ if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
+ return kAudioUnitErr_CannotDoInCurrentContext;
+ }
+
+ Timecode::BBT_Time bbt;
+ TempoMetric metric = tmap.metric_at (_session.transport_frame() + current_offset);
+ tmap.bbt_time_with_metric (_session.transport_frame() + current_offset, bbt, metric);
+
+ if (outDeltaSampleOffsetToNextBeat) {
+ if (bbt.ticks == 0) {
+ /* on the beat */
+ *outDeltaSampleOffsetToNextBeat = 0;
+ } else {
+ *outDeltaSampleOffsetToNextBeat = (UInt32)
+ floor (((Timecode::BBT_Time::ticks_per_beat - bbt.ticks)/Timecode::BBT_Time::ticks_per_beat) * // fraction of a beat to next beat
+ metric.tempo().frames_per_beat(_session.frame_rate(), metric.meter())); // frames per beat
+ }
+ }
+
+ if (outTimeSig_Numerator) {
+ *outTimeSig_Numerator = (UInt32) lrintf (metric.meter().beats_per_bar());
+ }
+ if (outTimeSig_Denominator) {
+ *outTimeSig_Denominator = (UInt32) lrintf (metric.meter().note_divisor());
+ }
+
+ if (outCurrentMeasureDownBeat) {
+
+ /* beat for the start of the bar.
+ 1|1|0 -> 1
+ 2|1|0 -> 1 + beats_per_bar
+ 3|1|0 -> 1 + (2 * beats_per_bar)
+ etc.
+ */
+
+ *outCurrentMeasureDownBeat = 1 + metric.meter().beats_per_bar() * (bbt.bars - 1);
+ }
+
+ return noErr;
+}
+
+OSStatus
+AUPlugin::get_transport_state_callback (Boolean* outIsPlaying,
+ Boolean* outTransportStateChanged,
+ Float64* outCurrentSampleInTimeLine,
+ Boolean* outIsCycling,
+ Float64* outCycleStartBeat,
+ Float64* outCycleEndBeat)
+{
+ bool rolling;
+ float speed;
+
+ DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour transport state callback\n");
+
+ rolling = _session.transport_rolling();
+ speed = _session.transport_speed ();
+
+ if (outIsPlaying) {
+ *outIsPlaying = _session.transport_rolling();
+ }
+
+ if (outTransportStateChanged) {
+ if (rolling != last_transport_rolling) {
+ *outTransportStateChanged = true;
+ } else if (speed != last_transport_speed) {
+ *outTransportStateChanged = true;
+ } else {
+ *outTransportStateChanged = false;
+ }
+ }
+
+ if (outCurrentSampleInTimeLine) {
+ /* this assumes that the AU can only call this host callback from render context,
+ where current_offset is valid.
+ */
+ *outCurrentSampleInTimeLine = _session.transport_frame() + current_offset;
+ }
+
+ if (outIsCycling) {
+ Location* loc = _session.locations()->auto_loop_location();
+
+ *outIsCycling = (loc && _session.transport_rolling() && _session.get_play_loop());
+
+ if (*outIsCycling) {
+
+ if (outCycleStartBeat || outCycleEndBeat) {
+
+ TempoMap& tmap (_session.tempo_map());
+
+ /* more than 1 meter means that a simplistic computation (and interpretation) of
+ a beat position will be incorrect. so refuse to offer the value.
+ */
+
+ if (tmap.n_meters() > 1) {
+ return kAudioUnitErr_CannotDoInCurrentContext;
+ }
+
+ Timecode::BBT_Time bbt;
+
+ if (outCycleStartBeat) {
+ TempoMetric metric = tmap.metric_at (loc->start() + current_offset);
+ _session.tempo_map().bbt_time_with_metric (loc->start(), bbt, metric);
+
+ float beat;
+ beat = metric.meter().beats_per_bar() * bbt.bars;
+ beat += bbt.beats;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
+
+ *outCycleStartBeat = beat;
+ }
+
+ if (outCycleEndBeat) {
+ TempoMetric metric = tmap.metric_at (loc->end() + current_offset);
+ _session.tempo_map().bbt_time_with_metric (loc->end(), bbt, metric);
+
+ float beat;
+ beat = metric.meter().beats_per_bar() * bbt.bars;
+ beat += bbt.beats;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
+
+ *outCycleEndBeat = beat;
+ }
+ }
+ }
+ }
+
+ last_transport_rolling = rolling;
+ last_transport_speed = speed;
+
+ return noErr;
+}
+
+set<Evoral::Parameter>
+AUPlugin::automatable() const
+{
+ set<Evoral::Parameter> automates;
+
+ for (uint32_t i = 0; i < descriptors.size(); ++i) {
+ if (descriptors[i].automatable) {
+ automates.insert (automates.end(), Evoral::Parameter (PluginAutomation, 0, i));
+ }
+ }
+
+ return automates;
+}
+
+string
+AUPlugin::describe_parameter (Evoral::Parameter param)
+{
+ if (param.type() == PluginAutomation && param.id() < parameter_count()) {
+ return descriptors[param.id()].label;
+ } else {
+ return "??";
+ }
+}
+
+void
+AUPlugin::print_parameter (uint32_t /*param*/, char* /*buf*/, uint32_t /*len*/) const
+{
+ // NameValue stuff here
+}
+
+bool
+AUPlugin::parameter_is_audio (uint32_t) const
+{
+ return false;
+}
+
+bool
+AUPlugin::parameter_is_control (uint32_t) const
+{
+ return true;
+}
+
+bool
+AUPlugin::parameter_is_input (uint32_t) const
+{
+ return false;
+}
+
+bool
+AUPlugin::parameter_is_output (uint32_t) const