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.
This commit is contained in:
parent
bd79354f32
commit
9ca64d5fb3
@ -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);
|
||||
|
||||
|
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
|
Loading…
Reference in New Issue
Block a user