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 "qaudiobuffer.h"
11#include <QtCore/qloggingcategory.h>
12#include <private/qplatformmediadevices_p.h>
13#include <private/qplatformmediaintegration_p.h>
14#include <private/qplatformaudioresampler_p.h>
15
16static Q_LOGGING_CATEGORY(qLcSoundEffect, "qt.multimedia.soundeffect")
17
18QT_BEGIN_NAMESPACE
19
20Q_GLOBAL_STATIC(QSampleCache, sampleCache)
21
22namespace
23{
24struct AudioSinkDeleter
25{
26 void operator ()(QAudioSink* sink) const
27 {
28 sink->stop();
29 // Investigate:should we just delete?
30 sink->deleteLater();
31 }
32};
33
34struct SampleDeleter
35{
36 void operator ()(QSample* sample) const
37 {
38 sample->release();
39 }
40};
41}
42
43class QSoundEffectPrivate : public QIODevice
44{
45public:
46 QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice = QAudioDevice());
47 ~QSoundEffectPrivate() override = default;
48
49 qint64 readData(char *data, qint64 len) override;
50 qint64 writeData(const char *data, qint64 len) override;
51 qint64 size() const override {
52 if (m_sample->state() != QSample::Ready)
53 return 0;
54 return m_loopCount == QSoundEffect::Infinite ? 0 : m_loopCount * m_audioBuffer.byteCount();
55 }
56 qint64 bytesAvailable() const override {
57 if (m_sample->state() != QSample::Ready)
58 return 0;
59 if (m_loopCount == QSoundEffect::Infinite)
60 return std::numeric_limits<qint64>::max();
61 return m_runningCount * m_audioBuffer.byteCount() - m_offset;
62 }
63 bool isSequential() const override {
64 return m_loopCount == QSoundEffect::Infinite;
65 }
66 bool atEnd() const override {
67 return m_runningCount == 0;
68 }
69
70 void setLoopsRemaining(int loopsRemaining);
71 void setStatus(QSoundEffect::Status status);
72 void setPlaying(bool playing);
73 bool updateAudioOutput();
74
75public Q_SLOTS:
76 void sampleReady(QSample *);
77 void decoderError(QSample *);
78 void stateChanged(QAudio::State);
79
80public:
81 QSoundEffect *q_ptr;
82 QUrl m_url;
83 int m_loopCount = 1;
84 int m_runningCount = 0;
85 bool m_playing = false;
86 QSoundEffect::Status m_status = QSoundEffect::Null;
87 std::unique_ptr<QAudioSink, AudioSinkDeleter> m_audioSink;
88 std::unique_ptr<QSample, SampleDeleter> m_sample;
89 QAudioBuffer m_audioBuffer;
90 bool m_muted = false;
91 float m_volume = 1.0;
92 bool m_sampleReady = false;
93 qint64 m_offset = 0;
94 QAudioDevice m_audioDevice;
95};
96
97QSoundEffectPrivate::QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice)
98 : QIODevice(q)
99 , q_ptr(q)
100 , m_audioDevice(audioDevice)
101{
102 open(mode: QIODevice::ReadOnly);
103
104 QPlatformMediaIntegration::instance()->mediaDevices()->prepareAudio();
105}
106
107void QSoundEffectPrivate::sampleReady(QSample *sample)
108{
109 if (sample && sample != m_sample.get())
110 return;
111
112 if (m_status == QSoundEffect::Error)
113 return;
114
115 qCDebug(qLcSoundEffect) << this << "sampleReady: sample size:" << m_sample->data().size();
116 disconnect(sender: m_sample.get(), signal: &QSample::error, receiver: this, slot: &QSoundEffectPrivate::decoderError);
117 disconnect(sender: m_sample.get(), signal: &QSample::ready, receiver: this, slot: &QSoundEffectPrivate::sampleReady);
118
119 if (!m_audioSink) {
120 if (!updateAudioOutput()) // Create audio sink
121 return; // Returns if no audio devices are available
122 }
123
124 m_sampleReady = true;
125 setStatus(QSoundEffect::Ready);
126
127 if (m_playing && m_audioSink->state() == QAudio::StoppedState) {
128 qCDebug(qLcSoundEffect) << this << "starting playback on audiooutput";
129 m_audioSink->start(device: this);
130 }
131}
132
133void QSoundEffectPrivate::decoderError(QSample *sample)
134{
135 if (sample && sample != m_sample.get())
136 return;
137
138 qWarning(msg: "QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
139 disconnect(sender: m_sample.get(), signal: &QSample::ready, receiver: this, slot: &QSoundEffectPrivate::sampleReady);
140 disconnect(sender: m_sample.get(), signal: &QSample::error, receiver: this, slot: &QSoundEffectPrivate::decoderError);
141 m_playing = false;
142 setStatus(QSoundEffect::Error);
143}
144
145void QSoundEffectPrivate::stateChanged(QAudio::State state)
146{
147 qCDebug(qLcSoundEffect) << this << "stateChanged " << state;
148 if ((state == QAudio::IdleState && m_runningCount == 0) || state == QAudio::StoppedState)
149 q_ptr->stop();
150}
151
152bool QSoundEffectPrivate::updateAudioOutput()
153{
154 const auto audioDevice =
155 m_audioDevice.isNull() ? QMediaDevices::defaultAudioOutput() : m_audioDevice;
156
157 if (audioDevice.isNull()) {
158 // We are likely on a virtual machine, for example in CI
159 qCCritical(qLcSoundEffect) << "Failed to update audio output. No audio devices available.";
160 setStatus(QSoundEffect::Error);
161 return false;
162 }
163
164 if (m_audioDevice.isNull()) {
165 q_ptr->setAudioDevice(audioDevice); // Updates m_audioDevice and emits audioDeviceChanged
166 }
167
168 m_audioBuffer = {};
169
170 Q_ASSERT(m_sample);
171
172 const auto &sampleFormat = m_sample->format();
173 const auto sampleChannelConfig =
174 sampleFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
175 ? QAudioFormat::defaultChannelConfigForChannelCount(channelCount: sampleFormat.channelCount())
176 : sampleFormat.channelConfig();
177
178 if (sampleChannelConfig != audioDevice.channelConfiguration()
179 && audioDevice.channelConfiguration() != QAudioFormat::ChannelConfigUnknown) {
180 qCDebug(qLcSoundEffect) << "Create resampler for channels mapping: config"
181 << sampleFormat.channelConfig() << "=> config"
182 << audioDevice.channelConfiguration();
183 auto outputFormat = sampleFormat;
184 outputFormat.setChannelConfig(audioDevice.channelConfiguration());
185
186 const auto resampler = QPlatformMediaIntegration::instance()->createAudioResampler(
187 m_sample->format(), outputFormat);
188 if (resampler)
189 m_audioBuffer = resampler.value()->resample(data: m_sample->data().constData(),
190 size: m_sample->data().size());
191 else
192 qCDebug(qLcSoundEffect) << "Cannot create resampler for channels mapping";
193 }
194
195 if (!m_audioBuffer.isValid())
196 m_audioBuffer = QAudioBuffer(m_sample->data(), m_sample->format());
197
198 m_audioSink.reset(p: new QAudioSink(audioDevice, m_audioBuffer.format()));
199
200 connect(sender: m_audioSink.get(), signal: &QAudioSink::stateChanged, context: this, slot: &QSoundEffectPrivate::stateChanged);
201 if (!m_muted)
202 m_audioSink->setVolume(m_volume);
203 else
204 m_audioSink->setVolume(0);
205
206 return true;
207}
208
209qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
210{
211 qCDebug(qLcSoundEffect) << this << "readData" << len << m_runningCount;
212 if (!len)
213 return 0;
214 if (m_sample->state() != QSample::Ready)
215 return 0;
216 if (m_runningCount == 0 || !m_playing)
217 return 0;
218
219 qint64 bytesWritten = 0;
220
221 const int sampleSize = m_audioBuffer.byteCount();
222 const char *sampleData = m_audioBuffer.constData<char>();
223
224 while (len && m_runningCount) {
225 int toWrite = qMax(a: 0, b: qMin(a: sampleSize - m_offset, b: len));
226 memcpy(dest: data, src: sampleData + m_offset, n: toWrite);
227 bytesWritten += toWrite;
228 data += toWrite;
229 len -= toWrite;
230 m_offset += toWrite;
231 if (m_offset >= sampleSize) {
232 if (m_runningCount > 0 && m_runningCount != QSoundEffect::Infinite)
233 setLoopsRemaining(m_runningCount - 1);
234 m_offset = 0;
235 }
236 }
237
238 return bytesWritten;
239}
240
241qint64 QSoundEffectPrivate::writeData(const char *data, qint64 len)
242{
243 Q_UNUSED(data);
244 Q_UNUSED(len);
245 return 0;
246}
247
248void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining)
249{
250 if (m_runningCount == loopsRemaining)
251 return;
252 qCDebug(qLcSoundEffect) << this << "setLoopsRemaining " << loopsRemaining;
253 m_runningCount = loopsRemaining;
254 emit q_ptr->loopsRemainingChanged();
255}
256
257void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
258{
259 qCDebug(qLcSoundEffect) << this << "setStatus" << status;
260 if (m_status == status)
261 return;
262 bool oldLoaded = q_ptr->isLoaded();
263 m_status = status;
264 emit q_ptr->statusChanged();
265 if (oldLoaded != q_ptr->isLoaded())
266 emit q_ptr->loadedChanged();
267}
268
269void QSoundEffectPrivate::setPlaying(bool playing)
270{
271 qCDebug(qLcSoundEffect) << this << "setPlaying(" << playing << ")" << m_playing;
272 if (m_audioSink) {
273 m_audioSink->stop();
274 if (playing && !m_sampleReady)
275 return;
276 }
277
278 if (m_playing == playing)
279 return;
280 m_playing = playing;
281
282 if (m_audioSink && playing)
283 m_audioSink->start(device: this);
284
285 emit q_ptr->playingChanged();
286}
287
288/*!
289 \class QSoundEffect
290 \brief The QSoundEffect class provides a way to play low latency sound effects.
291
292 \ingroup multimedia
293 \ingroup multimedia_audio
294 \inmodule QtMultimedia
295
296 This class allows you to play uncompressed audio files (typically WAV files) in
297 a generally lower latency way, and is suitable for "feedback" type sounds in
298 response to user actions (e.g. virtual keyboard sounds, positive or negative
299 feedback for popup dialogs, or game sounds). If low latency is not important,
300 consider using the QMediaPlayer class instead, since it supports a wider
301 variety of media formats and is less resource intensive.
302
303 This example shows how a looping, somewhat quiet sound effect
304 can be played:
305
306 \snippet multimedia-snippets/qsound.cpp 2
307
308 Typically the sound effect should be reused, which allows all the
309 parsing and preparation to be done ahead of time, and only triggered
310 when necessary. This assists with lower latency audio playback.
311
312 \snippet multimedia-snippets/qsound.cpp 3
313
314 Since QSoundEffect requires slightly more resources to achieve lower
315 latency playback, the platform may limit the number of simultaneously playing
316 sound effects.
317*/
318
319
320/*!
321 \qmltype SoundEffect
322 \nativetype QSoundEffect
323 \brief The SoundEffect type provides a way to play sound effects in QML.
324
325 \inmodule QtMultimedia
326 \ingroup multimedia_qml
327 \ingroup multimedia_audio_qml
328 \inqmlmodule QtMultimedia
329
330 This type allows you to play uncompressed audio files (typically WAV files) in
331 a generally lower latency way, and is suitable for "feedback" type sounds in
332 response to user actions (e.g. virtual keyboard sounds, positive or negative
333 feedback for popup dialogs, or game sounds). If low latency is not important,
334 consider using the MediaPlayer type instead, since it support a wider
335 variety of media formats and is less resource intensive.
336
337 Typically the sound effect should be reused, which allows all the
338 parsing and preparation to be done ahead of time, and only triggered
339 when necessary. This is easy to achieve with QML, since you can declare your
340 SoundEffect instance and refer to it elsewhere.
341
342 The following example plays a WAV file on mouse click.
343
344 \snippet multimedia-snippets/soundeffect.qml complete snippet
345
346 Since SoundEffect requires slightly more resources to achieve lower
347 latency playback, the platform may limit the number of simultaneously playing
348 sound effects.
349*/
350
351/*!
352 Creates a QSoundEffect with the given \a parent.
353*/
354QSoundEffect::QSoundEffect(QObject *parent)
355 : QSoundEffect(QAudioDevice(), parent)
356{
357}
358
359/*!
360 Creates a QSoundEffect with the given \a audioDevice and \a parent.
361*/
362QSoundEffect::QSoundEffect(const QAudioDevice &audioDevice, QObject *parent)
363 : QObject(parent)
364 , d(new QSoundEffectPrivate(this, audioDevice))
365{
366}
367
368/*!
369 Destroys this sound effect.
370 */
371QSoundEffect::~QSoundEffect()
372{
373 stop();
374 d->m_audioSink.reset();
375 d->m_sample.reset();
376 delete d;
377}
378
379/*!
380 \fn QSoundEffect::supportedMimeTypes()
381
382 Returns a list of the supported mime types for this platform.
383*/
384QStringList QSoundEffect::supportedMimeTypes()
385{
386 // Only return supported mime types if we have a audio device available
387 const QList<QAudioDevice> devices = QMediaDevices::audioOutputs();
388 if (devices.isEmpty())
389 return QStringList();
390
391 return QStringList() << QLatin1String("audio/x-wav")
392 << QLatin1String("audio/wav")
393 << QLatin1String("audio/wave")
394 << QLatin1String("audio/x-pn-wav");
395}
396
397/*!
398 \qmlproperty url QtMultimedia::SoundEffect::source
399
400 This property holds the url for the sound to play. For the SoundEffect
401 to attempt to load the source, the URL must exist and the application must have read permission
402 in the specified directory. If the desired source is a local file the URL may be specified
403 using either absolute or relative (to the file that declared the SoundEffect) pathing.
404*/
405/*!
406 \property QSoundEffect::source
407
408 This property holds the url for the sound to play. For the SoundEffect
409 to attempt to load the source, the URL must exist and the application must have read permission
410 in the specified directory.
411*/
412
413/*! Returns the URL of the current source to play */
414QUrl QSoundEffect::source() const
415{
416 return d->m_url;
417}
418
419/*! Set the current URL to play to \a url. */
420void QSoundEffect::setSource(const QUrl &url)
421{
422 qCDebug(qLcSoundEffect) << this << "setSource current=" << d->m_url << ", to=" << url;
423 if (d->m_url == url)
424 return;
425
426 Q_ASSERT(d->m_url != url);
427
428 stop();
429
430 d->m_url = url;
431 d->m_sampleReady = false;
432
433 if (url.isEmpty()) {
434 d->setStatus(QSoundEffect::Null);
435 return;
436 }
437
438 if (!url.isValid()) {
439 d->setStatus(QSoundEffect::Error);
440 return;
441 }
442
443 if (d->m_sample) {
444 if (!d->m_sampleReady) {
445 disconnect(sender: d->m_sample.get(), signal: &QSample::error, receiver: d, slot: &QSoundEffectPrivate::decoderError);
446 disconnect(sender: d->m_sample.get(), signal: &QSample::ready, receiver: d, slot: &QSoundEffectPrivate::sampleReady);
447 }
448 d->m_sample.reset();
449 }
450
451 if (d->m_audioSink) {
452 disconnect(sender: d->m_audioSink.get(), signal: &QAudioSink::stateChanged, receiver: d, slot: &QSoundEffectPrivate::stateChanged);
453 d->m_audioSink.reset();
454 }
455
456 d->setStatus(QSoundEffect::Loading);
457 d->m_sample.reset(p: sampleCache()->requestSample(url));
458 connect(sender: d->m_sample.get(), signal: &QSample::error, context: d, slot: &QSoundEffectPrivate::decoderError);
459 connect(sender: d->m_sample.get(), signal: &QSample::ready, context: d, slot: &QSoundEffectPrivate::sampleReady);
460
461 switch (d->m_sample->state()) {
462 case QSample::Ready:
463 d->sampleReady(sample: d->m_sample.get());
464 break;
465 case QSample::Error:
466 d->decoderError(sample: d->m_sample.get());
467 break;
468 default:
469 break;
470 }
471
472 emit sourceChanged();
473}
474
475/*!
476 \qmlproperty int QtMultimedia::SoundEffect::loops
477
478 This property holds the number of times the sound is played. A value of 0 or 1 means
479 the sound will be played only once; set to SoundEffect.Infinite to enable infinite looping.
480
481 The value can be changed while the sound effect is playing, in which case it will update
482 the remaining loops to the new value.
483*/
484
485/*!
486 \property QSoundEffect::loops
487 This property holds the number of times the sound is played. A value of 0 or 1 means
488 the sound will be played only once; set to SoundEffect.Infinite to enable infinite looping.
489
490 The value can be changed while the sound effect is playing, in which case it will update
491 the remaining loops to the new value.
492*/
493
494/*!
495 Returns the total number of times that this sound effect will be played before stopping.
496
497 See the \l loopsRemaining() method for the number of loops currently remaining.
498 */
499int QSoundEffect::loopCount() const
500{
501 return d->m_loopCount;
502}
503
504/*!
505 \enum QSoundEffect::Loop
506
507 \value Infinite Used as a parameter to \l setLoopCount() for infinite looping
508*/
509
510/*!
511 Set the total number of times to play this sound effect to \a loopCount.
512
513 Setting the loop count to 0 or 1 means the sound effect will be played only once;
514 pass \c QSoundEffect::Infinite to repeat indefinitely. The loop count can be changed while
515 the sound effect is playing, in which case it will update the remaining loops to
516 the new \a loopCount.
517
518 \sa loopsRemaining()
519*/
520void QSoundEffect::setLoopCount(int loopCount)
521{
522 if (loopCount < 0 && loopCount != Infinite) {
523 qWarning(msg: "SoundEffect: loops should be SoundEffect.Infinite, 0 or positive integer");
524 return;
525 }
526 if (loopCount == 0)
527 loopCount = 1;
528 if (d->m_loopCount == loopCount)
529 return;
530
531 d->m_loopCount = loopCount;
532 if (d->m_playing)
533 d->setLoopsRemaining(loopCount);
534 emit loopCountChanged();
535}
536
537/*!
538 \property QSoundEffect::audioDevice
539
540 Returns the QAudioDevice instance.
541*/
542QAudioDevice QSoundEffect::audioDevice()
543{
544 return d->m_audioDevice;
545}
546
547void QSoundEffect::setAudioDevice(const QAudioDevice &device)
548{
549 qCDebug(qLcSoundEffect) << this << "setAudioDevice:" << device.description();
550
551 if (d->m_audioDevice == device)
552 return;
553
554 d->m_audioDevice = device;
555
556 if (!d->m_sampleReady) {
557 emit audioDeviceChanged();
558 return; // The audio sink will be recreated later by QSoundEffect::sampleReady()
559 }
560
561 bool playing = d->m_playing;
562 std::chrono::microseconds current_time{d->m_audioBuffer.format().durationForBytes(byteCount: d->m_offset)};
563
564 // Recreate the QAudioSink with the new audio device and current sample
565 if (d->updateAudioOutput() && playing) {
566 // Resume playback from current position
567 d->m_offset = d->m_audioBuffer.format().bytesForDuration(microseconds: current_time.count());
568 d->setPlaying(true);
569 }
570
571 emit audioDeviceChanged();
572}
573
574/*!
575 \qmlproperty int QtMultimedia::SoundEffect::loopsRemaining
576
577 This property contains the number of loops remaining before the sound effect
578 stops by itself, or SoundEffect.Infinite if that's what has been set in \l loops.
579*/
580/*!
581 \property QSoundEffect::loopsRemaining
582
583 This property contains the number of loops remaining before the sound effect
584 stops by itself, or QSoundEffect::Infinite if that's what has been set in \l loops.
585*/
586int QSoundEffect::loopsRemaining() const
587{
588 return d->m_runningCount;
589}
590
591
592/*!
593 \qmlproperty qreal QtMultimedia::SoundEffect::volume
594
595 This property holds the volume of the sound effect playback.
596
597 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
598 range will be clamped.
599
600 The default volume is \c 1.0.
601
602 UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
603 will produce linear changes in perceived loudness, which is what a user would normally expect
604 from a volume control. See \l {QtAudio::convertVolume()}{convertVolume()}
605 for more details.
606*/
607/*!
608 \property QSoundEffect::volume
609
610 This property holds the volume of the sound effect playback, from 0.0 (silence) to 1.0 (full volume).
611*/
612
613/*!
614 Returns the current volume of this sound effect, from 0.0 (silent) to 1.0 (maximum volume).
615 */
616float QSoundEffect::volume() const
617{
618 if (d->m_audioSink && !d->m_muted)
619 return d->m_audioSink->volume();
620
621 return d->m_volume;
622}
623
624/*!
625 Sets the sound effect volume to \a volume.
626
627 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
628 range will be clamped.
629
630 The default volume is \c 1.0.
631
632 UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
633 will produce linear changes in perceived loudness, which is what a user would normally expect
634 from a volume control. See QtAudio::convertVolume() for more details.
635 */
636void QSoundEffect::setVolume(float volume)
637{
638 volume = qBound(min: 0.0f, val: volume, max: 1.0f);
639 if (d->m_volume == volume)
640 return;
641
642 d->m_volume = volume;
643
644 if (d->m_audioSink && !d->m_muted)
645 d->m_audioSink->setVolume(volume);
646
647 emit volumeChanged();
648}
649
650/*!
651 \qmlproperty bool QtMultimedia::SoundEffect::muted
652
653 This property provides a way to control muting. A value of \c true will mute this effect.
654 Otherwise, playback will occur with the currently specified \l volume.
655*/
656/*!
657 \property QSoundEffect::muted
658
659 This property provides a way to control muting. A value of \c true will mute this effect.
660*/
661
662/*! Returns whether this sound effect is muted */
663bool QSoundEffect::isMuted() const
664{
665 return d->m_muted;
666}
667
668/*!
669 Sets whether to mute this sound effect's playback.
670
671 If \a muted is true, playback will be muted (silenced),
672 and otherwise playback will occur with the currently
673 specified volume().
674*/
675void QSoundEffect::setMuted(bool muted)
676{
677 if (d->m_muted == muted)
678 return;
679
680 if (muted && d->m_audioSink)
681 d->m_audioSink->setVolume(0);
682 else if (!muted && d->m_audioSink && d->m_muted)
683 d->m_audioSink->setVolume(d->m_volume);
684
685 d->m_muted = muted;
686 emit mutedChanged();
687}
688
689/*!
690 \fn QSoundEffect::isLoaded() const
691
692 Returns whether the sound effect has finished loading the \l source().
693*/
694/*!
695 \qmlmethod bool QtMultimedia::SoundEffect::isLoaded()
696
697 Returns whether the sound effect has finished loading the \l source.
698*/
699bool QSoundEffect::isLoaded() const
700{
701 return d->m_status == QSoundEffect::Ready;
702}
703
704/*!
705 \qmlmethod QtMultimedia::SoundEffect::play()
706
707 Start playback of the sound effect, looping the effect for the number of
708 times as specified in the loops property.
709
710 This is the default method for SoundEffect.
711
712 \snippet multimedia-snippets/soundeffect.qml play sound on click
713*/
714/*!
715 \fn QSoundEffect::play()
716
717 Start playback of the sound effect, looping the effect for the number of
718 times as specified in the loops property.
719*/
720void QSoundEffect::play()
721{
722 d->m_offset = 0;
723 d->setLoopsRemaining(d->m_loopCount);
724 qCDebug(qLcSoundEffect) << this << "play" << d->m_loopCount << d->m_runningCount;
725 if (d->m_status == QSoundEffect::Null || d->m_status == QSoundEffect::Error) {
726 d->setStatus(QSoundEffect::Null);
727 return;
728 }
729 d->setPlaying(true);
730}
731
732/*!
733 \qmlproperty bool QtMultimedia::SoundEffect::playing
734
735 This property indicates whether the sound effect is playing or not.
736*/
737/*!
738 \property QSoundEffect::playing
739
740 This property indicates whether the sound effect is playing or not.
741*/
742
743/*! Returns true if the sound effect is currently playing, or false otherwise */
744bool QSoundEffect::isPlaying() const
745{
746 return d->m_playing;
747}
748
749/*!
750 \enum QSoundEffect::Status
751
752 \value Null No source has been set or the source is null.
753 \value Loading The SoundEffect is trying to load the source.
754 \value Ready The source is loaded and ready for play.
755 \value Error An error occurred during operation, such as failure of loading the source.
756
757*/
758
759/*!
760 \qmlproperty enumeration QtMultimedia::SoundEffect::status
761
762 This property indicates the current status of the SoundEffect
763 as enumerated within SoundEffect.
764 Possible statuses are listed below.
765
766 \table
767 \header \li Value \li Description
768 \row \li SoundEffect.Null \li No source has been set or the source is null.
769 \row \li SoundEffect.Loading \li The SoundEffect is trying to load the source.
770 \row \li SoundEffect.Ready \li The source is loaded and ready for play.
771 \row \li SoundEffect.Error \li An error occurred during operation, such as failure of loading the source.
772 \endtable
773*/
774/*!
775 \property QSoundEffect::status
776
777 This property indicates the current status of the sound effect
778 from the \l QSoundEffect::Status enumeration.
779*/
780
781/*!
782 Returns the current status of this sound effect.
783 */
784QSoundEffect::Status QSoundEffect::status() const
785{
786 return d->m_status;
787}
788
789/*!
790 \qmlmethod QtMultimedia::SoundEffect::stop()
791
792 Stop current playback.
793
794 */
795/*!
796 \fn QSoundEffect::stop()
797
798 Stop current playback.
799
800 */
801void QSoundEffect::stop()
802{
803 if (!d->m_playing)
804 return;
805 qCDebug(qLcSoundEffect) << "stop()";
806 d->m_offset = 0;
807
808 d->setPlaying(false);
809}
810
811/* Signals */
812
813/*!
814 \fn void QSoundEffect::sourceChanged()
815
816 The \c sourceChanged signal is emitted when the source has been changed.
817*/
818/*!
819 \qmlsignal QtMultimedia::SoundEffect::sourceChanged()
820
821 The \c sourceChanged signal is emitted when the source has been changed.
822*/
823/*!
824 \fn void QSoundEffect::loadedChanged()
825
826 The \c loadedChanged signal is emitted when the loading state has changed.
827*/
828/*!
829 \qmlsignal QtMultimedia::SoundEffect::loadedChanged()
830
831 The \c loadedChanged signal is emitted when the loading state has changed.
832*/
833
834/*!
835 \fn void QSoundEffect::loopCountChanged()
836
837 The \c loopCountChanged signal is emitted when the initial number of loops has changed.
838*/
839/*!
840 \qmlsignal QtMultimedia::SoundEffect::loopCountChanged()
841
842 The \c loopCountChanged signal is emitted when the initial number of loops has changed.
843*/
844
845/*!
846 \fn void QSoundEffect::loopsRemainingChanged()
847
848 The \c loopsRemainingChanged signal is emitted when the remaining number of loops has changed.
849*/
850/*!
851 \qmlsignal QtMultimedia::SoundEffect::loopsRemainingChanged()
852
853 The \c loopsRemainingChanged signal is emitted when the remaining number of loops has changed.
854*/
855
856/*!
857 \fn void QSoundEffect::volumeChanged()
858
859 The \c volumeChanged signal is emitted when the volume has changed.
860*/
861/*!
862 \qmlsignal QtMultimedia::SoundEffect::volumeChanged()
863
864 The \c volumeChanged signal is emitted when the volume has changed.
865*/
866
867/*!
868 \fn void QSoundEffect::mutedChanged()
869
870 The \c mutedChanged signal is emitted when the mute state has changed.
871*/
872/*!
873 \qmlsignal QtMultimedia::SoundEffect::mutedChanged()
874
875 The \c mutedChanged signal is emitted when the mute state has changed.
876*/
877
878/*!
879 \fn void QSoundEffect::playingChanged()
880
881 The \c playingChanged signal is emitted when the playing property has changed.
882*/
883/*!
884 \qmlsignal QtMultimedia::SoundEffect::playingChanged()
885
886 The \c playingChanged signal is emitted when the playing property has changed.
887*/
888
889/*!
890 \fn void QSoundEffect::statusChanged()
891
892 The \c statusChanged signal is emitted when the status property has changed.
893*/
894/*!
895 \qmlsignal QtMultimedia::SoundEffect::statusChanged()
896
897 The \c statusChanged signal is emitted when the status property has changed.
898*/
899
900QT_END_NAMESPACE
901
902#include "moc_qsoundeffect.cpp"
903

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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