Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
au_v2_adapter.hpp
Go to the documentation of this file.
1#pragma once
2
3// The AU v2 effect adapter wraps Apple's AudioUnitSDK (Apple-only,
4// developer-supplied). The whole header is gated on __APPLE__ so it stays
5// self-contained — an empty no-op — on the Linux header-hygiene check and any
6// non-Apple TU.
7#if defined(__APPLE__)
8
9#include <AudioUnitSDK/AUMIDIEffectBase.h>
10#include <AudioToolbox/AudioUnitProperties.h> // AUMIDIOutputCallbackStruct
11
18#include <pulp/midi/buffer.hpp>
19#include <pulp/midi/message.hpp>
23
24#include <array>
25#include <atomic>
26#include <cstdint>
27#include <limits>
28#include <memory>
29#include <string_view>
30#include <vector>
31
32namespace pulp::format::au {
33
39
64inline UInt32 build_channel_info(const PluginDescriptor& desc,
65 AUChannelInfo* out) noexcept {
66 if (!desc.supported_bus_layouts.empty()) {
67 UInt32 count = 0;
68 for (const auto& layout : desc.supported_bus_layouts) {
69 const int in_w = layout.inputs.empty() ? 0 : layout.inputs.front();
70 const int out_w = layout.outputs.empty() ? 0 : layout.outputs.front();
71 if (in_w < 0 || out_w <= 0 || in_w > std::numeric_limits<SInt16>::max() ||
72 out_w > std::numeric_limits<SInt16>::max()) continue;
73 bool duplicate = false;
74 for (UInt32 i = 0; i < count; ++i) {
75 duplicate |= out[i].inChannels == in_w && out[i].outChannels == out_w;
76 }
77 if (duplicate) continue;
78 if (count == kMaxChannelInfoPairs) break;
79 out[count++] = {static_cast<SInt16>(in_w), static_cast<SInt16>(out_w)};
80 }
81 return count;
82 }
83 // Clamp the declared widths into the supported {1, 2} flex range; a declared
84 // width of 0 means "no bus" (instrument input) and stays 0.
85 auto clamp_width = [](int n) -> int {
86 if (n <= 0) return 0;
87 return n >= 2 ? 2 : 1;
88 };
89 const int in_w = clamp_width(desc.default_input_channels());
90 const int out_w = clamp_width(desc.default_output_channels());
91
92 UInt32 count = 0;
93 // An instrument whose main input is optional must also validate with that
94 // input unconnected (a host that offers no audio to a synth still has to
95 // initialize it), so its table carries the no-input rows ahead of the
96 // connected pairs. Effects are unchanged: their main input is required.
97 const bool optional_input = in_w > 0 && desc.category == PluginCategory::Instrument &&
98 !desc.input_buses.empty() && desc.input_buses[0].optional;
99 if (in_w == 0 || optional_input) {
100 // No input bus (instrument / generator): 0 in, each output width up to
101 // the declared maximum.
102 for (int w = 1; w <= out_w && count < kMaxChannelInfoPairs; ++w) {
103 out[count].inChannels = 0;
104 out[count].outChannels = static_cast<SInt16>(w);
105 ++count;
106 }
107 }
108 if (in_w == 0) {
109 // Nothing else to advertise without an input bus.
110 } else if (in_w == out_w) {
111 // Symmetric effect: matching in==out pairs up to the declared width.
112 for (int w = 1; w <= in_w && count < kMaxChannelInfoPairs; ++w) {
113 out[count].inChannels = static_cast<SInt16>(w);
114 out[count].outChannels = static_cast<SInt16>(w);
115 ++count;
116 }
117 } else if (count < kMaxChannelInfoPairs) {
118 // Asymmetric effect: report the exact declared pair (e.g. {1,2}).
119 out[count].inChannels = static_cast<SInt16>(in_w);
120 out[count].outChannels = static_cast<SInt16>(out_w);
121 ++count;
122 }
123 return count;
124}
125
127inline constexpr AudioUnitElement kSidechainInputElement = 1;
128
131inline constexpr const char* kSidechainElementName = "Side Chain";
132
139inline bool effect_has_sidechain_element(const PluginDescriptor& desc) noexcept {
140 return desc.input_buses.size() > 1 && desc.input_buses[0].default_channels > 0 &&
141 desc.input_buses[1].default_channels > 0;
142}
143
147inline UInt32 effect_input_element_count(const PluginDescriptor& desc) noexcept {
148 return effect_has_sidechain_element(desc) ? 2u : 1u;
149}
150
155inline UInt32 instrument_input_element_count(const PluginDescriptor& desc) noexcept {
156 if (desc.input_buses.empty() || desc.input_buses[0].default_channels <= 0)
157 return 0u;
158 return effect_input_element_count(desc);
159}
160
163inline UInt32 sidechain_element_default_channels(const PluginDescriptor& desc) noexcept {
164 if (!effect_has_sidechain_element(desc)) return 0;
165 const auto declared = desc.input_buses[1].default_channels;
166 const auto cap = static_cast<int>(boundary::kBoundaryMaxChannels);
167 return static_cast<UInt32>(declared > cap ? cap : declared);
168}
169
184inline std::size_t build_input_bus_infos(const PluginDescriptor& desc,
186 std::size_t cap) noexcept {
187 if (cap == 0) return 0;
188 std::size_t n = 0;
189 out[n].name = desc.input_buses.empty()
190 ? std::string_view{"Audio In"}
191 : std::string_view{desc.input_buses[0].name};
192 out[n].index = 0;
193 out[n].direction = BusDirection::Input;
194 out[n].role = BusRole::Main;
195 out[n].declared_channels = desc.default_input_channels();
196 // A declared-optional main input (an instrument that can also process
197 // audio) stays optional; a 0-channel input is optional by construction.
198 out[n].optional = desc.default_input_channels() == 0 ||
199 (!desc.input_buses.empty() && desc.input_buses[0].optional);
200 out[n].active = true; // adapter overwrites from live channel count
201 ++n;
202 if (desc.input_buses.size() > 1 && n < cap) {
203 out[n].name = desc.input_buses[1].name;
204 out[n].index = 1;
205 out[n].direction = BusDirection::Input;
206 out[n].role = BusRole::Sidechain;
207 out[n].declared_channels = desc.input_buses[1].default_channels;
208 out[n].optional = true;
209 out[n].active = false; // adapter activates it once element 1 is pulled
210 ++n;
211 }
212 return n;
213}
214
225inline UInt32 resolve_sidechain_channels(const AudioBufferList* list,
226 UInt32 expected_channels,
227 UInt32 frame_offset,
228 UInt32 frames,
229 const float** out,
230 std::size_t cap) noexcept {
231 if (list == nullptr || expected_channels == 0 || expected_channels > cap) return 0;
232 if (list->mNumberBuffers != expected_channels) return 0;
233 const std::uint64_t needed_bytes =
234 (static_cast<std::uint64_t>(frame_offset) + frames) * sizeof(float);
235 for (UInt32 ch = 0; ch < expected_channels; ++ch) {
236 const AudioBuffer& buffer = list->mBuffers[ch];
237 if (buffer.mNumberChannels != 1 || buffer.mData == nullptr ||
238 buffer.mDataByteSize < needed_bytes)
239 return 0;
240 }
241 for (UInt32 ch = 0; ch < expected_channels; ++ch) {
242 out[ch] = static_cast<const float*>(list->mBuffers[ch].mData) + frame_offset;
243 }
244 return expected_channels;
245}
246
247class PulpAUEffect : public ausdk::AUMIDIEffectBase {
248public:
249 explicit PulpAUEffect(AudioComponentInstance ci);
250 // Multi-plugin-bundle ctor: the component is bound to its own factory
251 // lexically (no global registry lookup). The single-arg ctor delegates
252 // here with the legacy `registered_factory()` for single-plugin bundles.
253 PulpAUEffect(AudioComponentInstance ci, ProcessorFactory factory);
254
255 OSStatus GetParameterList(AudioUnitScope inScope,
256 AudioUnitParameterID* outParameterList,
257 UInt32& outNumParameters) override;
258 OSStatus GetParameterInfo(AudioUnitScope inScope,
259 AudioUnitParameterID inParameterID,
260 AudioUnitParameterInfo& outParameterInfo) override;
261 OSStatus GetParameterValueStrings(AudioUnitScope inScope,
262 AudioUnitParameterID inParameterID,
263 CFArrayRef* outStrings) override;
264
265 // Single-source-of-truth parameter access. The plugin's
266 // StateStore IS the parameter store: the host reads it via GetParameter and
267 // writes it via SetParameter, so there is no separate AUElement/Globals
268 // copy to reconcile each block (that split caused the UI snap-back / render
269 // stalls). process() reads the same store; UI edits notify the host via a
270 // main-thread listener. See the au_v2_adapter.cpp definitions.
271 OSStatus GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
272 AudioUnitElement inElement, Float32& outValue) override;
273 OSStatus SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
274 AudioUnitElement inElement, Float32 inValue,
275 UInt32 inBufferOffsetInFrames) override;
276 OSStatus ScheduleParameter(const AudioUnitParameterEvent* events,
277 UInt32 num_events) override;
278
279 OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
280 AudioUnitElement inElement, UInt32& outDataSize,
281 bool& outWritable) override;
282 OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
283 AudioUnitElement inElement, void* outData) override;
284 OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
285 AudioUnitElement inElement, const void* inData,
286 UInt32 inDataSize) override;
287
293 UInt32 SupportedNumChannels(const AUChannelInfo** outInfo) override;
294
295 OSStatus Initialize() override;
296 void Cleanup() override;
297
303 void CreateExtendedElements() override;
304
309 OSStatus Render(AudioUnitRenderActionFlags& ioActionFlags,
310 const AudioTimeStamp& inTimeStamp, UInt32 nFrames) override;
311
314 bool ValidFormat(AudioUnitScope inScope, AudioUnitElement inElement,
315 const AudioStreamBasicDescription& inNewFormat) override;
316
319 OSStatus ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
320 const AudioStreamBasicDescription& inPrevFormat,
321 const AudioStreamBasicDescription& inNewFormat) override;
322
323 OSStatus ProcessBufferLists(AudioUnitRenderActionFlags& ioActionFlags,
324 const AudioBufferList& inBuffer,
325 AudioBufferList& outBuffer,
326 UInt32 inFramesToProcess) override;
327
332 OSStatus GetPresets(CFArrayRef* outData) const override;
333
338 OSStatus NewFactoryPresetSet(const AUPreset& inNewFactoryPreset) override;
339
344 return factory_presets_;
345 }
346
347 OSStatus SaveState(CFPropertyListRef* outData) override;
348 OSStatus RestoreState(CFPropertyListRef plist) override;
349
350 bool SupportsTail() override;
351 Float64 GetTailTime() override;
352 Float64 GetLatency() override;
353
356 return param_events_monitor_;
357 }
358
359 protected:
360 void queue_scheduled_events(const ParameterEventList& events, UInt32 frames_to_process);
361 void write_scheduled_end_values(ParameterEventList& events, UInt32 frames_to_process);
362 OSStatus ProcessForScheduledParams(ParameterEventList& events, UInt32 frames_to_process,
363 void* user_data) override;
364 OSStatus ProcessScheduledSlice(void* user_data,
365 UInt32 start_frame_in_buffer,
366 UInt32 slice_frames,
367 UInt32 total_buffer_frames) override;
368
371
382 // NB: AudioUnitSDK declares these as `AUSDK_RTSAFE` (which expands to
383 // `[[clang::nonblocking]]`). Propagating that attribute into an
384 // `override` declaration compiles under older Xcode but Xcode 16.4 /
385 // Clang 17+ rejects the attribute position with
386 // "expected ';' at end of declaration list". The attribute is a
387 // static-analysis hint only — dropping it has no runtime effect, and
388 // matches the pattern used by `PulpAUInstrument::HandleNoteOn/Off`.
389 OSStatus HandleMIDIEvent(UInt8 inStatus, UInt8 inChannel,
390 UInt8 inData1, UInt8 inData2,
391 UInt32 inStartFrame) override;
392
397 OSStatus HandleSysEx(const UInt8* inData, UInt32 inLength) override;
398
399private:
400 // Declared FIRST so it is destroyed LAST: the final detach cancels and joins
401 // the tracing auto-flush timer and writes the .pftrace, and that must happen
402 // after every span this instance can still emit. Tracing used to be wired
403 // into VST3 ONLY, so a Perfetto capture of an AU/CLAP/AAX/standalone session
404 // recorded nothing while the API happily claimed to be process-global.
405 // No-op unless PULP_TRACING=ON.
407
411 bool plugin_produces_midi() const noexcept;
412
413 // The store is declared before the Processor so it is destroyed after it.
414 // `Processor::state()` dereferences a pointer to this store, and a Processor
415 // may read it from its destructor or from a worker thread that destructor is
416 // about to join. Reversing these two lines hands that thread a freed store.
417 state::StateStore store_;
418 std::unique_ptr<Processor> processor_;
419 // Panic requests (Processor::request_panic), serviced before each block.
420 std::shared_ptr<PanicState> panic_state_;
421 // Factory presets, bound to store_ in the constructor. Declared after
422 // store_ so reverse member destruction retires the PresetManager while the
423 // store it holds a reference to is still alive.
424 FactoryPresetTable factory_presets_;
425 // kAudioUnitProperty_RenderContextObserver; bound once the processor
426 // exists, advertised only for an AudioWorkgroupClient processor.
427 RenderContextObserverProperty render_context_;
428 // Keeps a host class-info restore off the processor while Render() is
429 // inside it. Declared after processor_ so it outlives every render that
430 // consults it.
431 StateRestoreGate state_restore_gate_;
432 // Declared after processor_ so reverse member destruction retires retained
433 // editor handles before either referenced object is released.
434 runtime::AliveToken owner_alive_;
435 // Contract editors the host still holds. Declared after processor_ so its
436 // destruction invalidates them while the processor and store_ exist.
437 ContractSessionRegistry contract_sessions_;
438
439 // Immutable plugin metadata, cached once in the constructor so the render
440 // path can view its bus-name strings without copying the descriptor per
441 // block (which would allocate std::string members on the audio thread).
442 PluginDescriptor descriptor_{};
443
444 // Main-thread listener that pushes editor parameter edits to the host
445 // (AudioUnitSetParameter), kept alive for the adapter's lifetime so host
446 // param writes/notifications never run on the render thread. See ctor.
447 state::ListenerToken ui_push_listener_;
448 ParameterDisplayNamePublisher parameter_display_names_;
449
450 // Parameter-event sidecar, set on the Processor each block. During a
451 // scheduled AU render slice it contains slice-relative immediate/ramp
452 // events translated from AudioUnitParameterEvent; otherwise it is empty.
453 state::ParameterEventQueue param_events_;
454 bool scheduled_slice_active_ = false;
455 // Processor::wants_sample_accurate_parameters(), asked at construction.
456 // False: scheduled automation renders the buffer in one call.
457 bool sample_accurate_params_ = false;
458 // Records an opted-out processor reading param_events(); see processor.hpp.
459 ParamEventsOptOutMonitor param_events_monitor_;
460 // Processor::wants_au_scheduled_parameter_slices(): render one call per
461 // scheduled slice instead of one call per buffer.
462 bool slice_scheduled_params_ = false;
463
464 // One scheduled render: the time-sorted event list, how far its
465 // immediates have been applied, and its ramps (by index, reserved in
466 // Initialize()), so each slice touches only what changes at its start
467 // instead of rescanning and rewriting every event.
468 struct ScheduledRamp {
469 std::uint32_t index = 0;
470 bool superseded = false; // a later immediate set the same parameter
471 };
472 const ParameterEventList* scheduled_events_ = nullptr;
473 std::size_t scheduled_cursor_ = 0;
474 std::vector<ScheduledRamp> scheduled_ramps_;
475 bool scheduled_ramps_overflowed_ = false;
476 std::vector<AudioUnitParameterEvent> scheduled_sort_scratch_;
477 // Block-rate path: per store parameter, the latest scheduled event seen
478 // this render (event < 0 when none), and which parameters were touched.
479 struct LatestScheduledEvent {
480 SInt32 offset = 0;
481 std::int32_t event = -1;
482 };
483 std::vector<LatestScheduledEvent> scheduled_latest_;
484 std::vector<std::uint32_t> scheduled_touched_;
485 std::vector<state::StateStore::ParamWrite> scheduled_writes_;
486 std::vector<std::uint32_t> scheduled_offset_counts_;
487
488 // Per-block MIDI I/O. Hoisted to members (not constructed per render call)
489 // and given reserved, realtime-capacity-limited storage in Initialize() so
490 // the drain loop's add()/add_sysex_copy() never grows a vector on the audio
491 // thread. Reset with clear()+clear_sysex() each block. Capacities match the
492 // VST3 adapter so behavior is uniform across formats.
493 static constexpr std::size_t kMaxEventsPerBlock = kMaxMidiEventsPerBlock;
494 static constexpr std::size_t kMaxSysexPerBlock = 64;
495 static constexpr std::size_t kMaxSysexPayloadBytes = 512;
496 midi::MidiBuffer midi_in_;
497 midi::MidiBuffer midi_out_;
498
499 // Host accommodations, resolved once in the constructor via the
500 // runtime policy.
501 HostQuirks host_quirks_{};
502
503 // Cached ParamID of the "Bypass" parameter (plugin-declared or
504 // synthesized by host-quirk policy). 0 when none is available, so
505 // ProcessBufferLists never short-circuits to pass-through.
506 state::ParamID bypass_param_id_ = 0;
507 // Dry-delay line for the bypass pass-through. Sized to the processor's
508 // reported latency in Initialize(); keeps the bypassed dry signal aligned
509 // with the host's plugin-delay-compensation instead of arriving `latency`
510 // samples early (a zero latency leaves it a no-op zero-copy passthrough).
512 std::vector<const float*> input_ptrs_;
513 std::vector<float*> output_ptrs_;
514
515 // Side-chain input element state. `has_sidechain_element_` is fixed at
516 // construction from the descriptor. `sidechain_pulled_` points at input
517 // element 1's buffer list only while Render() is on the stack and only when
518 // that pull succeeded; null means "not connected" for the block.
519 // `slice_frame_offset_` is the start of the current scheduled-parameter
520 // slice within the pulled block (AUEffectBase advances only the main
521 // buffers between slices). Pointer storage is fixed-size so building the
522 // side-chain view never allocates on the audio thread.
523 bool has_sidechain_element_ = false;
524 const AudioBufferList* sidechain_pulled_ = nullptr;
525 UInt32 slice_frame_offset_ = 0;
526 std::array<const float*, boundary::kBoundaryMaxChannels> sidechain_ptrs_{};
527
528 // Previous-block transport snapshot used to derive change flags on
529 // `ProcessContext`. Default-constructed so the first process() call
530 // after init reports no changes.
531 detail::PlayheadSnapshot playhead_prev_{};
532 // Set while Initialize() runs ProcessBufferLists() once on silence to fault
533 // in the validation and buffer-wiring path. The render returns right after
534 // wiring: before the bypass check, the MIDI queue drain, host callbacks and
535 // the Processor, so nothing host- or plug-in-visible moves.
536 bool warming_render_path_ = false;
537 void warm_render_path();
538
539 // Host offline-render intent (kAudioUnitProperty_OfflineRender), written by
540 // the host before a faster-than-realtime bounce and read each render block.
541 OfflineRenderProperty offline_render_;
542
543 // MIDI input path — AU v2 effects that declare accepts_midi are packaged as
544 // aumf (kAudioUnitType_MusicEffect). The host routes inbound MIDI through
545 // AUMIDIBase::MIDIEvent / SysEx → HandleMIDIEvent / HandleSysEx. Those are
546 // LOCK-FREE single-producer (host MIDI/render thread) queues drained by the
547 // single consumer (ProcessBufferLists) — no mutex on the audio thread, the
548 // same atomic/wait-free discipline as the parameter store. Short messages
549 // are allocation-free; under flood, excess events are dropped rather than
550 // blocking the render (lossy, like the RT parameter notify path). aufx
551 // effects never receive MIDI, so the queues stay empty.
552 runtime::SpscQueue<midi::MidiEvent, 1024> midi_in_queue_;
553 struct SysexChunk {
554 std::array<uint8_t, 512> bytes{};
555 uint16_t length = 0;
556 };
557 runtime::SpscQueue<SysexChunk, 32> sysex_in_queue_;
558
559 // MIDI OUTPUT path. A plugin that declares `produces_midi` (aumf MIDI effect
560 // or an instrument routed back to MIDI) emits events into midi_out_ during
561 // process(). AU v2 delivers them through kAudioUnitProperty_MIDIOutputCallback:
562 // the host writes a callback (SetProperty) and reads the output name list
563 // (kAudioUnitProperty_MIDIOutputCallbackInfo). The render path packs midi_out_
564 // into a MIDIPacketList and calls the host callback. The publisher owns the
565 // main-thread → render-thread handoff of the (callback, userData) pair.
566 MidiOutputCallbackPublisher midi_output_callback_;
567 // Pre-reserved packet-list storage so the render-path build never allocates.
568 MidiOutputPacketBuilder midi_out_packet_builder_;
569
570 // Per-instance channel-config table filled by SupportedNumChannels. Member
571 // (not call-local) so the pointer handed to the host outlives the call.
572 std::array<AUChannelInfo, kMaxChannelInfoPairs> channel_info_{};
573};
574
575} // namespace pulp::format::au
576
577#endif // defined(__APPLE__)
The shared adapter-boundary core.
The contract sessions one adapter instance opened that a host still holds.
Definition contract_editor_session.hpp:73
Lock-free panic request and output-note state for one Processor.
Definition panic.hpp:73
Records, for one adapter, whether an opted-out processor read param_events(), and logs it once.
Definition processor.hpp:1362
Definition processor.hpp:174
Definition state_restore_gate.hpp:87
The host-facing factory-preset table for one plug-in instance.
Definition au_factory_presets.hpp:56
Coalesces StateStore presentation-name changes and republishes only the affected stable parameter IDs...
Definition au_v2_common.hpp:589
void queue_scheduled_events(const ParameterEventList &events, UInt32 frames_to_process)
FactoryPresetTable & factory_preset_table() noexcept
The factory-preset table this instance serves.
Definition au_v2_adapter.hpp:343
OSStatus ProcessForScheduledParams(ParameterEventList &events, UInt32 frames_to_process, void *user_data) override
void CreateExtendedElements() override
Adds the side-chain input element (element 1, named "Side Chain") when the descriptor declares a side...
OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, bool &outWritable) override
OSStatus GetPresets(CFArrayRef *outData) const override
kAudioUnitProperty_FactoryPresets — the presets bundled with the plug-in, discovered from its Resourc...
OSStatus ProcessBufferLists(AudioUnitRenderActionFlags &ioActionFlags, const AudioBufferList &inBuffer, AudioBufferList &outBuffer, UInt32 inFramesToProcess) override
OSStatus SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, Float32 inValue, UInt32 inBufferOffsetInFrames) override
OSStatus GetParameterInfo(AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo &outParameterInfo) override
OSStatus RestoreState(CFPropertyListRef plist) override
OSStatus GetParameterList(AudioUnitScope inScope, AudioUnitParameterID *outParameterList, UInt32 &outNumParameters) override
OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void *inData, UInt32 inDataSize) override
OSStatus HandleSysEx(const UInt8 *inData, UInt32 inLength) override
System-exclusive payload (F0 … F7).
OSStatus HandleMIDIEvent(UInt8 inStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame) override
Called by the AU host for every short MIDI message (status has already been split into inStatus / inC...
bool ValidFormat(AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription &inNewFormat) override
Side-chain element formats must also fit the adapter's per-bus channel ceiling; every other element k...
OSStatus ProcessScheduledSlice(void *user_data, UInt32 start_frame_in_buffer, UInt32 slice_frames, UInt32 total_buffer_frames) override
void publish_parameter_display_changes()
Drain host-facing name changes on the caller's control/main thread.
PulpAUEffect(AudioComponentInstance ci, ProcessorFactory factory)
Float64 GetTailTime() override
OSStatus Initialize() override
PulpAUEffect(AudioComponentInstance ci)
OSStatus Render(AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 nFrames) override
Pulls the side-chain input element (when the host connected it or installed a render callback on it),...
OSStatus ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription &inPrevFormat, const AudioStreamBasicDescription &inNewFormat) override
Keeps the side-chain element's sample rate in step with the main bus so a host that only configures e...
void write_scheduled_end_values(ParameterEventList &events, UInt32 frames_to_process)
ParamEventsOptOutMonitor & param_events_monitor()
The opted-out-reader record for this unit's processor.
Definition au_v2_adapter.hpp:355
OSStatus SaveState(CFPropertyListRef *outData) override
OSStatus NewFactoryPresetSet(const AUPreset &inNewFactoryPreset) override
kAudioUnitProperty_PresentPreset carrying a non-negative preset number.
OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData) override
Float64 GetLatency() override
OSStatus ScheduleParameter(const AudioUnitParameterEvent *events, UInt32 num_events) override
OSStatus GetParameterValueStrings(AudioUnitScope inScope, AudioUnitParameterID inParameterID, CFArrayRef *outStrings) override
UInt32 SupportedNumChannels(const AUChannelInfo **outInfo) override
Surface the plugin's supported (input, output) channel-count pairs so the host (and auval) can negoti...
OSStatus GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, Float32 &outValue) override
kAudioUnitProperty_RenderContextObserver, the v2 form of AUv3's renderContextObserver.
Definition au_v2_common.hpp:492
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
RAII attachment.
Definition trace_session.hpp:276
RAII handle for a registered parameter-change listener.
Definition listener_token.hpp:47
Definition parameter_event_queue.hpp:82
Per-host accommodation flags consumed by format adapters.
Definition child_process_manager.hpp:18
Apple-only: the whole header is gated on APPLE so it stays an empty no-op on the Linux header-hygiene...
Definition au_factory_presets.hpp:33
UInt32 resolve_sidechain_channels(const AudioBufferList *list, UInt32 expected_channels, UInt32 frame_offset, UInt32 frames, const float **out, std::size_t cap) noexcept
Resolve the per-channel read pointers of a pulled side-chain buffer list for one render slice.
Definition au_v2_adapter.hpp:225
std::size_t build_input_bus_infos(const PluginDescriptor &desc, ProcessBusBufferInfo *out, std::size_t cap) noexcept
Fill ProcessBusBufferInfo for an AU v2 EFFECT's input buses: the main input (index 0,...
Definition au_v2_adapter.hpp:184
constexpr const char * kSidechainElementName
Element name a host shows for the side-chain input.
Definition au_v2_adapter.hpp:131
constexpr std::size_t kMaxMidiEventsPerBlock
RT-safe builder for the MIDIPacketList an AU v2 plugin hands the host via kAudioUnitProperty_MIDIOutp...
Definition au_v2_common.hpp:310
UInt32 build_channel_info(const PluginDescriptor &desc, AUChannelInfo *out) noexcept
Maximum number of AUChannelInfo pairs build_channel_info can produce.
Definition au_v2_adapter.hpp:64
UInt32 instrument_input_element_count(const PluginDescriptor &desc) noexcept
Number of AU input elements an AU v2 INSTRUMENT built from desc exposes: 0 without a main input bus (...
Definition au_v2_adapter.hpp:155
UInt32 effect_input_element_count(const PluginDescriptor &desc) noexcept
Number of AU input elements an AU v2 effect built from desc exposes: 2 with a side-chain bus (main + ...
Definition au_v2_adapter.hpp:147
constexpr AudioUnitElement kSidechainInputElement
AU input element that carries the side-chain bus of an AU v2 effect.
Definition au_v2_adapter.hpp:127
bool effect_has_sidechain_element(const PluginDescriptor &desc) noexcept
True when an AU v2 unit built from desc exposes a second input element for side-chain audio: the desc...
Definition au_v2_adapter.hpp:139
constexpr std::size_t kMaxChannelInfoPairs
Upper bound on AUChannelInfo pairs an adapter advertises through SupportedNumChannels.
Definition au_v2_common.hpp:58
UInt32 sidechain_element_default_channels(const PluginDescriptor &desc) noexcept
Default channel count of the side-chain input element, clamped into the adapter's per-bus ceiling.
Definition au_v2_adapter.hpp:163
LatencyCompensatedBypassT< kBoundaryMaxChannels > LatencyCompensatedBypass
Definition adapter_boundary.hpp:294
constexpr std::size_t kBoundaryMaxChannels
Channel ceiling — the single source of truth for the per-bus channel count every adapter caps at.
Definition adapter_boundary.hpp:83
std::unique_ptr< Processor >(*)() ProcessorFactory
Factory function type — plugins provide this to create processor instances.
Definition processor.hpp:1416
@ Input
Definition process_block.hpp:113
@ Instrument
Synth/sampler (receives MIDI, produces audio).
Definition plugin_descriptor.hpp:187
@ Main
Definition bus_role.hpp:10
@ Sidechain
Definition bus_role.hpp:12
Definition generated_event_source.hpp:33
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
uint32_t ParamID
Unique parameter identifier, stable across plugin versions.
Definition parameter.hpp:55
STL namespace.
Plugin metadata — declared once, immutable.
Definition plugin_descriptor.hpp:245
Metadata that travels with a non-owning richer-surface process bus view.
Definition process_block.hpp:219