diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 738c52fafb..8220b3e891 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1688,7 +1688,6 @@ void Application::updateHeartbeat() const { void Application::aboutToQuit() { emit beforeAboutToQuit(); - DependencyManager::get()->beforeAboutToQuit(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { if (inputPlugin->isActive()) { @@ -1789,14 +1788,13 @@ void Application::cleanupBeforeQuit() { _snapshotSoundInjector->stop(); } - // stop audio after QML, as there are unexplained audio crashes originating in qtwebengine - - // stop the AudioClient, synchronously + // FIXME: something else is holding a reference to AudioClient, + // so it must be explicitly synchronously stopped here QMetaObject::invokeMethod(DependencyManager::get().data(), - "stop", Qt::BlockingQueuedConnection); - + "cleanupBeforeQuit", Qt::BlockingQueuedConnection); // destroy Audio so it and its threads have a chance to go down safely + // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 680e9129aa..1561eabf05 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -76,42 +76,58 @@ using Mutex = std::mutex; using Lock = std::unique_lock; static Mutex _deviceMutex; -// background thread that continuously polls for device changes -class CheckDevicesThread : public QThread { +class BackgroundThread : public QThread { public: - const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; + BackgroundThread(AudioClient* client) : QThread((QObject*)client), _client(client) {} + virtual void join() = 0; +protected: + AudioClient* _client; +}; - CheckDevicesThread(AudioClient* audioClient) - : _audioClient(audioClient) { - } - - void beforeAboutToQuit() { - Lock lock(_checkDevicesMutex); - _quit = true; +// background thread continuously polling device changes +class CheckDevicesThread : public BackgroundThread { +public: + CheckDevicesThread(AudioClient* client) : BackgroundThread(client) {} + + void join() override { + _shouldQuit = true; + std::unique_lock lock(_joinMutex); + _joinCondition.wait(lock, [&]{ return !_isRunning; }); } +protected: void run() override { - while (true) { - { - Lock lock(_checkDevicesMutex); - if (_quit) { - break; - } - _audioClient->checkDevices(); - } + while (!_shouldQuit) { + _client->checkDevices(); + + const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS); } + std::lock_guard lock(_joinMutex); + _isRunning = false; + _joinCondition.notify_one(); } private: - AudioClient* _audioClient { nullptr }; - Mutex _checkDevicesMutex; - bool _quit { false }; + std::atomic _shouldQuit { false }; + bool _isRunning { true }; + std::mutex _joinMutex; + std::condition_variable _joinCondition; }; -void AudioInjectorsThread::prepare() { - _audio->prepareLocalAudioInjectors(); -} +// background thread buffering local injectors +class LocalInjectorsThread : public BackgroundThread { + Q_OBJECT +public: + LocalInjectorsThread(AudioClient* client) : BackgroundThread(client) {} + + void join() override { return; } + +private slots: + void prepare() { _client->prepareLocalAudioInjectors(); } +}; + +#include "AudioClient.moc" static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) { for (int i = 0; i < numSamples/2; i++) { @@ -179,7 +195,6 @@ AudioClient::AudioClient() : _inputToNetworkResampler(NULL), _networkToOutputResampler(NULL), _localToOutputResampler(NULL), - _localAudioThread(this), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), @@ -210,13 +225,14 @@ AudioClient::AudioClient() : // start a thread to detect any device changes _checkDevicesThread = new CheckDevicesThread(this); - _checkDevicesThread->setObjectName("CheckDevices Thread"); + _checkDevicesThread->setObjectName("AudioClient CheckDevices Thread"); _checkDevicesThread->setPriority(QThread::LowPriority); _checkDevicesThread->start(); // start a thread to process local injectors - _localAudioThread.setObjectName("LocalAudio Thread"); - _localAudioThread.start(); + _localInjectorsThread = new LocalInjectorsThread(this); + _localInjectorsThread->setObjectName("AudioClient LocalInjectors Thread"); + _localInjectorsThread->start(); configureReverb(); @@ -231,18 +247,32 @@ AudioClient::AudioClient() : } AudioClient::~AudioClient() { - delete _checkDevicesThread; - stop(); if (_codec && _encoder) { _codec->releaseEncoder(_encoder); _encoder = nullptr; } } -void AudioClient::beforeAboutToQuit() { - static_cast(_checkDevicesThread)->beforeAboutToQuit(); +void AudioClient::customDeleter() { + deleteLater(); } +void AudioClient::cleanupBeforeQuit() { + // FIXME: this should be put in customDeleter, but there is still a reference to this when it is called, + // so this must be explicitly, synchronously stopped + + stop(); + + if (_checkDevicesThread) { + static_cast(_checkDevicesThread)->join(); + delete _checkDevicesThread; + } + + if (_localInjectorsThread) { + static_cast(_localInjectorsThread)->join(); + delete _localInjectorsThread; + } +} void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) { qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec << "recievedCodec:" << recievedCodec; @@ -1328,7 +1358,7 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) { // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop()) injectorBuffer->setParent(nullptr); - injectorBuffer->moveToThread(&_localAudioThread); + injectorBuffer->moveToThread(_localInjectorsThread); } else { qCDebug(audioclient) << "injector exists in active list already"; } @@ -1530,8 +1560,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _outputScratchBuffer = new int16_t[_outputPeriod]; // size local output mix buffer based on resampled network frame size - _networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); - _localOutputMixBuffer = new float[_networkPeriod]; + int networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); + _localOutputMixBuffer = new float[networkPeriod]; int localPeriod = _outputPeriod * 2; _localInjectorsStream.resizeForFrameSize(localPeriod); @@ -1698,7 +1728,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { } // prepare injectors for the next callback - QMetaObject::invokeMethod(&_audio->_localAudioThread, "prepare", Qt::QueuedConnection); + QMetaObject::invokeMethod(_audio->_localInjectorsThread, "prepare", Qt::QueuedConnection); int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped); int framesPopped = samplesPopped / AudioConstants::STEREO; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index aaedee7456..4a9e51c16c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -71,19 +71,6 @@ class QIODevice; class Transform; class NLPacket; -class AudioInjectorsThread : public QThread { - Q_OBJECT - -public: - AudioInjectorsThread(AudioClient* audio) : _audio(audio) {} - -public slots : - void prepare(); - -private: - AudioClient* _audio; -}; - class AudioClient : public AbstractAudioInterface, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -169,6 +156,7 @@ public: public slots: void start(); void stop(); + void cleanupBeforeQuit(); void handleAudioEnvironmentDataPacket(QSharedPointer message); void handleAudioDataPacket(QSharedPointer message); @@ -184,8 +172,6 @@ public slots: void audioMixerKilled(); void toggleMute(); - void beforeAboutToQuit(); - virtual void setIsStereoInput(bool stereo) override; void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } @@ -242,9 +228,7 @@ protected: AudioClient(); ~AudioClient(); - virtual void customDeleter() override { - deleteLater(); - } + virtual void customDeleter() override; private: void outputFormatChanged(); @@ -337,19 +321,17 @@ private: // for network audio (used by network audio thread) int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - // for local audio (used by audio injectors thread) - int _networkPeriod { 0 }; - float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; - int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - float* _localOutputMixBuffer { NULL }; - AudioInjectorsThread _localAudioThread; - Mutex _localAudioMutex; - // for output audio (used by this thread) int _outputPeriod { 0 }; float* _outputMixBuffer { NULL }; int16_t* _outputScratchBuffer { NULL }; + // for local audio (used by audio injectors thread) + float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; + int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; + float* _localOutputMixBuffer { NULL }; + Mutex _localAudioMutex; + AudioLimiter _audioLimiter; // Adds Reverb @@ -392,12 +374,13 @@ private: QString _selectedCodecName; Encoder* _encoder { nullptr }; // for outbound mic stream - QThread* _checkDevicesThread { nullptr }; - RateCounter<> _silentOutbound; RateCounter<> _audioOutbound; RateCounter<> _silentInbound; RateCounter<> _audioInbound; + + QThread* _checkDevicesThread { nullptr }; + QThread* _localInjectorsThread { nullptr }; }; diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 8fedf926dc..db12d25e0d 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -41,7 +41,7 @@ void releaseOpenVrSystem(); static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; -const quint64 CALIBRATION_TIMELAPSE = 3 * USECS_PER_SECOND; +const quint64 CALIBRATION_TIMELAPSE = 2 * USECS_PER_SECOND; static const char* MENU_PARENT = "Avatar"; static const char* MENU_NAME = "Vive Controllers"; @@ -297,12 +297,12 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr // done } else if (_config == Config::FeetAndHips) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; - _pucksOffset[_validTrackedObjects[2].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[2].second); + _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); } else if (_config == Config::FeetHipsAndChest) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; - _pucksOffset[_validTrackedObjects[2].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[2].second); + _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first; - _pucksOffset[_validTrackedObjects[3].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[3].second); + _pucksOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } _calibrated = true; }