mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 15:47:02 +02:00
switch bare pointers to AudioInjector objects to be QSharedPointers
This commit is contained in:
parent
4a35ad105b
commit
3670a04d8e
13 changed files with 148 additions and 120 deletions
interface/src
libraries
audio-client/src
audio/src
AbstractAudioInterface.hAudioInjector.cppAudioInjector.hAudioInjectorManager.cppAudioInjectorManager.h
script-engine/src
ui/src/ui/types
|
@ -678,7 +678,7 @@ private:
|
|||
QTimer _addAssetToWorldErrorTimer;
|
||||
|
||||
FileScriptingInterface* _fileDownload;
|
||||
AudioInjector* _snapshotSoundInjector { nullptr };
|
||||
AudioInjectorPointer _snapshotSoundInjector;
|
||||
SharedSoundPointer _snapshotSound;
|
||||
|
||||
DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;
|
||||
|
|
|
@ -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.
|
||||
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
||||
|
||||
|
||||
_collisionInjectors.remove_if([](QPointer<AudioInjector>& injector) {
|
||||
_collisionInjectors.remove_if([](AudioInjectorPointer injector) {
|
||||
return !injector || injector->isFinished();
|
||||
});
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
#include "AvatarMotionState.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "AudioInjector.h"
|
||||
|
||||
class AudioInjector;
|
||||
|
||||
class AvatarManager : public AvatarHashMap {
|
||||
Q_OBJECT
|
||||
|
@ -104,7 +104,7 @@ private:
|
|||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
std::list<QPointer<AudioInjector>> _collisionInjectors;
|
||||
std::list<AudioInjectorPointer> _collisionInjectors;
|
||||
|
||||
RateCounter<> _myAvatarSendRate;
|
||||
int _numAvatarsUpdated { 0 };
|
||||
|
|
|
@ -210,9 +210,9 @@ AudioClient::AudioClient() :
|
|||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
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() << "]";
|
||||
switchOutputToAudioDevice(outputDeviceInfo);
|
||||
switchOutputToAudioDevice(outputDeviceInfo);
|
||||
});
|
||||
|
||||
connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
|
||||
|
@ -261,10 +261,10 @@ void AudioClient::cleanupBeforeQuit() {
|
|||
// so this must be explicitly, synchronously stopped
|
||||
static ConditionalGuard guard;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
|
||||
guard.wait();
|
||||
|
@ -630,7 +630,7 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessag
|
|||
message->readPrimitive(&bitset);
|
||||
|
||||
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);
|
||||
|
||||
|
||||
if (hasReverb) {
|
||||
float reverbTime, wetLevel;
|
||||
message->readPrimitive(&reverbTime);
|
||||
|
@ -728,7 +728,7 @@ void AudioClient::Gate::flush() {
|
|||
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_muted) {
|
||||
toggleMute();
|
||||
|
||||
|
||||
// have the audio scripting interface emit a signal to say we were muted by the mixer
|
||||
emit mutedByMixer();
|
||||
}
|
||||
|
@ -737,7 +737,7 @@ void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message)
|
|||
void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
|
||||
|
||||
message->readPrimitive(&position);
|
||||
message->readPrimitive(&radius);
|
||||
|
||||
|
@ -770,7 +770,7 @@ void AudioClient::handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> mess
|
|||
}
|
||||
|
||||
void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
||||
|
||||
|
||||
_selectedCodecName = selectedCodecName;
|
||||
|
||||
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName;
|
||||
|
@ -787,7 +787,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
for (auto& plugin : codecPlugins) {
|
||||
if (_selectedCodecName == plugin->getName()) {
|
||||
_codec = plugin;
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
|
||||
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
|
||||
break;
|
||||
|
@ -795,7 +795,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) {
|
||||
auto device = deviceInfo;
|
||||
|
||||
|
@ -1203,11 +1203,11 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
// lock the injectors
|
||||
Lock lock(_injectorsMutex);
|
||||
|
||||
QVector<AudioInjector*> injectorsToRemove;
|
||||
QVector<AudioInjectorPointer> injectorsToRemove;
|
||||
|
||||
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
|
||||
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
|
||||
if (injectorBuffer) {
|
||||
|
@ -1220,7 +1220,7 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
// get one frame from the injector
|
||||
memset(_localScratchBuffer, 0, bytesToRead);
|
||||
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
|
||||
|
||||
|
||||
if (injector->isAmbisonic()) {
|
||||
|
||||
// no distance attenuation
|
||||
|
@ -1249,36 +1249,36 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
|
||||
mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// calculate distance, gain and azimuth for hrtf
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
float gain = gainForSource(distance, injector->getVolume());
|
||||
float gain = gainForSource(distance, injector->getVolume());
|
||||
float azimuth = azimuthForSource(relativePosition);
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
qCDebug(audioclient) << "injector has no more data, marking finished for removal";
|
||||
injector->finishLocalInjection();
|
||||
injectorsToRemove.append(injector);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal";
|
||||
injector->finishLocalInjection();
|
||||
injectorsToRemove.append(injector);
|
||||
}
|
||||
}
|
||||
|
||||
for (AudioInjector* injector : injectorsToRemove) {
|
||||
|
||||
for (AudioInjectorPointer injector : injectorsToRemove) {
|
||||
qCDebug(audioclient) << "removing 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();
|
||||
if (injectorBuffer) {
|
||||
// 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) {
|
||||
glm::quat inverseOrientation = glm::inverse(_orientationGetter());
|
||||
|
||||
|
||||
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
|
||||
|
||||
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
rotatedSourcePosition.y = 0.0f;
|
||||
|
||||
|
@ -1721,15 +1721,15 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
|
|||
|
||||
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
|
||||
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
|
||||
|
||||
|
||||
// produce an oriented angle about the y-axis
|
||||
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
|
||||
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
return (direction.x < 0.0f) ? -angle : angle;
|
||||
|
||||
} else {
|
||||
} else {
|
||||
// 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(); });
|
||||
}
|
||||
|
||||
void AudioClient::setInputVolume(float volume) {
|
||||
if (_audioInput && volume != (float)_audioInput->volume()) {
|
||||
_audioInput->setVolume(volume);
|
||||
void AudioClient::setInputVolume(float volume) {
|
||||
if (_audioInput && volume != (float)_audioInput->volume()) {
|
||||
_audioInput->setVolume(volume);
|
||||
emit inputVolumeChanged(_audioInput->volume());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
|
||||
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;
|
||||
QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
|
||||
|
@ -380,7 +380,7 @@ private:
|
|||
|
||||
bool _hasReceivedFirstPacket { false };
|
||||
|
||||
QVector<AudioInjector*> _activeLocalAudioInjectors;
|
||||
QVector<AudioInjectorPointer> _activeLocalAudioInjectors;
|
||||
|
||||
bool _isPlayingBackRecording { false };
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "AudioInjectorOptions.h"
|
||||
#include "AudioInjector.h"
|
||||
|
||||
class AudioInjector;
|
||||
class AudioInjectorLocalBuffer;
|
||||
|
@ -35,7 +36,7 @@ public:
|
|||
// threadsafe
|
||||
// moves injector->getLocalBuffer() to another thread (so removes its parent)
|
||||
// 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:
|
||||
virtual bool shouldLoopbackInjectors() { return false; }
|
||||
|
|
|
@ -132,7 +132,7 @@ void AudioInjector::restart() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjector*)) {
|
||||
bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjectorPointer)) {
|
||||
_state = AudioInjectorState::NotFinished;
|
||||
|
||||
int byteOffset = 0;
|
||||
|
@ -150,7 +150,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(AudioInjector*
|
|||
bool success = true;
|
||||
if (!_options.localOnly) {
|
||||
auto injectorManager = DependencyManager::get<AudioInjectorManager>();
|
||||
if (!(*injectorManager.*injection)(this)) {
|
||||
if (!(*injectorManager.*injection)(getThisPointer())) {
|
||||
success = false;
|
||||
finishNetworkInjection();
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ bool AudioInjector::injectLocally() {
|
|||
|
||||
// call this function on the AudioClient's thread
|
||||
// this will move the local buffer's thread to the LocalInjectorThread
|
||||
success = _localAudioInterface->outputLocalInjector(this);
|
||||
success = _localAudioInterface->outputLocalInjector(getThisPointer());
|
||||
|
||||
if (!success) {
|
||||
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
|
||||
|
@ -429,7 +429,8 @@ void AudioInjector::stopAndDeleteLater() {
|
|||
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()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -462,8 +463,8 @@ AudioInjector* AudioInjector::playSound(SharedSoundPointer sound, const float vo
|
|||
return playSoundAndDelete(resampled, options);
|
||||
}
|
||||
|
||||
AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||
AudioInjector* sound = playSound(buffer, options);
|
||||
AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||
AudioInjectorPointer sound = playSound(buffer, options);
|
||||
|
||||
if (sound) {
|
||||
sound->_state |= AudioInjectorState::PendingDelete;
|
||||
|
@ -473,10 +474,27 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const
|
|||
}
|
||||
|
||||
|
||||
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||
AudioInjector* injector = new AudioInjector(buffer, options);
|
||||
AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||
AudioInjectorPointer injector = AudioInjectorPointer(new AudioInjector(buffer, options));
|
||||
injector->setThisPointer(injector);
|
||||
|
||||
if (!injector->inject(&AudioInjectorManager::threadInjector)) {
|
||||
qWarning() << "AudioInjector::playSound failed to thread 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();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
class AbstractAudioInterface;
|
||||
class AudioInjectorManager;
|
||||
class AudioInjector;
|
||||
using AudioInjectorPointer = QSharedPointer<AudioInjector>;
|
||||
|
||||
|
||||
enum class AudioInjectorState : uint8_t {
|
||||
|
@ -46,19 +48,19 @@ 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
|
||||
// until it dies.
|
||||
// until it dies.
|
||||
class AudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
|
||||
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
|
||||
~AudioInjector();
|
||||
|
||||
|
||||
bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); }
|
||||
|
||||
|
||||
int getCurrentSendOffset() const { return _currentSendOffset; }
|
||||
void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; }
|
||||
|
||||
|
||||
AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; }
|
||||
AudioHRTF& getLocalHRTF() { return _localHRTF; }
|
||||
AudioFOA& getLocalFOA() { return _localFOA; }
|
||||
|
@ -72,36 +74,41 @@ public:
|
|||
|
||||
bool stateHas(AudioInjectorState state) const ;
|
||||
static void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; }
|
||||
static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options);
|
||||
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options);
|
||||
static AudioInjector* playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position);
|
||||
static AudioInjectorPointer playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options);
|
||||
static AudioInjectorPointer playSound(const QByteArray& buffer, const AudioInjectorOptions options);
|
||||
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:
|
||||
void restart();
|
||||
|
||||
|
||||
void stop();
|
||||
void triggerDeleteAfterFinish();
|
||||
void stopAndDeleteLater();
|
||||
|
||||
|
||||
const AudioInjectorOptions& getOptions() const { return _options; }
|
||||
void setOptions(const AudioInjectorOptions& options);
|
||||
|
||||
|
||||
float getLoudness() const { return _loudness; }
|
||||
bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); }
|
||||
void finish();
|
||||
void finishLocalInjection();
|
||||
void finishNetworkInjection();
|
||||
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
void restarting();
|
||||
|
||||
|
||||
private:
|
||||
int64_t injectNextFrame();
|
||||
bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*));
|
||||
bool inject(bool(AudioInjectorManager::*injection)(AudioInjectorPointer));
|
||||
bool injectLocally();
|
||||
void deleteLocalBuffer();
|
||||
|
||||
|
||||
static AbstractAudioInterface* _localAudioInterface;
|
||||
|
||||
QByteArray _audioData;
|
||||
|
@ -112,17 +119,20 @@ private:
|
|||
int _currentSendOffset { 0 };
|
||||
std::unique_ptr<NLPacket> _currentPacket { nullptr };
|
||||
AudioInjectorLocalBuffer* _localBuffer { nullptr };
|
||||
|
||||
|
||||
int64_t _nextFrame { 0 };
|
||||
std::unique_ptr<QElapsedTimer> _frameTimer { nullptr };
|
||||
quint16 _outgoingSequenceNumber { 0 };
|
||||
|
||||
|
||||
// when the injector is local, we need this
|
||||
AudioHRTF _localHRTF;
|
||||
AudioFOA _localFOA;
|
||||
friend class AudioInjectorManager;
|
||||
|
||||
QWeakPointer<AudioInjector> _self;
|
||||
mutable std::mutex _refLock;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AudioInjector*)
|
||||
|
||||
Q_DECLARE_METATYPE(AudioInjectorPointer)
|
||||
|
||||
#endif // hifi_AudioInjector_h
|
||||
|
|
|
@ -21,26 +21,26 @@
|
|||
|
||||
AudioInjectorManager::~AudioInjectorManager() {
|
||||
_shouldStop = true;
|
||||
|
||||
|
||||
Lock lock(_injectorsMutex);
|
||||
|
||||
|
||||
// make sure any still living injectors are stopped and deleted
|
||||
while (!_injectors.empty()) {
|
||||
// grab the injector at the front
|
||||
auto& timePointerPair = _injectors.top();
|
||||
|
||||
|
||||
// ask it to stop and be deleted
|
||||
timePointerPair.second->stopAndDeleteLater();
|
||||
|
||||
|
||||
_injectors.pop();
|
||||
}
|
||||
|
||||
|
||||
// get rid of the lock now that we've stopped all living injectors
|
||||
lock.unlock();
|
||||
|
||||
|
||||
// in case the thread is waiting for injectors wake it up now
|
||||
_injectorReady.notify_one();
|
||||
|
||||
|
||||
// quit and wait on the manager thread, if we ever created it
|
||||
if (_thread) {
|
||||
_thread->quit();
|
||||
|
@ -51,10 +51,10 @@ AudioInjectorManager::~AudioInjectorManager() {
|
|||
void AudioInjectorManager::createThread() {
|
||||
_thread = new QThread;
|
||||
_thread->setObjectName("Audio Injector Thread");
|
||||
|
||||
|
||||
// when the thread is started, have it call our run to handle injection of audio
|
||||
connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection);
|
||||
|
||||
|
||||
// start the thread
|
||||
_thread->start();
|
||||
}
|
||||
|
@ -63,20 +63,20 @@ void AudioInjectorManager::run() {
|
|||
while (!_shouldStop) {
|
||||
// wait until the next injector is ready, or until we get a new injector given to us
|
||||
Lock lock(_injectorsMutex);
|
||||
|
||||
|
||||
if (_injectors.size() > 0) {
|
||||
// when does the next injector need to send a frame?
|
||||
// do we get to wait or should we just go for it now?
|
||||
|
||||
|
||||
auto timeInjectorPair = _injectors.top();
|
||||
|
||||
|
||||
auto nextTimestamp = timeInjectorPair.first;
|
||||
int64_t difference = int64_t(nextTimestamp - usecTimestampNow());
|
||||
|
||||
|
||||
if (difference > 0) {
|
||||
_injectorReady.wait_for(lock, std::chrono::microseconds(difference));
|
||||
}
|
||||
|
||||
|
||||
if (_injectors.size() > 0) {
|
||||
// loop through the injectors in the map and send whatever frames need to go out
|
||||
auto front = _injectors.top();
|
||||
|
@ -90,7 +90,7 @@ void AudioInjectorManager::run() {
|
|||
// either way we're popping this injector off - get a copy first
|
||||
auto injector = front.second;
|
||||
_injectors.pop();
|
||||
|
||||
|
||||
if (!injector.isNull()) {
|
||||
// this is an injector that's ready to go, have it send a frame now
|
||||
auto nextCallDelta = injector->injectNextFrame();
|
||||
|
@ -100,7 +100,7 @@ void AudioInjectorManager::run() {
|
|||
heldInjectors.emplace(heldInjectors.end(), usecTimestampNow() + nextCallDelta, injector);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_injectors.size() > 0) {
|
||||
front = _injectors.top();
|
||||
} else {
|
||||
|
@ -120,10 +120,10 @@ void AudioInjectorManager::run() {
|
|||
// we have no current injectors, wait until we get at least one before we do anything
|
||||
_injectorReady.wait(lock);
|
||||
}
|
||||
|
||||
|
||||
// unlock the lock in case something in process events needs to modify the queue
|
||||
lock.unlock();
|
||||
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
@ -139,36 +139,36 @@ bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
|
||||
bool AudioInjectorManager::threadInjector(AudioInjectorPointer injector) {
|
||||
if (_shouldStop) {
|
||||
qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// guard the injectors vector with a mutex
|
||||
Lock lock(_injectorsMutex);
|
||||
|
||||
|
||||
if (wouldExceedLimits()) {
|
||||
return false;
|
||||
} else {
|
||||
if (!_thread) {
|
||||
createThread();
|
||||
}
|
||||
|
||||
|
||||
// move the injector to the QThread
|
||||
injector->moveToThread(_thread);
|
||||
|
||||
|
||||
// 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
|
||||
_injectorReady.notify_one();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) {
|
||||
bool AudioInjectorManager::restartFinishedInjector(AudioInjectorPointer injector) {
|
||||
if (_shouldStop) {
|
||||
qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
|
||||
return false;
|
||||
|
@ -181,8 +181,8 @@ bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) {
|
|||
return false;
|
||||
} else {
|
||||
// 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
|
||||
_injectorReady.notify_one();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class AudioInjector;
|
||||
#include "AudioInjector.h"
|
||||
|
||||
class AudioInjectorManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -33,39 +33,38 @@ public:
|
|||
private slots:
|
||||
void run();
|
||||
private:
|
||||
|
||||
using InjectorQPointer = QPointer<AudioInjector>;
|
||||
using TimeInjectorPointerPair = std::pair<uint64_t, InjectorQPointer>;
|
||||
|
||||
|
||||
using TimeInjectorPointerPair = std::pair<uint64_t, AudioInjectorPointer>;
|
||||
|
||||
struct greaterTime {
|
||||
bool operator() (const TimeInjectorPointerPair& x, const TimeInjectorPointerPair& y) const {
|
||||
return x.first > y.first;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using InjectorQueue = std::priority_queue<TimeInjectorPointerPair,
|
||||
std::deque<TimeInjectorPointerPair>,
|
||||
greaterTime>;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
bool threadInjector(AudioInjector* injector);
|
||||
bool restartFinishedInjector(AudioInjector* injector);
|
||||
|
||||
bool threadInjector(AudioInjectorPointer injector);
|
||||
bool restartFinishedInjector(AudioInjectorPointer injector);
|
||||
void notifyInjectorReadyCondition() { _injectorReady.notify_one(); }
|
||||
bool wouldExceedLimits();
|
||||
|
||||
|
||||
AudioInjectorManager() {};
|
||||
AudioInjectorManager(const AudioInjectorManager&) = delete;
|
||||
AudioInjectorManager& operator=(const AudioInjectorManager&) = delete;
|
||||
|
||||
|
||||
void createThread();
|
||||
|
||||
|
||||
QThread* _thread { nullptr };
|
||||
bool _shouldStop { false };
|
||||
InjectorQueue _injectors;
|
||||
Mutex _injectorsMutex;
|
||||
std::condition_variable _injectorReady;
|
||||
|
||||
|
||||
friend class AudioInjector;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* c
|
|||
// when the script goes down we want to cleanup the injector
|
||||
QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately,
|
||||
Qt::DirectConnection);
|
||||
|
||||
|
||||
return engine->newQObject(in, QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,10 @@ void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& o
|
|||
out = qobject_cast<ScriptAudioInjector*>(object.toQObject());
|
||||
}
|
||||
|
||||
ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) :
|
||||
ScriptAudioInjector::ScriptAudioInjector(AudioInjectorPointer injector) :
|
||||
_injector(injector)
|
||||
{
|
||||
QObject::connect(injector, &AudioInjector::finished, this, &ScriptAudioInjector::finished);
|
||||
QObject::connect(injector.data(), &AudioInjector::finished, this, &ScriptAudioInjector::finished);
|
||||
}
|
||||
|
||||
ScriptAudioInjector::~ScriptAudioInjector() {
|
||||
|
|
|
@ -18,31 +18,31 @@
|
|||
|
||||
class ScriptAudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
Q_PROPERTY(bool playing READ isPlaying)
|
||||
Q_PROPERTY(float loudness READ getLoudness)
|
||||
Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions)
|
||||
public:
|
||||
ScriptAudioInjector(AudioInjector* injector);
|
||||
ScriptAudioInjector(AudioInjectorPointer injector);
|
||||
~ScriptAudioInjector();
|
||||
public slots:
|
||||
void restart() { _injector->restart(); }
|
||||
void stop() { _injector->stop(); }
|
||||
|
||||
|
||||
const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); }
|
||||
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
|
||||
|
||||
|
||||
float getLoudness() const { return _injector->getLoudness(); }
|
||||
bool isPlaying() const { return _injector->isPlaying(); }
|
||||
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
|
||||
protected slots:
|
||||
void stopInjectorImmediately();
|
||||
private:
|
||||
QPointer<AudioInjector> _injector;
|
||||
|
||||
AudioInjectorPointer _injector;
|
||||
|
||||
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <SoundCache.h>
|
||||
|
||||
class AudioInjector;
|
||||
using AudioInjectorPointer = QSharedPointer<AudioInjector>;
|
||||
|
||||
// SoundEffect object, exposed to qml only, not interface JavaScript.
|
||||
// This is used to play spatial sound effects on tablets/web entities from within QML.
|
||||
|
@ -38,7 +39,7 @@ protected:
|
|||
QUrl _url;
|
||||
float _volume { 1.0f };
|
||||
SharedSoundPointer _sound;
|
||||
AudioInjector* _injector { nullptr };
|
||||
AudioInjectorPointer _injector;
|
||||
};
|
||||
|
||||
#endif // hifi_SoundEffect_h
|
||||
|
|
Loading…
Reference in a new issue