Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpipewire_audiodevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qdebug.h>
7#include <QtMultimedia/private/qaudioformat_p.h>
8
10
11namespace QtPipeWire {
12
13namespace {
14
15QAudioFormat::SampleFormat toSampleFormat(spa_audio_format fmt)
16{
17 switch (fmt) {
18 case SPA_AUDIO_FORMAT_S16:
19 return QAudioFormat::Int16;
20 case SPA_AUDIO_FORMAT_S32:
21 return QAudioFormat::Int32;
22 case SPA_AUDIO_FORMAT_U8:
23 return QAudioFormat::UInt8;
24 case SPA_AUDIO_FORMAT_F32:
25 return QAudioFormat::Float;
26 default:
27 return QAudioFormat::Unknown;
28 }
29}
30
31QByteArray inferDeviceId(const PwPropertyDict &properties)
32{
33 auto nodeName = getNodeName(properties);
34 Q_ASSERT(nodeName);
35 if (nodeName)
36 return QByteArray{ *nodeName };
37 return {};
38}
39
40template <typename Lhs, typename Rhs>
41bool channelPositionsEqual(const Lhs &lhs, const Rhs &rhs)
42{
43 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
44}
45
46QAudioDevicePrivate::AudioDeviceFormat toAudioDeviceFormat(const SpaObjectAudioFormat &formats)
47{
48 QAudioDevicePrivate::AudioDeviceFormat format;
49
50 static const QList allSampleFormats = {
51 QAudioFormat::SampleFormat::UInt8,
52 QAudioFormat::SampleFormat::Int16,
53 QAudioFormat::SampleFormat::Int32,
54 QAudioFormat::SampleFormat::Float,
55 };
56
57 format.supportedSampleFormats = allSampleFormats;
58
59 format.minimumSampleRate = QtMultimediaPrivate::allSupportedSampleRates.front();
60 format.maximumSampleRate = QtMultimediaPrivate::allSupportedSampleRates.back();
61
62 // Set preferred sample rate
63 constexpr int defaultPipewireSamplingRate = 48000;
64 if (formats.rates) {
65 std::visit([&](const auto &arg) {
66 if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, int>) {
67 format.preferredFormat.setSampleRate(arg);
68 } else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, QSpan<const int>>) {
69 format.preferredFormat.setSampleRate(QtMultimediaPrivate::findClosestSamplingRate(
70 defaultPipewireSamplingRate, arg));
71 } else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, SpaRange<int>>) {
72 format.preferredFormat.setSampleRate(arg.defaultValue);
73 }
74 }, *formats.rates);
75 } else {
76 format.preferredFormat.setSampleRate(defaultPipewireSamplingRate);
77 }
78
79 // Set preferred sample format
80 std::visit([&](const auto &arg) {
81 if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, spa_audio_format>) {
82 QAudioFormat::SampleFormat fmt = toSampleFormat(arg);
83 if (fmt != QAudioFormat::Unknown) {
84 format.preferredFormat.setSampleFormat(fmt);
85 } else {
86 // fallback to float
87 format.preferredFormat.setSampleFormat(QAudioFormat::Float);
88 }
89 } else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, spa_audio_iec958_codec>) {
90 Q_ASSERT(arg == SPA_AUDIO_IEC958_CODEC_PCM);
91 format.preferredFormat.setSampleFormat(QAudioFormat::Float);
92 } else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
93 SpaEnum<spa_audio_format>>) {
94 QAudioFormat::SampleFormat sampleFormat = toSampleFormat(arg.defaultValue());
95 if (sampleFormat != QAudioFormat::Unknown) {
96 format.preferredFormat.setSampleFormat(sampleFormat);
97 } else {
98 if (!format.supportedSampleFormats.empty())
99 format.preferredFormat.setSampleFormat(QAudioFormat::Float);
100 }
101 }
102 }, formats.sampleTypes);
103
104 format.minimumChannelCount = 1;
105 format.maximumChannelCount = formats.channelCount;
106
107 // Set channel configuration
108 if (formats.channelPositions) {
109 if (channelPositionsEqual(*formats.channelPositions, channelPositionsMono)) {
110 format.channelConfiguration = QAudioFormat::ChannelConfigMono;
111 } else if (channelPositionsEqual(*formats.channelPositions, channelPositionsStereo)) {
112 format.channelConfiguration = QAudioFormat::ChannelConfigStereo;
113 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions2Dot1)) {
114 format.channelConfiguration = QAudioFormat::ChannelConfig2Dot1;
115 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions3Dot0)) {
116 format.channelConfiguration = QAudioFormat::ChannelConfig3Dot0;
117 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions3Dot1)) {
118 format.channelConfiguration = QAudioFormat::ChannelConfig3Dot1;
119 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions5Dot0)) {
120 format.channelConfiguration = QAudioFormat::ChannelConfigSurround5Dot0;
121 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions5Dot1)) {
122 format.channelConfiguration = QAudioFormat::ChannelConfigSurround5Dot1;
123 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions7Dot0)) {
124 format.channelConfiguration = QAudioFormat::ChannelConfigSurround7Dot0;
125 } else if (channelPositionsEqual(*formats.channelPositions, channelPositions7Dot1)) {
126 format.channelConfiguration = QAudioFormat::ChannelConfigSurround7Dot1;
127 } else {
128 format.channelConfiguration =
129 QAudioFormat::defaultChannelConfigForChannelCount(formats.channelCount);
130 }
131 } else {
132 format.channelConfiguration =
133 QAudioFormat::defaultChannelConfigForChannelCount(formats.channelCount);
134 }
135
136 format.preferredFormat.setChannelCount(formats.channelCount);
137 format.preferredFormat.setChannelConfig(format.channelConfiguration);
138
139 return format;
140}
141
142} // namespace
143
165
166} // namespace QtPipeWire
167
168QT_END_NAMESPACE
Combined button and popup list for selecting options.