8Q_LOGGING_CATEGORY(qLcPulseAudioOut,
"qt.multimedia.pulseaudio.output")
9Q_LOGGING_CATEGORY(qLcPulseAudioIn,
"qt.multimedia.pulseaudio.input")
10Q_LOGGING_CATEGORY(qLcPulseAudioEngine,
"qt.multimedia.pulseaudio.engine")
20 spec.format = PA_SAMPLE_INVALID;
21 const bool isBigEndian = QSysInfo::ByteOrder == QSysInfo::BigEndian;
24 spec.format = PA_SAMPLE_U8;
26 spec.format = isBigEndian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
28 spec.format = isBigEndian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
30 spec.format = isBigEndian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
42 if (config == QAudioFormat::ChannelConfigUnknown)
43 config = QAudioFormat::defaultChannelConfigForChannelCount(format.channelCount());
47 map.map[0] = PA_CHANNEL_POSITION_MONO;
50 map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
52 map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
54 map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_CENTER;
56 map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;
58 map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
60 map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_RIGHT;
62 map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
64 map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
66 map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_CENTER;
68 map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;
70 map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
72 map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
74 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
76 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
78 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
80 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_CENTER;
82 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
84 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
86 map.map[map.channels++] = PA_CHANNEL_POSITION_AUX0;
88 map.map[map.channels++] = PA_CHANNEL_POSITION_AUX1;
90 map.map[map.channels++] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
92 map.map[map.channels++] = PA_CHANNEL_POSITION_AUX2;
94 map.map[map.channels++] = PA_CHANNEL_POSITION_AUX3;
96 map.map[map.channels++] = PA_CHANNEL_POSITION_AUX4;
99 Q_ASSERT(qPopulationCount(config) == map.channels);
106 for (
int i = 0; i < map.channels; ++i) {
107 switch (map.map[i]) {
108 case PA_CHANNEL_POSITION_MONO:
109 case PA_CHANNEL_POSITION_FRONT_CENTER:
112 case PA_CHANNEL_POSITION_FRONT_LEFT:
115 case PA_CHANNEL_POSITION_FRONT_RIGHT:
118 case PA_CHANNEL_POSITION_REAR_CENTER:
121 case PA_CHANNEL_POSITION_REAR_LEFT:
124 case PA_CHANNEL_POSITION_REAR_RIGHT:
127 case PA_CHANNEL_POSITION_LFE:
130 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
133 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
136 case PA_CHANNEL_POSITION_SIDE_LEFT:
139 case PA_CHANNEL_POSITION_SIDE_RIGHT:
143 case PA_CHANNEL_POSITION_TOP_CENTER:
146 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
149 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
152 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
155 case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
158 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
161 case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
178 switch (spec.format) {
182 case PA_SAMPLE_S16LE:
183 case PA_SAMPLE_S16BE:
186 case PA_SAMPLE_FLOAT32LE:
187 case PA_SAMPLE_FLOAT32BE:
190 case PA_SAMPLE_S32LE:
191 case PA_SAMPLE_S32BE:
204 return pa_strerror(pa_context_errno(context));
209 return currentError(pa_stream_get_context(stream));
214 return PAOperationHandle{
215 pa_stream_cork(stream.get(), corkStream ? 1 : 0,
nullptr,
nullptr),
216 PAOperationHandle::HasRef,
222 using namespace Qt::StringLiterals;
224 auto ml = std::unique_ptr<pa_mainloop, PaMainLoopDeleter>(pa_mainloop_new());
225 pa_mainloop_api *api = pa_mainloop_get_api(ml.get());
227 pa_context_new(api,
"pipewire-check"),
228 PAContextHandle::HasRef,
234 bool is_pipewire =
false;
238 pa_context_set_state_callback(ctx.get(), [](pa_context *ctx,
void *userdata) {
239 auto *data =
static_cast<ContextData *>(userdata);
240 switch (pa_context_get_state(ctx)) {
241 case PA_CONTEXT_READY:
242 case PA_CONTEXT_FAILED:
243 case PA_CONTEXT_TERMINATED:
250 pa_context_connect(ctx.get(),
nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr);
251 auto disconnect = qScopeGuard([&] {
252 pa_context_disconnect(ctx.get());
256 pa_mainloop_iterate(ml.get(), 1,
nullptr);
258 if (pa_context_get_state(ctx.get()) != PA_CONTEXT_READY)
259 return PulseaudioServerType::NotConnected;
261 PAOperationHandle op{
262 pa_context_get_server_info(ctx.get(),
263 [](pa_context *,
const pa_server_info *info,
void *userdata) {
264 auto *data =
static_cast<ContextData *>(userdata);
265 if (info && info->server_name)
266 data->is_pipewire = QLatin1StringView(info->server_name).contains(
"PipeWire"_L1);
268 PAOperationHandle::HasRef,
271 while (pa_operation_get_state(op.get()) == PA_OPERATION_RUNNING)
272 pa_mainloop_iterate(ml.get(), 1,
nullptr);
274 return data.is_pipewire ? PulseaudioServerType::Pipewire : PulseaudioServerType::Pulseaudio;
281 using namespace Qt::StringLiterals;
284 case PA_STREAM_UNCONNECTED:
return "Unconnected"_L1;
285 case PA_STREAM_CREATING:
return "Creating"_L1;
286 case PA_STREAM_READY:
return "Ready"_L1;
287 case PA_STREAM_FAILED:
return "Failed"_L1;
288 case PA_STREAM_TERMINATED:
return "Terminated"_L1;
289 default: Q_UNREACHABLE_RETURN(
"Unknown stream state"_L1);
295 using namespace Qt::StringLiterals;
298 case PA_SAMPLE_U8:
return "Unsigned 8 Bit PCM."_L1;
299 case PA_SAMPLE_ALAW:
return "8 Bit a-Law "_L1;
300 case PA_SAMPLE_ULAW:
return "8 Bit mu-Law"_L1;
301 case PA_SAMPLE_S16LE:
return "Signed 16 Bit PCM, little endian (PC)."_L1;
302 case PA_SAMPLE_S16BE:
return "Signed 16 Bit PCM, big endian."_L1;
303 case PA_SAMPLE_FLOAT32LE:
return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0"_L1;
304 case PA_SAMPLE_FLOAT32BE:
return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0"_L1;
305 case PA_SAMPLE_S32LE:
return "Signed 32 Bit PCM, little endian (PC)."_L1;
306 case PA_SAMPLE_S32BE:
return "Signed 32 Bit PCM, big endian."_L1;
307 case PA_SAMPLE_S24LE:
return "Signed 24 Bit PCM packed, little endian (PC)."_L1;
308 case PA_SAMPLE_S24BE:
return "Signed 24 Bit PCM packed, big endian."_L1;
309 case PA_SAMPLE_S24_32LE:
return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC)."_L1;
310 case PA_SAMPLE_S24_32BE:
return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian."_L1;
311 case PA_SAMPLE_MAX:
return "Upper limit of valid sample types."_L1;
312 case PA_SAMPLE_INVALID:
return "Invalid sample format"_L1;
313 default: Q_UNREACHABLE_RETURN(
"Unknown sample format"_L1);
319 using namespace Qt::StringLiterals;
322 case PA_CONTEXT_UNCONNECTED:
return "Unconnected"_L1;
323 case PA_CONTEXT_CONNECTING:
return "Connecting"_L1;
324 case PA_CONTEXT_AUTHORIZING:
return "Authorizing"_L1;
325 case PA_CONTEXT_SETTING_NAME:
return "Setting Name"_L1;
326 case PA_CONTEXT_READY:
return "Ready"_L1;
327 case PA_CONTEXT_FAILED:
return "Failed"_L1;
328 case PA_CONTEXT_TERMINATED:
return "Terminated"_L1;
329 default: Q_UNREACHABLE_RETURN(
"Unknown context state"_L1);
336 return dbg << stateToQStringView(state);
341 return dbg << sampleFormatToQStringView(format);
346 return dbg << stateToQStringView(state);
QUtf8StringView currentError(const pa_context *context)
QUtf8StringView currentError(const pa_stream *stream)
pa_channel_map channelMapForAudioFormat(const QAudioFormat &format)
QAudioFormat::ChannelConfig channelConfigFromMap(const pa_channel_map &map)
QAudioFormat sampleSpecToAudioFormat(const pa_sample_spec &spec)
PulseaudioServerType pulseaudioDetectServerType()
pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format)
PAOperationHandle streamCork(const PAStreamHandle &stream, bool corkStream)
Combined button and popup list for selecting options.
static QLatin1StringView stateToQStringView(pa_stream_state_t state)
static QLatin1StringView stateToQStringView(pa_context_state_t state)
static QLatin1StringView sampleFormatToQStringView(pa_sample_format format)
QDebug operator<<(QDebug dbg, pa_sample_format format)
QDebug operator<<(QDebug dbg, pa_stream_state_t state)
QDebug operator<<(QDebug dbg, pa_context_state_t state)