From 3b653ca542ca3313f3594c02182928e583b43d81 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:00:12 -0700 Subject: [PATCH 1/3] add a sendAvatarAudioStream option to Agent for mixer load testing, closes #2291 --- assignment-client/src/Agent.cpp | 28 +++++++++++++++++++- assignment-client/src/Agent.h | 7 +++++ libraries/script-engine/src/ScriptEngine.cpp | 27 +++++++++++++------ libraries/script-engine/src/ScriptEngine.h | 11 ++++++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 13b1f2bdcf..9d4f07043d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,8 @@ Agent::Agent(const QByteArray& packet) : ThreadedAssignment(packet), _voxelEditSender(), - _particleEditSender() + _particleEditSender(), + _avatarAudioStream(NULL) { // be the parent of the script engine so it gets moved when we do _scriptEngine.setParent(this); @@ -34,6 +36,30 @@ Agent::Agent(const QByteArray& packet) : _scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); } +Agent::~Agent() { + delete _avatarAudioStream; +} + +const int SCRIPT_AUDIO_BUFFER_SAMPLES = (SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000); + +void Agent::setSendAvatarAudioStream(bool sendAvatarAudioStream) { + if (sendAvatarAudioStream) { + // the agentAudioStream number of samples is related to the ScriptEngine callback rate + _avatarAudioStream = new int16_t[SCRIPT_AUDIO_BUFFER_SAMPLES]; + + // fill the _audioStream with zeroes to start + memset(_avatarAudioStream, 0, SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t)); + + _scriptEngine.setNumAvatarAudioBufferSamples(SCRIPT_AUDIO_BUFFER_SAMPLES); + _scriptEngine.setAvatarAudioBuffer(_avatarAudioStream); + } else { + delete _avatarAudioStream; + _avatarAudioStream = NULL; + + _scriptEngine.setAvatarAudioBuffer(NULL); + } +} + void Agent::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1b41636874..a051f42faf 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -28,12 +28,17 @@ class Agent : public ThreadedAssignment { Q_OBJECT Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) + Q_PROPERTY(bool sendAvatarAudioStream READ isSendingAvatarAudioStream WRITE setSendAvatarAudioStream) public: Agent(const QByteArray& packet); + ~Agent(); void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); } bool isAvatar() const { return _scriptEngine.isAvatar(); } + void setSendAvatarAudioStream(bool sendAvatarAudioStream); + bool isSendingAvatarAudioStream() const { return (bool) _scriptEngine.sendsAvatarAudioStream(); } + public slots: void run(); void readPendingDatagrams(); @@ -45,6 +50,8 @@ private: ParticleTreeHeadlessViewer _particleViewer; VoxelTreeHeadlessViewer _voxelViewer; + + int16_t* _avatarAudioStream; }; #endif /* defined(__hifi__Agent__) */ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 04916687fb..9a672317ae 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -28,8 +28,6 @@ #include "LocalVoxels.h" #include "ScriptEngine.h" -const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -54,6 +52,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), _timerFunctionMap(), + _avatarAudioBuffer(NULL), _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _wantMenuItems(wantMenuItems), @@ -77,9 +76,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _scriptNumber++; } -ScriptEngine::~ScriptEngine() { -} - void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; @@ -169,8 +165,8 @@ void ScriptEngine::init() { _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); // let the VoxelPacketSender know how frequently we plan to call it - _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); + _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); + _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); } @@ -228,7 +224,7 @@ void ScriptEngine::run() { qint64 lastUpdate = usecTimestampNow(); while (!_isFinished) { - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } @@ -268,6 +264,21 @@ void ScriptEngine::run() { avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); + + if (_avatarAudioBuffer && _numAvatarAudioBufferSamples > 0) { + // if have an avatar audio stream then send it out to our audio-mixer + QByteArray audioPacket = byteArrayWithPopulatedHeader(PacketTypeMicrophoneAudioNoEcho); + QDataStream packetStream(&audioPacket, QIODevice::Append); + + // use the orientation and position of this avatar for the source of this audio + packetStream.writeRawData(reinterpret_cast(&_avatarData->getPosition()), sizeof(glm::vec3)); + glm::quat headOrientation = _avatarData->getHeadOrientation(); + packetStream.writeRawData(reinterpret_cast(&headOrientation), sizeof(glm::quat)); + packetStream.writeRawData(reinterpret_cast(_avatarAudioBuffer), + _numAvatarAudioBufferSamples * sizeof(int16_t)); + + nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer); + } } qint64 now = usecTimestampNow(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 186524eb7f..9a1d2172dd 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -28,6 +28,8 @@ class ParticlesScriptingInterface; const QString NO_SCRIPT(""); +const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); + class ScriptEngine : public QObject { Q_OBJECT public: @@ -35,8 +37,6 @@ public: const QString& scriptMenuName = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); - ~ScriptEngine(); - /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } @@ -56,6 +56,11 @@ public: void setAvatarData(AvatarData* avatarData, const QString& objectName); + void setAvatarAudioBuffer(int16_t* avatarAudioBuffer) { _avatarAudioBuffer = avatarAudioBuffer; } + bool sendsAvatarAudioStream() const { return (bool) _avatarAudioBuffer; } + void setNumAvatarAudioBufferSamples(int numAvatarAudioBufferSamples) + { _numAvatarAudioBufferSamples = numAvatarAudioBufferSamples; } + void init(); void run(); /// runs continuously until Agent.stop() is called void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller @@ -86,6 +91,8 @@ protected: QTimer* _avatarIdentityTimer; QTimer* _avatarBillboardTimer; QHash _timerFunctionMap; + int16_t* _avatarAudioBuffer; + int _numAvatarAudioBufferSamples; private: void sendAvatarIdentityPacket(); From bd6c42a68505465fd1e4bc9b068dda4733c399bd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:32:52 -0700 Subject: [PATCH 2/3] define roundf for use on Win from ScriptEngine --- libraries/script-engine/src/ScriptEngine.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 9a1d2172dd..7dadc17cc6 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -20,14 +20,20 @@ #include -class ParticlesScriptingInterface; - #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "Vec3.h" +class ParticlesScriptingInterface; + const QString NO_SCRIPT(""); +#ifdef Q_OS_WIN32 +inline double roundf(double value) { + return (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5); +} +#endif + const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); class ScriptEngine : public QObject { From 54d73d4a6458df8041459b410e7cadd2382ebb5a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:50:58 -0700 Subject: [PATCH 3/3] use floor since round is missing on windows --- assignment-client/src/Agent.cpp | 2 +- libraries/script-engine/src/ScriptEngine.h | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9d4f07043d..46f4d233c2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -40,7 +40,7 @@ Agent::~Agent() { delete _avatarAudioStream; } -const int SCRIPT_AUDIO_BUFFER_SAMPLES = (SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000); +const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5); void Agent::setSendAvatarAudioStream(bool sendAvatarAudioStream) { if (sendAvatarAudioStream) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7dadc17cc6..606d0aabf4 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -28,13 +28,7 @@ class ParticlesScriptingInterface; const QString NO_SCRIPT(""); -#ifdef Q_OS_WIN32 -inline double roundf(double value) { - return (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5); -} -#endif - -const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); +const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5); class ScriptEngine : public QObject { Q_OBJECT