mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 07:51:01 +02:00
Merge pull request #2292 from birarda/agent-audio
add sendAvatarAudioStream option to Agent
This commit is contained in:
commit
e9b622361a
4 changed files with 64 additions and 13 deletions
|
@ -13,6 +13,7 @@
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
|
||||||
|
#include <AudioRingBuffer.h>
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
@ -25,7 +26,8 @@
|
||||||
Agent::Agent(const QByteArray& packet) :
|
Agent::Agent(const QByteArray& packet) :
|
||||||
ThreadedAssignment(packet),
|
ThreadedAssignment(packet),
|
||||||
_voxelEditSender(),
|
_voxelEditSender(),
|
||||||
_particleEditSender()
|
_particleEditSender(),
|
||||||
|
_avatarAudioStream(NULL)
|
||||||
{
|
{
|
||||||
// be the parent of the script engine so it gets moved when we do
|
// be the parent of the script engine so it gets moved when we do
|
||||||
_scriptEngine.setParent(this);
|
_scriptEngine.setParent(this);
|
||||||
|
@ -34,6 +36,30 @@ Agent::Agent(const QByteArray& packet) :
|
||||||
_scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
_scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Agent::~Agent() {
|
||||||
|
delete _avatarAudioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5);
|
||||||
|
|
||||||
|
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() {
|
void Agent::readPendingDatagrams() {
|
||||||
QByteArray receivedPacket;
|
QByteArray receivedPacket;
|
||||||
HifiSockAddr senderSockAddr;
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
|
@ -28,12 +28,17 @@ class Agent : public ThreadedAssignment {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||||
|
Q_PROPERTY(bool sendAvatarAudioStream READ isSendingAvatarAudioStream WRITE setSendAvatarAudioStream)
|
||||||
public:
|
public:
|
||||||
Agent(const QByteArray& packet);
|
Agent(const QByteArray& packet);
|
||||||
|
~Agent();
|
||||||
|
|
||||||
void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); }
|
void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); }
|
||||||
bool isAvatar() const { return _scriptEngine.isAvatar(); }
|
bool isAvatar() const { return _scriptEngine.isAvatar(); }
|
||||||
|
|
||||||
|
void setSendAvatarAudioStream(bool sendAvatarAudioStream);
|
||||||
|
bool isSendingAvatarAudioStream() const { return (bool) _scriptEngine.sendsAvatarAudioStream(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void run();
|
void run();
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
|
@ -45,6 +50,8 @@ private:
|
||||||
|
|
||||||
ParticleTreeHeadlessViewer _particleViewer;
|
ParticleTreeHeadlessViewer _particleViewer;
|
||||||
VoxelTreeHeadlessViewer _voxelViewer;
|
VoxelTreeHeadlessViewer _voxelViewer;
|
||||||
|
|
||||||
|
int16_t* _avatarAudioStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Agent__) */
|
#endif /* defined(__hifi__Agent__) */
|
||||||
|
|
|
@ -28,8 +28,6 @@
|
||||||
#include "LocalVoxels.h"
|
#include "LocalVoxels.h"
|
||||||
#include "ScriptEngine.h"
|
#include "ScriptEngine.h"
|
||||||
|
|
||||||
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
|
|
||||||
|
|
||||||
int ScriptEngine::_scriptNumber = 1;
|
int ScriptEngine::_scriptNumber = 1;
|
||||||
VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface;
|
VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface;
|
||||||
ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface;
|
ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface;
|
||||||
|
@ -54,6 +52,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co
|
||||||
_avatarIdentityTimer(NULL),
|
_avatarIdentityTimer(NULL),
|
||||||
_avatarBillboardTimer(NULL),
|
_avatarBillboardTimer(NULL),
|
||||||
_timerFunctionMap(),
|
_timerFunctionMap(),
|
||||||
|
_avatarAudioBuffer(NULL),
|
||||||
_controllerScriptingInterface(controllerScriptingInterface),
|
_controllerScriptingInterface(controllerScriptingInterface),
|
||||||
_avatarData(NULL),
|
_avatarData(NULL),
|
||||||
_wantMenuItems(wantMenuItems),
|
_wantMenuItems(wantMenuItems),
|
||||||
|
@ -77,9 +76,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co
|
||||||
_scriptNumber++;
|
_scriptNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine::~ScriptEngine() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptEngine::setIsAvatar(bool isAvatar) {
|
void ScriptEngine::setIsAvatar(bool isAvatar) {
|
||||||
_isAvatar = isAvatar;
|
_isAvatar = isAvatar;
|
||||||
|
|
||||||
|
@ -169,8 +165,8 @@ void ScriptEngine::init() {
|
||||||
_engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
_engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||||
|
|
||||||
// let the VoxelPacketSender know how frequently we plan to call it
|
// let the VoxelPacketSender know how frequently we plan to call it
|
||||||
_voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
_voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS);
|
||||||
_particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
_particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +224,7 @@ void ScriptEngine::run() {
|
||||||
qint64 lastUpdate = usecTimestampNow();
|
qint64 lastUpdate = usecTimestampNow();
|
||||||
|
|
||||||
while (!_isFinished) {
|
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) {
|
if (usecToSleep > 0) {
|
||||||
usleep(usecToSleep);
|
usleep(usecToSleep);
|
||||||
}
|
}
|
||||||
|
@ -268,6 +264,21 @@ void ScriptEngine::run() {
|
||||||
avatarPacket.append(_avatarData->toByteArray());
|
avatarPacket.append(_avatarData->toByteArray());
|
||||||
|
|
||||||
nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer);
|
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<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3));
|
||||||
|
glm::quat headOrientation = _avatarData->getHeadOrientation();
|
||||||
|
packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat));
|
||||||
|
packetStream.writeRawData(reinterpret_cast<const char*>(_avatarAudioBuffer),
|
||||||
|
_numAvatarAudioBufferSamples * sizeof(int16_t));
|
||||||
|
|
||||||
|
nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 now = usecTimestampNow();
|
qint64 now = usecTimestampNow();
|
||||||
|
|
|
@ -20,14 +20,16 @@
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
|
|
||||||
class ParticlesScriptingInterface;
|
|
||||||
|
|
||||||
#include "AbstractControllerScriptingInterface.h"
|
#include "AbstractControllerScriptingInterface.h"
|
||||||
#include "Quat.h"
|
#include "Quat.h"
|
||||||
#include "Vec3.h"
|
#include "Vec3.h"
|
||||||
|
|
||||||
|
class ParticlesScriptingInterface;
|
||||||
|
|
||||||
const QString NO_SCRIPT("");
|
const QString NO_SCRIPT("");
|
||||||
|
|
||||||
|
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5);
|
||||||
|
|
||||||
class ScriptEngine : public QObject {
|
class ScriptEngine : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -35,8 +37,6 @@ public:
|
||||||
const QString& scriptMenuName = QString(""),
|
const QString& scriptMenuName = QString(""),
|
||||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||||
|
|
||||||
~ScriptEngine();
|
|
||||||
|
|
||||||
/// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
/// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||||
static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
|
static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
|
||||||
|
|
||||||
|
@ -56,6 +56,11 @@ public:
|
||||||
|
|
||||||
void setAvatarData(AvatarData* avatarData, const QString& objectName);
|
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 init();
|
||||||
void run(); /// runs continuously until Agent.stop() is called
|
void run(); /// runs continuously until Agent.stop() is called
|
||||||
void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller
|
void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller
|
||||||
|
@ -86,6 +91,8 @@ protected:
|
||||||
QTimer* _avatarIdentityTimer;
|
QTimer* _avatarIdentityTimer;
|
||||||
QTimer* _avatarBillboardTimer;
|
QTimer* _avatarBillboardTimer;
|
||||||
QHash<QTimer*, QScriptValue> _timerFunctionMap;
|
QHash<QTimer*, QScriptValue> _timerFunctionMap;
|
||||||
|
int16_t* _avatarAudioBuffer;
|
||||||
|
int _numAvatarAudioBufferSamples;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendAvatarIdentityPacket();
|
void sendAvatarIdentityPacket();
|
||||||
|
|
Loading…
Reference in a new issue