From 0eae0f129cd96f473baa1979ec89625803e14e2b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 11 Oct 2013 17:37:48 -0700 Subject: [PATCH] allow audio injection from javascript --- assignment-client/src/Agent.cpp | 75 ++++++++++++--------------- assignment-client/src/Agent.h | 1 + libraries/audio/src/AudioInjector.cpp | 32 ++++-------- libraries/audio/src/AudioInjector.h | 5 +- libraries/shared/src/NodeList.cpp | 2 - 5 files changed, 45 insertions(+), 70 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index b2b4a72a10..81fe4cb5e5 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -45,15 +45,6 @@ void Agent::setStaticInstance(Agent* staticInstance) { _staticInstance = staticInstance; } -QScriptValue Agent::AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine) { - AudioInjector* injector = new AudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - - // add this injector to the vector of audio injectors so we know we have to tell it to send its audio during loop - _staticInstance->_audioInjectors.push_back(injector); - - return engine->newQObject(injector, QScriptEngine::ScriptOwnership); -} - QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec3.x); @@ -126,19 +117,14 @@ void Agent::run() { QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); - - const long long VISUAL_DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000; // let the VoxelPacketSender know how frequently we plan to call it - voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_SEND_INTERVAL_USECS); - - QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000))); - engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue); + voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(INJECT_INTERVAL_USECS); // hook in a constructor for audio injectorss - QScriptValue audioInjectorConstructor = engine.newFunction(AudioInjectorConstructor); - QScriptValue audioMetaObject = engine.newQMetaObject(&AudioInjector::staticMetaObject, audioInjectorConstructor); - engine.globalObject().setProperty("AudioInjector", audioMetaObject); + AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector); + engine.globalObject().setProperty("AudioInjector", audioInjectorValue); qDebug() << "Downloaded script:" << scriptContents << "\n"; QScriptValue result = engine.evaluate(scriptContents); @@ -149,19 +135,20 @@ void Agent::run() { qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; } - timeval thisSend; + timeval startTime; + gettimeofday(&startTime, NULL); + timeval lastDomainServerCheckIn = {}; - int numMicrosecondsSleep = 0; sockaddr_in senderAddress; unsigned char receivedData[MAX_PACKET_SIZE]; ssize_t receivedBytes; - bool hasVoxelServer = false; + int thisFrame = 0; + + bool firstDomainCheckIn = false; while (!_shouldStop) { - // update the thisSend timeval to the current time - gettimeofday(&thisSend, NULL); // if we're not hearing from the domain-server we should stop running if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { @@ -174,38 +161,44 @@ void Agent::run() { NodeList::getInstance()->sendDomainServerCheckIn(); } - if (!hasVoxelServer) { - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { - hasVoxelServer = true; + if (firstDomainCheckIn) { + // find the audio-mixer in the NodeList so we can inject audio at it + Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + + emit willSendAudioDataCallback(); + + if (audioMixer) { + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); + if (usecToSleep > 0) { + usleep(usecToSleep); } + + scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getPublicSocket()); } - } - - if (hasVoxelServer) { + // allow the scripter's call back to setup visual data emit willSendVisualDataCallback(); - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; - } - // release the queue of edit voxel messages. voxelScripter.getVoxelPacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent voxelScripter.getVoxelPacketSender()->process(); + } + if (engine.hasUncaughtException()) { + int line = engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; + } + while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { + if (!firstDomainCheckIn && receivedData[0] == PACKET_TYPE_DOMAIN) { + firstDomainCheckIn = true; + } + NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } - - // sleep for the correct amount of time to have data send be consistently timed - if ((numMicrosecondsSleep = VISUAL_DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { - usleep(numMicrosecondsSleep); - } } } else { diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 84e8e11269..de6a94fd6b 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -27,6 +27,7 @@ public: public slots: void stop(); signals: + void willSendAudioDataCallback(); void willSendVisualDataCallback(); private: static void setStaticInstance(Agent* staticInstance); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index cc0a329641..acdee03e8f 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -21,8 +21,7 @@ AudioInjector::AudioInjector(const char* filename) : _radius(0.0f), _volume(MAX_INJECTOR_VOLUME), _indexOfNextSlot(0), - _isInjectingAudio(false), - _lastFrameIntensity(0.0f) + _isInjectingAudio(false) { loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); @@ -52,8 +51,7 @@ AudioInjector::AudioInjector(int maxNumSamples) : _radius(0.0f), _volume(MAX_INJECTOR_VOLUME), _indexOfNextSlot(0), - _isInjectingAudio(false), - _lastFrameIntensity(0.0f) + _isInjectingAudio(false) { loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); @@ -103,6 +101,11 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination int nextFrame = 0; for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + int usecToSleep = usecTimestamp(&startTime) + (nextFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); + if (usecToSleep > 0) { + usleep(usecToSleep); + } + int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL; if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { @@ -115,23 +118,6 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination memcpy(currentPacketPtr, _audioSampleArray + i, numSamplesToCopy * sizeof(int16_t)); injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket)); - - // calculate the intensity for this frame - float lastRMS = 0; - - for (int j = 0; j < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; j++) { - lastRMS += _audioSampleArray[i + j] * _audioSampleArray[i + j]; - } - - lastRMS /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - lastRMS = sqrtf(lastRMS); - - _lastFrameIntensity = lastRMS / std::numeric_limits::max(); - - int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } } _isInjectingAudio = false; @@ -159,8 +145,8 @@ int16_t& AudioInjector::sampleAt(const int index) { return _audioSampleArray[index]; } -void AudioInjector::insertSample(const int index, int16_t sample) { +void AudioInjector::insertSample(const int index, int sample) { assert (index >= 0 && index < _numTotalSamples); - _audioSampleArray[index] = sample; + _audioSampleArray[index] = (int16_t) sample; } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 9054b08a16..626cdc3149 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -42,8 +42,6 @@ public: unsigned char getVolume() const { return _volume; } void setVolume(unsigned char volume) { _volume = volume; } - float getLastFrameIntensity() const { return _lastFrameIntensity; } - const glm::vec3& getPosition() const { return _position; } void setPosition(const glm::vec3& position) { _position = position; } @@ -57,7 +55,7 @@ public: void addSamples(int16_t* sampleBuffer, int numSamples); public slots: int16_t& sampleAt(const int index); - void insertSample(const int index, int16_t sample); + void insertSample(const int index, int sample); private: unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES]; int16_t* _audioSampleArray; @@ -68,7 +66,6 @@ private: unsigned char _volume; int _indexOfNextSlot; bool _isInjectingAudio; - float _lastFrameIntensity; }; #endif /* defined(__hifi__AudioInjector__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index a559b58f10..12a24c535f 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -441,10 +441,8 @@ Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, c if (publicSocket) { for (node = begin(); node != end(); node++) { - qDebug() << "comparing to node with ID " << node->getNodeID() << "\n"; if (node->matches(publicSocket, localSocket, nodeType)) { // we already have this node, stop checking - qDebug() << "Matched node to existing\n"; break; } }