From f9ec11a25e0d04485745fbb9d9fb929da3459826 Mon Sep 17 00:00:00 2001 From: Mithra Pattison Date: Thu, 22 Dec 2011 15:12:03 +1000 Subject: Add new implementation of QSound class Added a re-implementation of QSound, using functionality derived from QSoundEffect. QSound API remains the same as the original 4.x version. It offers both a static interface (with auto resource cleanup on sound completion), as well as an object instance interface for more detailed control. Change-Id: I85c00dd88547f8dea9b1e1ef2da31d2f2e28a172 Reviewed-by: Michael Goddard --- src/multimedia/effects/effects.pri | 6 +- src/multimedia/effects/qsound.cpp | 236 +++++++++++++++++++++++ src/multimedia/effects/qsound.h | 93 +++++++++ src/multimedia/effects/qsoundeffect.cpp | 17 +- src/multimedia/effects/qsoundeffect.h | 3 + src/multimedia/effects/qsoundeffect_pulse_p.cpp | 30 ++- src/multimedia/effects/qsoundeffect_pulse_p.h | 3 + src/multimedia/effects/qsoundeffect_qmedia_p.cpp | 30 ++- src/multimedia/effects/qsoundeffect_qmedia_p.h | 3 + 9 files changed, 403 insertions(+), 18 deletions(-) create mode 100644 src/multimedia/effects/qsound.cpp create mode 100644 src/multimedia/effects/qsound.h (limited to 'src/multimedia') diff --git a/src/multimedia/effects/effects.pri b/src/multimedia/effects/effects.pri index f83f7296f..531a9b352 100644 --- a/src/multimedia/effects/effects.pri +++ b/src/multimedia/effects/effects.pri @@ -21,7 +21,8 @@ unix:!mac { } PUBLIC_HEADERS += \ - effects/qsoundeffect.h + effects/qsoundeffect.h \ + effects/qsound.h PRIVATE_HEADERS += \ effects/qwavedecoder_p.h \ @@ -30,6 +31,7 @@ PRIVATE_HEADERS += \ SOURCES += \ effects/qsoundeffect.cpp \ effects/qwavedecoder_p.cpp \ - effects/qsamplecache_p.cpp + effects/qsamplecache_p.cpp \ + effects/qsound.cpp HEADERS += diff --git a/src/multimedia/effects/qsound.cpp b/src/multimedia/effects/qsound.cpp new file mode 100644 index 000000000..3749764ca --- /dev/null +++ b/src/multimedia/effects/qsound.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsound.h" +#include "qsoundeffect.h" +#include "qcoreapplication.h" + + +/*! + \class QSound + \brief The QSound class provides a way to play .wav sound files. + + \ingroup multimedia + + + Qt provides the most commonly required audio operation in GUI + applications: asynchronously playing a sound file. This is most + easily accomplished using the static play() function: + + \snippet doc/src/snippets/multimedia-snippets/qsound.cpp 0 + + Alternatively, create a QSound object from the sound file first + and then call the play() slot: + + \snippet doc/src/snippets/multimedia-snippets/qsound.cpp 1 + + Once created a QSound object can be queried for its fileName() and + total number of loops() (i.e. the number of times the sound will + play). The number of repetitions can be altered using the + setLoops() function. While playing the sound, the loopsRemaining() + function returns the remaining number of repetitions. Use the + isFinished() function to determine whether the sound has finished + playing. + + Sounds played using a QSound object may use more memory than the + static play() function, but it may also play more immediately + (depending on the underlying platform audio facilities). + +*/ + + +/*! + Plays the sound stored in the file specified by the given \a filename. + + \since 5.0 + \sa stop(), loopsRemaining(), isFinished() +*/ +void QSound::play(const QString& filename) +{ + // Object destruction is generaly handled via deleteOnComplete + // Unexpected cases will be handled via parenting of QSound objects to qApp + QSound *sound = new QSound(filename, qApp); + sound->connect(sound->m_soundEffect, SIGNAL(playingChanged()), SLOT(deleteOnComplete())); + sound->play(); +} + +/*! + Constructs a QSound object from the file specified by the given \a + filename and with the given \a parent. + + \since 5.0 + \sa play() +*/ +QSound::QSound(const QString& filename, QObject* parent) + : QObject(parent) +{ + m_soundEffect = new QSoundEffect(this); + m_soundEffect->setSource(QUrl::fromLocalFile(filename)); +} + +/*! + Destroys this sound object. If the sound is not finished playing, + the stop() function is called before the sound object is + destroyed. + + \since 5.0 + \sa stop(), isFinished() +*/ +QSound::~QSound() +{ + if (!isFinished()) + stop(); +} + +/*! + Returns true if the sound has finished playing; otherwise returns false. +*/ +bool QSound::isFinished() const +{ + return !m_soundEffect->isPlaying(); +} + +/*! + \overload + + Starts playing the sound specified by this QSound object. + + The function returns immediately. Depending on the platform audio + facilities, other sounds may stop or be mixed with the new + sound. The sound can be played again at any time, possibly mixing + or replacing previous plays of the sound. + + \since 5.0 + \sa fileName() +*/ +void QSound::play() +{ + m_soundEffect->play(); +} + +/*! + Returns the number of times the sound will play. + Return value of \c QSound::Infinite indicates infinite number of loops + + \since 5.0 + \sa loopsRemaining(), setLoops() +*/ +int QSound::loops() const +{ + // retain old API value for infite loops + int loopCount = m_soundEffect->loopCount(); + if (loopCount == QSoundEffect::Infinite) + loopCount = Infinite; + + return loopCount; +} + +/*! + Returns the remaining number of times the sound will loop (for all + positive values this value decreases each time the sound is played). + Return value of \c QSound::Infinite indicates infinite number of loops + + \since 5.0 + \sa loops(), isFinished() +*/ +int QSound::loopsRemaining() const +{ + // retain old API value for infite loops + int loopsRemaining = m_soundEffect->loopsRemaining(); + if (loopsRemaining == QSoundEffect::Infinite) + loopsRemaining = Infinite; + + return loopsRemaining; +} + +/*! + \fn void QSound::setLoops(int number) + + Sets the sound to repeat the given \a number of times when it is + played. + + Note that passing the value \c QSound::Infinite will cause the sound to loop + indefinitely. + + \since 5.0 + \sa loops() +*/ +void QSound::setLoops(int n) +{ + if (n == Infinite) + n = QSoundEffect::Infinite; + + m_soundEffect->setLoopCount(n); +} + +/*! + Returns the filename associated with this QSound object. + + \since 5.0 + \sa QSound() +*/ +QString QSound::fileName() const +{ + return m_soundEffect->source().toLocalFile(); +} + +/*! + Stops the sound playing. + + \since 5.0 + \sa play() +*/ +void QSound::stop() +{ + m_soundEffect->stop(); +} + +/*! + \internal + \since 5.0 +*/ +void QSound::deleteOnComplete() +{ + if (!m_soundEffect->isPlaying()) + deleteLater(); +} + +#include "moc_qsound.cpp" diff --git a/src/multimedia/effects/qsound.h b/src/multimedia/effects/qsound.h new file mode 100644 index 000000000..c45ddb10f --- /dev/null +++ b/src/multimedia/effects/qsound.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSOUND_H +#define QSOUND_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QSoundEffect; + +class Q_MULTIMEDIA_EXPORT QSound : public QObject +{ + Q_OBJECT +public: + enum Loop + { + Infinite = -1, + }; + + static void play(const QString& filename); + + explicit QSound(const QString& filename, QObject* parent = 0); + ~QSound(); + + int loops() const; + int loopsRemaining() const; + void setLoops(int); + QString fileName() const; + + bool isFinished() const; + +public Q_SLOTS: + void play(); + void stop(); + +private Q_SLOTS: + void deleteOnComplete(); + +private: + QSoundEffect *m_soundEffect; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif // QSOUND_H diff --git a/src/multimedia/effects/qsoundeffect.cpp b/src/multimedia/effects/qsoundeffect.cpp index 19021fd0c..b1c359ef0 100644 --- a/src/multimedia/effects/qsoundeffect.cpp +++ b/src/multimedia/effects/qsoundeffect.cpp @@ -121,10 +121,17 @@ QT_BEGIN_NAMESPACE */ /*! - \qmlsignal SoundEffect::loopsChanged() + \qmlsignal SoundEffect::loopCountChanged() \since 1.0 - This handler is called when the number of loops has changed. + This handler is called when the initial number of loops has changed. +*/ + +/*! + \qmlsignal SoundEffect::loopsRemainingChanged() + \since 1.0 + + This handler is called when the remaining number of loops has changed. */ /*! @@ -164,6 +171,7 @@ QSoundEffect::QSoundEffect(QObject *parent) : QObject(parent) { d = new QSoundEffectPrivate(this); + connect(d, SIGNAL(loopsRemainingChanged()), SIGNAL(loopsRemainingChanged())); connect(d, SIGNAL(volumeChanged()), SIGNAL(volumeChanged())); connect(d, SIGNAL(mutedChanged()), SIGNAL(mutedChanged())); connect(d, SIGNAL(loadedChanged()), SIGNAL(loadedChanged())); @@ -201,6 +209,11 @@ int QSoundEffect::loopCount() const return d->loopCount(); } +int QSoundEffect::loopsRemaining() const +{ + return d->loopsRemaining(); +} + void QSoundEffect::setLoopCount(int loopCount) { if (loopCount < 0 && loopCount != Infinite) { diff --git a/src/multimedia/effects/qsoundeffect.h b/src/multimedia/effects/qsoundeffect.h index bac325d9c..f5df8328e 100644 --- a/src/multimedia/effects/qsoundeffect.h +++ b/src/multimedia/effects/qsoundeffect.h @@ -63,6 +63,7 @@ class Q_MULTIMEDIA_EXPORT QSoundEffect : public QObject Q_CLASSINFO("DefaultMethod", "play()") Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged) + Q_PROPERTY(int loopsRemaining READ loopsRemaining NOTIFY loopsRemainingChanged) Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool playing READ isPlaying NOTIFY playingChanged) @@ -93,6 +94,7 @@ public: void setSource(const QUrl &url); int loopCount() const; + int loopsRemaining() const; void setLoopCount(int loopCount); qreal volume() const; @@ -109,6 +111,7 @@ public: Q_SIGNALS: void sourceChanged(); void loopCountChanged(); + void loopsRemainingChanged(); void volumeChanged(); void mutedChanged(); void loadedChanged(); diff --git a/src/multimedia/effects/qsoundeffect_pulse_p.cpp b/src/multimedia/effects/qsoundeffect_pulse_p.cpp index aa88f4c4b..373586ef5 100644 --- a/src/multimedia/effects/qsoundeffect_pulse_p.cpp +++ b/src/multimedia/effects/qsoundeffect_pulse_p.cpp @@ -410,7 +410,7 @@ void QSoundEffectPrivate::setSource(const QUrl &url) m_sampleReady = false; PulseDaemonLocker locker; - m_runningCount = 0; + setLoopsRemaining(0); if (m_pulseStream && !pa_stream_is_corked(m_pulseStream)) { pa_stream_set_write_callback(m_pulseStream, 0, 0); pa_stream_set_underflow_callback(m_pulseStream, 0, 0); @@ -444,6 +444,11 @@ int QSoundEffectPrivate::loopCount() const return m_loopCount; } +int QSoundEffectPrivate::loopsRemaining() const +{ + return m_runningCount; +} + void QSoundEffectPrivate::setLoopCount(int loopCount) { if (loopCount == 0) @@ -542,6 +547,17 @@ void QSoundEffectPrivate::setStatus(QSoundEffect::Status status) emit loadedChanged(); } +void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining) +{ +#ifdef QT_PA_DEBUG + qDebug() << this << "setLoopsRemaining " << loopsRemaining; +#endif + if (m_runningCount == loopsRemaining) + return; + m_runningCount = loopsRemaining; + emit loopsRemainingChanged(); +} + void QSoundEffectPrivate::play() { #ifdef QT_PA_DEBUG @@ -561,7 +577,7 @@ void QSoundEffectPrivate::play() #ifdef QT_PA_DEBUG qDebug() << this << "restart playing"; #endif - m_runningCount = 0; + setLoopsRemaining(0); m_playQueued = true; Q_ASSERT(m_pulseStream); emptyStream(); @@ -708,7 +724,7 @@ void QSoundEffectPrivate::prepare() } if (m_playQueued) { m_playQueued = false; - m_runningCount = m_loopCount; + setLoopsRemaining(m_loopCount); playSample(); } } @@ -727,7 +743,7 @@ void QSoundEffectPrivate::uploadSample() if (m_position == m_sample->data().size()) { m_position = 0; if (m_runningCount > 0) - m_runningCount--; + setLoopsRemaining(m_runningCount - 1); if (m_runningCount == 0) { return; } @@ -745,7 +761,7 @@ void QSoundEffectPrivate::uploadSample() if (m_position == m_sample->data().size()) { m_position = 0; if (m_runningCount > 0) - m_runningCount--; + setLoopsRemaining(m_runningCount - 1); if (m_runningCount != 0 && firstPartLength < writableSize) { while (writtenBytes < writableSize) { @@ -760,7 +776,7 @@ void QSoundEffectPrivate::uploadSample() break; } if (m_runningCount > 0) - m_runningCount--; + setLoopsRemaining(m_runningCount - 1); if (m_runningCount == 0) break; } @@ -793,7 +809,7 @@ void QSoundEffectPrivate::stop() m_stopping = true; if (m_pulseStream) emptyStream(); - m_runningCount = 0; + setLoopsRemaining(0); m_position = 0; m_playQueued = false; } diff --git a/src/multimedia/effects/qsoundeffect_pulse_p.h b/src/multimedia/effects/qsoundeffect_pulse_p.h index 3992617ba..35b586e8d 100644 --- a/src/multimedia/effects/qsoundeffect_pulse_p.h +++ b/src/multimedia/effects/qsoundeffect_pulse_p.h @@ -82,6 +82,7 @@ public: QUrl source() const; void setSource(const QUrl &url); int loopCount() const; + int loopsRemaining() const; void setLoopCount(int loopCount); int volume() const; void setVolume(int volume); @@ -98,6 +99,7 @@ public Q_SLOTS: void stop(); Q_SIGNALS: + void loopsRemainingChanged(); void volumeChanged(); void mutedChanged(); void loadedChanged(); @@ -125,6 +127,7 @@ private: void setPlaying(bool playing); void setStatus(QSoundEffect::Status status); + void setLoopsRemaining(int loopsRemaining); static void stream_write_callback(pa_stream *s, size_t length, void *userdata); static void stream_state_callback(pa_stream *s, void *userdata); diff --git a/src/multimedia/effects/qsoundeffect_qmedia_p.cpp b/src/multimedia/effects/qsoundeffect_qmedia_p.cpp index 7ae785cd9..511aa06b5 100644 --- a/src/multimedia/effects/qsoundeffect_qmedia_p.cpp +++ b/src/multimedia/effects/qsoundeffect_qmedia_p.cpp @@ -105,6 +105,11 @@ int QSoundEffectPrivate::loopCount() const return m_loopCount; } +int QSoundEffectPrivate::loopsRemaining() const +{ + return m_runningCount; +} + void QSoundEffectPrivate::setLoopCount(int loopCount) { m_loopCount = loopCount; @@ -150,19 +155,19 @@ void QSoundEffectPrivate::play() if (m_status == QSoundEffect::Null || m_status == QSoundEffect::Error) return; if (m_loopCount < 0) { - m_runningCount = -1; + setLoopsRemaining(-1); } else { if (m_runningCount < 0) - m_runningCount = 0; - m_runningCount += m_loopCount; + setLoopsRemaining(0); + setLoopsRemaining(m_runningCount + m_loopCount); } m_player->play(); } void QSoundEffectPrivate::stop() { - m_runningCount = 0; + setLoopsRemaining(0); m_player->stop(); } @@ -174,10 +179,13 @@ void QSoundEffectPrivate::stateChanged(QMediaPlayer::State state) } else if (m_runningCount == 0) { setPlaying(false); return; - } else if (--m_runningCount > 0) { - m_player->play(); } else { - setPlaying(false); + setLoopsRemaining(m_runningCount - 1); + if (m_runningCount > 0) { + m_player->play(); + } else { + setPlaying(false); + } } } else { setPlaying(true); @@ -233,6 +241,14 @@ void QSoundEffectPrivate::setPlaying(bool playing) emit playingChanged(); } +void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining) +{ + if (m_runningCount == loopsRemaining) + return; + m_runningCount = loopsRemaining; + emit loopsRemainingChanged(); +} + QT_END_NAMESPACE #include "moc_qsoundeffect_qmedia_p.cpp" diff --git a/src/multimedia/effects/qsoundeffect_qmedia_p.h b/src/multimedia/effects/qsoundeffect_qmedia_p.h index b93f47ce3..bb91e50b0 100644 --- a/src/multimedia/effects/qsoundeffect_qmedia_p.h +++ b/src/multimedia/effects/qsoundeffect_qmedia_p.h @@ -79,6 +79,7 @@ public: QUrl source() const; void setSource(const QUrl &url); int loopCount() const; + int loopsRemaining() const; void setLoopCount(int loopCount); int volume() const; void setVolume(int volume); @@ -95,6 +96,7 @@ public Q_SLOTS: void stop(); Q_SIGNALS: + void loopsRemainingChanged(); void volumeChanged(); void mutedChanged(); void loadedChanged(); @@ -109,6 +111,7 @@ private Q_SLOTS: private: void setStatus(QSoundEffect::Status status); void setPlaying(bool playing); + void setLoopsRemaining(int loopsRemaining); int m_loopCount; int m_runningCount; -- cgit v1.2.3