Commit 52da06c7 authored by Andreas Traczyk's avatar Andreas Traczyk

mediasettings: implement audio meter

- adds a level meter to media settings
- fixes issues changing audio devices
- removes calls to start/stop preview from ui thread

Change-Id: Iadf502ca4642cf0b6fdbfc85872afe362a429c6e
parent fa7d8476
/***************************************************************************
* Copyright (C) 2019 by Savoir-faire Linux *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "levelmeter.h"
#include <QPainter>
#include <QTimer>
#include <QDebug>
#include <cmath>
#include <limits>
constexpr static const int redrawIntervalMs = 16;
constexpr static const int rmsThreshold = 0.01;
LevelMeter::LevelMeter(QWidget *parent)
: QProgressBar(parent)
, rmsLevel_(0.0)
, currentLevel_(0.0)
, redrawTimer_(new QTimer(this))
{
}
LevelMeter::~LevelMeter()
{
}
void LevelMeter::start()
{
rmsLevel_ = 0;
currentLevel_ = 0;
connect(redrawTimer_, SIGNAL(timeout()), this, SLOT(redrawTimerExpired()));
redrawTimer_->start(redrawIntervalMs);
}
void LevelMeter::stop()
{
disconnect(redrawTimer_, SIGNAL(timeout()), this, SLOT(redrawTimerExpired()));
redrawTimer_->stop();
}
void LevelMeter::setLevel(float rmsLevel)
{
rmsLevel_ = rmsLevel;
update();
}
void LevelMeter::redrawTimerExpired()
{
animateLevel();
this->setValue(std::clamp(currentLevel_ * 100.0f, 0.0f, 100.0f));
update();
}
void LevelMeter::animateLevel()
{
auto distance = rmsLevel_ - currentLevel_;
auto velocity = 0.5f * (distance);
if (abs(distance) <= rmsThreshold) {
currentLevel_ = rmsLevel_;
return;
}
currentLevel_ += velocity;
}
\ No newline at end of file
/***************************************************************************
* Copyright (C) 2019 by Savoir-faire Linux *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#pragma once
#include <QTime>
#include <QProgressBar>
class LevelMeter : public QProgressBar {
Q_OBJECT;
public:
explicit LevelMeter(QWidget *parent = 0);
~LevelMeter();
public:
void start();
void stop();
void setLevel(float rmsLevel);
private slots:
void redrawTimerExpired();
private:
void animateLevel();
float rmsLevel_;
float currentLevel_;
QTimer* redrawTimer_;
};
......@@ -234,6 +234,7 @@
<ClCompile Include="contactpickeritemdelegate.cpp" />
<ClCompile Include="currentaccountcombobox.cpp" />
<ClCompile Include="aboutdialog.cpp" />
<ClCompile Include="levelmeter.cpp" />
<ClCompile Include="updatedownloaddialog.cpp" />
<ClCompile Include="downloadmanager.cpp" />
<ClCompile Include="accountitemdelegate.cpp" />
......@@ -440,6 +441,10 @@
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
</QtMoc>
<QtMoc Include="levelmeter.h">
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
</QtMoc>
<ClInclude Include="pixbufmanipulator.h" />
<QtMoc Include="ringbutton.h">
</QtMoc>
......@@ -712,4 +717,4 @@
<UserProperties UicDir=".\GeneratedFiles" RccDir=".\GeneratedFiles" Qt5Version_x0020_x64="$(DefaultQtVersion)" />
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>
\ No newline at end of file
......@@ -213,6 +213,9 @@
<ClCompile Include="contactpickeritemdelegate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="levelmeter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="aboutdialog.h">
......@@ -368,6 +371,9 @@
<QtMoc Include="contactpickeritemdelegate.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="levelmeter.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
......
......@@ -155,7 +155,11 @@ void SettingsWidget::leaveSettingsSlot()
toggleAdvancedSIPSettings();
}
QtConcurrent::run([this] { ui->currentAccountAvatar->stopBooth(); });
stopAudioMeter();
if (!LRCInstance::getActiveCalls().size()) {
QtConcurrent::run( [this] { ui->currentAccountAvatar->stopBooth(); });
}
emit NavigationRequested(ScreenEnum::CallScreen);
}
......@@ -169,6 +173,7 @@ void SettingsWidget::setSelected(Button sel)
{
switch (sel) {
case Button::accountSettingsButton:
ui->accountSettingsButton->setChecked(true);
ui->generalSettingsButton->setChecked(false);
ui->mediaSettingsButton->setChecked(false);
......@@ -176,9 +181,8 @@ void SettingsWidget::setSelected(Button sel)
return;
}
if (!LRCInstance::getActiveCalls().size()) {
QtConcurrent::run( [this] { LRCInstance::avModel().stopPreview(); });
}
stopAudioMeter();
stopPreviewing();
if (LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::SIP) {
ui->stackedWidget->setCurrentWidget(ui->currentSIPAccountSettingsScrollWidget);
......@@ -197,14 +201,14 @@ void SettingsWidget::setSelected(Button sel)
break;
case Button::generalSettingsButton:
ui->generalSettingsButton->setChecked(true);
ui->accountSettingsButton->setChecked(false);
ui->mediaSettingsButton->setChecked(false);
if (pastButton_ == sel) { return; }
if (!LRCInstance::getActiveCalls().size()) {
QtConcurrent::run([this] { LRCInstance::avModel().stopPreview(); });
}
stopAudioMeter();
stopPreviewing();
ui->stackedWidget->setCurrentWidget(ui->generalSettings);
populateGeneralSettings();
......@@ -221,6 +225,8 @@ void SettingsWidget::setSelected(Button sel)
currentDisplayedVideoDevice_.clear();
populateAVSettings();
startAudioMeter();
break;
}
......@@ -940,6 +946,13 @@ void SettingsWidget::populateAVSettings()
connect(ui->inputComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &SettingsWidget::inputdevIndexChangedSlot);
connect(&LRCInstance::avModel(), &lrc::api::AVModel::audioMeter,
[this](const std::string& id, float level) {
if (id == "audiolayer_id") {
ui->audioInputMeter->setLevel(level);
}
});
// audio output devices
disconnect(ui->outputComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &SettingsWidget::outputDevIndexChangedSlot);
......@@ -989,7 +1002,7 @@ void SettingsWidget::populateAVSettings()
this, &SettingsWidget::slotFormatBoxCurrentIndexChanged);
if (shouldReinitializePreview) {
showPreview();
startPreviewing();
}
auto encodeAccel = LRCInstance::avModel().getHardwareAcceleration();
......@@ -998,15 +1011,20 @@ void SettingsWidget::populateAVSettings()
void SettingsWidget::outputDevIndexChangedSlot(int index)
{
auto selectedOutputDeviceName = ui->outputComboBox->itemData(index, Qt::DisplayRole).toString();
LRCInstance::avModel().setOutputDevice(selectedOutputDeviceName.toStdString());
stopAudioMeter(true);
auto selectedOutputDeviceName = ui->outputComboBox->itemData(index, Qt::DisplayRole)
.toString().toStdString();
LRCInstance::avModel().setOutputDevice(selectedOutputDeviceName);
startAudioMeter(true);
}
void SettingsWidget::inputdevIndexChangedSlot(int index)
{
stopAudioMeter(true);
auto selectedInputDeviceName = ui->inputComboBox->itemData(index, Qt::DisplayRole)
.toString().toStdString();
LRCInstance::avModel().setInputDevice(selectedInputDeviceName);
startAudioMeter(true);
}
void SettingsWidget::slotDeviceBoxCurrentIndexChanged(int index)
......@@ -1016,7 +1034,7 @@ void SettingsWidget::slotDeviceBoxCurrentIndexChanged(int index)
LRCInstance::avModel().setDefaultDevice(currentDisplayedVideoDevice_);
setFormatListForDevice(currentDisplayedVideoDevice_);
if (!LRCInstance::getActiveCalls().size()) {
showPreview();
startPreviewing();
}
}
......@@ -1030,15 +1048,29 @@ void SettingsWidget::slotFormatBoxCurrentIndexChanged(int index)
LRCInstance::avModel().setDeviceSettings(settings);
}
void SettingsWidget::startVideo()
void SettingsWidget::startPreviewing()
{
LRCInstance::avModel().stopPreview();
LRCInstance::avModel().startPreview();
if (!LRCInstance::getActiveCalls().size()) {
ui->videoWidget->connectRendering();
ui->previewUnavailableLabel->hide();
ui->videoLayoutWidget->show();
QtConcurrent::run(
[this] {
LRCInstance::avModel().stopPreview();
LRCInstance::avModel().startPreview();
});
ui->videoWidget->setIsFullPreview(true);
} else {
ui->previewUnavailableLabel->show();
ui->videoLayoutWidget->hide();
}
}
void SettingsWidget::stopVideo()
void SettingsWidget::stopPreviewing()
{
LRCInstance::avModel().stopPreview();
if (!LRCInstance::getActiveCalls().size()) {
QtConcurrent::run( [this] { LRCInstance::avModel().stopPreview(); });
}
}
void SettingsWidget::toggleVideoSettings(bool enabled)
......@@ -1055,21 +1087,6 @@ void SettingsWidget::toggleVideoPreview(bool enabled)
ui->videoLayoutWidget->setVisible(enabled);
}
void SettingsWidget::showPreview()
{
ui->videoWidget->connectRendering();
if (!LRCInstance::getActiveCalls().size()) {
ui->previewUnavailableLabel->hide();
ui->videoLayoutWidget->show();
startVideo();
ui->videoWidget->setIsFullPreview(true);
} else {
ui->previewUnavailableLabel->show();
ui->videoLayoutWidget->hide();
}
}
void SettingsWidget::setFormatListForDevice(const std::string& device)
{
auto deviceCapabilities = LRCInstance::avModel().getDeviceCapabilities(device);
......@@ -1105,3 +1122,27 @@ void SettingsWidget::slotSetHardwareAccel(bool state)
{
LRCInstance::avModel().setHardwareAcceleration(state);
}
void SettingsWidget::startAudioMeter(bool blocking)
{
if (LRCInstance::getActiveCalls().size()) {
return;
}
ui->audioInputMeter->start();
auto f = [this] {
LRCInstance::avModel().startAudioDevice();
LRCInstance::avModel().setAudioMeterState(true);
};
blocking ? f() : QtConcurrent::run(f);
}
void SettingsWidget::stopAudioMeter(bool blocking)
{
if (LRCInstance::getActiveCalls().size()) {
return;
}
LRCInstance::avModel().setAudioMeterState(false);
ui->audioInputMeter->stop();
auto f = [this] { LRCInstance::avModel().stopAudioDevice(); };
blocking ? f() : QtConcurrent::run(f);
}
\ No newline at end of file
......@@ -82,11 +82,12 @@ private:
void populateGeneralSettings();
void populateAVSettings();
void setFormatListForDevice(const std::string& device);
void showPreview();
void startVideo();
void stopVideo();
void startPreviewing();
void stopPreviewing();
void toggleVideoSettings(bool enabled);
void toggleVideoPreview(bool enabled);
void startAudioMeter(bool blocking = false);
void stopAudioMeter(bool blocking = false);
QString registeredName_;
lrc::api::account::ConfProperties_t confProps_;
......
......@@ -376,7 +376,7 @@
</size>
</property>
<property name="currentIndex">
<number>0</number>
<number>3</number>
</property>
<widget class="QWidget" name="generalSettings">
<property name="sizePolicy">
......@@ -3693,6 +3693,57 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_34">
<property name="spacing">
<number>7</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="LevelMeter" name="audioInputMeter" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>5</height>
</size>
</property>
<property name="maximum" stdset="0">
<number>100</number>
</property>
<property name="value" stdset="0">
<number>-1</number>
</property>
<property name="textVisible" stdset="0">
<bool>false</bool>
</property>
<property name="format" stdset="0">
<string>%p%</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_39">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_31">
<property name="spacing">
......@@ -4204,6 +4255,12 @@
<header>photoboothwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeter</class>
<extends>QWidget</extends>
<header>levelmeter.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="ressources.qrc"/>
......
......@@ -729,3 +729,13 @@ QWidget#contactPickerWidget {
border:solid 1px transparent;
background-color: white;
}
QProgressBar#audioInputMeter {
border: 0px solid #606060;
background: lightgrey;
}
QProgressBar#audioInputMeter::chunk {
background-color: #0c79aa;
margin: 2px;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment