4#include <QtCore/qdebug.h>
7#include <QGuiApplication>
19template<
typename Info>
26 bool isDefault = defaultDeviceId ==
info.name;
27 auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(
info.name,
info.description, isDefault,
mode);
30 newDeviceInfo->preferredFormat.setChannelConfig(newDeviceInfo->channelConfiguration);
33 if (
device.handle() && *newDeviceInfo == *
device.handle())
36 device = newDeviceInfo.release()->create();
41 QMap<int, QAudioDevice> &
devices)
48 auto deviceInfo =
device.handle();
49 const auto isDefault = deviceInfo->id == defaultDeviceId;
50 if (deviceInfo->isDefault != isDefault) {
52 auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(
54 newDeviceInfo->isDefault = isDefault;
55 device = newDeviceInfo.release()->create();
69 qWarning() <<
"Failed to get server information:" << currentError(
context);
74 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
76 pa_sample_spec_snprint(ss,
sizeof(ss), &
info->sample_spec);
77 pa_channel_map_snprint(cm,
sizeof(cm), &
info->channel_map);
83 "Server Version: %4\n"
84 "Default Sample Specification: %5\n"
85 "Default Channel Map: %6\n"
87 "Default Source: %8\n")
98 bool defaultSinkChanged =
false;
99 bool defaultSourceChanged =
false;
106 defaultSinkChanged =
true;
111 defaultSourceChanged =
true;
115 if (defaultSinkChanged
120 if (defaultSourceChanged
125 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
136 qWarning() <<
"Failed to get sink information:" << currentError(
context);
141 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
148 static const QMap<pa_sink_state, QString> stateMap{
149 { PA_SINK_INVALID_STATE, u
"n/a"_s }, { PA_SINK_RUNNING, u
"RUNNING"_s },
150 { PA_SINK_IDLE, u
"IDLE"_s }, { PA_SINK_SUSPENDED, u
"SUSPENDED"_s },
151 { PA_SINK_UNLINKED, u
"UNLINKED"_s },
158 "\tDescription: %4\n")
177 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
184 static const QMap<pa_source_state, QString> stateMap{
185 { PA_SOURCE_INVALID_STATE, u
"n/a"_s }, { PA_SOURCE_RUNNING, u
"RUNNING"_s },
186 { PA_SOURCE_IDLE, u
"IDLE"_s }, { PA_SOURCE_SUSPENDED, u
"SUSPENDED"_s },
187 { PA_SOURCE_UNLINKED, u
"UNLINKED"_s },
194 "\tDescription: %4\n")
201 if (
info->monitor_of_sink != PA_INVALID_INDEX)
213 int type =
t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
214 int facility =
t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
217 case PA_SUBSCRIPTION_EVENT_NEW:
218 case PA_SUBSCRIPTION_EVENT_CHANGE:
220 case PA_SUBSCRIPTION_EVENT_SERVER: {
223 qWarning() <<
"PulseAudioService: failed to get server info";
226 case PA_SUBSCRIPTION_EVENT_SINK: {
230 qWarning() <<
"PulseAudioService: failed to get sink info";
233 case PA_SUBSCRIPTION_EVENT_SOURCE: {
237 qWarning() <<
"PulseAudioService: failed to get source info";
244 case PA_SUBSCRIPTION_EVENT_REMOVE:
246 case PA_SUBSCRIPTION_EVENT_SINK: {
251 case PA_SUBSCRIPTION_EVENT_SOURCE: {
273 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
279 pa_context_state_t
state = pa_context_get_state(
c);
284 if (
state == PA_CONTEXT_FAILED)
305void QPulseAudioEngine::prepare()
308 bool keepGoing =
true;
311 m_mainLoop = pa_threaded_mainloop_new();
312 if (m_mainLoop ==
nullptr) {
313 qWarning() <<
"PulseAudioService: unable to create pulseaudio mainloop";
317 if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
318 qWarning() <<
"PulseAudioService: unable to start pulseaudio mainloop";
319 pa_threaded_mainloop_free(m_mainLoop);
320 m_mainLoop =
nullptr;
324 m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
328 pa_proplist *proplist = pa_proplist_new();
334 pa_proplist_sets(proplist, PA_PROP_WINDOW_ICON_NAME,
qUtf8Printable(windowIconName));
336 m_context = pa_context_new_with_proplist(m_mainLoopApi,
nullptr, proplist);
337 pa_proplist_free(proplist);
339 if (m_context ==
nullptr) {
340 qWarning() <<
"PulseAudioService: Unable to create new pulseaudio context";
341 pa_threaded_mainloop_unlock(m_mainLoop);
342 pa_threaded_mainloop_free(m_mainLoop);
343 m_mainLoop =
nullptr;
350 if (pa_context_connect(m_context,
nullptr,
static_cast<pa_context_flags_t
>(0),
nullptr) < 0) {
351 qWarning() <<
"PulseAudioService: pa_context_connect() failed";
352 pa_context_unref(m_context);
353 pa_threaded_mainloop_unlock(m_mainLoop);
354 pa_threaded_mainloop_free(m_mainLoop);
355 m_mainLoop =
nullptr;
360 pa_threaded_mainloop_wait(m_mainLoop);
363 switch (pa_context_get_state(m_context)) {
364 case PA_CONTEXT_CONNECTING:
365 case PA_CONTEXT_AUTHORIZING:
366 case PA_CONTEXT_SETTING_NAME:
369 case PA_CONTEXT_READY:
370 qCDebug(qLcPulseAudioEngine) <<
"Connection established.";
374 case PA_CONTEXT_TERMINATED:
375 qCritical(
"PulseAudioService: Context terminated.");
380 case PA_CONTEXT_FAILED:
382 qCritical() <<
"PulseAudioService: Connection failure:"
389 pa_threaded_mainloop_wait(m_mainLoop);
395 pa_context_set_subscribe_callback(m_context,
event_cb,
this);
398 pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE
399 | PA_SUBSCRIPTION_MASK_SERVER),
402 qWarning() <<
"PulseAudioService: failed to subscribe to context notifications";
404 pa_context_unref(m_context);
414 pa_threaded_mainloop_free(m_mainLoop);
415 m_mainLoop =
nullptr;
420void QPulseAudioEngine::release()
426 pa_context_disconnect(m_context);
427 pa_context_unref(m_context);
432 pa_threaded_mainloop_stop(m_mainLoop);
433 pa_threaded_mainloop_free(m_mainLoop);
434 m_mainLoop =
nullptr;
440void QPulseAudioEngine::updateDevices()
442 std::lock_guard
lock(*
this);
447 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
448 pa_threaded_mainloop_wait(m_mainLoop);
450 qWarning() <<
"PulseAudioService: failed to get server info";
454 operation.reset(pa_context_get_sink_info_list(m_context,
sinkInfoCallback,
this));
456 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
457 pa_threaded_mainloop_wait(m_mainLoop);
459 qWarning() <<
"PulseAudioService: failed to get sink info";
463 operation.reset(pa_context_get_source_info_list(m_context,
sourceInfoCallback,
this));
465 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
466 pa_threaded_mainloop_wait(m_mainLoop);
468 qWarning() <<
"PulseAudioService: failed to get source info";
472void QPulseAudioEngine::onContextFailed()
485 return pulseEngine();
IOBluetoothDevice * device
The QAudioDevice class provides an information about audio devices and their functionality.
Mode
Describes the mode of this device.
QString applicationDisplayName
the user-visible name of this application
QString desktopFileName
the base name of the desktop entry for this application
QIcon windowIcon
the default window icon
QList< T > values() const
size_type remove(const Key &key)
QReadWriteLock m_sinkLock
QMap< int, QAudioDevice > m_sinks
static QPulseAudioEngine * instance()
QPulseAudioEngine(QObject *parent=0)
pa_threaded_mainloop * mainloop()
QByteArray defaultDevice(QAudioDevice::Mode mode) const
void audioOutputsChanged()
QByteArray m_defaultSource
QMap< int, QAudioDevice > m_sources
QList< QAudioDevice > availableDevices(QAudioDevice::Mode mode) const
QReadWriteLock m_sourceLock
void audioInputsChanged()
QReadWriteLock m_serverLock
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool singleShot
whether the timer is a single-shot timer
QUtf8StringView currentError(const pa_context *context)
QAudioFormat::ChannelConfig channelConfigFromMap(const pa_channel_map &map)
QAudioFormat sampleSpecToAudioFormat(const pa_sample_spec &spec)
Combined button and popup list for selecting options.
static void contextStateCallback(pa_context *c, void *userdata)
static QT_BEGIN_NAMESPACE bool updateDevicesMap(QReadWriteLock &lock, QByteArray defaultDeviceId, QMap< int, QAudioDevice > &devices, QAudioDevice::Mode mode, const Info &info)
static void serverInfoCallback(pa_context *context, const pa_server_info *info, void *userdata)
static void event_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
static void contextStateCallbackInit(pa_context *context, void *userdata)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCDebug(category,...)
std::unique_ptr< pa_operation, PAOperationDeleter > PAOperationUPtr
#define qUtf8Printable(string)
#define QStringLiteral(str)