AudioDeviceManager — persistence + MIDI hub. More...
#include <audio_device_manager.hpp>
Classes | |
| struct | ResolveResult |
| Resolve a stored selection against the live device list. More... | |
| struct | RuntimeTelemetrySnapshot |
| Manager-owned runtime telemetry that combines callback load and backend xrun/overload counters for live tools. More... | |
| struct | DeviceChangeEvent |
| class | DeviceChangeToken |
| RAII subscription token for device-change events. More... | |
| struct | MidiEndpoint |
| struct | MidiEndpointChange |
Public Types | |
| enum class | DeviceChangeKind { Unknown , Added , Removed , Replaced } |
| What kind of device-list mutation occurred. More... | |
| enum class | MidiEndpointChangeKind { Added , Removed } |
| using | MidiHandler = std::function<void(const pulp::midi::MidiEvent&)> |
| using | DeviceChangeHandler = std::function<void(const DeviceChangeEvent&)> |
| using | DefaultDeviceChangeHandler |
| Subscribers learn when the system default output / input changed. | |
| using | SampleRateChangeHandler = std::function<void(double new_rate)> |
| Subscribers learn when the active device's nominal sample rate changed. | |
| using | MidiEndpointChangeHandler = std::function<void(const MidiEndpointChange&)> |
Public Member Functions | |
| AudioDeviceManager () | |
| ~AudioDeviceManager () | |
| AudioDeviceManager (const AudioDeviceManager &)=delete | |
| AudioDeviceManager & | operator= (const AudioDeviceManager &)=delete |
| MidiSubscriptionToken | subscribe_midi (MidiHandler handler) |
| Subscribe handler to incoming MIDI events. | |
| void | dispatch_midi_event (const pulp::midi::MidiEvent &event) |
| Dispatch event to every active subscriber. | |
| std::size_t | midi_subscriber_count () const |
| Number of currently registered subscribers. | |
| void | begin_cpu_measure (int num_frames, float sample_rate) |
| Begin a CPU-load measurement window. | |
| void | end_cpu_measure () |
| End the current CPU-load window. | |
| float | cpu_load () const |
| Smoothed CPU load in [0, +inf). 1.0 == full buffer. | |
| float | peak_cpu_load () const |
| Peak CPU load since last reset_peak_cpu_load(). | |
| AudioProcessLoadSnapshot | process_load_snapshot () const |
| Latest process-load telemetry. | |
| void | reset_peak_cpu_load () |
| Reset the peak tracker. | |
| RuntimeTelemetrySnapshot | runtime_telemetry_snapshot () const |
| DeviceChangeToken | subscribe_device_changes (DeviceChangeHandler handler) |
| Subscribe to device-change events. | |
| std::size_t | device_change_subscriber_count () const |
| Number of currently active device-change subscribers (tests). | |
| void | attach_audio_system (AudioSystem *system) |
| Bind a live AudioSystem whose device-change callback should drive dispatch_device_change(). | |
| void | dispatch_device_change (const DeviceChangeEvent &event) |
| Dispatch a device-change event to subscribers. | |
| void | set_default_device_change_handler (DefaultDeviceChangeHandler handler) |
| Register a single default-device-change handler. | |
| void | dispatch_default_device_change (bool is_input, const std::string &new_device_id) |
| Dispatch a default-device-change to the registered handler. | |
| void | set_sample_rate_change_handler (SampleRateChangeHandler handler) |
| void | dispatch_sample_rate_change (double new_rate) |
| Dispatch a sample-rate-change event to the registered handler. | |
| std::uint64_t | sample_rate_change_count () const |
| Total number of sample-rate changes seen since construction. | |
| double | last_sample_rate () const |
| Last known sample rate (set every dispatch_sample_rate_change). | |
| void | bump_xrun_counter (std::uint64_t delta=1) |
| Increment the manager's internal xrun counter. | |
| std::uint64_t | xrun_count () const |
| Snapshot the current xrun count. Safe from any thread. | |
| void | reset_xrun_counter () |
| Reset the xrun counter to zero. | |
| MidiSubscriptionToken | subscribe_midi_endpoints (MidiEndpointChangeHandler handler) |
| Subscribe to MIDI endpoint add/remove events. | |
| std::size_t | midi_endpoint_subscriber_count () const |
| void | set_midi_endpoints (std::vector< MidiEndpoint > endpoints) |
| Replace the manager's snapshot of live MIDI endpoints. | |
| std::vector< MidiEndpoint > | midi_endpoints () const |
| Current MIDI endpoint snapshot. | |
| 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. | |
| void | refresh_midi_endpoints () |
| Re-enumerate the attached system, publish the result through set_midi_endpoints() and apply the reopen policy. | |
| bool | set_midi_input_enabled (const std::string &id, bool enabled) |
| Enable or disable a MIDI input by identifier. | |
| bool | is_midi_input_enabled (const std::string &id) const |
| True when the user has enabled id, whether or not it is connected. | |
| bool | is_midi_input_open (const std::string &id) const |
| True when id is enabled and currently open. | |
| std::vector< std::string > | enabled_midi_inputs () const |
| Identifiers of every enabled input, in the order they were enabled. | |
| void | bind_settings (pulp::state::ApplicationProperties *props) |
| Load the saved enabled set from props and write every later change to it. | |
| void | latch_close () |
| Latch the manager so subsequent dispatches are no-ops. | |
| bool | is_closed () const noexcept |
| True after latch_close() has been called. | |
Static Public Member Functions | |
| static std::optional< DeviceSelection > | load_selection (const pulp::state::ApplicationProperties &props) |
| Read persisted selection from props.user_settings(). | |
| static bool | save_selection (pulp::state::ApplicationProperties &props, const DeviceSelection &sel) |
| Write sel into props.user_settings() and save() the underlying file. | |
| static ResolveResult | resolve_selection (const DeviceSelection &sel, const std::vector< DeviceInfo > &available, const std::string &fallback_output_id, const std::string &fallback_input_id) |
| 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(). | |
| static std::vector< std::string > | load_enabled_midi_inputs (const pulp::state::ApplicationProperties &props) |
| Read the saved enabled-input identifiers. | |
| 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. | |
Friends | |
| class | MidiSubscriptionToken |
| class | DeviceChangeToken |
AudioDeviceManager — persistence + MIDI hub.
Construct one per application (the standalone host owns one; plugin adapters typically don't). Threading:
Subscribers learn when the system default output / input changed.
is_input == true means the default input device changed; false means default output. The handler receives the new device id (callers query the platform and forward it).
| using pulp::audio::AudioDeviceManager::DeviceChangeHandler = std::function<void(const DeviceChangeEvent&)> |
| using pulp::audio::AudioDeviceManager::MidiEndpointChangeHandler = std::function<void(const MidiEndpointChange&)> |
| using pulp::audio::AudioDeviceManager::MidiHandler = std::function<void(const pulp::midi::MidiEvent&)> |
| using pulp::audio::AudioDeviceManager::SampleRateChangeHandler = std::function<void(double new_rate)> |
Subscribers learn when the active device's nominal sample rate changed.
Hosts typically react by re-preparing their processor graph against the new rate.
|
strong |
What kind of device-list mutation occurred.
Unknown is what CoreAudio's hardware-devices listener delivers — the OS tells us the list changed without saying how. The manager passes the live device snapshot to the subscriber so it can diff against its previous snapshot if it cares about adds / removes. Backends that surface deltas natively (WASAPI IMMNotificationClient) populate Added/Removed.
| Enumerator | |
|---|---|
| Unknown | List mutated; reason not disclosed. |
| Added | A device became available. |
| Removed | A device went away. |
| Replaced | Bulk swap (e.g., USB hub reset). |
|
strong |
| pulp::audio::AudioDeviceManager::AudioDeviceManager | ( | ) |
| pulp::audio::AudioDeviceManager::~AudioDeviceManager | ( | ) |
|
delete |
| void pulp::audio::AudioDeviceManager::attach_audio_system | ( | AudioSystem * | system | ) |
Bind a live AudioSystem whose device-change callback should drive dispatch_device_change().
The manager registers itself via system->set_device_change_callback(...) and re-issues DeviceChangeEvents with the snapshot. Pass nullptr to unbind. Tests can skip this entirely and call dispatch_device_change directly with an injected snapshot.
| void pulp::audio::AudioDeviceManager::attach_midi_system | ( | pulp::midi::MidiSystem * | system, |
| bool | install_port_change_callback = true ) |
Bind the MIDI system the manager enumerates and opens inputs on.
The manager snapshots the port list immediately and, when install_port_change_callback is true, registers itself with MidiSystem::set_port_change_callback so every plug or unplug refreshes the snapshot. A system has one port-change slot: pass false and call refresh_midi_endpoints() from your own callback when something else owns it. The system must outlive the manager or be detached with attach_midi_system(nullptr) first.
| void pulp::audio::AudioDeviceManager::begin_cpu_measure | ( | int | num_frames, |
| float | sample_rate ) |
Begin a CPU-load measurement window.
Mirrors AudioProcessLoadMeasurer::begin() — call at the start of the audio callback.
| void pulp::audio::AudioDeviceManager::bind_settings | ( | pulp::state::ApplicationProperties * | props | ) |
Load the saved enabled set from props and write every later change to it.
Inputs loaded here open as soon as they are present. Pass nullptr to stop persisting. props must outlive the manager or be unbound first, and is only touched from the thread that calls set_midi_input_enabled() or bind_settings().
| void pulp::audio::AudioDeviceManager::bump_xrun_counter | ( | std::uint64_t | delta = 1 | ) |
Increment the manager's internal xrun counter.
Hosts wire this to the active AudioDevice::xrun_count() delta, or call it directly when a backend doesn't surface overload events.
| float pulp::audio::AudioDeviceManager::cpu_load | ( | ) | const |
Smoothed CPU load in [0, +inf). 1.0 == full buffer.
| std::size_t pulp::audio::AudioDeviceManager::device_change_subscriber_count | ( | ) | const |
Number of currently active device-change subscribers (tests).
| void pulp::audio::AudioDeviceManager::dispatch_default_device_change | ( | bool | is_input, |
| const std::string & | new_device_id ) |
Dispatch a default-device-change to the registered handler.
Safe to call from any thread; tests use it to simulate default-device flips. No-op after latch_close().
| void pulp::audio::AudioDeviceManager::dispatch_device_change | ( | const DeviceChangeEvent & | event | ) |
Dispatch a device-change event to subscribers.
Safe to call from any thread; tests use it to simulate hotplug without an AudioSystem. After latch_close() returns, this is a no-op.
| void pulp::audio::AudioDeviceManager::dispatch_midi_event | ( | const pulp::midi::MidiEvent & | event | ) |
Dispatch event to every active subscriber.
Typically invoked from the MIDI input callback (MidiInput::open callback) by the standalone host or plugin glue. Safe to call from any thread.
| void pulp::audio::AudioDeviceManager::dispatch_sample_rate_change | ( | double | new_rate | ) |
Dispatch a sample-rate-change event to the registered handler.
Also bumps a counter so a host can observe how many rate changes the active session has weathered. No-op after latch_close().
| std::vector< std::string > pulp::audio::AudioDeviceManager::enabled_midi_inputs | ( | ) | const |
Identifiers of every enabled input, in the order they were enabled.
| void pulp::audio::AudioDeviceManager::end_cpu_measure | ( | ) |
End the current CPU-load window.
|
noexcept |
True after latch_close() has been called.
| bool pulp::audio::AudioDeviceManager::is_midi_input_enabled | ( | const std::string & | id | ) | const |
True when the user has enabled id, whether or not it is connected.
| bool pulp::audio::AudioDeviceManager::is_midi_input_open | ( | const std::string & | id | ) | const |
True when id is enabled and currently open.
| double pulp::audio::AudioDeviceManager::last_sample_rate | ( | ) | const |
Last known sample rate (set every dispatch_sample_rate_change).
| void pulp::audio::AudioDeviceManager::latch_close | ( | ) |
Latch the manager so subsequent dispatches are no-ops.
Blocks until any in-flight dispatch (MIDI event, device change, default-device change, sample-rate change, endpoint change) has returned. After latch_close() returns, the host can safely destroy the objects that own the subscriber callbacks.
Symmetric to the close()-then-stop() pattern in core/audio::AudioDevice — this is the manager-level latch.
|
static |
Read the saved enabled-input identifiers.
|
static |
Read persisted selection from props.user_settings().
Returns std::nullopt if no audio_device_manager.* keys exist.
| std::size_t pulp::audio::AudioDeviceManager::midi_endpoint_subscriber_count | ( | ) | const |
| std::vector< MidiEndpoint > pulp::audio::AudioDeviceManager::midi_endpoints | ( | ) | const |
Current MIDI endpoint snapshot.
| std::size_t pulp::audio::AudioDeviceManager::midi_subscriber_count | ( | ) | const |
Number of currently registered subscribers.
Mostly used by tests; safe to call from any thread.
|
delete |
| float pulp::audio::AudioDeviceManager::peak_cpu_load | ( | ) | const |
Peak CPU load since last reset_peak_cpu_load().
| AudioProcessLoadSnapshot pulp::audio::AudioDeviceManager::process_load_snapshot | ( | ) | const |
Latest process-load telemetry.
Safe to poll from UI/diagnostic threads; fields are relaxed latest-value counters.
| void pulp::audio::AudioDeviceManager::refresh_midi_endpoints | ( | ) |
Re-enumerate the attached system, publish the result through set_midi_endpoints() and apply the reopen policy.
Safe from any thread, including a MIDI system's notification thread.
| void pulp::audio::AudioDeviceManager::reset_peak_cpu_load | ( | ) |
Reset the peak tracker.
| void pulp::audio::AudioDeviceManager::reset_xrun_counter | ( | ) |
Reset the xrun counter to zero.
|
static |
| RuntimeTelemetrySnapshot pulp::audio::AudioDeviceManager::runtime_telemetry_snapshot | ( | ) | const |
| std::uint64_t pulp::audio::AudioDeviceManager::sample_rate_change_count | ( | ) | const |
Total number of sample-rate changes seen since construction.
|
static |
Write the enabled-input identifiers; an empty list removes the key.
|
static |
Write sel into props.user_settings() and save() the underlying file.
Returns true on successful save.
|
static |
Convenience: convert a DeviceSelection to a DeviceConfig for AudioDevice::open().
Channel counts derive from popcount() of the masks when non-zero; otherwise the caller's default_output_channels / default_input_channels win.
| void pulp::audio::AudioDeviceManager::set_default_device_change_handler | ( | DefaultDeviceChangeHandler | handler | ) |
Register a single default-device-change handler.
Pass nullptr to clear. The manager fans out to a single handler (not a list) because the typical host has exactly one recovery policy.
| void pulp::audio::AudioDeviceManager::set_midi_endpoints | ( | std::vector< MidiEndpoint > | endpoints | ) |
Replace the manager's snapshot of live MIDI endpoints.
Deltas from the previous snapshot dispatch to subscribers as MidiEndpointChange events. Endpoint identity is by id. No-op after latch_close() (state still updates, but no dispatch fires).
| bool pulp::audio::AudioDeviceManager::set_midi_input_enabled | ( | const std::string & | id, |
| bool | enabled ) |
Enable or disable a MIDI input by identifier.
Enabling records the choice (and saves it when settings are bound), then opens the input if it is present. Returns true when the input is open afterwards. An enabled input that is not currently present stays enabled and opens as soon as it appears. Disabling closes the input and removes it from the saved settings. Incoming events from enabled inputs reach subscribe_midi() subscribers, which must not call back into the enable/disable API from the delivering thread (closing an input waits for its callback to return).
| void pulp::audio::AudioDeviceManager::set_sample_rate_change_handler | ( | SampleRateChangeHandler | handler | ) |
|
nodiscard |
Subscribe to device-change events.
Returns an RAII token whose destruction unsubscribes. Safe to call from any thread.
|
nodiscard |
Subscribe handler to incoming MIDI events.
The returned token auto-unsubscribes on destruction. Subscribers are invoked in registration order on whatever thread dispatch_midi_event() runs on.
|
nodiscard |
Subscribe to MIDI endpoint add/remove events.
Returns the same MidiSubscriptionToken flavor as subscribe_midi. The id namespace is separate from MIDI-event subscriptions, so midi_subscriber_count() does not include endpoint listeners.
| std::uint64_t pulp::audio::AudioDeviceManager::xrun_count | ( | ) | const |
Snapshot the current xrun count. Safe from any thread.
|
friend |
|
friend |