18#include <AudioUnitSDK/AUBase.h>
21#include <AudioToolbox/AudioUnitProperties.h>
23#include <AudioUnit/AudioUnit.h>
24#include <CoreFoundation/CoreFoundation.h>
25#include <CoreMIDI/MIDIServices.h>
26#include <mach/mach_time.h>
28#include <pulp/format/detail/host_sample_position.hpp>
32#include <pulp/format/detail/midi_out_offset.hpp>
33#include <pulp/format/detail/playhead_diff.hpp>
123 uint8_t inData2)
noexcept;
133 bool offline =
false) noexcept {
164 bool& out_writable)
const noexcept {
165 if (scope != kAudioUnitScope_Global)
return kAudioUnitErr_InvalidScope;
166 out_size =
sizeof(UInt32);
171 OSStatus
get(AudioUnitScope scope,
void* out_data)
const noexcept {
172 if (scope != kAudioUnitScope_Global)
return kAudioUnitErr_InvalidScope;
173 if (!out_data)
return kAudioUnitErr_InvalidPropertyValue;
174 *
static_cast<UInt32*
>(out_data) =
offline() ? 1u : 0u;
178 OSStatus
set(AudioUnitScope scope,
const void* in_data,
179 UInt32 in_size)
noexcept {
180 if (scope != kAudioUnitScope_Global)
return kAudioUnitErr_InvalidScope;
181 if (!in_data || in_size <
sizeof(UInt32))
182 return kAudioUnitErr_InvalidPropertyValue;
183 offline_.store(*
static_cast<const UInt32*
>(in_data) != 0,
184 std::memory_order_release);
185 written_since_session_start_.store(
true, std::memory_order_release);
194 if (!written_since_session_start_.exchange(
false,
195 std::memory_order_acq_rel))
196 offline_.store(
false, std::memory_order_release);
201 return offline_.load(std::memory_order_acquire);
205 std::atomic<bool> offline_{
false};
206 std::atomic<bool> written_since_session_start_{
false};
216 const ausdk::AUBase& unit,
217 detail::PlayheadSnapshot& previous)
noexcept {
220 if (unit.CallHostBeatAndTempo(&beat, &tempo) == noErr) {
221 ctx.position_beats = beat;
224 ctx.tempo_bpm = tempo;
229 UInt32 delta_samples = 0;
230 Float32 ts_num = 0.0f;
232 Float64 current_measure_downbeat = 0.0;
233 if (unit.CallHostMusicalTimeLocation(&delta_samples, &ts_num, &ts_denom,
234 ¤t_measure_downbeat) == noErr) {
235 if (ts_num > 0.0f && ts_denom > 0) {
236 ctx.time_sig_numerator =
static_cast<int>(ts_num);
237 ctx.time_sig_denominator =
static_cast<int>(ts_denom);
242 Boolean is_playing =
false;
243 Boolean transport_state_changed =
false;
244 Float64 current_sample_in_timeline = 0.0;
245 Boolean is_cycling =
false;
246 Float64 cycle_start = 0.0;
247 Float64 cycle_end = 0.0;
248 if (unit.CallHostTransportState(&is_playing, &transport_state_changed,
249 ¤t_sample_in_timeline, &is_cycling,
250 &cycle_start, &cycle_end) == noErr) {
251 ctx.is_playing = (is_playing != 0);
252 ctx.is_looping = (is_cycling != 0);
255 if (
const auto position = detail::host_sample_position(current_sample_in_timeline)) {
256 ctx.position_samples = *position;
259 if (ctx.is_looping) {
260 ctx.loop_start_beats = cycle_start;
261 ctx.loop_end_beats = cycle_end;
266 static mach_timebase_info_data_t
timebase = {0, 0};
269 const uint64_t now = mach_absolute_time();
270 ctx.host_time_ns =
static_cast<int64_t
>(
275 detail::derive_bar_from_beats(ctx);
280 detail::compute_playhead_changes(
281 ctx, previous, detail::TransportDiffMode::FieldValidity);
285 double sample_rate)
noexcept {
286 if (tail_samples < 0)
return std::numeric_limits<Float64>::infinity();
287 if (tail_samples == 0 || sample_rate <= 0.0)
return 0.0;
288 return static_cast<Float64
>(tail_samples) / sample_rate;
322 alignas(MIDIPacketList) std::array<std::uint8_t, kBufferBytes>
storage{};
338 std::uint32_t frame_count)
noexcept {
350 std::array<Ref, kMaxOutputEvents> refs;
351 std::size_t n_refs = 0;
352 std::uint32_t order = 0;
354 const auto clamp_offset = [&](std::int32_t off) -> std::int32_t {
358 return detail::au_output_offset(off, frame_count);
361 std::uint32_t ev_index = 0;
362 for (
const auto& e : midi_out) {
363 const std::uint32_t len = e.message.length();
364 if (len != 0 && len <= 3) {
365 if (n_refs < refs.size()) {
366 refs[n_refs++] = Ref{clamp_offset(e.sample_offset),
367 order++, ev_index,
false};
374 const auto& sysex = midi_out.sysex();
375 for (std::uint32_t i = 0; i < sysex.size(); ++i) {
376 if (sysex[i].data.empty())
continue;
377 if (n_refs < refs.size()) {
378 refs[n_refs++] = Ref{clamp_offset(sysex[i].sample_offset),
387 for (std::size_t i = 1; i < n_refs; ++i) {
390 while (j > 0 && (refs[j - 1].offset > key.offset ||
391 (refs[j - 1].offset == key.offset &&
392 refs[j - 1].order > key.order))) {
393 refs[j] = refs[j - 1];
399 auto* list =
reinterpret_cast<MIDIPacketList*
>(
storage.data());
400 MIDIPacket* cur = MIDIPacketListInit(list);
403 for (std::size_t i = 0; i < n_refs; ++i) {
404 const Ref& r = refs[i];
405 const auto ts =
static_cast<MIDITimeStamp
>(r.offset);
406 MIDIPacket* next =
nullptr;
408 const auto& sx = sysex[r.index];
410 sx.data.size(), sx.data.data());
414 const std::uint32_t len = e.message.length();
415 std::uint8_t bytes[3] = {0, 0, 0};
416 const auto* d = e.data();
417 for (std::uint32_t b = 0; b < len; ++b) bytes[b] = d[b];
418 next = MIDIPacketListAdd(list,
kBufferBytes, cur, ts, len, bytes);
420 if (next ==
nullptr) {
428 return wrote ? list :
nullptr;
441 AudioUnitScope scope,
442 AudioUnitParameterID* out_list,
455 AudioUnitScope scope,
456 AudioUnitParameterID param_id,
457 AudioUnitParameterInfo& out_info,
458 bool advertise_value_strings);
461 AudioUnitScope scope,
504 OSStatus
property_info(AudioUnitScope scope, AudioUnitElement element, UInt32& out_size,
505 bool& out_writable)
const;
506 OSStatus
get(AudioUnitScope scope, AudioUnitElement element,
void* out_data)
const;
523 void* block_ =
nullptr;
527 AudioUnitScope scope,
537 AudioUnitScope scope,
538 AudioUnitParameterID param_id,
539 CFArrayRef* out_strings);
578 state::ListenerToken& out_listener);
612 std::unique_ptr<Impl> impl_;
622 CFPropertyListRef* out_data);
636 CFPropertyListRef plist);
660 OSStatus
publish(
const void* in_data, UInt32 in_data_size)
noexcept;
664 OSStatus
reflect(
void* out_data)
const noexcept;
669 return active_.load(std::memory_order_acquire);
673 std::array<Pair, 2> slots_{};
674 std::atomic<std::uint8_t> write_slot_{0};
675 std::atomic<const Pair*> active_{
nullptr};
The factory-preset surface both Audio Unit adapters serve to the host.
void clear_after_render_resources() const noexcept
Off the render thread, when render resources go away (Cleanup): publish removal and wait until every ...
~RenderContextObserverProperty()
void bind(Processor *processor)
Bind the processor (call once it exists, before the host can query).
AudioWorkgroupClient * client() const noexcept
Definition au_v2_common.hpp:517
void prepare_for_render() const noexcept
Render entry guard: idempotent, realtime safe.
OSStatus property_info(AudioUnitScope scope, AudioUnitElement element, UInt32 &out_size, bool &out_writable) const
RenderContextObserverProperty(const RenderContextObserverProperty &)=delete
OSStatus get(AudioUnitScope scope, AudioUnitElement element, void *out_data) const
RenderContextObserverProperty & operator=(const RenderContextObserverProperty &)=delete
RenderContextObserverProperty()=default
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
std::shared_ptr< State > Handle
Definition alive_token.hpp:53
Centralized parameter storage with lock-free value access.
Definition store.hpp:164
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
Definition beat_division.hpp:11
Proving the audio thread is outside process() while plugin state is restored under it.
Process context — passed every audio callback with transport state.
Definition process_context.hpp:89
int num_samples
Definition process_context.hpp:91
RenderSpeedHint render_speed_hint
Advisory render-speed category for hosts that know whether the current pass is live,...
Definition process_context.hpp:121
double sample_rate
Definition process_context.hpp:90
ProcessMode process_mode
Runtime mode for this block.
Definition process_context.hpp:96
Definition au_v2_common.hpp:67
pulp::runtime::AliveToken::Handle owner_alive
Definition au_v2_common.hpp:70
pulp::format::Processor * processor
Definition au_v2_common.hpp:68
pulp::format::ContractSessionRegistry * contract_sessions
Contract editors register here (contract_editor_session.hpp) so the AU instance invalidates them befo...
Definition au_v2_common.hpp:73
pulp::state::StateStore * store
Definition au_v2_common.hpp:69
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32