Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::audio::AudioDeviceManager Class Reference

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

Detailed Description

AudioDeviceManager — persistence + MIDI hub.

Construct one per application (the standalone host owns one; plugin adapters typically don't). Threading:

Member Typedef Documentation

◆ DefaultDeviceChangeHandler

Initial value:
std::function<void(bool is_input, const std::string& new_device_id)>

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).

◆ DeviceChangeHandler

◆ MidiEndpointChangeHandler

◆ MidiHandler

◆ SampleRateChangeHandler

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.

Member Enumeration Documentation

◆ DeviceChangeKind

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).

◆ MidiEndpointChangeKind

Enumerator
Added 
Removed 

Constructor & Destructor Documentation

◆ AudioDeviceManager() [1/2]

pulp::audio::AudioDeviceManager::AudioDeviceManager ( )

◆ ~AudioDeviceManager()

pulp::audio::AudioDeviceManager::~AudioDeviceManager ( )

◆ AudioDeviceManager() [2/2]

pulp::audio::AudioDeviceManager::AudioDeviceManager ( const AudioDeviceManager & )
delete

Member Function Documentation

◆ attach_audio_system()

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.

◆ attach_midi_system()

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.

◆ begin_cpu_measure()

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.

◆ bind_settings()

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().

◆ bump_xrun_counter()

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.

◆ cpu_load()

float pulp::audio::AudioDeviceManager::cpu_load ( ) const

Smoothed CPU load in [0, +inf). 1.0 == full buffer.

◆ device_change_subscriber_count()

std::size_t pulp::audio::AudioDeviceManager::device_change_subscriber_count ( ) const

Number of currently active device-change subscribers (tests).

◆ dispatch_default_device_change()

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().

◆ dispatch_device_change()

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.

◆ dispatch_midi_event()

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.

◆ dispatch_sample_rate_change()

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().

◆ enabled_midi_inputs()

std::vector< std::string > pulp::audio::AudioDeviceManager::enabled_midi_inputs ( ) const

Identifiers of every enabled input, in the order they were enabled.

◆ end_cpu_measure()

void pulp::audio::AudioDeviceManager::end_cpu_measure ( )

End the current CPU-load window.

◆ is_closed()

bool pulp::audio::AudioDeviceManager::is_closed ( ) const
noexcept

True after latch_close() has been called.

◆ is_midi_input_enabled()

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.

◆ is_midi_input_open()

bool pulp::audio::AudioDeviceManager::is_midi_input_open ( const std::string & id) const

True when id is enabled and currently open.

◆ last_sample_rate()

double pulp::audio::AudioDeviceManager::last_sample_rate ( ) const

Last known sample rate (set every dispatch_sample_rate_change).

◆ latch_close()

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.

◆ load_enabled_midi_inputs()

std::vector< std::string > pulp::audio::AudioDeviceManager::load_enabled_midi_inputs ( const pulp::state::ApplicationProperties & props)
static

Read the saved enabled-input identifiers.

◆ load_selection()

std::optional< DeviceSelection > pulp::audio::AudioDeviceManager::load_selection ( const pulp::state::ApplicationProperties & props)
static

Read persisted selection from props.user_settings().

Returns std::nullopt if no audio_device_manager.* keys exist.

◆ midi_endpoint_subscriber_count()

std::size_t pulp::audio::AudioDeviceManager::midi_endpoint_subscriber_count ( ) const

◆ midi_endpoints()

std::vector< MidiEndpoint > pulp::audio::AudioDeviceManager::midi_endpoints ( ) const

Current MIDI endpoint snapshot.

◆ midi_subscriber_count()

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.

◆ operator=()

AudioDeviceManager & pulp::audio::AudioDeviceManager::operator= ( const AudioDeviceManager & )
delete

◆ peak_cpu_load()

float pulp::audio::AudioDeviceManager::peak_cpu_load ( ) const

Peak CPU load since last reset_peak_cpu_load().

◆ process_load_snapshot()

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.

◆ refresh_midi_endpoints()

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.

◆ reset_peak_cpu_load()

void pulp::audio::AudioDeviceManager::reset_peak_cpu_load ( )

Reset the peak tracker.

◆ reset_xrun_counter()

void pulp::audio::AudioDeviceManager::reset_xrun_counter ( )

Reset the xrun counter to zero.

◆ resolve_selection()

ResolveResult pulp::audio::AudioDeviceManager::resolve_selection ( const DeviceSelection & sel,
const std::vector< DeviceInfo > & available,
const std::string & fallback_output_id,
const std::string & fallback_input_id )
static

◆ runtime_telemetry_snapshot()

RuntimeTelemetrySnapshot pulp::audio::AudioDeviceManager::runtime_telemetry_snapshot ( ) const

◆ sample_rate_change_count()

std::uint64_t pulp::audio::AudioDeviceManager::sample_rate_change_count ( ) const

Total number of sample-rate changes seen since construction.

◆ save_enabled_midi_inputs()

bool pulp::audio::AudioDeviceManager::save_enabled_midi_inputs ( pulp::state::ApplicationProperties & props,
const std::vector< std::string > & ids )
static

Write the enabled-input identifiers; an empty list removes the key.

◆ save_selection()

bool pulp::audio::AudioDeviceManager::save_selection ( pulp::state::ApplicationProperties & props,
const DeviceSelection & sel )
static

Write sel into props.user_settings() and save() the underlying file.

Returns true on successful save.

◆ selection_to_config()

DeviceConfig pulp::audio::AudioDeviceManager::selection_to_config ( const DeviceSelection & sel,
int default_output_channels = 2,
int default_input_channels = 0 )
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.

◆ set_default_device_change_handler()

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.

◆ set_midi_endpoints()

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).

◆ set_midi_input_enabled()

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).

◆ set_sample_rate_change_handler()

void pulp::audio::AudioDeviceManager::set_sample_rate_change_handler ( SampleRateChangeHandler handler)

◆ subscribe_device_changes()

DeviceChangeToken pulp::audio::AudioDeviceManager::subscribe_device_changes ( DeviceChangeHandler handler)
nodiscard

Subscribe to device-change events.

Returns an RAII token whose destruction unsubscribes. Safe to call from any thread.

◆ subscribe_midi()

MidiSubscriptionToken pulp::audio::AudioDeviceManager::subscribe_midi ( MidiHandler handler)
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.

◆ subscribe_midi_endpoints()

MidiSubscriptionToken pulp::audio::AudioDeviceManager::subscribe_midi_endpoints ( MidiEndpointChangeHandler handler)
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.

◆ xrun_count()

std::uint64_t pulp::audio::AudioDeviceManager::xrun_count ( ) const

Snapshot the current xrun count. Safe from any thread.

◆ DeviceChangeToken

friend class DeviceChangeToken
friend

◆ MidiSubscriptionToken

friend class MidiSubscriptionToken
friend

The documentation for this class was generated from the following file: