diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 548f61e1de..ddb0743bf8 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 3225dd9f53..8e7d040fab 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -125,17 +125,23 @@ Rectangle { Separator {} RowLayout { - HiFiGlyphs { - text: hifi.glyphs.unmuted; - color: hifi.colors.primaryHighlight; - anchors.verticalCenter: parent.verticalCenter; - size: 36; - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("CHOOSE OUTPUT DEVICE"); + Column { + RowLayout { + HiFiGlyphs { + text: hifi.glyphs.unmuted; + color: hifi.colors.primaryHighlight; + anchors.verticalCenter: parent.verticalCenter; + size: 36; + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE OUTPUT DEVICE"); + } + } + + PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }} } } diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml new file mode 100644 index 0000000000..99f3648ec3 --- /dev/null +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -0,0 +1,86 @@ +// +// PlaySampleSound.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/13/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls + +RowLayout { + property var sound: null; + property var sample: null; + property bool isPlaying: false; + function createSampleSound() { + var SOUND = Qt.resolvedUrl("../../../sounds/sample.wav"); + sound = SoundCache.getSound(SOUND); + sample = null; + } + function playSound() { + // FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap + // FIXME: Audio.playSystemSound should not require position + sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition); + isPlaying = true; + sample.finished.connect(function() { isPlaying = false; sample = null; }); + } + function stopSound() { + sample && sample.stop(); + } + + Component.onCompleted: createSampleSound(); + Component.onDestruction: stopSound(); + onVisibleChanged: { + if (!visible) { + stopSound(); + } + } + + HifiConstants { id: hifi; } + + Button { + style: ButtonStyle { + background: Rectangle { + implicitWidth: 20; + implicitHeight: 20; + radius: hifi.buttons.radius; + gradient: Gradient { + GradientStop { + position: 0.2; + color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black]; + } + GradientStop { + position: 1.0; + color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black]; + } + } + } + label: HiFiGlyphs { + // absolutely position due to asymmetry in glyph + x: isPlaying ? 0 : 1; + y: 1; + size: 14; + color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white"; + text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play; + } + } + onClicked: isPlaying ? stopSound() : playSound(); + } + + RalewayRegular { + Layout.leftMargin: 2; + size: 14; + color: "white"; + text: isPlaying ? qsTr("Stop sample sound") : qsTr("Play sample sound"); + } + +} diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 7b6efbd573..f321f49478 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -333,5 +333,7 @@ Item { readonly property string vol_x_2: "\ue015" readonly property string vol_x_3: "\ue016" readonly property string vol_x_4: "\ue017" + readonly property string playback_play: "\ue01d" + readonly property string stop_square: "\ue01e" } } diff --git a/interface/resources/sounds/sample.wav b/interface/resources/sounds/sample.wav new file mode 100644 index 0000000000..d461ab9186 Binary files /dev/null and b/interface/resources/sounds/sample.wav differ diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 752b89bef6..473bcb0de2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -56,6 +56,7 @@ class MyAvatar : public Avatar { * * @namespace MyAvatar * @augments Avatar + * @property qmlPosition {Vec3} Used as a stopgap for position access by QML, as glm::vec3 is unavailable outside of scripts * @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface, * and false if you would not like to see MyAvatar in your local interface. * @property motorVelocity {Vec3} Can be used to move the avatar with this velocity. @@ -101,6 +102,10 @@ class MyAvatar : public Avatar { * "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js". */ + // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type + Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) + QVector3D getQmlPosition() { auto p = getPosition(); return QVector3D(p.x, p.y, p.z); } + Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index aa3fefce09..a532e9d8fd 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -51,6 +51,7 @@ #include "avatar/AvatarManager.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "ui/Snapshot.h" +#include "SoundCache.h" static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -199,6 +200,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index b5dd60d03b..ecaffaf35c 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -11,6 +11,8 @@ #include "AudioScriptingInterface.h" +#include + #include "ScriptAudioInjector.h" #include "ScriptEngineLogging.h" @@ -19,6 +21,13 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } +ScriptAudioInjector* AudioScriptingInterface::playSystemSound(SharedSoundPointer sound, const QVector3D& position) { + AudioInjectorOptions options; + options.position = glm::vec3(position.x(), position.y(), position.z()); + options.localOnly = true; + return playSound(sound, options); +} + ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) { if (QThread::currentThread() != thread()) { ScriptAudioInjector* injector = NULL; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 9ee2af7738..23a0861acd 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -30,8 +30,10 @@ public: protected: AudioScriptingInterface() {} - // this method is protected to stop C++ callers from calling, but invokable from script + // these methods are protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); + // FIXME: there is no way to play a positionless sound + Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position); Q_INVOKABLE void setStereoInput(bool stereo);