4#include <QtCore/qdebug.h>
17template<
typename Info>
24 bool isDefault = defaultDeviceId ==
info.name;
25 auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(
info.name,
info.description, isDefault,
mode);
28 newDeviceInfo->preferredFormat.setChannelConfig(newDeviceInfo->channelConfiguration);
31 if (
device.handle() && *newDeviceInfo == *
device.handle())
34 device = newDeviceInfo.release()->create();
46 auto deviceInfo =
device.handle();
47 const auto isDefault = deviceInfo->id == defaultDeviceId;
48 if (deviceInfo->isDefault != isDefault) {
50 auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(
52 newDeviceInfo->isDefault = isDefault;
53 device = newDeviceInfo.release()->create();
69 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
71 pa_sample_spec_snprint(ss,
sizeof(ss), &
info->sample_spec);
72 pa_channel_map_snprint(cm,
sizeof(cm), &
info->channel_map);
77 "Server Version: %4\n"
78 "Default Sample Specification: %5\n"
79 "Default Channel Map: %6\n"
81 "Default Source: %8\n").
arg(
88 info->default_sink_name,
89 info->default_source_name);
94 bool defaultSinkChanged =
false;
95 bool defaultSourceChanged =
false;
102 defaultSinkChanged =
true;
107 defaultSourceChanged =
true;
111 if (defaultSinkChanged
116 if (defaultSourceChanged
121 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
134 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
142 { PA_SINK_INVALID_STATE,
"n/a" }, { PA_SINK_RUNNING,
"RUNNING" },
143 { PA_SINK_IDLE,
"IDLE" }, { PA_SINK_SUSPENDED,
"SUSPENDED" },
144 { PA_SINK_UNLINKED,
"UNLINKED" },
150 "\tDescription: %4\n"
152 stateMap.value(
info->state),
168 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
176 { PA_SOURCE_RUNNING,
"RUNNING" },
177 { PA_SOURCE_IDLE,
"IDLE" },
178 { PA_SOURCE_SUSPENDED,
"SUSPENDED" },
179 { PA_SOURCE_UNLINKED,
"UNLINKED" } };
184 "\tDescription: %4\n"
186 stateMap.value(
info->state),
192 if (
info->monitor_of_sink != PA_INVALID_INDEX)
204 int type =
t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
205 int facility =
t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
208 case PA_SUBSCRIPTION_EVENT_NEW:
209 case PA_SUBSCRIPTION_EVENT_CHANGE:
211 case PA_SUBSCRIPTION_EVENT_SERVER: {
214 qWarning(
"PulseAudioService: failed to get server info");
217 case PA_SUBSCRIPTION_EVENT_SINK: {
221 qWarning(
"PulseAudioService: failed to get sink info");
224 case PA_SUBSCRIPTION_EVENT_SOURCE: {
228 qWarning(
"PulseAudioService: failed to get source info");
235 case PA_SUBSCRIPTION_EVENT_REMOVE:
237 case PA_SUBSCRIPTION_EVENT_SINK: {
242 case PA_SUBSCRIPTION_EVENT_SOURCE: {
263 pa_threaded_mainloop_signal(pulseEngine->
mainloop(), 0);
269 pa_context_state_t
state = pa_context_get_state(
c);
275 if (
state == PA_CONTEXT_FAILED)
296void QPulseAudioEngine::prepare()
298 bool keepGoing =
true;
301 m_mainLoop = pa_threaded_mainloop_new();
302 if (m_mainLoop ==
nullptr) {
303 qWarning(
"PulseAudioService: unable to create pulseaudio mainloop");
307 if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
308 qWarning(
"PulseAudioService: unable to start pulseaudio mainloop");
309 pa_threaded_mainloop_free(m_mainLoop);
310 m_mainLoop =
nullptr;
314 m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
318 m_context = pa_context_new(m_mainLoopApi,
QString(
QLatin1String(
"QtPulseAudio:%1")).
arg(::getpid()).toLatin1().constData());
320 if (m_context ==
nullptr) {
321 qWarning(
"PulseAudioService: Unable to create new pulseaudio context");
322 pa_threaded_mainloop_unlock(m_mainLoop);
323 pa_threaded_mainloop_free(m_mainLoop);
324 m_mainLoop =
nullptr;
331 if (pa_context_connect(m_context,
nullptr, (pa_context_flags_t)0,
nullptr) < 0) {
332 qWarning(
"PulseAudioService: pa_context_connect() failed");
333 pa_context_unref(m_context);
334 pa_threaded_mainloop_unlock(m_mainLoop);
335 pa_threaded_mainloop_free(m_mainLoop);
336 m_mainLoop =
nullptr;
341 pa_threaded_mainloop_wait(m_mainLoop);
344 switch (pa_context_get_state(m_context)) {
345 case PA_CONTEXT_CONNECTING:
346 case PA_CONTEXT_AUTHORIZING:
347 case PA_CONTEXT_SETTING_NAME:
350 case PA_CONTEXT_READY:
352 qDebug(
"Connection established.");
357 case PA_CONTEXT_TERMINATED:
358 qCritical(
"PulseAudioService: Context terminated.");
363 case PA_CONTEXT_FAILED:
372 pa_threaded_mainloop_wait(m_mainLoop);
378 pa_context_set_subscribe_callback(m_context,
event_cb,
this);
381 pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE
382 | PA_SUBSCRIPTION_MASK_SERVER),
385 qWarning(
"PulseAudioService: failed to subscribe to context notifications");
387 pa_context_unref(m_context);
397 pa_threaded_mainloop_free(m_mainLoop);
398 m_mainLoop =
nullptr;
403void QPulseAudioEngine::release()
409 pa_context_disconnect(m_context);
410 pa_context_unref(m_context);
415 pa_threaded_mainloop_stop(m_mainLoop);
416 pa_threaded_mainloop_free(m_mainLoop);
417 m_mainLoop =
nullptr;
423void QPulseAudioEngine::updateDevices()
425 std::lock_guard
lock(*
this);
430 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
431 pa_threaded_mainloop_wait(m_mainLoop);
433 qWarning(
"PulseAudioService: failed to get server info");
437 operation.reset(pa_context_get_sink_info_list(m_context,
sinkInfoCallback,
this));
439 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
440 pa_threaded_mainloop_wait(m_mainLoop);
442 qWarning(
"PulseAudioService: failed to get sink info");
446 operation.reset(pa_context_get_source_info_list(m_context,
sourceInfoCallback,
this));
448 while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
449 pa_threaded_mainloop_wait(m_mainLoop);
451 qWarning(
"PulseAudioService: failed to get source info");
455void QPulseAudioEngine::onContextFailed()
468 return pulseEngine();
IOBluetoothDevice * device
The QAudioDevice class provides an information about audio devices and their functionality.
Mode
Describes the mode of this device.
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 fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
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
QAudioFormat::ChannelConfig channelConfigFromMap(const pa_channel_map &map)
QAudioFormat sampleSpecToAudioFormat(const pa_sample_spec &spec)
static QString stateToQString(pa_stream_state_t state)
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,...)
std::unique_ptr< pa_operation, PAOperationDeleter > PAOperationUPtr
QLatin1StringView QLatin1String
QFileInfo info(fileName)
[8]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent