| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qdeclarative_soundinstance_p.h" |
| #include "qdeclarative_sound_p.h" |
| #include "qdeclarative_audioengine_p.h" |
| #include "qaudioengine_p.h" |
| #include "qsoundinstance_p.h" |
| #include "qdebug.h" |
| |
| #define DEBUG_AUDIOENGINE |
| |
| QT_USE_NAMESPACE |
| |
| /*! |
| \qmltype SoundInstance |
| \instantiates QDeclarativeSoundInstance |
| \since 5.0 |
| \brief Play 3d audio content. |
| \inqmlmodule QtAudioEngine |
| \ingroup multimedia_audioengine |
| \inherits Item |
| \preliminary |
| \deprecated |
| |
| There are two ways to create SoundInstance objects. You can obtain it by calling newInstance |
| method of a \l Sound: |
| |
| \qml |
| Rectangle { |
| id:root |
| color:"white" |
| width: 300 |
| height: 500 |
| |
| AudioEngine { |
| id:audioengine |
| |
| AudioSample { |
| name:"explosion01" |
| source: "explosion-01.wav" |
| } |
| |
| Sound { |
| name:"explosion" |
| PlayVariation { |
| sample:"explosion01" |
| } |
| } |
| } |
| |
| property variant soundEffect: audioengine.sounds["explosion"].newInstance(); |
| |
| MouseArea { |
| anchors.fill: parent |
| onPressed: { |
| root.soundEffect.play(); |
| } |
| } |
| } |
| \endqml |
| |
| Or alternatively, you can explicitly define SoundInstance outside of AudioEngine for |
| easier qml bindings: |
| |
| \qml |
| Rectangle { |
| id:root |
| color:"white" |
| width: 300 |
| height: 500 |
| |
| AudioEngine { |
| id:audioengine |
| |
| AudioSample { |
| name:"explosion01" |
| source: "explosion-01.wav" |
| } |
| |
| Sound { |
| name:"explosion" |
| PlayVariation { |
| sample:"explosion01" |
| } |
| } |
| } |
| |
| Item { |
| id: animator |
| x: 10 + observer.percent * 100 |
| y: 20 + observer.percent * 80 |
| property real percent: 0 |
| SequentialAnimation on percent { |
| loops: Animation.Infinite |
| running: true |
| NumberAnimation { |
| duration: 8000 |
| from: 0 |
| to: 1 |
| } |
| |
| } |
| } |
| |
| SoundInstance { |
| id:soundEffect |
| engine:audioengine |
| sound:"explosion" |
| position:Qt.vector3d(animator.x, animator.y, 0); |
| } |
| |
| MouseArea { |
| anchors.fill: parent |
| onPressed: { |
| soundEffect.play(); |
| } |
| } |
| } |
| \endqml |
| */ |
| |
| QDeclarativeSoundInstance::QDeclarativeSoundInstance(QObject *parent) |
| : QObject(parent) |
| , m_position(0, 0, 0) |
| , m_direction(0, 1, 0) |
| , m_velocity(0, 0, 0) |
| , m_gain(1) |
| , m_pitch(1) |
| , m_requestState(QDeclarativeSoundInstance::StoppedState) |
| , m_coneInnerAngle(360) |
| , m_coneOuterAngle(360) |
| , m_coneOuterGain(0) |
| , m_instance(0) |
| , m_engine(0) |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::ctor()"; |
| #endif |
| } |
| |
| /*! |
| \qmlproperty QtAudioEngine::AudioEngine QtAudioEngine::SoundInstance::engine |
| |
| This property holds the reference to AudioEngine, must be set only once. |
| */ |
| QDeclarativeAudioEngine* QDeclarativeSoundInstance::engine() const |
| { |
| return m_engine; |
| } |
| |
| void QDeclarativeSoundInstance::setEngine(QDeclarativeAudioEngine *engine) |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::setEngine(" << engine << ")"; |
| #endif |
| if (!engine) |
| return; |
| |
| if (m_engine) { |
| qWarning("SoundInstance: you can not set different value for engine property"); |
| return; |
| } |
| m_engine = engine; |
| if (!m_engine->isReady()) { |
| connect(m_engine, SIGNAL(ready()), this, SLOT(engineComplete())); |
| } else { |
| engineComplete(); |
| } |
| } |
| |
| void QDeclarativeSoundInstance::engineComplete() |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::engineComplete()"; |
| #endif |
| disconnect(m_engine, SIGNAL(ready()), this, SLOT(engineComplete())); |
| if (m_sound.isEmpty()) |
| return; |
| |
| //rebind to actual engine resource |
| QString sound = m_sound; |
| m_sound.clear(); |
| setSound(sound); |
| } |
| |
| QDeclarativeSoundInstance::~QDeclarativeSoundInstance() |
| { |
| } |
| |
| /*! |
| \qmlproperty string QtAudioEngine::SoundInstance::sound |
| |
| This property specifies which Sound this SoundInstance will use. Unlike some properties in |
| other types, this property can be changed dynamically. |
| */ |
| QString QDeclarativeSoundInstance::sound() const |
| { |
| return m_sound; |
| } |
| |
| void QDeclarativeSoundInstance::setSound(const QString& sound) |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::setSound(" << sound << ")"; |
| #endif |
| if (m_sound == sound) |
| return; |
| |
| if (!m_engine || !m_engine->isReady()) { |
| m_sound = sound; |
| emit soundChanged(); |
| return; |
| } |
| |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "SoundInstance switch sound from [" << m_sound << "] to [" << sound << "]"; |
| #endif |
| |
| stop(); |
| dropInstance(); |
| |
| m_sound = sound; |
| if (!m_sound.isEmpty()) { |
| m_instance = m_engine->newSoundInstance(m_sound); |
| connect(m_instance, SIGNAL(stateChanged(QSoundInstance::State)), this, SLOT(handleStateChanged())); |
| m_instance->setPosition(m_position); |
| m_instance->setDirection(m_direction); |
| m_instance->setVelocity(m_velocity); |
| m_instance->setGain(m_gain); |
| m_instance->setPitch(m_pitch); |
| m_instance->setCone(m_coneInnerAngle, m_coneOuterAngle, m_coneOuterGain); |
| if (m_requestState == QDeclarativeSoundInstance::PlayingState) { |
| m_instance->play(); |
| } else if (m_requestState == QDeclarativeSoundInstance::PausedState) { |
| m_instance->pause(); |
| } |
| } |
| emit soundChanged(); |
| } |
| |
| void QDeclarativeSoundInstance::dropInstance() |
| { |
| if (m_instance) { |
| disconnect(m_instance, SIGNAL(stateChanged(QSoundInstance::State)), this, SLOT(handleStateChanged())); |
| m_engine->releaseSoundInstance(m_instance); |
| m_instance = 0; |
| } |
| } |
| |
| /*! |
| \qmlproperty enumeration QtAudioEngine::SoundInstance::state |
| |
| This property holds the current playback state. It can be one of: |
| |
| \table |
| \header \li Value \li Description |
| \row \li StopppedState |
| \li The SoundInstance is not playing, and when playback begins next it |
| will play from position zero. |
| \row \li PlayingState |
| \li The SoundInstance is playing the media. |
| \row \li PausedState |
| \li The SoundInstance is not playing, and when playback begins next it |
| will play from the position that it was paused at. |
| \endtable |
| */ |
| QDeclarativeSoundInstance::State QDeclarativeSoundInstance::state() const |
| { |
| if (m_instance) |
| return State(m_instance->state()); |
| return m_requestState; |
| } |
| |
| /*! |
| \qmlmethod QtAudioEngine::SoundInstance::play() |
| |
| Starts playback. |
| */ |
| void QDeclarativeSoundInstance::play() |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::play()"; |
| #endif |
| if (!m_instance) { |
| m_requestState = QDeclarativeSoundInstance::PlayingState; |
| return; |
| } |
| m_instance->play(); |
| } |
| |
| /*! |
| \qmlmethod QtAudioEngine::SoundInstance::stop() |
| |
| Stops current playback. |
| */ |
| void QDeclarativeSoundInstance::stop() |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::stop()"; |
| #endif |
| m_requestState = QDeclarativeSoundInstance::StoppedState; |
| if (!m_instance) |
| return; |
| m_instance->stop(); |
| } |
| |
| /*! |
| \qmlmethod QtAudioEngine::SoundInstance::pause() |
| |
| Pauses current playback. |
| */ |
| void QDeclarativeSoundInstance::pause() |
| { |
| #ifdef DEBUG_AUDIOENGINE |
| qDebug() << "QDeclarativeSoundInstance::pause()"; |
| #endif |
| if (!m_instance) { |
| m_requestState = QDeclarativeSoundInstance::PausedState; |
| return; |
| } |
| m_instance->pause(); |
| } |
| |
| void QDeclarativeSoundInstance::updatePosition(qreal deltaTime) |
| { |
| if (!m_instance || deltaTime == 0 || m_velocity.lengthSquared() == 0) |
| return; |
| setPosition(m_position + m_velocity * deltaTime); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtAudioEngine::SoundInstance::position |
| |
| This property holds the current 3d position. |
| */ |
| QVector3D QDeclarativeSoundInstance::position() const |
| { |
| return m_position; |
| } |
| |
| void QDeclarativeSoundInstance::setPosition(const QVector3D& position) |
| { |
| if (m_position == position) |
| return; |
| m_position = position; |
| emit positionChanged(); |
| if (!m_instance) { |
| return; |
| } |
| m_instance->setPosition(m_position); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtAudioEngine::SoundInstance::direction |
| |
| This property holds the current 3d direction. |
| */ |
| QVector3D QDeclarativeSoundInstance::direction() const |
| { |
| return m_direction; |
| } |
| |
| void QDeclarativeSoundInstance::setDirection(const QVector3D& direction) |
| { |
| if (m_direction == direction) |
| return; |
| m_direction = direction; |
| emit directionChanged(); |
| if (!m_instance) { |
| return; |
| } |
| m_instance->setDirection(m_direction); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtAudioEngine::SoundInstance::velocity |
| |
| This property holds the current 3d velocity. |
| */ |
| QVector3D QDeclarativeSoundInstance::velocity() const |
| { |
| return m_velocity; |
| } |
| |
| void QDeclarativeSoundInstance::setVelocity(const QVector3D& velocity) |
| { |
| if (m_velocity == velocity) |
| return; |
| m_velocity = velocity; |
| emit velocityChanged(); |
| if (!m_instance) |
| return; |
| m_instance->setVelocity(m_velocity); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtAudioEngine::SoundInstance::gain |
| |
| This property holds the gain adjustment which will be used to modulate the audio output level |
| from this SoundInstance. |
| */ |
| qreal QDeclarativeSoundInstance::gain() const |
| { |
| return m_gain; |
| } |
| |
| void QDeclarativeSoundInstance::setGain(qreal gain) |
| { |
| if (gain == m_gain) |
| return; |
| if (gain < 0) { |
| qWarning("gain must be a positive value!"); |
| return; |
| } |
| m_gain = gain; |
| emit gainChanged(); |
| if (!m_instance) |
| return; |
| m_instance->setGain(m_gain); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtAudioEngine::SoundInstance::pitch |
| |
| This property holds the pitch adjustment which will be used to modulate the audio pitch |
| from this SoundInstance. |
| */ |
| qreal QDeclarativeSoundInstance::pitch() const |
| { |
| return m_pitch; |
| } |
| |
| void QDeclarativeSoundInstance::setPitch(qreal pitch) |
| { |
| if (pitch == m_pitch) |
| return; |
| if (pitch < 0) { |
| qWarning("pitch must be a positive value!"); |
| return; |
| } |
| m_pitch = pitch; |
| emit pitchChanged(); |
| if (!m_instance) |
| return; |
| m_instance->setPitch(m_pitch); |
| } |
| |
| void QDeclarativeSoundInstance::setConeInnerAngle(qreal innerAngle) |
| { |
| if (m_coneInnerAngle == innerAngle) |
| return; |
| m_coneInnerAngle = innerAngle; |
| if (!m_instance) |
| return; |
| m_instance->setCone(m_coneInnerAngle, m_coneOuterAngle, m_coneOuterGain); |
| } |
| |
| void QDeclarativeSoundInstance::setConeOuterAngle(qreal outerAngle) |
| { |
| if (m_coneOuterAngle == outerAngle) |
| return; |
| m_coneOuterAngle = outerAngle; |
| if (!m_instance) |
| return; |
| m_instance->setCone(m_coneInnerAngle, m_coneOuterAngle, m_coneOuterGain); |
| } |
| |
| void QDeclarativeSoundInstance::setConeOuterGain(qreal outerGain) |
| { |
| if (m_coneOuterGain == outerGain) |
| return; |
| m_coneOuterGain = outerGain; |
| if (!m_instance) |
| return; |
| m_instance->setCone(m_coneInnerAngle, m_coneOuterAngle, m_coneOuterGain); |
| } |
| |
| void QDeclarativeSoundInstance::handleStateChanged() |
| { |
| emit stateChanged(); |
| } |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::stateChanged(state) |
| |
| This signal is emitted when \a state is changed. |
| |
| The corresponding handler is \c onStateChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::positionChanged() |
| |
| This signal is emitted when \l position is changed. |
| |
| The corresponding handler is \c onPositionChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::directionChanged() |
| |
| This signal is emitted when \l direction is changed. |
| |
| The corresponding handler is \c onDirectionChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::velocityChanged() |
| |
| This signal is emitted when \l velocity is changed. |
| |
| The corresponding handler is \c onVelocityChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::gainChanged() |
| |
| This signal is emitted when \l gain is changed. |
| |
| The corresponding handler is \c onGainChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::pitchChanged() |
| |
| This signal is emitted when \l pitch is changed. |
| |
| The corresponding handler is \c onPitchChanged. |
| */ |
| |
| /*! |
| \qmlsignal QtAudioEngine::SoundInstance::soundChanged() |
| |
| This signal is emitted when \l sound is changed. |
| |
| The corresponding handler is \c onSoundChanged. |
| */ |