Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
device.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <cstdint>
6#include <functional>
7#include <memory>
8#include <mutex>
9#include <optional>
10#include <string>
11#include <utility>
12#include <vector>
13
14namespace pulp::audio {
15
16// Information about an audio device
17struct DeviceInfo {
18 std::string id; // Platform-specific unique ID
19 std::string name; // Human-readable name
22 std::vector<double> sample_rates; // Supported sample rates
23 std::vector<int> buffer_sizes; // Supported buffer sizes
24 bool is_default_input = false;
25 bool is_default_output = false;
26};
27
28// WASAPI (Windows) sharing model. `shared` routes through the system audio
29// engine (default — coexists with other apps); `exclusive` takes sole ownership
30// of the endpoint for lower latency. Non-Windows backends (CoreAudio / ALSA /
31// JACK) ignore this and always use their native path.
32enum class ShareMode { shared, exclusive };
33
34// Configuration for opening a device
36 std::string device_id; // Empty = default device
37 double sample_rate = 48000.0;
38 int buffer_size = 256;
41 ShareMode share_mode = ShareMode::shared; // Windows WASAPI only; see above
42
43 // Windows WASAPI only: opt into the shared-mode low-latency path
44 // (IAudioClient3::InitializeSharedAudioStream at the engine's minimum
45 // period). Only honored when `share_mode == ShareMode::shared`; ignored
46 // in exclusive mode (which already drives at the device minimum period)
47 // and on every non-Windows backend. Defaults false so existing callers
48 // and other backends are unaffected. If IAudioClient3 is unavailable or
49 // the low-latency init fails, the backend honestly degrades to the
50 // standard shared-mode Initialize at the requested buffer size.
51 bool low_latency = false;
52};
53
54// Audio callback context — passed to the render function
56 double sample_rate = 0;
57 int buffer_size = 0;
58 uint64_t sample_position = 0; // Running sample count
59};
60
61// The audio callback: receives input, fills output
62// Called on the real-time audio thread — no allocation, no locks, no blocking.
63using AudioCallback = std::function<void(
64 const BufferView<const float>& input,
65 BufferView<float>& output,
66 const CallbackContext& context
67)>;
68
69// Abstract audio device interface
71public:
72 using WorkgroupChangeCallback = std::function<void(void* workgroup)>;
73
74 virtual ~AudioDevice() = default;
75
76 virtual bool open(const DeviceConfig& config) = 0;
77 virtual void close() = 0;
78 virtual bool start(AudioCallback callback) = 0;
79 virtual void stop() = 0;
80
81 virtual bool is_open() const = 0;
82 virtual bool is_running() const = 0;
83 virtual DeviceInfo info() const = 0;
84 virtual double sample_rate() const = 0;
85 virtual int buffer_size() const = 0;
86
105 virtual void* callback_workgroup() const { return nullptr; }
106
111 virtual std::uint64_t xrun_count() const { return 0; }
112
114 virtual void reset_xrun_counter() {}
115
130 if (callback) callback(callback_workgroup());
131 }
132
138};
139
145std::optional<AudioIoTiming> query_audio_io_timing(
146 const AudioDevice& device) noexcept;
147
148// Audio system — enumerates devices and creates device instances
150public:
151 virtual ~AudioSystem() = default;
152
153 virtual std::vector<DeviceInfo> enumerate_devices() = 0;
154 virtual std::unique_ptr<AudioDevice> create_device(const std::string& device_id = "") = 0;
157
161 using DeviceChangeCallback = std::function<void()>;
162
169 std::lock_guard<std::mutex> lock(device_change_callback_mutex_);
170 if (cb) {
171 device_change_callback_ = std::make_shared<DeviceChangeCallback>(std::move(cb));
172 } else {
173 device_change_callback_.reset();
174 }
175 }
176
192 std::shared_ptr<DeviceChangeCallback> callback;
193 {
194 std::lock_guard<std::mutex> lock(device_change_callback_mutex_);
195 callback = device_change_callback_;
196 }
197 if (callback && *callback) (*callback)();
198 }
199
201 std::lock_guard<std::mutex> lock(device_change_callback_mutex_);
202 return static_cast<bool>(device_change_callback_ && *device_change_callback_);
203 }
204
205private:
206 mutable std::mutex device_change_callback_mutex_;
207 std::shared_ptr<DeviceChangeCallback> device_change_callback_;
208};
209
210// Create the platform-appropriate audio system
211std::unique_ptr<AudioSystem> create_audio_system();
212
213} // namespace pulp::audio
Definition device.hpp:70
virtual int buffer_size() const =0
virtual bool is_running() const =0
virtual void stop()=0
virtual void set_workgroup_change_callback(WorkgroupChangeCallback callback)
Atomically subscribe to callback_workgroup() changes and synchronously deliver the current value befo...
Definition device.hpp:129
virtual ~AudioDevice()=default
virtual bool is_open() const =0
virtual DeviceInfo info() const =0
virtual void quiesce_workgroup_changes()
Disable future workgroup-change notifications and serialize with any notification already in flight.
Definition device.hpp:137
virtual double sample_rate() const =0
virtual bool open(const DeviceConfig &config)=0
virtual bool start(AudioCallback callback)=0
std::function< void(void *workgroup)> WorkgroupChangeCallback
Definition device.hpp:72
virtual void * callback_workgroup() const
Opaque handle to the OS-supplied real-time workgroup associated with this device's I/O thread,...
Definition device.hpp:105
virtual void close()=0
virtual std::uint64_t xrun_count() const
Cumulative xrun (overload/underrun) count since start() or the last reset_xrun_counter().
Definition device.hpp:111
virtual void reset_xrun_counter()
Reset the xrun counter to 0. Safe from any thread.
Definition device.hpp:114
Definition device.hpp:149
virtual std::unique_ptr< AudioDevice > create_device(const std::string &device_id="")=0
virtual DeviceInfo default_output_device()=0
virtual DeviceInfo default_input_device()=0
virtual ~AudioSystem()=default
virtual std::vector< DeviceInfo > enumerate_devices()=0
virtual void set_device_change_callback(DeviceChangeCallback cb)
Default implementation stores the callback in a base-class slot.
Definition device.hpp:168
bool has_device_change_callback() const
Observe whether a caller registered a callback.
Definition device.hpp:200
std::function< void()> DeviceChangeCallback
Register a callback for device list changes (hotplug/unplug).
Definition device.hpp:161
void fire_device_change()
Dispatch a stored device-change callback.
Definition device.hpp:191
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Definition analyzer_provider.hpp:12
std::function< void( const BufferView< const float > &input, BufferView< float > &output, const CallbackContext &context)> AudioCallback
Definition device.hpp:63
std::optional< AudioIoTiming > query_audio_io_timing(const AudioDevice &device) noexcept
Current route timing, or no value when the concrete backend has not implemented timing publication.
std::unique_ptr< AudioSystem > create_audio_system()
ShareMode
Definition device.hpp:32
@ shared
Definition device.hpp:32
@ exclusive
Definition device.hpp:32
Definition device.hpp:55
int buffer_size
Definition device.hpp:57
double sample_rate
Definition device.hpp:56
uint64_t sample_position
Definition device.hpp:58
Definition device.hpp:35
std::string device_id
Definition device.hpp:36
double sample_rate
Definition device.hpp:37
bool low_latency
Definition device.hpp:51
int output_channels
Definition device.hpp:40
int input_channels
Definition device.hpp:39
int buffer_size
Definition device.hpp:38
ShareMode share_mode
Definition device.hpp:41
Definition device.hpp:17
bool is_default_input
Definition device.hpp:24
std::vector< int > buffer_sizes
Definition device.hpp:23
std::string id
Definition device.hpp:18
std::string name
Definition device.hpp:19
int max_output_channels
Definition device.hpp:21
int max_input_channels
Definition device.hpp:20
bool is_default_output
Definition device.hpp:25
std::vector< double > sample_rates
Definition device.hpp:22