9#include <AudioUnitSDK/AUMIDIEffectBase.h>
10#include <AudioToolbox/AudioUnitProperties.h>
65 AUChannelInfo* out)
noexcept {
66 if (!desc.supported_bus_layouts.empty()) {
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;
77 if (duplicate)
continue;
79 out[count++] = {
static_cast<SInt16
>(in_w),
static_cast<SInt16
>(out_w)};
85 auto clamp_width = [](
int n) ->
int {
87 return n >= 2 ? 2 : 1;
89 const int in_w = clamp_width(desc.default_input_channels());
90 const int out_w = clamp_width(desc.default_output_channels());
98 !desc.input_buses.empty() && desc.input_buses[0].optional;
99 if (in_w == 0 || optional_input) {
103 out[count].inChannels = 0;
104 out[count].outChannels =
static_cast<SInt16
>(w);
110 }
else if (in_w == out_w) {
113 out[count].inChannels =
static_cast<SInt16
>(w);
114 out[count].outChannels =
static_cast<SInt16
>(w);
119 out[count].inChannels =
static_cast<SInt16
>(in_w);
120 out[count].outChannels =
static_cast<SInt16
>(out_w);
140 return desc.input_buses.size() > 1 && desc.input_buses[0].default_channels > 0 &&
141 desc.input_buses[1].default_channels > 0;
156 if (desc.input_buses.empty() || desc.input_buses[0].default_channels <= 0)
165 const auto declared = desc.input_buses[1].default_channels;
167 return static_cast<UInt32
>(declared > cap ? cap : declared);
186 std::size_t cap)
noexcept {
187 if (cap == 0)
return 0;
189 out[n].name = desc.input_buses.empty()
190 ? std::string_view{
"Audio In"}
191 : std::string_view{desc.input_buses[0].name};
195 out[n].declared_channels = desc.default_input_channels();
198 out[n].optional = desc.default_input_channels() == 0 ||
199 (!desc.input_buses.empty() && desc.input_buses[0].optional);
200 out[n].active =
true;
202 if (desc.input_buses.size() > 1 && n < cap) {
203 out[n].name = desc.input_buses[1].name;
207 out[n].declared_channels = desc.input_buses[1].default_channels;
208 out[n].optional =
true;
209 out[n].active =
false;
226 UInt32 expected_channels,
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)
241 for (UInt32 ch = 0; ch < expected_channels; ++ch) {
242 out[ch] =
static_cast<const float*
>(list->mBuffers[ch].mData) + frame_offset;
244 return expected_channels;
256 AudioUnitParameterID* outParameterList,
257 UInt32& outNumParameters)
override;
259 AudioUnitParameterID inParameterID,
260 AudioUnitParameterInfo& outParameterInfo)
override;
262 AudioUnitParameterID inParameterID,
263 CFArrayRef* outStrings)
override;
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;
277 UInt32 num_events)
override;
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;
309 OSStatus
Render(AudioUnitRenderActionFlags& ioActionFlags,
310 const AudioTimeStamp& inTimeStamp, UInt32 nFrames)
override;
314 bool ValidFormat(AudioUnitScope inScope, AudioUnitElement inElement,
315 const AudioStreamBasicDescription& inNewFormat)
override;
320 const AudioStreamBasicDescription& inPrevFormat,
321 const AudioStreamBasicDescription& inNewFormat)
override;
324 const AudioBufferList& inBuffer,
325 AudioBufferList& outBuffer,
326 UInt32 inFramesToProcess)
override;
344 return factory_presets_;
347 OSStatus
SaveState(CFPropertyListRef* outData)
override;
356 return param_events_monitor_;
363 void* user_data)
override;
365 UInt32 start_frame_in_buffer,
367 UInt32 total_buffer_frames)
override;
390 UInt8 inData1, UInt8 inData2,
391 UInt32 inStartFrame)
override;
397 OSStatus
HandleSysEx(
const UInt8* inData, UInt32 inLength)
override;
411 bool plugin_produces_midi() const noexcept;
417 state::StateStore store_;
434 runtime::AliveToken owner_alive_;
454 bool scheduled_slice_active_ =
false;
457 bool sample_accurate_params_ =
false;
462 bool slice_scheduled_params_ =
false;
468 struct ScheduledRamp {
469 std::uint32_t index = 0;
470 bool superseded =
false;
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_;
479 struct LatestScheduledEvent {
481 std::int32_t
event = -1;
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_;
494 static constexpr std::size_t kMaxSysexPerBlock = 64;
495 static constexpr std::size_t kMaxSysexPayloadBytes = 512;
501 HostQuirks host_quirks_{};
512 std::vector<const float*> input_ptrs_;
513 std::vector<float*> output_ptrs_;
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_{};
531 detail::PlayheadSnapshot playhead_prev_{};
536 bool warming_render_path_ =
false;
537 void warm_render_path();
541 OfflineRenderProperty offline_render_;
552 runtime::SpscQueue<midi::MidiEvent, 1024> midi_in_queue_;
554 std::array<uint8_t, 512> bytes{};
557 runtime::SpscQueue<SysexChunk, 32> sysex_in_queue_;
566 MidiOutputCallbackPublisher midi_output_callback_;
568 MidiOutputPacketBuilder midi_out_packet_builder_;
572 std::array<AUChannelInfo, kMaxChannelInfoPairs> channel_info_{};
The shared adapter-boundary core.
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
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