Apple-only: the whole header is gated on APPLE so it stays an empty no-op on the Linux header-hygiene check. More...
Classes | |
| class | FactoryPresetTable |
| The host-facing factory-preset table for one plug-in instance. More... | |
| class | PulpAUEffect |
| struct | PulpEditorContext |
| class | OfflineRenderProperty |
| Host offline-render intent for an AU v2 instance. More... | |
| struct | MidiOutputPacketBuilder |
| class | RenderContextObserverProperty |
| kAudioUnitProperty_RenderContextObserver, the v2 form of AUv3's renderContextObserver. More... | |
| class | ScopedHostParamWrite |
| RAII marker for the host-write window. Scoped to the calling thread. More... | |
| class | ParameterDisplayNamePublisher |
| Coalesces StateStore presentation-name changes and republishes only the affected stable parameter IDs from the host's main thread. More... | |
| class | MidiOutputCallbackPublisher |
| Publishes the host's kAudioUnitProperty_MIDIOutputCallback pair from the main thread to the render thread. More... | |
| class | PulpAUInstrument |
| class | PulpAUMidiProcessor |
| AU v2 adapter for a Pulp Processor whose descriptor declares PluginCategory::MidiEffect — an AU MIDI processor, component type aumi (kAudioUnitType_MIDIProcessor). More... | |
Typedefs | |
| using | CocoaViewInfoFiller = bool (*)(void*) |
| Cross-TU Cocoa-view hook. | |
Functions | |
| std::filesystem::path | factory_presets_dir_for_binary (const std::filesystem::path &binary) |
| Map a plug-in binary's path to the folder holding its factory presets. | |
| std::filesystem::path | loaded_bundle_factory_presets_dir () |
| factory_presets_dir_for_binary applied to the image this code is linked into. | |
| UInt32 | build_channel_info (const PluginDescriptor &desc, AUChannelInfo *out) noexcept |
| Maximum number of AUChannelInfo pairs build_channel_info can produce. | |
| 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 descriptor declares a main input bus plus a second input bus with at least one channel. | |
| 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 + side chain), otherwise 1. | |
| 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 (the classic synth), 1 with a main input, 2 with a main input and a side chain. | |
| 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. | |
| 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, role Main) plus — when the descriptor declares a second input bus — a Sidechain bus (index 1, role Sidechain). | |
| 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. | |
| bool | fill_cocoa_view_info (void *outData) |
| Fills an AudioUnitCocoaViewInfo with the Pulp Cocoa view factory's bundle URL + class name. | |
| ContractEditorSession * | contract_view_session (void *ns_view) |
| The session behind an AU v2 contract view (the NSView the contract view factory returned), or null for any other view or once it is invalidated, so an in-process host (automation, tests) can reach the editor and its EditorAutomation. | |
| midi::MidiEvent | decode_midi_event (uint8_t inStatus, uint8_t inChannel, uint8_t inData1, uint8_t inData2) noexcept |
| Decode a short MIDI status byte and two data bytes into a midi::MidiEvent. | |
| ProcessContext | make_render_process_context (double sample_rate, int num_samples, bool offline=false) noexcept |
| Build the AU v2 render-path ProcessContext fields that are independent of host callbacks. | |
| void | apply_host_callbacks_to_process_context (ProcessContext &ctx, const ausdk::AUBase &unit, detail::PlayheadSnapshot &previous) noexcept |
| Populate AU v2 render-context transport metadata from host callbacks and derive per-block playhead change flags. | |
| Float64 | tail_samples_to_seconds (int tail_samples, double sample_rate) noexcept |
| OSStatus | fill_parameter_list (const state::StateStore &store, AudioUnitScope scope, AudioUnitParameterID *out_list, UInt32 &out_count) |
| GetParameterList: report every global-scope parameter ID. | |
| OSStatus | fill_parameter_info (const state::StateStore &store, AudioUnitScope scope, AudioUnitParameterID param_id, AudioUnitParameterInfo &out_info, bool advertise_value_strings) |
| GetParameterInfo: name, range, and unit. | |
| OSStatus | fill_parameter_clump_property_info (const state::StateStore &store, AudioUnitScope scope, UInt32 &out_size, bool &out_writable) |
| OSStatus | fill_supports_mpe_property_info (bool supports_mpe, AudioUnitScope scope, UInt32 &out_size, bool &out_writable) |
| kAudioUnitProperty_SupportsMPE (the v2 bridge of AUv3's supportsMPE). | |
| OSStatus | fill_supports_mpe (bool supports_mpe, AudioUnitScope scope, void *out_data) |
| OSStatus | fill_parameter_clump_name (const state::StateStore &store, AudioUnitScope scope, void *out_data) |
| OSStatus | fill_parameter_value_strings (const state::StateStore &store, AudioUnitScope scope, AudioUnitParameterID param_id, CFArrayRef *out_strings) |
| GetParameterValueStrings: the enumerated display strings for a DISCRETE parameter. | |
| OSStatus | parameter_string_from_value (const state::StateStore &store, void *out_data) |
| kAudioUnitProperty_ParameterStringFromValue. | |
| OSStatus | parameter_value_from_string (const state::StateStore &store, void *out_data) |
| kAudioUnitProperty_ParameterValueFromString — text entry, string → value. | |
| bool | host_is_writing_param () noexcept |
| True while this thread is inside a host-originated SetParameter. | |
| void | wire_host_parameter_bridge (state::StateStore &store, AudioUnit unit, state::ListenerToken &out_listener) |
| Wire the editor → host parameter path: gesture begin/end brackets (so a drag records one clean automation pass) and a value-change notification so the host re-reads through GetParameter. | |
| OSStatus | save_pulp_state (state::StateStore &store, Processor &processor, CFPropertyListRef *out_data) |
| Attach Pulp's serialized state to the AU class-info dictionary the base class produced. | |
| OSStatus | restore_pulp_state (state::StateStore &store, Processor &processor, StateRestoreGate &gate, CFPropertyListRef plist) |
| Read the pulp-state blob back out of an AU class-info dictionary. | |
| CFArrayRef | make_midi_output_names (const char *stream_name) |
| Build the one-element CFArrayRef a host reads from kAudioUnitProperty_MIDIOutputCallbackInfo — the names of the plugin's MIDI output streams. | |
| std::size_t | instrument_output_element_count (const PluginDescriptor &desc) noexcept |
| Number of AU output ELEMENTS (buses) a multi-output instrument advertises: one per declared output bus, floored at 1 and capped at BusBufferSet::kMaxBuses. | |
| std::size_t | build_output_bus_infos (const PluginDescriptor &desc, ProcessBusBufferInfo *out, std::size_t cap) noexcept |
| Fill one ProcessBusBufferInfo per declared output bus (index 0 = Main, the rest = Aux) from the descriptor. | |
Variables | |
| constexpr AudioUnitElement | kSidechainInputElement = 1 |
| AU input element that carries the side-chain bus of an AU v2 effect. | |
| constexpr const char * | kSidechainElementName = "Side Chain" |
| Element name a host shows for the side-chain input. | |
| constexpr std::size_t | kMaxChannelInfoPairs = 16 |
| Upper bound on AUChannelInfo pairs an adapter advertises through SupportedNumChannels. | |
| static constexpr AudioUnitPropertyID | kPulpEditorContextProperty = 0x50754564 |
| Custom AU property ID the Cocoa view factory queries to obtain the host-side Processor + StateStore pointers. | |
| CocoaViewInfoFiller | g_cocoa_view_info_filler |
| constexpr std::size_t | kMaxMidiEventsPerBlock = 2048 |
| RT-safe builder for the MIDIPacketList an AU v2 plugin hands the host via kAudioUnitProperty_MIDIOutputCallback. | |
Apple-only: the whole header is gated on APPLE so it stays an empty no-op on the Linux header-hygiene check.
| using pulp::format::au::CocoaViewInfoFiller = bool (*)(void*) |
Cross-TU Cocoa-view hook.
The AU adapter classes are compiled into the shared pulp-format library WITHOUT PULP_AU_GUI, while the Cocoa view factory + its AudioUnitCocoaViewInfo filler live in au_v2_cocoa_view.mm, added per-plugin to the *_AU target WITH PULP_AU_GUI. A compile-time #ifdef in the adapters would therefore always be off. Instead, the GUI module registers its filler here at static-init, and the adapters read it (ungated) from GetProperty(kAudioUnitProperty_CocoaUI).
Null when no GUI module is linked (CLAP / Standalone / headless tests) — the adapters then report no Cocoa view and the host uses its generic view. Without this hook the host is NEVER told the plugin has a custom editor.
|
inlinenoexcept |
Populate AU v2 render-context transport metadata from host callbacks and derive per-block playhead change flags.
The AU v2 SDK only exposes these callbacks through AUBase during render, but keeping the mapping here lets every adapter share one contract and lets tests install HostCallbackInfo without constructing a full AudioComponentInstance.
|
inlinenoexcept |
Maximum number of AUChannelInfo pairs build_channel_info can produce.
Pulp's flexible channel contract allows mono / stereo per main bus (validate_channel_layout constrains every count to {1, 2}), so the worst case is a handful of pairs. The bound itself, kMaxChannelInfoPairs, lives in au_v2_common.hpp so the instrument adapter shares it. Derive the AU kAudioUnitProperty_SupportedNumChannels table from a Pulp PluginDescriptor. Surfaces which (input, output) channel-count pairs the plugin handles so hosts and auval can negotiate a layout instead of guessing.
Reports the channel counts the descriptor ACTUALLY declares (clamped into the supported {1, 2} flex range per validate_channel_layout), so asymmetric configs are not hidden from the host:
Writes at most kMaxChannelInfoPairs entries into out and returns the count. out must point to storage for at least kMaxChannelInfoPairs entries. Pure / allocation-free so the AU SupportedNumChannels override can fill caller-owned storage.
|
inlinenoexcept |
Fill ProcessBusBufferInfo for an AU v2 EFFECT's input buses: the main input (index 0, role Main) plus — when the descriptor declares a second input bus — a Sidechain bus (index 1, role Sidechain).
The side-chain info is emitted INACTIVE. The adapter activates it for a block only after it has pulled audio from the side-chain input element (element 1, see effect_has_sidechain_element); while the host leaves that element unconnected the bus stays inactive and Processor::sidechain_input() returns nullptr, the same "not connected" contract the VST3, CLAP and AU v3 adapters use. The bus->buffer mapping is identical either way.
Pure / allocation-free and unit-testable without an AudioComponentInstance. Names view into desc, which must outlive the filled infos. The adapter sets each view's .active from the live host channel count before process().
|
inlinenoexcept |
Fill one ProcessBusBufferInfo per declared output bus (index 0 = Main, the rest = Aux) from the descriptor.
Bus names are string_views INTO desc, so desc must outlive the filled infos (the render path views the adapter's cached descriptor, whose strings live for the plugin's lifetime).
Pure / allocation-free: the render path uses it to tag each per-block output view so a multi-out processor that overrides process(ProcessBuffers&) sees stable bus identity, and it is unit-testable without a live AU host. Writes at most cap entries and returns the count.
| ContractEditorSession * pulp::format::au::contract_view_session | ( | void * | ns_view | ) |
The session behind an AU v2 contract view (the NSView the contract view factory returned), or null for any other view or once it is invalidated, so an in-process host (automation, tests) can reach the editor and its EditorAutomation.
Defined in au_v2_contract_view.mm. Main thread.
|
noexcept |
Decode a short MIDI status byte and two data bytes into a midi::MidiEvent.
Exposed at namespace scope so the AU-MIDI routing can be unit tested without constructing a real AudioComponentInstance.
Channel voice messages arrive at AUMIDIBase::HandleMIDIEvent already split into inStatus (top nibble, e.g. 0x80, 0xB0) and inChannel (bottom nibble, 0–15). This helper re-combines them into a single status byte so the resulting choc::midi::ShortMessage has a consistent on-the-wire layout regardless of whether the caller already split the channel out.
| inStatus | Status byte (top nibble, bottom nibble optional). |
| inChannel | MIDI channel (0–15). Ignored for system messages. |
| inData1 | Data byte 1 (note number, CC number, etc.). |
| inData2 | Data byte 2 (velocity, CC value, etc.). |
|
inlinenoexcept |
True when an AU v2 unit built from desc exposes a second input element for side-chain audio: the descriptor declares a main input bus plus a second input bus with at least one channel.
AU input elements are positional, so a side chain is only reachable behind a main input; the Processor API exposes exactly one side-chain slot (input bus 1), so further input buses are not surfaced. Shared by the effect and the instrument adapter.
|
inlinenoexcept |
Number of AU input elements an AU v2 effect built from desc exposes: 2 with a side-chain bus (main + side chain), otherwise 1.
An effect always has a main input element, even for a descriptor that declares none.
| std::filesystem::path pulp::format::au::factory_presets_dir_for_binary | ( | const std::filesystem::path & | binary | ) |
Map a plug-in binary's path to the folder holding its factory presets.
Recognises the two Apple bundle shapes a Pulp plug-in is loaded from:
Returns an empty path for a binary that is not inside a bundle — a unit-test executable, a standalone build — so discovery degrades to "no factory presets" instead of scanning an unrelated directory.
| bool pulp::format::au::fill_cocoa_view_info | ( | void * | outData | ) |
Fills an AudioUnitCocoaViewInfo with the Pulp Cocoa view factory's bundle URL + class name.
Defined in au_v2_cocoa_view.mm (PULP_AU_GUI); installed into g_cocoa_view_info_filler at static-init. Returns false if the factory class isn't available. outData must point to an AudioUnitCocoaViewInfo.
| OSStatus pulp::format::au::fill_parameter_clump_name | ( | const state::StateStore & | store, |
| AudioUnitScope | scope, | ||
| void * | out_data ) |
| OSStatus pulp::format::au::fill_parameter_clump_property_info | ( | const state::StateStore & | store, |
| AudioUnitScope | scope, | ||
| UInt32 & | out_size, | ||
| bool & | out_writable ) |
| OSStatus pulp::format::au::fill_parameter_info | ( | const state::StateStore & | store, |
| AudioUnitScope | scope, | ||
| AudioUnitParameterID | param_id, | ||
| AudioUnitParameterInfo & | out_info, | ||
| bool | advertise_value_strings ) |
GetParameterInfo: name, range, and unit.
advertise_value_strings controls kAudioUnitParameterFlag_ValuesHaveStrings for parameters that declare a to_string or explicit value labels. Setting the flag is what makes a host ASK for display text — through GetParameterValueStrings for discrete parameters and kAudioUnitProperty_ParameterStringFromValue / ...ValueFromString for continuous ones. An adapter that does not serve those must pass false, or the host asks a question nothing answers.
| OSStatus pulp::format::au::fill_parameter_list | ( | const state::StateStore & | store, |
| AudioUnitScope | scope, | ||
| AudioUnitParameterID * | out_list, | ||
| UInt32 & | out_count ) |
GetParameterList: report every global-scope parameter ID.
A non-global scope reports zero parameters rather than an error, matching AUBase.
| OSStatus pulp::format::au::fill_parameter_value_strings | ( | const state::StateStore & | store, |
| AudioUnitScope | scope, | ||
| AudioUnitParameterID | param_id, | ||
| CFArrayRef * | out_strings ) |
GetParameterValueStrings: the enumerated display strings for a DISCRETE parameter.
Continuous parameters reach the host through kAudioUnitProperty_ParameterStringFromValue instead and report kAudioUnitErr_InvalidPropertyValue here. A null out_strings is the GetPropertyInfo availability query and returns noErr for a discrete parameter without allocating.
| OSStatus pulp::format::au::fill_supports_mpe | ( | bool | supports_mpe, |
| AudioUnitScope | scope, | ||
| void * | out_data ) |
| OSStatus pulp::format::au::fill_supports_mpe_property_info | ( | bool | supports_mpe, |
| AudioUnitScope | scope, | ||
| UInt32 & | out_size, | ||
| bool & | out_writable ) |
kAudioUnitProperty_SupportsMPE (the v2 bridge of AUv3's supportsMPE).
An MPE-aware host reads it to decide whether to route an MPE zone's per-member-channel stream to this unit at all, so a plug-in with MPE handling wired but this property unanswered is simply never offered MPE input. Report kAudioUnitErr_InvalidProperty when the plug-in did not opt in, which is what a host reads as "no" and matches leaving the property unimplemented.
|
noexcept |
True while this thread is inside a host-originated SetParameter.
The store's UI-push listener fires inline on the writing thread, so it consults this to skip echoing the host's own write back at it.
|
inlinenoexcept |
Number of AU input elements an AU v2 INSTRUMENT built from desc exposes: 0 without a main input bus (the classic synth), 1 with a main input, 2 with a main input and a side chain.
MusicDeviceBase pulls no input itself, so the instrument adapter pulls each of these elements by hand in Render().
|
inlinenoexcept |
Number of AU output ELEMENTS (buses) a multi-output instrument advertises: one per declared output bus, floored at 1 and capped at BusBufferSet::kMaxBuses.
Passed to the MusicDeviceBase base constructor so an AU host (Logic, Live, Cubase) lists each output bus — main plus every aux — and can route them to separate mixer channels.
The cap is load-bearing, not cosmetic: Render() only fills output views up to kMaxOutputBuses (== BusBufferSet::kMaxBuses), so advertising more AU elements than that would materialise output elements the render path never touches — the host would show buses that stay permanently silent. Clamp here so the advertised element count and the routed count agree; the constructor's caller (registry_output_element_count) logs a warning when a descriptor's declared bus count exceeds the cap so the truncation is observable, not silent.
Pure so the multi-output element-count contract is unit-testable without an AudioComponentInstance. Mirrors how VST3 (addAudioOutput per bus) and CLAP (audio-ports per bus) already iterate descriptor().output_buses, and how CLAP caps routed output buses at its own kMaxOutputBuses.
| std::filesystem::path pulp::format::au::loaded_bundle_factory_presets_dir | ( | ) |
factory_presets_dir_for_binary applied to the image this code is linked into.
Empty when that image is not inside a bundle.
| CFArrayRef pulp::format::au::make_midi_output_names | ( | const char * | stream_name | ) |
Build the one-element CFArrayRef a host reads from kAudioUnitProperty_MIDIOutputCallbackInfo — the names of the plugin's MIDI output streams.
The host owns and releases the array, so it is created with a +1 retain.
|
inlinenoexcept |
Build the AU v2 render-path ProcessContext fields that are independent of host callbacks.
offline is the host's kAudioUnitProperty_OfflineRender value (see OfflineRenderProperty): a host doing a faster-than-realtime bounce sets it before rendering, and the block is then reported as ProcessMode::Offline + RenderSpeedHint::FasterThanRealtime, the same mapping the AU v3, VST3, and CLAP adapters use.
| OSStatus pulp::format::au::parameter_string_from_value | ( | const state::StateStore & | store, |
| void * | out_data ) |
kAudioUnitProperty_ParameterStringFromValue.
The host owns and releases outString, so it is created with a +1 retain; inValue == nullptr means "use the parameter's current value".
| OSStatus pulp::format::au::parameter_value_from_string | ( | const state::StateStore & | store, |
| void * | out_data ) |
kAudioUnitProperty_ParameterValueFromString — text entry, string → value.
|
inlinenoexcept |
Resolve the per-channel read pointers of a pulled side-chain buffer list for one render slice.
frame_offset is the slice start within the pulled block (non-zero only while scheduled parameters split a render into slices) and frames the slice length. Writes up to cap pointers and returns the channel count, or 0 when the list is absent, malformed (wrong shape, null or undersized storage) or wider than cap — a malformed side chain is reported as "not connected" rather than handing the processor bad pointers.
Pure / allocation-free. Expects the non-interleaved Float32 layout the side-chain element's ValidFormat enforces (one channel per buffer).
| OSStatus pulp::format::au::restore_pulp_state | ( | state::StateStore & | store, |
| Processor & | processor, | ||
| StateRestoreGate & | gate, | ||
| CFPropertyListRef | plist ) |
Read the pulp-state blob back out of an AU class-info dictionary.
A dictionary without the key restores nothing and succeeds (an AU preset saved by a different plugin version is not an error).
gate is held across the restore so it cannot run while the audio thread is inside Processor::process(). Hosts set class info on the main thread while the unit is initialized and rendering, and Processor::deserialize_plugin_state() is documented as running with the audio thread stopped.
| OSStatus pulp::format::au::save_pulp_state | ( | state::StateStore & | store, |
| Processor & | processor, | ||
| CFPropertyListRef * | out_data ) |
Attach Pulp's serialized state to the AU class-info dictionary the base class produced.
Takes ownership semantics identical to AUBase::SaveState: *out_data is replaced with a dictionary carrying the pulp-state blob.
|
inlinenoexcept |
Default channel count of the side-chain input element, clamped into the adapter's per-bus ceiling.
0 when the descriptor declares no side chain.
|
inlinenoexcept |
| void pulp::format::au::wire_host_parameter_bridge | ( | state::StateStore & | store, |
| AudioUnit | unit, | ||
| state::ListenerToken & | out_listener ) |
Wire the editor → host parameter path: gesture begin/end brackets (so a drag records one clean automation pass) and a value-change notification so the host re-reads through GetParameter.
All three are plain AUEventListenerNotify calls on the editing thread — never AudioUnitSetParameter on ourselves, and never from the render thread.
unit is captured by value, so the callbacks never dereference the adapter after it is gone. The returned listener token must be held for the adapter's lifetime.
|
extern |
|
inlineconstexpr |
Upper bound on AUChannelInfo pairs an adapter advertises through SupportedNumChannels.
Descriptor widths are validated into {1, 2}, so a table is a handful of pairs; the constant is a small fixed size so callers back it with instance storage and never allocate.
|
inlineconstexpr |
RT-safe builder for the MIDIPacketList an AU v2 plugin hands the host via kAudioUnitProperty_MIDIOutputCallback.
Backed by a fixed-size byte buffer so building the list never allocates on the audio thread — the same pre-reserved-storage discipline the MIDI input queues use.
Short channel-voice / system messages (1–3 bytes) and the Processor's output SysEx are merged into a single ascending-sample_offset order before being appended via the CoreMIDI packet-list builders (which write into the caller's buffer). CoreMIDI packet lists are expected time-ordered, so the merge guarantees a SysEx at offset 0 is delivered before a note at offset 64 even though they live in separate sidecars. Each packet carries the Processor's per-event sample_offset, clamped to the current block, as its timestamp — the offset semantics kAudioUnitProperty_MIDIOutputCallback documents.
|
staticconstexpr |
Custom AU property ID the Cocoa view factory queries to obtain the host-side Processor + StateStore pointers.
Fixes the former "dual-Processor" bug where the Cocoa view created a second Processor instance that silently desynchronized from the host's audio-thread Processor. Property is Global scope, read-only.
|
inlineconstexpr |
Element name a host shows for the side-chain input.
Logic labels its "Side Chain" pop-up from the presence of this second input element.
|
inlineconstexpr |
AU input element that carries the side-chain bus of an AU v2 effect.