Merge pull request #2292 from birarda/agent-audio

add sendAvatarAudioStream option to Agent
This commit is contained in:
Philip Rosedale 2014-03-12 18:05:10 -07:00
commit e9b622361a
4 changed files with 64 additions and 13 deletions

View file

@ -13,6 +13,7 @@
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <AudioRingBuffer.h>
#include <AvatarData.h>
#include <NodeList.h>
#include <PacketHeaders.h>
@ -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 = 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() {
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;

View file

@ -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__) */

View file

@ -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<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();

View file

@ -20,14 +20,16 @@
#include <AvatarData.h>
class ParticlesScriptingInterface;
#include "AbstractControllerScriptingInterface.h"
#include "Quat.h"
#include "Vec3.h"
class ParticlesScriptingInterface;
const QString NO_SCRIPT("");
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5);
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<QTimer*, QScriptValue> _timerFunctionMap;
int16_t* _avatarAudioBuffer;
int _numAvatarAudioBufferSamples;
private:
void sendAvatarIdentityPacket();