Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
qsoundeffect.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 <QtMultimedia/private/qtmultimediaglobal_p.h>
5#include "qsoundeffect.h"
6#include "qsamplecache_p.h"
7#include "qaudiodevice.h"
8#include "qaudiosink.h"
9#include "qmediadevices.h"
10#include <QtCore/qloggingcategory.h>
11#include <private/qplatformmediadevices_p.h>
12
13static Q_LOGGING_CATEGORY(qLcSoundEffect, "qt.multimedia.soundeffect")
14
16
17Q_GLOBAL_STATIC(QSampleCache, sampleCache)
18
19class QSoundEffectPrivate : public QIODevice
20{
21public:
22 QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice = QAudioDevice());
23 ~QSoundEffectPrivate() override = default;
24
25 qint64 readData(char *data, qint64 len) override;
26 qint64 writeData(const char *data, qint64 len) override;
27 qint64 size() const override {
28 if (m_sample->state() != QSample::Ready)
29 return 0;
30 return m_loopCount == QSoundEffect::Infinite ? 0 : m_loopCount * m_sample->data().size();
31 }
32 qint64 bytesAvailable() const override {
33 if (m_sample->state() != QSample::Ready)
34 return 0;
35 return m_loopCount == QSoundEffect::Infinite
36 ? std::numeric_limits<qint64>::max() : m_runningCount * m_sample->data().size() - m_offset;
37 }
38 bool isSequential() const override {
39 return m_loopCount == QSoundEffect::Infinite;
40 }
41 bool atEnd() const override {
42 return m_runningCount == 0;
43 }
44
45 void setLoopsRemaining(int loopsRemaining);
46 void setStatus(QSoundEffect::Status status);
47 void setPlaying(bool playing);
48
49public Q_SLOTS:
50 void sampleReady();
51 void decoderError();
52 void stateChanged(QAudio::State);
53
54public:
55 QSoundEffect *q_ptr;
56 QUrl m_url;
57 int m_loopCount = 1;
58 int m_runningCount = 0;
59 bool m_playing = false;
61 QAudioSink *m_audioOutput = nullptr;
62 QSample *m_sample = nullptr;
63 bool m_muted = false;
64 float m_volume = 1.0;
65 bool m_sampleReady = false;
66 qint64 m_offset = 0;
67 QAudioDevice m_audioDevice;
68};
69
70QSoundEffectPrivate::QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice)
71 : QIODevice(q)
72 , q_ptr(q)
73 , m_audioDevice(audioDevice)
74{
76
78}
79
80void QSoundEffectPrivate::sampleReady()
81{
82 if (m_status == QSoundEffect::Error)
83 return;
84
85 qCDebug(qLcSoundEffect) << this << "sampleReady: sample size:" << m_sample->data().size();
86 disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
87 disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
88 if (!m_audioOutput) {
89 m_audioOutput = new QAudioSink(m_audioDevice, m_sample->format());
90 connect(m_audioOutput, &QAudioSink::stateChanged, this, &QSoundEffectPrivate::stateChanged);
91 if (!m_muted)
92 m_audioOutput->setVolume(m_volume);
93 else
94 m_audioOutput->setVolume(0);
95 }
96 m_sampleReady = true;
97 setStatus(QSoundEffect::Ready);
98
99 if (m_playing && m_audioOutput->state() == QAudio::StoppedState) {
100 qCDebug(qLcSoundEffect) << this << "starting playback on audiooutput";
101 m_audioOutput->start(this);
102 }
103}
104
105void QSoundEffectPrivate::decoderError()
106{
107 qWarning("QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
108 disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
109 disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
110 m_playing = false;
111 setStatus(QSoundEffect::Error);
112}
113
114void QSoundEffectPrivate::stateChanged(QAudio::State state)
115{
116 qCDebug(qLcSoundEffect) << this << "stateChanged " << state;
117 if ((state == QAudio::IdleState && m_runningCount == 0) || state == QAudio::StoppedState)
118 q_ptr->stop();
119}
120
121qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
122{
123 qCDebug(qLcSoundEffect) << this << "readData" << len << m_runningCount;
124 if (!len)
125 return 0;
126 if (m_sample->state() != QSample::Ready)
127 return 0;
128 if (m_runningCount == 0 || !m_playing)
129 return 0;
130
132
133 const int sampleSize = m_sample->data().size();
134 const char* sampleData = m_sample->data().constData();
135
136 while (len && m_runningCount) {
137 int toWrite = qMin(sampleSize - m_offset, len);
138 memcpy(data, sampleData + m_offset, toWrite);
139 bytesWritten += toWrite;
140 data += toWrite;
141 len -= toWrite;
142 m_offset += toWrite;
143 if (m_offset >= sampleSize) {
144 if (m_runningCount > 0 && m_runningCount != QSoundEffect::Infinite)
145 setLoopsRemaining(m_runningCount - 1);
146 m_offset = 0;
147 }
148 }
149
150 return bytesWritten;
151}
152
153qint64 QSoundEffectPrivate::writeData(const char *data, qint64 len)
154{
155 Q_UNUSED(data);
156 Q_UNUSED(len);
157 return 0;
158}
159
160void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining)
161{
162 if (m_runningCount == loopsRemaining)
163 return;
164 qCDebug(qLcSoundEffect) << this << "setLoopsRemaining " << loopsRemaining;
165 m_runningCount = loopsRemaining;
166 emit q_ptr->loopsRemainingChanged();
167}
168
169void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
170{
171 qCDebug(qLcSoundEffect) << this << "setStatus" << status;
172 if (m_status == status)
173 return;
174 bool oldLoaded = q_ptr->isLoaded();
175 m_status = status;
176 emit q_ptr->statusChanged();
177 if (oldLoaded != q_ptr->isLoaded())
178 emit q_ptr->loadedChanged();
179}
180
181void QSoundEffectPrivate::setPlaying(bool playing)
182{
183 qCDebug(qLcSoundEffect) << this << "setPlaying(" << playing << ")" << m_playing;
184 if (m_audioOutput) {
185 m_audioOutput->stop();
186 if (playing && !m_sampleReady)
187 return;
188 }
189
190 if (m_playing == playing)
191 return;
192 m_playing = playing;
193
194 if (m_audioOutput && playing)
195 m_audioOutput->start(this);
196
197 emit q_ptr->playingChanged();
198}
199
268{
269}
270
275 : QObject(parent)
276 , d(new QSoundEffectPrivate(this, audioDevice))
277{
278}
279
284{
285 stop();
286 if (d->m_audioOutput) {
287 d->m_audioOutput->stop();
288 d->m_audioOutput->deleteLater();
289 d->m_sample->release();
290 }
291 delete d;
292}
293
300{
301 // Only return supported mime types if we have a audio device available
303 if (devices.isEmpty())
304 return QStringList();
305
306 return QStringList() << QLatin1String("audio/x-wav")
307 << QLatin1String("audio/wav")
308 << QLatin1String("audio/wave")
309 << QLatin1String("audio/x-pn-wav");
310}
311
330{
331 return d->m_url;
332}
333
336{
337 qCDebug(qLcSoundEffect) << this << "setSource current=" << d->m_url << ", to=" << url;
338 if (d->m_url == url)
339 return;
340
341 Q_ASSERT(d->m_url != url);
342
343 stop();
344
345 d->m_url = url;
346
347 d->m_sampleReady = false;
348
349 if (url.isEmpty()) {
350 d->setStatus(QSoundEffect::Null);
351 return;
352 }
353
354 if (!url.isValid()) {
355 d->setStatus(QSoundEffect::Error);
356 return;
357 }
358
359 if (d->m_sample) {
360 if (!d->m_sampleReady) {
361 QObject::disconnect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
362 QObject::disconnect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
363 }
364 d->m_sample->release();
365 d->m_sample = nullptr;
366 }
367
368 if (d->m_audioOutput) {
369 QObject::disconnect(d->m_audioOutput, &QAudioSink::stateChanged, d, &QSoundEffectPrivate::stateChanged);
370 d->m_audioOutput->stop();
371 d->m_audioOutput->deleteLater();
372 d->m_audioOutput = nullptr;
373 }
374
375 d->setStatus(QSoundEffect::Loading);
376 d->m_sample = sampleCache()->requestSample(url);
377 QObject::connect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
378 QObject::connect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
379
380 switch (d->m_sample->state()) {
381 case QSample::Ready:
382 d->sampleReady();
383 break;
384 case QSample::Error:
385 d->decoderError();
386 break;
387 default:
388 break;
389 }
390
392}
393
419{
420 return d->m_loopCount;
421}
422
439void QSoundEffect::setLoopCount(int loopCount)
440{
441 if (loopCount < 0 && loopCount != Infinite) {
442 qWarning("SoundEffect: loops should be SoundEffect.Infinite, 0 or positive integer");
443 return;
444 }
445 if (loopCount == 0)
446 loopCount = 1;
447 if (d->m_loopCount == loopCount)
448 return;
449
450 d->m_loopCount = loopCount;
451 if (d->m_playing)
452 d->setLoopsRemaining(loopCount);
454}
455
462{
463 return d->m_audioDevice;
464}
465
467{
468 if (d->m_audioDevice == device)
469 return;
470 // ### recreate the QAudioSink if needed
471 d->m_audioDevice = device;
473}
474
488{
489 return d->m_runningCount;
490}
491
492
518{
519 if (d->m_audioOutput && !d->m_muted)
520 return d->m_audioOutput->volume();
521
522 return d->m_volume;
523}
524
537void QSoundEffect::setVolume(float volume)
538{
539 volume = qBound(0.0f, volume, 1.0f);
540 if (d->m_volume == volume)
541 return;
542
543 d->m_volume = volume;
544
545 if (d->m_audioOutput && !d->m_muted)
546 d->m_audioOutput->setVolume(volume);
547
549}
550
565{
566 return d->m_muted;
567}
568
577{
578 if (d->m_muted == muted)
579 return;
580
581 if (muted && d->m_audioOutput)
582 d->m_audioOutput->setVolume(0);
583 else if (!muted && d->m_audioOutput && d->m_muted)
584 d->m_audioOutput->setVolume(d->m_volume);
585
586 d->m_muted = muted;
588}
589
601{
602 return d->m_status == QSoundEffect::Ready;
603}
604
622{
623 d->m_offset = 0;
624 d->setLoopsRemaining(d->m_loopCount);
625 qCDebug(qLcSoundEffect) << this << "play" << d->m_loopCount << d->m_runningCount;
626 if (d->m_status == QSoundEffect::Null || d->m_status == QSoundEffect::Error) {
627 d->setStatus(QSoundEffect::Null);
628 return;
629 }
630 d->setPlaying(true);
631}
632
646{
647 return d->m_playing;
648}
649
686{
687 return d->m_status;
688}
689
703{
704 if (!d->m_playing)
705 return;
706 qCDebug(qLcSoundEffect) << "stop()";
707 d->m_offset = 0;
708
709 d->setPlaying(false);
710}
711
712/* Signals */
713
802
803#include "moc_qsoundeffect.cpp"
IOBluetoothDevice * device
The QAudioDevice class provides an information about audio devices and their functionality.
The QAudioSink class provides an interface for sending audio data to an audio output device.
Definition qaudiosink.h:24
void stateChanged(QAudio::State state)
This signal is emitted when the device state has changed.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
Definition qlist.h:74
QList< QAudioDevice > audioOutputs
\qmlproperty list<audioDevice> QtMultimedia::MediaDevices::audioOutputs Contains a list of available ...
\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
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
static QPlatformMediaDevices * instance()
void ready()
void error()
The QSoundEffect class provides a way to play low latency sound effects.
void setLoopCount(int loopCount)
Set the total number of times to play this sound effect to loopCount.
void sourceChanged()
The sourceChanged signal is emitted when the source has been changed.
int loopCount() const
Returns the total number of times that this sound effect will be played before stopping.
void setVolume(float volume)
Sets the sound effect volume to volume.
Status status
\qmlproperty enumeration QtMultimedia::SoundEffect::status
bool isMuted() const
Returns whether this sound effect is muted.
void stop()
\qmlmethod QtMultimedia::SoundEffect::stop()
~QSoundEffect()
Destroys this sound effect.
bool muted
\qmlproperty bool QtMultimedia::SoundEffect::muted
QUrl source
\qmlproperty url QtMultimedia::SoundEffect::source
void audioDeviceChanged()
bool isLoaded() const
Returns whether the sound effect has finished loading the \l source().
float volume
\qmlproperty qreal QtMultimedia::SoundEffect::volume
bool isPlaying() const
Returns true if the sound effect is currently playing, or false otherwise.
void volumeChanged()
\qmlsignal QtMultimedia::SoundEffect::loopsRemainingChanged()
void play()
\qmlmethod QtMultimedia::SoundEffect::play()
QAudioDevice audioDevice
Returns the QAudioDevice instance.
int loopsRemaining
\qmlproperty int QtMultimedia::SoundEffect::loopsRemaining
void setAudioDevice(const QAudioDevice &device)
void setMuted(bool muted)
Sets whether to mute this sound effect's playback.
static QStringList supportedMimeTypes()
Returns a list of the supported mime types for this platform.
Status
\value Null No source has been set or the source is null.
void setSource(const QUrl &url)
Set the current URL to play to url.
void mutedChanged()
\qmlsignal QtMultimedia::SoundEffect::volumeChanged()
void loopCountChanged()
\qmlsignal QtMultimedia::SoundEffect::loadedChanged()
QSoundEffect(QObject *parent=nullptr)
\qmltype SoundEffect \instantiates QSoundEffect
\inmodule QtCore
\inmodule QtCore
Definition qurl.h:94
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1874
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
#define this
Definition dialogs.cpp:9
else opt state
[0]
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
@ IdleState
Definition qaudio.h:25
Combined button and popup list for selecting options.
EGLDeviceEXT * devices
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#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
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLsizei len
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qUtf16Printable(string)
Definition qstring.h:1403
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_SLOTS
#define emit
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:55
file open(QIODevice::ReadOnly)
QUrl url("example.com")
[constructor-url-reference]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
void writeData(const QByteArray &data)
QByteArray readData()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent