diff --git a/examples/dialTone.js b/examples/dialTone.js new file mode 100644 index 0000000000..0748d0ba94 --- /dev/null +++ b/examples/dialTone.js @@ -0,0 +1,23 @@ +// +// dialTone.js +// examples +// +// Created by Stephen Birarda on 06/08/15. +// Copyright 2015 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 +// + +// setup the local sound we're going to use +var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav"); + +// setup the options needed for that sound +var connectSoundOptions = { + localOnly: true +} + +// play the sound locally once we get the first audio packet from a mixer +Audio.receivedFirstPacket.connect(function(){ + Audio.playSound(connectSound, connectSoundOptions); +}); diff --git a/interface/resources/sounds/short1.wav b/interface/resources/sounds/short1.wav new file mode 100644 index 0000000000..fb03f5dd49 Binary files /dev/null and b/interface/resources/sounds/short1.wav differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dbdcaa122a..64136627d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -250,12 +250,12 @@ bool setupEssentials(int& argc, char** argv) { } // Set build version QCoreApplication::setApplicationVersion(BUILD_VERSION); - + DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); Setting::init(); - + // Set dependencies auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); @@ -289,6 +289,7 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); + auto pathUtils = DependencyManager::set(); return true; } @@ -355,9 +356,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); _window->setWindowTitle("Interface"); - + Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - + auto nodeList = DependencyManager::get(); _myAvatar = DependencyManager::get()->getMyAvatar(); @@ -369,7 +370,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _bookmarks = new Bookmarks(); // Before setting up the menu _runningScriptsWidget = new RunningScriptsWidget(_window); - + // start the nodeThread so its event loop is running QThread* nodeThread = new QThread(this); nodeThread->setObjectName("Datagram Processor Thread"); @@ -377,14 +378,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // make sure the node thread is given highest priority nodeThread->setPriority(QThread::TimeCriticalPriority); - + _datagramProcessor = new DatagramProcessor(nodeList.data()); - + // have the NodeList use deleteLater from DM customDeleter nodeList->setCustomDeleter([](Dependency* dependency) { static_cast(dependency)->deleteLater(); }); - + // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); @@ -401,20 +402,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // put the audio processing on a separate thread QThread* audioThread = new QThread(); audioThread->setObjectName("Audio Thread"); - + auto audioIO = DependencyManager::get(); - + audioIO->setPositionGetter(getPositionForAudio); audioIO->setOrientationGetter(getOrientationForAudio); - + audioIO->moveToThread(audioThread); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); + connect(audioIO.data(), &AudioClient::receivedFirstPacket, + &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket); audioThread->start(); - + const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); @@ -433,7 +436,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto discoverabilityManager = DependencyManager::get(); connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); - + // if we get a domain change, immediately attempt update location in metaverse server connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); @@ -467,13 +470,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); - + auto addressManager = DependencyManager::get(); - + // use our MyAvatar position and quat for address manager path addressManager->setPositionGetter(getPositionForPath); addressManager->setOrientationGetter(getOrientationForPath); - + connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); @@ -505,7 +508,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); - + ResourceCache::setRequestLimit(3); _window->setCentralWidget(_glWidget); @@ -556,11 +559,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)), bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int))); - connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions - // Setup the keyboardMouseDevice and the user input mapper with the default bindings + // Setup the keyboardMouseDevice and the user input mapper with the default bindings _keyboardMouseDevice.registerToUserInputMapper(_userInputMapper); _keyboardMouseDevice.assignDefaultInputMapping(_userInputMapper); @@ -576,7 +579,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // do this as late as possible so that all required subsystems are initialized loadScripts(); } - + loadSettings(); int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); @@ -586,12 +589,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _settingsTimer.setSingleShot(false); _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); _settingsThread.start(); - + _trayIcon->show(); - + // set the local loopback interface for local sounds from audio scripts AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); - + #ifdef HAVE_RTMIDI // setup the MIDIManager MIDIManager& midiManagerInstance = MIDIManager::getInstance(); @@ -599,7 +602,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : #endif this->installEventFilter(this); - // The offscreen UI needs to intercept the mouse and keyboard + // The offscreen UI needs to intercept the mouse and keyboard // events coming from the onscreen window _glWidget->installEventFilter(DependencyManager::get().data()); @@ -617,7 +620,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : void Application::aboutToQuit() { emit beforeAboutToQuit(); - + _aboutToQuit = true; cleanupBeforeQuit(); } @@ -629,7 +632,7 @@ void Application::cleanupBeforeQuit() { _datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts - + // first stop all timers directly or by invokeMethod // depending on what thread they run in locationUpdateTimer->stop(); @@ -656,11 +659,11 @@ void Application::cleanupBeforeQuit() { // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); - + // stop the AudioClient QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); - + // destroy the AudioClient so it and its thread have a chance to go down safely DependencyManager::destroy(); @@ -669,12 +672,12 @@ void Application::cleanupBeforeQuit() { #endif } -Application::~Application() { +Application::~Application() { EntityTree* tree = _entities.getTree(); tree->lockForWrite(); _entities.getTree()->setSimulation(NULL); tree->unlock(); - + _octreeProcessor.terminate(); _entityEditSender.terminate(); @@ -684,7 +687,7 @@ Application::~Application() { _myAvatar = NULL; ModelEntityItem::cleanupLoadedAnimations(); - + // stop the glWidget frame timer so it doesn't call paintGL _glWidget->stopFrameTimer(); @@ -699,10 +702,10 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); - + QThread* nodeThread = DependencyManager::get()->thread(); DependencyManager::destroy(); - + // ask the node thread to quit and wait until it is done nodeThread->quit(); nodeThread->wait(); @@ -756,8 +759,8 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); - // The UI can't be created until the primary OpenGL - // context is created, because it needs to share + // The UI can't be created until the primary OpenGL + // context is created, because it needs to share // texture resources initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); @@ -909,7 +912,7 @@ void Application::paintGL() { OculusManager::display(_glWidget, _myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), _myCamera); } } else if (TV3DManager::isConnected()) { - + TV3DManager::display(_myCamera); } else { @@ -928,7 +931,7 @@ void Application::paintGL() { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true, _glWidget->mapFromGlobal(QCursor::pos())); } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - renderRearViewMirror(_mirrorViewRect); + renderRearViewMirror(_mirrorViewRect); } auto finalFbo = DependencyManager::get()->render(); @@ -945,7 +948,7 @@ void Application::paintGL() { if (!OculusManager::isConnected() || OculusManager::allowSwap()) { _glWidget->swapBuffers(); - } + } if (OculusManager::isConnected()) { OculusManager::endFrameTiming(); @@ -1035,7 +1038,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) // Tell our viewFrustum about this change, using the application camera if (updateViewFrustum) { loadViewFrustum(camera, _viewFrustum); - } + } glMatrixMode(GL_MODELVIEW); } @@ -1103,11 +1106,11 @@ bool Application::event(QEvent* event) { // handle custom URL if (event->type() == QEvent::FileOpen) { - + QFileOpenEvent* fileEvent = static_cast(event); QUrl url = fileEvent->url(); - + if (!url.isEmpty()) { QString urlString = url.toString(); if (canAcceptURL(urlString)) { @@ -1116,7 +1119,7 @@ bool Application::event(QEvent* event) { } return false; } - + if (HFActionEvent::types().contains(event->type())) { _controllerScriptingInterface.handleMetaEvent(static_cast(event)); } @@ -1182,7 +1185,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::AddressBar); } else if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::LodTools); - } + } break; case Qt::Key_F: { @@ -1223,7 +1226,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Backslash: Menu::getInstance()->triggerOption(MenuOption::Chat); break; - + case Qt::Key_Up: if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (!isShifted) { @@ -1352,18 +1355,18 @@ void Application::keyPressEvent(QKeyEvent* event) { computePickRay(getTrueMouseX(), getTrueMouseY())); sendEvent(this, &startActionEvent); } - + break; } case Qt::Key_Escape: { OculusManager::abandonCalibration(); - + if (!event->isAutoRepeat()) { // this starts the HFCancelEvent HFBackEvent startBackEvent(HFBackEvent::startType()); sendEvent(this, &startBackEvent); } - + break; } @@ -1425,7 +1428,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::focusOutEvent(QFocusEvent* event) { _keyboardMouseDevice.focusOutEvent(event); - + // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier); @@ -1440,12 +1443,12 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { if (!_lastMouseMoveWasSimulated) { _lastMouseMove = usecTimestampNow(); } - + if (_aboutToQuit) { return; } - - if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) + + if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) && !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { // Show/hide menu bar in fullscreen if (event->globalY() > _menuBarHeight) { @@ -1458,7 +1461,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { } _entities.mouseMoveEvent(event, deviceID); - + _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface.isMouseCaptured()) { @@ -1466,7 +1469,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { } _keyboardMouseDevice.mouseMoveEvent(event, deviceID); - + } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1492,7 +1495,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { _mouseDragStartedX = getTrueMouseX(); _mouseDragStartedY = getTrueMouseY(); _mousePressed = true; - + if (mouseOnScreen()) { if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { // stop propagation @@ -1509,7 +1512,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { return; } } - + // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), computePickRay(event->x(), event->y())); @@ -1557,14 +1560,14 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mousePressed = false; - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH; Stats::getInstance()->checkClick(getMouseX(), getMouseY(), getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); } - + // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), computePickRay(event->x(), event->y())); @@ -1673,7 +1676,7 @@ void Application::dropEvent(QDropEvent *event) { } } } - + if (atLeastOneFileAccepted) { event->acceptProposedAction(); } @@ -1693,7 +1696,7 @@ void Application::dragEnterEvent(QDragEnterEvent* event) { bool Application::acceptSnapshot(const QString& urlString) { QUrl url(urlString); QString snapshotPath = url.toLocalFile(); - + SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); if (snapshotData) { if (!snapshotData->getURL().toString().isEmpty()) { @@ -1703,7 +1706,7 @@ bool Application::acceptSnapshot(const QString& urlString) { QMessageBox msgBox; msgBox.setText("No location details were found in the file " + snapshotPath + ", try dragging in an authentic Hifi snapshot."); - + msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } @@ -1736,7 +1739,7 @@ void Application::checkFPS() { void Application::idle() { PerformanceTimer perfTimer("idle"); - + if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -1865,15 +1868,15 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::recalibrate(); } else { OculusManager::abandonCalibration(); - + _mirrorCamera.setHmdPosition(glm::vec3()); _mirrorCamera.setHmdRotation(glm::quat()); _myCamera.setHmdPosition(glm::vec3()); _myCamera.setHmdRotation(glm::quat()); } - + resizeGL(); - + updateCursorVisibility(); } @@ -1926,7 +1929,7 @@ int Application::getMouseDragStartedY() const { FaceTracker* Application::getActiveFaceTracker() { auto faceshift = DependencyManager::get(); auto dde = DependencyManager::get(); - + return (dde->isActive() ? static_cast(dde.data()) : (faceshift->isActive() ? static_cast(faceshift.data()) : NULL)); } @@ -2059,14 +2062,14 @@ void Application::saveSettings() { bool Application::importEntities(const QString& urlOrFilename) { _entityClipboard.eraseAllOctreeElements(); - + QUrl url(urlOrFilename); - + // if the URL appears to be invalid or relative, then it is probably a local file if (!url.isValid() || url.isRelative()) { url = QUrl::fromLocalFile(urlOrFilename); } - + bool success = _entityClipboard.readFromURL(url.toString()); if (success) { _entityClipboard.reaverageOctreeElements(); @@ -2090,7 +2093,7 @@ void Application::initDisplay() { void Application::init() { // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); - + _environment.init(); DependencyManager::get()->init(this); @@ -2118,7 +2121,7 @@ void Application::init() { _timerStart.start(); _lastTimeUpdated.start(); - + // when --url in command line, teleport to location const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); @@ -2126,11 +2129,11 @@ void Application::init() { if (urlIndex != -1) { addressLookupString = arguments().value(urlIndex + 1); } - + DependencyManager::get()->loadSettings(addressLookupString); - + qCDebug(interfaceapp) << "Loaded settings"; - + #ifdef __APPLE__ if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) { // on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash @@ -2238,7 +2241,7 @@ void Application::updateMouseRay() { PickRay pickRay = computePickRay(getTrueMouseX(), getTrueMouseY()); _mouseRayOrigin = pickRay.origin; _mouseRayDirection = pickRay.direction; - + // adjust for mirroring if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); @@ -2270,11 +2273,11 @@ void Application::updateMyAvatarLookAtPosition() { lookAtSpot = OculusManager::getRightEyePosition(); } } - + } else { AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef(); if (lookingAt && _myAvatar != lookingAt.data()) { - + isLookingAtSomeone = true; // If I am looking at someone else, look directly at one of their eyes if (tracker && !tracker->isMuted()) { @@ -2361,7 +2364,7 @@ void Application::updateDialogs(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateDialogs()"); auto dialogsManager = DependencyManager::get(); - + // Update bandwidth dialog, if any BandwidthDialog* bandwidthDialog = dialogsManager->getBandwidthDialog(); if (bandwidthDialog) { @@ -2567,7 +2570,7 @@ void Application::update(float deltaTime) { } } - // send packet containing downstream audio stats to the AudioMixer + // send packet containing downstream audio stats to the AudioMixer { quint64 sinceLastNack = now - _lastSendDownstreamAudioStats; if (sinceLastNack > TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS) { @@ -2589,64 +2592,64 @@ int Application::sendNackPackets() { // iterates thru all nodes in NodeList auto nodeList = DependencyManager::get(); - + nodeList->eachNode([&](const SharedNodePointer& node){ - + if (node->getActiveSocket() && node->getType() == NodeType::EntityServer) { - + QUuid nodeUUID = node->getUUID(); - + // if there are octree packets from this node that are waiting to be processed, // don't send a NACK since the missing packets may be among those waiting packets. if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) { return; } - + _octreeSceneStatsLock.lockForRead(); - + // retreive octree scene stats of this node if (_octreeServerSceneStats.find(nodeUUID) == _octreeServerSceneStats.end()) { _octreeSceneStatsLock.unlock(); return; } - + // get sequence number stats of node, prune its missing set, and make a copy of the missing set SequenceNumberStats& sequenceNumberStats = _octreeServerSceneStats[nodeUUID].getIncomingOctreeSequenceNumberStats(); sequenceNumberStats.pruneMissingSet(); const QSet missingSequenceNumbers = sequenceNumberStats.getMissingSet(); - + _octreeSceneStatsLock.unlock(); - + // construct nack packet(s) for this node int numSequenceNumbersAvailable = missingSequenceNumbers.size(); QSet::const_iterator missingSequenceNumbersIterator = missingSequenceNumbers.constBegin(); while (numSequenceNumbersAvailable > 0) { - + char* dataAt = packet; int bytesRemaining = MAX_PACKET_SIZE; - + // pack header int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeOctreeDataNack); dataAt += numBytesPacketHeader; bytesRemaining -= numBytesPacketHeader; - + // calculate and pack the number of sequence numbers int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(OCTREE_PACKET_SEQUENCE); uint16_t numSequenceNumbers = min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor); uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt; *numSequenceNumbersAt = numSequenceNumbers; dataAt += sizeof(uint16_t); - + // pack sequence numbers for (int i = 0; i < numSequenceNumbers; i++) { OCTREE_PACKET_SEQUENCE* sequenceNumberAt = (OCTREE_PACKET_SEQUENCE*)dataAt; *sequenceNumberAt = *missingSequenceNumbersIterator; dataAt += sizeof(OCTREE_PACKET_SEQUENCE); - + missingSequenceNumbersIterator++; } numSequenceNumbersAvailable -= numSequenceNumbers; - + // send it nodeList->writeUnverifiedDatagram(packet, dataAt - packet, node); packetsSent++; @@ -2688,7 +2691,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node int unknownJurisdictionServers = 0; auto nodeList = DependencyManager::get(); - + nodeList->eachNode([&](const SharedNodePointer& node) { // only send to the NodeTypes that are serverType if (node->getActiveSocket() && node->getType() == serverType) { @@ -2748,17 +2751,17 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node if (wantExtraDebugging) { qCDebug(interfaceapp, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer); } - + nodeList->eachNode([&](const SharedNodePointer& node){ // only send to the NodeTypes that are serverType if (node->getActiveSocket() && node->getType() == serverType) { - + // get the server bounds for this server QUuid nodeUUID = node->getUUID(); - + bool inView = false; bool unknownView = false; - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2768,9 +2771,9 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } else { const JurisdictionMap& map = (jurisdictions)[nodeUUID]; - + unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2780,7 +2783,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node rootDetails.s * TREE_SCALE); - + ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds); if (serverFrustumLocation != ViewFrustum::OUTSIDE) { inView = true; @@ -2793,7 +2796,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } } - + if (inView) { _octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS); } else if (unknownView) { @@ -2801,7 +2804,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of " << perUnknownServer << " to send us jurisdiction."; } - + // set the query's position/orientation to be degenerate in a manner that will get the scene quickly // If there's only one server, then don't do this, and just let the normal voxel query pass through // as expected... this way, we will actually get a valid scene if there is one to be seen @@ -2828,12 +2831,12 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node // insert packet type/version and node UUID endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); - + // encode the query data... endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); - + int packetLength = endOfQueryPacket - queryPacket; - + // make sure we still have an active socket nodeList->writeUnverifiedDatagram(reinterpret_cast(queryPacket), packetLength, node); } @@ -2850,7 +2853,7 @@ bool Application::isHMDMode() const { QRect Application::getDesirableApplicationGeometry() { QRect applicationGeometry = getWindow()->geometry(); - + // If our parent window is on the HMD, then don't use its geometry, instead use // the "main screen" geometry. HMDToolsDialog* hmdTools = DependencyManager::get()->getHMDToolsDialog(); @@ -2911,14 +2914,14 @@ void Application::updateShadowMap() { glm::vec3 lightDirection = getSunDirection(); glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection); glm::quat inverseRotation = glm::inverse(rotation); - + const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f }; const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f), glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) }; - + float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); loadViewFrustum(_myCamera, _viewFrustum); - + int matrixCount = 1; //int targetSize = fbo->width(); int sourceSize = shadowFramebuffer->getWidth(); @@ -2960,12 +2963,12 @@ void Application::updateShadowMap() { _shadowDistances[i] = -glm::distance(_viewFrustum.getPosition(), center) - radius * RADIUS_SCALE; } center = inverseRotation * center; - + // to reduce texture "shimmer," move in texel increments float texelSize = (2.0f * radius) / targetSize; center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize, roundf(center.z / texelSize) * texelSize); - + glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); @@ -3004,7 +3007,7 @@ void Application::updateShadowMap() { // store view matrix without translation, which we'll use for precision-sensitive objects updateUntranslatedViewMatrix(); - + // Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation // the viewTransofmr object is updatded with the correct values and saved, // this is what is used for rendering the Entities and avatars @@ -3047,9 +3050,9 @@ void Application::updateShadowMap() { glMatrixMode(GL_MODELVIEW); } - + // fbo->release(); - + glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); activeRenderingThread = nullptr; } @@ -3081,11 +3084,11 @@ bool Application::shouldRenderMesh(float largestDimension, float distanceToCamer return DependencyManager::get()->shouldRenderMesh(largestDimension, distanceToCamera); } -float Application::getSizeScale() const { +float Application::getSizeScale() const { return DependencyManager::get()->getOctreeSizeScale(); } -int Application::getBoundaryLevelAdjust() const { +int Application::getBoundaryLevelAdjust() const { return DependencyManager::get()->getBoundaryLevelAdjust(); } @@ -3109,27 +3112,27 @@ PickRay Application::computePickRay(float x, float y) const { QImage Application::renderAvatarBillboard() { auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); - + // clear the alpha channel so the background is transparent glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - + // the "glow" here causes an alpha of one Glower glower; - + const int BILLBOARD_SIZE = 64; renderRearViewMirror(QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - + QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + return image; } @@ -3214,11 +3217,11 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb } if (renderSide != RenderArgs::MONO) { glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); - + viewTransform.evalFromRawMatrix(invView); viewTransform.preTranslate(_viewMatrixTranslation); } - + setViewTransform(viewTransform); glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z); @@ -3269,7 +3272,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; - glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation()) + glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation()) / closestData.getAtmosphereOuterRadius(); float height = glm::distance(theCamera.getPosition(), closestData.getAtmosphereCenter()); if (height < closestData.getAtmosphereInnerRadius()) { @@ -3277,20 +3280,20 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb alpha = 0.0f; if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + APPROXIMATE_DISTANCE_FROM_HORIZON; alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); } - + } else if (height < closestData.getAtmosphereOuterRadius()) { alpha = (height - closestData.getAtmosphereInnerRadius()) / (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + APPROXIMATE_DISTANCE_FROM_HORIZON; alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); } @@ -3331,14 +3334,14 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); - + DependencyManager::get()->prepare(); if (!selfAvatarOnly) { // draw a red sphere float originSphereRadius = 0.05f; DependencyManager::get()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - + // render models... if (DependencyManager::get()->shouldRenderEntities()) { PerformanceTimer perfTimer("entities"); @@ -3359,7 +3362,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb renderMode = RenderArgs::MIRROR_RENDER_MODE; } _entities.render(renderMode, renderSide, renderDebugFlags); - + if (!Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) { // Restaure polygon mode glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -3380,13 +3383,13 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb DependencyManager::get()->render(); } } - + bool mirrorMode = (theCamera.getMode() == CAMERA_MODE_MIRROR); - + { PerformanceTimer perfTimer("avatars"); DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE, - false, selfAvatarOnly); + false, selfAvatarOnly); } if (!billboard) { @@ -3396,7 +3399,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb DependencyManager::get()->setGlobalAtmosphere(skyStage->getAtmosphere()); DependencyManager::get()->setGlobalSkybox(skybox); - PROFILE_RANGE("DeferredLighting"); + PROFILE_RANGE("DeferredLighting"); PerformanceTimer perfTimer("lighting"); DependencyManager::get()->render(); } @@ -3404,9 +3407,9 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb { PerformanceTimer perfTimer("avatarsPostLighting"); DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE, - true, selfAvatarOnly); + true, selfAvatarOnly); } - + //Render the sixense lasers if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { _myAvatar->renderLaserPointers(); @@ -3414,7 +3417,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb if (!selfAvatarOnly) { _nodeBoundsDisplay.draw(); - + // Render the world box if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { PerformanceTimer perfTimer("worldBox"); @@ -3491,9 +3494,9 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom _displayViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); if (OculusManager::isConnected()) { OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - + } else if (TV3DManager::isConnected()) { - TV3DManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + TV3DManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); } } @@ -3503,8 +3506,8 @@ bool Application::getShadowsEnabled() { menubar->isOptionChecked(MenuOption::CascadedShadows); } -bool Application::getCascadeShadowsEnabled() { - return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows); +bool Application::getCascadeShadowsEnabled() { + return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows); } glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { @@ -3551,22 +3554,22 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); } else { // HEAD zoom level - // FIXME note that the positioing of the camera relative to the avatar can suffer limited - // precision as the user's position moves further away from the origin. Thus at - // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // wildly as you rotate your avatar because the floating point values are becoming - // larger, squeezing out the available digits of precision you have available at the - // human scale for camera positioning. + // FIXME note that the positioing of the camera relative to the avatar can suffer limited + // precision as the user's position moves further away from the origin. Thus at + // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // wildly as you rotate your avatar because the floating point values are becoming + // larger, squeezing out the available digits of precision you have available at the + // human scale for camera positioning. - // Previously there was a hack to correct this using the mechanism of repositioning - // the avatar at the origin of the world for the purposes of rendering the mirror, - // but it resulted in failing to render the avatar's head model in the mirror view - // when in first person mode. Presumably this was because of some missed culling logic - // that was not accounted for in the hack. + // Previously there was a hack to correct this using the mechanism of repositioning + // the avatar at the origin of the world for the purposes of rendering the mirror, + // but it resulted in failing to render the avatar's head model in the mirror view + // when in first person mode. Presumably this was because of some missed culling logic + // that was not accounted for in the hack. - // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further // investigated in order to adapt the technique while fixing the head rendering issue, - // but the complexity of the hack suggests that a better approach + // but the complexity of the hack suggests that a better approach _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); } @@ -3577,7 +3580,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { if (billboard) { QSize size = DependencyManager::get()->getFrameBufferSize(); glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device QSize size = DependencyManager::get()->getFrameBufferSize(); @@ -3618,7 +3621,7 @@ void Application::resetSensors() { QWindow* mainWindow = _window->windowHandle(); QPoint windowCenter = mainWindow->geometry().center(); _glWidget->cursor().setPos(currentScreen, windowCenter); - + _myAvatar->reset(); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); @@ -3651,11 +3654,11 @@ void Application::updateWindowTitle(){ QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) "; QString username = AccountManager::getInstance().getAccountInfo().getUsername(); QString currentPlaceName = DependencyManager::get()->getHost(); - + if (currentPlaceName.isEmpty()) { currentPlaceName = nodeList->getDomainHandler().getHostname(); } - + QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) + currentPlaceName + connectionStatus + buildVersion; @@ -3701,7 +3704,7 @@ void Application::domainConnectionDenied(const QString& reason) { void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); const QUuid& domainID = DependencyManager::get()->getDomainHandler().getUUID(); - + if (accountManager.isLoggedIn() && !domainID.isNull()) { _notifiedPacketVersionMismatchThisDomain = false; } @@ -3885,7 +3888,7 @@ void Application::saveScripts() { Settings settings; settings.beginWriteArray(SETTINGS_KEY); settings.remove(""); - + QStringList runningScripts = getRunningScripts(); int i = 0; for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) { @@ -3936,7 +3939,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); - qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, + qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); @@ -3947,7 +3950,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri LocationScriptingInterface::locationSetter); scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); - + scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); @@ -3959,7 +3962,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); - + scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); @@ -3967,6 +3970,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); + QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance()); scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); @@ -4014,7 +4019,7 @@ void Application::initializeAcceptedFiles() { bool Application::canAcceptURL(const QString& urlString) { initializeAcceptedFiles(); - + QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; @@ -4032,7 +4037,7 @@ bool Application::canAcceptURL(const QString& urlString) { bool Application::acceptURL(const QString& urlString) { initializeAcceptedFiles(); - + if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", @@ -4070,19 +4075,19 @@ bool Application::askToSetAvatarUrl(const QString& url) { msgBox.exec(); return false; } - + // Download the FST file, to attempt to determine its model type QVariantHash fstMapping = FSTReader::downloadMapping(url); - + FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping); - + QMessageBox msgBox; msgBox.setIcon(QMessageBox::Question); msgBox.setWindowTitle("Set Avatar"); QPushButton* headButton = NULL; QPushButton* bodyButton = NULL; QPushButton* bodyAndHeadButton = NULL; - + QString modelName = fstMapping["name"].toString(); QString message; QString typeInfo; @@ -4101,7 +4106,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { message = QString("Would you like to use '") + modelName + QString("' for your avatar?"); bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); break; - + default: message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?"); headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole); @@ -4127,7 +4132,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { } else { qCDebug(interfaceapp) << "Declined to use the avatar: " << url; } - + return true; } @@ -4152,7 +4157,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser if (isAboutToQuit()) { return NULL; } - + QUrl scriptUrl(scriptFilename); const QString& scriptURLString = scriptUrl.toString(); if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor @@ -4163,18 +4168,18 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); scriptEngine->setUserLoaded(isUserLoaded); - + if (scriptFilename.isNull()) { // this had better be the script editor (we should de-couple so somebody who thinks they are loading a script // doesn't just get an empty script engine) - + // we can complete setup now since there isn't a script we have to load registerScriptEngineWithApplicationServices(scriptEngine); } else { // connect to the appropriate signals of this script engine connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded); connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); - + // get the script engine object to load the script at the designated script URL scriptEngine->loadURL(scriptUrl); } @@ -4189,11 +4194,11 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); - + _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); UserActivityLogger::getInstance().loadedScript(scriptFilename); - + // register our application services and set it off on its own thread registerScriptEngineWithApplicationServices(scriptEngine); } @@ -4303,7 +4308,7 @@ void Application::updateMyAvatarTransform() { glm::vec3 newOriginOffset = avatarPosition; int halfExtent = (int)HALF_SIMULATION_EXTENT; for (int i = 0; i < 3; ++i) { - newOriginOffset[i] = (float)(glm::max(halfExtent, + newOriginOffset[i] = (float)(glm::max(halfExtent, ((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION)); } // TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine @@ -4317,23 +4322,23 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; const QString VOXEL_WALLET_UUID = "voxel-wallet"; - + const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); - + qint64 satoshisPerVoxel = 0; qint64 satoshisPerMeterCubed = 0; QUuid voxelWalletUUID; - + if (!domainSettingsObject.isEmpty()) { float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); - + satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); - + voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); } - + qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed"; qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; } @@ -4536,7 +4541,7 @@ bool Application::isVSyncOn() const { } else { return true; } - */ + */ #endif return true; } @@ -4634,14 +4639,14 @@ void Application::notifyPacketVersionMismatch() { void Application::checkSkeleton() { if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; - + QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; QMessageBox msgBox; msgBox.setText(message); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); - + _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default"); } else { _physicsEngine.setCharacterController(_myAvatar->getCharacterController()); @@ -4654,7 +4659,7 @@ void Application::showFriendsWindow() { const int FRIENDS_WINDOW_WIDTH = 290; const int FRIENDS_WINDOW_HEIGHT = 500; if (!_friendsWindow) { - _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, + _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, FRIENDS_WINDOW_HEIGHT, false); connect(_friendsWindow, &WebWindowClass::closed, this, &Application::friendsWindowClosed); } @@ -4695,16 +4700,16 @@ QSize Application::getDeviceSize() const { return _glWidget->getDeviceSize(); } -int Application::getTrueMouseX() const { - return _glWidget->mapFromGlobal(QCursor::pos()).x(); +int Application::getTrueMouseX() const { + return _glWidget->mapFromGlobal(QCursor::pos()).x(); } -int Application::getTrueMouseY() const { - return _glWidget->mapFromGlobal(QCursor::pos()).y(); +int Application::getTrueMouseY() const { + return _glWidget->mapFromGlobal(QCursor::pos()).y(); } -bool Application::isThrottleRendering() const { - return _glWidget->isThrottleRendering(); +bool Application::isThrottleRendering() const { + return _glWidget->isThrottleRendering(); } PickRay Application::computePickRay() const { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 41c8cb9537..340ca9374d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -153,6 +153,7 @@ void AudioClient::reset() { } void AudioClient::audioMixerKilled() { + _hasReceivedFirstPacket = false; _outgoingAvatarAudioSequenceNumber = 0; _stats.reset(); } @@ -481,6 +482,7 @@ void AudioClient::start() { qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format."; qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat); } + if (!outputFormatSupported) { qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format."; qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.nearestFormat(_desiredOutputFormat); @@ -489,6 +491,7 @@ void AudioClient::start() { if (_audioInput) { _inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 8 ); } + _inputGain.initialize(); _sourceGain.initialize(); _noiseSource.initialize(); @@ -926,6 +929,14 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket); if (_audioOutput) { + + if (!_hasReceivedFirstPacket) { + _hasReceivedFirstPacket = true; + + // have the audio scripting interface emit a signal to say we just connected to mixer + emit receivedFirstPacket(); + } + // Audio output must exist and be correctly set up if we're going to process received audio _receivedAudioStream.parseData(audioByteArray); } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index f9392c6a10..3b2c1c1ae6 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "AudioIOStats.h" @@ -57,7 +58,7 @@ static const int NUM_AUDIO_CHANNELS = 2; static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3; static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1; static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20; -#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN) +#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN) static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false; #else static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true; @@ -88,14 +89,14 @@ public: void stop() { close(); } qint64 readData(char * data, qint64 maxSize); qint64 writeData(const char * data, qint64 maxSize) { return 0; } - + int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; } private: MixedProcessedAudioStream& _receivedAudioStream; AudioClient* _audio; int _unfulfilledReads; }; - + const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } @@ -105,30 +106,30 @@ public: float getAudioAverageInputLoudness() const { return _lastInputLoudness; } int getDesiredJitterBufferFrames() const { return _receivedAudioStream.getDesiredJitterBufferFrames(); } - + bool isMuted() { return _muted; } - + const AudioIOStats& getStats() const { return _stats; } float getInputRingBufferMsecsAvailable() const; float getAudioOutputMsecsUnplayed() const; int getOutputBufferSize() { return _outputBufferSizeFrames.get(); } - + bool getOutputStarveDetectionEnabled() { return _outputStarveDetectionEnabled.get(); } void setOutputStarveDetectionEnabled(bool enabled) { _outputStarveDetectionEnabled.set(enabled); } int getOutputStarveDetectionPeriod() { return _outputStarveDetectionPeriodMsec.get(); } void setOutputStarveDetectionPeriod(int msecs) { _outputStarveDetectionPeriodMsec.set(msecs); } - + int getOutputStarveDetectionThreshold() { return _outputStarveDetectionThreshold.get(); } void setOutputStarveDetectionThreshold(int threshold) { _outputStarveDetectionThreshold.set(threshold); } - + void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } static const float CALLBACK_ACCELERATOR_RATIO; - + public slots: void start(); void stop(); @@ -140,7 +141,7 @@ public slots: void reset(); void audioMixerKilled(); void toggleMute(); - + virtual void enableAudioSourceInject(bool enable); virtual void selectAudioSourcePinkNoise(); virtual void selectAudioSourceSine440(); @@ -148,10 +149,10 @@ public slots: virtual void setIsStereoInput(bool stereo); void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } - + void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } - + void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); void sendMuteEnvironmentPacket(); @@ -172,10 +173,10 @@ public slots: void setReverbOptions(const AudioEffectOptions* options); void outputNotify(); - + void loadSettings(); void saveSettings(); - + signals: bool muteToggled(); void inputReceived(const QByteArray& inputSamples); @@ -184,14 +185,16 @@ signals: void deviceChanged(); + void receivedFirstPacket(); + protected: AudioClient(); ~AudioClient(); - + virtual void customDeleter() { deleteLater(); } - + private: void outputFormatChanged(); @@ -216,35 +219,35 @@ private: QString _inputAudioDeviceName; QString _outputAudioDeviceName; - + quint64 _outputStarveDetectionStartTimeMsec; int _outputStarveDetectionCount; - + Setting::Handle _outputBufferSizeFrames; Setting::Handle _outputStarveDetectionEnabled; Setting::Handle _outputStarveDetectionPeriodMsec; // Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size Setting::Handle _outputStarveDetectionThreshold; - + StDev _stdev; QElapsedTimer _timeSinceLastReceived; float _averagedLatency; float _lastInputLoudness; float _timeSinceLastClip; int _totalInputAudioSamples; - + bool _muted; bool _shouldEchoLocally; bool _shouldEchoToServer; bool _isNoiseGateEnabled; bool _audioSourceInjectEnabled; - + bool _reverb; AudioEffectOptions _scriptReverbOptions; AudioEffectOptions _zoneReverbOptions; AudioEffectOptions* _reverbOptions; ty_gverb* _gverb; - + // possible soxr streams needed for resample soxr* _inputToNetworkResampler; soxr* _networkToOutputResampler; @@ -268,17 +271,17 @@ private: // Input framebuffer AudioBufferFloat32 _inputFrameBuffer; - + // Input gain AudioGain _inputGain; - + // Post tone/pink noise generator gain AudioGain _sourceGain; // Pink noise source bool _noiseSourceEnabled; AudioSourcePinkNoise _noiseSource; - + // Tone source bool _toneSourceEnabled; AudioSourceTone _toneSource; @@ -286,17 +289,19 @@ private: quint16 _outgoingAvatarAudioSequenceNumber; AudioOutputIODevice _audioOutputIODevice; - + AudioIOStats _stats; - + AudioNoiseGate _inputGate; - + AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; - + QVector _inputDevices; QVector _outputDevices; void checkDevices(); + + bool _hasReceivedFirstPacket = false; }; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index ae397ba97e..5456624b68 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -26,7 +26,7 @@ AudioInjector::AudioInjector(QObject* parent) : QObject(parent) { - + } AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) : @@ -39,24 +39,24 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt _audioData(audioData), _options(injectorOptions) { - + } void AudioInjector::setIsFinished(bool isFinished) { _isFinished = isFinished; - + if (_isFinished) { emit finished(); - + if (_localBuffer) { _localBuffer->stop(); _localBuffer->deleteLater(); _localBuffer = NULL; } - + _isStarted = false; _shouldStop = false; - + if (_shouldDeleteAfterFinish) { // we've been asked to delete after finishing, trigger a queued deleteLater here qCDebug(audio) << "AudioInjector triggering delete from setIsFinished"; @@ -69,16 +69,16 @@ void AudioInjector::injectAudio() { if (!_isStarted) { // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { - + // convert the offset into a number of bytes int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); byteOffset *= sizeof(int16_t); - + _currentSendPosition = byteOffset; } else { _currentSendPosition = 0; } - + if (_options.localOnly) { injectLocally(); } else { @@ -86,7 +86,7 @@ void AudioInjector::injectAudio() { } } else { qCDebug(audio) << "AudioInjector::injectAudio called but already started."; - } + } } void AudioInjector::restart() { @@ -100,37 +100,37 @@ void AudioInjector::injectLocally() { bool success = false; if (_localAudioInterface) { if (_audioData.size() > 0) { - + _localBuffer = new AudioInjectorLocalBuffer(_audioData, this); - + _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); _localBuffer->setVolume(_options.volume); - + // give our current send position to the local buffer _localBuffer->setCurrentOffset(_currentSendPosition); - + success = _localAudioInterface->outputLocalInjector(_options.stereo, this); - + // if we're not looping and the buffer tells us it is empty then emit finished connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop); - + if (!success) { qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; } } else { qCDebug(audio) << "AudioInjector::injectLocally called without any data in Sound QByteArray"; } - + } else { qCDebug(audio) << "AudioInjector::injectLocally cannot inject locally with no local audio interface present."; } - + if (!success) { // we never started so we are finished, call our stop method stop(); } - + } const uchar MAX_INJECTOR_VOLUME = 0xFF; @@ -140,65 +140,65 @@ void AudioInjector::injectToMixer() { _currentSendPosition >= _audioData.size()) { _currentSendPosition = 0; } - + auto nodeList = DependencyManager::get(); - + // make sure we actually have samples downloaded to inject if (_audioData.size()) { - + // setup the packet for injected audio QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio); QDataStream packetStream(&injectAudioPacket, QIODevice::Append); - + // pack some placeholder sequence number for now int numPreSequenceNumberBytes = injectAudioPacket.size(); packetStream << (quint16)0; - + // pack stream identifier (a generated UUID) packetStream << QUuid::createUuid(); - + // pack the stereo/mono type of the stream packetStream << _options.stereo; - + // pack the flag for loopback uchar loopbackFlag = (uchar) true; packetStream << loopbackFlag; - + // pack the position for injected audio int positionOptionOffset = injectAudioPacket.size(); packetStream.writeRawData(reinterpret_cast(&_options.position), sizeof(_options.position)); - + // pack our orientation for injected audio int orientationOptionOffset = injectAudioPacket.size(); packetStream.writeRawData(reinterpret_cast(&_options.orientation), sizeof(_options.orientation)); - + // pack zero for radius float radius = 0; packetStream << radius; - + // pack 255 for attenuation byte int volumeOptionOffset = injectAudioPacket.size(); quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; packetStream << volume; - + packetStream << _options.ignorePenumbra; - + QElapsedTimer timer; timer.start(); int nextFrame = 0; - + int numPreAudioDataBytes = injectAudioPacket.size(); bool shouldLoop = _options.loop; - + // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; while (_currentSendPosition < _audioData.size() && !_shouldStop) { - + int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, _audioData.size() - _currentSendPosition); - + // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { @@ -215,45 +215,45 @@ void AudioInjector::injectToMixer() { sizeof(_options.orientation)); volume = MAX_INJECTOR_VOLUME * _options.volume; memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume)); - + // resize the QByteArray to the right size injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); // pack the sequence number memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); - + // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet memcpy(injectAudioPacket.data() + numPreAudioDataBytes, _audioData.data() + _currentSendPosition, bytesToCopy); - + // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - + // send off this audio packet nodeList->writeDatagram(injectAudioPacket, audioMixer); outgoingInjectedAudioSequenceNumber++; - + _currentSendPosition += bytesToCopy; - + // send two packets before the first sleep so the mixer can start playback right away - + if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { - + // process events in case we have been told to stop and be deleted QCoreApplication::processEvents(); - + if (_shouldStop) { break; } - + // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; - + if (usecToSleep > 0) { usleep(usecToSleep); - } + } } if (shouldLoop && _currentSendPosition >= _audioData.size()) { @@ -261,13 +261,13 @@ void AudioInjector::injectToMixer() { } } } - + setIsFinished(true); } void AudioInjector::stop() { _shouldStop = true; - + if (_options.localOnly) { // we're only a local injector, so we can say we are finished right away too setIsFinished(true); diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 577328ee18..7dc6010f8f 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -53,21 +53,25 @@ Sound::Sound(const QUrl& url, bool isStereo) : _isStereo(isStereo), _isReady(false) { - + } void Sound::downloadFinished(QNetworkReply* reply) { // replace our byte array with the downloaded data QByteArray rawAudioByteArray = reply->readAll(); + QString fileName = reply->url().fileName(); - if (reply->hasRawHeader("Content-Type")) { + const QString WAV_EXTENSION = ".wav"; + + if (reply->hasRawHeader("Content-Type") || fileName.endsWith(WAV_EXTENSION)) { QByteArray headerContentType = reply->rawHeader("Content-Type"); // WAV audio file encountered if (headerContentType == "audio/x-wav" || headerContentType == "audio/wav" - || headerContentType == "audio/wave") { + || headerContentType == "audio/wave" + || fileName.endsWith(WAV_EXTENSION)) { QByteArray outputAudioByteArray; @@ -80,7 +84,7 @@ void Sound::downloadFinished(QNetworkReply* reply) { _isStereo = true; qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << reply->url() << "as stereo audio file."; } - + // Process as RAW file downSample(rawAudioByteArray); } @@ -88,7 +92,7 @@ void Sound::downloadFinished(QNetworkReply* reply) { } else { qCDebug(audio) << "Network reply without 'Content-Type'."; } - + _isReady = true; reply->deleteLater(); } @@ -99,16 +103,16 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { // we want to convert it to the format that the audio-mixer wants // which is signed, 16-bit, 24Khz - + int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample); - + int numDestinationBytes = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample); if (_isStereo && numSourceSamples % 2 != 0) { numDestinationBytes += sizeof(AudioConstants::AudioSample); } - + _byteArray.resize(numDestinationBytes); - + int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data(); int16_t* destinationSamples = (int16_t*) _byteArray.data(); @@ -134,22 +138,22 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { } void Sound::trimFrames() { - + const uint32_t inputFrameCount = _byteArray.size() / sizeof(int16_t); const uint32_t trimCount = 1024; // number of leading and trailing frames to trim - + if (inputFrameCount <= (2 * trimCount)) { return; } - + int16_t* inputFrameData = (int16_t*)_byteArray.data(); AudioEditBufferFloat32 editBuffer(1, inputFrameCount); editBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); - + editBuffer.linearFade(0, trimCount, true); editBuffer.linearFade(inputFrameCount - trimCount, inputFrameCount, false); - + editBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); } @@ -238,7 +242,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou } else if (qFromLittleEndian(fileHeader.wave.numChannels) > 2) { qCDebug(audio) << "Currently not support audio files with more than 2 channels."; } - + if (qFromLittleEndian(fileHeader.wave.bitsPerSample) != 16) { qCDebug(audio) << "Currently not supporting non 16bit audio files."; return; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index e210ee6f6e..9e3e924933 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -28,19 +28,19 @@ AudioScriptingInterface& AudioScriptingInterface::getInstance() { AudioScriptingInterface::AudioScriptingInterface() : _localAudioInterface(NULL) { - + } ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { if (QThread::currentThread() != thread()) { ScriptAudioInjector* injector = NULL; - + QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptAudioInjector*, injector), Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); return injector; } - + if (sound) { // stereo option isn't set from script, this comes from sound metadata or filename AudioInjectorOptions optionsCopy = injectorOptions; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index bbc9a57db8..d74e1ed1e0 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -22,22 +22,23 @@ class AudioScriptingInterface : public QObject { Q_OBJECT public: static AudioScriptingInterface& getInstance(); - + void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } - + protected: // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); - + Q_INVOKABLE void injectGeneratedNoise(bool inject); Q_INVOKABLE void selectPinkNoise(); Q_INVOKABLE void selectSine440(); Q_INVOKABLE void setStereoInput(bool stereo); - + signals: void mutedByMixer(); void environmentMuted(); + void receivedFirstPacket(); private: AudioScriptingInterface(); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 84c8ae4939..79e83e9b40 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -18,7 +18,7 @@ #include "PathUtils.h" -QString& PathUtils::resourcesPath() { +const QString& PathUtils::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; #else diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 6b6893574b..12b1d57641 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -12,12 +12,17 @@ #ifndef hifi_PathUtils_h #define hifi_PathUtils_h +#include -#include +#include "DependencyManager.h" -namespace PathUtils { - QString& resourcesPath(); -} +class PathUtils : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + Q_PROPERTY(QString resources READ resourcesPath) +public: + static const QString& resourcesPath(); +}; QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions);