mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
[WIP] implement threaded level meter for input level
Issues: 1. Using 100% processor. 2. Temporary silences an active call -> reactivate after widget hide. Still greatly improves metering audio data, removing "read buffer" errors.
This commit is contained in:
parent
cbf0f2e7e0
commit
44d1c6fe74
|
@ -34,6 +34,7 @@
|
|||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMutexLocker>
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
#include <QWaitCondition>
|
||||
|
||||
|
@ -47,6 +48,8 @@
|
|||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
Audio* Audio::instance{nullptr};
|
||||
|
||||
class AudioPrivate
|
||||
{
|
||||
public:
|
||||
|
@ -86,6 +89,8 @@ public:
|
|||
Audio::PtrList outputSubscriptions;
|
||||
bool inputInitialized;
|
||||
bool outputInitialized;
|
||||
|
||||
QPointer<AudioMeter> 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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <QWidget>
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user