diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index abf0964f6..44ac4ad2e 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #endif +Audio* Audio::instance{nullptr}; + class AudioPrivate { public: @@ -86,6 +89,8 @@ public: Audio::PtrList outputSubscriptions; bool inputInitialized; bool outputInitialized; + + QPointer mAudioMeter; }; /** @@ -134,7 +139,115 @@ private: ALuint mSource; }; -Audio* Audio::instance{nullptr}; +class AudioMeter : public QThread +{ +public: + AudioMeter() + : mActive(false) + { + connect(this, &AudioMeter::finished, this, &AudioMeter::deleteLater); + } + + void waitForData(QMutex* condition) + { + mCheckGainChanged.wait(condition); + } + + void monitorFrame() + { + mDoMonitoring.wakeAll(); + } + +private: + void run() override final + { + static const int framesize = AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; + + Audio& audio = Audio::getInstance(); + + mNewMaxGain = 0.f; + mActive = true; + + while (mActive) { + int16_t buff[framesize] = {0}; + if (audio.tryCaptureSamples(buff, AUDIO_FRAME_SAMPLE_COUNT)) { + mMeterLock.lock(); + mNewMaxGain = 0.f; + for (int i = 0; i < framesize; ++i) { + mNewMaxGain = qMax(mNewMaxGain, qAbs(buff[i]) / 32767.0); + } + mMeterLock.unlock(); + } else if (mNewMaxGain > 0.f) { + mNewMaxGain -= 0.01f; + } + + mMeterLock.lock(); + mCheckGainChanged.wakeAll(); + mDoMonitoring.wait(&mMeterLock); + mMeterLock.unlock(); + } + } + +public: + QMutex mMeterLock; + QWaitCondition mDoMonitoring; + QWaitCondition mCheckGainChanged; + bool mActive; + qreal mNewMaxGain; +}; + +AudioMeterListener::AudioMeterListener(AudioMeter* measureThread) + : mActive(false) + , mAudioMeter(measureThread) +{ + assert(mAudioMeter); +} + +void AudioMeterListener::start() +{ + QThread* listener = new QThread; + connect(listener, &QThread::started, this, &AudioMeterListener::doListen); + connect(listener, &QThread::finished, listener, &QThread::deleteLater); + moveToThread(listener); + + listener->start(); + mAudioMeter->start(); +} + +void AudioMeterListener::stop() +{ + mActive = false; +} + +void AudioMeterListener::doListen() +{ + mMaxGain = 0.f; + mActive = true; + + QMutexLocker locker(&mAudioMeter->mMeterLock); + while (mActive) { + mAudioMeter->waitForData(locker.mutex()); + + locker.unlock(); + //qDebug() << "GAIN:" << mAudioMeter->mNewMaxGain << "/" << mMaxGain; + if (mAudioMeter->mNewMaxGain != mMaxGain) { + if (mAudioMeter->mNewMaxGain > mMaxGain) + { + mMaxGain = mAudioMeter->mNewMaxGain; + emit gainChanged(mMaxGain); + } else if (mMaxGain > 0.02f) { + mMaxGain -= 0.0005f; + emit gainChanged(mMaxGain); + } + } + locker.relock(); + + mAudioMeter->monitorFrame(); + } + + mAudioMeter->mActive = false; +} + /** Returns the singleton's instance. Will construct on first call. @@ -149,6 +262,14 @@ Audio& Audio::getInstance() return *instance; } +AudioMeterListener* Audio::createAudioMeterListener() const +{ + if (!d->mAudioMeter) + d->mAudioMeter = new AudioMeter; + + return new AudioMeterListener(d->mAudioMeter); +} + Audio::Audio() : d(new AudioPrivate) { diff --git a/src/audio/audio.h b/src/audio/audio.h index 52a8f4245..c981b9564 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -28,6 +28,8 @@ struct Tox; class AudioFilterer; +class AudioMeter; +class AudioMeterListener; class AudioPrivate; // Public default audio settings @@ -49,6 +51,8 @@ public: public: void startAudioThread(); + AudioMeterListener* createAudioMeterListener() const; + qreal outputVolume(); void setOutputVolume(qreal volume); @@ -105,4 +109,25 @@ private: AudioPrivate* d; }; +class AudioMeterListener : public QObject +{ + Q_OBJECT +public: + explicit AudioMeterListener(AudioMeter* measureThread); + + void start(); + void stop(); + +signals: + void gainChanged(qreal newMaxGain); + +private slots: + void doListen(); + +private: + bool mActive; + AudioMeter* mAudioMeter; + qreal mMaxGain; +}; + #endif // AUDIO_H diff --git a/src/widget/tool/micfeedbackwidget.cpp b/src/widget/tool/micfeedbackwidget.cpp index 3295ec69f..f18c59219 100644 --- a/src/widget/tool/micfeedbackwidget.cpp +++ b/src/widget/tool/micfeedbackwidget.cpp @@ -24,7 +24,6 @@ MicFeedbackWidget::MicFeedbackWidget(QWidget *parent) : QWidget(parent) - , timerId(0) { setFixedHeight(20); } @@ -59,41 +58,21 @@ void MicFeedbackWidget::paintEvent(QPaintEvent*) } } -void MicFeedbackWidget::timerEvent(QTimerEvent*) -{ - const int framesize = AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; - int16_t buff[framesize] = {0}; - - if (Audio::getInstance().tryCaptureSamples(buff, AUDIO_FRAME_SAMPLE_COUNT)) - { - double max = 0; - - for (int i = 0; i < framesize; ++i) - max = std::max(max, fabs(buff[i] / 32767.0)); - - if (max > current) - current = max; - else - current -= 0.05; - - update(); - } - else if (current > 0) - { - current -= 0.01; - } -} - void MicFeedbackWidget::showEvent(QShowEvent*) { - timerId = startTimer(60); + mMeterListener = Audio::getInstance().createAudioMeterListener(); + connect(mMeterListener, &AudioMeterListener::gainChanged, this, &MicFeedbackWidget::onGainMetered); + mMeterListener->start(); } void MicFeedbackWidget::hideEvent(QHideEvent*) { - if (timerId != 0) - { - killTimer(timerId); - timerId = 0; - } + mMeterListener->stop(); +} + +void MicFeedbackWidget::onGainMetered(qreal value) +{ + current = value; + //qDebug("Gain metered at %.3f", current); + update(); } diff --git a/src/widget/tool/micfeedbackwidget.h b/src/widget/tool/micfeedbackwidget.h index 188c7f27d..7c900b456 100644 --- a/src/widget/tool/micfeedbackwidget.h +++ b/src/widget/tool/micfeedbackwidget.h @@ -22,6 +22,8 @@ #include +class AudioMeterListener; + class MicFeedbackWidget : public QWidget { Q_OBJECT @@ -30,13 +32,15 @@ public: protected: void paintEvent(QPaintEvent* event) override; - void timerEvent(QTimerEvent* event) override; void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; +private slots: + void onGainMetered(qreal value); + private: - double current; - int timerId; + qreal current; + AudioMeterListener* mMeterListener; }; #endif // MICFEEDBACKWIDGET_H