Commit 3a3c8aba authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix audio levels showing incorrect values, and not impacted by master effects

Related to #798.
Track audio levels still don't display the correct values, a change in MLT is required, patch will be posted soon
parent 7b7c568b
......@@ -41,6 +41,8 @@ public:
/** @brief Return description of asset */
QString getDescription(const QString &assetId) const;
/** @brief Return version of asset */
int getVersion(const QString &assetId) const;
/** @brief Returns a DomElement representing the asset's properties */
QDomElement getXml(const QString &assetId) const;
......
......@@ -326,6 +326,12 @@ template <typename AssetType> QString AbstractAssetsRepository<AssetType>::getDe
return m_assets.at(assetId).description;
}
template <typename AssetType> int AbstractAssetsRepository<AssetType>::getVersion(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).version;
}
template <typename AssetType> bool AbstractAssetsRepository<AssetType>::parseInfoFromXml(const QDomElement &currentAsset, Info &res) const
{
QString tag = currentAsset.attribute(QStringLiteral("tag"), QString());
......
......@@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "audiolevelwidget.hpp"
#include "core.h"
#include "profiles/profilemodel.hpp"
#include "iecscale.h"
#include "mlt++/Mlt.h"
#include <cmath>
......@@ -37,7 +37,7 @@ AudioLevelWidget::~AudioLevelWidget()
void AudioLevelWidget::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event)
QWidget::resizeEvent(event);
drawBackground(m_peaks.size());
}
......@@ -63,19 +63,29 @@ void AudioLevelWidget::drawBackground(int channels)
if (!newSize.isValid()) {
return;
}
newSize.setWidth(newSize.width() - m_offset - 1);
QLinearGradient gradient(0, newSize.height(), 0, 0);
gradient.setColorAt(0.0, Qt::darkGreen);
gradient.setColorAt(0.379, Qt::darkGreen);
gradient.setColorAt(0.38, Qt::green); // -20db
gradient.setColorAt(0.868, Qt::green);
gradient.setColorAt(0.869, Qt::yellow); // -2db
gradient.setColorAt(0.95, Qt::yellow);
gradient.setColorAt(0.951, Qt::red); // 0db
m_pixmap = QPixmap(QWidget::size());
m_pixmap = QPixmap(newSize);
if (m_pixmap.isNull()) {
return;
}
// Channel labels are vertical along the left.
QVector<int> dbscale;
dbscale << 0 << -5 << -10 << -15 << -20 << -25 << -30 << -35 << -40 << -50;
m_maxDb = dbscale.first();
int dbLabelCount = dbscale.size();
newSize.setWidth(newSize.width() - m_offset - 1);
QLinearGradient gradient(0, newSize.height(), 0, 0);
double gradientVal = 0.;
gradient.setColorAt(gradientVal, Qt::darkGreen);
gradientVal = IEC_ScaleMax(-12, m_maxDb);
gradient.setColorAt(gradientVal - 0.001, Qt::darkGreen);
gradient.setColorAt(gradientVal, Qt::green); // -12db
gradientVal = IEC_ScaleMax(-6, m_maxDb);
gradient.setColorAt(gradientVal - 0.001, Qt::green);
gradient.setColorAt(gradientVal, Qt::yellow); // -6db
gradientVal = IEC_ScaleMax(0, m_maxDb);
gradient.setColorAt(gradientVal - 0.001, Qt::yellow);
gradient.setColorAt(gradientVal, Qt::red); // 0db
m_pixmap.fill(Qt::transparent);
int totalWidth;
if (channels < 2) {
......@@ -91,26 +101,26 @@ void AudioLevelWidget::drawBackground(int channels)
p.setFont(font());
p.fillRect(rect, QBrush(gradient));
// Channel labels are vertical along the bottom.
QVector<int> dbscale;
dbscale << 0 << -2 << -5 << -10 << -15 << -20 << -30 << -45;
int dbLabelCount = dbscale.size();
// dB scale is vertical along the bottom
int labelHeight = fontMetrics().ascent();
int prevY = -1;
p.setPen(palette().text().color().rgb());
int y = 0;
for (int i = 0; i < dbLabelCount; i++) {
int value = dbscale.at(i);
QString label = QString::number(value);
//int labelWidth = fontMetrics().width(label);
double xf = m_pixmap.height() - pow(10.0, double(dbscale.at(i)) / 50.0) * m_pixmap.height() * 40.0 / 42;
/*if (xf + labelWidth / 2 > m_pixmap.height()) {
xf = height() - labelWidth / 2;
}*/
p.setPen(palette().dark().color());
p.drawLine(m_offset, int(xf), m_offset + totalWidth - 1, int(xf));
xf -= labelHeight * 2 / 3;
p.setPen(palette().text().color().rgb());
p.drawText(QRectF(0, xf, m_offset - 5, labelHeight), label, QTextOption(Qt::AlignRight));
int value = dbscale[i];
QString label = QString::asprintf("%d", value);
y = newSize.height() - qRound(IEC_ScaleMax(value, m_maxDb) * (double)newSize.height() + (double)labelHeight / 2.0);
if (y - labelHeight < 0) {
y = 0;
}
if (prevY < 0 || y - prevY > 2) {
p.drawText(QRectF(0, y, m_offset - 5, labelHeight), label, QTextOption(Qt::AlignRight));
prevY = y + labelHeight;
}
p.drawLine(m_offset, y + labelHeight / 2., m_offset + totalWidth - 1, y + labelHeight / 2.);
}
p.setOpacity(isEnabled() ? 1 : 0.5);
p.setPen(palette().dark().color());
// Clear space between the channels
......@@ -144,7 +154,7 @@ void AudioLevelWidget::setAudioValues(const QVector<double> &values)
drawBackground(values.size());
} else {
for (int i = 0; i < m_values.size(); i++) {
m_peaks[i] -= .003;
m_peaks[i] -= .2;
if (m_values.at(i) > m_peaks.at(i)) {
m_peaks[i] = m_values.at(i);
}
......@@ -185,8 +195,9 @@ void AudioLevelWidget::paintEvent(QPaintEvent *pe)
if (m_values.at(i) >= 100) {
continue;
}
//int val = (50 + m_values.at(i)) / 150.0 * rect.height();
p.fillRect(m_offset + i * (m_channelWidth + m_channelDistance) + 1, 0, m_channelFillWidth, height() - int(m_values.at(i) * rect.height()), palette().dark());
p.fillRect(m_offset + i * (m_channelWidth + m_channelDistance) + 1, height() - int(m_peaks.at(i) * rect.height()), m_channelFillWidth, 1, palette().text());
int val = IEC_ScaleMax(m_values.at(i), m_maxDb) * rect.height();
int peak = IEC_ScaleMax(m_peaks.at(i), m_maxDb) * rect.height();
p.fillRect(m_offset + i * (m_channelWidth + m_channelDistance) + 1, 0, m_channelFillWidth, rect.height() - val, palette().dark());
p.fillRect(m_offset + i * (m_channelWidth + m_channelDistance) + 1, rect.height() - peak, m_channelFillWidth, 1, palette().text());
}
}
......@@ -34,6 +34,7 @@ private:
QPixmap m_pixmap;
QVector<double> m_peaks;
QVector<double> m_values;
int m_maxDb;
int m_channelWidth;
int m_channelDistance;
int m_channelFillWidth;
......
/*
SPDX-FileCopyrightText: 2015 Meltytech, LLC
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef IECSCALE_H
#define IECSCALE_H
//----------------------------------------------------------------------------
// IEC standard dB scaling -- as borrowed from meterbridge (c) Steve Harris
static inline double IEC_Scale(double dB)
{
double fScale = 1.0f;
if (dB < -70.0f)
fScale = 0.0f;
else if (dB < -60.0f)
fScale = (dB + 70.0f) * 0.0025f;
else if (dB < -50.0f)
fScale = (dB + 60.0f) * 0.005f + 0.025f;
else if (dB < -40.0)
fScale = (dB + 50.0f) * 0.0075f + 0.075f;
else if (dB < -30.0f)
fScale = (dB + 40.0f) * 0.015f + 0.15f;
else if (dB < -20.0f)
fScale = (dB + 30.0f) * 0.02f + 0.3f;
else if (dB < -0.001f || dB > 0.001f) /* if (dB < 0.0f) */
fScale = (dB + 20.0f) * 0.025f + 0.5f;
return fScale;
}
static inline double IEC_ScaleMax(double dB, double max)
{
return IEC_Scale(dB) / IEC_Scale(max);
}
#endif // IECSCALE_H
......@@ -9,6 +9,7 @@
#include "mainwindow.h"
#include "mixerwidget.hpp"
#include "timeline2/model/timelineitemmodel.hpp"
#include "effects/effectsrepository.hpp"
#include "mlt++/MltService.h"
#include "mlt++/MltTractor.h"
......@@ -29,6 +30,7 @@ MixerManager::MixerManager(QWidget *parent)
, m_visibleMixerManager(false)
, m_expandedWidth(-1)
, m_recommendedWidth(300)
, m_filterIsV2(false)
{
m_masterBox = new QHBoxLayout;
setContentsMargins(0, 0, 0, 0);
......@@ -51,6 +53,11 @@ MixerManager::MixerManager(QWidget *parent)
setLayout(m_box);
}
void MixerManager::checkAudioLevelVersion()
{
m_filterIsV2 = EffectsRepository::get()->exists(QStringLiteral("audiolevel")) && EffectsRepository::get()->getVersion(QStringLiteral("audiolevel")) > 100;
}
void MixerManager::registerTrack(int tid, std::shared_ptr<Mlt::Tractor> service, const QString &trackTag, const QString &trackName)
{
if (m_mixers.count(tid) > 0) {
......@@ -62,9 +69,9 @@ void MixerManager::registerTrack(int tid, std::shared_ptr<Mlt::Tractor> service,
m_model->setTrackProperty(id, "hide", mute ? QStringLiteral("1") : QStringLiteral("3"));
});
if (m_visibleMixerManager) {
mixer->connectMixer(!KdenliveSettings::mixerCollapse());
mixer->connectMixer(!KdenliveSettings::mixerCollapse(), m_filterIsV2);
}
connect(this, &MixerManager::updateLevels, mixer.get(), &MixerWidget::updateAudioLevel);
connect(pCore.get(), &Core::updateMixerLevels, mixer.get(), &MixerWidget::updateAudioLevel);
connect(this, &MixerManager::clearMixers, mixer.get(), &MixerWidget::clear);
connect(mixer.get(), &MixerWidget::toggleSolo, this, [&](int trid, bool solo) {
if (!solo) {
......@@ -163,9 +170,8 @@ void MixerManager::setModel(std::shared_ptr<TimelineItemModel> model)
m_model->tractor()->set("hide", mute ? 3 : 1);
});
if (m_visibleMixerManager) {
m_masterMixer->connectMixer(true);
m_masterMixer->connectMixer(true, m_filterIsV2);
}
connect(this, &MixerManager::updateLevels, m_masterMixer.get(), &MixerWidget::updateAudioLevel);
connect(this, &MixerManager::clearMixers, m_masterMixer.get(), &MixerWidget::clear);
m_masterBox->addWidget(m_masterMixer.get());
if (KdenliveSettings::mixerCollapse()) {
......@@ -184,10 +190,10 @@ void MixerManager::connectMixer(bool doConnect)
{
m_visibleMixerManager = doConnect;
for (const auto &item : m_mixers) {
item.second->connectMixer(m_visibleMixerManager && !KdenliveSettings::mixerCollapse());
item.second->connectMixer(m_visibleMixerManager && !KdenliveSettings::mixerCollapse(), m_filterIsV2);
}
if (m_masterMixer != nullptr) {
m_masterMixer->connectMixer(m_visibleMixerManager);
m_masterMixer->connectMixer(m_visibleMixerManager, m_filterIsV2);
}
}
......
......@@ -38,6 +38,8 @@ public:
void pauseMonitoring(bool pause);
/** @brief Release the timeline model ownership */
void unsetModel();
/** @brief Some features rely on a specific version of MLT's audiolevel filter, so check it */
void checkAudioLevelVersion();
public slots:
void recordStateChanged(int tid, bool recording);
......@@ -69,5 +71,6 @@ private:
int m_expandedWidth;
QVector <int> m_soloMuted;
int m_recommendedWidth;
bool m_filterIsV2;
};
......@@ -8,6 +8,7 @@
#include "audiolevelwidget.hpp"
#include "capture/mediacapture.h"
#include "core.h"
#include "iecscale.h"
#include "kdenlivesettings.h"
#include "mixermanager.hpp"
#include "mlt++/MltEvent.h"
......@@ -31,29 +32,6 @@
#include <klocalizedstring.h>
#include <utility>
static inline double IEC_Scale(double dB)
{
dB = log10(dB) * 20.0;
double fScale = 1.0;
if (dB < -70.0)
fScale = 0.0;
else if (dB < -60.0)
fScale = (dB + 70.0) * 0.0025;
else if (dB < -50.0)
fScale = (dB + 60.0) * 0.005 + 0.025;
else if (dB < -40.0)
fScale = (dB + 50.0) * 0.0075 + 0.075;
else if (dB < -30.0)
fScale = (dB + 40.0) * 0.015 + 0.15;
else if (dB < -20.0)
fScale = (dB + 30.0) * 0.02 + 0.3;
else if (dB < -0.001 || dB > 0.001) /* if (dB < 0.0f) */
fScale = (dB + 20.0) * 0.025 + 0.5;
return fScale;
}
static inline int fromDB(double level)
{
int value = 60;
......@@ -74,10 +52,28 @@ void MixerWidget::property_changed( mlt_service , MixerWidget *widget, mlt_event
if (!widget->m_levels.contains(pos)) {
QVector<double> levels;
for (int i = 0; i < widget->m_channels; i++) {
levels << IEC_Scale(mlt_properties_get_double(filter_props, QString("_audio_level.%1").arg(i).toUtf8().constData()));
// NOTE: this is an approximation. To get the real peak level, we need version 2 of audiolevel MLT filter, see property_changedV2
levels << log10(mlt_properties_get_double(filter_props, QString("_audio_level.%1").arg(i).toUtf8().constData()) / 1.18) * 20;
}
widget->m_levels[pos] = std::move(levels);
if (widget->m_levels.size() > widget->m_maxLevels) {
widget->m_levels.erase(widget->m_levels.begin());
}
}
}
}
void MixerWidget::property_changedV2( mlt_service , MixerWidget *widget, mlt_event_data data )
{
if (widget && !strcmp(Mlt::EventData(data).to_string(), "_position")) {
mlt_properties filter_props = MLT_FILTER_PROPERTIES( widget->m_monitorFilter->get_filter());
int pos = mlt_properties_get_int(filter_props, "_position");
if (!widget->m_levels.contains(pos)) {
QVector<double> levels;
for (int i = 0; i < widget->m_channels; i++) {
levels << mlt_properties_get_double(filter_props, QString("_audio_level.%1").arg(i).toUtf8().constData());
}
widget->m_levels[pos] = std::move(levels);
//{IEC_Scale(mlt_properties_get_double(filter_props, "_audio_level.0")), IEC_Scale(mlt_properties_get_double(filter_props, "_audio_level.1"))};
if (widget->m_levels.size() > widget->m_maxLevels) {
widget->m_levels.erase(widget->m_levels.begin());
}
......@@ -228,6 +224,7 @@ void MixerWidget::buildUI(Mlt::Tractor *service, const QString &trackName)
m_monitorFilter.reset(new Mlt::Filter(service->get_profile(), "audiolevel"));
if (m_monitorFilter->is_valid()) {
m_monitorFilter->set("iec_scale", 0);
m_monitorFilter->set("peak", 1);
service->attach(*m_monitorFilter.get());
}
}
......@@ -492,10 +489,10 @@ void MixerWidget::gotRecLevels(QVector<qreal>levels)
m_audioMeterWidget->setAudioValues({-100, -100});
break;
case 1:
m_audioMeterWidget->setAudioValues({IEC_Scale(levels[0]), -100});
m_audioMeterWidget->setAudioValues({IEC_Scale(log10(levels[0]) * 20.0), -100});
break;
default:
m_audioMeterWidget->setAudioValues({IEC_Scale(levels[0]), IEC_Scale(levels[1])});
m_audioMeterWidget->setAudioValues({IEC_Scale(log10(levels[0]) * 20.0), IEC_Scale(log10(levels[1]) * 20.0)});
break;
}
}
......@@ -531,16 +528,24 @@ void MixerWidget::setRecordState(bool recording)
updateLabel();
}
void MixerWidget::connectMixer(bool doConnect)
void MixerWidget::connectMixer(bool doConnect, bool filterV2)
{
if (doConnect) {
if (m_listener == nullptr) {
m_listener = m_monitorFilter->listen("property-changed", this, reinterpret_cast<mlt_listener>(property_changed));
if (m_tid == -1) {
// Master level
connect(pCore.get(), &Core::audioLevelsAvailable, m_audioMeterWidget.get(), &AudioLevelWidget::setAudioValues);
} else if (m_listener == nullptr) {
m_listener = m_monitorFilter->listen("property-changed", this, filterV2 ? reinterpret_cast<mlt_listener>(property_changedV2) : reinterpret_cast<mlt_listener>(property_changed));
}
} else {
delete m_listener;
m_listener = nullptr;
if (m_tid == -1) {
disconnect(pCore.get(), &Core::audioLevelsAvailable, m_audioMeterWidget.get(), &AudioLevelWidget::setAudioValues);
} else {
delete m_listener;
m_listener = nullptr;
}
}
pauseMonitoring(!doConnect);
}
void MixerWidget::pauseMonitoring(bool pause)
......
......@@ -43,6 +43,7 @@ public:
/** @brief discard stored audio values */
void clear();
static void property_changed( mlt_service , MixerWidget *self, mlt_event_data data );
static void property_changedV2( mlt_service , MixerWidget *widget, mlt_event_data data );
void setTrackName(const QString &name);
void setMute(bool mute);
/** @brief Returns true if track is muted
......@@ -52,7 +53,7 @@ public:
* */
void unSolo();
/** @brief Connect the mixer widgets to the correspondent filters */
void connectMixer(bool doConnect);
void connectMixer(bool doConnect, bool filterV2);
/** @brief Disable/enable monitoring by disabling/enabling filter */
void pauseMonitoring(bool pause);
......
......@@ -146,15 +146,16 @@ void Core::initGUI(bool inSandbox, const QString &MltPath, const QUrl &Url, cons
m_library = new LibraryWidget(m_projectManager, m_mainWindow);
m_subtitleWidget = new SubtitleEdit(m_mainWindow);
m_mixerWidget = new MixerManager(m_mainWindow);
m_textEditWidget = new TextBasedEdit(m_mainWindow);
m_timeRemapWidget = new TimeRemap(m_mainWindow);
connect(m_library, SIGNAL(addProjectClips(QList<QUrl>)), m_mainWindow->getBin(), SLOT(droppedUrls(QList<QUrl>)));
connect(this, &Core::updateLibraryPath, m_library, &LibraryWidget::slotUpdateLibraryPath);
m_monitorManager = new MonitorManager(this);
m_mixerWidget = new MixerManager(m_mainWindow);
connect(m_capture.get(), &MediaCapture::recordStateChanged, m_mixerWidget, &MixerManager::recordStateChanged);
connect(m_mixerWidget, &MixerManager::updateRecVolume, m_capture.get(), &MediaCapture::setAudioVolume);
m_monitorManager = new MonitorManager(this);
connect(m_monitorManager, &MonitorManager::cleanMixer, m_mixerWidget, &MixerManager::clearMixers);
connect(m_subtitleWidget, &SubtitleEdit::addSubtitle, m_mainWindow, &MainWindow::slotAddSubtitle);
connect(m_subtitleWidget, &SubtitleEdit::cutSubtitle, this, [this](int id, int cursorPos) {
if (m_guiConstructed && m_mainWindow->getCurrentTimeline()->controller()) {
......@@ -162,7 +163,6 @@ void Core::initGUI(bool inSandbox, const QString &MltPath, const QUrl &Url, cons
}
});
// The MLT Factory will be initiated there, all MLT classes will be usable only after this
if (inSandbox) {
// In a sandbox enviroment we need to search some paths recursively
......@@ -176,6 +176,7 @@ void Core::initGUI(bool inSandbox, const QString &MltPath, const QUrl &Url, cons
// Open connection with Mlt
m_mainWindow->init(MltPath);
}
m_mixerWidget->checkAudioLevelVersion();
m_projectItemModel->buildPlaylist();
// load the profiles from disk
ProfileRepository::get()->refresh();
......@@ -1219,3 +1220,4 @@ void Core::loadTimelinePreview(const QString &chunks, const QString &dirty, int
{
pCore->window()->getMainTimeline()->controller()->loadPreview(chunks, dirty, enablePreview, playlist);
}
......@@ -277,7 +277,8 @@ public:
/** @brief Get the frame size of the clip above a composition */
const QSize getCompositionSizeOnTrack(const ObjectId &id);
void loadTimelinePreview(const QString &chunks, const QString &dirty, int enablePreview, Mlt::Playlist &playlist);
/** @brief Returns true if the audio mixer widget is visible */
bool audioMixerVisible{false};
QString packageType() { return m_packageType; };
private:
......@@ -371,4 +372,8 @@ signals:
void updatePalette();
/** @brief Emitted when a clip is resized (to handle clip monitor inserted zones) */
void clipInstanceResized(const QString &binId);
/** @brief Contains the project audio levels */
void audioLevelsAvailable(const QVector<double>& levels);
/** @brief A frame was displayed in monitor, update audio mixer */
void updateMixerLevels(int pos);
};
......@@ -478,6 +478,8 @@ void MainWindow::init(const QString &mltPath)
addAction(QStringLiteral("audiomixer_button"), showMixer);
connect(m_mixerDock, &QDockWidget::visibilityChanged, this, [&, showMixer](bool visible) {
pCore->mixer()->connectMixer(visible);
pCore->audioMixerVisible = visible;
m_projectMonitor->displayAudioMonitor(m_projectMonitor->isActive());
showMixer->setChecked(visible);
});
connect(showMixer, &QAction::triggered, this, [&]() {
......@@ -2330,12 +2332,7 @@ void MainWindow::connectDocument()
});
connect(pCore->library(), &LibraryWidget::saveTimelineSelection, getMainTimeline()->controller(), &TimelineController::saveTimelineSelection,
Qt::UniqueConnection);
connect(pCore->monitorManager(), &MonitorManager::frameDisplayed, [&](const SharedFrame &frame) {
emit pCore->mixer()->updateLevels(frame.get_position());
//QMetaObject::invokeMethod(this, "setAudioValues", Qt::QueuedConnection, Q_ARG(const QVector<int> &, levels));
});
connect(pCore->mixer(), &MixerManager::purgeCache, m_projectMonitor, &Monitor::purgeCache);
getMainTimeline()->controller()->clipActions = kdenliveCategoryMap.value(QStringLiteral("timelineselection"))->actions();
connect(m_projectMonitor, &Monitor::zoneUpdated, project, [&](const QPoint &) { project->setModified(); });
......
......@@ -424,6 +424,9 @@ Monitor::Monitor(Kdenlive::MonitorId id, MonitorManager *manager, QWidget *paren
m_audioMeterWidget->setVisibility(false);
} else {
m_audioMeterWidget->setVisibility((KdenliveSettings::monitoraudio() & m_id) != 0);
if (id == Kdenlive::ProjectMonitor) {
connect(m_audioMeterWidget, &MonitorAudioLevel::audioLevelsAvailable, pCore.get(), &Core::audioLevelsAvailable);
}
}
// Trimming tool bar buttons
......@@ -2021,10 +2024,13 @@ void Monitor::updateAudioForAnalysis()
void Monitor::onFrameDisplayed(const SharedFrame &frame)
{
emit m_monitorManager->frameDisplayed(frame);
if (m_id == Kdenlive::ProjectMonitor) {
emit pCore->updateMixerLevels(frame.get_position());
}
if (!m_glMonitor->checkFrameNumber(frame.get_position(), m_playAction->isActive())) {
updatePlayAction(false);
}
emit m_monitorManager->frameDisplayed(frame);
}
void Monitor::checkDrops()
......@@ -2403,7 +2409,7 @@ void Monitor::slotSwitchAudioMonitor()
void Monitor::displayAudioMonitor(bool isActive)
{
bool enable = isActive && ((KdenliveSettings::monitoraudio() & m_id) != 0);
bool enable = isActive && ((KdenliveSettings::monitoraudio() & m_id) != 0 || (m_id == Kdenlive::ProjectMonitor && pCore->audioMixerVisible));
if (enable) {
connect(m_monitorManager, &MonitorManager::frameDisplayed, m_audioMeterWidget, &ScopeWidget::onNewFrame, Qt::UniqueConnection);
} else {
......
......@@ -8,6 +8,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "monitoraudiolevel.h"
#include "core.h"
#include "profiles/profilemodel.hpp"
#include "audiomixer/iecscale.h"
#include "mlt++/Mlt.h"
......@@ -18,16 +19,6 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QPainter>
#include <memory>
const double log_factor = 1.0 / log10(1.0 / 127);
static inline double levelToDB(double level)
{
if (level <= 0) {
return -100;
}
return 100 * (1.0 - log10(level) * log_factor);
}
MonitorAudioLevel::MonitorAudioLevel(int height, QWidget *parent)
: ScopeWidget(parent)
, audioChannels(2)
......@@ -37,13 +28,8 @@ MonitorAudioLevel::MonitorAudioLevel(int height, QWidget *parent)
, m_channelFillHeight(m_channelHeight)
{
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_filter = std::make_unique<Mlt::Filter>(pCore->getCurrentProfile()->profile(), "audiolevel");
if (!m_filter->is_valid()) {
isValid = false;
return;
}
m_filter->set("iec_scale", 0);
isValid = true;
connect(this, &MonitorAudioLevel::audioLevelsAvailable, this, &MonitorAudioLevel::setAudioValues);
}
MonitorAudioLevel::~MonitorAudioLevel()
......@@ -55,23 +41,25 @@ void MonitorAudioLevel::refreshScope(const QSize & /*size*/, bool /*full*/)
while (m_queue.count() > 0) {
sFrame = m_queue.pop();
if (sFrame.is_valid() && sFrame.get_audio_samples() > 0) {
mlt_audio_format format = mlt_audio_s16;
int channels = sFrame.get_audio_channels();
int frequency = sFrame.get_audio_frequency();
int samples = sFrame.get_audio_samples();
Mlt::Frame mFrame = sFrame.clone(true, false, false);
m_filter->process(mFrame);
mFrame.get_audio(format, frequency, channels, samples);
if (samples == 0) {
// There was an error processing audio from frame
continue;
}
QVector<int> levels;
for (int i = 0; i < audioChannels; i++) {
QString s = QStringLiteral("meta.media.audio_level.%1").arg(i);
levels << int(levelToDB(mFrame.get_double(s.toLatin1().constData())));
QVector<double> levels;
const int16_t* audio = sFrame.get_audio();
for ( int c = 0; c < channels; c++ ) {
int16_t peak = 0;
const int16_t* p = audio + c;
for ( int s = 0; s < samples; s++ ) {
int16_t sample = abs( *p );
if (sample > peak) peak = sample;
p += channels;
}