Merge branch 'config-pucks' into vive-head-input

This commit is contained in:
Dante Ruiz 2017-05-09 22:36:21 +01:00
commit 2ee71d3002
4 changed files with 85 additions and 74 deletions

View file

@ -1688,7 +1688,6 @@ void Application::updateHeartbeat() const {
void Application::aboutToQuit() { void Application::aboutToQuit() {
emit beforeAboutToQuit(); emit beforeAboutToQuit();
DependencyManager::get<AudioClient>()->beforeAboutToQuit();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) { if (inputPlugin->isActive()) {
@ -1789,14 +1788,13 @@ void Application::cleanupBeforeQuit() {
_snapshotSoundInjector->stop(); _snapshotSoundInjector->stop();
} }
// stop audio after QML, as there are unexplained audio crashes originating in qtwebengine // FIXME: something else is holding a reference to AudioClient,
// so it must be explicitly synchronously stopped here
// stop the AudioClient, synchronously
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection); "cleanupBeforeQuit", Qt::BlockingQueuedConnection);
// destroy Audio so it and its threads have a chance to go down safely // 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<AudioClient>(); DependencyManager::destroy<AudioClient>();
DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AudioInjectorManager>();

View file

@ -76,42 +76,58 @@ using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
static Mutex _deviceMutex; static Mutex _deviceMutex;
// background thread that continuously polls for device changes class BackgroundThread : public QThread {
class CheckDevicesThread : public QThread {
public: 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) // background thread continuously polling device changes
: _audioClient(audioClient) { class CheckDevicesThread : public BackgroundThread {
} public:
CheckDevicesThread(AudioClient* client) : BackgroundThread(client) {}
void beforeAboutToQuit() {
Lock lock(_checkDevicesMutex); void join() override {
_quit = true; _shouldQuit = true;
std::unique_lock<std::mutex> lock(_joinMutex);
_joinCondition.wait(lock, [&]{ return !_isRunning; });
} }
protected:
void run() override { void run() override {
while (true) { while (!_shouldQuit) {
{ _client->checkDevices();
Lock lock(_checkDevicesMutex);
if (_quit) { const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
break;
}
_audioClient->checkDevices();
}
QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS); QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS);
} }
std::lock_guard<std::mutex> lock(_joinMutex);
_isRunning = false;
_joinCondition.notify_one();
} }
private: private:
AudioClient* _audioClient { nullptr }; std::atomic<bool> _shouldQuit { false };
Mutex _checkDevicesMutex; bool _isRunning { true };
bool _quit { false }; std::mutex _joinMutex;
std::condition_variable _joinCondition;
}; };
void AudioInjectorsThread::prepare() { // background thread buffering local injectors
_audio->prepareLocalAudioInjectors(); 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) { static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) { for (int i = 0; i < numSamples/2; i++) {
@ -179,7 +195,6 @@ AudioClient::AudioClient() :
_inputToNetworkResampler(NULL), _inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL), _networkToOutputResampler(NULL),
_localToOutputResampler(NULL), _localToOutputResampler(NULL),
_localAudioThread(this),
_audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT),
_outgoingAvatarAudioSequenceNumber(0), _outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this),
@ -210,13 +225,14 @@ AudioClient::AudioClient() :
// start a thread to detect any device changes // start a thread to detect any device changes
_checkDevicesThread = new CheckDevicesThread(this); _checkDevicesThread = new CheckDevicesThread(this);
_checkDevicesThread->setObjectName("CheckDevices Thread"); _checkDevicesThread->setObjectName("AudioClient CheckDevices Thread");
_checkDevicesThread->setPriority(QThread::LowPriority); _checkDevicesThread->setPriority(QThread::LowPriority);
_checkDevicesThread->start(); _checkDevicesThread->start();
// start a thread to process local injectors // start a thread to process local injectors
_localAudioThread.setObjectName("LocalAudio Thread"); _localInjectorsThread = new LocalInjectorsThread(this);
_localAudioThread.start(); _localInjectorsThread->setObjectName("AudioClient LocalInjectors Thread");
_localInjectorsThread->start();
configureReverb(); configureReverb();
@ -231,18 +247,32 @@ AudioClient::AudioClient() :
} }
AudioClient::~AudioClient() { AudioClient::~AudioClient() {
delete _checkDevicesThread;
stop();
if (_codec && _encoder) { if (_codec && _encoder) {
_codec->releaseEncoder(_encoder); _codec->releaseEncoder(_encoder);
_encoder = nullptr; _encoder = nullptr;
} }
} }
void AudioClient::beforeAboutToQuit() { void AudioClient::customDeleter() {
static_cast<CheckDevicesThread*>(_checkDevicesThread)->beforeAboutToQuit(); 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<BackgroundThread*>(_checkDevicesThread)->join();
delete _checkDevicesThread;
}
if (_localInjectorsThread) {
static_cast<BackgroundThread*>(_localInjectorsThread)->join();
delete _localInjectorsThread;
}
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) { void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec << "recievedCodec:" << 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()) // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())
injectorBuffer->setParent(nullptr); injectorBuffer->setParent(nullptr);
injectorBuffer->moveToThread(&_localAudioThread); injectorBuffer->moveToThread(_localInjectorsThread);
} else { } else {
qCDebug(audioclient) << "injector exists in active list already"; qCDebug(audioclient) << "injector exists in active list already";
} }
@ -1530,8 +1560,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
_outputScratchBuffer = new int16_t[_outputPeriod]; _outputScratchBuffer = new int16_t[_outputPeriod];
// size local output mix buffer based on resampled network frame size // size local output mix buffer based on resampled network frame size
_networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); int networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
_localOutputMixBuffer = new float[_networkPeriod]; _localOutputMixBuffer = new float[networkPeriod];
int localPeriod = _outputPeriod * 2; int localPeriod = _outputPeriod * 2;
_localInjectorsStream.resizeForFrameSize(localPeriod); _localInjectorsStream.resizeForFrameSize(localPeriod);
@ -1698,7 +1728,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
} }
// prepare injectors for the next callback // 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 samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
int framesPopped = samplesPopped / AudioConstants::STEREO; int framesPopped = samplesPopped / AudioConstants::STEREO;

View file

@ -71,19 +71,6 @@ class QIODevice;
class Transform; class Transform;
class NLPacket; 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 { class AudioClient : public AbstractAudioInterface, public Dependency {
Q_OBJECT Q_OBJECT
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
@ -169,6 +156,7 @@ public:
public slots: public slots:
void start(); void start();
void stop(); void stop();
void cleanupBeforeQuit();
void handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message); void handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message);
void handleAudioDataPacket(QSharedPointer<ReceivedMessage> message); void handleAudioDataPacket(QSharedPointer<ReceivedMessage> message);
@ -184,8 +172,6 @@ public slots:
void audioMixerKilled(); void audioMixerKilled();
void toggleMute(); void toggleMute();
void beforeAboutToQuit();
virtual void setIsStereoInput(bool stereo) override; virtual void setIsStereoInput(bool stereo) override;
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
@ -242,9 +228,7 @@ protected:
AudioClient(); AudioClient();
~AudioClient(); ~AudioClient();
virtual void customDeleter() override { virtual void customDeleter() override;
deleteLater();
}
private: private:
void outputFormatChanged(); void outputFormatChanged();
@ -337,19 +321,17 @@ private:
// for network audio (used by network audio thread) // for network audio (used by network audio thread)
int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; 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) // for output audio (used by this thread)
int _outputPeriod { 0 }; int _outputPeriod { 0 };
float* _outputMixBuffer { NULL }; float* _outputMixBuffer { NULL };
int16_t* _outputScratchBuffer { 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; AudioLimiter _audioLimiter;
// Adds Reverb // Adds Reverb
@ -392,12 +374,13 @@ private:
QString _selectedCodecName; QString _selectedCodecName;
Encoder* _encoder { nullptr }; // for outbound mic stream Encoder* _encoder { nullptr }; // for outbound mic stream
QThread* _checkDevicesThread { nullptr };
RateCounter<> _silentOutbound; RateCounter<> _silentOutbound;
RateCounter<> _audioOutbound; RateCounter<> _audioOutbound;
RateCounter<> _silentInbound; RateCounter<> _silentInbound;
RateCounter<> _audioInbound; RateCounter<> _audioInbound;
QThread* _checkDevicesThread { nullptr };
QThread* _localInjectorsThread { nullptr };
}; };

View file

@ -41,7 +41,7 @@ void releaseOpenVrSystem();
static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; 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_PARENT = "Avatar";
static const char* MENU_NAME = "Vive Controllers"; static const char* MENU_NAME = "Vive Controllers";
@ -297,12 +297,12 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
// done // done
} else if (_config == Config::FeetAndHips) { } else if (_config == Config::FeetAndHips) {
_jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _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) { } else if (_config == Config::FeetHipsAndChest) {
_jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _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; _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; _calibrated = true;
} }