summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2016-06-22 11:15:49 +0200
committerMax Kellermann <max@duempel.org>2016-07-01 21:04:24 +0200
commit3a212412487e8868946e7c845b6830f880790f91 (patch)
tree5a6120b77a34e1c24da340a66fe5a8e56666333b
parent5c75096bcd21f2780e469e06bc4fc7bddf75684c (diff)
filter/FilterInternal: split class Filter, add class PreparedFilter
For easier state management inside filter plugins.
-rw-r--r--Makefile.am2
-rw-r--r--src/filter/FilterConfig.cxx6
-rw-r--r--src/filter/FilterConfig.hxx4
-rw-r--r--src/filter/FilterInternal.hxx48
-rw-r--r--src/filter/FilterPlugin.cxx4
-rw-r--r--src/filter/FilterPlugin.hxx8
-rw-r--r--src/filter/Observer.cxx115
-rw-r--r--src/filter/Observer.hxx47
-rw-r--r--src/filter/plugins/AutoConvertFilterPlugin.cxx85
-rw-r--r--src/filter/plugins/AutoConvertFilterPlugin.hxx6
-rw-r--r--src/filter/plugins/ChainFilterPlugin.cxx126
-rw-r--r--src/filter/plugins/ChainFilterPlugin.hxx7
-rw-r--r--src/filter/plugins/ConvertFilterPlugin.cxx78
-rw-r--r--src/filter/plugins/ConvertFilterPlugin.hxx5
-rw-r--r--src/filter/plugins/NormalizeFilterPlugin.cxx37
-rw-r--r--src/filter/plugins/NullFilterPlugin.cxx19
-rw-r--r--src/filter/plugins/ReplayGainFilterPlugin.cxx72
-rw-r--r--src/filter/plugins/ReplayGainFilterPlugin.hxx3
-rw-r--r--src/filter/plugins/RouteFilterPlugin.cxx98
-rw-r--r--src/filter/plugins/VolumeFilterPlugin.cxx40
-rw-r--r--src/output/Finish.cxx6
-rw-r--r--src/output/Init.cxx56
-rw-r--r--src/output/Internal.hxx21
-rw-r--r--src/output/OutputControl.cxx9
-rw-r--r--src/output/OutputThread.cxx64
-rw-r--r--test/run_filter.cxx25
26 files changed, 609 insertions, 382 deletions
diff --git a/Makefile.am b/Makefile.am
index dd47df7e8..4345a65b4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,7 @@ libmpd_a_SOURCES = \
src/filter/FilterPlugin.cxx src/filter/FilterPlugin.hxx \
src/filter/FilterInternal.hxx \
src/filter/FilterRegistry.cxx src/filter/FilterRegistry.hxx \
+ src/filter/Observer.cxx src/filter/Observer.hxx \
src/client/Client.cxx src/client/Client.hxx \
src/client/ClientInternal.hxx \
src/client/ClientEvent.cxx \
@@ -2068,6 +2069,7 @@ test_run_output_SOURCES = test/run_output.cxx \
src/mixer/MixerType.cxx \
src/filter/FilterPlugin.cxx \
src/filter/FilterConfig.cxx \
+ src/filter/Observer.cxx \
src/ReplayGainInfo.cxx
test_read_mixer_LDADD = \
diff --git a/src/filter/FilterConfig.cxx b/src/filter/FilterConfig.cxx
index fa5fcc8ff..236297eba 100644
--- a/src/filter/FilterConfig.cxx
+++ b/src/filter/FilterConfig.cxx
@@ -33,7 +33,7 @@
#include <string.h>
static bool
-filter_chain_append_new(Filter &chain, const char *template_name, Error &error)
+filter_chain_append_new(PreparedFilter &chain, const char *template_name, Error &error)
{
const auto *cfg = config_find_block(ConfigBlockOption::AUDIO_FILTER,
"name", template_name);
@@ -45,7 +45,7 @@ filter_chain_append_new(Filter &chain, const char *template_name, Error &error)
}
// Instantiate one of those filter plugins with the template name as a hint
- Filter *f = filter_configured_new(*cfg, error);
+ PreparedFilter *f = filter_configured_new(*cfg, error);
if (f == nullptr)
// The error has already been set, just stop.
return false;
@@ -58,7 +58,7 @@ filter_chain_append_new(Filter &chain, const char *template_name, Error &error)
}
bool
-filter_chain_parse(Filter &chain, const char *spec, Error &error)
+filter_chain_parse(PreparedFilter &chain, const char *spec, Error &error)
{
const char *const end = spec + strlen(spec);
diff --git a/src/filter/FilterConfig.hxx b/src/filter/FilterConfig.hxx
index cd0455fef..6d6fb4744 100644
--- a/src/filter/FilterConfig.hxx
+++ b/src/filter/FilterConfig.hxx
@@ -25,7 +25,7 @@
#ifndef MPD_FILTER_CONFIG_HXX
#define MPD_FILTER_CONFIG_HXX
-class Filter;
+class PreparedFilter;
class Error;
/**
@@ -38,6 +38,6 @@ class Error;
* @return true on success
*/
bool
-filter_chain_parse(Filter &chain, const char *spec, Error &error);
+filter_chain_parse(PreparedFilter &chain, const char *spec, Error &error);
#endif
diff --git a/src/filter/FilterInternal.hxx b/src/filter/FilterInternal.hxx
index 5b204b7b0..701f73b5e 100644
--- a/src/filter/FilterInternal.hxx
+++ b/src/filter/FilterInternal.hxx
@@ -25,6 +25,8 @@
#ifndef MPD_FILTER_INTERNAL_HXX
#define MPD_FILTER_INTERNAL_HXX
+#include "AudioFormat.hxx"
+
#include <stddef.h>
struct AudioFormat;
@@ -32,25 +34,22 @@ class Error;
template<typename T> struct ConstBuffer;
class Filter {
+protected:
+ AudioFormat out_audio_format;
+
+ Filter() = default;
+ explicit Filter(AudioFormat _out_audio_format)
+ :out_audio_format(_out_audio_format) {}
+
public:
virtual ~Filter() {}
/**
- * Opens the filter, preparing it for FilterPCM().
- *
- * @param af the audio format of incoming data; the
- * plugin may modify the object to enforce another input
- * format
- * @param error location to store the error occurring
- * @return the format of outgoing data or
- * AudioFormat::Undefined() on error
- */
- virtual AudioFormat Open(AudioFormat &af, Error &error) = 0;
-
- /**
- * Closes the filter. After that, you may call Open() again.
+ * Returns the #AudioFormat produced by FilterPCM().
*/
- virtual void Close() = 0;
+ const AudioFormat &GetOutAudioFormat() const {
+ return out_audio_format;
+ }
/**
* Filters a block of PCM data.
@@ -58,10 +57,27 @@ public:
* @param src the input buffer
* @param error location to store the error occurring
* @return the destination buffer on success (will be
- * invalidated by Close() or FilterPCM()), nullptr on
- * error
+ * invalidated by deleting this object or the next FilterPCM()
+ * call), nullptr on error
*/
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, Error &error) = 0;
};
+class PreparedFilter {
+public:
+ virtual ~PreparedFilter() {}
+
+ /**
+ * Opens the filter, preparing it for FilterPCM().
+ *
+ * @param af the audio format of incoming data; the
+ * plugin may modify the object to enforce another input
+ * format
+ * @param error location to store the error occurring
+ * @return the format of outgoing data or
+ * AudioFormat::Undefined() on error
+ */
+ virtual Filter *Open(AudioFormat &af, Error &error) = 0;
+};
+
#endif
diff --git a/src/filter/FilterPlugin.cxx b/src/filter/FilterPlugin.cxx
index 6e21ca662..f43fee06c 100644
--- a/src/filter/FilterPlugin.cxx
+++ b/src/filter/FilterPlugin.cxx
@@ -26,7 +26,7 @@
#include <assert.h>
-Filter *
+PreparedFilter *
filter_new(const struct filter_plugin *plugin,
const ConfigBlock &block, Error &error)
{
@@ -36,7 +36,7 @@ filter_new(const struct filter_plugin *plugin,
return plugin->init(block, error);
}
-Filter *
+PreparedFilter *
filter_configured_new(const ConfigBlock &block, Error &error)
{
assert(!error.IsDefined());
diff --git a/src/filter/FilterPlugin.hxx b/src/filter/FilterPlugin.hxx
index 8c1f9da35..2ebca232b 100644
--- a/src/filter/FilterPlugin.hxx
+++ b/src/filter/FilterPlugin.hxx
@@ -27,7 +27,7 @@
#define MPD_FILTER_PLUGIN_HXX
struct ConfigBlock;
-class Filter;
+class PreparedFilter;
class Error;
struct filter_plugin {
@@ -36,7 +36,7 @@ struct filter_plugin {
/**
* Allocates and configures a filter.
*/
- Filter *(*init)(const ConfigBlock &block, Error &error);
+ PreparedFilter *(*init)(const ConfigBlock &block, Error &error);
};
/**
@@ -48,7 +48,7 @@ struct filter_plugin {
* ignore errors.
* @return a new filter object, or nullptr on error
*/
-Filter *
+PreparedFilter *
filter_new(const struct filter_plugin *plugin,
const ConfigBlock &block, Error &error);
@@ -61,7 +61,7 @@ filter_new(const struct filter_plugin *plugin,
* ignore errors.
* @return a new filter object, or nullptr on error
*/
-Filter *
+PreparedFilter *
filter_configured_new(const ConfigBlock &block, Error &error);
#endif
diff --git a/src/filter/Observer.cxx b/src/filter/Observer.cxx
new file mode 100644
index 000000000..b3cadfb30
--- /dev/null
+++ b/src/filter/Observer.cxx
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2003-2016 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 "config.h"
+#include "Observer.hxx"
+#include "FilterInternal.hxx"
+#include "util/ConstBuffer.hxx"
+
+#include <assert.h>
+
+class FilterObserver::PreparedProxy final : public PreparedFilter {
+ FilterObserver &observer;
+
+ PreparedFilter *const prepared_filter;
+ Proxy *child = nullptr;
+
+public:
+ PreparedProxy(FilterObserver &_observer,
+ PreparedFilter *_prepared_filter)
+ :observer(_observer),
+ prepared_filter(_prepared_filter) {}
+
+ ~PreparedProxy() {
+ assert(child == nullptr);
+ assert(observer.proxy == this);
+
+ observer.proxy = nullptr;
+ }
+
+ void Clear(gcc_unused Proxy *_child) {
+ assert(child == _child);
+ child = nullptr;
+ }
+
+ Filter *Get();
+
+ Filter *Open(AudioFormat &af, Error &error) override;
+};
+
+class FilterObserver::Proxy final : public Filter {
+ PreparedProxy &parent;
+
+ Filter *const filter;
+
+public:
+ Proxy(PreparedProxy &_parent, Filter *_filter)
+ :Filter(_filter->GetOutAudioFormat()),
+ parent(_parent), filter(_filter) {}
+
+ ~Proxy() {
+ parent.Clear(this);
+ delete filter;
+ }
+
+ Filter *Get() {
+ return filter;
+ }
+
+ ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
+ Error &error) override {
+ return filter->FilterPCM(src, error);
+ }
+};
+
+Filter *
+FilterObserver::PreparedProxy::Get()
+{
+ return child != nullptr
+ ? child->Get()
+ : nullptr;
+}
+
+Filter *
+FilterObserver::PreparedProxy::Open(AudioFormat &af, Error &error)
+{
+ assert(child == nullptr);
+
+ Filter *f = prepared_filter->Open(af, error);
+ if (f == nullptr)
+ return f;
+
+ return child = new Proxy(*this, f);
+}
+
+PreparedFilter *
+FilterObserver::Set(PreparedFilter *pf)
+{
+ assert(proxy == nullptr);
+
+ return proxy = new PreparedProxy(*this, pf);
+}
+
+Filter *
+FilterObserver::Get()
+{
+ return proxy != nullptr
+ ? proxy->Get()
+ : nullptr;
+}
diff --git a/src/filter/Observer.hxx b/src/filter/Observer.hxx
new file mode 100644
index 000000000..aad0c7927
--- /dev/null
+++ b/src/filter/Observer.hxx
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2016 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.
+ */
+
+#ifndef MPD_FILTER_OBSERVER_HXX
+#define MPD_FILTER_OBSERVER_HXX
+
+#include "check.h"
+
+class PreparedFilter;
+class Filter;
+
+/**
+ * A helper class which observes calls to a #PreparedFilter and allows
+ * the caller to access the #Filter instances created by it.
+ */
+class FilterObserver {
+ class PreparedProxy;
+ class Proxy;
+
+ PreparedProxy *proxy = nullptr;
+
+public:
+ /**
+ * @return a proxy object
+ */
+ PreparedFilter *Set(PreparedFilter *pf);
+
+ Filter *Get();
+};
+
+#endif
diff --git a/src/filter/plugins/AutoConvertFilterPlugin.cxx b/src/filter/plugins/AutoConvertFilterPlugin.cxx
index 2400d6dd4..e3639e181 100644
--- a/src/filter/plugins/AutoConvertFilterPlugin.cxx
+++ b/src/filter/plugins/AutoConvertFilterPlugin.cxx
@@ -24,7 +24,6 @@
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "AudioFormat.hxx"
-#include "config/Block.hxx"
#include "util/ConstBuffer.hxx"
#include <assert.h>
@@ -33,82 +32,70 @@ class AutoConvertFilter final : public Filter {
/**
* The underlying filter.
*/
- Filter *filter;
+ Filter *const filter;
/**
* A convert_filter, just in case conversion is needed. nullptr
* if unused.
*/
- Filter *convert;
+ Filter *const convert;
public:
- AutoConvertFilter(Filter *_filter):filter(_filter) {}
+ AutoConvertFilter(Filter *_filter, Filter *_convert)
+ :filter(_filter), convert(_convert) {}
+
~AutoConvertFilter() {
+ delete convert;
delete filter;
}
- virtual AudioFormat Open(AudioFormat &af, Error &error) override;
- virtual void Close() override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
-AudioFormat
-AutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error)
+class PreparedAutoConvertFilter final : public PreparedFilter {
+ /**
+ * The underlying filter.
+ */
+ PreparedFilter *const filter;
+
+public:
+ PreparedAutoConvertFilter(PreparedFilter *_filter):filter(_filter) {}
+ ~PreparedAutoConvertFilter() {
+ delete filter;
+ }
+
+ virtual Filter *Open(AudioFormat &af, Error &error) override;
+};
+
+Filter *
+PreparedAutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error)
{
assert(in_audio_format.IsValid());
/* open the "real" filter */
AudioFormat child_audio_format = in_audio_format;
- AudioFormat out_audio_format = filter->Open(child_audio_format, error);
- if (!out_audio_format.IsDefined())
- return out_audio_format;
+ auto *new_filter = filter->Open(child_audio_format, error);
+ if (new_filter == nullptr)
+ return nullptr;
/* need to convert? */
+ Filter *convert = nullptr;
if (in_audio_format != child_audio_format) {
/* yes - create a convert_filter */
- const ConfigBlock empty;
- convert = filter_new(&convert_filter_plugin, empty, error);
+ convert = convert_filter_new(in_audio_format,
+ child_audio_format,
+ error);
if (convert == nullptr) {
- filter->Close();
- return AudioFormat::Undefined();
- }
-
- AudioFormat audio_format2 = in_audio_format;
- AudioFormat audio_format3 =
- convert->Open(audio_format2, error);
- if (!audio_format3.IsDefined()) {
- delete convert;
- filter->Close();
- return AudioFormat::Undefined();
- }
-
- assert(audio_format2 == in_audio_format);
-
- if (!convert_filter_set(convert, child_audio_format, error)) {
- delete convert;
- filter->Close();
- return AudioFormat::Undefined();
+ delete new_filter;
+ return nullptr;
}
- } else
- /* no */
- convert = nullptr;
-
- return out_audio_format;
-}
-
-void
-AutoConvertFilter::Close()
-{
- if (convert != nullptr) {
- convert->Close();
- delete convert;
}
- filter->Close();
+ return new AutoConvertFilter(new_filter, convert);
}
ConstBuffer<void>
@@ -123,8 +110,8 @@ AutoConvertFilter::FilterPCM(ConstBuffer<void> src, Error &error)
return filter->FilterPCM(src, error);
}
-Filter *
-autoconvert_filter_new(Filter *filter)
+PreparedFilter *
+autoconvert_filter_new(PreparedFilter *filter)
{
- return new AutoConvertFilter(filter);
+ return new PreparedAutoConvertFilter(filter);
}
diff --git a/src/filter/plugins/AutoConvertFilterPlugin.hxx b/src/filter/plugins/AutoConvertFilterPlugin.hxx
index acec195a1..6b831d3ce 100644
--- a/src/filter/plugins/AutoConvertFilterPlugin.hxx
+++ b/src/filter/plugins/AutoConvertFilterPlugin.hxx
@@ -20,7 +20,7 @@
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
-class Filter;
+class PreparedFilter;
/**
* Creates a new "autoconvert" filter. When opened, it ensures that
@@ -28,7 +28,7 @@ class Filter;
* requests a different format, it automatically creates a
* convert_filter.
*/
-Filter *
-autoconvert_filter_new(Filter *filter);
+PreparedFilter *
+autoconvert_filter_new(PreparedFilter *filter);
#endif
diff --git a/src/filter/plugins/ChainFilterPlugin.cxx b/src/filter/plugins/ChainFilterPlugin.cxx
index 8bacc2a1f..4b79baf5b 100644
--- a/src/filter/plugins/ChainFilterPlugin.cxx
+++ b/src/filter/plugins/ChainFilterPlugin.cxx
@@ -27,6 +27,7 @@
#include "util/Domain.hxx"
#include "util/ConstBuffer.hxx"
+#include <memory>
#include <list>
#include <assert.h>
@@ -49,100 +50,98 @@ class ChainFilter final : public Filter {
std::list<Child> children;
public:
+ explicit ChainFilter(AudioFormat _audio_format)
+ :Filter(_audio_format) {}
+
void Append(const char *name, Filter *filter) {
+ assert(out_audio_format.IsValid());
+ out_audio_format = filter->GetOutAudioFormat();
+ assert(out_audio_format.IsValid());
+
children.emplace_back(name, filter);
}
/* virtual methods from class Filter */
- AudioFormat Open(AudioFormat &af, Error &error) override;
- void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
+};
+
+class PreparedChainFilter final : public PreparedFilter {
+ struct Child {
+ const char *name;
+ PreparedFilter *filter;
+
+ Child(const char *_name, PreparedFilter *_filter)
+ :name(_name), filter(_filter) {}
+ ~Child() {
+ delete filter;
+ }
+
+ Child(const Child &) = delete;
+ Child &operator=(const Child &) = delete;
+
+ Filter *Open(const AudioFormat &prev_audio_format,
+ Error &error);
+ };
+
+ std::list<Child> children;
+
+public:
+ void Append(const char *name, PreparedFilter *filter) {
+ children.emplace_back(name, filter);
+ }
-private:
- /**
- * Close all filters in the chain until #until is reached.
- * #until itself is not closed.
- */
- void CloseUntil(const Filter *until);
+ /* virtual methods from class PreparedFilter */
+ Filter *Open(AudioFormat &af, Error &error) override;
};
static constexpr Domain chain_filter_domain("chain_filter");
-static Filter *
+static PreparedFilter *
chain_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new ChainFilter();
+ return new PreparedChainFilter();
}
-void
-ChainFilter::CloseUntil(const Filter *until)
-{
- for (auto &child : children) {
- if (child.filter == until)
- /* don't close this filter */
- return;
-
- /* close this filter */
- child.filter->Close();
- }
-
- /* this assertion fails if #until does not exist (anymore) */
- assert(false);
- gcc_unreachable();
-}
-
-static AudioFormat
-chain_open_child(const char *name, Filter *filter,
- const AudioFormat &prev_audio_format,
- Error &error)
+Filter *
+PreparedChainFilter::Child::Open(const AudioFormat &prev_audio_format,
+ Error &error)
{
AudioFormat conv_audio_format = prev_audio_format;
- const AudioFormat next_audio_format =
- filter->Open(conv_audio_format, error);
- if (!next_audio_format.IsDefined())
- return next_audio_format;
+ Filter *new_filter = filter->Open(conv_audio_format, error);
+ if (new_filter == nullptr)
+ return nullptr;
if (conv_audio_format != prev_audio_format) {
- struct audio_format_string s;
-
- filter->Close();
+ delete new_filter;
+ struct audio_format_string s;
error.Format(chain_filter_domain,
"Audio format not supported by filter '%s': %s",
name,
audio_format_to_string(prev_audio_format, &s));
- return AudioFormat::Undefined();
+ return nullptr;
}
- return next_audio_format;
+ return new_filter;
}
-AudioFormat
-ChainFilter::Open(AudioFormat &in_audio_format, Error &error)
+Filter *
+PreparedChainFilter::Open(AudioFormat &in_audio_format, Error &error)
{
- AudioFormat audio_format = in_audio_format;
+ std::unique_ptr<ChainFilter> chain(new ChainFilter(in_audio_format));
for (auto &child : children) {
- audio_format = chain_open_child(child.name, child.filter,
- audio_format, error);
- if (!audio_format.IsDefined()) {
- /* rollback, close all children */
- CloseUntil(child.filter);
- break;
- }
- }
+ AudioFormat audio_format = chain->GetOutAudioFormat();
+ auto *filter = child.Open(audio_format, error);
+ if (filter == nullptr)
+ return nullptr;
- /* return the output format of the last filter */
- return audio_format;
-}
+ chain->Append(child.name, filter);
+ }
-void
-ChainFilter::Close()
-{
- for (auto &child : children)
- child.filter->Close();
+ return chain.release();
}
ConstBuffer<void>
@@ -165,16 +164,17 @@ const struct filter_plugin chain_filter_plugin = {
chain_filter_init,
};
-Filter *
+PreparedFilter *
filter_chain_new(void)
{
- return new ChainFilter();
+ return new PreparedChainFilter();
}
void
-filter_chain_append(Filter &_chain, const char *name, Filter *filter)
+filter_chain_append(PreparedFilter &_chain, const char *name,
+ PreparedFilter *filter)
{
- ChainFilter &chain = (ChainFilter &)_chain;
+ PreparedChainFilter &chain = (PreparedChainFilter &)_chain;
chain.Append(name, filter);
}
diff --git a/src/filter/plugins/ChainFilterPlugin.hxx b/src/filter/plugins/ChainFilterPlugin.hxx
index 6667127a2..5b29db920 100644
--- a/src/filter/plugins/ChainFilterPlugin.hxx
+++ b/src/filter/plugins/ChainFilterPlugin.hxx
@@ -27,12 +27,12 @@
#ifndef MPD_FILTER_CHAIN_HXX
#define MPD_FILTER_CHAIN_HXX
-class Filter;
+class PreparedFilter;
/**
* Creates a new filter chain.
*/
-Filter *
+PreparedFilter *
filter_chain_new();
/**
@@ -43,6 +43,7 @@ filter_chain_new();
* @param filter the filter to be appended to #chain
*/
void
-filter_chain_append(Filter &chain, const char *name, Filter *filter);
+filter_chain_append(PreparedFilter &chain, const char *name,
+ PreparedFilter *filter);
#endif
diff --git a/src/filter/plugins/ConvertFilterPlugin.cxx b/src/filter/plugins/ConvertFilterPlugin.cxx
index e84cdf14b..b8a1b905d 100644
--- a/src/filter/plugins/ConvertFilterPlugin.cxx
+++ b/src/filter/plugins/ConvertFilterPlugin.cxx
@@ -38,31 +38,33 @@ class ConvertFilter final : public Filter {
AudioFormat in_audio_format;
/**
- * The output audio format; the consumer of this plugin
- * expects PCM data in this format.
- *
- * If this is AudioFormat::Undefined(), then the #PcmConvert
- * attribute is not open. This can mean that Set() has failed
- * or that no conversion is necessary.
+ * This object is only "open" if #in_audio_format !=
+ * #out_audio_format.
*/
- AudioFormat out_audio_format;
-
- Manual<PcmConvert> state;
+ PcmConvert state;
public:
+ ConvertFilter(const AudioFormat &audio_format);
+ ~ConvertFilter();
+
bool Set(const AudioFormat &_out_audio_format, Error &error);
- virtual AudioFormat Open(AudioFormat &af, Error &error) override;
- virtual void Close() override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
-static Filter *
+class PreparedConvertFilter final : public PreparedFilter {
+public:
+ bool Set(const AudioFormat &_out_audio_format, Error &error);
+
+ Filter *Open(AudioFormat &af, Error &error) override;
+};
+
+static PreparedFilter *
convert_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new ConvertFilter();
+ return new PreparedConvertFilter();
}
bool
@@ -75,47 +77,41 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format, Error &error)
/* no change */
return true;
- if (out_audio_format.IsValid()) {
- out_audio_format.Clear();
- state->Close();
+ if (out_audio_format != in_audio_format) {
+ out_audio_format = in_audio_format;
+ state.Close();
}
if (_out_audio_format == in_audio_format)
/* optimized special case: no-op */
return true;
- if (!state->Open(in_audio_format, _out_audio_format, error))
+ if (!state.Open(in_audio_format, _out_audio_format, error))
return false;
out_audio_format = _out_audio_format;
return true;
}
-AudioFormat
-ConvertFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
+ConvertFilter::ConvertFilter(const AudioFormat &audio_format)
+ :Filter(audio_format), in_audio_format(audio_format)
{
- assert(audio_format.IsValid());
-
- in_audio_format = audio_format;
- out_audio_format.Clear();
+}
- state.Construct();
+Filter *
+PreparedConvertFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
+{
+ assert(audio_format.IsValid());
- return in_audio_format;
+ return new ConvertFilter(audio_format);
}
-void
-ConvertFilter::Close()
+ConvertFilter::~ConvertFilter()
{
assert(in_audio_format.IsValid());
if (out_audio_format.IsValid())
- state->Close();
-
- state.Destruct();
-
- poison_undefined(&in_audio_format, sizeof(in_audio_format));
- poison_undefined(&out_audio_format, sizeof(out_audio_format));
+ state.Close();
}
ConstBuffer<void>
@@ -127,7 +123,7 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src, Error &error)
/* optimized special case: no-op */
return src;
- return state->Convert(src, error);
+ return state.Convert(src, error);
}
const struct filter_plugin convert_filter_plugin = {
@@ -135,6 +131,20 @@ const struct filter_plugin convert_filter_plugin = {
convert_filter_init,
};
+Filter *
+convert_filter_new(const AudioFormat in_audio_format,
+ const AudioFormat out_audio_format,
+ Error &error)
+{
+ auto *filter = new ConvertFilter(in_audio_format);
+ if (!filter->Set(out_audio_format, error)) {
+ delete filter;
+ return nullptr;
+ }
+
+ return filter;
+}
+
bool
convert_filter_set(Filter *_filter, AudioFormat out_audio_format,
Error &error)
diff --git a/src/filter/plugins/ConvertFilterPlugin.hxx b/src/filter/plugins/ConvertFilterPlugin.hxx
index a514ff388..6bdd5ef1b 100644
--- a/src/filter/plugins/ConvertFilterPlugin.hxx
+++ b/src/filter/plugins/ConvertFilterPlugin.hxx
@@ -24,6 +24,11 @@ class Filter;
class Error;
struct AudioFormat;
+Filter *
+convert_filter_new(AudioFormat in_audio_format,
+ AudioFormat out_audio_format,
+ Error &error);
+
/**
* Sets the output audio format for the specified filter. You must
* call this after the filter has been opened. Since this audio
diff --git a/src/filter/plugins/NormalizeFilterPlugin.cxx b/src/filter/plugins/NormalizeFilterPlugin.cxx
index 5f66330bf..e83b6553d 100644
--- a/src/filter/plugins/NormalizeFilterPlugin.cxx
+++ b/src/filter/plugins/NormalizeFilterPlugin.cxx
@@ -29,40 +29,43 @@
#include <string.h>
class NormalizeFilter final : public Filter {
- struct Compressor *compressor;
+ Compressor *const compressor;
PcmBuffer buffer;
public:
+ NormalizeFilter(const AudioFormat &audio_format)
+ :Filter(audio_format), compressor(Compressor_new(0)) {
+ }
+
+ ~NormalizeFilter() {
+ Compressor_delete(compressor);
+ }
+
/* virtual methods from class Filter */
- AudioFormat Open(AudioFormat &af, Error &error) override;
- void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
-static Filter *
+class PreparedNormalizeFilter final : public PreparedFilter {
+public:
+ /* virtual methods from class PreparedFilter */
+ Filter *Open(AudioFormat &af, Error &error) override;
+};
+
+static PreparedFilter *
normalize_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new NormalizeFilter();
+ return new PreparedNormalizeFilter();
}
-AudioFormat
-NormalizeFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
+Filter *
+PreparedNormalizeFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
audio_format.format = SampleFormat::S16;
- compressor = Compressor_new(0);
-
- return audio_format;
-}
-
-void
-NormalizeFilter::Close()
-{
- buffer.Clear();
- Compressor_delete(compressor);
+ return new NormalizeFilter(audio_format);
}
ConstBuffer<void>
diff --git a/src/filter/plugins/NullFilterPlugin.cxx b/src/filter/plugins/NullFilterPlugin.cxx
index 26926f752..fc43bb7d1 100644
--- a/src/filter/plugins/NullFilterPlugin.cxx
+++ b/src/filter/plugins/NullFilterPlugin.cxx
@@ -34,12 +34,7 @@
class NullFilter final : public Filter {
public:
- virtual AudioFormat Open(AudioFormat &af,
- gcc_unused Error &error) override {
- return af;
- }
-
- virtual void Close() override {}
+ explicit NullFilter(const AudioFormat &af):Filter(af) {}
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
gcc_unused Error &error) override {
@@ -47,11 +42,19 @@ public:
}
};
-static Filter *
+class PreparedNullFilter final : public PreparedFilter {
+public:
+ virtual Filter *Open(AudioFormat &af,
+ gcc_unused Error &error) override {
+ return new NullFilter(af);
+ }
+};
+
+static PreparedFilter *
null_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new NullFilter();
+ return new PreparedNullFilter();
}
const struct filter_plugin null_filter_plugin = {
diff --git a/src/filter/plugins/ReplayGainFilterPlugin.cxx b/src/filter/plugins/ReplayGainFilterPlugin.cxx
index b9527531e..7512c180d 100644
--- a/src/filter/plugins/ReplayGainFilterPlugin.cxx
+++ b/src/filter/plugins/ReplayGainFilterPlugin.cxx
@@ -41,13 +41,13 @@ class ReplayGainFilter final : public Filter {
* If set, then this hardware mixer is used for applying
* replay gain, instead of the software volume library.
*/
- Mixer *mixer = nullptr;
+ Mixer *const mixer;
/**
* The base volume level for scale=1.0, between 1 and 100
* (including).
*/
- unsigned base;
+ const unsigned base;
ReplayGainMode mode = REPLAY_GAIN_OFF;
@@ -68,17 +68,15 @@ class ReplayGainFilter final : public Filter {
PcmVolume pv;
public:
- ReplayGainFilter() {
+ ReplayGainFilter(const AudioFormat &audio_format,
+ Mixer *_mixer, unsigned _base)
+ :Filter(audio_format),
+ mixer(_mixer), base(_base), mode(REPLAY_GAIN_OFF) {
info.Clear();
}
- void SetMixer(Mixer *_mixer, unsigned _base) {
- assert(_mixer == nullptr || (_base > 0 && _base <= 100));
-
- mixer = _mixer;
- base = _base;
-
- Update();
+ bool Open(Error &error) {
+ return pv.Open(out_audio_format.format, error);
}
void SetInfo(const ReplayGainInfo *_info) {
@@ -110,12 +108,35 @@ public:
void Update();
/* virtual methods from class Filter */
- AudioFormat Open(AudioFormat &af, Error &error) override;
- void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
+class PreparedReplayGainFilter final : public PreparedFilter {
+ /**
+ * If set, then this hardware mixer is used for applying
+ * replay gain, instead of the software volume library.
+ */
+ Mixer *mixer = nullptr;
+
+ /**
+ * The base volume level for scale=1.0, between 1 and 100
+ * (including).
+ */
+ unsigned base;
+
+public:
+ void SetMixer(Mixer *_mixer, unsigned _base) {
+ assert(_mixer == nullptr || (_base > 0 && _base <= 100));
+
+ mixer = _mixer;
+ base = _base;
+ }
+
+ /* virtual methods from class Filter */
+ Filter *Open(AudioFormat &af, Error &error) override;
+};
+
void
ReplayGainFilter::Update()
{
@@ -146,26 +167,23 @@ ReplayGainFilter::Update()
}
}
-static Filter *
+static PreparedFilter *
replay_gain_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new ReplayGainFilter();
+ return new PreparedReplayGainFilter();
}
-AudioFormat
-ReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error)
+Filter *
+PreparedReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error)
{
- if (!pv.Open(af.format, error))
- return AudioFormat::Undefined();
-
- return af;
-}
+ auto *filter = new ReplayGainFilter(af, mixer, base);
+ if (!filter->Open(error)) {
+ delete filter;
+ return nullptr;
+ }
-void
-ReplayGainFilter::Close()
-{
- pv.Close();
+ return filter;
}
ConstBuffer<void>
@@ -180,10 +198,10 @@ const struct filter_plugin replay_gain_filter_plugin = {
};
void
-replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
+replay_gain_filter_set_mixer(PreparedFilter *_filter, Mixer *mixer,
unsigned base)
{
- ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
+ PreparedReplayGainFilter *filter = (PreparedReplayGainFilter *)_filter;
filter->SetMixer(mixer, base);
}
diff --git a/src/filter/plugins/ReplayGainFilterPlugin.hxx b/src/filter/plugins/ReplayGainFilterPlugin.hxx
index 90accd8ea..734f864af 100644
--- a/src/filter/plugins/ReplayGainFilterPlugin.hxx
+++ b/src/filter/plugins/ReplayGainFilterPlugin.hxx
@@ -23,6 +23,7 @@
#include "ReplayGainInfo.hxx"
class Filter;
+class PreparedFilter;
class Mixer;
/**
@@ -34,7 +35,7 @@ class Mixer;
* (including).
*/
void
-replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
+replay_gain_filter_set_mixer(PreparedFilter *_filter, Mixer *mixer,
unsigned base);
/**
diff --git a/src/filter/plugins/RouteFilterPlugin.cxx b/src/filter/plugins/RouteFilterPlugin.cxx
index 0f061e597..39986a376 100644
--- a/src/filter/plugins/RouteFilterPlugin.cxx
+++ b/src/filter/plugins/RouteFilterPlugin.cxx
@@ -60,42 +60,23 @@
class RouteFilter final : public Filter {
/**
- * The minimum number of channels we need for output
- * to be able to perform all the copies the user has specified
- */
- unsigned min_output_channels;
-
- /**
- * The minimum number of input channels we need to
- * copy all the data the user has requested. If fewer
- * than this many are supplied by the input, undefined
- * copy operations are given zeroed sources in stead.
- */
- unsigned min_input_channels;
-
- /**
* The set of copy operations to perform on each sample
* The index is an output channel to use, the value is
* a corresponding input channel from which to take the
* data. A -1 means "no source"
*/
- std::array<int8_t, MAX_CHANNELS> sources;
+ const std::array<int8_t, MAX_CHANNELS> sources;
/**
* The actual input format of our signal, once opened
*/
- AudioFormat input_format;
-
- /**
- * The decided upon output format, once opened
- */
- AudioFormat output_format;
+ const AudioFormat input_format;
/**
* The size, in bytes, of each multichannel frame in the
* input buffer
*/
- size_t input_frame_size;
+ const size_t input_frame_size;
/**
* The size, in bytes, of each multichannel frame in the
@@ -109,6 +90,38 @@ class RouteFilter final : public Filter {
PcmBuffer output_buffer;
public:
+ RouteFilter(const AudioFormat &audio_format, unsigned out_channels,
+ const std::array<int8_t, MAX_CHANNELS> &_sources);
+
+ /* virtual methods from class Filter */
+ ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
+ Error &error) override;
+};
+
+class PreparedRouteFilter final : public PreparedFilter {
+ /**
+ * The minimum number of channels we need for output
+ * to be able to perform all the copies the user has specified
+ */
+ unsigned min_output_channels;
+
+ /**
+ * The minimum number of input channels we need to
+ * copy all the data the user has requested. If fewer
+ * than this many are supplied by the input, undefined
+ * copy operations are given zeroed sources in stead.
+ */
+ unsigned min_input_channels;
+
+ /**
+ * The set of copy operations to perform on each sample
+ * The index is an output channel to use, the value is
+ * a corresponding input channel from which to take the
+ * data. A -1 means "no source"
+ */
+ std::array<int8_t, MAX_CHANNELS> sources;
+
+public:
/**
* Parse the "routes" section, a string on the form
* a>b, c>d, e>f, ...
@@ -120,16 +133,13 @@ public:
*/
bool Configure(const ConfigBlock &block, Error &error);
- /* virtual methods from class Filter */
- AudioFormat Open(AudioFormat &af, Error &error) override;
- void Close() override;
- ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
- Error &error) override;
+ /* virtual methods from class PreparedFilter */
+ Filter *Open(AudioFormat &af, Error &error) override;
};
bool
-RouteFilter::Configure(const ConfigBlock &block, Error &error) {
-
+PreparedRouteFilter::Configure(const ConfigBlock &block, Error &error)
+{
/* TODO:
* With a more clever way of marking "don't copy to output N",
* This could easily be merged into a single loop with some
@@ -204,10 +214,10 @@ RouteFilter::Configure(const ConfigBlock &block, Error &error) {
return true;
}
-static Filter *
+static PreparedFilter *
route_filter_init(const ConfigBlock &block, Error &error)
{
- RouteFilter *filter = new RouteFilter();
+ auto *filter = new PreparedRouteFilter();
if (!filter->Configure(block, error)) {
delete filter;
return nullptr;
@@ -216,28 +226,24 @@ route_filter_init(const ConfigBlock &block, Error &error)
return filter;
}
-AudioFormat
-RouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
+RouteFilter::RouteFilter(const AudioFormat &audio_format,
+ unsigned out_channels,
+ const std::array<int8_t, MAX_CHANNELS> &_sources)
+ :Filter(audio_format), sources(_sources), input_format(audio_format),
+ input_frame_size(input_format.GetFrameSize())
{
- // Copy the input format for later reference
- input_format = audio_format;
- input_frame_size = input_format.GetFrameSize();
-
// Decide on an output format which has enough channels,
// and is otherwise identical
- output_format = audio_format;
- output_format.channels = min_output_channels;
+ out_audio_format.channels = out_channels;
// Precalculate this simple value, to speed up allocation later
- output_frame_size = output_format.GetFrameSize();
-
- return output_format;
+ output_frame_size = out_audio_format.GetFrameSize();
}
-void
-RouteFilter::Close()
+Filter *
+PreparedRouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
- output_buffer.Clear();
+ return new RouteFilter(audio_format, min_output_channels, sources);
}
ConstBuffer<void>
@@ -261,7 +267,7 @@ RouteFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error)
for (unsigned int s=0; s<number_of_frames; ++s) {
// Need to perform one copy per output channel
- for (unsigned int c=0; c<min_output_channels; ++c) {
+ for (unsigned c = 0; c < out_audio_format.channels; ++c) {
if (sources[c] == -1 ||
(unsigned)sources[c] >= input_format.channels) {
// No source for this destination output,
diff --git a/src/filter/plugins/VolumeFilterPlugin.cxx b/src/filter/plugins/VolumeFilterPlugin.cxx
index c29c92886..7e7fa2a4a 100644
--- a/src/filter/plugins/VolumeFilterPlugin.cxx
+++ b/src/filter/plugins/VolumeFilterPlugin.cxx
@@ -30,6 +30,13 @@ class VolumeFilter final : public Filter {
PcmVolume pv;
public:
+ explicit VolumeFilter(const AudioFormat &audio_format)
+ :Filter(audio_format) {}
+
+ bool Open(Error &error) {
+ return pv.Open(out_audio_format.format, error);
+ }
+
unsigned GetVolume() const {
return pv.GetVolume();
}
@@ -39,32 +46,35 @@ public:
}
/* virtual methods from class Filter */
- AudioFormat Open(AudioFormat &af, Error &error) override;
- void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
-static Filter *
+class PreparedVolumeFilter final : public PreparedFilter {
+ PcmVolume pv;
+
+public:
+ /* virtual methods from class Filter */
+ Filter *Open(AudioFormat &af, Error &error) override;
+};
+
+static PreparedFilter *
volume_filter_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
- return new VolumeFilter();
+ return new PreparedVolumeFilter();
}
-AudioFormat
-VolumeFilter::Open(AudioFormat &audio_format, Error &error)
+Filter *
+PreparedVolumeFilter::Open(AudioFormat &audio_format, Error &error)
{
- if (!pv.Open(audio_format.format, error))
- return AudioFormat::Undefined();
-
- return audio_format;
-}
+ auto *filter = new VolumeFilter(audio_format);
+ if (!filter->Open(error)) {
+ delete filter;
+ return nullptr;
+ }
-void
-VolumeFilter::Close()
-{
- pv.Close();
+ return filter;
}
ConstBuffer<void>
diff --git a/src/output/Finish.cxx b/src/output/Finish.cxx
index a92718734..13c1c82bc 100644
--- a/src/output/Finish.cxx
+++ b/src/output/Finish.cxx
@@ -34,9 +34,9 @@ AudioOutput::~AudioOutput()
if (mixer != nullptr)
mixer_free(mixer);
- delete replay_gain_filter;
- delete other_replay_gain_filter;
- delete filter;
+ delete prepared_replay_gain_filter;
+ delete prepared_other_replay_gain_filter;
+ delete prepared_filter;
}
void
diff --git a/src/output/Init.cxx b/src/output/Init.cxx
index 5bfb4d109..00dc9d569 100644
--- a/src/output/Init.cxx
+++ b/src/output/Init.cxx
@@ -102,7 +102,7 @@ audio_output_mixer_type(const ConfigBlock &block)
"hardware"));
}
-static Filter *
+static PreparedFilter *
CreateVolumeFilter()
{
return filter_new(&volume_filter_plugin, ConfigBlock(),
@@ -113,12 +113,10 @@ static Mixer *
audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
const ConfigBlock &block,
const MixerPlugin *plugin,
- Filter &filter_chain,
+ PreparedFilter &filter_chain,
MixerListener &listener,
Error &error)
{
- assert(ao.volume_filter == nullptr);
-
Mixer *mixer;
switch (audio_output_mixer_type(block)) {
@@ -144,13 +142,8 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
IgnoreError());
assert(mixer != nullptr);
- ao.volume_filter = CreateVolumeFilter();
- assert(ao.volume_filter != nullptr);
-
filter_chain_append(filter_chain, "software_mixer",
- ao.volume_filter);
-
- software_mixer_set_filter(*mixer, ao.volume_filter);
+ ao.volume_filter.Set(CreateVolumeFilter()));
return mixer;
}
@@ -190,23 +183,23 @@ AudioOutput::Configure(const ConfigBlock &block, Error &error)
/* set up the filter chain */
- filter = filter_chain_new();
- assert(filter != nullptr);
+ prepared_filter = filter_chain_new();
+ assert(prepared_filter != nullptr);
/* create the normalization filter (if configured) */
if (config_get_bool(ConfigOption::VOLUME_NORMALIZATION, false)) {
- Filter *normalize_filter =
+ auto *normalize_filter =
filter_new(&normalize_filter_plugin, ConfigBlock(),
IgnoreError());
assert(normalize_filter != nullptr);
- filter_chain_append(*filter, "normalize",
+ filter_chain_append(*prepared_filter, "normalize",
autoconvert_filter_new(normalize_filter));
}
Error filter_error;
- filter_chain_parse(*filter,
+ filter_chain_parse(*prepared_filter,
block.GetBlockValue(AUDIO_FILTERS, ""),
filter_error);
@@ -235,21 +228,21 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
block.GetBlockValue("replay_gain_handler", "software");
if (strcmp(replay_gain_handler, "none") != 0) {
- ao.replay_gain_filter = filter_new(&replay_gain_filter_plugin,
- block, IgnoreError());
- assert(ao.replay_gain_filter != nullptr);
+ ao.prepared_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+ block, IgnoreError());
+ assert(ao.prepared_replay_gain_filter != nullptr);
ao.replay_gain_serial = 0;
- ao.other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
- block,
- IgnoreError());
- assert(ao.other_replay_gain_filter != nullptr);
+ ao.prepared_other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+ block,
+ IgnoreError());
+ assert(ao.prepared_other_replay_gain_filter != nullptr);
ao.other_replay_gain_serial = 0;
} else {
- ao.replay_gain_filter = nullptr;
- ao.other_replay_gain_filter = nullptr;
+ ao.prepared_replay_gain_filter = nullptr;
+ ao.prepared_other_replay_gain_filter = nullptr;
}
/* set up the mixer */
@@ -257,7 +250,7 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
Error mixer_error;
ao.mixer = audio_output_load_mixer(event_loop, ao, block,
ao.plugin.mixer_plugin,
- *ao.filter,
+ *ao.prepared_filter,
mixer_listener,
mixer_error);
if (ao.mixer == nullptr && mixer_error.IsDefined())
@@ -269,13 +262,13 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
if (strcmp(replay_gain_handler, "mixer") == 0) {
if (ao.mixer != nullptr)
- replay_gain_filter_set_mixer(ao.replay_gain_filter,
+ replay_gain_filter_set_mixer(ao.prepared_replay_gain_filter,
ao.mixer, 100);
else
FormatError(output_domain,
"No such mixer for output '%s'", ao.name);
} else if (strcmp(replay_gain_handler, "software") != 0 &&
- ao.replay_gain_filter != nullptr) {
+ ao.prepared_replay_gain_filter != nullptr) {
error.Set(config_domain,
"Invalid \"replay_gain_handler\" value");
return false;
@@ -283,11 +276,12 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
/* the "convert" filter must be the last one in the chain */
- ao.convert_filter = filter_new(&convert_filter_plugin, ConfigBlock(),
- IgnoreError());
- assert(ao.convert_filter != nullptr);
+ auto *f = filter_new(&convert_filter_plugin, ConfigBlock(),
+ IgnoreError());
+ assert(f != nullptr);
- filter_chain_append(*ao.filter, "convert", ao.convert_filter);
+ filter_chain_append(*ao.prepared_filter, "convert",
+ ao.convert_filter.Set(f));
return true;
}
diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx
index 0caa1235a..c9b0be5ed 100644
--- a/src/output/Internal.hxx
+++ b/src/output/Internal.hxx
@@ -24,12 +24,14 @@
#include "pcm/PcmBuffer.hxx"
#include "pcm/PcmDither.hxx"
#include "ReplayGainInfo.hxx"
+#include "filter/Observer.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "thread/Thread.hxx"
#include "system/PeriodClock.hxx"
class Error;
+class PreparedFilter;
class Filter;
class MusicPipe;
class EventLoop;
@@ -147,6 +149,8 @@ struct AudioOutput {
*/
bool woken_for_play = false;
+ ReplayGainMode replay_gain_mode = REPLAY_GAIN_OFF;
+
/**
* If not nullptr, the device has failed, and this timer is used
* to estimate how long it should stay disabled (unless
@@ -187,19 +191,21 @@ struct AudioOutput {
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
*/
- Filter *filter = nullptr;
+ PreparedFilter *prepared_filter = nullptr;
+ Filter *filter_instance;
/**
* The #VolumeFilter instance of this audio output. It is
* used by the #SoftwareMixer.
*/
- Filter *volume_filter = nullptr;
+ FilterObserver volume_filter;
/**
* The replay_gain_filter_plugin instance of this audio
* output.
*/
- Filter *replay_gain_filter = nullptr;
+ PreparedFilter *prepared_replay_gain_filter = nullptr;
+ Filter *replay_gain_filter_instance;
/**
* The serial number of the last replay gain info. 0 means no
@@ -212,7 +218,8 @@ struct AudioOutput {
* output, to be applied to the second chunk during
* cross-fading.
*/
- Filter *other_replay_gain_filter = nullptr;
+ PreparedFilter *prepared_other_replay_gain_filter = nullptr;
+ Filter *other_replay_gain_filter_instance;
/**
* The serial number of the last replay gain info by the
@@ -226,7 +233,7 @@ struct AudioOutput {
* for converting the input data into the appropriate format
* for this audio output.
*/
- Filter *convert_filter;
+ FilterObserver convert_filter;
/**
* The thread handle, or nullptr if the output thread isn't
@@ -346,7 +353,9 @@ struct AudioOutput {
*/
void LockRelease();
- void SetReplayGainMode(ReplayGainMode mode);
+ void SetReplayGainMode(ReplayGainMode _mode) {
+ replay_gain_mode = _mode;
+ }
/**
* Caller must lock the mutex.
diff --git a/src/output/OutputControl.cxx b/src/output/OutputControl.cxx
index 47d1be300..090db35d9 100644
--- a/src/output/OutputControl.cxx
+++ b/src/output/OutputControl.cxx
@@ -69,15 +69,6 @@ AudioOutput::LockCommandWait(Command cmd)
}
void
-AudioOutput::SetReplayGainMode(ReplayGainMode mode)
-{
- if (replay_gain_filter != nullptr)
- replay_gain_filter_set_mode(replay_gain_filter, mode);
- if (other_replay_gain_filter != nullptr)
- replay_gain_filter_set_mode(other_replay_gain_filter, mode);
-}
-
-void
AudioOutput::LockEnableWait()
{
if (!thread.IsDefined()) {
diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx
index 4b2f336e8..53fc2edfb 100644
--- a/src/output/OutputThread.cxx
+++ b/src/output/OutputThread.cxx
@@ -27,6 +27,8 @@
#include "filter/FilterInternal.hxx"
#include "filter/plugins/ConvertFilterPlugin.hxx"
#include "filter/plugins/ReplayGainFilterPlugin.hxx"
+#include "mixer/MixerInternal.hxx"
+#include "mixer/plugins/SoftwareMixerPlugin.hxx"
#include "player/Control.hxx"
#include "MusicPipe.hxx"
#include "MusicChunk.hxx"
@@ -94,37 +96,44 @@ AudioOutput::OpenFilter(AudioFormat &format, Error &error_r)
assert(format.IsValid());
/* the replay_gain filter cannot fail here */
- if (replay_gain_filter != nullptr &&
- !replay_gain_filter->Open(format, error_r).IsDefined())
- return AudioFormat::Undefined();
+ if (prepared_replay_gain_filter != nullptr) {
+ replay_gain_filter_instance =
+ prepared_replay_gain_filter->Open(format, error_r);
+ if (replay_gain_filter_instance == nullptr)
+ return AudioFormat::Undefined();
+ }
- if (other_replay_gain_filter != nullptr &&
- !other_replay_gain_filter->Open(format, error_r).IsDefined()) {
- if (replay_gain_filter != nullptr)
- replay_gain_filter->Close();
- return AudioFormat::Undefined();
+ if (prepared_other_replay_gain_filter != nullptr) {
+ other_replay_gain_filter_instance =
+ prepared_other_replay_gain_filter->Open(format, error_r);
+ if (other_replay_gain_filter_instance == nullptr) {
+ delete replay_gain_filter_instance;
+ return AudioFormat::Undefined();
+ }
}
- const AudioFormat af = filter->Open(format, error_r);
- if (!af.IsDefined()) {
- if (replay_gain_filter != nullptr)
- replay_gain_filter->Close();
- if (other_replay_gain_filter != nullptr)
- other_replay_gain_filter->Close();
+ filter_instance = prepared_filter->Open(format, error_r);
+ if (filter_instance == nullptr) {
+ delete other_replay_gain_filter_instance;
+ delete replay_gain_filter_instance;
+ return AudioFormat::Undefined();
}
- return af;
+ if (mixer != nullptr && mixer->IsPlugin(software_mixer_plugin))
+ software_mixer_set_filter(*mixer, volume_filter.Get());
+
+ return filter_instance->GetOutAudioFormat();
}
void
AudioOutput::CloseFilter()
{
- if (replay_gain_filter != nullptr)
- replay_gain_filter->Close();
- if (other_replay_gain_filter != nullptr)
- other_replay_gain_filter->Close();
+ if (mixer != nullptr && mixer->IsPlugin(software_mixer_plugin))
+ software_mixer_set_filter(*mixer, nullptr);
- filter->Close();
+ delete replay_gain_filter_instance;
+ delete other_replay_gain_filter_instance;
+ delete filter_instance;
}
inline void
@@ -186,7 +195,7 @@ AudioOutput::Open()
return;
}
- if (!convert_filter_set(convert_filter, out_audio_format,
+ if (!convert_filter_set(convert_filter.Get(), out_audio_format,
error)) {
FormatError(error, "Failed to convert for \"%s\" [%s]",
name, plugin.name);
@@ -282,7 +291,7 @@ AudioOutput::ReopenFilter()
const AudioFormat filter_audio_format =
OpenFilter(in_audio_format, error);
if (!filter_audio_format.IsDefined() ||
- !convert_filter_set(convert_filter, out_audio_format,
+ !convert_filter_set(convert_filter.Get(), out_audio_format,
error)) {
FormatError(error,
"Failed to open filter for \"%s\" [%s]",
@@ -368,6 +377,9 @@ ao_chunk_data(AudioOutput *ao, const MusicChunk *chunk,
assert(data.size % ao->in_audio_format.GetFrameSize() == 0);
if (!data.IsEmpty() && replay_gain_filter != nullptr) {
+ replay_gain_filter_set_mode(replay_gain_filter,
+ ao->replay_gain_mode);
+
if (chunk->replay_gain_serial != *replay_gain_serial_p) {
replay_gain_filter_set_info(replay_gain_filter,
chunk->replay_gain_serial != 0
@@ -390,7 +402,7 @@ static ConstBuffer<void>
ao_filter_chunk(AudioOutput *ao, const MusicChunk *chunk)
{
ConstBuffer<void> data =
- ao_chunk_data(ao, chunk, ao->replay_gain_filter,
+ ao_chunk_data(ao, chunk, ao->replay_gain_filter_instance,
&ao->replay_gain_serial);
if (data.IsEmpty())
return data;
@@ -400,7 +412,7 @@ ao_filter_chunk(AudioOutput *ao, const MusicChunk *chunk)
if (chunk->other != nullptr) {
ConstBuffer<void> other_data =
ao_chunk_data(ao, chunk->other,
- ao->other_replay_gain_filter,
+ ao->other_replay_gain_filter_instance,
&ao->other_replay_gain_serial);
if (other_data.IsNull())
return nullptr;
@@ -443,7 +455,7 @@ ao_filter_chunk(AudioOutput *ao, const MusicChunk *chunk)
/* apply filter chain */
Error error;
- data = ao->filter->FilterPCM(data, error);
+ data = ao->filter_instance->FilterPCM(data, error);
if (data.IsNull()) {
FormatError(error, "\"%s\" [%s] failed to filter",
ao->name, ao->plugin.name);
@@ -456,7 +468,7 @@ ao_filter_chunk(AudioOutput *ao, const MusicChunk *chunk)
inline bool
AudioOutput::PlayChunk(const MusicChunk *chunk)
{
- assert(filter != nullptr);
+ assert(filter_instance != nullptr);
if (tags && gcc_unlikely(chunk->tag != nullptr)) {
mutex.unlock();
diff --git a/test/run_filter.cxx b/test/run_filter.cxx
index a4e9c3653..0fa414e39 100644
--- a/test/run_filter.cxx
+++ b/test/run_filter.cxx
@@ -32,6 +32,8 @@
#include "system/FatalError.hxx"
#include "Log.hxx"
+#include <memory>
+
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -46,7 +48,7 @@ mixer_set_volume(gcc_unused Mixer *mixer,
return true;
}
-static Filter *
+static PreparedFilter *
load_filter(const char *name)
{
const auto *param = config_find_block(ConfigBlockOption::AUDIO_FILTER,
@@ -57,7 +59,7 @@ load_filter(const char *name)
}
Error error;
- Filter *filter = filter_configured_new(*param, error);
+ auto *filter = filter_configured_new(*param, error);
if (filter == NULL) {
LogError(error, "Failed to load filter");
return NULL;
@@ -97,20 +99,22 @@ try {
/* initialize the filter */
- Filter *filter = load_filter(argv[2]);
- if (filter == NULL)
+ std::unique_ptr<PreparedFilter> prepared_filter(load_filter(argv[2]));
+ if (!prepared_filter)
return EXIT_FAILURE;
/* open the filter */
Error error;
- const AudioFormat out_audio_format = filter->Open(audio_format, error);
- if (!out_audio_format.IsDefined()) {
+ std::unique_ptr<Filter> filter(prepared_filter->Open(audio_format,
+ error));
+ if (!filter) {
LogError(error, "Failed to open filter");
- delete filter;
return EXIT_FAILURE;
}
+ const AudioFormat out_audio_format = filter->GetOutAudioFormat();
+
fprintf(stderr, "audio_format=%s\n",
audio_format_to_string(out_audio_format, &af_string));
@@ -127,8 +131,6 @@ try {
error);
if (dest.IsNull()) {
LogError(error, "filter/Filter failed");
- filter->Close();
- delete filter;
return EXIT_FAILURE;
}
@@ -136,17 +138,12 @@ try {
if (nbytes < 0) {
fprintf(stderr, "Failed to write: %s\n",
strerror(errno));
- filter->Close();
- delete filter;
return 1;
}
}
/* cleanup and exit */
- filter->Close();
- delete filter;
-
config_global_finish();
return EXIT_SUCCESS;