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
15QT_BEGIN_NAMESPACE
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;
60 QSoundEffect::Status m_status = QSoundEffect::Null;
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{
75 open(mode: QIODevice::ReadOnly);
76
77 QPlatformMediaDevices::instance()->prepareAudio();
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(sender: m_sample, signal: &QSample::error, receiver: this, slot: &QSoundEffectPrivate::decoderError);
87 disconnect(sender: m_sample, signal: &QSample::ready, receiver: this, slot: &QSoundEffectPrivate::sampleReady);
88 if (!m_audioOutput) {
89 m_audioOutput = new QAudioSink(m_audioDevice, m_sample->format());
90 connect(sender: m_audioOutput, signal: &QAudioSink::stateChanged, context: this, slot: &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(device: this);
102 }
103}
104
105void QSoundEffectPrivate::decoderError()
106{
107 qWarning(msg: "QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
108 disconnect(sender: m_sample, signal: &QSample::ready, receiver: this, slot: &QSoundEffectPrivate::sampleReady);
109 disconnect(sender: m_sample, signal: &QSample::error, receiver: this, slot: &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
131 qint64 bytesWritten = 0;
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(a: sampleSize - m_offset, b: len);
138 memcpy(dest: data, src: sampleData + m_offset, n: 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(device: this);
196
197 emit q_ptr->playingChanged();
198}
199
200/*!
201 \class QSoundEffect
202 \brief The QSoundEffect class provides a way to play low latency sound effects.
203
204 \ingroup multimedia
205 \ingroup multimedia_audio
206 \inmodule QtMultimedia
207
208 This class allows you to play uncompressed audio files (typically WAV files) in
209 a generally lower latency way, and is suitable for "feedback" type sounds in
210 response to user actions (e.g. virtual keyboard sounds, positive or negative
211 feedback for popup dialogs, or game sounds). If low latency is not important,
212 consider using the QMediaPlayer class instead, since it supports a wider
213 variety of media formats and is less resource intensive.
214
215 This example shows how a looping, somewhat quiet sound effect
216 can be played:
217
218 \snippet multimedia-snippets/qsound.cpp 2
219
220 Typically the sound effect should be reused, which allows all the
221 parsing and preparation to be done ahead of time, and only triggered
222 when necessary. This assists with lower latency audio playback.
223
224 \snippet multimedia-snippets/qsound.cpp 3
225
226 Since QSoundEffect requires slightly more resources to achieve lower
227 latency playback, the platform may limit the number of simultaneously playing
228 sound effects.
229*/
230
231
232/*!
233 \qmltype SoundEffect
234 \instantiates QSoundEffect
235 \brief The SoundEffect type provides a way to play sound effects in QML.
236
237 \inmodule QtMultimedia
238 \ingroup multimedia_qml
239 \ingroup multimedia_audio_qml
240 \inqmlmodule QtMultimedia
241
242 This type allows you to play uncompressed audio files (typically WAV files) in
243 a generally lower latency way, and is suitable for "feedback" type sounds in
244 response to user actions (e.g. virtual keyboard sounds, positive or negative
245 feedback for popup dialogs, or game sounds). If low latency is not important,
246 consider using the MediaPlayer type instead, since it support a wider
247 variety of media formats and is less resource intensive.
248
249 Typically the sound effect should be reused, which allows all the
250 parsing and preparation to be done ahead of time, and only triggered
251 when necessary. This is easy to achieve with QML, since you can declare your
252 SoundEffect instance and refer to it elsewhere.
253
254 The following example plays a WAV file on mouse click.
255
256 \snippet multimedia-snippets/soundeffect.qml complete snippet
257
258 Since SoundEffect requires slightly more resources to achieve lower
259 latency playback, the platform may limit the number of simultaneously playing
260 sound effects.
261*/
262
263/*!
264 Creates a QSoundEffect with the given \a parent.
265*/
266QSoundEffect::QSoundEffect(QObject *parent)
267 : QSoundEffect(QAudioDevice(), parent)
268{
269}
270
271/*!
272 Creates a QSoundEffect with the given \a audioDevice and \a parent.
273*/
274QSoundEffect::QSoundEffect(const QAudioDevice &audioDevice, QObject *parent)
275 : QObject(parent)
276 , d(new QSoundEffectPrivate(this, audioDevice))
277{
278}
279
280/*!
281 Destroys this sound effect.
282 */
283QSoundEffect::~QSoundEffect()
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
294/*!
295 \fn QSoundEffect::supportedMimeTypes()
296
297 Returns a list of the supported mime types for this platform.
298*/
299QStringList QSoundEffect::supportedMimeTypes()
300{
301 // Only return supported mime types if we have a audio device available
302 const QList<QAudioDevice> devices = QMediaDevices::audioOutputs();
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
312/*!
313 \qmlproperty url QtMultimedia::SoundEffect::source
314
315 This property holds the url for the sound to play. For the SoundEffect
316 to attempt to load the source, the URL must exist and the application must have read permission
317 in the specified directory. If the desired source is a local file the URL may be specified
318 using either absolute or relative (to the file that declared the SoundEffect) pathing.
319*/
320/*!
321 \property QSoundEffect::source
322
323 This property holds the url for the sound to play. For the SoundEffect
324 to attempt to load the source, the URL must exist and the application must have read permission
325 in the specified directory.
326*/
327
328/*! Returns the URL of the current source to play */
329QUrl QSoundEffect::source() const
330{
331 return d->m_url;
332}
333
334/*! Set the current URL to play to \a url. */
335void QSoundEffect::setSource(const QUrl &url)
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(sender: d->m_sample, signal: &QSample::error, receiver: d, slot: &QSoundEffectPrivate::decoderError);
362 QObject::disconnect(sender: d->m_sample, signal: &QSample::ready, receiver: d, slot: &QSoundEffectPrivate::sampleReady);
363 }
364 d->m_sample->release();
365 d->m_sample = nullptr;
366 }
367
368 if (d->m_audioOutput) {
369 QObject::disconnect(sender: d->m_audioOutput, signal: &QAudioSink::stateChanged, receiver: d, slot: &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(sender: d->m_sample, signal: &QSample::error, context: d, slot: &QSoundEffectPrivate::decoderError);
378 QObject::connect(sender: d->m_sample, signal: &QSample::ready, context: d, slot: &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
391 emit sourceChanged();
392}
393
394/*!
395 \qmlproperty int QtMultimedia::SoundEffect::loops
396
397 This property holds the number of times the sound is played. A value of 0 or 1 means
398 the sound will be played only once; set to SoundEffect.Infinite to enable infinite looping.
399
400 The value can be changed while the sound effect is playing, in which case it will update
401 the remaining loops to the new value.
402*/
403
404/*!
405 \property QSoundEffect::loops
406 This property holds the number of times the sound is played. A value of 0 or 1 means
407 the sound will be played only once; set to SoundEffect.Infinite to enable infinite looping.
408
409 The value can be changed while the sound effect is playing, in which case it will update
410 the remaining loops to the new value.
411*/
412
413/*!
414 Returns the total number of times that this sound effect will be played before stopping.
415
416 See the \l loopsRemaining() method for the number of loops currently remaining.
417 */
418int QSoundEffect::loopCount() const
419{
420 return d->m_loopCount;
421}
422
423/*!
424 \enum QSoundEffect::Loop
425
426 \value Infinite Used as a parameter to \l setLoopCount() for infinite looping
427*/
428
429/*!
430 Set the total number of times to play this sound effect to \a loopCount.
431
432 Setting the loop count to 0 or 1 means the sound effect will be played only once;
433 pass \c QSoundEffect::Infinite to repeat indefinitely. The loop count can be changed while
434 the sound effect is playing, in which case it will update the remaining loops to
435 the new \a loopCount.
436
437 \sa loopsRemaining()
438*/
439void QSoundEffect::setLoopCount(int loopCount)
440{
441 if (loopCount < 0 && loopCount != Infinite) {
442 qWarning(msg: "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);
453 emit loopCountChanged();
454}
455
456/*!
457 \property QSoundEffect::audioDevice
458
459 Returns the QAudioDevice instance.
460*/
461QAudioDevice QSoundEffect::audioDevice()
462{
463 return d->m_audioDevice;
464}
465
466void QSoundEffect::setAudioDevice(const QAudioDevice &device)
467{
468 if (d->m_audioDevice == device)
469 return;
470 // ### recreate the QAudioSink if needed
471 d->m_audioDevice = device;
472 emit audioDeviceChanged();
473}
474
475/*!
476 \qmlproperty int QtMultimedia::SoundEffect::loopsRemaining
477
478 This property contains the number of loops remaining before the sound effect
479 stops by itself, or SoundEffect.Infinite if that's what has been set in \l loops.
480*/
481/*!
482 \property QSoundEffect::loopsRemaining
483
484 This property contains the number of loops remaining before the sound effect
485 stops by itself, or QSoundEffect::Infinite if that's what has been set in \l loops.
486*/
487int QSoundEffect::loopsRemaining() const
488{
489 return d->m_runningCount;
490}
491
492
493/*!
494 \qmlproperty qreal QtMultimedia::SoundEffect::volume
495
496 This property holds the volume of the sound effect playback.
497
498 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
499 range will be clamped.
500
501 The default volume is \c 1.0.
502
503 UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
504 will produce linear changes in perceived loudness, which is what a user would normally expect
505 from a volume control. See \l {QAudio::convertVolume()}{convertVolume()}
506 for more details.
507*/
508/*!
509 \property QSoundEffect::volume
510
511 This property holds the volume of the sound effect playback, from 0.0 (silence) to 1.0 (full volume).
512*/
513
514/*!
515 Returns the current volume of this sound effect, from 0.0 (silent) to 1.0 (maximum volume).
516 */
517float QSoundEffect::volume() const
518{
519 if (d->m_audioOutput && !d->m_muted)
520 return d->m_audioOutput->volume();
521
522 return d->m_volume;
523}
524
525/*!
526 Sets the sound effect volume to \a volume.
527
528 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
529 range will be clamped.
530
531 The default volume is \c 1.0.
532
533 UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
534 will produce linear changes in perceived loudness, which is what a user would normally expect
535 from a volume control. See QAudio::convertVolume() for more details.
536 */
537void QSoundEffect::setVolume(float volume)
538{
539 volume = qBound(min: 0.0f, val: volume, max: 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
548 emit volumeChanged();
549}
550
551/*!
552 \qmlproperty bool QtMultimedia::SoundEffect::muted
553
554 This property provides a way to control muting. A value of \c true will mute this effect.
555 Otherwise, playback will occur with the currently specified \l volume.
556*/
557/*!
558 \property QSoundEffect::muted
559
560 This property provides a way to control muting. A value of \c true will mute this effect.
561*/
562
563/*! Returns whether this sound effect is muted */
564bool QSoundEffect::isMuted() const
565{
566 return d->m_muted;
567}
568
569/*!
570 Sets whether to mute this sound effect's playback.
571
572 If \a muted is true, playback will be muted (silenced),
573 and otherwise playback will occur with the currently
574 specified volume().
575*/
576void QSoundEffect::setMuted(bool muted)
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;
587 emit mutedChanged();
588}
589
590/*!
591 \fn QSoundEffect::isLoaded() const
592
593 Returns whether the sound effect has finished loading the \l source().
594*/
595/*!
596 \qmlmethod bool QtMultimedia::SoundEffect::isLoaded()
597
598 Returns whether the sound effect has finished loading the \l source.
599*/
600bool QSoundEffect::isLoaded() const
601{
602 return d->m_status == QSoundEffect::Ready;
603}
604
605/*!
606 \qmlmethod QtMultimedia::SoundEffect::play()
607
608 Start playback of the sound effect, looping the effect for the number of
609 times as specified in the loops property.
610
611 This is the default method for SoundEffect.
612
613 \snippet multimedia-snippets/soundeffect.qml play sound on click
614*/
615/*!
616 \fn QSoundEffect::play()
617
618 Start playback of the sound effect, looping the effect for the number of
619 times as specified in the loops property.
620*/
621void QSoundEffect::play()
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
633/*!
634 \qmlproperty bool QtMultimedia::SoundEffect::playing
635
636 This property indicates whether the sound effect is playing or not.
637*/
638/*!
639 \property QSoundEffect::playing
640
641 This property indicates whether the sound effect is playing or not.
642*/
643
644/*! Returns true if the sound effect is currently playing, or false otherwise */
645bool QSoundEffect::isPlaying() const
646{
647 return d->m_playing;
648}
649
650/*!
651 \enum QSoundEffect::Status
652
653 \value Null No source has been set or the source is null.
654 \value Loading The SoundEffect is trying to load the source.
655 \value Ready The source is loaded and ready for play.
656 \value Error An error occurred during operation, such as failure of loading the source.
657
658*/
659
660/*!
661 \qmlproperty enumeration QtMultimedia::SoundEffect::status
662
663 This property indicates the current status of the SoundEffect
664 as enumerated within SoundEffect.
665 Possible statuses are listed below.
666
667 \table
668 \header \li Value \li Description
669 \row \li SoundEffect.Null \li No source has been set or the source is null.
670 \row \li SoundEffect.Loading \li The SoundEffect is trying to load the source.
671 \row \li SoundEffect.Ready \li The source is loaded and ready for play.
672 \row \li SoundEffect.Error \li An error occurred during operation, such as failure of loading the source.
673 \endtable
674*/
675/*!
676 \property QSoundEffect::status
677
678 This property indicates the current status of the sound effect
679 from the \l QSoundEffect::Status enumeration.
680*/
681
682/*!
683 Returns the current status of this sound effect.
684 */
685QSoundEffect::Status QSoundEffect::status() const
686{
687 return d->m_status;
688}
689
690/*!
691 \qmlmethod QtMultimedia::SoundEffect::stop()
692
693 Stop current playback.
694
695 */
696/*!
697 \fn QSoundEffect::stop()
698
699 Stop current playback.
700
701 */
702void QSoundEffect::stop()
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
714/*!
715 \fn void QSoundEffect::sourceChanged()
716
717 The \c sourceChanged signal is emitted when the source has been changed.
718*/
719/*!
720 \qmlsignal QtMultimedia::SoundEffect::sourceChanged()
721
722 The \c sourceChanged signal is emitted when the source has been changed.
723*/
724/*!
725 \fn void QSoundEffect::loadedChanged()
726
727 The \c loadedChanged signal is emitted when the loading state has changed.
728*/
729/*!
730 \qmlsignal QtMultimedia::SoundEffect::loadedChanged()
731
732 The \c loadedChanged signal is emitted when the loading state has changed.
733*/
734
735/*!
736 \fn void QSoundEffect::loopCountChanged()
737
738 The \c loopCountChanged signal is emitted when the initial number of loops has changed.
739*/
740/*!
741 \qmlsignal QtMultimedia::SoundEffect::loopCountChanged()
742
743 The \c loopCountChanged signal is emitted when the initial number of loops has changed.
744*/
745
746/*!
747 \fn void QSoundEffect::loopsRemainingChanged()
748
749 The \c loopsRemainingChanged signal is emitted when the remaining number of loops has changed.
750*/
751/*!
752 \qmlsignal QtMultimedia::SoundEffect::loopsRemainingChanged()
753
754 The \c loopsRemainingChanged signal is emitted when the remaining number of loops has changed.
755*/
756
757/*!
758 \fn void QSoundEffect::volumeChanged()
759
760 The \c volumeChanged signal is emitted when the volume has changed.
761*/
762/*!
763 \qmlsignal QtMultimedia::SoundEffect::volumeChanged()
764
765 The \c volumeChanged signal is emitted when the volume has changed.
766*/
767
768/*!
769 \fn void QSoundEffect::mutedChanged()
770
771 The \c mutedChanged signal is emitted when the mute state has changed.
772*/
773/*!
774 \qmlsignal QtMultimedia::SoundEffect::mutedChanged()
775
776 The \c mutedChanged signal is emitted when the mute state has changed.
777*/
778
779/*!
780 \fn void QSoundEffect::playingChanged()
781
782 The \c playingChanged signal is emitted when the playing property has changed.
783*/
784/*!
785 \qmlsignal QtMultimedia::SoundEffect::playingChanged()
786
787 The \c playingChanged signal is emitted when the playing property has changed.
788*/
789
790/*!
791 \fn void QSoundEffect::statusChanged()
792
793 The \c statusChanged signal is emitted when the status property has changed.
794*/
795/*!
796 \qmlsignal QtMultimedia::SoundEffect::statusChanged()
797
798 The \c statusChanged signal is emitted when the status property has changed.
799*/
800
801QT_END_NAMESPACE
802
803#include "moc_qsoundeffect.cpp"
804

source code of qtmultimedia/src/multimedia/audio/qsoundeffect.cpp