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 "Factory.hxx"
|
||||||
#include "Prepared.hxx"
|
#include "Prepared.hxx"
|
||||||
#include "plugins/AutoConvertFilterPlugin.hxx"
|
#include "plugins/AutoConvertFilterPlugin.hxx"
|
||||||
#include "plugins/ChainFilterPlugin.hxx"
|
#include "plugins/TwoFilters.hxx"
|
||||||
#include "util/IterableSplitString.hxx"
|
#include "util/IterableSplitString.hxx"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
static void
|
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)
|
std::string_view template_name)
|
||||||
{
|
{
|
||||||
/* using the AutoConvert filter just in case the specified
|
/* using the AutoConvert filter just in case the specified
|
||||||
filter plugin does not support the exact input format */
|
filter plugin does not support the exact input format */
|
||||||
|
|
||||||
filter_chain_append(chain, template_name,
|
chain = ChainFilters(std::move(chain),
|
||||||
/* unfortunately, MakeFilter() wants a
|
/* unfortunately, MakeFilter() wants a
|
||||||
null-terminated string, so we need to
|
null-terminated string, so we need to
|
||||||
copy it here */
|
copy it here */
|
||||||
autoconvert_filter_new(factory.MakeFilter(std::string(template_name).c_str())));
|
autoconvert_filter_new(factory.MakeFilter(std::string(template_name).c_str())),
|
||||||
|
template_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
filter_chain_parse(PreparedFilter &chain,
|
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
|
||||||
FilterFactory &factory,
|
FilterFactory &factory,
|
||||||
const char *spec)
|
const char *spec)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#ifndef MPD_FILTER_LOAD_CHAIN_HXX
|
#ifndef MPD_FILTER_LOAD_CHAIN_HXX
|
||||||
#define MPD_FILTER_LOAD_CHAIN_HXX
|
#define MPD_FILTER_LOAD_CHAIN_HXX
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class FilterFactory;
|
class FilterFactory;
|
||||||
class PreparedFilter;
|
class PreparedFilter;
|
||||||
|
|
||||||
@ -35,7 +37,7 @@ class PreparedFilter;
|
|||||||
* @param spec the filter chain specification
|
* @param spec the filter chain specification
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
filter_chain_parse(PreparedFilter &chain,
|
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
|
||||||
FilterFactory &factory,
|
FilterFactory &factory,
|
||||||
const char *spec);
|
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',
|
'../../AudioCompress/compress.c',
|
||||||
'NullFilterPlugin.cxx',
|
'NullFilterPlugin.cxx',
|
||||||
'TwoFilters.cxx',
|
'TwoFilters.cxx',
|
||||||
'ChainFilterPlugin.cxx',
|
|
||||||
'AutoConvertFilterPlugin.cxx',
|
'AutoConvertFilterPlugin.cxx',
|
||||||
'ConvertFilterPlugin.cxx',
|
'ConvertFilterPlugin.cxx',
|
||||||
'RouteFilterPlugin.cxx',
|
'RouteFilterPlugin.cxx',
|
||||||
|
@ -90,8 +90,8 @@ public:
|
|||||||
AudioFormat out_audio_format;
|
AudioFormat out_audio_format;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The filter object of this audio output. This is an
|
* The filter object of this audio output. This is a chain of
|
||||||
* instance of chain_filter_plugin.
|
* #PreparedTwoFilter instances.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<PreparedFilter> prepared_filter;
|
std::unique_ptr<PreparedFilter> prepared_filter;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
#include "filter/plugins/AutoConvertFilterPlugin.hxx"
|
#include "filter/plugins/AutoConvertFilterPlugin.hxx"
|
||||||
#include "filter/plugins/ConvertFilterPlugin.hxx"
|
#include "filter/plugins/ConvertFilterPlugin.hxx"
|
||||||
#include "filter/plugins/ReplayGainFilterPlugin.hxx"
|
#include "filter/plugins/ReplayGainFilterPlugin.hxx"
|
||||||
#include "filter/plugins/ChainFilterPlugin.hxx"
|
#include "filter/plugins/TwoFilters.hxx"
|
||||||
#include "filter/plugins/VolumeFilterPlugin.hxx"
|
#include "filter/plugins/VolumeFilterPlugin.hxx"
|
||||||
#include "filter/plugins/NormalizeFilterPlugin.hxx"
|
#include "filter/plugins/NormalizeFilterPlugin.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
@ -109,7 +109,7 @@ audio_output_load_mixer(EventLoop &event_loop, FilteredAudioOutput &ao,
|
|||||||
const ConfigBlock &block,
|
const ConfigBlock &block,
|
||||||
const MixerType mixer_type,
|
const MixerType mixer_type,
|
||||||
const MixerPlugin *plugin,
|
const MixerPlugin *plugin,
|
||||||
PreparedFilter &filter_chain,
|
std::unique_ptr<PreparedFilter> &filter_chain,
|
||||||
MixerListener &listener)
|
MixerListener &listener)
|
||||||
{
|
{
|
||||||
Mixer *mixer;
|
Mixer *mixer;
|
||||||
@ -137,8 +137,9 @@ audio_output_load_mixer(EventLoop &event_loop, FilteredAudioOutput &ao,
|
|||||||
ConfigBlock());
|
ConfigBlock());
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
|
|
||||||
filter_chain_append(filter_chain, "software_mixer",
|
filter_chain = ChainFilters(std::move(filter_chain),
|
||||||
ao.volume_filter.Set(volume_filter_prepare()));
|
ao.volume_filter.Set(volume_filter_prepare()),
|
||||||
|
"software_mixer");
|
||||||
return mixer;
|
return mixer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,21 +170,17 @@ FilteredAudioOutput::Configure(const ConfigBlock &block,
|
|||||||
|
|
||||||
log_name = StringFormat<256>("\"%s\" (%s)", name, plugin_name);
|
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) */
|
/* create the normalization filter (if configured) */
|
||||||
|
|
||||||
if (defaults.normalize) {
|
if (defaults.normalize) {
|
||||||
filter_chain_append(*prepared_filter, "normalize",
|
prepared_filter = ChainFilters(std::move(prepared_filter),
|
||||||
autoconvert_filter_new(normalize_filter_prepare()));
|
autoconvert_filter_new(normalize_filter_prepare()),
|
||||||
|
"normalize");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (filter_factory != nullptr)
|
if (filter_factory != nullptr)
|
||||||
filter_chain_parse(*prepared_filter, *filter_factory,
|
filter_chain_parse(prepared_filter, *filter_factory,
|
||||||
block.GetBlockValue(AUDIO_FILTERS, ""));
|
block.GetBlockValue(AUDIO_FILTERS, ""));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
/* It's not really fatal - Part of the filter chain
|
/* 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 = audio_output_load_mixer(event_loop, *this, block,
|
||||||
mixer_type,
|
mixer_type,
|
||||||
mixer_plugin,
|
mixer_plugin,
|
||||||
*prepared_filter,
|
prepared_filter,
|
||||||
mixer_listener);
|
mixer_listener);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
FmtError(output_domain,
|
FmtError(output_domain,
|
||||||
@ -260,8 +257,9 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
|
|||||||
|
|
||||||
/* the "convert" filter must be the last one in the chain */
|
/* the "convert" filter must be the last one in the chain */
|
||||||
|
|
||||||
filter_chain_append(*prepared_filter, "convert",
|
prepared_filter = ChainFilters(std::move(prepared_filter),
|
||||||
convert_filter.Set(convert_filter_prepare()));
|
convert_filter.Set(convert_filter_prepare()),
|
||||||
|
"convert");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<FilteredAudioOutput>
|
std::unique_ptr<FilteredAudioOutput>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user