Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
audio_device_manager.hpp
Go to the documentation of this file.
1#pragma once
2
31
32#include <pulp/audio/device.hpp>
34#include <pulp/midi/device.hpp>
35#include <pulp/midi/message.hpp>
36
37#include <atomic>
38#include <condition_variable>
39#include <cstdint>
40#include <functional>
41#include <memory>
42#include <mutex>
43#include <optional>
44#include <string>
45#include <unordered_map>
46#include <vector>
47
49
50namespace pulp::audio {
51
61 std::string output_device;
62 std::string input_device;
63 double sample_rate = 0.0;
64 int buffer_size = 0;
65 uint64_t output_channel_mask = 0;
66 uint64_t input_channel_mask = 0;
67};
68
72namespace adm_keys {
73constexpr const char* kPrefix = "audio_device_manager.";
74constexpr const char* kOutputDevice = "audio_device_manager.output_device";
75constexpr const char* kInputDevice = "audio_device_manager.input_device";
76constexpr const char* kSampleRate = "audio_device_manager.sample_rate";
77constexpr const char* kBufferSize = "audio_device_manager.buffer_size";
78constexpr const char* kOutputChannelMask = "audio_device_manager.output_channel_mask";
79constexpr const char* kInputChannelMask = "audio_device_manager.input_channel_mask";
82constexpr const char* kEnabledMidiInputs = "audio_device_manager.enabled_midi_inputs";
83} // namespace adm_keys
84
86
92public:
99
100 bool active() const noexcept { return id_ != 0 && !manager_.expired(); }
101
103 void reset() noexcept;
104
105private:
106 friend class AudioDeviceManager;
107 MidiSubscriptionToken(std::weak_ptr<void> manager_ptr,
108 AudioDeviceManager* manager_raw,
109 uint64_t id) noexcept
110 : manager_(std::move(manager_ptr)), manager_raw_(manager_raw), id_(id) {}
111
112 std::weak_ptr<void> manager_;
113 AudioDeviceManager* manager_raw_ = nullptr;
114 uint64_t id_ = 0;
115};
116
133public:
134 using MidiHandler = std::function<void(const pulp::midi::MidiEvent&)>;
135
138
141
142 // ── Persistence ─────────────────────────────────────────────────
143
146 static std::optional<DeviceSelection> load_selection(
148
152 const DeviceSelection& sel);
153
167 const DeviceSelection& sel,
168 const std::vector<DeviceInfo>& available,
169 const std::string& fallback_output_id,
170 const std::string& fallback_input_id);
171
177 const DeviceSelection& sel,
178 int default_output_channels = 2,
179 int default_input_channels = 0);
180
181 // ── MIDI hub ────────────────────────────────────────────────────
182
188
194
197 std::size_t midi_subscriber_count() const;
198
199 // ── CPU load ────────────────────────────────────────────────────
200
204 void begin_cpu_measure(int num_frames, float sample_rate);
205
208
210 float cpu_load() const;
211
213 float peak_cpu_load() const;
214
218
221
228
230
231 // ── Lifecycle / hotplug / recovery (1.2b) ───────────────────────
232
246
249 std::vector<DeviceInfo> devices;
250 std::string device_id;
251 };
252
253 using DeviceChangeHandler = std::function<void(const DeviceChangeEvent&)>;
254
259 public:
260 DeviceChangeToken() = default;
266
267 bool active() const noexcept {
268 return id_ != 0 && !manager_.expired();
269 }
270 void reset() noexcept;
271
272 private:
273 friend class AudioDeviceManager;
274 DeviceChangeToken(std::weak_ptr<void> manager_ptr,
275 AudioDeviceManager* manager_raw,
276 uint64_t id) noexcept
277 : manager_(std::move(manager_ptr)), manager_raw_(manager_raw), id_(id) {}
278
279 std::weak_ptr<void> manager_;
280 AudioDeviceManager* manager_raw_ = nullptr;
281 uint64_t id_ = 0;
282 };
283
287
290
298
303
309 std::function<void(bool is_input, const std::string& new_device_id)>;
310
315
320 const std::string& new_device_id);
321
325 using SampleRateChangeHandler = std::function<void(double new_rate)>;
326
328
333 void dispatch_sample_rate_change(double new_rate);
334
336 std::uint64_t sample_rate_change_count() const;
337
339 double last_sample_rate() const;
340
341 // ── xrun tracking ──────────────────────────────────────────────
342
346 void bump_xrun_counter(std::uint64_t delta = 1);
347
349 std::uint64_t xrun_count() const;
350
353
354 // ── MIDI endpoint tracking ─────────────────────────────────────
355
357 std::string id;
358 std::string name;
359 bool is_input = false;
360 };
361
366
371
372 using MidiEndpointChangeHandler = std::function<void(const MidiEndpointChange&)>;
373
380
382
388 void set_midi_endpoints(std::vector<MidiEndpoint> endpoints);
389
391 std::vector<MidiEndpoint> midi_endpoints() const;
392
393 // ── Enabled MIDI inputs and reconnection ───────────────────────
394
404 bool install_port_change_callback = true);
405
410
420 bool set_midi_input_enabled(const std::string& id, bool enabled);
421
423 bool is_midi_input_enabled(const std::string& id) const;
424
426 bool is_midi_input_open(const std::string& id) const;
427
429 std::vector<std::string> enabled_midi_inputs() const;
430
437
439 static std::vector<std::string>
441
444 const std::vector<std::string>& ids);
445
446 // ── Concurrent-callback shutdown ───────────────────────────────
447
457
459 bool is_closed() const noexcept;
460
461private:
463 friend class DeviceChangeToken;
464
465 void unsubscribe_midi(uint64_t id) noexcept;
466 void unsubscribe_device_change(uint64_t id) noexcept;
467 void unsubscribe_midi_endpoint(uint64_t id) noexcept;
468 void reconcile_midi_inputs();
469
473 struct MidiPortLink;
474
475 // RAII guard used by every dispatcher; blocks latch_close() until
476 // all in-flight dispatches return.
477 class DispatchGuard;
478
481 std::shared_ptr<void> lifetime_;
482
491 std::atomic<uint64_t> next_token_id_{1};
492
493 mutable std::mutex midi_mu_;
494 std::unordered_map<uint64_t, MidiHandler> midi_subs_;
495
496 mutable std::mutex device_change_mu_;
497 std::unordered_map<uint64_t, DeviceChangeHandler> device_change_subs_;
498
499 mutable std::mutex default_dev_mu_;
500 DefaultDeviceChangeHandler default_dev_handler_;
501
502 mutable std::mutex sr_mu_;
503 SampleRateChangeHandler sr_handler_;
504 std::atomic<std::uint64_t> sr_change_count_{0};
505 std::atomic<double> last_sr_{0.0};
506
507 std::atomic<std::uint64_t> xrun_counter_{0};
508
509 mutable std::mutex midi_ep_mu_;
510 std::unordered_map<uint64_t, MidiEndpointChangeHandler> midi_ep_subs_;
511 std::vector<MidiEndpoint> midi_endpoints_;
512 bool endpoints_known_ = false;
513
514 AudioSystem* attached_system_ = nullptr;
515
520 struct EnabledInput {
521 std::string id;
522 std::unique_ptr<pulp::midi::MidiInput> device;
523 };
524 mutable std::mutex input_mu_;
525 std::vector<EnabledInput> enabled_inputs_;
526 pulp::midi::MidiSystem* midi_system_ = nullptr;
527 std::shared_ptr<MidiPortLink> midi_link_;
532 std::recursive_mutex refresh_mu_;
533 bool midi_port_callback_installed_ = false;
534 pulp::state::ApplicationProperties* settings_ = nullptr;
535
536 AudioProcessLoadMeasurer load_;
537
541 std::atomic<bool> closed_{false};
542 mutable std::mutex latch_mu_;
543 std::condition_variable latch_cv_;
544 int in_flight_ = 0;
545};
546
547} // namespace pulp::audio
RAII subscription token for device-change events.
Definition audio_device_manager.hpp:258
DeviceChangeToken & operator=(const DeviceChangeToken &)=delete
DeviceChangeToken(DeviceChangeToken &&other) noexcept
bool active() const noexcept
Definition audio_device_manager.hpp:267
friend class AudioDeviceManager
Definition audio_device_manager.hpp:273
DeviceChangeToken & operator=(DeviceChangeToken &&other) noexcept
DeviceChangeToken(const DeviceChangeToken &)=delete
AudioDeviceManager — persistence + MIDI hub.
Definition audio_device_manager.hpp:132
void latch_close()
Latch the manager so subsequent dispatches are no-ops.
MidiSubscriptionToken subscribe_midi(MidiHandler handler)
Subscribe handler to incoming MIDI events.
std::size_t device_change_subscriber_count() const
Number of currently active device-change subscribers (tests).
bool is_closed() const noexcept
True after latch_close() has been called.
std::vector< std::string > enabled_midi_inputs() const
Identifiers of every enabled input, in the order they were enabled.
double last_sample_rate() const
Last known sample rate (set every dispatch_sample_rate_change).
std::function< void(const DeviceChangeEvent &)> DeviceChangeHandler
Definition audio_device_manager.hpp:253
static std::optional< DeviceSelection > load_selection(const pulp::state::ApplicationProperties &props)
Read persisted selection from props.user_settings().
void reset_peak_cpu_load()
Reset the peak tracker.
void bind_settings(pulp::state::ApplicationProperties *props)
Load the saved enabled set from props and write every later change to it.
void attach_midi_system(pulp::midi::MidiSystem *system, bool install_port_change_callback=true)
Bind the MIDI system the manager enumerates and opens inputs on.
MidiEndpointChangeKind
Definition audio_device_manager.hpp:362
friend class MidiSubscriptionToken
Definition audio_device_manager.hpp:462
static std::vector< std::string > load_enabled_midi_inputs(const pulp::state::ApplicationProperties &props)
Read the saved enabled-input identifiers.
std::function< void(double new_rate)> SampleRateChangeHandler
Subscribers learn when the active device's nominal sample rate changed.
Definition audio_device_manager.hpp:325
bool set_midi_input_enabled(const std::string &id, bool enabled)
Enable or disable a MIDI input by identifier.
void set_default_device_change_handler(DefaultDeviceChangeHandler handler)
Register a single default-device-change handler.
AudioDeviceManager & operator=(const AudioDeviceManager &)=delete
void set_midi_endpoints(std::vector< MidiEndpoint > endpoints)
Replace the manager's snapshot of live MIDI endpoints.
static DeviceConfig selection_to_config(const DeviceSelection &sel, int default_output_channels=2, int default_input_channels=0)
Convenience: convert a DeviceSelection to a DeviceConfig for AudioDevice::open().
void dispatch_default_device_change(bool is_input, const std::string &new_device_id)
Dispatch a default-device-change to the registered handler.
void dispatch_device_change(const DeviceChangeEvent &event)
Dispatch a device-change event to subscribers.
void bump_xrun_counter(std::uint64_t delta=1)
Increment the manager's internal xrun counter.
void end_cpu_measure()
End the current CPU-load window.
DeviceChangeKind
What kind of device-list mutation occurred.
Definition audio_device_manager.hpp:240
@ Unknown
List mutated; reason not disclosed.
Definition audio_device_manager.hpp:241
@ Removed
A device went away.
Definition audio_device_manager.hpp:243
@ Replaced
Bulk swap (e.g., USB hub reset).
Definition audio_device_manager.hpp:244
@ Added
A device became available.
Definition audio_device_manager.hpp:242
void reset_xrun_counter()
Reset the xrun counter to zero.
AudioDeviceManager(const AudioDeviceManager &)=delete
std::vector< MidiEndpoint > midi_endpoints() const
Current MIDI endpoint snapshot.
void set_sample_rate_change_handler(SampleRateChangeHandler handler)
std::size_t midi_subscriber_count() const
Number of currently registered subscribers.
MidiSubscriptionToken subscribe_midi_endpoints(MidiEndpointChangeHandler handler)
Subscribe to MIDI endpoint add/remove events.
static ResolveResult resolve_selection(const DeviceSelection &sel, const std::vector< DeviceInfo > &available, const std::string &fallback_output_id, const std::string &fallback_input_id)
std::function< void(bool is_input, const std::string &new_device_id)> DefaultDeviceChangeHandler
Subscribers learn when the system default output / input changed.
Definition audio_device_manager.hpp:308
std::uint64_t xrun_count() const
Snapshot the current xrun count. Safe from any thread.
float cpu_load() const
Smoothed CPU load in [0, +inf). 1.0 == full buffer.
bool is_midi_input_open(const std::string &id) const
True when id is enabled and currently open.
std::uint64_t sample_rate_change_count() const
Total number of sample-rate changes seen since construction.
void begin_cpu_measure(int num_frames, float sample_rate)
Begin a CPU-load measurement window.
std::function< void(const pulp::midi::MidiEvent &)> MidiHandler
Definition audio_device_manager.hpp:134
bool is_midi_input_enabled(const std::string &id) const
True when the user has enabled id, whether or not it is connected.
std::function< void(const MidiEndpointChange &)> MidiEndpointChangeHandler
Definition audio_device_manager.hpp:372
void attach_audio_system(AudioSystem *system)
Bind a live AudioSystem whose device-change callback should drive dispatch_device_change().
void refresh_midi_endpoints()
Re-enumerate the attached system, publish the result through set_midi_endpoints() and apply the reope...
DeviceChangeToken subscribe_device_changes(DeviceChangeHandler handler)
Subscribe to device-change events.
std::size_t midi_endpoint_subscriber_count() const
void dispatch_midi_event(const pulp::midi::MidiEvent &event)
Dispatch event to every active subscriber.
static bool save_selection(pulp::state::ApplicationProperties &props, const DeviceSelection &sel)
Write sel into props.user_settings() and save() the underlying file.
AudioProcessLoadSnapshot process_load_snapshot() const
Latest process-load telemetry.
void dispatch_sample_rate_change(double new_rate)
Dispatch a sample-rate-change event to the registered handler.
RuntimeTelemetrySnapshot runtime_telemetry_snapshot() const
static bool save_enabled_midi_inputs(pulp::state::ApplicationProperties &props, const std::vector< std::string > &ids)
Write the enabled-input identifiers; an empty list removes the key.
float peak_cpu_load() const
Peak CPU load since last reset_peak_cpu_load().
Definition device.hpp:149
MidiSubscriptionToken(const MidiSubscriptionToken &)=delete
MidiSubscriptionToken & operator=(MidiSubscriptionToken &&other) noexcept
bool active() const noexcept
Definition audio_device_manager.hpp:100
MidiSubscriptionToken & operator=(const MidiSubscriptionToken &)=delete
void reset() noexcept
Reset and unsubscribe early.
friend class AudioDeviceManager
Definition audio_device_manager.hpp:106
MidiSubscriptionToken(MidiSubscriptionToken &&other) noexcept
Definition device.hpp:52
Application-level properties with platform-standard paths.
Definition properties_file.hpp:72
Real-time CPU load measurement for audio callbacks.
Keys used under ApplicationProperties::user_settings().
Definition audio_device_manager.hpp:72
constexpr const char * kInputChannelMask
Definition audio_device_manager.hpp:79
constexpr const char * kEnabledMidiInputs
Newline-separated identifiers of the MIDI inputs the user enabled.
Definition audio_device_manager.hpp:82
constexpr const char * kSampleRate
Definition audio_device_manager.hpp:76
constexpr const char * kBufferSize
Definition audio_device_manager.hpp:77
constexpr const char * kOutputChannelMask
Definition audio_device_manager.hpp:78
constexpr const char * kOutputDevice
Definition audio_device_manager.hpp:74
constexpr const char * kInputDevice
Definition audio_device_manager.hpp:75
constexpr const char * kPrefix
Definition audio_device_manager.hpp:73
Definition analyzer_provider.hpp:12
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
STL namespace.
Definition audio_device_manager.hpp:247
DeviceChangeKind kind
Definition audio_device_manager.hpp:248
std::string device_id
Affected id (when kind != Unknown).
Definition audio_device_manager.hpp:250
std::vector< DeviceInfo > devices
Snapshot at the time of the change.
Definition audio_device_manager.hpp:249
Definition audio_device_manager.hpp:367
MidiEndpointChangeKind kind
Definition audio_device_manager.hpp:368
MidiEndpoint endpoint
Definition audio_device_manager.hpp:369
Definition audio_device_manager.hpp:356
std::string name
Definition audio_device_manager.hpp:358
bool is_input
true == input; false == output.
Definition audio_device_manager.hpp:359
std::string id
Definition audio_device_manager.hpp:357
Resolve a stored selection against the live device list.
Definition audio_device_manager.hpp:161
bool fallback_used_input
Definition audio_device_manager.hpp:164
DeviceSelection resolved
Definition audio_device_manager.hpp:162
bool fallback_used_output
Definition audio_device_manager.hpp:163
Manager-owned runtime telemetry that combines callback load and backend xrun/overload counters for li...
Definition audio_device_manager.hpp:224
AudioProcessLoadSnapshot process_load
Definition audio_device_manager.hpp:225
std::uint64_t xrun_count
Definition audio_device_manager.hpp:226
Snapshot published by AudioProcessLoadMeasurer.
Definition load_measurer.hpp:25
Definition device.hpp:35
Persisted audio-device selection.
Definition audio_device_manager.hpp:60
uint64_t output_channel_mask
Definition audio_device_manager.hpp:65
double sample_rate
0 = use default
Definition audio_device_manager.hpp:63
std::string input_device
Definition audio_device_manager.hpp:62
std::string output_device
Definition audio_device_manager.hpp:61
int buffer_size
0 = use default
Definition audio_device_manager.hpp:64
uint64_t input_channel_mask
Definition audio_device_manager.hpp:66
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32