allow audio injection from javascript

This commit is contained in:
Stephen Birarda 2013-10-11 17:37:48 -07:00
parent 515e2d5a23
commit 0eae0f129c
5 changed files with 45 additions and 70 deletions

View file

@ -45,15 +45,6 @@ void Agent::setStaticInstance(Agent* staticInstance) {
_staticInstance = staticInstance; _staticInstance = staticInstance;
} }
QScriptValue Agent::AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine) {
AudioInjector* injector = new AudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
// add this injector to the vector of audio injectors so we know we have to tell it to send its audio during loop
_staticInstance->_audioInjectors.push_back(injector);
return engine->newQObject(injector, QScriptEngine::ScriptOwnership);
}
QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) { QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
obj.setProperty("x", vec3.x); obj.setProperty("x", vec3.x);
@ -126,19 +117,14 @@ void Agent::run() {
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); 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 // 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 // hook in a constructor for audio injectorss
QScriptValue audioInjectorConstructor = engine.newFunction(AudioInjectorConstructor); AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
QScriptValue audioMetaObject = engine.newQMetaObject(&AudioInjector::staticMetaObject, audioInjectorConstructor); QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector);
engine.globalObject().setProperty("AudioInjector", audioMetaObject); engine.globalObject().setProperty("AudioInjector", audioInjectorValue);
qDebug() << "Downloaded script:" << scriptContents << "\n"; qDebug() << "Downloaded script:" << scriptContents << "\n";
QScriptValue result = engine.evaluate(scriptContents); QScriptValue result = engine.evaluate(scriptContents);
@ -149,19 +135,20 @@ void Agent::run() {
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
} }
timeval thisSend; timeval startTime;
gettimeofday(&startTime, NULL);
timeval lastDomainServerCheckIn = {}; timeval lastDomainServerCheckIn = {};
int numMicrosecondsSleep = 0;
sockaddr_in senderAddress; sockaddr_in senderAddress;
unsigned char receivedData[MAX_PACKET_SIZE]; unsigned char receivedData[MAX_PACKET_SIZE];
ssize_t receivedBytes; ssize_t receivedBytes;
bool hasVoxelServer = false; int thisFrame = 0;
bool firstDomainCheckIn = false;
while (!_shouldStop) { 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 we're not hearing from the domain-server we should stop running
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
@ -174,38 +161,44 @@ void Agent::run() {
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn();
} }
if (!hasVoxelServer) { if (firstDomainCheckIn) {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // find the audio-mixer in the NodeList so we can inject audio at it
if (node->getType() == NODE_TYPE_VOXEL_SERVER) { Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
hasVoxelServer = true;
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 // allow the scripter's call back to setup visual data
emit willSendVisualDataCallback(); 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. // release the queue of edit voxel messages.
voxelScripter.getVoxelPacketSender()->releaseQueuedMessages(); voxelScripter.getVoxelPacketSender()->releaseQueuedMessages();
// since we're in non-threaded mode, call process so that the packets are sent // since we're in non-threaded mode, call process so that the packets are sent
voxelScripter.getVoxelPacketSender()->process(); 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)) { 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); 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 { } else {

View file

@ -27,6 +27,7 @@ public:
public slots: public slots:
void stop(); void stop();
signals: signals:
void willSendAudioDataCallback();
void willSendVisualDataCallback(); void willSendVisualDataCallback();
private: private:
static void setStaticInstance(Agent* staticInstance); static void setStaticInstance(Agent* staticInstance);

View file

@ -21,8 +21,7 @@ AudioInjector::AudioInjector(const char* filename) :
_radius(0.0f), _radius(0.0f),
_volume(MAX_INJECTOR_VOLUME), _volume(MAX_INJECTOR_VOLUME),
_indexOfNextSlot(0), _indexOfNextSlot(0),
_isInjectingAudio(false), _isInjectingAudio(false)
_lastFrameIntensity(0.0f)
{ {
loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES);
@ -52,8 +51,7 @@ AudioInjector::AudioInjector(int maxNumSamples) :
_radius(0.0f), _radius(0.0f),
_volume(MAX_INJECTOR_VOLUME), _volume(MAX_INJECTOR_VOLUME),
_indexOfNextSlot(0), _indexOfNextSlot(0),
_isInjectingAudio(false), _isInjectingAudio(false)
_lastFrameIntensity(0.0f)
{ {
loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES);
@ -103,6 +101,11 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
int nextFrame = 0; int nextFrame = 0;
for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { 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; int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
if (_numTotalSamples - i < 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)); memcpy(currentPacketPtr, _audioSampleArray + i, numSamplesToCopy * sizeof(int16_t));
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket)); 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<int16_t>::max();
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
} }
_isInjectingAudio = false; _isInjectingAudio = false;
@ -159,8 +145,8 @@ int16_t& AudioInjector::sampleAt(const int index) {
return _audioSampleArray[index]; return _audioSampleArray[index];
} }
void AudioInjector::insertSample(const int index, int16_t sample) { void AudioInjector::insertSample(const int index, int sample) {
assert (index >= 0 && index < _numTotalSamples); assert (index >= 0 && index < _numTotalSamples);
_audioSampleArray[index] = sample; _audioSampleArray[index] = (int16_t) sample;
} }

View file

@ -42,8 +42,6 @@ public:
unsigned char getVolume() const { return _volume; } unsigned char getVolume() const { return _volume; }
void setVolume(unsigned char volume) { _volume = volume; } void setVolume(unsigned char volume) { _volume = volume; }
float getLastFrameIntensity() const { return _lastFrameIntensity; }
const glm::vec3& getPosition() const { return _position; } const glm::vec3& getPosition() const { return _position; }
void setPosition(const glm::vec3& position) { _position = position; } void setPosition(const glm::vec3& position) { _position = position; }
@ -57,7 +55,7 @@ public:
void addSamples(int16_t* sampleBuffer, int numSamples); void addSamples(int16_t* sampleBuffer, int numSamples);
public slots: public slots:
int16_t& sampleAt(const int index); int16_t& sampleAt(const int index);
void insertSample(const int index, int16_t sample); void insertSample(const int index, int sample);
private: private:
unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES]; unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES];
int16_t* _audioSampleArray; int16_t* _audioSampleArray;
@ -68,7 +66,6 @@ private:
unsigned char _volume; unsigned char _volume;
int _indexOfNextSlot; int _indexOfNextSlot;
bool _isInjectingAudio; bool _isInjectingAudio;
float _lastFrameIntensity;
}; };
#endif /* defined(__hifi__AudioInjector__) */ #endif /* defined(__hifi__AudioInjector__) */

View file

@ -441,10 +441,8 @@ Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, c
if (publicSocket) { if (publicSocket) {
for (node = begin(); node != end(); node++) { for (node = begin(); node != end(); node++) {
qDebug() << "comparing to node with ID " << node->getNodeID() << "\n";
if (node->matches(publicSocket, localSocket, nodeType)) { if (node->matches(publicSocket, localSocket, nodeType)) {
// we already have this node, stop checking // we already have this node, stop checking
qDebug() << "Matched node to existing\n";
break; break;
} }
} }