"use strict"; /*jslint vars:true, plusplus:true, forin:true*/ /*global Tablet, Script, */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // HRTF.js // // Created by Zach Fox on 2018-03-16 // Copyright 2018 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 // (function () { // BEGIN LOCAL_SCOPE var SOUND_SONG = SoundCache.getSound(Script.resolvePath("song.wav")); var soundsArray = [SOUND_SONG]; // Function Name: addOrRemoveButton() // // Description: // -Used to add or remove the "HRTF" app button from the HUD/tablet. Set the "isShuttingDown" argument // to true if you're calling this function upon script shutdown. // // Relevant Variables: // -button: The tablet button. // -buttonName: The name of the button. var button = false; var buttonName = "HRTF"; function addOrRemoveButton(isShuttingDown) { if (!tablet) { print("Warning in addOrRemoveButton(): 'tablet' undefined!"); return; } if (!button && !isShuttingDown) { button = tablet.addButton({ text: buttonName, icon: Script.resolvePath("hrtf-i.svg"), activeIcon: Script.resolvePath("hrtf-a.svg") }); button.clicked.connect(onTabletButtonClicked); } else if (button) { button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); button = false; } else { print("ERROR adding/removing HRTF button!"); } } // Function Name: startup() // // Description: // -startup() will be called when the script is loaded. // // Relevant Variables: // -tablet: The tablet instance to be modified. var tablet = null; function startup() { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); addOrRemoveButton(false); tablet.screenChanged.connect(onTabletScreenChanged); } // Function Name: wireEventBridge() // // Description: // -Used to connect/disconnect the script's response to the tablet's "fromQml" signal. Set the "on" argument to enable or // disable to event bridge. // // Relevant Variables: // -hasEventBridge: true/false depending on whether we've already connected the event bridge. var hasEventBridge = false; function wireEventBridge(on) { if (!tablet) { print("Warning in wireEventBridge(): 'tablet' undefined!"); return; } if (on) { if (!hasEventBridge) { tablet.fromQml.connect(fromQml); hasEventBridge = true; } } else { if (hasEventBridge) { tablet.fromQml.disconnect(fromQml); hasEventBridge = false; } } } // Function Name: onTabletButtonClicked() // // Description: // -Fired when the app button is pressed. // // Relevant Variables: // -HRTF_QML_SOURCE: The path to the app QML // -onHRTFScreen: true/false depending on whether we're looking at the app. var HRTF_QML_SOURCE = Script.resolvePath("HRTF.qml"); var onHRTFScreen = false; function onTabletButtonClicked() { if (!tablet) { print("Warning in onTabletButtonClicked(): 'tablet' undefined!"); return; } if (onHRTFScreen) { // In Toolbar Mode, `gotoHomeScreen` will close the app window. tablet.gotoHomeScreen(); } else { tablet.loadQMLSource(HRTF_QML_SOURCE); } } // Function Name: onTabletScreenChanged() // // Description: // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. function onTabletScreenChanged(type, url) { onHRTFScreen = (type === "QML" && url === HRTF_QML_SOURCE); wireEventBridge(onHRTFScreen); if (onHRTFScreen && !audioSphere) { audioSphere = Entities.addEntity({ "angularDamping": 1, "clientOnly": true, "collidesWith": "", "collisionMask": 0, "collisionless": true, "color": { "blue": 0, "green": 255, "red": 255 }, "damping": 1, "dimensions": { "x": 0.15, "y": 0.15, "z": 0.15 }, "ignoreForCollisions": true, "type": "Sphere" }, true); } else if (audioSphere && !audioInjector) { Entities.deleteEntity(audioSphere); audioSphere = null; } if (onHRTFScreen) { sendToQml({ method: 'updateState', isPlaying: !!audioInjector }); } // Change button to active when window is first opened OR if a sound is playing, false otherwise. if (button) { button.editProperties({ isActive: (onHRTFScreen || !!audioInjector) }); } } // Function Name: sendToQml() // // Description: // -Use this function to send a message to the QML (i.e. to change appearances). The "message" argument is what is sent to // the QML in the format "{method, params}", like json-rpc. See also fromQml(). function sendToQml(message) { tablet.sendToQml(message); } var audioInjector; var audioSphere; var currentlyPlayingSound; var currentPosition = MyAvatar.position; function playCurrentSound() { if (audioInjector) { audioInjector.stop(); } audioInjector = Audio.playSound(currentlyPlayingSound, { position: currentPosition, localOnly: false, volume: 1.0, loop: true }); } // Function Name: fromQml() // // Description: // -Called when a message is received from the app's QML. The "message" argument is what is sent from the QML // in the format "{method, params}", like json-rpc. See also sendToQml(). function fromQml(message) { switch (message.method) { case 'stopSounds': currentlyPlayingSound = null; if (audioInjector) { audioInjector.stop(); audioInjector = null; } break; case 'soundSelected': if (message.soundID <= soundsArray.length) { currentlyPlayingSound = soundsArray[message.soundID - 1]; playCurrentSound(); } else { console.log("soundID out of range!"); } break; case 'positionSelected': currentPosition = { x: MyAvatar.position.x + message.x, y: MyAvatar.position.y + message.y + 0.6, z: MyAvatar.position.z + message.z }; if (audioInjector) { audioInjector.setOptions({ position: currentPosition, localOnly: false, volume: 1.0, loop: true }); } if (audioSphere) { Entities.editEntity(audioSphere, { position: currentPosition }) } break; default: print('Unrecognized message from HRTF.qml:', JSON.stringify(message)); break; } } // Function Name: shutdown() // // Description: // -shutdown() will be called when the script ends (i.e. is stopped). function shutdown() { addOrRemoveButton(true); if (audioInjector) { audioInjector.stop(); audioInjector = null; } if (audioSphere) { Entities.deleteEntity(audioSphere); audioSphere = null; } if (tablet) { tablet.screenChanged.disconnect(onTabletScreenChanged); if (onHRTFScreen) { tablet.gotoHomeScreen(); } } } // These functions will be called when the script is loaded. startup(); Script.scriptEnding.connect(shutdown); }()); // END LOCAL_SCOPE