Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qpulseaudiosink.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
4#include <QtCore/qcoreapplication.h>
5#include <QtCore/qdebug.h>
6#include <QtCore/qmath.h>
7#include <private/qaudiohelpers_p.h>
8
9#include "qpulseaudiosink_p.h"
10#include "qpulseaudiodevice_p.h"
12#include "qpulsehelpers_p.h"
13#include <sys/types.h>
14#include <unistd.h>
15#include <mutex> // for st::lock_guard
16
18
19const int SinkPeriodTimeMs = 20;
20
21#define LOW_LATENCY_CATEGORY_NAME "game"
22
23static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
24{
26 Q_UNUSED(userdata);
27 qCDebug(qLcPulseAudioOut) << "Write callback:" << length;
29 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
30}
31
32static void outputStreamStateCallback(pa_stream *stream, void *userdata)
33{
34 Q_UNUSED(userdata);
35 pa_stream_state_t state = pa_stream_get_state(stream);
36 qCDebug(qLcPulseAudioOut) << "Stream state callback:" << state;
37 switch (state) {
38 case PA_STREAM_CREATING:
39 case PA_STREAM_READY:
40 case PA_STREAM_TERMINATED:
41 break;
42
43 case PA_STREAM_FAILED:
44 default:
45 qWarning() << QString::fromLatin1("Stream error: %1").arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(stream)))));
47 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
48 break;
49 }
50}
51
52static void outputStreamUnderflowCallback(pa_stream *stream, void *userdata)
53{
55 qCDebug(qLcPulseAudioOut) << "Buffer underflow";
56 if (userdata)
57 static_cast<QPulseAudioSink *>(userdata)->streamUnderflowCallback();
58}
59
60static void outputStreamOverflowCallback(pa_stream *stream, void *userdata)
61{
63 Q_UNUSED(userdata);
64 qCDebug(qLcPulseAudioOut) << "Buffer overflow";
65}
66
67static void outputStreamLatencyCallback(pa_stream *stream, void *userdata)
68{
70 Q_UNUSED(userdata);
71
72 if (Q_UNLIKELY(qLcPulseAudioOut().isEnabled(QtDebugMsg))) {
73 const pa_timing_info *info = pa_stream_get_timing_info(stream);
74
75 qCDebug(qLcPulseAudioOut) << "Latency callback:";
76 qCDebug(qLcPulseAudioOut) << "\tWrite index corrupt: " << info->write_index_corrupt;
77 qCDebug(qLcPulseAudioOut) << "\tWrite index: " << info->write_index;
78 qCDebug(qLcPulseAudioOut) << "\tRead index corrupt: " << info->read_index_corrupt;
79 qCDebug(qLcPulseAudioOut) << "\tRead index: " << info->read_index;
80 qCDebug(qLcPulseAudioOut) << "\tSink usec: " << info->sink_usec;
81 qCDebug(qLcPulseAudioOut) << "\tConfigured sink usec: " << info->configured_sink_usec;
82 }
83}
84
85static void outputStreamSuccessCallback(pa_stream *stream, int success, void *userdata)
86{
88 Q_UNUSED(userdata);
89
90 qCDebug(qLcPulseAudioOut) << "Stream successful:" << success;
92 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
93}
94
95static void outputStreamDrainComplete(pa_stream *stream, int success, void *userdata)
96{
98
99 qCDebug(qLcPulseAudioOut) << "Stream drained:" << bool(success) << userdata;
100
101 if (userdata && success)
102 static_cast<QPulseAudioSink *>(userdata)->streamDrainedCallback();
103}
104
105static void streamAdjustPrebufferCallback(pa_stream *stream, int success, void *userdata)
106{
108 Q_UNUSED(success);
109 Q_UNUSED(userdata);
110
111 qCDebug(qLcPulseAudioOut) << "Prebuffer adjusted:" << bool(success);
112}
113
114
117 m_device(device)
118{
119}
120
122{
123 close();
125}
126
128{
129 return m_errorState;
130}
131
132void QPulseAudioSink::setStateAndError(QAudio::State state, QAudio::Error error,
133 bool forceEmitState)
134{
135 // TODO: reimplement with atomic state changings (compare_exchange)
136
137 const auto isStateChanged = m_deviceState.exchange(state) != state;
138 const auto isErrorChanged = m_errorState.exchange(error) != error;
139
140 if (isStateChanged || forceEmitState)
142
143 if (isErrorChanged)
145}
146
148{
149 return m_deviceState;
150}
151
153{
154 if (m_audioSource && m_audioSource->atEnd()) {
155 qCDebug(qLcPulseAudioOut) << "Draining stream at end of buffer";
156
157 exchangeDrainOperation(pa_stream_drain(m_stream, outputStreamDrainComplete, this));
158 } else if (m_deviceState != QAudio::IdleState && !m_resuming) {
159 setStateAndError(QAudio::IdleState, QAudio::UnderrunError);
160 }
161}
162
164{
165 if (!exchangeDrainOperation(nullptr))
166 return;
167
168 setStateAndError(QAudio::IdleState, QAudio::NoError);
169}
170
172{
173 setStateAndError(QAudio::StoppedState, QAudio::NoError);
174
175 close();
176
177 m_pullMode = true;
178 m_audioSource = device;
179
180 if (!open()) {
181 m_audioSource = nullptr;
182 return;
183 }
184
185 // ensure we only process timing infos that are up to date
186 gettimeofday(&lastTimingInfo, nullptr);
187 lastProcessedUSecs = 0;
188
189 connect(m_audioSource, &QIODevice::readyRead, this, &QPulseAudioSink::startReading);
190 setStateAndError(QAudio::ActiveState, QAudio::NoError);
191}
192
193void QPulseAudioSink::startReading()
194{
195 if (!m_tickTimer.isActive())
196 m_tickTimer.start(m_periodTime, this);
197}
198
200{
201 setStateAndError(QAudio::StoppedState, QAudio::NoError);
202
203 close();
204
205 m_pullMode = false;
206
207 if (!open())
208 return nullptr;
209
210 m_audioSource = new PulseOutputPrivate(this);
212
213 // ensure we only process timing infos that are up to date
214 gettimeofday(&lastTimingInfo, nullptr);
215 lastProcessedUSecs = 0;
216
217 setStateAndError(QAudio::IdleState, QAudio::NoError);
218
219 return m_audioSource;
220}
221
222bool QPulseAudioSink::open()
223{
224 if (m_opened)
225 return true;
226
228
229 if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
230 setStateAndError(QAudio::StoppedState, QAudio::FatalError, true);
231 return false;
232 }
233
234 pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
235 pa_channel_map channel_map = QPulseAudioInternal::channelMapForAudioFormat(m_format);
236 Q_ASSERT(spec.channels == channel_map.channels);
237
238 if (!pa_sample_spec_valid(&spec)) {
239 setStateAndError(QAudio::StoppedState, QAudio::OpenError, true);
240 return false;
241 }
242
243 m_spec = spec;
244 m_totalTimeValue = 0;
245
246 if (m_streamName.isNull())
247 m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
248
249 if (Q_UNLIKELY(qLcPulseAudioOut().isEnabled(QtDebugMsg))) {
250 qCDebug(qLcPulseAudioOut) << "Opening stream with.";
251 qCDebug(qLcPulseAudioOut) << "\tFormat: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
252 qCDebug(qLcPulseAudioOut) << "\tRate: " << spec.rate;
253 qCDebug(qLcPulseAudioOut) << "\tChannels: " << spec.channels;
254 qCDebug(qLcPulseAudioOut) << "\tFrame size: " << pa_frame_size(&spec);
255 }
256
257 pulseEngine->lock();
258
259
260 pa_proplist *propList = pa_proplist_new();
261#if 0
262 qint64 bytesPerSecond = m_format.sampleRate() * m_format.bytesPerFrame();
263 static const char *mediaRoleFromAudioRole[] = {
264 nullptr, // UnknownRole
265 "music", // MusicRole
266 "video", // VideoRole
267 "phone", // VoiceCommunicationRole
268 "event", // AlarmRole
269 "event", // NotificationRole
270 "phone", // RingtoneRole
271 "a11y", // AccessibilityRole
272 nullptr, // SonificationRole
273 "game" // GameRole
274 };
275
276 const char *r = mediaRoleFromAudioRole[m_role];
277 if (r)
278 pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, r);
279#endif
280
281 m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, &channel_map, propList);
282 pa_proplist_free(propList);
283
284 if (!m_stream) {
285 qCWarning(qLcPulseAudioOut) << "QAudioSink: pa_stream_new_with_proplist() failed!";
286 pulseEngine->unlock();
287
288 setStateAndError(QAudio::StoppedState, QAudio::OpenError, true);
289 return false;
290 }
291
292 pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this);
293 pa_stream_set_write_callback(m_stream, outputStreamWriteCallback, this);
294
295 pa_stream_set_underflow_callback(m_stream, outputStreamUnderflowCallback, this);
296 pa_stream_set_overflow_callback(m_stream, outputStreamOverflowCallback, this);
297 pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this);
298
299 pa_buffer_attr requestedBuffer;
300 requestedBuffer.fragsize = (uint32_t)-1;
301 requestedBuffer.maxlength = (uint32_t)-1;
302 requestedBuffer.minreq = (uint32_t)-1;
303 requestedBuffer.prebuf = (uint32_t)-1;
304 requestedBuffer.tlength = m_bufferSize;
305
306 pa_stream_flags flags = pa_stream_flags(PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY);
307 if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : nullptr, flags, nullptr, nullptr) < 0) {
308 qCWarning(qLcPulseAudioOut) << "pa_stream_connect_playback() failed!";
309 pa_stream_unref(m_stream);
310 m_stream = nullptr;
311 pulseEngine->unlock();
312 setStateAndError(QAudio::StoppedState, QAudio::OpenError, true);
313 return false;
314 }
315
316 while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
317 pa_threaded_mainloop_wait(pulseEngine->mainloop());
318
319 const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream);
320 m_periodTime = SinkPeriodTimeMs;
321 m_periodSize = pa_usec_to_bytes(m_periodTime * 1000, &m_spec);
322 m_bufferSize = buffer->tlength;
323 m_audioBuffer.resize(buffer->maxlength);
324
325 const qint64 streamSize = m_audioSource ? m_audioSource->size() : 0;
326 if (m_pullMode && streamSize > 0 && static_cast<qint64>(buffer->prebuf) > streamSize) {
327 pa_buffer_attr newBufferAttr;
328 newBufferAttr = *buffer;
329 newBufferAttr.prebuf = streamSize;
330 PAOperationUPtr(pa_stream_set_buffer_attr(m_stream, &newBufferAttr,
332 }
333
334 if (Q_UNLIKELY(qLcPulseAudioOut().isEnabled(QtDebugMsg))) {
335 qCDebug(qLcPulseAudioOut) << "Buffering info:";
336 qCDebug(qLcPulseAudioOut) << "\tMax length: " << buffer->maxlength;
337 qCDebug(qLcPulseAudioOut) << "\tTarget length: " << buffer->tlength;
338 qCDebug(qLcPulseAudioOut) << "\tPre-buffering: " << buffer->prebuf;
339 qCDebug(qLcPulseAudioOut) << "\tMinimum request: " << buffer->minreq;
340 qCDebug(qLcPulseAudioOut) << "\tFragment size: " << buffer->fragsize;
341 }
342
343 pulseEngine->unlock();
344
345 connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed);
346
347 m_opened = true;
348
349 startReading();
350
351 m_elapsedTimeOffset = 0;
352
353 return true;
354}
355
356void QPulseAudioSink::close()
357{
358 if (!m_opened)
359 return;
360
361 m_tickTimer.stop();
362
364
365 if (m_stream) {
366 std::lock_guard lock(*pulseEngine);
367
368 pa_stream_set_state_callback(m_stream, nullptr, nullptr);
369 pa_stream_set_write_callback(m_stream, nullptr, nullptr);
370 pa_stream_set_underflow_callback(m_stream, nullptr, nullptr);
371 pa_stream_set_overflow_callback(m_stream, nullptr, nullptr);
372 pa_stream_set_latency_update_callback(m_stream, nullptr, nullptr);
373
374 if (auto prevOp = exchangeDrainOperation(nullptr))
375 // cancel the draining callback that is not relevant already
376 pa_operation_cancel(prevOp.get());
377
378 PAOperationUPtr(pa_stream_drain(m_stream, outputStreamDrainComplete, nullptr));
379
380 pa_stream_disconnect(m_stream);
381 pa_stream_unref(m_stream);
382 m_stream = nullptr;
383 }
384
385 disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed);
386
387 if (m_audioSource) {
388 if (m_pullMode) {
389 disconnect(m_audioSource, &QIODevice::readyRead, this, nullptr);
390 } else {
391 delete m_audioSource;
392 m_audioSource = nullptr;
393 }
394 }
395 m_opened = false;
396 m_resuming = false;
397 m_audioBuffer.clear();
398}
399
401{
402 if (event->timerId() == m_tickTimer.timerId())
403 userFeed();
404
406}
407
408void QPulseAudioSink::userFeed()
409{
410 const auto state = this->state();
412 return;
413
414 m_resuming = false;
415
416 if (m_pullMode) {
417 setStateAndError(QAudio::ActiveState, QAudio::NoError);
418 int writableSize = bytesFree();
419 int chunks = writableSize / m_periodSize;
420 if (chunks == 0)
421 return;
422
423 const int input = std::min(
424 m_periodSize,
425 static_cast<int>(m_audioBuffer.size())); // always request 1 chunk of data from user
426
427 Q_ASSERT(!m_audioBuffer.empty());
428 int audioBytesPulled = m_audioSource->read(m_audioBuffer.data(), input);
429 Q_ASSERT(audioBytesPulled <= input);
430 if (audioBytesPulled > 0) {
431 if (audioBytesPulled > input) {
432 qCWarning(qLcPulseAudioOut) << "Invalid audio data size provided by pull source:"
433 << audioBytesPulled << "should be less than" << input;
434 audioBytesPulled = input;
435 }
436 qint64 bytesWritten = write(m_audioBuffer.data(), audioBytesPulled);
437 Q_ASSERT(bytesWritten == audioBytesPulled); //unfinished write should not happen since the data provided is less than writableSize
439
440 if (chunks > 1) {
441 // PulseAudio needs more data. Ask for it immediately.
443 }
444 } else if (audioBytesPulled == 0) {
445 m_tickTimer.stop();
446 const auto atEnd = m_audioSource->atEnd();
447 qCDebug(qLcPulseAudioOut) << "No more data available, source is done:" << atEnd;
448 setStateAndError(QAudio::IdleState, atEnd ? QAudio::NoError : QAudio::UnderrunError);
449 }
450 } else {
452 setStateAndError(state, QAudio::UnderrunError);
453 }
454}
455
456qint64 QPulseAudioSink::write(const char *data, qint64 len)
457{
459
460 pulseEngine->lock();
461
462 size_t nbytes = len;
463 void *dest = nullptr;
464
465 if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) {
466 pulseEngine->unlock();
467 qCWarning(qLcPulseAudioOut) << "pa_stream_begin_write error:"
468 << pa_strerror(pa_context_errno(pulseEngine->context()));
469 setStateAndError(state(), QAudio::IOError);
470 return 0;
471 }
472
473 len = qMin(len, qint64(nbytes));
474
475 if (m_volume < 1.0f) {
476 // Don't use PulseAudio volume, as it might affect all other streams of the same category
477 // or even affect the system volume if flat volumes are enabled
478 QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, dest, len);
479 } else {
480 memcpy(dest, data, len);
481 }
482
483 data = reinterpret_cast<char *>(dest);
484
485 if ((pa_stream_write(m_stream, data, len, nullptr, 0, PA_SEEK_RELATIVE)) < 0) {
486 pulseEngine->unlock();
487 qCWarning(qLcPulseAudioOut) << "pa_stream_write error:"
488 << pa_strerror(pa_context_errno(pulseEngine->context()));
489 setStateAndError(state(), QAudio::IOError);
490 return 0;
491 }
492
493 pulseEngine->unlock();
494 m_totalTimeValue += len;
495
496 setStateAndError(QAudio::ActiveState, QAudio::NoError);
497
498 return len;
499}
500
502{
503 if (m_deviceState == QAudio::StoppedState)
504 return;
505
506 close();
507
508 setStateAndError(QAudio::StoppedState, QAudio::NoError);
509}
510
512{
513 const auto state = this->state();
515 return 0;
516
517 std::lock_guard lock(*QPulseAudioEngine::instance());
518 return pa_stream_writable_size(m_stream);
519}
520
522{
523 m_bufferSize = value;
524}
525
527{
528 return m_bufferSize;
529}
530
531static qint64 operator-(timeval t1, timeval t2)
532{
533 constexpr qint64 secsToUSecs = 1000000;
534 return (t1.tv_sec - t2.tv_sec)*secsToUSecs + (t1.tv_usec - t2.tv_usec);
535}
536
538{
539 const auto state = this->state();
540 if (!m_stream || state == QAudio::StoppedState)
541 return 0;
543 return lastProcessedUSecs;
544
545 auto info = pa_stream_get_timing_info(m_stream);
546 if (!info)
547 return lastProcessedUSecs;
548
549 // if the info changed, update our cached data, and recalculate the average latency
550 if (info->timestamp - lastTimingInfo > 0) {
551 lastTimingInfo.tv_sec = info->timestamp.tv_sec;
552 lastTimingInfo.tv_usec = info->timestamp.tv_usec;
553 averageLatency = 0; // also use that as long as we don't have valid data from the timing info
554
555 // Only use timing values when playing, otherwise the latency numbers can be way off
556 if (info->since_underrun >= 0 && pa_bytes_to_usec(info->since_underrun, &m_spec) > info->sink_usec) {
557 latencyList.append(info->sink_usec);
558 // Average over the last X timing infos to keep numbers more stable.
559 // 10 seems to be a decent number that keeps values relatively stable but doesn't make the list too big
560 const int latencyListMaxSize = 10;
561 if (latencyList.size() > latencyListMaxSize)
562 latencyList.pop_front();
563 for (const auto l : latencyList)
564 averageLatency += l;
565 averageLatency /= latencyList.size();
566 if (averageLatency < 0)
567 averageLatency = 0;
568 }
569 }
570
571 const qint64 usecsRead = info->read_index < 0 ? 0 : pa_bytes_to_usec(info->read_index, &m_spec);
572 const qint64 usecsWritten = info->write_index < 0 ? 0 : pa_bytes_to_usec(info->write_index, &m_spec);
573
574 // processed data is the amount read by the server minus its latency
575 qint64 usecs = usecsRead - averageLatency;
576
577 timeval tv;
578 gettimeofday(&tv, nullptr);
579
580 // and now adjust for the time since the last update
581 qint64 timeSinceUpdate = tv - info->timestamp;
582 if (timeSinceUpdate > 0)
583 usecs += timeSinceUpdate;
584
585 // We can never have processed more than we've written to the sink
586 if (usecs > usecsWritten)
587 usecs = usecsWritten;
588
589 // make sure timing is monotonic
590 if (usecs < lastProcessedUSecs)
591 usecs = lastProcessedUSecs;
592 else
593 lastProcessedUSecs = usecs;
594
595 return usecs;
596}
597
599{
600 if (m_deviceState == QAudio::SuspendedState) {
601 m_resuming = true;
602
603 {
605
606 std::lock_guard lock(*pulseEngine);
607
608 PAOperationUPtr operation(
609 pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, nullptr));
610 pulseEngine->wait(operation.get());
611
612 operation.reset(pa_stream_trigger(m_stream, outputStreamSuccessCallback, nullptr));
613 pulseEngine->wait(operation.get());
614 }
615
616 m_tickTimer.start(m_periodTime, this);
617
618 setStateAndError(m_suspendedInState, QAudio::NoError);
619 }
620}
621
623{
624 m_format = format;
625}
626
628{
629 return m_format;
630}
631
633{
634 const auto state = this->state();
635 if (state == QAudio::ActiveState || state == QAudio::IdleState) {
636 m_suspendedInState = state;
637 setStateAndError(QAudio::SuspendedState, QAudio::NoError);
638
639 m_tickTimer.stop();
640
642
643 std::lock_guard lock(*pulseEngine);
644
645 PAOperationUPtr operation(
646 pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, nullptr));
647 pulseEngine->wait(operation.get());
648 }
649}
650
652{
653 stop();
654}
655
657{
658 m_audioDevice = qobject_cast<QPulseAudioSink*>(audio);
659}
660
662{
663 Q_UNUSED(data);
664 Q_UNUSED(len);
665
666 return 0;
667}
668
670{
671 qint64 written = 0;
672
673 const auto state = m_audioDevice->state();
675 while (written < len) {
676 int chunk = m_audioDevice->write(data+written, (len-written));
677 if (chunk <= 0)
678 return written;
679 written+=chunk;
680 }
681 }
682
683 return written;
684}
685
687{
688 if (qFuzzyCompare(m_volume, vol))
689 return;
690
691 m_volume = qBound(qreal(0), vol, qreal(1));
692}
693
695{
696 return m_volume;
697}
698
699void QPulseAudioSink::onPulseContextFailed()
700{
701 close();
702
703 setStateAndError(QAudio::StoppedState, QAudio::FatalError);
704}
705
706PAOperationUPtr QPulseAudioSink::exchangeDrainOperation(pa_operation *newOperation)
707{
708 return PAOperationUPtr(m_drainOperation.exchange(newOperation));
709}
710
712
713#include "moc_qpulseaudiosink_p.cpp"
IOBluetoothDevice * device
PulseOutputPrivate(QPulseAudioSink *audio)
qint64 readData(char *data, qint64 len) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
The QAudioFormat class stores audio stream parameter information.
constexpr int sampleRate() const noexcept
Returns the current sample rate in Hertz.
constexpr int bytesPerFrame() const
Returns the number of bytes required to represent one frame (a sample in each channel) in this format...
void start(int msec, QObject *obj)
\obsolete Use chrono overload instead.
int timerId() const noexcept
Returns the timer's ID.
Definition qbasictimer.h:35
void stop()
Stops the timer.
bool isActive() const noexcept
Returns true if the timer is running and has not been stopped; otherwise returns false.
Definition qbasictimer.h:34
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes some pending events for the calling thread according to the specified flags.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
qsizetype size() const noexcept
Definition qlist.h:386
void pop_front() noexcept
Definition qlist.h:677
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
virtual void timerEvent(QTimerEvent *event)
This event handler can be reimplemented in a subclass to receive timer events for the object.
Definition qobject.cpp:1433
void errorChanged(QAudio::Error error)
void stateChanged(QAudio::State state)
static QPulseAudioEngine * instance()
pa_threaded_mainloop * mainloop()
void wait(pa_operation *op)
QAudio::Error error() const override
qreal volume() const override
QIODevice * start() override
QPulseAudioSink(const QByteArray &device, QObject *parent)
void setVolume(qreal volume) override
qint64 processedUSecs() const override
void suspend() override
void resume() override
void setBufferSize(qsizetype value) override
void reset() override
QAudio::State state() const override
friend class PulseOutputPrivate
void setFormat(const QAudioFormat &format) override
void timerEvent(QTimerEvent *event) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
QAudioFormat format() const override
qsizetype bytesFree() const override
void stop() override
qsizetype bufferSize() const override
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
Definition qcoreevent.h:359
else opt state
[0]
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
The QAudio namespace contains enums used by the audio classes.
State
\value ActiveState Audio data is being processed, this state is set after start() is called and while...
Definition qaudio.h:25
@ StoppedState
Definition qaudio.h:25
@ SuspendedState
Definition qaudio.h:25
@ IdleState
Definition qaudio.h:25
@ ActiveState
Definition qaudio.h:25
Error
\value NoError No errors have occurred \value OpenError An error occurred opening the audio device \v...
Definition qaudio.h:24
@ UnderrunError
Definition qaudio.h:24
@ FatalError
Definition qaudio.h:24
@ OpenError
Definition qaudio.h:24
@ NoError
Definition qaudio.h:24
@ IOError
Definition qaudio.h:24
static QString sampleFormatToQString(pa_sample_format format)
pa_channel_map channelMapForAudioFormat(const QAudioFormat &format)
pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format)
Combined button and popup list for selecting options.
bool isEnabled()
@ QueuedConnection
#define Q_UNLIKELY(x)
DBusConnection const char DBusError * error
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
@ QtDebugMsg
Definition qlogging.h:30
#define qWarning
Definition qlogging.h:162
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLenum GLsizei GLuint GLint * bytesWritten
GLboolean r
[2]
GLenum GLuint GLenum GLsizei length
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint buffer
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
struct _cl_event * event
GLenum GLsizei len
GLenum GLenum GLenum input
static void outputStreamStateCallback(pa_stream *stream, void *userdata)
static void outputStreamOverflowCallback(pa_stream *stream, void *userdata)
QT_BEGIN_NAMESPACE const int SinkPeriodTimeMs
static void outputStreamSuccessCallback(pa_stream *stream, int success, void *userdata)
static void outputStreamUnderflowCallback(pa_stream *stream, void *userdata)
static void streamAdjustPrebufferCallback(pa_stream *stream, int success, void *userdata)
static void outputStreamDrainComplete(pa_stream *stream, int success, void *userdata)
static qint64 operator-(timeval t1, timeval t2)
static void outputStreamLatencyCallback(pa_stream *stream, void *userdata)
static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
std::unique_ptr< pa_operation, PAOperationDeleter > PAOperationUPtr
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define t2
#define emit
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:72
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
QFileInfo info(fileName)
[8]
QObject::connect nullptr
myObject disconnect()
[26]
QReadWriteLock lock
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent