mirror of
https://github.com/overte-org/overte.git
synced 2025-08-12 11:14:10 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into metavoxels
This commit is contained in:
commit
f2ebd76362
38 changed files with 473 additions and 75 deletions
|
@ -22,6 +22,8 @@
|
|||
const char ASSIGNMENT_CLIENT_TARGET_NAME[] = "assignment-client";
|
||||
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
||||
|
||||
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
||||
AssignmentClient::AssignmentClient(int &argc, char **argv,
|
||||
Assignment::Type requestAssignmentType,
|
||||
const HifiSockAddr& customAssignmentServerSocket,
|
||||
|
@ -31,7 +33,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv,
|
|||
_currentAssignment(NULL)
|
||||
{
|
||||
// register meta type is required for queued invoke method on Assignment subclasses
|
||||
qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
|
|
@ -53,8 +53,6 @@
|
|||
const short JITTER_BUFFER_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE) * 1000 * 1000);
|
||||
|
||||
const char AUDIO_MIXER_LOGGING_TARGET_NAME[] = "audio-mixer";
|
||||
|
||||
void attachNewBufferToNode(Node *newNode) {
|
||||
|
@ -202,8 +200,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i];
|
||||
|
||||
if ((*otherNode != *node
|
||||
|| otherNodeBuffer->getType() != PositionalAudioRingBuffer::Microphone
|
||||
|| nodeRingBuffer->shouldLoopbackForNode())
|
||||
|| otherNodeBuffer->shouldLoopbackForNode())
|
||||
&& otherNodeBuffer->willBeAddedToMix()) {
|
||||
addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer);
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include "AvatarAudioRingBuffer.h"
|
||||
|
||||
AvatarAudioRingBuffer::AvatarAudioRingBuffer() :
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone),
|
||||
_shouldLoopbackForNode(false) {
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,10 @@ public:
|
|||
AvatarAudioRingBuffer();
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; }
|
||||
private:
|
||||
// disallow copying of AvatarAudioRingBuffer objects
|
||||
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);
|
||||
AvatarAudioRingBuffer& operator= (const AvatarAudioRingBuffer&);
|
||||
|
||||
bool _shouldLoopbackForNode;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AvatarAudioRingBuffer__) */
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <Logging.h>
|
||||
#include <OctalCode.h>
|
||||
|
@ -153,7 +154,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
qDebug("[VERSION] Build sequence: %i", BUILD_VERSION);
|
||||
qDebug("[VERSION] Build sequence: %i\n", BUILD_VERSION);
|
||||
|
||||
unsigned int listenPort = 0; // bind to an ephemeral port by default
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
|
@ -1251,12 +1252,10 @@ void Application::sendPingPackets() {
|
|||
const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER,
|
||||
NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER};
|
||||
|
||||
uint64_t currentTime = usecTimestampNow();
|
||||
unsigned char pingPacket[numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_PING) + sizeof(currentTime)];
|
||||
int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
|
||||
|
||||
memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
getInstance()->controlledBroadcastToNodes(pingPacket, sizeof(pingPacket),
|
||||
unsigned char pingPacket[MAX_PACKET_SIZE];
|
||||
int length = NodeList::getInstance()->fillPingPacket(pingPacket);
|
||||
|
||||
getInstance()->controlledBroadcastToNodes(pingPacket, length,
|
||||
nodesToPing, sizeof(nodesToPing));
|
||||
}
|
||||
|
||||
|
@ -4239,15 +4238,15 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m
|
|||
const HifiSockAddr& senderSockAddr, bool wasStatsPacket) {
|
||||
|
||||
// Attempt to identify the sender from it's address.
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (voxelServer) {
|
||||
QUuid nodeUUID = voxelServer->getUUID();
|
||||
Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (serverNode) {
|
||||
QUuid nodeUUID = serverNode->getUUID();
|
||||
|
||||
// now that we know the node ID, let's add these stats to the stats for that node...
|
||||
_voxelSceneStatsLock.lockForWrite();
|
||||
if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {
|
||||
VoxelSceneStats& stats = _octreeServerSceneStats[nodeUUID];
|
||||
stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket);
|
||||
stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket, serverNode->getClockSkewUsec());
|
||||
}
|
||||
_voxelSceneStatsLock.unlock();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ static const int ICON_LEFT = 20;
|
|||
static const int BOTTOM_PADDING = 110;
|
||||
|
||||
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent) :
|
||||
QObject(parent),
|
||||
AbstractAudioInterface(parent),
|
||||
_audioInput(NULL),
|
||||
_desiredInputFormat(),
|
||||
_inputFormat(),
|
||||
|
@ -458,9 +458,37 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
int16_t ringBufferSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO];
|
||||
_ringBuffer.readSamples(ringBufferSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO);
|
||||
|
||||
// add the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL from each QByteArray
|
||||
// in our _localInjectionByteArrays QVector to the _localInjectedSamples
|
||||
|
||||
// add to the output samples whatever is in the _localAudioOutput byte array
|
||||
// that lets this user hear sound effects and loopback (if enabled)
|
||||
|
||||
for (int b = 0; b < _localInjectionByteArrays.size(); b++) {
|
||||
QByteArray audioByteArray = _localInjectionByteArrays.at(b);
|
||||
|
||||
int16_t* byteArraySamples = (int16_t*) audioByteArray.data();
|
||||
|
||||
int samplesToRead = MIN(audioByteArray.size() / sizeof(int16_t),
|
||||
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
for (int i = 0; i < samplesToRead; i++) {
|
||||
_localInjectedSamples[i] = glm::clamp(_localInjectedSamples[i] + byteArraySamples[i],
|
||||
MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
|
||||
}
|
||||
|
||||
if (samplesToRead < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
// there isn't anything left to inject from this byte array, remove it from the vector
|
||||
_localInjectionByteArrays.remove(b);
|
||||
} else {
|
||||
// pull out the bytes we just read for outputs
|
||||
audioByteArray.remove(0, samplesToRead * sizeof(int16_t));
|
||||
|
||||
// still data left to read - replace the byte array in the QVector with the smaller one
|
||||
_localInjectionByteArrays.replace(b, audioByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
ringBufferSamples[i * 2] = glm::clamp(ringBufferSamples[i * 2] + _localInjectedSamples[i],
|
||||
MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
|
||||
|
@ -696,6 +724,11 @@ void Audio::startDrumSound(float volume, float frequency, float duration, float
|
|||
_drumSoundSample = 0;
|
||||
}
|
||||
|
||||
void Audio::handleAudioByteArray(const QByteArray& audioByteArray) {
|
||||
// add this byte array to our QVector
|
||||
_localInjectionByteArrays.append(audioByteArray);
|
||||
}
|
||||
|
||||
void Audio::renderToolIcon(int screenHeight) {
|
||||
|
||||
_iconBounds = QRect(ICON_LEFT, screenHeight - BOTTOM_PADDING, ICON_SIZE, ICON_SIZE);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtMultimedia/QAudioFormat>
|
||||
|
||||
#include <AbstractAudioInterface.h>
|
||||
|
@ -31,7 +32,7 @@ class QAudioInput;
|
|||
class QAudioOutput;
|
||||
class QIODevice;
|
||||
|
||||
class Audio : public QObject, public AbstractAudioInterface {
|
||||
class Audio : public AbstractAudioInterface {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// setup for audio I/O
|
||||
|
@ -51,7 +52,7 @@ public:
|
|||
|
||||
virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen);
|
||||
virtual void startDrumSound(float volume, float frequency, float duration, float decay);
|
||||
|
||||
|
||||
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }
|
||||
|
||||
bool getCollisionFlashesScreen() { return _collisionFlashesScreen; }
|
||||
|
@ -65,14 +66,17 @@ public slots:
|
|||
void handleAudioInput();
|
||||
void reset();
|
||||
|
||||
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
|
||||
|
||||
private:
|
||||
QByteArray firstInputFrame;
|
||||
QAudioInput* _audioInput;
|
||||
QAudioFormat _desiredInputFormat;
|
||||
QAudioFormat _inputFormat;
|
||||
QIODevice* _inputDevice;
|
||||
int16_t _localInjectedSamples[NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL];
|
||||
int _numInputCallbackBytes;
|
||||
int16_t _localInjectedSamples[NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL];
|
||||
QVector<QByteArray> _localInjectionByteArrays;
|
||||
QAudioOutput* _audioOutput;
|
||||
QAudioFormat _desiredOutputFormat;
|
||||
QAudioFormat _outputFormat;
|
||||
|
|
|
@ -56,12 +56,12 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
|
||||
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) {
|
||||
Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) {
|
||||
|
||||
switch(packetData[0]) {
|
||||
case PACKET_TYPE_PARTICLE_DATA: {
|
||||
app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr);
|
||||
app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr, serverNode);
|
||||
} break;
|
||||
|
||||
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
||||
|
@ -69,7 +69,7 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
} break;
|
||||
|
||||
default : {
|
||||
app->_voxels.setDataSourceUUID(voxelServer->getUUID());
|
||||
app->_voxels.setDataSourceUUID(serverNode->getUUID());
|
||||
app->_voxels.parseData(packetData, messageLength);
|
||||
app->_voxels.setDataSourceUUID(QUuid());
|
||||
} break;
|
||||
|
|
|
@ -34,16 +34,16 @@ VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model
|
|||
_labels[i] = NULL;
|
||||
}
|
||||
|
||||
this->setWindowTitle("Voxel Statistics");
|
||||
this->setWindowTitle("Octree Server Statistics");
|
||||
|
||||
// Create layouter
|
||||
_form = new QFormLayout();
|
||||
this->QDialog::setLayout(_form);
|
||||
|
||||
// Setup stat items
|
||||
_serverVoxels = AddStatItem("Voxels on Servers");
|
||||
_localVoxels = AddStatItem("Local Voxels");
|
||||
_localVoxelsMemory = AddStatItem("Voxels Memory");
|
||||
_serverVoxels = AddStatItem("Elements on Servers");
|
||||
_localVoxels = AddStatItem("Local Elements");
|
||||
_localVoxelsMemory = AddStatItem("Elements Memory");
|
||||
_voxelsRendered = AddStatItem("Voxels Rendered");
|
||||
_sendingMode = AddStatItem("Sending Mode");
|
||||
|
||||
|
@ -136,7 +136,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
|
|||
label = _labels[_localVoxelsMemory];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Nodes RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
|
||||
"Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
|
||||
"Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
|
||||
"VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
|
||||
if (voxels->hasVoxelMemoryUsageGPU()) {
|
||||
|
@ -344,15 +344,25 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NODE_TYPE serve
|
|||
QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes());
|
||||
QString incomingOutOfOrderString = locale.toString((uint)stats.getIncomingOutOfOrder());
|
||||
QString incomingLikelyLostString = locale.toString((uint)stats.getIncomingLikelyLost());
|
||||
QString incomingFlightTimeString = locale.toString(stats.getIncomingFlightTimeAverage());
|
||||
|
||||
int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC;
|
||||
QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage());
|
||||
QString incomingPingTimeString = locale.toString(node->getPingMs());
|
||||
QString incomingClockSkewString = locale.toString(clockSkewInMS);
|
||||
|
||||
serverDetails << "<br/>" << "Incoming Packets: " <<
|
||||
incomingPacketsString.toLocal8Bit().constData() <<
|
||||
" Out of Order: " << incomingOutOfOrderString.toLocal8Bit().constData() <<
|
||||
" Likely Lost: " << incomingLikelyLostString.toLocal8Bit().constData();
|
||||
|
||||
serverDetails << "<br/>" <<
|
||||
serverDetails << "<br/>" <<
|
||||
" Average Flight Time: " << incomingFlightTimeString.toLocal8Bit().constData() << " msecs";
|
||||
|
||||
serverDetails << "<br/>" <<
|
||||
" Average Ping Time: " << incomingPingTimeString.toLocal8Bit().constData() << " msecs";
|
||||
|
||||
serverDetails << "<br/>" <<
|
||||
" Average Clock Skew: " << incomingClockSkewString.toLocal8Bit().constData() << " msecs";
|
||||
|
||||
serverDetails << "<br/>" << "Incoming" <<
|
||||
" Bytes: " << incomingBytesString.toLocal8Bit().constData() <<
|
||||
|
|
|
@ -10,10 +10,19 @@
|
|||
#ifndef __hifi__AbstractAudioInterface__
|
||||
#define __hifi__AbstractAudioInterface__
|
||||
|
||||
class AbstractAudioInterface {
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class AbstractAudioInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {};
|
||||
|
||||
virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen) = 0;
|
||||
virtual void startDrumSound(float volume, float frequency, float duration, float decay) = 0;
|
||||
public slots:
|
||||
virtual void handleAudioByteArray(const QByteArray& audioByteArray) = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AbstractAudioInterface*)
|
||||
|
||||
#endif /* defined(__hifi__AbstractAudioInterface__) */
|
162
libraries/audio/src/AudioInjector.cpp
Normal file
162
libraries/audio/src/AudioInjector.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// AudioInjector.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/19/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AbstractAudioInterface.h"
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
int abstractAudioPointerMeta = qRegisterMetaType<AbstractAudioInterface*>("AbstractAudioInterface*");
|
||||
|
||||
AudioInjector::AudioInjector(const QUrl& sampleURL) :
|
||||
_currentSendPosition(0),
|
||||
_sourceURL(sampleURL),
|
||||
_position(0,0,0),
|
||||
_orientation(),
|
||||
_volume(1.0f),
|
||||
_shouldLoopback(true)
|
||||
{
|
||||
// we want to live on our own thread
|
||||
moveToThread(&_thread);
|
||||
connect(&_thread, SIGNAL(started()), this, SLOT(startDownload()));
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
void AudioInjector::startDownload() {
|
||||
// assume we have a QApplication or QCoreApplication instance and use the
|
||||
// QNetworkAccess manager to grab the raw audio file at the given URL
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
||||
this, SLOT(replyFinished(QNetworkReply*)));
|
||||
|
||||
manager->get(QNetworkRequest(_sourceURL));
|
||||
}
|
||||
|
||||
void AudioInjector::replyFinished(QNetworkReply* reply) {
|
||||
// replace our samples array with the downloaded data
|
||||
_sampleByteArray = reply->readAll();
|
||||
}
|
||||
|
||||
void AudioInjector::injectViaThread(AbstractAudioInterface* localAudioInterface) {
|
||||
// use Qt::AutoConnection so that this is called on our thread, if appropriate
|
||||
QMetaObject::invokeMethod(this, "injectAudio", Qt::AutoConnection, Q_ARG(AbstractAudioInterface*, localAudioInterface));
|
||||
}
|
||||
|
||||
void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) {
|
||||
|
||||
// make sure we actually have samples downloaded to inject
|
||||
if (_sampleByteArray.size()) {
|
||||
// give our sample byte array to the local audio interface, if we have it, so it can be handled locally
|
||||
if (localAudioInterface) {
|
||||
// assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly
|
||||
QMetaObject::invokeMethod(localAudioInterface, "handleAudioByteArray",
|
||||
Qt::AutoConnection,
|
||||
Q_ARG(QByteArray, _sampleByteArray));
|
||||
|
||||
}
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// reset the current send position to the beginning
|
||||
_currentSendPosition = 0;
|
||||
|
||||
// setup the packet for injected audio
|
||||
unsigned char injectedAudioPacket[MAX_PACKET_SIZE];
|
||||
unsigned char* currentPacketPosition = injectedAudioPacket;
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(injectedAudioPacket, PACKET_TYPE_INJECT_AUDIO);
|
||||
currentPacketPosition += numBytesPacketHeader;
|
||||
|
||||
// pack the session UUID for this Node
|
||||
QByteArray rfcSessionUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122();
|
||||
memcpy(currentPacketPosition, rfcSessionUUID.constData(), rfcSessionUUID.size());
|
||||
currentPacketPosition += rfcSessionUUID.size();
|
||||
|
||||
// pick a random UUID to use for this stream
|
||||
QUuid randomStreamUUID;
|
||||
QByteArray rfcStreamUUID = randomStreamUUID.toRfc4122();
|
||||
memcpy(currentPacketPosition, rfcStreamUUID, rfcStreamUUID.size());
|
||||
currentPacketPosition += rfcStreamUUID.size();
|
||||
|
||||
// pack the flag for loopback
|
||||
memcpy(currentPacketPosition, &_shouldLoopback, sizeof(_shouldLoopback));
|
||||
currentPacketPosition += sizeof(_shouldLoopback);
|
||||
|
||||
// pack the position for injected audio
|
||||
memcpy(currentPacketPosition, &_position, sizeof(_position));
|
||||
currentPacketPosition += sizeof(_position);
|
||||
|
||||
// pack our orientation for injected audio
|
||||
memcpy(currentPacketPosition, &_orientation, sizeof(_orientation));
|
||||
currentPacketPosition += sizeof(_orientation);
|
||||
|
||||
// pack zero for radius
|
||||
float radius = 0;
|
||||
memcpy(currentPacketPosition, &radius, sizeof(radius));
|
||||
currentPacketPosition += sizeof(radius);
|
||||
|
||||
// pack 255 for attenuation byte
|
||||
uchar volume = MAX_INJECTOR_VOLUME * _volume;
|
||||
memcpy(currentPacketPosition, &volume, sizeof(volume));
|
||||
currentPacketPosition += sizeof(volume);
|
||||
|
||||
timeval startTime = {};
|
||||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
||||
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
|
||||
while (_currentSendPosition < _sampleByteArray.size()) {
|
||||
|
||||
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||
_sampleByteArray.size() - _currentSendPosition);
|
||||
|
||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
||||
memcpy(currentPacketPosition, _sampleByteArray.data() + _currentSendPosition,
|
||||
bytesToCopy);
|
||||
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
Node* audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
||||
|
||||
if (audioMixer && nodeList->getNodeActiveSocketOrPing(audioMixer)) {
|
||||
// send off this audio packet
|
||||
nodeList->getNodeSocket().writeDatagram((char*) injectedAudioPacket,
|
||||
(currentPacketPosition - injectedAudioPacket) + bytesToCopy,
|
||||
audioMixer->getActiveSocket()->getAddress(),
|
||||
audioMixer->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
_currentSendPosition += bytesToCopy;
|
||||
|
||||
// send two packets before the first sleep so the mixer can start playback right away
|
||||
|
||||
if (_currentSendPosition != bytesToCopy && _currentSendPosition < _sampleByteArray.size()) {
|
||||
// not the first packet and not done
|
||||
// sleep for the appropriate time
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
54
libraries/audio/src/AudioInjector.h
Normal file
54
libraries/audio/src/AudioInjector.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// AudioInjector.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/19/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AudioInjector__
|
||||
#define __hifi__AudioInjector__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
class AbstractAudioInterface;
|
||||
class QNetworkReply;
|
||||
|
||||
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
class AudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioInjector(const QUrl& sampleURL);
|
||||
|
||||
int size() const { return _sampleByteArray.size(); }
|
||||
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
void setVolume(float volume) { _volume = std::max(fabsf(volume), 1.0f); }
|
||||
void setShouldLoopback(bool shouldLoopback) { _shouldLoopback = shouldLoopback; }
|
||||
public slots:
|
||||
void injectViaThread(AbstractAudioInterface* localAudioInterface = NULL);
|
||||
|
||||
private:
|
||||
QByteArray _sampleByteArray;
|
||||
int _currentSendPosition;
|
||||
QThread _thread;
|
||||
QUrl _sourceURL;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _volume;
|
||||
uchar _shouldLoopback;
|
||||
|
||||
private slots:
|
||||
void startDownload();
|
||||
void replyFinished(QNetworkReply* reply);
|
||||
void injectAudio(AbstractAudioInterface* localAudioInterface);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioInjector__) */
|
|
@ -25,6 +25,9 @@ const int NETWORK_BUFFER_LENGTH_SAMPLES_STEREO = NETWORK_BUFFER_LENGTH_BYTES_STE
|
|||
const int NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL = 512;
|
||||
const int NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL = NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL / sizeof(int16_t);
|
||||
|
||||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
/ (float) SAMPLE_RATE) * 1000 * 1000);
|
||||
|
||||
const short RING_BUFFER_LENGTH_FRAMES = 10;
|
||||
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
|
|
|
@ -32,6 +32,12 @@ int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes
|
|||
// push past the UUID for this node and the stream identifier
|
||||
currentBuffer += (NUM_BYTES_RFC4122_UUID * 2);
|
||||
|
||||
// pull the loopback flag and set our boolean
|
||||
uchar shouldLoopback;
|
||||
memcpy(&shouldLoopback, currentBuffer, sizeof(shouldLoopback));
|
||||
currentBuffer += sizeof(shouldLoopback);
|
||||
_shouldLoopbackForNode = (shouldLoopback == 1);
|
||||
|
||||
// use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data
|
||||
currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
|
||||
|
||||
|
@ -42,6 +48,8 @@ int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes
|
|||
unsigned int attenuationByte = *(currentBuffer++);
|
||||
_attenuationRatio = attenuationByte / (float) MAX_INJECTOR_VOLUME;
|
||||
|
||||
qDebug() << "Copying" << numBytes - (currentBuffer - sourceBuffer) << "for injected ring buffer\n";
|
||||
|
||||
currentBuffer += writeData((char*) currentBuffer, numBytes - (currentBuffer - sourceBuffer));
|
||||
|
||||
return currentBuffer - sourceBuffer;
|
||||
|
|
|
@ -19,7 +19,8 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::
|
|||
_type(type),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_willBeAddedToMix(false)
|
||||
_willBeAddedToMix(false),
|
||||
_shouldLoopbackForNode(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
bool willBeAddedToMix() const { return _willBeAddedToMix; }
|
||||
void setWillBeAddedToMix(bool willBeAddedToMix) { _willBeAddedToMix = willBeAddedToMix; }
|
||||
|
||||
bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; }
|
||||
|
||||
PositionalAudioRingBuffer::Type getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
|
@ -46,6 +48,7 @@ protected:
|
|||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
bool _willBeAddedToMix;
|
||||
bool _shouldLoopbackForNode;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__PositionalAudioRingBuffer__) */
|
||||
|
|
|
@ -1342,7 +1342,7 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
fileOk = true; // assume the file is ok
|
||||
}
|
||||
if (fileOk) {
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, wantImportProgress);
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, NULL, wantImportProgress);
|
||||
readBitstreamToTree(dataAt, dataLength, args);
|
||||
}
|
||||
delete[] entireFile;
|
||||
|
@ -1481,7 +1481,7 @@ void Octree::copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinat
|
|||
|
||||
// ask destination tree to read the bitstream
|
||||
bool wantImportProgress = true;
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, 0, wantImportProgress);
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, 0, NULL, wantImportProgress);
|
||||
readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,6 +156,7 @@ public:
|
|||
bool includeExistsBits;
|
||||
OctreeElement* destinationNode;
|
||||
QUuid sourceUUID;
|
||||
Node* sourceNode;
|
||||
bool wantImportProgress;
|
||||
|
||||
ReadBitstreamToTreeParams(
|
||||
|
@ -163,11 +164,13 @@ public:
|
|||
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
OctreeElement* destinationNode = NULL,
|
||||
QUuid sourceUUID = QUuid(),
|
||||
Node* sourceNode = NULL,
|
||||
bool wantImportProgress = false) :
|
||||
includeColor(includeColor),
|
||||
includeExistsBits(includeExistsBits),
|
||||
destinationNode(destinationNode),
|
||||
sourceUUID(sourceUUID),
|
||||
sourceNode(sourceNode),
|
||||
wantImportProgress(wantImportProgress)
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -81,7 +81,7 @@ bool OctreeEditPacketSender::serversExist() const {
|
|||
}
|
||||
|
||||
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
||||
// a known nodeID. However, we also want to handle the case where the
|
||||
// a known nodeID.
|
||||
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
|
@ -243,6 +243,14 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned c
|
|||
initializePacket(packetBuffer, type);
|
||||
}
|
||||
|
||||
// This is really the first time we know which server/node this particular edit message
|
||||
// is going to, so we couldn't adjust for clock skew till now. But here's our chance.
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (node->getClockSkewUsec() != 0) {
|
||||
adjustEditPacketForClockSkew(codeColorBuffer, length, node->getClockSkewUsec());
|
||||
}
|
||||
|
||||
memcpy(&packetBuffer._currentBuffer[packetBuffer._currentSize], codeColorBuffer, length);
|
||||
packetBuffer._currentSize += length;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ public:
|
|||
|
||||
// you must override these...
|
||||
virtual unsigned char getMyNodeType() const = 0;
|
||||
virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { };
|
||||
|
||||
protected:
|
||||
bool _shouldSend;
|
||||
|
|
|
@ -26,7 +26,7 @@ void OctreeRenderer::init() {
|
|||
OctreeRenderer::~OctreeRenderer() {
|
||||
}
|
||||
|
||||
void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode) {
|
||||
bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
bool extraDebugging = false; // Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)
|
||||
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails);
|
||||
|
@ -57,7 +57,8 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
|
|||
bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||
|
||||
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
|
||||
int flightTime = arrivedAt - sentAt;
|
||||
int clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0;
|
||||
int flightTime = arrivedAt - sentAt + clockSkew;
|
||||
|
||||
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0;
|
||||
int dataBytes = packetLength - OCTREE_PACKET_HEADER_SIZE;
|
||||
|
@ -88,7 +89,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
|
|||
if (sectionLength) {
|
||||
// ask the VoxelTree to read the bitstream into the tree
|
||||
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
|
||||
getDataSourceUUID());
|
||||
getDataSourceUUID(), sourceNode);
|
||||
_tree->lockForWrite();
|
||||
OctreePacketData packetData(packetIsCompressed);
|
||||
packetData.loadFinalizedContent(dataAt, sectionLength);
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0;
|
||||
|
||||
/// process incoming data
|
||||
virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
/// initialize and GPU/rendering related resources
|
||||
void init();
|
||||
|
|
|
@ -798,7 +798,8 @@ const char* OctreeSceneStats::getItemValue(Item item) {
|
|||
return _itemValueBuffer;
|
||||
}
|
||||
|
||||
void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, bool wasStatsPacket) {
|
||||
void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength,
|
||||
bool wasStatsPacket, int nodeClockSkewUsec) {
|
||||
_incomingPacket++;
|
||||
_incomingBytes += messageLength;
|
||||
if (!wasStatsPacket) {
|
||||
|
@ -820,7 +821,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssi
|
|||
//bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||
|
||||
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
|
||||
int flightTime = arrivedAt - sentAt;
|
||||
int flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
|
||||
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
|
||||
|
|
|
@ -153,7 +153,8 @@ public:
|
|||
unsigned long getLastFullElapsedTime() const { return _lastFullElapsed; }
|
||||
|
||||
// Used in client implementations to track individual octree packets
|
||||
void trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, bool wasStatsPacket);
|
||||
void trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength,
|
||||
bool wasStatsPacket, int nodeClockSkewUsec);
|
||||
|
||||
unsigned int getIncomingPackets() const { return _incomingPacket; }
|
||||
unsigned long getIncomingBytes() const { return _incomingBytes; }
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h> // usecTimestampNow()
|
||||
#include <Octree.h>
|
||||
|
||||
#include "Particle.h"
|
||||
|
||||
|
@ -136,6 +137,8 @@ int Particle::expectedEditMessageBytes() {
|
|||
int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||
int bytesRead = 0;
|
||||
if (bytesLeftToRead >= expectedBytes()) {
|
||||
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
||||
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
// id
|
||||
|
@ -154,11 +157,13 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated));
|
||||
dataAt += sizeof(_lastUpdated);
|
||||
bytesRead += sizeof(_lastUpdated);
|
||||
_lastUpdated -= clockSkew;
|
||||
|
||||
// _lastEdited
|
||||
memcpy(&_lastEdited, dataAt, sizeof(_lastEdited));
|
||||
dataAt += sizeof(_lastEdited);
|
||||
bytesRead += sizeof(_lastEdited);
|
||||
_lastEdited -= clockSkew;
|
||||
|
||||
// radius
|
||||
memcpy(&_radius, dataAt, sizeof(_radius));
|
||||
|
@ -357,7 +362,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
sizeOut += sizeof(details[i].creatorTokenID);
|
||||
}
|
||||
|
||||
// radius
|
||||
// lastEdited
|
||||
memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited));
|
||||
copyAt += sizeof(details[i].lastEdited);
|
||||
sizeOut += sizeof(details[i].lastEdited);
|
||||
|
@ -420,6 +425,38 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
return success;
|
||||
}
|
||||
|
||||
// adjust any internal timestamps to fix clock skew for this server
|
||||
void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
|
||||
unsigned char* dataAt = codeColorBuffer;
|
||||
int octets = numberOfThreeBitSectionsInCode(dataAt);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
dataAt += lengthOfOctcode;
|
||||
|
||||
// id
|
||||
uint32_t id;
|
||||
memcpy(&id, dataAt, sizeof(id));
|
||||
dataAt += sizeof(id);
|
||||
// special case for handling "new" particles
|
||||
if (id == NEW_PARTICLE) {
|
||||
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
||||
// we want to send back to the creator as an map to the actual id
|
||||
dataAt += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
// lastEdited
|
||||
uint64_t lastEditedInLocalTime;
|
||||
memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
|
||||
uint64_t lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
|
||||
memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug("Particle::adjustEditPacketForClockSkew()...\n");
|
||||
qDebug(" lastEditedInLocalTime: %llu\n", lastEditedInLocalTime);
|
||||
qDebug(" clockSkew: %d\n", clockSkew);
|
||||
qDebug(" lastEditedInServerTime: %llu\n", lastEditedInServerTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Particle::update() {
|
||||
|
||||
|
|
|
@ -106,6 +106,8 @@ public:
|
|||
|
||||
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut);
|
||||
|
||||
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
|
||||
|
||||
void update();
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, const P
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
|
||||
Particle::adjustEditPacketForClockSkew(codeColorBuffer, length, clockSkew);
|
||||
}
|
||||
|
||||
|
||||
void ParticleEditPacketSender::queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details) {
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
|
|
|
@ -28,7 +28,8 @@ public:
|
|||
/// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
|
||||
void queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details);
|
||||
|
||||
// My server type is the voxel server
|
||||
// My server type is the particle server
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
|
||||
};
|
||||
#endif // __shared__ParticleEditPacketSender__
|
||||
|
|
|
@ -47,7 +47,7 @@ bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraDat
|
|||
return true;
|
||||
}
|
||||
|
||||
void ParticleTree::storeParticle(const Particle& particle) {
|
||||
void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) {
|
||||
// First, look for the existing particle in the tree..
|
||||
FindAndUpdateParticleArgs args = { particle, false };
|
||||
recurseTreeWithOperation(findAndUpdateOperation, &args);
|
||||
|
@ -58,7 +58,7 @@ void ParticleTree::storeParticle(const Particle& particle) {
|
|||
float size = particle.getRadius();
|
||||
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
|
||||
|
||||
element->storeParticle(particle);
|
||||
element->storeParticle(particle, senderNode);
|
||||
}
|
||||
// what else do we need to do here to get reaveraging to work
|
||||
_isDirty = true;
|
||||
|
@ -165,7 +165,7 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
|
|||
switch (packetType) {
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes);
|
||||
storeParticle(newParticle);
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
|
||||
virtual void update();
|
||||
|
||||
void storeParticle(const Particle& particle);
|
||||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
|
||||
const Particle* findParticleByID(uint32_t id);
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ bool ParticleTreeElement::collapseChildren() {
|
|||
}
|
||||
|
||||
|
||||
void ParticleTreeElement::storeParticle(const Particle& particle) {
|
||||
void ParticleTreeElement::storeParticle(const Particle& particle, Node* senderNode) {
|
||||
_particles.push_back(particle);
|
||||
markWithChangedTime();
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
void storeParticle(const Particle& particle);
|
||||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
|
||||
ParticleTree* _myTree;
|
||||
std::vector<Particle> _particles;
|
||||
|
|
|
@ -32,7 +32,8 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const
|
|||
_activeSocket(NULL),
|
||||
_bytesReceivedMovingAverage(NULL),
|
||||
_linkedData(NULL),
|
||||
_isAlive(true)
|
||||
_isAlive(true),
|
||||
_clockSkewUsec(0)
|
||||
{
|
||||
pthread_mutex_init(&_mutex, 0);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,9 @@ public:
|
|||
|
||||
int getPingMs() const { return _pingMs; }
|
||||
void setPingMs(int pingMs) { _pingMs = pingMs; }
|
||||
|
||||
int getClockSkewUsec() const { return _clockSkewUsec; }
|
||||
void setClockSkewUsec(int clockSkew) { _clockSkewUsec = clockSkew; }
|
||||
|
||||
void lock() { pthread_mutex_lock(&_mutex); }
|
||||
|
||||
|
@ -93,6 +96,7 @@ private:
|
|||
NodeData* _linkedData;
|
||||
bool _isAlive;
|
||||
int _pingMs;
|
||||
int _clockSkewUsec;
|
||||
pthread_mutex_t _mutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -112,9 +112,33 @@ void NodeList::timePingReply(const HifiSockAddr& nodeAddress, unsigned char *pac
|
|||
if (node->getPublicSocket() == nodeAddress ||
|
||||
node->getLocalSocket() == nodeAddress) {
|
||||
|
||||
int pingTime = usecTimestampNow() - *(uint64_t*)(packetData + numBytesForPacketHeader(packetData));
|
||||
unsigned char* dataAt = packetData + numBytesForPacketHeader(packetData);
|
||||
uint64_t ourOriginalTime = *(uint64_t*)(dataAt);
|
||||
dataAt += sizeof(ourOriginalTime);
|
||||
uint64_t othersReplyTime = *(uint64_t*)(dataAt);
|
||||
uint64_t now = usecTimestampNow();
|
||||
int pingTime = now - ourOriginalTime;
|
||||
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
||||
|
||||
// The other node's expected time should be our original time plus the one way flight time
|
||||
// anything other than that is clock skew
|
||||
uint64_t othersExprectedReply = ourOriginalTime + oneWayFlightTime;
|
||||
int clockSkew = othersReplyTime - othersExprectedReply;
|
||||
|
||||
node->setPingMs(pingTime / 1000);
|
||||
node->setClockSkewUsec(clockSkew);
|
||||
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "PING_REPLY from node " << *node << "\n" <<
|
||||
" now: " << now << "\n" <<
|
||||
" ourTime: " << ourOriginalTime << "\n" <<
|
||||
" pingTime: " << pingTime << "\n" <<
|
||||
" oneWayFlightTime: " << oneWayFlightTime << "\n" <<
|
||||
" othersReplyTime: " << othersReplyTime << "\n" <<
|
||||
" othersExprectedReply: " << othersExprectedReply << "\n" <<
|
||||
" clockSkew: " << clockSkew << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -131,9 +155,11 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char
|
|||
break;
|
||||
}
|
||||
case PACKET_TYPE_PING: {
|
||||
// send it right back
|
||||
populateTypeAndVersion(packetData, PACKET_TYPE_PING_REPLY);
|
||||
_nodeSocket.writeDatagram((char*) packetData, dataBytes, senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
// send back a reply
|
||||
unsigned char replyPacket[MAX_PACKET_SIZE];
|
||||
int replyPacketLength = fillPingReplyPacket(packetData, replyPacket);
|
||||
_nodeSocket.writeDatagram((char*)replyPacket, replyPacketLength,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_PING_REPLY: {
|
||||
|
@ -616,21 +642,42 @@ void NodeList::sendAssignment(Assignment& assignment) {
|
|||
assignmentServerSocket->getPort());
|
||||
}
|
||||
|
||||
int NodeList::fillPingPacket(unsigned char* buffer) {
|
||||
int numHeaderBytes = populateTypeAndVersion(buffer, PACKET_TYPE_PING);
|
||||
uint64_t currentTime = usecTimestampNow();
|
||||
memcpy(buffer + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
return numHeaderBytes + sizeof(currentTime);
|
||||
}
|
||||
|
||||
int NodeList::fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer) {
|
||||
int numHeaderBytesOriginal = numBytesForPacketHeader(pingBuffer);
|
||||
uint64_t timeFromOriginalPing = *(uint64_t*)(pingBuffer + numHeaderBytesOriginal);
|
||||
|
||||
int numHeaderBytesReply = populateTypeAndVersion(replyBuffer, PACKET_TYPE_PING_REPLY);
|
||||
int length = numHeaderBytesReply;
|
||||
uint64_t ourReplyTime = usecTimestampNow();
|
||||
|
||||
unsigned char* dataAt = replyBuffer + numHeaderBytesReply;
|
||||
memcpy(dataAt, &timeFromOriginalPing, sizeof(timeFromOriginalPing));
|
||||
dataAt += sizeof(timeFromOriginalPing);
|
||||
length += sizeof(timeFromOriginalPing);
|
||||
|
||||
memcpy(dataAt, &ourReplyTime, sizeof(ourReplyTime));
|
||||
dataAt += sizeof(ourReplyTime);
|
||||
length += sizeof(ourReplyTime);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) {
|
||||
|
||||
uint64_t currentTime = 0;
|
||||
|
||||
// setup a ping packet to send to this node
|
||||
unsigned char pingPacket[numBytesForPacketHeader((uchar*) &PACKET_TYPE_PING) + sizeof(currentTime)];
|
||||
int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
|
||||
|
||||
currentTime = usecTimestampNow();
|
||||
memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
unsigned char pingPacket[MAX_PACKET_SIZE];
|
||||
int pingPacketLength = fillPingPacket(pingPacket);
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
|
||||
node->getLocalSocket().getAddress(), node->getLocalSocket().getPort());
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
|
||||
node->getPublicSocket().getAddress(), node->getPublicSocket().getPort());
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,8 @@ public:
|
|||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
int fillPingPacket(unsigned char* buffer);
|
||||
int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
|
||||
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
|
||||
|
||||
void sendKillNode(const char* nodeTypes, int numNodeTypes);
|
||||
|
|
|
@ -18,7 +18,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO:
|
||||
case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO:
|
||||
return 2;
|
||||
|
||||
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
return 12;
|
||||
|
||||
|
@ -56,6 +56,9 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
return 5;
|
||||
|
||||
case PACKET_TYPE_PING_REPLY:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,9 @@ static const float METER = 1.0f;
|
|||
static const float DECIMETER = 0.1f;
|
||||
static const float CENTIMETER = 0.01f;
|
||||
static const float MILLIIMETER = 0.001f;
|
||||
static const uint64_t USECS_PER_SECOND = 1000 * 1000;
|
||||
static const uint64_t USECS_PER_MSEC = 1000;
|
||||
static const uint64_t MSECS_PER_SECOND = 1000;
|
||||
static const uint64_t USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
||||
|
||||
uint64_t usecTimestamp(const timeval *time);
|
||||
uint64_t usecTimestampNow();
|
||||
|
|
Loading…
Reference in a new issue