Class thl::AudioDeviceManager#
-
class AudioDeviceManager#
Manages audio device initialisation, lifecycle, and callback dispatch.
AudioDeviceManager provides a high-level interface for audio I/O using a cross-platform audio backend. It handles device enumeration, initialisation, and dispatches audio data to registered callbacks.
The typical usage pattern is:
Construct an AudioDeviceManager (initialises the audio context)
Enumerate available devices using enumerateInputDevices() / enumerateOutputDevices()
Call initialise() with the desired device configuration
Register callbacks using addPlaybackCallback(), addCaptureCallback(), and/or addDuplexCallback()
Call startPlayback() (and optionally startCapture()/startDuplex()) to begin audio processing
Call stopPlayback()/stopCapture()/stopDuplex() when done
Call shutdown() to release the device (or let destructor handle it)
- Device Lifecycle
Device enumeration, initialisation, start/stop, and callback registration are performed on the calling thread (typically the main thread).
Audio processing (AudioIODeviceCallback::process()) occurs on a dedicated audio thread created by the audio backend.
Callback registration is protected by a mutex and is safe to call from any thread, though adding/removing callbacks during audio processing may cause brief audio glitches.
- Threading Model
Only the internal processCallbacks() method runs on the audio thread. All public methods are called from the main thread and are NOT real-time safe. Registered callbacks must implement process() in a real-time safe manner.
- Real-Time Safety
On iOS, audio routing is managed by AVAudioSession. When the route changes (e.g., headphones connected/disconnected, Bluetooth device paired), the sample rate, buffer size, or channel count may change. Use setDeviceNotificationCallback() to receive DeviceNotificationType::Rerouted notifications and query the new configuration via getSampleRate(), getBufferSize(), etc. Note that process() may receive varying frame_count values after a route change.
- iOS Audio Rerouting
AudioDeviceManager manager; auto outputs = manager.enumerateOutputDevices(); if (!outputs.empty()) { manager.initialise(nullptr, &outputs[0], 48000, 256, 0, 2); manager.addPlaybackCallback(&myProcessor); manager.startPlayback(); // ... audio is now running ... manager.stopPlayback(); }
See also
See also
Public Types
-
enum class DeviceRole#
Identifies a device role for queries and internal routing.
Values:
-
enumerator Playback#
-
enumerator Capture#
-
enumerator Duplex#
-
enumerator Playback#
-
using DeviceNotificationCallback = std::function<void(DeviceNotificationType)>#
Callback type for device notification events.
This callback is invoked when device events occur, such as the device being started, stopped, or disconnected.
See also
setDeviceNotificationCallback()
-
using LogCallback = std::function<void(Logger::LogLevel level, const char *message)>#
Callback type for log messages from the audio backend.
- Param level:
Normalised log level.
- Param message:
The log message.
Public Functions
-
AudioDeviceManager()#
Constructs an AudioDeviceManager and initialises the audio context.
The audio context is initialised automatically, enabling device enumeration. Check isContextInitialised() to verify successful initialisation.
Warning
NOT real-time safe - performs system calls and allocations.
-
~AudioDeviceManager()#
Destructs the AudioDeviceManager, stopping and releasing all resources.
Automatically calls shutdown() if a device is initialised.
Warning
NOT real-time safe - performs system calls and deallocations.
-
AudioDeviceManager(const AudioDeviceManager&) = delete#
Copy constructor (deleted - AudioDeviceManager is non-copyable)
-
AudioDeviceManager &operator=(const AudioDeviceManager&) = delete#
Copy assignment (deleted - AudioDeviceManager is non-copyable)
-
bool is_context_initialised() const#
Checks if the audio context was successfully initialised.
Note
If this returns false, device enumeration and initialisation will fail.
- Returns:
true if the context is ready for device operations, false otherwise.
-
std::vector<AudioDeviceInfo> enumerate_input_devices() const#
Enumerates all available audio input (capture) devices.
Queries the system for available input devices and returns their information including supported sample rates.
Warning
NOT real-time safe - performs system queries.
- Returns:
Vector of AudioDeviceInfo structures for each available input device. Returns an empty vector if no devices are available or context is not initialised.
-
std::vector<AudioDeviceInfo> enumerate_output_devices() const#
Enumerates all available audio output (playback) devices.
Queries the system for available output devices and returns their information including supported sample rates.
Warning
NOT real-time safe - performs system queries.
- Returns:
Vector of AudioDeviceInfo structures for each available output device. Returns an empty vector if no devices are available or context is not initialised.
-
bool initialise(const AudioDeviceInfo *input_device, const AudioDeviceInfo *output_device, uint32_t sample_rate = 44100, uint32_t buffer_size_in_frames = 512, uint32_t num_input_channels = 1, uint32_t num_output_channels = 1)#
Initialises the audio device with the specified configuration.
Sets up separate playback, capture, and (when both devices are provided) duplex audio devices depending on which device pointers are provided.
Note
At least one of input_device or output_device must be non-null.
Note
The actual buffer size may differ from the requested size depending on the audio driver.
Warning
NOT real-time safe - performs system calls and allocations.
Warning
Must call shutdown() before re-initialising with different settings.
- Parameters:
input_device – Pointer to the input device info, or nullptr for output-only.
output_device – Pointer to the output device info, or nullptr for input-only.
sample_rate – Desired sample rate in Hz (default: 44100).
buffer_size_in_frames – Desired buffer size in frames (default: 512). Lower values reduce latency but increase CPU load.
num_input_channels – Number of input audio channels (default: 1).
num_output_channels – Number of output audio channels (default: 1).
- Returns:
true if initialisation succeeded, false otherwise.
-
void shutdown()#
Shuts down the audio device and releases associated resources.
Stops audio processing if running and releases the audio device. Safe to call multiple times or on an uninitialised manager.
Warning
NOT real-time safe - performs system calls and deallocations.
-
bool start_playback()#
Starts playback audio processing.
Begins audio output on the playback device. Playback callbacks will receive process() calls on the audio thread.
Warning
NOT real-time safe - may block while waiting for audio thread to start.
- Returns:
true if the device was started successfully, false otherwise.
- Pre:
initialise() must have been called successfully.
-
void stop_playback()#
Stops playback audio processing.
Halts audio output on the playback device. Playback callbacks will receive release_resources() after audio processing stops.
Warning
NOT real-time safe - may block while waiting for audio thread to stop.
-
bool start_capture()#
Starts capture audio processing.
Begins audio input on the capture device. Capture callbacks will receive process() calls on the audio thread.
Warning
NOT real-time safe - may block while waiting for audio thread to start.
- Returns:
true if the device was started successfully, false otherwise.
- Pre:
initialise() must have been called successfully.
-
void stop_capture()#
Stops capture audio processing.
Halts audio input on the capture device. Capture callbacks will receive release_resources() after audio processing stops.
Warning
NOT real-time safe - may block while waiting for audio thread to stop.
-
bool start_duplex()#
Starts duplex audio processing.
Begins audio input and output on the duplex device. Duplex callbacks will receive process() calls on the audio thread.
Warning
NOT real-time safe - may block while waiting for audio thread to start.
- Returns:
true if the device was started successfully, false otherwise.
- Pre:
initialise() must have been called successfully.
-
void stop_duplex()#
Stops duplex audio processing.
Halts audio input and output on the duplex device. Duplex callbacks will receive release_resources() after audio processing stops.
Warning
NOT real-time safe - may block while waiting for audio thread to stop.
-
inline bool is_playback_running() const#
Checks if playback processing is currently running.
- Returns:
true if playback is actively processing audio, false otherwise.
-
inline bool is_capture_running() const#
Checks if capture processing is currently running.
- Returns:
true if capture is actively processing audio, false otherwise.
-
inline bool is_duplex_running() const#
Checks if duplex processing is currently running.
- Returns:
true if duplex is actively processing audio, false otherwise.
-
void add_playback_callback(AudioIODeviceCallback *callback)#
Registers a playback audio callback to receive audio data.
The callback’s process() method will be called on the playback audio thread for each buffer of audio data. Multiple callbacks can be registered and will be called in registration order.
Note
If the playback device is already initialised, prepare_to_play() will be called on the callback before it starts receiving process() calls.
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to register. Must remain valid until removed or the manager is destroyed.
-
void add_capture_callback(AudioIODeviceCallback *callback)#
Registers a capture audio callback to receive audio data.
The callback’s process() method will be called on the capture audio thread for each buffer of audio data.
Note
If the capture device is already initialised, prepare_to_play() will be called on the callback before it starts receiving process() calls.
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to register. Must remain valid until removed or the manager is destroyed.
-
void add_duplex_callback(AudioIODeviceCallback *callback)#
Registers a duplex audio callback to receive audio data.
The callback’s process() method will be called on the duplex audio thread for each buffer of audio data.
Note
If the duplex device is already initialised, prepare_to_play() will be called on the callback before it starts receiving process() calls.
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to register. Must remain valid until removed or the manager is destroyed.
-
void remove_playback_callback(AudioIODeviceCallback *callback)#
Unregisters a playback audio callback.
Removes the callback from the list of registered callbacks. The callback’s release_resources() method will be called if playback is currently running.
Note
Safe to call with a callback that is not registered (no-op).
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to remove.
-
void remove_capture_callback(AudioIODeviceCallback *callback)#
Unregisters a capture audio callback.
Removes the callback from the list of registered callbacks. The callback’s release_resources() method will be called if capture is currently running.
Note
Safe to call with a callback that is not registered (no-op).
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to remove.
-
void remove_duplex_callback(AudioIODeviceCallback *callback)#
Unregisters a duplex audio callback.
Removes the callback from the list of registered callbacks. The callback’s release_resources() method will be called if duplex is currently running.
Note
Safe to call with a callback that is not registered (no-op).
Note
Thread-safe - uses RCU for lock-free audio thread access.
Warning
NOT real-time safe - performs allocation.
- Parameters:
callback – Pointer to the callback to remove.
-
void set_device_notification_callback(DeviceNotificationCallback callback)#
Sets a callback for device notification events.
The notification callback is invoked when device events occur, such as:
DeviceNotificationType::Started - Device started
DeviceNotificationType::Stopped - Device stopped
DeviceNotificationType::Rerouted - Device rerouted
DeviceNotificationType::InterruptionBegan/InterruptionEnded
Warning
NOT real-time safe - modifies internal state.
- Parameters:
callback – The callback function to invoke on notifications, or nullptr to disable notifications.
-
void set_log_callback(LogCallback callback)#
Sets a callback for log messages from the audio backend.
This is post-init only. Logs emitted during context initialization will not be captured.
- Parameters:
callback – Log callback function, or nullptr to clear.
-
bool set_bluetooth_profile(BluetoothProfile profile)#
Switches the iOS Bluetooth audio profile.
Reconfigures the AVAudioSession category options to use the specified Bluetooth profile. After calling this you must shutdown() and re-initialise() the device because the sample rate and buffer configuration will have changed.
On non-iOS platforms this is a no-op that returns true.
See also
BluetoothProfile
Warning
Must be called while no devices are running.
Warning
NOT real-time safe - performs system calls.
- Parameters:
profile – The desired Bluetooth profile.
- Returns:
true if the session was reconfigured successfully.
-
BluetoothProfile get_bluetooth_profile() const#
Returns the currently configured Bluetooth profile.
- Returns:
The active BluetoothProfile (defaults to HFP).
-
uint32_t get_sample_rate() const#
Gets the current sample rate.
- Returns:
The actual device sample rate in Hz if initialised, otherwise the default (44100).
-
uint32_t get_capture_sample_rate() const#
Gets the actual capture device sample rate.
On Android with Bluetooth SCO active, AAudio may report an incorrect sample rate for the capture stream. This method uses a callback-based measurement of the actual frame delivery rate when available. In all other cases it returns getSampleRate().
Use this when opening a recording file so the WAV header matches the actual audio data rate.
- Returns:
The true capture sample rate in Hz.
-
bool wait_for_capture_rate_measurement(uint32_t timeout_ms = 2000) const#
Blocks until the capture rate measurement has completed or the timeout expires.
On Android with Bluetooth SCO, the actual sample rate is measured by timing capture callbacks. Call this before getCaptureSampleRate() when accuracy is critical (e.g. before writing a WAV header).
If capture is not running or SCO is not active, returns immediately.
- Parameters:
timeout_ms – Maximum time to wait in milliseconds (default 2000).
- Returns:
true if a measurement is available, false on timeout.
-
uint32_t get_buffer_size(DeviceRole role) const#
Gets the current buffer size.
Returns the resolved period size in frames used for callback preparation. Without a role argument, returns the value using priority order: playback > duplex > capture. With a role argument, returns the value for that specific role.
Note
The returned value may differ from the requested buffer size depending on the audio driver.
- Parameters:
role – Optional device role to query. If omitted, uses priority order.
- Returns:
The resolved period size in frames, or 512 if no device is initialised.
-
uint32_t get_buffer_size() const#
-
uint32_t get_period_size(DeviceRole role) const#
Gets the per-callback period (chunk) size in frames.
Returns the actual period size observed from the audio callback. On the first callback, the actual frame count is recorded and used for subsequent queries — this may differ from the prepared period size (e.g. on iOS where the Audio Unit may deliver fewer frames than AVAudioSession reports).
Without a role argument, returns the value using priority order: playback > duplex > capture. With a role argument, returns the value for that specific role.
Note
Updated on the first audio callback to reflect the true frame count.
- Parameters:
role – Optional device role to query. If omitted, uses priority order.
- Returns:
The period size used for each audio callback, or the buffer size default if not initialised.
-
uint32_t get_period_size() const#
-
uint32_t get_period_count(DeviceRole role) const#
Gets the number of periods that make up the total buffer.
On Android this is buffer_size / burstSize; on Apple platforms it is 1. Without a role argument, returns the value using priority order: playback > duplex > capture.
- Parameters:
role – Optional device role to query. If omitted, uses priority order.
- Returns:
The period count, or 1 if not initialised.
-
uint32_t get_period_count() const#
-
uint32_t get_burst_size(DeviceRole role) const#
Gets the hardware burst size in frames.
On Android this is the AAudio framesPerBurst (the smallest callback granularity). On Apple platforms it equals the period size. Use this as the base unit for generating selectable buffer sizes.
Without a role argument, returns the value using priority order: playback > duplex > capture. With a role argument, returns the value for that specific role. Falls back to the period size if unavailable.
- Parameters:
role – Optional device role to query. If omitted, uses priority order.
- Returns:
The hardware burst size, or the period size if unavailable.
-
uint32_t get_burst_size() const#
-
uint32_t get_num_input_channels() const#
Gets the current number of input (capture) channels.
- Returns:
The actual capture channel count if initialised, otherwise the requested input channel count.
-
uint32_t get_num_output_channels() const#
Gets the current number of output (playback) channels.
- Returns:
The actual playback channel count if initialised, otherwise the requested output channel count.
-
std::string get_current_output_device_name() const#
Gets the name of the current output device.
On iOS returns the active AVAudioSession output route name. On other platforms returns the device name passed to initialise().
- Returns:
Device name string, or empty if not initialised.
-
std::string get_current_input_device_name() const#
Gets the name of the current input device.
On iOS returns the active AVAudioSession input route name. On other platforms returns the device name passed to initialise().
- Returns:
Device name string, or empty if not initialised.
Public Static Functions
-
static uint32_t clamp_buffer_size_for_bluetooth_route(uint32_t buffer_size_in_frames, uint32_t sample_rate)#
Clamps a buffer size so the resulting IO buffer duration stays within the Bluetooth HFP safe limit.
If the buffer size would produce an IO buffer duration longer than kMaxBluetoothIOBufferDurationSeconds, the returned size is reduced to exactly that limit.
- Parameters:
buffer_size_in_frames – The requested buffer size in frames.
sample_rate – The sample rate in Hz.
- Returns:
The (possibly reduced) buffer size in frames.
Public Static Attributes
-
static constexpr float k_max_bluetooth_io_buffer_duration_seconds = 0.064f#
Maximum IO buffer duration (in seconds) safe for Bluetooth HFP.
Bluetooth SCO links used by HFP can fail silently when the iOS AVAudioSession preferred IO buffer duration exceeds this threshold.