switch bare pointers to AudioInjector objects to be QSharedPointers

This commit is contained in:
Seth Alves 2017-07-11 14:24:58 -07:00
parent 4a35ad105b
commit 3670a04d8e
13 changed files with 148 additions and 120 deletions

View file

@ -678,7 +678,7 @@ private:
QTimer _addAssetToWorldErrorTimer; QTimer _addAssetToWorldErrorTimer;
FileScriptingInterface* _fileDownload; FileScriptingInterface* _fileDownload;
AudioInjector* _snapshotSoundInjector { nullptr }; AudioInjectorPointer _snapshotSoundInjector;
SharedSoundPointer _snapshotSound; SharedSoundPointer _snapshotSound;
DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;

View file

@ -434,8 +434,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
// but most avatars are roughly the same size, so let's not be so fancy yet. // but most avatars are roughly the same size, so let's not be so fancy yet.
const float AVATAR_STRETCH_FACTOR = 1.0f; const float AVATAR_STRETCH_FACTOR = 1.0f;
_collisionInjectors.remove_if([](AudioInjectorPointer injector) {
_collisionInjectors.remove_if([](QPointer<AudioInjector>& injector) {
return !injector || injector->isFinished(); return !injector || injector->isFinished();
}); });

View file

@ -25,8 +25,8 @@
#include "AvatarMotionState.h" #include "AvatarMotionState.h"
#include "MyAvatar.h" #include "MyAvatar.h"
#include "AudioInjector.h"
class AudioInjector;
class AvatarManager : public AvatarHashMap { class AvatarManager : public AvatarHashMap {
Q_OBJECT Q_OBJECT
@ -104,7 +104,7 @@ private:
std::shared_ptr<MyAvatar> _myAvatar; std::shared_ptr<MyAvatar> _myAvatar;
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
std::list<QPointer<AudioInjector>> _collisionInjectors; std::list<AudioInjectorPointer> _collisionInjectors;
RateCounter<> _myAvatarSendRate; RateCounter<> _myAvatarSendRate;
int _numAvatarsUpdated { 0 }; int _numAvatarsUpdated { 0 };

View file

@ -210,9 +210,9 @@ AudioClient::AudioClient() :
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
this, &AudioClient::processReceivedSamples, Qt::DirectConnection); this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) {
qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
switchOutputToAudioDevice(outputDeviceInfo); switchOutputToAudioDevice(outputDeviceInfo);
}); });
connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
@ -261,10 +261,10 @@ void AudioClient::cleanupBeforeQuit() {
// so this must be explicitly, synchronously stopped // so this must be explicitly, synchronously stopped
static ConditionalGuard guard; static ConditionalGuard guard;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// This will likely be called from the main thread, but we don't want to do blocking queued calls // This will likely be called from the main thread, but we don't want to do blocking queued calls
// from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait // from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait
// for completion // for completion
// The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection // The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection
// in the code // in the code
QMetaObject::invokeMethod(this, "cleanupBeforeQuit"); QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
guard.wait(); guard.wait();
@ -630,7 +630,7 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessag
message->readPrimitive(&bitset); message->readPrimitive(&bitset);
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT); bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);
if (hasReverb) { if (hasReverb) {
float reverbTime, wetLevel; float reverbTime, wetLevel;
message->readPrimitive(&reverbTime); message->readPrimitive(&reverbTime);
@ -728,7 +728,7 @@ void AudioClient::Gate::flush() {
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) { void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
if (!_muted) { if (!_muted) {
toggleMute(); toggleMute();
// have the audio scripting interface emit a signal to say we were muted by the mixer // have the audio scripting interface emit a signal to say we were muted by the mixer
emit mutedByMixer(); emit mutedByMixer();
} }
@ -737,7 +737,7 @@ void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message)
void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message) { void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message) {
glm::vec3 position; glm::vec3 position;
float radius; float radius;
message->readPrimitive(&position); message->readPrimitive(&position);
message->readPrimitive(&radius); message->readPrimitive(&radius);
@ -770,7 +770,7 @@ void AudioClient::handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> mess
} }
void AudioClient::selectAudioFormat(const QString& selectedCodecName) { void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
_selectedCodecName = selectedCodecName; _selectedCodecName = selectedCodecName;
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName; qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName;
@ -787,7 +787,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
for (auto& plugin : codecPlugins) { for (auto& plugin : codecPlugins) {
if (_selectedCodecName == plugin->getName()) { if (_selectedCodecName == plugin->getName()) {
_codec = plugin; _codec = plugin;
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO); _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get(); qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
break; break;
@ -795,7 +795,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
} }
} }
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) { bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) {
auto device = deviceInfo; auto device = deviceInfo;
@ -1203,11 +1203,11 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
// lock the injectors // lock the injectors
Lock lock(_injectorsMutex); Lock lock(_injectorsMutex);
QVector<AudioInjector*> injectorsToRemove; QVector<AudioInjectorPointer> injectorsToRemove;
memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float)); memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float));
for (AudioInjector* injector : _activeLocalAudioInjectors) { for (AudioInjectorPointer injector : _activeLocalAudioInjectors) {
// the lock guarantees that injectorBuffer, if found, is invariant // the lock guarantees that injectorBuffer, if found, is invariant
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
if (injectorBuffer) { if (injectorBuffer) {
@ -1220,7 +1220,7 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
// get one frame from the injector // get one frame from the injector
memset(_localScratchBuffer, 0, bytesToRead); memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
if (injector->isAmbisonic()) { if (injector->isAmbisonic()) {
// no distance attenuation // no distance attenuation
@ -1249,36 +1249,36 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) { for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain; mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain;
} }
} else { } else {
// calculate distance, gain and azimuth for hrtf // calculate distance, gain and azimuth for hrtf
glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
float distance = glm::max(glm::length(relativePosition), EPSILON); float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = gainForSource(distance, injector->getVolume()); float gain = gainForSource(distance, injector->getVolume());
float azimuth = azimuthForSource(relativePosition); float azimuth = azimuthForSource(relativePosition);
// mono gets spatialized into mixBuffer // mono gets spatialized into mixBuffer
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
} }
} else { } else {
qCDebug(audioclient) << "injector has no more data, marking finished for removal"; qCDebug(audioclient) << "injector has no more data, marking finished for removal";
injector->finishLocalInjection(); injector->finishLocalInjection();
injectorsToRemove.append(injector); injectorsToRemove.append(injector);
} }
} else { } else {
qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal";
injector->finishLocalInjection(); injector->finishLocalInjection();
injectorsToRemove.append(injector); injectorsToRemove.append(injector);
} }
} }
for (AudioInjector* injector : injectorsToRemove) { for (AudioInjectorPointer injector : injectorsToRemove) {
qCDebug(audioclient) << "removing injector"; qCDebug(audioclient) << "removing injector";
_activeLocalAudioInjectors.removeOne(injector); _activeLocalAudioInjectors.removeOne(injector);
} }
@ -1369,7 +1369,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
} }
} }
bool AudioClient::outputLocalInjector(AudioInjector* injector) { bool AudioClient::outputLocalInjector(AudioInjectorPointer injector) {
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
if (injectorBuffer) { if (injectorBuffer) {
// local injectors are on the AudioInjectorsThread, so we must guard access // local injectors are on the AudioInjectorsThread, so we must guard access
@ -1711,9 +1711,9 @@ int AudioClient::calculateNumberOfFrameSamples(int numBytes) const {
float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
glm::quat inverseOrientation = glm::inverse(_orientationGetter()); glm::quat inverseOrientation = glm::inverse(_orientationGetter());
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
// project the rotated source position vector onto the XZ plane // project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f; rotatedSourcePosition.y = 0.0f;
@ -1721,15 +1721,15 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
// produce an oriented angle about the y-axis // produce an oriented angle about the y-axis
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle; return (direction.x < 0.0f) ? -angle : angle;
} else { } else {
// no azimuth if they are in same spot // no azimuth if they are in same spot
return 0.0f; return 0.0f;
} }
} }
@ -1869,9 +1869,9 @@ void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); }); moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
} }
void AudioClient::setInputVolume(float volume) { void AudioClient::setInputVolume(float volume) {
if (_audioInput && volume != (float)_audioInput->volume()) { if (_audioInput && volume != (float)_audioInput->volume()) {
_audioInput->setVolume(volume); _audioInput->setVolume(volume);
emit inputVolumeChanged(_audioInput->volume()); emit inputVolumeChanged(_audioInput->volume());
} }
} }

View file

@ -143,7 +143,7 @@ public:
Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale);
bool outputLocalInjector(AudioInjector* injector) override; bool outputLocalInjector(AudioInjectorPointer injector) override;
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const; QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
@ -380,7 +380,7 @@ private:
bool _hasReceivedFirstPacket { false }; bool _hasReceivedFirstPacket { false };
QVector<AudioInjector*> _activeLocalAudioInjectors; QVector<AudioInjectorPointer> _activeLocalAudioInjectors;
bool _isPlayingBackRecording { false }; bool _isPlayingBackRecording { false };

View file

@ -18,6 +18,7 @@
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include "AudioInjectorOptions.h" #include "AudioInjectorOptions.h"
#include "AudioInjector.h"
class AudioInjector; class AudioInjector;
class AudioInjectorLocalBuffer; class AudioInjectorLocalBuffer;
@ -35,7 +36,7 @@ public:
// threadsafe // threadsafe
// moves injector->getLocalBuffer() to another thread (so removes its parent) // moves injector->getLocalBuffer() to another thread (so removes its parent)
// take care to delete it when ~AudioInjector, as parenting Qt semantics will not work // take care to delete it when ~AudioInjector, as parenting Qt semantics will not work
virtual bool outputLocalInjector(AudioInjector* injector) = 0; virtual bool outputLocalInjector(AudioInjectorPointer injector) = 0;
public slots: public slots:
virtual bool shouldLoopbackInjectors() { return false; } virtual bool shouldLoopbackInjectors() { return false; }

View file

@ -132,7 +132,7 @@ void AudioInjector::restart() {
} }
} }
bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjector*)) { bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjectorPointer)) {
_state = AudioInjectorState::NotFinished; _state = AudioInjectorState::NotFinished;
int byteOffset = 0; int byteOffset = 0;
@ -150,7 +150,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjector*
bool success = true; bool success = true;
if (!_options.localOnly) { if (!_options.localOnly) {
auto injectorManager = DependencyManager::get<AudioInjectorManager>(); auto injectorManager = DependencyManager::get<AudioInjectorManager>();
if (!(*injectorManager.*injection)(this)) { if (!(*injectorManager.*injection)(getThisPointer())) {
success = false; success = false;
finishNetworkInjection(); finishNetworkInjection();
} }
@ -173,7 +173,7 @@ bool AudioInjector::injectLocally() {
// call this function on the AudioClient's thread // call this function on the AudioClient's thread
// this will move the local buffer's thread to the LocalInjectorThread // this will move the local buffer's thread to the LocalInjectorThread
success = _localAudioInterface->outputLocalInjector(this); success = _localAudioInterface->outputLocalInjector(getThisPointer());
if (!success) { if (!success) {
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
@ -429,7 +429,8 @@ void AudioInjector::stopAndDeleteLater() {
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
} }
AudioInjector* AudioInjector::playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position) { AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const float volume,
const float stretchFactor, const glm::vec3 position) {
if (!sound || !sound->isReady()) { if (!sound || !sound->isReady()) {
return nullptr; return nullptr;
} }
@ -462,8 +463,8 @@ AudioInjector* AudioInjector::playSound(SharedSoundPointer sound, const float vo
return playSoundAndDelete(resampled, options); return playSoundAndDelete(resampled, options);
} }
AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) { AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) {
AudioInjector* sound = playSound(buffer, options); AudioInjectorPointer sound = playSound(buffer, options);
if (sound) { if (sound) {
sound->_state |= AudioInjectorState::PendingDelete; sound->_state |= AudioInjectorState::PendingDelete;
@ -473,10 +474,27 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const
} }
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) { AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
AudioInjector* injector = new AudioInjector(buffer, options); AudioInjectorPointer injector = AudioInjectorPointer(new AudioInjector(buffer, options));
injector->setThisPointer(injector);
if (!injector->inject(&AudioInjectorManager::threadInjector)) { if (!injector->inject(&AudioInjectorManager::threadInjector)) {
qWarning() << "AudioInjector::playSound failed to thread injector"; qWarning() << "AudioInjector::playSound failed to thread injector";
} }
return injector; return injector;
} }
AudioInjectorPointer AudioInjector::getThisPointer() {
std::lock_guard<std::mutex> lock(_refLock);
QSharedPointer<AudioInjector> this_ref(_self);
if (this_ref.isNull()) {
this_ref = QSharedPointer<AudioInjector>(this);
_self = this_ref.toWeakRef();
}
return this_ref;
}
void AudioInjector::setThisPointer(AudioInjectorPointer self) {
std::lock_guard<std::mutex> lock(_refLock);
_self = self.toWeakRef();
}

View file

@ -32,6 +32,8 @@
class AbstractAudioInterface; class AbstractAudioInterface;
class AudioInjectorManager; class AudioInjectorManager;
class AudioInjector;
using AudioInjectorPointer = QSharedPointer<AudioInjector>;
enum class AudioInjectorState : uint8_t { enum class AudioInjectorState : uint8_t {
@ -46,19 +48,19 @@ AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs);
AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs); AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs);
// In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object // In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object
// until it dies. // until it dies.
class AudioInjector : public QObject { class AudioInjector : public QObject {
Q_OBJECT Q_OBJECT
public: public:
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
~AudioInjector(); ~AudioInjector();
bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); } bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); }
int getCurrentSendOffset() const { return _currentSendOffset; } int getCurrentSendOffset() const { return _currentSendOffset; }
void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; }
AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; }
AudioHRTF& getLocalHRTF() { return _localHRTF; } AudioHRTF& getLocalHRTF() { return _localHRTF; }
AudioFOA& getLocalFOA() { return _localFOA; } AudioFOA& getLocalFOA() { return _localFOA; }
@ -72,36 +74,41 @@ public:
bool stateHas(AudioInjectorState state) const ; bool stateHas(AudioInjectorState state) const ;
static void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } static void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; }
static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options); static AudioInjectorPointer playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options);
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options); static AudioInjectorPointer playSound(const QByteArray& buffer, const AudioInjectorOptions options);
static AudioInjector* playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position); static AudioInjectorPointer playSound(SharedSoundPointer sound, const float volume,
const float stretchFactor, const glm::vec3 position);
QWeakPointer<AudioInjector> weakRef() const { return _self; }
AudioInjectorPointer getThisPointer();
void setThisPointer(AudioInjectorPointer self);
public slots: public slots:
void restart(); void restart();
void stop(); void stop();
void triggerDeleteAfterFinish(); void triggerDeleteAfterFinish();
void stopAndDeleteLater(); void stopAndDeleteLater();
const AudioInjectorOptions& getOptions() const { return _options; } const AudioInjectorOptions& getOptions() const { return _options; }
void setOptions(const AudioInjectorOptions& options); void setOptions(const AudioInjectorOptions& options);
float getLoudness() const { return _loudness; } float getLoudness() const { return _loudness; }
bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); } bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); }
void finish(); void finish();
void finishLocalInjection(); void finishLocalInjection();
void finishNetworkInjection(); void finishNetworkInjection();
signals: signals:
void finished(); void finished();
void restarting(); void restarting();
private: private:
int64_t injectNextFrame(); int64_t injectNextFrame();
bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*)); bool inject(bool(AudioInjectorManager::*injection)(AudioInjectorPointer));
bool injectLocally(); bool injectLocally();
void deleteLocalBuffer(); void deleteLocalBuffer();
static AbstractAudioInterface* _localAudioInterface; static AbstractAudioInterface* _localAudioInterface;
QByteArray _audioData; QByteArray _audioData;
@ -112,17 +119,20 @@ private:
int _currentSendOffset { 0 }; int _currentSendOffset { 0 };
std::unique_ptr<NLPacket> _currentPacket { nullptr }; std::unique_ptr<NLPacket> _currentPacket { nullptr };
AudioInjectorLocalBuffer* _localBuffer { nullptr }; AudioInjectorLocalBuffer* _localBuffer { nullptr };
int64_t _nextFrame { 0 }; int64_t _nextFrame { 0 };
std::unique_ptr<QElapsedTimer> _frameTimer { nullptr }; std::unique_ptr<QElapsedTimer> _frameTimer { nullptr };
quint16 _outgoingSequenceNumber { 0 }; quint16 _outgoingSequenceNumber { 0 };
// when the injector is local, we need this // when the injector is local, we need this
AudioHRTF _localHRTF; AudioHRTF _localHRTF;
AudioFOA _localFOA; AudioFOA _localFOA;
friend class AudioInjectorManager; friend class AudioInjectorManager;
QWeakPointer<AudioInjector> _self;
mutable std::mutex _refLock;
}; };
Q_DECLARE_METATYPE(AudioInjector*) Q_DECLARE_METATYPE(AudioInjectorPointer)
#endif // hifi_AudioInjector_h #endif // hifi_AudioInjector_h

View file

@ -21,26 +21,26 @@
AudioInjectorManager::~AudioInjectorManager() { AudioInjectorManager::~AudioInjectorManager() {
_shouldStop = true; _shouldStop = true;
Lock lock(_injectorsMutex); Lock lock(_injectorsMutex);
// make sure any still living injectors are stopped and deleted // make sure any still living injectors are stopped and deleted
while (!_injectors.empty()) { while (!_injectors.empty()) {
// grab the injector at the front // grab the injector at the front
auto& timePointerPair = _injectors.top(); auto& timePointerPair = _injectors.top();
// ask it to stop and be deleted // ask it to stop and be deleted
timePointerPair.second->stopAndDeleteLater(); timePointerPair.second->stopAndDeleteLater();
_injectors.pop(); _injectors.pop();
} }
// get rid of the lock now that we've stopped all living injectors // get rid of the lock now that we've stopped all living injectors
lock.unlock(); lock.unlock();
// in case the thread is waiting for injectors wake it up now // in case the thread is waiting for injectors wake it up now
_injectorReady.notify_one(); _injectorReady.notify_one();
// quit and wait on the manager thread, if we ever created it // quit and wait on the manager thread, if we ever created it
if (_thread) { if (_thread) {
_thread->quit(); _thread->quit();
@ -51,10 +51,10 @@ AudioInjectorManager::~AudioInjectorManager() {
void AudioInjectorManager::createThread() { void AudioInjectorManager::createThread() {
_thread = new QThread; _thread = new QThread;
_thread->setObjectName("Audio Injector Thread"); _thread->setObjectName("Audio Injector Thread");
// when the thread is started, have it call our run to handle injection of audio // when the thread is started, have it call our run to handle injection of audio
connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection); connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection);
// start the thread // start the thread
_thread->start(); _thread->start();
} }
@ -63,20 +63,20 @@ void AudioInjectorManager::run() {
while (!_shouldStop) { while (!_shouldStop) {
// wait until the next injector is ready, or until we get a new injector given to us // wait until the next injector is ready, or until we get a new injector given to us
Lock lock(_injectorsMutex); Lock lock(_injectorsMutex);
if (_injectors.size() > 0) { if (_injectors.size() > 0) {
// when does the next injector need to send a frame? // when does the next injector need to send a frame?
// do we get to wait or should we just go for it now? // do we get to wait or should we just go for it now?
auto timeInjectorPair = _injectors.top(); auto timeInjectorPair = _injectors.top();
auto nextTimestamp = timeInjectorPair.first; auto nextTimestamp = timeInjectorPair.first;
int64_t difference = int64_t(nextTimestamp - usecTimestampNow()); int64_t difference = int64_t(nextTimestamp - usecTimestampNow());
if (difference > 0) { if (difference > 0) {
_injectorReady.wait_for(lock, std::chrono::microseconds(difference)); _injectorReady.wait_for(lock, std::chrono::microseconds(difference));
} }
if (_injectors.size() > 0) { if (_injectors.size() > 0) {
// loop through the injectors in the map and send whatever frames need to go out // loop through the injectors in the map and send whatever frames need to go out
auto front = _injectors.top(); auto front = _injectors.top();
@ -90,7 +90,7 @@ void AudioInjectorManager::run() {
// either way we're popping this injector off - get a copy first // either way we're popping this injector off - get a copy first
auto injector = front.second; auto injector = front.second;
_injectors.pop(); _injectors.pop();
if (!injector.isNull()) { if (!injector.isNull()) {
// this is an injector that's ready to go, have it send a frame now // this is an injector that's ready to go, have it send a frame now
auto nextCallDelta = injector->injectNextFrame(); auto nextCallDelta = injector->injectNextFrame();
@ -100,7 +100,7 @@ void AudioInjectorManager::run() {
heldInjectors.emplace(heldInjectors.end(), usecTimestampNow() + nextCallDelta, injector); heldInjectors.emplace(heldInjectors.end(), usecTimestampNow() + nextCallDelta, injector);
} }
} }
if (_injectors.size() > 0) { if (_injectors.size() > 0) {
front = _injectors.top(); front = _injectors.top();
} else { } else {
@ -120,10 +120,10 @@ void AudioInjectorManager::run() {
// we have no current injectors, wait until we get at least one before we do anything // we have no current injectors, wait until we get at least one before we do anything
_injectorReady.wait(lock); _injectorReady.wait(lock);
} }
// unlock the lock in case something in process events needs to modify the queue // unlock the lock in case something in process events needs to modify the queue
lock.unlock(); lock.unlock();
QCoreApplication::processEvents(); QCoreApplication::processEvents();
} }
} }
@ -139,36 +139,36 @@ bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a
return false; return false;
} }
bool AudioInjectorManager::threadInjector(AudioInjector* injector) { bool AudioInjectorManager::threadInjector(AudioInjectorPointer injector) {
if (_shouldStop) { if (_shouldStop) {
qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
return false; return false;
} }
// guard the injectors vector with a mutex // guard the injectors vector with a mutex
Lock lock(_injectorsMutex); Lock lock(_injectorsMutex);
if (wouldExceedLimits()) { if (wouldExceedLimits()) {
return false; return false;
} else { } else {
if (!_thread) { if (!_thread) {
createThread(); createThread();
} }
// move the injector to the QThread // move the injector to the QThread
injector->moveToThread(_thread); injector->moveToThread(_thread);
// add the injector to the queue with a send timestamp of now // add the injector to the queue with a send timestamp of now
_injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); _injectors.emplace(usecTimestampNow(), AudioInjectorPointer { injector });
// notify our wait condition so we can inject two frames for this injector immediately // notify our wait condition so we can inject two frames for this injector immediately
_injectorReady.notify_one(); _injectorReady.notify_one();
return true; return true;
} }
} }
bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { bool AudioInjectorManager::restartFinishedInjector(AudioInjectorPointer injector) {
if (_shouldStop) { if (_shouldStop) {
qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
return false; return false;
@ -181,8 +181,8 @@ bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) {
return false; return false;
} else { } else {
// add the injector to the queue with a send timestamp of now // add the injector to the queue with a send timestamp of now
_injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); _injectors.emplace(usecTimestampNow(), injector->getThisPointer());
// notify our wait condition so we can inject two frames for this injector immediately // notify our wait condition so we can inject two frames for this injector immediately
_injectorReady.notify_one(); _injectorReady.notify_one();
} }

View file

@ -23,7 +23,7 @@
#include <DependencyManager.h> #include <DependencyManager.h>
class AudioInjector; #include "AudioInjector.h"
class AudioInjectorManager : public QObject, public Dependency { class AudioInjectorManager : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
@ -33,39 +33,38 @@ public:
private slots: private slots:
void run(); void run();
private: private:
using InjectorQPointer = QPointer<AudioInjector>; using TimeInjectorPointerPair = std::pair<uint64_t, AudioInjectorPointer>;
using TimeInjectorPointerPair = std::pair<uint64_t, InjectorQPointer>;
struct greaterTime { struct greaterTime {
bool operator() (const TimeInjectorPointerPair& x, const TimeInjectorPointerPair& y) const { bool operator() (const TimeInjectorPointerPair& x, const TimeInjectorPointerPair& y) const {
return x.first > y.first; return x.first > y.first;
} }
}; };
using InjectorQueue = std::priority_queue<TimeInjectorPointerPair, using InjectorQueue = std::priority_queue<TimeInjectorPointerPair,
std::deque<TimeInjectorPointerPair>, std::deque<TimeInjectorPointerPair>,
greaterTime>; greaterTime>;
using Mutex = std::mutex; using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
bool threadInjector(AudioInjector* injector); bool threadInjector(AudioInjectorPointer injector);
bool restartFinishedInjector(AudioInjector* injector); bool restartFinishedInjector(AudioInjectorPointer injector);
void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } void notifyInjectorReadyCondition() { _injectorReady.notify_one(); }
bool wouldExceedLimits(); bool wouldExceedLimits();
AudioInjectorManager() {}; AudioInjectorManager() {};
AudioInjectorManager(const AudioInjectorManager&) = delete; AudioInjectorManager(const AudioInjectorManager&) = delete;
AudioInjectorManager& operator=(const AudioInjectorManager&) = delete; AudioInjectorManager& operator=(const AudioInjectorManager&) = delete;
void createThread(); void createThread();
QThread* _thread { nullptr }; QThread* _thread { nullptr };
bool _shouldStop { false }; bool _shouldStop { false };
InjectorQueue _injectors; InjectorQueue _injectors;
Mutex _injectorsMutex; Mutex _injectorsMutex;
std::condition_variable _injectorReady; std::condition_variable _injectorReady;
friend class AudioInjector; friend class AudioInjector;
}; };

View file

@ -21,7 +21,7 @@ QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* c
// when the script goes down we want to cleanup the injector // when the script goes down we want to cleanup the injector
QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately, QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately,
Qt::DirectConnection); Qt::DirectConnection);
return engine->newQObject(in, QScriptEngine::ScriptOwnership); return engine->newQObject(in, QScriptEngine::ScriptOwnership);
} }
@ -29,10 +29,10 @@ void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& o
out = qobject_cast<ScriptAudioInjector*>(object.toQObject()); out = qobject_cast<ScriptAudioInjector*>(object.toQObject());
} }
ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) : ScriptAudioInjector::ScriptAudioInjector(AudioInjectorPointer injector) :
_injector(injector) _injector(injector)
{ {
QObject::connect(injector, &AudioInjector::finished, this, &ScriptAudioInjector::finished); QObject::connect(injector.data(), &AudioInjector::finished, this, &ScriptAudioInjector::finished);
} }
ScriptAudioInjector::~ScriptAudioInjector() { ScriptAudioInjector::~ScriptAudioInjector() {

View file

@ -18,31 +18,31 @@
class ScriptAudioInjector : public QObject { class ScriptAudioInjector : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool playing READ isPlaying) Q_PROPERTY(bool playing READ isPlaying)
Q_PROPERTY(float loudness READ getLoudness) Q_PROPERTY(float loudness READ getLoudness)
Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions)
public: public:
ScriptAudioInjector(AudioInjector* injector); ScriptAudioInjector(AudioInjectorPointer injector);
~ScriptAudioInjector(); ~ScriptAudioInjector();
public slots: public slots:
void restart() { _injector->restart(); } void restart() { _injector->restart(); }
void stop() { _injector->stop(); } void stop() { _injector->stop(); }
const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); }
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
float getLoudness() const { return _injector->getLoudness(); } float getLoudness() const { return _injector->getLoudness(); }
bool isPlaying() const { return _injector->isPlaying(); } bool isPlaying() const { return _injector->isPlaying(); }
signals: signals:
void finished(); void finished();
protected slots: protected slots:
void stopInjectorImmediately(); void stopInjectorImmediately();
private: private:
QPointer<AudioInjector> _injector; AudioInjectorPointer _injector;
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
}; };

View file

@ -15,6 +15,7 @@
#include <SoundCache.h> #include <SoundCache.h>
class AudioInjector; class AudioInjector;
using AudioInjectorPointer = QSharedPointer<AudioInjector>;
// SoundEffect object, exposed to qml only, not interface JavaScript. // SoundEffect object, exposed to qml only, not interface JavaScript.
// This is used to play spatial sound effects on tablets/web entities from within QML. // This is used to play spatial sound effects on tablets/web entities from within QML.
@ -38,7 +39,7 @@ protected:
QUrl _url; QUrl _url;
float _volume { 1.0f }; float _volume { 1.0f };
SharedSoundPointer _sound; SharedSoundPointer _sound;
AudioInjector* _injector { nullptr }; AudioInjectorPointer _injector;
}; };
#endif // hifi_SoundEffect_h #endif // hifi_SoundEffect_h