mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 01:13:11 +02:00
allow audio injection from javascript
This commit is contained in:
parent
515e2d5a23
commit
0eae0f129c
5 changed files with 45 additions and 70 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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__) */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue