Compare commits

...

1 Commits

Author SHA1 Message Date
Jean-Baptiste Mardelle
0adacfd5fa Initial work to use the recent avfilter link instead of filter so that we can use more advenced fffmpeg filters like arnndn (neural noise reduction).
Still needs work (added links are not displayed on project open) and more testing
2023-08-25 14:16:01 +02:00
10 changed files with 101 additions and 33 deletions

View File

@@ -12,6 +12,7 @@ avfilter_alimiter.xml
avfilter_allpass.xml
avfilter_aphaser.xml
avfilter_apulsator.xml
avfilter_arnndn.xml
avfilter_atadenoise.xml
avfilter_avgblur.xml
avfilter_bandpass.xml

View File

@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!DOCTYPE kpartgui>
<effect tag="avfilter.arnndn" id="avfilter.arnndn" type="audio">
<name>Reduce Noise From Speech</name>
<description>Noise reduction using neural models</description>
<author>libavfilter</author>
<parameter type="fixed" name="av.m" value="%avfiltermodels/mp.rnnn">
<paramdependencies files="%avfiltermodels/mp.rnnn" >Required model files for Noise Reduction Models not found in &lt;a href="file://%folder"&gt;models folder&lt;/a&gt;. Check our &lt;a href="https://docs.kdenlive.org/en/effects_and_compositions/effect_groups/alpha_manipulation/motion_tracker.html?highlight=motion%20tracker#id2"&gt;manual&lt;/a&gt; for instructions.</paramdependencies>
</parameter>
<parameter type="constant" name="av.mix" default="1" min="-1" max="1" decimals="3">
<name>Mix level</name>
</parameter>
</effect>

View File

@@ -78,7 +78,7 @@
<group list="avfilter.flanger,avfilter.aphaser,avfilter.apulsator,sox_flanger,sox_phaser,avfilter.treble,avfilter.vibrato"><text>Modulators</text>
</group>
<group list="ladspa.9354877"><text>Noise Reduction and Audio Restoration</text>
<group list="ladspa.9354877,avfilter.arnndn"><text>Noise Reduction and Audio Restoration</text>
</group>
<group list="panner,audiomap,channelswap,mono,channelcopy,channelswap,swapchannels,audiobalance,audiopan">

View File

@@ -113,6 +113,10 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
}
bool isFixed = (type == QLatin1String("fixed"));
if (isFixed) {
if (value.contains(QLatin1Char('%'))) {
value.replace(QLatin1String("%avfiltermodels"),
QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/avfiltermodels")).absolutePath());
}
m_fixedParams[name] = value;
} else if (currentRow.type == ParamType::Position) {
int val = value.toInt();

View File

@@ -205,11 +205,14 @@ bool EffectsRepository::isPreferred(const QString &effectId) const
return m_preferred_list.contains(effectId);
}
std::unique_ptr<Mlt::Filter> EffectsRepository::getEffect(const QString &effectId) const
std::unique_ptr<Mlt::Properties> EffectsRepository::getEffect(const QString &effectId) const
{
Q_ASSERT(exists(effectId));
QString service_name = m_assets.at(effectId).mltId;
// We create the Mlt element from its name
if (effectId.startsWith(QStringLiteral("avfilter."))) {
return std::make_unique<Mlt::Link>(service_name.toLatin1().constData());
}
auto filter = std::make_unique<Mlt::Filter>(pCore->getProjectProfile(), service_name.toLatin1().constData(), nullptr);
return filter;
}

View File

@@ -24,7 +24,7 @@ public:
static std::unique_ptr<EffectsRepository> &get();
/** @brief returns a fresh instance of the given effect */
std::unique_ptr<Mlt::Filter> getEffect(const QString &effectId) const;
std::unique_ptr<Mlt::Properties> getEffect(const QString &effectId) const;
/** @brief returns true if an effect exists in MLT (bypasses the blacklist/metadata parsing) */
bool hasInternalEffect(const QString &effectId) const;
QPair<QString, QString> reloadCustom(const QString &path);

View File

@@ -92,7 +92,13 @@ std::shared_ptr<EffectItemModel> EffectItemModel::construct(std::unique_ptr<Mlt:
void EffectItemModel::plant(const std::weak_ptr<Mlt::Service> &service)
{
if (auto ptr = service.lock()) {
int ret = ptr->attach(filter());
int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.attach(getLink());
} else {
ret = ptr->attach(getFilter());
}
Q_ASSERT(ret == 0);
} else {
qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
@@ -141,7 +147,13 @@ void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service)
ptr->set("_childid", childId);
}
m_childEffects.insert(childId, effect);
int ret = ptr->attach(effect->filter());
int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.attach(effect->getLink());
} else {
ret = ptr->attach(effect->getFilter());
}
Q_ASSERT(ret == 0);
return;
}
@@ -153,7 +165,13 @@ void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service)
void EffectItemModel::unplant(const std::weak_ptr<Mlt::Service> &service)
{
if (auto ptr = service.lock()) {
int ret = ptr->detach(filter());
int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(getLink());
} else {
ret = ptr->detach(getFilter());
}
Q_ASSERT(ret == 0);
} else {
qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
@@ -167,12 +185,23 @@ void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
return;
}
if (auto ptr = service.lock()) {
int ret = ptr->detach(filter());
int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(getLink());
} else {
ret = ptr->detach(getFilter());
}
Q_ASSERT(ret == 0);
int childId = ptr->get_int("_childid");
auto effect = m_childEffects.take(childId);
if (effect && effect->isValid()) {
ptr->detach(effect->filter());
if (effect->isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(effect->getLink());
} else {
ret = ptr->detach(effect->getFilter());
}
effect.reset();
}
} else {
@@ -181,11 +210,26 @@ void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
}
}
Mlt::Filter &EffectItemModel::filter() const
Mlt::Properties &EffectItemModel::filter() const
{
return *(m_asset.get());
}
Mlt::Filter &EffectItemModel::getFilter() const
{
return *static_cast<Mlt::Filter *>(m_asset.get());
}
Mlt::Link &EffectItemModel::getLink() const
{
return *static_cast<Mlt::Link *>(m_asset.get());
}
bool EffectItemModel::isLink() const
{
return m_assetId.startsWith(QLatin1String("avfilter."));
}
bool EffectItemModel::isValid() const
{
return m_asset && m_asset->is_valid();

View File

@@ -9,6 +9,7 @@
#include "abstractmodel/treeitem.hpp"
#include "assets/model/assetparametermodel.hpp"
#include <mlt++/MltFilter.h>
#include <mlt++/MltLink.h>
class EffectStackModel;
/** @brief This represents an effect of the effectstack
@@ -37,7 +38,10 @@ public:
void unplant(const std::weak_ptr<Mlt::Service> &service) override;
void unplantClone(const std::weak_ptr<Mlt::Service> &service) override;
Mlt::Filter &filter() const;
Mlt::Properties &filter() const;
Mlt::Filter &getFilter() const;
Mlt::Link &getLink() const;
bool isLink() const;
/** @brief Return true if the effect applies only to audio */
bool isAudio() const override;

View File

@@ -367,7 +367,7 @@ bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &re
connect(effect.get(), &AssetParameterModel::showEffectZone, this, &EffectStackModel::updateEffectZones);
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
m_fadeIns.insert(effect->getId());
int duration = effect->filter().get_length() - 1;
int duration = effect->getFilter().get_length() - 1;
effect->filter().set("in", currentIn);
effect->filter().set("out", currentIn + duration);
if (effectId.startsWith(QLatin1String("fade_"))) {
@@ -380,7 +380,7 @@ bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &re
}
} else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) {
m_fadeOuts.insert(effect->getId());
int duration = effect->filter().get_length() - 1;
int duration = effect->getFilter().get_length() - 1;
int filterOut = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1;
effect->filter().set("in", filterOut - duration);
effect->filter().set("out", filterOut);
@@ -451,14 +451,14 @@ bool EffectStackModel::copyEffect(const std::shared_ptr<AbstractEffectItem> &sou
QVector<int> roles = {TimelineModel::EffectNamesRole};
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
m_fadeIns.insert(effect->getId());
int duration = effect->filter().get_length() - 1;
int duration = effect->getFilter().get_length() - 1;
int in = pCore->getItemIn(m_ownerId);
effect->filter().set("in", in);
effect->filter().set("out", in + duration);
roles << TimelineModel::FadeInRole;
} else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) {
m_fadeOuts.insert(effect->getId());
int duration = effect->filter().get_length() - 1;
int duration = effect->getFilter().get_length() - 1;
int out = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1;
effect->filter().set("in", out - duration);
effect->filter().set("out", out);
@@ -524,7 +524,7 @@ bool EffectStackModel::appendEffect(const QString &effectId, bool makeCurrent)
int inFades = 0;
int outFades = 0;
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
int duration = effect->filter().get_length() - 1;
int duration = effect->getFilter().get_length() - 1;
int in = pCore->getItemIn(m_ownerId);
effect->filter().set("in", in);
effect->filter().set("out", in + duration);
@@ -590,10 +590,10 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(leaf);
if (fadeInDuration > 0 && m_fadeIns.count(leaf->getId()) > 0) {
// Adjust fade in
int oldEffectIn = qMax(0, effect->filter().get_in());
int oldEffectOut = effect->filter().get_out();
int oldEffectIn = qMax(0, effect->getFilter().get_in());
int oldEffectOut = effect->getFilter().get_out();
qDebug() << "--previous effect: " << oldEffectIn << "-" << oldEffectOut;
int effectDuration = qMin(effect->filter().get_length() - 1, duration);
int effectDuration = qMin(effect->getFilter().get_length() - 1, duration);
if (!adjustFromEnd && (oldIn != newIn || duration != oldDuration)) {
// Clip start was resized, adjust effect in / out
Fun operation = [effect, newIn, effectDuration, logUndo]() {
@@ -687,7 +687,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
qDebug() << "// NULL Keyframes---------";
}
if (m_ownerId.type == ObjectType::TimelineTrack && !hasZone) {
int oldEffectOut = effect->filter().get_out();
int oldEffectOut = effect->getFilter().get_out();
Fun operation = [effect, out, logUndo]() {
effect->setParameter(QStringLiteral("out"), out, logUndo);
return true;
@@ -705,12 +705,12 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
PUSH_LAMBDA(reverse, undo);
}
} else if (m_ownerId.type == ObjectType::TimelineClip && effect->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) {
int oldEffectIn = qMax(0, effect->filter().get_in());
int oldEffectOut = effect->filter().get_out();
int oldEffectIn = qMax(0, effect->getFilter().get_in());
int oldEffectOut = effect->getFilter().get_out();
int newIn = pCore->getItemIn(m_ownerId);
int newOut = newIn + pCore->getItemDuration(m_ownerId) - 1;
Fun operation = [effect, newIn, newOut]() {
effect->filter().set_in_and_out(newIn, newOut);
effect->getFilter().set_in_and_out(newIn, newOut);
qDebug() << "--new effect: " << newIn << "-" << newOut;
return true;
};
@@ -719,7 +719,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
return false;
}
Fun reverse = [effect, oldEffectIn, oldEffectOut]() {
effect->filter().set_in_and_out(oldEffectIn, oldEffectOut);
effect->getFilter().set_in_and_out(oldEffectIn, oldEffectOut);
return true;
};
PUSH_LAMBDA(operation, redo);
@@ -754,7 +754,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
if (m_fadeIns.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
if (oldDuration == -1) {
oldDuration = effect->filter().get_length();
oldDuration = effect->getFilter().get_length();
}
effect->filter().set("in", in);
duration = qMin(pCore->getItemDuration(m_ownerId), duration);
@@ -803,7 +803,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
if (m_fadeOuts.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
if (oldDuration == -1) {
oldDuration = effect->filter().get_length();
oldDuration = effect->getFilter().get_length();
}
effect->filter().set("out", out);
duration = qMin(itemDuration, duration);
@@ -843,7 +843,7 @@ int EffectStackModel::getFadePosition(bool fromStart)
for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(m_fadeIns.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
return effect->filter().get_length() - 1;
return effect->getFilter().get_length() - 1;
}
}
} else {
@@ -853,7 +853,7 @@ int EffectStackModel::getFadePosition(bool fromStart)
for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(m_fadeOuts.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
return effect->filter().get_length() - 1;
return effect->getFilter().get_length() - 1;
}
}
}
@@ -908,7 +908,7 @@ void EffectStackModel::registerItem(const std::shared_ptr<TreeItem> &item)
if (effectItem->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) {
int in = pCore->getItemIn(m_ownerId);
int out = in + pCore->getItemDuration(m_ownerId) - 1;
effectItem->filter().set_in_and_out(in, out);
effectItem->getFilter().set_in_and_out(in, out);
}
if (!m_loadingExisting) {
// qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting effect in " << m_childServices.size();
@@ -1078,7 +1078,7 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
effect = EffectItemModel::construct(std::move(filter), shared_from_this(), originalDecimalPoint);
} else {
// duplicate effect
std::unique_ptr<Mlt::Filter> asset = EffectsRepository::get()->getEffect(effectId);
std::unique_ptr<Mlt::Properties> asset = EffectsRepository::get()->getEffect(effectId);
asset->inherit(*(filter));
effect = EffectItemModel::construct(std::move(asset), shared_from_this(), originalDecimalPoint);
}
@@ -1109,7 +1109,7 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
m_fadeIns.insert(effect->getId());
if (effect->filter().get_int("in") != clipIn) {
// Broken fade, fix
int filterLength = effect->filter().get_length() - 1;
int filterLength = effect->getFilter().get_length() - 1;
effect->filter().set("in", clipIn);
effect->filter().set("out", clipIn + filterLength);
}
@@ -1117,7 +1117,7 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
m_fadeOuts.insert(effect->getId());
if (effect->filter().get_int("out") != clipOut) {
// Broken fade, fix
int filterLength = effect->filter().get_length() - 1;
int filterLength = effect->getFilter().get_length() - 1;
effect->filter().set("in", clipOut - filterLength);
effect->filter().set("out", clipOut);
}

View File

@@ -44,8 +44,7 @@ ClipStabilize::ClipStabilize(const std::vector<QString> &binIds, QString filterN
if (m_filtername == QLatin1String("vidstab")) {
m_view = std::make_unique<AssetParameterView>(this);
qDebug() << "// Fetching effect: " << m_filtername;
std::unique_ptr<Mlt::Filter> asset = EffectsRepository::get()->getEffect(m_filtername);
auto prop = std::make_unique<Mlt::Properties>(asset->get_properties());
std::unique_ptr<Mlt::Properties> prop = EffectsRepository::get()->getEffect(m_filtername);
QDomElement xml = EffectsRepository::get()->getXml(m_filtername);
m_assetModel.reset(new AssetParameterModel(std::move(prop), xml, m_filtername, ObjectId()));
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/presets/"));