From 85de0d3bbfce3ba9f0a84f82f80d076cfa949680 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Jan 2017 13:54:06 -0800 Subject: [PATCH] Added local only spatialized button click sounds to tablet --- .../resources/qml/hifi/tablet/Tablet.qml | 3 + .../qml/hifi/tablet/TabletButton.qml | 4 ++ .../resources/qml/hifi/tablet/TabletRoot.qml | 10 ++++ .../src/TabletScriptingInterface.cpp | 58 ++++++++++++++++++- .../src/TabletScriptingInterface.h | 44 +++++++++++++- scripts/system/libraries/WebTablet.js | 5 ++ scripts/system/tablet-ui/tabletUI.js | 11 ++-- 7 files changed, 129 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index d19fc4ffdd..fbce67a7be 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -40,6 +40,9 @@ Item { button[key] = properties[key]; }); + // pass a reference to the tabletRoot object to the button. + button.tabletRoot = parent.parent; + return button; } diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 636b6dbebc..f9c668a81f 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -11,6 +11,7 @@ Item { property bool isActive: false property bool inDebugMode: false property bool isEntered: false + property var tabletRoot; width: 129 height: 129 @@ -118,6 +119,9 @@ Item { } } tabletButton.clicked(); + if (tabletRoot) { + tabletRoot.playButtonClickSound(); + } } onEntered: { console.log("Tablet Button Hovered!"); diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index f72bbd5c7d..e05f867919 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import Hifi 1.0 Item { id: tabletRoot @@ -13,6 +14,15 @@ Item { loader.item.url = url; } + SoundEffect { + id: buttonClickSound + source: "../../../sounds/button-click.wav" + } + + function playButtonClickSound() { + buttonClickSound.play(); + } + Loader { id: loader objectName: "loader" diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 458dc64983..3e0a25d1a2 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -10,8 +10,18 @@ #include +#include +#include #include "ScriptEngineLogging.h" +TabletScriptingInterface::TabletScriptingInterface() { + qmlRegisterType("Hifi", 1, 0, "SoundEffect"); +} + +TabletScriptingInterface::~TabletScriptingInterface() { + qDebug() << "Shutting down TabletScriptingInterface"; +} + QObject* TabletScriptingInterface::getTablet(const QString& tabletId) { std::lock_guard guard(_mutex); @@ -31,7 +41,7 @@ QObject* TabletScriptingInterface::getTablet(const QString& tabletId) { void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) { TabletProxy* tablet = qobject_cast(getTablet(tabletId)); - if (tablet && qmlOffscreenSurface) { + if (tablet) { tablet->setQmlTabletRoot(qmlTabletRoot, qmlOffscreenSurface); } else { qCWarning(scriptengine) << "TabletScriptingInterface::setupTablet() bad tablet object"; @@ -155,6 +165,10 @@ void TabletProxy::updateAudioBar(const double micLevel) { } } +void TabletProxy::updateTabletPosition(glm::vec3 tabletPosition) { + _position.store(tabletPosition); +} + void TabletProxy::emitScriptEvent(QVariant msg) { if (_qmlOffscreenSurface) { QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg)); @@ -233,4 +247,46 @@ void TabletButtonProxy::editProperties(QVariantMap properties) { } } +// +// SoundEffect +// + +SoundEffect::~SoundEffect() { + if (_sound) { + _sound->deleteLater(); + } + if (_injector) { + _injector->deleteLater(); + } +} + +QUrl SoundEffect::getSource() const { + return _url; +} + +void SoundEffect::setSource(QUrl url) { + _url = url; + _sound = DependencyManager::get()->getSound(_url); +} + +void SoundEffect::play() { + auto tsi = DependencyManager::get(); + if (tsi) { + TabletProxy* tablet = qobject_cast(tsi->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet) { + AudioInjectorOptions options; + options.position = tablet->getPosition(); + options.localOnly = true; + if (_injector) { + _injector->setOptions(options); + _injector->restart(); + } else { + QByteArray samples = _sound->getByteArray(); + _injector = AudioInjector::playSound(samples, options); + } + } + } +} + #include "TabletScriptingInterface.moc" + diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 265ff48fd5..29bfe6a98a 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -10,6 +10,7 @@ #define hifi_TabletScriptingInterface_h #include +#include #include #include @@ -17,11 +18,17 @@ #include #include +#include +#include +#include + #include +#include class TabletProxy; class TabletButtonProxy; +class AudioInjector; /**jsdoc * @namespace Tablet @@ -29,6 +36,9 @@ class TabletButtonProxy; class TabletScriptingInterface : public QObject, public Dependency { Q_OBJECT public: + TabletScriptingInterface(); + virtual ~TabletScriptingInterface(); + /**jsdoc * Creates or retruns a new TabletProxy and returns it. * @function Tablet.getTablet @@ -85,12 +95,23 @@ public: Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); /**jsdoc - * @function TabletProxy#updateAudioBar * Updates the audio bar in tablet to reflect latest mic level + * @function TabletProxy#updateAudioBar * @param micLevel {double} mic level value between 0 and 1 */ Q_INVOKABLE void updateAudioBar(const double micLevel); + + /**jsdoc + * Updates the tablet's position in world space. This is necessary for the tablet + * to emit audio with the correct spatialization. + * @function TabletProxy#updateTabletPosition + * @param position {vec3} tablet position in world space. + */ + Q_INVOKABLE void updateTabletPosition(glm::vec3 tabletPosition); + + glm::vec3 getPosition() const { return _position; } + QString getName() const { return _name; } /**jsdoc @@ -120,6 +141,7 @@ protected: std::vector> _tabletButtonProxies; QQuickItem* _qmlTabletRoot { nullptr }; QObject* _qmlOffscreenSurface { nullptr }; + std::atomic _position; }; /**jsdoc @@ -168,4 +190,24 @@ protected: QVariantMap _properties; }; + +// Exposed to qml only, not java script +class SoundEffect : public QQuickItem { + Q_OBJECT + Q_PROPERTY(QUrl source READ getSource WRITE setSource) +public: + + virtual ~SoundEffect(); + + QUrl getSource() const; + void setSource(QUrl url); + + Q_INVOKABLE void play(); +protected: + QUrl _url; + SharedSoundPointer _sound; + AudioInjector* _injector { nullptr }; +}; + + #endif // hifi_TabletScriptingInterface_h diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 42fbbe106a..d6a465c362 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -171,3 +171,8 @@ WebTablet.unpickle = function (string) { tablet.__proto__ = WebTablet.prototype; return tablet; }; + +WebTablet.prototype.getPosition = function () { + return Overlays.getProperty(this.webOverlayID, "position"); +} + diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 4f2b5ed94a..d3561185f0 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -78,7 +78,7 @@ accumulatedLevel = AVERAGING_RATIO * accumulatedLevel + (1 - AVERAGING_RATIO) * (MyAvatar.audioLoudness); // Convert to log base 2 var logLevel = Math.log(accumulatedLevel + 1) / LOG2; - + if (logLevel <= LOUDNESS_FLOOR) { micLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; } else { @@ -92,9 +92,12 @@ Script.setInterval(function() { if (tabletShown) { - var currentMicLevel = getMicLevel(); - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.updateAudioBar(currentMicLevel); + var currentMicLevel = getMicLevel(); + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + tablet.updateAudioBar(currentMicLevel); + if (UIWebTablet) { + tablet.updateTabletPosition(UIWebTablet.getPosition()); + } } }, MIC_LEVEL_UPDATE_INTERVAL_MS);