diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 133a72859e..d9cf479b17 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -8,8 +8,6 @@ #include -#include - #include #include #include @@ -22,9 +20,9 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes), _shouldStop(false) { - } + void Agent::stop() { _shouldStop = true; } @@ -41,10 +39,27 @@ static size_t writeScriptDataToString(void *contents, size_t size, size_t nmemb, return realSize; } +QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec3.x); + obj.setProperty("y", vec3.y); + obj.setProperty("z", vec3.z); + return obj; +} + +void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { + vec3.x = object.property("x").toVariant().toFloat(); + vec3.y = object.property("y").toVariant().toFloat(); + vec3.z = object.property("z").toVariant().toFloat(); +} + void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); - nodeList->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); + + const char AGENT_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER }; + + nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); nodeList->getNodeSocket()->setBlocking(false); @@ -84,6 +99,9 @@ void Agent::run() { QScriptEngine engine; + // register meta-type for glm::vec3 conversions + qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue); + QScriptValue agentValue = engine.newQObject(this); engine.globalObject().setProperty("Agent", agentValue); @@ -93,14 +111,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); + voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(INJECT_INTERVAL_USECS); - QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000))); - engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue); + // hook in a constructor for audio injectorss + 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); @@ -111,19 +129,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) { @@ -136,38 +155,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 0490238d3f..ba70fd58ec 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -9,9 +9,13 @@ #ifndef __hifi__Agent__ #define __hifi__Agent__ +#include + +#include #include #include +#include #include class Agent : public Assignment { @@ -23,9 +27,13 @@ public: public slots: void stop(); signals: + void willSendAudioDataCallback(); void willSendVisualDataCallback(); private: + static QScriptValue AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine); + bool volatile _shouldStop; + std::vector _audioInjectors; }; #endif /* defined(__hifi__Operative__) */ diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 9d50469c6d..03578c4e8a 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -66,12 +66,13 @@ void childClient() { // create a request assignment, accept assignments defined by the overidden type Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType); - // if we're here we have no assignment, so send a request qDebug() << "Waiting for assignment -" << requestAssignment << "\n"; while (true) { if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { gettimeofday(&lastRequest, NULL); + + // if we're here we have no assignment, so send a request nodeList->sendAssignment(requestAssignment); } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index b9a6d8a0ba..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; @@ -152,3 +138,15 @@ void AudioInjector::addSamples(int16_t* sampleBuffer, int numSamples) { _indexOfNextSlot += numSamples; } } + +int16_t& AudioInjector::sampleAt(const int index) { + assert(index >= 0 && index < _numTotalSamples); + + return _audioSampleArray[index]; +} + +void AudioInjector::insertSample(const int index, int sample) { + assert (index >= 0 && index < _numTotalSamples); + + _audioSampleArray[index] = (int16_t) sample; +} diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index b102d58e19..626cdc3149 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -12,6 +12,9 @@ #include #include +#include + +#include #include #include "AudioRingBuffer.h" @@ -22,7 +25,11 @@ const int MAX_INJECTOR_VOLUME = 0xFF; const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); -class AudioInjector { +class AudioInjector : public QObject { + Q_OBJECT + + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(uchar volume READ getVolume WRITE setVolume); public: AudioInjector(const char* filename); AudioInjector(int maxNumSamples); @@ -35,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; } @@ -48,6 +53,9 @@ public: void addSample(const int16_t sample); void addSamples(int16_t* sampleBuffer, int numSamples); +public slots: + int16_t& sampleAt(const int index); + void insertSample(const int index, int sample); private: unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES]; int16_t* _audioSampleArray; @@ -58,7 +66,6 @@ private: unsigned char _volume; int _indexOfNextSlot; bool _isInjectingAudio; - float _lastFrameIntensity; }; #endif /* defined(__hifi__AudioInjector__) */ diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f0085a4138..46aad2d86f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -53,38 +53,6 @@ AvatarData::~AvatarData() { delete _handData; } -void AvatarData::setPositionFromVariantMap(QVariantMap positionMap) { - _position = glm::vec3(positionMap.value("x").toFloat(), - positionMap.value("y").toFloat(), - positionMap.value("z").toFloat()); -} - -QVariantMap AvatarData::getPositionVariantMap() { - QVariantMap positionMap; - - positionMap.insert("x", _position.x); - positionMap.insert("y", _position.y); - positionMap.insert("z", _position.z); - - return positionMap; -} - -void AvatarData::setHandPositionFromVariantMap(QVariantMap handPositionMap) { - _handPosition = glm::vec3(handPositionMap.value("x").toFloat(), - handPositionMap.value("y").toFloat(), - handPositionMap.value("z").toFloat()); -} - -QVariantMap AvatarData::getHandPositionVariantMap() { - QVariantMap positionMap; - - positionMap.insert("x", _handPosition.x); - positionMap.insert("y", _handPosition.y); - positionMap.insert("z", _handPosition.z); - - return positionMap; -} - void AvatarData::sendData() { // called from Agent visual loop to send data diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 57327f3bba..f3ccab1504 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -20,6 +20,8 @@ #include #include +#include + #include #include "HeadData.h" #include "HandData.h" @@ -49,8 +51,8 @@ class JointData; class AvatarData : public NodeData { Q_OBJECT - Q_PROPERTY(QVariantMap position READ getPositionVariantMap WRITE setPositionFromVariantMap) - Q_PROPERTY(QVariantMap handPosition READ getHandPositionVariantMap WRITE setHandPositionFromVariantMap) + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) @@ -60,16 +62,11 @@ public: ~AvatarData(); const glm::vec3& getPosition() const { return _position; } - void setPosition(const glm::vec3 position) { _position = position; } + + const glm::vec3& getHandPosition() const { return _handPosition; } void setHandPosition(const glm::vec3 handPosition) { _handPosition = handPosition; } - void setPositionFromVariantMap(QVariantMap positionMap); - QVariantMap getPositionVariantMap(); - - void setHandPositionFromVariantMap(QVariantMap handPositionMap); - QVariantMap getHandPositionVariantMap(); - int getBroadcastData(unsigned char* destinationBuffer); int parseData(unsigned char* sourceBuffer, int numBytes); diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 11ae372830..12a24c535f 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -437,7 +437,6 @@ void NodeList::sendAssignment(Assignment& assignment) { } Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId) { - NodeList::iterator node = end(); if (publicSocket) { @@ -553,7 +552,7 @@ void* removeSilentNodes(void *args) { node->lock(); if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) { - + qDebug() << "Killed " << *node << "\n"; nodeList->notifyHooksOfKilledNode(&*node); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h new file mode 100644 index 0000000000..e15e053ebb --- /dev/null +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -0,0 +1,16 @@ +// +// RegisteredMetaTypes.h +// hifi +// +// Created by Stephen Birarda on 10/3/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// +// Used to register meta-types with Qt so that they can be used as properties for objects exposed to our +// Agent scripting. + +#ifndef hifi_RegisteredMetaTypes_h +#define hifi_RegisteredMetaTypes_h + +Q_DECLARE_METATYPE(glm::vec3) + +#endif