diff options
author | Max Kellermann <max@duempel.org> | 2016-06-22 11:15:49 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2016-07-01 21:04:24 +0200 |
commit | 3a212412487e8868946e7c845b6830f880790f91 (patch) | |
tree | 5a6120b77a34e1c24da340a66fe5a8e56666333b | |
parent | 5c75096bcd21f2780e469e06bc4fc7bddf75684c (diff) |
filter/FilterInternal: split class Filter, add class PreparedFilter
For easier state management inside filter plugins.
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; |