From bdf8c4b9b206c233fe74d9113eab36d176882f06 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Dec 2013 15:17:03 -0800 Subject: [PATCH] implement threaded send for AudioInjector --- assignment-client/src/audio/AudioMixer.cpp | 2 - interface/src/Application.cpp | 4 +- libraries/audio/src/AudioInjector.cpp | 96 ++++++++++++++++++- libraries/audio/src/AudioInjector.h | 9 ++ libraries/audio/src/AudioRingBuffer.h | 3 + .../audio/src/InjectedAudioRingBuffer.cpp | 2 + 6 files changed, 112 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 19a592754a..c2c8e618bf 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -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) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 370d9cda4e..ae71f12fb5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1321,7 +1321,9 @@ void Application::timer() { static AudioInjector testInjector(QUrl("https://dl.dropboxusercontent.com/u/1864924/throw.raw")); if (testInjector.size()) { - testInjector.injectViaThread(&_audio); + testInjector.setPosition(_myAvatar.getHead().getPosition()); + testInjector.setOrientation(_myAvatar.getOrientation()); + testInjector.injectViaThread(); } // give the MyAvatar object position to the Profile so it can propagate to the data-server diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 9aaa39c84d..606c7f37e6 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -6,18 +6,29 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + #include #include #include +#include +#include +#include +#include + #include "AbstractAudioInterface.h" +#include "AudioRingBuffer.h" #include "AudioInjector.h" int abstractAudioPointerMeta = qRegisterMetaType("AbstractAudioInterface*"); AudioInjector::AudioInjector(const QUrl& sampleURL) : - _sourceURL(sampleURL) + _currentSendPosition(0), + _sourceURL(sampleURL), + _position(0,0,0), + _orientation() { // we want to live on our own thread moveToThread(&_thread); @@ -58,5 +69,88 @@ void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) { 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 position for injected audio + memcpy(currentPacketPosition, &_position, sizeof(_position)); + currentPacketPosition += sizeof(_position); + + // pack a zero 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 = 1; + 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); + } + } + } + } } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index bbe8301a78..37413fda7a 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -13,6 +13,9 @@ #include #include +#include +#include + class AbstractAudioInterface; class QNetworkReply; @@ -22,13 +25,19 @@ 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; } public slots: void injectViaThread(AbstractAudioInterface* localAudioInterface = NULL); private: QByteArray _sampleByteArray; + int _currentSendPosition; QThread _thread; QUrl _sourceURL; + glm::vec3 _position; + glm::quat _orientation; private slots: void startDownload(); diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index addad13146..4860b47af2 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -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::max(); diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index d66a24672a..b089a20b83 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -42,6 +42,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;