Commit 9ca64d5f authored by Max Kellermann's avatar Max Kellermann

filter/Chain: eliminate, just use a chain of TwoFilters instead

The ChainFilter class is extremely complicated code, and will grow to be even more complicated when the Filter interface gets extended. Let's just remove it; we can easily chain many TwoFilters instead.
parent bd79354f
......@@ -21,27 +21,29 @@
#include "Factory.hxx"
#include "Prepared.hxx"
#include "plugins/AutoConvertFilterPlugin.hxx"
#include "plugins/ChainFilterPlugin.hxx"
#include "plugins/TwoFilters.hxx"
#include "util/IterableSplitString.hxx"
#include <string>
static void
filter_chain_append_new(PreparedFilter &chain, FilterFactory &factory,
filter_chain_append_new(std::unique_ptr<PreparedFilter> &chain,
FilterFactory &factory,
std::string_view template_name)
{
/* using the AutoConvert filter just in case the specified
filter plugin does not support the exact input format */
filter_chain_append(chain, template_name,
/* unfortunately, MakeFilter() wants a
null-terminated string, so we need to
copy it here */
autoconvert_filter_new(factory.MakeFilter(std::string(template_name).c_str())));
chain = ChainFilters(std::move(chain),
/* unfortunately, MakeFilter() wants a
null-terminated string, so we need to
copy it here */
autoconvert_filter_new(factory.MakeFilter(std::string(template_name).c_str())),
template_name);
}
void
filter_chain_parse(PreparedFilter &chain,
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
FilterFactory &factory,
const char *spec)
{
......
......@@ -20,6 +20,8 @@
#ifndef MPD_FILTER_LOAD_CHAIN_HXX
#define MPD_FILTER_LOAD_CHAIN_HXX
#include <memory>
class FilterFactory;
class PreparedFilter;
......@@ -35,7 +37,7 @@ class PreparedFilter;
* @param spec the filter chain specification
*/
void
filter_chain_parse(PreparedFilter &chain,
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
FilterFactory &factory,
const char *spec);
......
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "ChainFilterPlugin.hxx"
#include "filter/Filter.hxx"
#include "filter/Prepared.hxx"
#include "pcm/AudioFormat.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StringBuffer.hxx"
#include "util/RuntimeError.hxx"
#include <cassert>
#include <list>
#include <memory>
#include <string>
class ChainFilter final : public Filter {
struct Child {
std::unique_ptr<Filter> filter;
explicit Child(std::unique_ptr<Filter> &&_filter) noexcept
:filter(std::move(_filter)) {}
};
std::list<Child> children;
/**
* The child which will be flushed in the next Flush() call.
*/
std::list<Child>::iterator flushing = children.end();
public:
explicit ChainFilter(AudioFormat _audio_format)
:Filter(_audio_format) {}
void Append(std::unique_ptr<Filter> filter) noexcept {
assert(out_audio_format.IsValid());
out_audio_format = filter->GetOutAudioFormat();
assert(out_audio_format.IsValid());
children.emplace_back(std::move(filter));
RewindFlush();
}
/* virtual methods from class Filter */
void Reset() noexcept override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
ConstBuffer<void> Flush() override;
private:
void RewindFlush() {
flushing = children.begin();
}
};
class PreparedChainFilter final : public PreparedFilter {
struct Child {
const std::string name;
std::unique_ptr<PreparedFilter> filter;
Child(std::string_view _name,
std::unique_ptr<PreparedFilter> _filter)
:name(_name), filter(std::move(_filter)) {}
Child(const Child &) = delete;
Child &operator=(const Child &) = delete;
std::unique_ptr<Filter> Open(const AudioFormat &prev_audio_format);
};
std::list<Child> children;
public:
void Append(std::string_view name,
std::unique_ptr<PreparedFilter> filter) noexcept {
children.emplace_back(name, std::move(filter));
}
/* virtual methods from class PreparedFilter */
std::unique_ptr<Filter> Open(AudioFormat &af) override;
};
std::unique_ptr<Filter>
PreparedChainFilter::Child::Open(const AudioFormat &prev_audio_format)
{
AudioFormat conv_audio_format = prev_audio_format;
auto new_filter = filter->Open(conv_audio_format);
if (conv_audio_format != prev_audio_format)
throw FormatRuntimeError("Audio format not supported by filter '%s': %s",
name.c_str(),
ToString(prev_audio_format).c_str());
return new_filter;
}
std::unique_ptr<Filter>
PreparedChainFilter::Open(AudioFormat &in_audio_format)
{
auto chain = std::make_unique<ChainFilter>(in_audio_format);
for (auto &child : children) {
AudioFormat audio_format = chain->GetOutAudioFormat();
chain->Append(child.Open(audio_format));
}
return chain;
}
void
ChainFilter::Reset() noexcept
{
RewindFlush();
for (auto &child : children)
child.filter->Reset();
}
template<typename I>
static ConstBuffer<void>
ApplyFilterChain(I begin, I end, ConstBuffer<void> src)
{
for (auto i = begin; i != end; ++i)
/* feed the output of the previous filter as input
into the current one */
src = i->filter->FilterPCM(src);
return src;
}
ConstBuffer<void>
ChainFilter::FilterPCM(ConstBuffer<void> src)
{
RewindFlush();
/* return the output of the last filter */
return ApplyFilterChain(children.begin(), children.end(), src);
}
ConstBuffer<void>
ChainFilter::Flush()
{
for (auto end = children.end(); flushing != end; ++flushing) {
auto data = flushing->filter->Flush();
if (!data.IsNull())
return ApplyFilterChain(std::next(flushing), end,
data);
}
return nullptr;
}
std::unique_ptr<PreparedFilter>
filter_chain_new() noexcept
{
return std::make_unique<PreparedChainFilter>();
}
void
filter_chain_append(PreparedFilter &_chain, std::string_view name,
std::unique_ptr<PreparedFilter> filter) noexcept
{
auto &chain = (PreparedChainFilter &)_chain;
chain.Append(name, std::move(filter));
}
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/** \file
*
* A filter chain is a container for several filters. They are
* chained together, i.e. called in a row, one filter passing its
* output to the next one.
*/
#ifndef MPD_FILTER_CHAIN_HXX
#define MPD_FILTER_CHAIN_HXX
#include <memory>
#include <string_view>
class PreparedFilter;
/**
* Creates a new filter chain.
*/
std::unique_ptr<PreparedFilter>
filter_chain_new() noexcept;
/**
* Appends a new filter at the end of the filter chain. You must call
* this function before the first filter_open() call.
*
* @param chain the filter chain created with filter_chain_new()
* @param filter the filter to be appended to #chain
*/
void
filter_chain_append(PreparedFilter &chain, std::string_view name,
std::unique_ptr<PreparedFilter> filter) noexcept;
#endif
......@@ -15,7 +15,6 @@ filter_plugins = static_library(
'../../AudioCompress/compress.c',
'NullFilterPlugin.cxx',
'TwoFilters.cxx',
'ChainFilterPlugin.cxx',
'AutoConvertFilterPlugin.cxx',
'ConvertFilterPlugin.cxx',
'RouteFilterPlugin.cxx',
......
......@@ -90,8 +90,8 @@ public:
AudioFormat out_audio_format;
/**
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
* The filter object of this audio output. This is a chain of
* #PreparedTwoFilter instances.
*/
std::unique_ptr<PreparedFilter> prepared_filter;
......
......@@ -32,7 +32,7 @@
#include "filter/plugins/AutoConvertFilterPlugin.hxx"
#include "filter/plugins/ConvertFilterPlugin.hxx"
#include "filter/plugins/ReplayGainFilterPlugin.hxx"
#include "filter/plugins/ChainFilterPlugin.hxx"
#include "filter/plugins/TwoFilters.hxx"
#include "filter/plugins/VolumeFilterPlugin.hxx"
#include "filter/plugins/NormalizeFilterPlugin.hxx"
#include "util/RuntimeError.hxx"
......@@ -109,7 +109,7 @@ audio_output_load_mixer(EventLoop &event_loop, FilteredAudioOutput &ao,
const ConfigBlock &block,
const MixerType mixer_type,
const MixerPlugin *plugin,
PreparedFilter &filter_chain,
std::unique_ptr<PreparedFilter> &filter_chain,
MixerListener &listener)
{
Mixer *mixer;
......@@ -137,8 +137,9 @@ audio_output_load_mixer(EventLoop &event_loop, FilteredAudioOutput &ao,
ConfigBlock());
assert(mixer != nullptr);
filter_chain_append(filter_chain, "software_mixer",
ao.volume_filter.Set(volume_filter_prepare()));
filter_chain = ChainFilters(std::move(filter_chain),
ao.volume_filter.Set(volume_filter_prepare()),
"software_mixer");
return mixer;
}
......@@ -169,21 +170,17 @@ FilteredAudioOutput::Configure(const ConfigBlock &block,
log_name = StringFormat<256>("\"%s\" (%s)", name, plugin_name);
/* set up the filter chain */
prepared_filter = filter_chain_new();
assert(prepared_filter != nullptr);
/* create the normalization filter (if configured) */
if (defaults.normalize) {
filter_chain_append(*prepared_filter, "normalize",
autoconvert_filter_new(normalize_filter_prepare()));
prepared_filter = ChainFilters(std::move(prepared_filter),
autoconvert_filter_new(normalize_filter_prepare()),
"normalize");
}
try {
if (filter_factory != nullptr)
filter_chain_parse(*prepared_filter, *filter_factory,
filter_chain_parse(prepared_filter, *filter_factory,
block.GetBlockValue(AUDIO_FILTERS, ""));
} catch (...) {
/* It's not really fatal - Part of the filter chain
......@@ -236,7 +233,7 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
mixer = audio_output_load_mixer(event_loop, *this, block,
mixer_type,
mixer_plugin,
*prepared_filter,
prepared_filter,
mixer_listener);
} catch (...) {
FmtError(output_domain,
......@@ -260,8 +257,9 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
/* the "convert" filter must be the last one in the chain */
filter_chain_append(*prepared_filter, "convert",
convert_filter.Set(convert_filter_prepare()));
prepared_filter = ChainFilters(std::move(prepared_filter),
convert_filter.Set(convert_filter_prepare()),
"convert");
}
std::unique_ptr<FilteredAudioOutput>
......
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