From a9ce0218c1879a752c9d9ec6ef21fcf44eab51ab Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 1 Feb 2013 18:40:36 +0100 Subject: [PATCH] FilterInternal: convert struct filter to a OO interface --- src/FilterConfig.cxx | 15 +- src/FilterConfig.hxx | 4 +- src/FilterInternal.hxx | 49 +++++-- src/FilterPlugin.cxx | 58 +------- src/FilterPlugin.hxx | 87 +---------- src/OutputFinish.cxx | 12 +- src/OutputInit.cxx | 14 +- src/OutputThread.cxx | 42 +++--- src/filter/AutoConvertFilterPlugin.cxx | 127 ++++++---------- src/filter/AutoConvertFilterPlugin.hxx | 6 +- src/filter/ChainFilterPlugin.cxx | 143 +++++++++--------- src/filter/ChainFilterPlugin.hxx | 6 +- src/filter/ConvertFilterPlugin.cxx | 105 ++++++-------- src/filter/ConvertFilterPlugin.hxx | 5 +- src/filter/NormalizeFilterPlugin.cxx | 73 ++++------ src/filter/NullFilterPlugin.cxx | 66 +++------ src/filter/ReplayGainFilterPlugin.cxx | 191 ++++++++++++------------- src/filter/ReplayGainFilterPlugin.hxx | 9 +- src/filter/RouteFilterPlugin.cxx | 161 +++++++++------------ src/filter/VolumeFilterPlugin.cxx | 111 +++++++------- src/filter/VolumeFilterPlugin.hxx | 6 +- src/mixer/SoftwareMixerPlugin.cxx | 4 +- src/mixer/SoftwareMixerPlugin.hxx | 4 +- src/output_internal.h | 14 +- test/run_filter.cxx | 29 ++-- 25 files changed, 534 insertions(+), 807 deletions(-) diff --git a/src/FilterConfig.cxx b/src/FilterConfig.cxx index 7fca2ae67..e56c5a988 100644 --- a/src/FilterConfig.cxx +++ b/src/FilterConfig.cxx @@ -78,7 +78,7 @@ filter_plugin_config(const char *filter_template_name, GError **error_r) * @return the number of filters which were successfully added */ unsigned int -filter_chain_parse(struct filter *chain, const char *spec, GError **error_r) +filter_chain_parse(Filter &chain, const char *spec, GError **error_r) { // Split on comma @@ -89,26 +89,27 @@ filter_chain_parse(struct filter *chain, const char *spec, GError **error_r) // Add each name to the filter chain by instantiating an actual filter char **template_names = tokens; while (*template_names != NULL) { - struct filter *f; - const struct config_param *cfg; - // Squeeze whitespace g_strstrip(*template_names); - cfg = filter_plugin_config(*template_names, error_r); + const struct config_param *cfg = + filter_plugin_config(*template_names, error_r); if (cfg == NULL) { // The error has already been set, just stop. break; } // Instantiate one of those filter plugins with the template name as a hint - f = filter_configured_new(cfg, error_r); + Filter *f = filter_configured_new(cfg, error_r); if (f == NULL) { // The error has already been set, just stop. break; } - filter_chain_append(chain, f); + const char *plugin_name = + config_get_block_string(cfg, "plugin", "unknown"); + + filter_chain_append(chain, plugin_name, f); ++added_filters; ++template_names; diff --git a/src/FilterConfig.hxx b/src/FilterConfig.hxx index 18cc5c44f..bad186354 100644 --- a/src/FilterConfig.hxx +++ b/src/FilterConfig.hxx @@ -27,7 +27,7 @@ #include "gerror.h" -struct filter; +class Filter; /** * Builds a filter chain from a configuration string on the form @@ -39,6 +39,6 @@ struct filter; * @return the number of filters which were successfully added */ unsigned int -filter_chain_parse(struct filter *chain, const char *spec, GError **error_r); +filter_chain_parse(Filter &chain, const char *spec, GError **error_r); #endif diff --git a/src/FilterInternal.hxx b/src/FilterInternal.hxx index 7ffcec334..cdc2d0ea1 100644 --- a/src/FilterInternal.hxx +++ b/src/FilterInternal.hxx @@ -25,14 +25,47 @@ #ifndef MPD_FILTER_INTERNAL_HXX #define MPD_FILTER_INTERNAL_HXX -struct filter { - const struct filter_plugin *plugin; +struct audio_format; + +class Filter { +public: + virtual ~Filter() {} + + /** + * Opens the filter, preparing it for FilterPCM(). + * + * @param filter the filter object + * @param audio_format 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, or NULL + * to ignore errors. + * @return the format of outgoing data + */ + virtual const audio_format *Open(audio_format &af, + GError **error_r) = 0; + + /** + * Closes the filter. After that, you may call Open() again. + */ + virtual void Close() = 0; + + /** + * Filters a block of PCM data. + * + * @param filter the filter object + * @param src the input buffer + * @param src_size the size of #src_buffer in bytes + * @param dest_size_r the size of the returned buffer + * @param error location to store the error occurring, or NULL + * to ignore errors. + * @return the destination buffer on success (will be + * invalidated by filter_close() or filter_filter()), NULL on + * error + */ + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, + GError **error_r) = 0; }; -static inline void -filter_init(struct filter *filter, const struct filter_plugin *plugin) -{ - filter->plugin = plugin; -} - #endif diff --git a/src/FilterPlugin.cxx b/src/FilterPlugin.cxx index f1f7561a9..953f404ed 100644 --- a/src/FilterPlugin.cxx +++ b/src/FilterPlugin.cxx @@ -24,13 +24,9 @@ #include "conf.h" #include "ConfigQuark.hxx" -#ifndef NDEBUG -#include "audio_format.h" -#endif - #include -struct filter * +Filter * filter_new(const struct filter_plugin *plugin, const struct config_param *param, GError **error_r) { @@ -40,7 +36,7 @@ filter_new(const struct filter_plugin *plugin, return plugin->init(param, error_r); } -struct filter * +Filter * filter_configured_new(const struct config_param *param, GError **error_r) { const char *plugin_name; @@ -65,53 +61,3 @@ filter_configured_new(const struct config_param *param, GError **error_r) return filter_new(plugin, param, error_r); } - -void -filter_free(struct filter *filter) -{ - assert(filter != NULL); - - filter->plugin->finish(filter); -} - -const struct audio_format * -filter_open(struct filter *filter, struct audio_format *audio_format, - GError **error_r) -{ - const struct audio_format *out_audio_format; - - assert(filter != NULL); - assert(audio_format != NULL); - assert(audio_format_valid(audio_format)); - assert(error_r == NULL || *error_r == NULL); - - out_audio_format = filter->plugin->open(filter, audio_format, error_r); - - assert(out_audio_format == NULL || audio_format_valid(audio_format)); - assert(out_audio_format == NULL || - audio_format_valid(out_audio_format)); - - return out_audio_format; -} - -void -filter_close(struct filter *filter) -{ - assert(filter != NULL); - - filter->plugin->close(filter); -} - -const void * -filter_filter(struct filter *filter, const void *src, size_t src_size, - size_t *dest_size_r, - GError **error_r) -{ - assert(filter != NULL); - assert(src != NULL); - assert(src_size > 0); - assert(dest_size_r != NULL); - assert(error_r == NULL || *error_r == NULL); - - return filter->plugin->filter(filter, src, src_size, dest_size_r, error_r); -} diff --git a/src/FilterPlugin.hxx b/src/FilterPlugin.hxx index 6476a621f..80bb8a0b6 100644 --- a/src/FilterPlugin.hxx +++ b/src/FilterPlugin.hxx @@ -31,7 +31,7 @@ #include struct config_param; -struct filter; +class Filter; struct filter_plugin { const char *name; @@ -39,38 +39,7 @@ struct filter_plugin { /** * Allocates and configures a filter. */ - struct filter *(*init)(const struct config_param *param, - GError **error_r); - - /** - * Free instance data. - */ - void (*finish)(struct filter *filter); - - /** - * Opens a filter. - * - * @param audio_format the audio format of incoming data; the - * plugin may modify the object to enforce another input - * format - */ - const struct audio_format * - (*open)(struct filter *filter, - struct audio_format *audio_format, - GError **error_r); - - /** - * Closes a filter. - */ - void (*close)(struct filter *filter); - - /** - * Filters a block of PCM data. - */ - const void *(*filter)(struct filter *filter, - const void *src, size_t src_size, - size_t *dest_buffer_r, - GError **error_r); + Filter *(*init)(const struct config_param *param, GError **error_r); }; /** @@ -82,7 +51,7 @@ struct filter_plugin { * ignore errors. * @return a new filter object, or NULL on error */ -struct filter * +Filter * filter_new(const struct filter_plugin *plugin, const struct config_param *param, GError **error_r); @@ -95,55 +64,7 @@ filter_new(const struct filter_plugin *plugin, * ignore errors. * @return a new filter object, or NULL on error */ -struct filter * +Filter * filter_configured_new(const struct config_param *param, GError **error_r); -/** - * Deletes a filter. It must be closed prior to calling this - * function, see filter_close(). - * - * @param filter the filter object - */ -void -filter_free(struct filter *filter); - -/** - * Opens the filter, preparing it for filter_filter(). - * - * @param filter the filter object - * @param audio_format 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, or NULL to - * ignore errors. - * @return the format of outgoing data - */ -const struct audio_format * -filter_open(struct filter *filter, struct audio_format *audio_format, - GError **error_r); - -/** - * Closes the filter. After that, you may call filter_open() again. - * - * @param filter the filter object - */ -void -filter_close(struct filter *filter); - -/** - * Filters a block of PCM data. - * - * @param filter the filter object - * @param src the input buffer - * @param src_size the size of #src_buffer in bytes - * @param dest_size_r the size of the returned buffer - * @param error location to store the error occurring, or NULL to - * ignore errors. - * @return the destination buffer on success (will be invalidated by - * filter_close() or filter_filter()), NULL on error - */ -const void * -filter_filter(struct filter *filter, const void *src, size_t src_size, - size_t *dest_size_r, - GError **error_r); - #endif diff --git a/src/OutputFinish.cxx b/src/OutputFinish.cxx index f5a1f9921..8b9480b88 100644 --- a/src/OutputFinish.cxx +++ b/src/OutputFinish.cxx @@ -25,7 +25,7 @@ extern "C" { #include "mixer_control.h" } -#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" #include @@ -42,13 +42,9 @@ ao_base_finish(struct audio_output *ao) g_cond_free(ao->cond); g_mutex_free(ao->mutex); - if (ao->replay_gain_filter != NULL) - filter_free(ao->replay_gain_filter); - - if (ao->other_replay_gain_filter != NULL) - filter_free(ao->other_replay_gain_filter); - - filter_free(ao->filter); + delete ao->replay_gain_filter; + delete ao->other_replay_gain_filter; + delete ao->filter; pcm_buffer_deinit(&ao->cross_fade_buffer); } diff --git a/src/OutputInit.cxx b/src/OutputInit.cxx index 5acb8c6cb..8c60fe4f1 100644 --- a/src/OutputInit.cxx +++ b/src/OutputInit.cxx @@ -100,7 +100,7 @@ static struct mixer * audio_output_load_mixer(struct audio_output *ao, const struct config_param *param, const struct mixer_plugin *plugin, - struct filter *filter_chain, + Filter &filter_chain, GError **error_r) { struct mixer *mixer; @@ -120,7 +120,7 @@ audio_output_load_mixer(struct audio_output *ao, mixer = mixer_new(&software_mixer_plugin, NULL, NULL, NULL); assert(mixer != NULL); - filter_chain_append(filter_chain, + filter_chain_append(filter_chain, "software_mixer", software_mixer_get_filter(mixer)); return mixer; } @@ -190,15 +190,15 @@ ao_base_init(struct audio_output *ao, /* create the normalization filter (if configured) */ if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) { - struct filter *normalize_filter = + Filter *normalize_filter = filter_new(&normalize_filter_plugin, NULL, NULL); assert(normalize_filter != NULL); - filter_chain_append(ao->filter, + filter_chain_append(*ao->filter, "normalize", autoconvert_filter_new(normalize_filter)); } - filter_chain_parse(ao->filter, + filter_chain_parse(*ao->filter, config_get_block_string(param, AUDIO_FILTERS, ""), &error ); @@ -258,7 +258,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param, GError *error = NULL; ao->mixer = audio_output_load_mixer(ao, param, ao->plugin->mixer_plugin, - ao->filter, &error); + *ao->filter, &error); if (ao->mixer == NULL && error != NULL) { g_warning("Failed to initialize hardware mixer for '%s': %s", ao->name, error->message); @@ -285,7 +285,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param, ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL); assert(ao->convert_filter != NULL); - filter_chain_append(ao->filter, ao->convert_filter); + filter_chain_append(*ao->filter, "convert", ao->convert_filter); return true; } diff --git a/src/OutputThread.cxx b/src/OutputThread.cxx index e3efbceaa..f1ffe876f 100644 --- a/src/OutputThread.cxx +++ b/src/OutputThread.cxx @@ -27,7 +27,7 @@ extern "C" { } #include "notify.hxx" -#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" #include "filter/ConvertFilterPlugin.hxx" #include "filter/ReplayGainFilterPlugin.hxx" #include "PlayerControl.hxx" @@ -98,26 +98,24 @@ ao_disable(struct audio_output *ao) } static const struct audio_format * -ao_filter_open(struct audio_output *ao, - struct audio_format *audio_format, +ao_filter_open(struct audio_output *ao, audio_format &format, GError **error_r) { - assert(audio_format_valid(audio_format)); + assert(audio_format_valid(&format)); /* the replay_gain filter cannot fail here */ if (ao->replay_gain_filter != NULL) - filter_open(ao->replay_gain_filter, audio_format, error_r); + ao->replay_gain_filter->Open(format, error_r); if (ao->other_replay_gain_filter != NULL) - filter_open(ao->other_replay_gain_filter, audio_format, - error_r); + ao->other_replay_gain_filter->Open(format, error_r); const struct audio_format *af - = filter_open(ao->filter, audio_format, error_r); + = ao->filter->Open(format, error_r); if (af == NULL) { if (ao->replay_gain_filter != NULL) - filter_close(ao->replay_gain_filter); + ao->replay_gain_filter->Close(); if (ao->other_replay_gain_filter != NULL) - filter_close(ao->other_replay_gain_filter); + ao->other_replay_gain_filter->Close(); } return af; @@ -127,11 +125,11 @@ static void ao_filter_close(struct audio_output *ao) { if (ao->replay_gain_filter != NULL) - filter_close(ao->replay_gain_filter); + ao->replay_gain_filter->Close(); if (ao->other_replay_gain_filter != NULL) - filter_close(ao->other_replay_gain_filter); + ao->other_replay_gain_filter->Close(); - filter_close(ao->filter); + ao->filter->Close(); } static void @@ -139,7 +137,6 @@ ao_open(struct audio_output *ao) { bool success; GError *error = NULL; - const struct audio_format *filter_audio_format; struct audio_format_string af_string; assert(!ao->open); @@ -164,7 +161,8 @@ ao_open(struct audio_output *ao) /* open the filter */ - filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error); + const audio_format *filter_audio_format = + ao_filter_open(ao, ao->in_audio_format, &error); if (filter_audio_format == NULL) { g_warning("Failed to open filter for \"%s\" [%s]: %s", ao->name, ao->plugin->name, error->message); @@ -196,7 +194,7 @@ ao_open(struct audio_output *ao) return; } - convert_filter_set(ao->convert_filter, &ao->out_audio_format); + convert_filter_set(ao->convert_filter, ao->out_audio_format); ao->open = true; @@ -244,7 +242,7 @@ ao_reopen_filter(struct audio_output *ao) GError *error = NULL; ao_filter_close(ao); - filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error); + filter_audio_format = ao_filter_open(ao, ao->in_audio_format, &error); if (filter_audio_format == NULL) { g_warning("Failed to open filter for \"%s\" [%s]: %s", ao->name, ao->plugin->name, error->message); @@ -267,7 +265,7 @@ ao_reopen_filter(struct audio_output *ao) return; } - convert_filter_set(ao->convert_filter, &ao->out_audio_format); + convert_filter_set(ao->convert_filter, ao->out_audio_format); } static void @@ -322,7 +320,7 @@ ao_wait(struct audio_output *ao) static const void * ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, - struct filter *replay_gain_filter, + Filter *replay_gain_filter, unsigned *replay_gain_serial_p, size_t *length_r) { @@ -347,8 +345,8 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, } GError *error = NULL; - data = filter_filter(replay_gain_filter, data, length, - &length, &error); + data = replay_gain_filter->FilterPCM(data, length, + &length, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); @@ -421,7 +419,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk, /* apply filter chain */ - data = filter_filter(ao->filter, data, length, &length, &error); + data = ao->filter->FilterPCM(data, length, &length, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); diff --git a/src/filter/AutoConvertFilterPlugin.cxx b/src/filter/AutoConvertFilterPlugin.cxx index 2a183b579..55ee46948 100644 --- a/src/filter/AutoConvertFilterPlugin.cxx +++ b/src/filter/AutoConvertFilterPlugin.cxx @@ -27,143 +27,106 @@ #include -struct AutoConvertFilter { - struct filter base; - +class AutoConvertFilter final : public Filter { /** * The audio format being fed to the underlying filter. This * plugin actually doesn't need this variable, we have it here * just so our open() method doesn't return a stack pointer. */ - struct audio_format in_audio_format; + audio_format child_audio_format; /** * The underlying filter. */ - struct filter *filter; + Filter *filter; /** * A convert_filter, just in case conversion is needed. nullptr * if unused. */ - struct filter *convert; - - AutoConvertFilter(const filter_plugin &plugin, struct filter *_filter) - :filter(_filter) { - filter_init(&base, &plugin); - } + Filter *convert; +public: + AutoConvertFilter(Filter *_filter):filter(_filter) {} ~AutoConvertFilter() { - filter_free(filter); + delete filter; } + + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; -static void -autoconvert_filter_finish(struct filter *_filter) +const struct audio_format * +AutoConvertFilter::Open(audio_format &in_audio_format, GError **error_r) { - AutoConvertFilter *filter = (AutoConvertFilter *)_filter; - - delete filter; -} - -static const struct audio_format * -autoconvert_filter_open(struct filter *_filter, - struct audio_format *in_audio_format, - GError **error_r) -{ - AutoConvertFilter *filter = (AutoConvertFilter *)_filter; - const struct audio_format *out_audio_format; - - assert(audio_format_valid(in_audio_format)); + assert(audio_format_valid(&in_audio_format)); /* open the "real" filter */ - filter->in_audio_format = *in_audio_format; - - out_audio_format = filter_open(filter->filter, - &filter->in_audio_format, error_r); + child_audio_format = in_audio_format; + const audio_format *out_audio_format = + filter->Open(child_audio_format, error_r); if (out_audio_format == nullptr) return nullptr; /* need to convert? */ - if (!audio_format_equals(&filter->in_audio_format, in_audio_format)) { + if (!audio_format_equals(&child_audio_format, &in_audio_format)) { /* yes - create a convert_filter */ - struct audio_format audio_format2 = *in_audio_format; - const struct audio_format *audio_format3; - filter->convert = filter_new(&convert_filter_plugin, nullptr, - error_r); - if (filter->convert == nullptr) { - filter_close(filter->filter); + convert = filter_new(&convert_filter_plugin, nullptr, error_r); + if (convert == nullptr) { + filter->Close(); return nullptr; } - audio_format3 = filter_open(filter->convert, &audio_format2, - error_r); + audio_format audio_format2 = in_audio_format; + const audio_format *audio_format3 = + convert->Open(audio_format2, error_r); if (audio_format3 == nullptr) { - filter_free(filter->convert); - filter_close(filter->filter); + delete convert; + filter->Close(); return nullptr; } - assert(audio_format_equals(&audio_format2, in_audio_format)); + assert(audio_format_equals(&audio_format2, &in_audio_format)); - convert_filter_set(filter->convert, &filter->in_audio_format); + convert_filter_set(convert, child_audio_format); } else /* no */ - filter->convert = nullptr; + convert = nullptr; return out_audio_format; } -static void -autoconvert_filter_close(struct filter *_filter) +void +AutoConvertFilter::Close() { - AutoConvertFilter *filter = - (AutoConvertFilter *)_filter; - - if (filter->convert != nullptr) { - filter_close(filter->convert); - filter_free(filter->convert); + if (convert != nullptr) { + convert->Close(); + delete convert; } - filter_close(filter->filter); + filter->Close(); } -static const void * -autoconvert_filter_filter(struct filter *_filter, const void *src, - size_t src_size, size_t *dest_size_r, - GError **error_r) +const void * +AutoConvertFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) { - AutoConvertFilter *filter = (AutoConvertFilter *)_filter; - - if (filter->convert != nullptr) { - src = filter_filter(filter->convert, src, src_size, &src_size, - error_r); + if (convert != nullptr) { + src = convert->FilterPCM(src, src_size, &src_size, error_r); if (src == nullptr) return nullptr; } - return filter_filter(filter->filter, src, src_size, dest_size_r, - error_r); + return filter->FilterPCM(src, src_size, dest_size_r, error_r); } -static const struct filter_plugin autoconvert_filter_plugin = { - "convert", - nullptr, - autoconvert_filter_finish, - autoconvert_filter_open, - autoconvert_filter_close, - autoconvert_filter_filter, -}; - -struct filter * -autoconvert_filter_new(struct filter *_filter) +Filter * +autoconvert_filter_new(Filter *filter) { - AutoConvertFilter *filter = - new AutoConvertFilter(autoconvert_filter_plugin, - _filter); - - return &filter->base; + return new AutoConvertFilter(filter); } diff --git a/src/filter/AutoConvertFilterPlugin.hxx b/src/filter/AutoConvertFilterPlugin.hxx index 062efcbd0..7db72a345 100644 --- a/src/filter/AutoConvertFilterPlugin.hxx +++ b/src/filter/AutoConvertFilterPlugin.hxx @@ -20,7 +20,7 @@ #ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX #define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX -struct filter; +class Filter; /** * Creates a new "autoconvert" filter. When opened, it ensures that @@ -28,7 +28,7 @@ struct filter; * requests a different format, it automatically creates a * convert_filter. */ -struct filter * -autoconvert_filter_new(struct filter *filter); +Filter * +autoconvert_filter_new(Filter *filter); #endif diff --git a/src/filter/ChainFilterPlugin.cxx b/src/filter/ChainFilterPlugin.cxx index b232d6568..c8666615f 100644 --- a/src/filter/ChainFilterPlugin.cxx +++ b/src/filter/ChainFilterPlugin.cxx @@ -31,20 +31,39 @@ #include -struct ChainFilter { - /** the base class */ - struct filter base; +class ChainFilter final : public Filter { + struct Child { + const char *name; + Filter *filter; - std::list children; + Child(const char *_name, Filter *_filter) + :name(_name), filter(_filter) {} + ~Child() { + delete filter; + } - ChainFilter() { - filter_init(&base, &chain_filter_plugin); + Child(const Child &) = delete; + Child &operator=(const Child &) = delete; + }; + + std::list children; + +public: + void Append(const char *name, Filter *filter) { + children.emplace_back(name, filter); } - ~ChainFilter() { - for (auto i : children) - filter_free(i); - } + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); + +private: + /** + * Close all filters in the chain until #until is reached. + * #until itself is not closed. + */ + void CloseUntil(const Filter *until); }; static inline GQuark @@ -53,37 +72,23 @@ filter_quark(void) return g_quark_from_static_string("filter"); } -static struct filter * +static Filter * chain_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - ChainFilter *chain = new ChainFilter(); - - return &chain->base; + return new ChainFilter(); } -static void -chain_filter_finish(struct filter *_filter) +void +ChainFilter::CloseUntil(const Filter *until) { - ChainFilter *chain = (ChainFilter *)_filter; - - delete chain; -} - -/** - * Close all filters in the chain until #until is reached. #until - * itself is not closed. - */ -static void -chain_close_until(ChainFilter *chain, const struct filter *until) -{ - for (auto filter : chain->children) { - if (filter == until) + for (auto &child : children) { + if (child.filter == until) /* don't close this filter */ return; /* close this filter */ - filter_close(filter); + child.filter->Close(); } /* this assertion fails if #until does not exist (anymore) */ @@ -91,43 +96,41 @@ chain_close_until(ChainFilter *chain, const struct filter *until) } static const struct audio_format * -chain_open_child(struct filter *filter, - const struct audio_format *prev_audio_format, +chain_open_child(const char *name, Filter *filter, + const audio_format &prev_audio_format, GError **error_r) { - struct audio_format conv_audio_format = *prev_audio_format; - const struct audio_format *next_audio_format; - - next_audio_format = filter_open(filter, &conv_audio_format, error_r); + audio_format conv_audio_format = prev_audio_format; + const audio_format *next_audio_format = + filter->Open(conv_audio_format, error_r); if (next_audio_format == NULL) return NULL; - if (!audio_format_equals(&conv_audio_format, prev_audio_format)) { + if (!audio_format_equals(&conv_audio_format, &prev_audio_format)) { struct audio_format_string s; - filter_close(filter); + filter->Close(); g_set_error(error_r, filter_quark(), 0, "Audio format not supported by filter '%s': %s", - filter->plugin->name, - audio_format_to_string(prev_audio_format, &s)); + name, + audio_format_to_string(&prev_audio_format, &s)); return NULL; } return next_audio_format; } -static const struct audio_format * -chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format, - GError **error_r) +const audio_format * +ChainFilter::Open(audio_format &in_audio_format, GError **error_r) { - ChainFilter *chain = (ChainFilter *)_filter; - const struct audio_format *audio_format = in_audio_format; + const audio_format *audio_format = &in_audio_format; - for (auto filter : chain->children) { - audio_format = chain_open_child(filter, audio_format, error_r); + for (auto &child : children) { + audio_format = chain_open_child(child.name, child.filter, + *audio_format, error_r); if (audio_format == NULL) { /* rollback, close all children */ - chain_close_until(chain, filter); + CloseUntil(child.filter); return NULL; } } @@ -136,26 +139,22 @@ chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format, return audio_format; } -static void -chain_filter_close(struct filter *_filter) +void +ChainFilter::Close() { - ChainFilter *chain = (ChainFilter *)_filter; - - for (auto filter : chain->children) - filter_close(filter); + for (auto &child : children) + child.filter->Close(); } -static const void * -chain_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) +const void * +ChainFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) { - ChainFilter *chain = (ChainFilter *)_filter; - - for (auto filter : chain->children) { + for (auto &child : children) { /* feed the output of the previous filter as input into the current one */ - src = filter_filter(filter, src, src_size, &src_size, error_r); + src = child.filter->FilterPCM(src, src_size, &src_size, + error_r); if (src == NULL) return NULL; } @@ -168,26 +167,18 @@ chain_filter_filter(struct filter *_filter, const struct filter_plugin chain_filter_plugin = { "chain", chain_filter_init, - chain_filter_finish, - chain_filter_open, - chain_filter_close, - chain_filter_filter, }; -struct filter * +Filter * filter_chain_new(void) { - struct filter *filter = filter_new(&chain_filter_plugin, NULL, NULL); - /* chain_filter_init() never fails */ - assert(filter != NULL); - - return filter; + return new ChainFilter(); } void -filter_chain_append(struct filter *_chain, struct filter *filter) +filter_chain_append(Filter &_chain, const char *name, Filter *filter) { - ChainFilter *chain = (ChainFilter *)_chain; + ChainFilter &chain = (ChainFilter &)_chain; - chain->children.push_back(filter); + chain.Append(name, filter); } diff --git a/src/filter/ChainFilterPlugin.hxx b/src/filter/ChainFilterPlugin.hxx index 242d29dae..884c7ca19 100644 --- a/src/filter/ChainFilterPlugin.hxx +++ b/src/filter/ChainFilterPlugin.hxx @@ -27,12 +27,12 @@ #ifndef MPD_FILTER_CHAIN_HXX #define MPD_FILTER_CHAIN_HXX -struct filter; +class Filter; /** * Creates a new filter chain. */ -struct filter * +Filter * filter_chain_new(void); /** @@ -43,6 +43,6 @@ filter_chain_new(void); * @param filter the filter to be appended to #chain */ void -filter_chain_append(struct filter *chain, struct filter *filter); +filter_chain_append(Filter &chain, const char *name, Filter *filter); #endif diff --git a/src/filter/ConvertFilterPlugin.cxx b/src/filter/ConvertFilterPlugin.cxx index b07cf80f1..2c6907655 100644 --- a/src/filter/ConvertFilterPlugin.cxx +++ b/src/filter/ConvertFilterPlugin.cxx @@ -31,114 +31,89 @@ #include #include -struct ConvertFilter { - struct filter base; - +class ConvertFilter final : public Filter { /** * The input audio format; PCM data is passed to the filter() * method in this format. */ - struct audio_format in_audio_format; + audio_format in_audio_format; /** * The output audio format; the consumer of this plugin * expects PCM data in this format. This defaults to * #in_audio_format, and can be set with convert_filter_set(). */ - struct audio_format out_audio_format; + audio_format out_audio_format; Manual state; - ConvertFilter() { - filter_init(&base, &convert_filter_plugin); +public: + void Set(const audio_format &_out_audio_format) { + assert(audio_format_valid(&in_audio_format)); + assert(audio_format_valid(&out_audio_format)); + assert(audio_format_valid(&_out_audio_format)); + + out_audio_format = _out_audio_format; } + + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; -static struct filter * +static Filter * convert_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - ConvertFilter *filter = new ConvertFilter(); - return &filter->base; + return new ConvertFilter(); } -static void -convert_filter_finish(struct filter *filter) +const struct audio_format * +ConvertFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) { - delete filter; + assert(audio_format_valid(&audio_format)); + + in_audio_format = out_audio_format = audio_format; + state.Construct(); + + return &in_audio_format; } -static const struct audio_format * -convert_filter_open(struct filter *_filter, struct audio_format *audio_format, - gcc_unused GError **error_r) +void +ConvertFilter::Close() { - ConvertFilter *filter = (ConvertFilter *)_filter; + state.Destruct(); - assert(audio_format_valid(audio_format)); - - filter->in_audio_format = filter->out_audio_format = *audio_format; - filter->state.Construct(); - - return &filter->in_audio_format; + poison_undefined(&in_audio_format, sizeof(in_audio_format)); + poison_undefined(&out_audio_format, sizeof(out_audio_format)); } -static void -convert_filter_close(struct filter *_filter) +const void * +ConvertFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) { - ConvertFilter *filter = (ConvertFilter *)_filter; - - filter->state.Destruct(); - - poison_undefined(&filter->in_audio_format, - sizeof(filter->in_audio_format)); - poison_undefined(&filter->out_audio_format, - sizeof(filter->out_audio_format)); -} - -static const void * -convert_filter_filter(struct filter *_filter, const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) -{ - ConvertFilter *filter = (ConvertFilter *)_filter; - const void *dest; - - if (audio_format_equals(&filter->in_audio_format, - &filter->out_audio_format)) { + if (audio_format_equals(&in_audio_format, &out_audio_format)) { /* optimized special case: no-op */ *dest_size_r = src_size; return src; } - dest = filter->state->Convert(&filter->in_audio_format, - src, src_size, - &filter->out_audio_format, dest_size_r, - error_r); - if (dest == NULL) - return NULL; - - return dest; + return state->Convert(&in_audio_format, + src, src_size, + &out_audio_format, dest_size_r, + error_r); } const struct filter_plugin convert_filter_plugin = { "convert", convert_filter_init, - convert_filter_finish, - convert_filter_open, - convert_filter_close, - convert_filter_filter, }; void -convert_filter_set(struct filter *_filter, - const struct audio_format *out_audio_format) +convert_filter_set(Filter *_filter, const audio_format &out_audio_format) { ConvertFilter *filter = (ConvertFilter *)_filter; - assert(filter != NULL); - assert(audio_format_valid(&filter->in_audio_format)); - assert(audio_format_valid(&filter->out_audio_format)); - assert(out_audio_format != NULL); - assert(audio_format_valid(out_audio_format)); - - filter->out_audio_format = *out_audio_format; + filter->Set(out_audio_format); } diff --git a/src/filter/ConvertFilterPlugin.hxx b/src/filter/ConvertFilterPlugin.hxx index 15f0fa92c..840bf496f 100644 --- a/src/filter/ConvertFilterPlugin.hxx +++ b/src/filter/ConvertFilterPlugin.hxx @@ -20,7 +20,7 @@ #ifndef MPD_CONVERT_FILTER_PLUGIN_HXX #define MPD_CONVERT_FILTER_PLUGIN_HXX -struct filter; +class Filter; struct audio_format; /** @@ -30,7 +30,6 @@ struct audio_format; * the last in a chain. */ void -convert_filter_set(struct filter *filter, - const audio_format *out_audio_format); +convert_filter_set(Filter *filter, const audio_format &out_audio_format); #endif diff --git a/src/filter/NormalizeFilterPlugin.cxx b/src/filter/NormalizeFilterPlugin.cxx index af7a5efbd..e18c5cdf9 100644 --- a/src/filter/NormalizeFilterPlugin.cxx +++ b/src/filter/NormalizeFilterPlugin.cxx @@ -28,68 +28,51 @@ #include #include -struct normalize_filter { - struct filter filter; - +class NormalizeFilter final : public Filter { struct Compressor *compressor; struct pcm_buffer buffer; + +public: + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; -static struct filter * +static Filter * normalize_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - struct normalize_filter *filter = g_new(struct normalize_filter, 1); - - filter_init(&filter->filter, &normalize_filter_plugin); - - return &filter->filter; + return new NormalizeFilter(); } -static void -normalize_filter_finish(struct filter *filter) +const struct audio_format * +NormalizeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) { - g_free(filter); + audio_format.format = SAMPLE_FORMAT_S16; + + compressor = Compressor_new(0); + pcm_buffer_init(&buffer); + + return &audio_format; } -static const struct audio_format * -normalize_filter_open(struct filter *_filter, - struct audio_format *audio_format, - gcc_unused GError **error_r) +void +NormalizeFilter::Close() { - struct normalize_filter *filter = (struct normalize_filter *)_filter; - - audio_format->format = SAMPLE_FORMAT_S16; - - filter->compressor = Compressor_new(0); - - pcm_buffer_init(&filter->buffer); - - return audio_format; + pcm_buffer_deinit(&buffer); + Compressor_delete(compressor); } -static void -normalize_filter_close(struct filter *_filter) +const void * +NormalizeFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, gcc_unused GError **error_r) { - struct normalize_filter *filter = (struct normalize_filter *)_filter; - - pcm_buffer_deinit(&filter->buffer); - Compressor_delete(filter->compressor); -} - -static const void * -normalize_filter_filter(struct filter *_filter, - const void *src, size_t src_size, size_t *dest_size_r, - gcc_unused GError **error_r) -{ - struct normalize_filter *filter = (struct normalize_filter *)_filter; - - int16_t *dest = (int16_t *)pcm_buffer_get(&filter->buffer, src_size); - + int16_t *dest = (int16_t *)pcm_buffer_get(&buffer, src_size); memcpy(dest, src, src_size); - Compressor_Process_int16(filter->compressor, dest, src_size / 2); + Compressor_Process_int16(compressor, dest, src_size / 2); *dest_size_r = src_size; return dest; @@ -98,8 +81,4 @@ normalize_filter_filter(struct filter *_filter, const struct filter_plugin normalize_filter_plugin = { "normalize", normalize_filter_init, - normalize_filter_finish, - normalize_filter_open, - normalize_filter_close, - normalize_filter_filter, }; diff --git a/src/filter/NullFilterPlugin.cxx b/src/filter/NullFilterPlugin.cxx index 83082de55..d68065a39 100644 --- a/src/filter/NullFilterPlugin.cxx +++ b/src/filter/NullFilterPlugin.cxx @@ -30,65 +30,31 @@ #include "FilterRegistry.hxx" #include "gcc.h" -#include +class NullFilter final : public Filter { +public: + virtual const audio_format *Open(audio_format &af, + gcc_unused GError **error_r) { + return ⁡ + } -struct null_filter { - struct filter filter; + virtual void Close() {} + + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, + gcc_unused GError **error_r) { + *dest_size_r = src_size; + return src; + } }; -static struct filter * +static Filter * null_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - struct null_filter *filter = g_new(struct null_filter, 1); - - filter_init(&filter->filter, &null_filter_plugin); - return &filter->filter; -} - -static void -null_filter_finish(struct filter *_filter) -{ - struct null_filter *filter = (struct null_filter *)_filter; - - g_free(filter); -} - -static const struct audio_format * -null_filter_open(struct filter *_filter, struct audio_format *audio_format, - gcc_unused GError **error_r) -{ - struct null_filter *filter = (struct null_filter *)_filter; - (void)filter; - - return audio_format; -} - -static void -null_filter_close(struct filter *_filter) -{ - struct null_filter *filter = (struct null_filter *)_filter; - (void)filter; -} - -static const void * -null_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, gcc_unused GError **error_r) -{ - struct null_filter *filter = (struct null_filter *)_filter; - (void)filter; - - /* return the unmodified source buffer */ - *dest_size_r = src_size; - return src; + return new NullFilter(); } const struct filter_plugin null_filter_plugin = { "null", null_filter_init, - null_filter_finish, - null_filter_open, - null_filter_close, - null_filter_filter, }; diff --git a/src/filter/ReplayGainFilterPlugin.cxx b/src/filter/ReplayGainFilterPlugin.cxx index 59756a619..13c8a4063 100644 --- a/src/filter/ReplayGainFilterPlugin.cxx +++ b/src/filter/ReplayGainFilterPlugin.cxx @@ -38,9 +38,7 @@ extern "C" { #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "replay_gain" -struct replay_gain_filter { - struct filter filter; - +class ReplayGainFilter final : public Filter { /** * If set, then this hardware mixer is used for applying * replay gain, instead of the software volume library. @@ -71,9 +69,56 @@ struct replay_gain_filter { */ unsigned volume; - struct audio_format audio_format; + struct audio_format format; struct pcm_buffer buffer; + +public: + ReplayGainFilter() + :mixer(nullptr), mode(REPLAY_GAIN_OFF), + volume(PCM_VOLUME_1) { + replay_gain_info_init(&info); + } + + void SetMixer(struct mixer *_mixer, unsigned _base) { + assert(_mixer == NULL || (_base > 0 && _base <= 100)); + + mixer = _mixer; + base = _base; + + Update(); + } + + void SetInfo(const struct replay_gain_info *_info) { + if (_info != NULL) { + info = *_info; + replay_gain_info_complete(&info); + } else + replay_gain_info_init(&info); + + Update(); + } + + void SetMode(enum replay_gain_mode _mode) { + if (_mode == mode) + /* no change */ + return; + + g_debug("replay gain mode has changed %d->%d\n", mode, _mode); + + mode = _mode; + Update(); + } + + /** + * Recalculates the new volume after a property was changed. + */ + void Update(); + + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; static inline GQuark @@ -82,30 +127,27 @@ replay_gain_quark(void) return g_quark_from_static_string("replay_gain"); } -/** - * Recalculates the new volume after a property was changed. - */ -static void -replay_gain_filter_update(struct replay_gain_filter *filter) +void +ReplayGainFilter::Update() { - if (filter->mode != REPLAY_GAIN_OFF) { - float scale = replay_gain_tuple_scale(&filter->info.tuples[filter->mode], + if (mode != REPLAY_GAIN_OFF) { + float scale = replay_gain_tuple_scale(&info.tuples[mode], replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit); g_debug("scale=%f\n", (double)scale); - filter->volume = pcm_float_to_volume(scale); + volume = pcm_float_to_volume(scale); } else - filter->volume = PCM_VOLUME_1; + volume = PCM_VOLUME_1; - if (filter->mixer != NULL) { + if (mixer != NULL) { /* update the hardware mixer volume */ - unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1; - if (volume > 100) - volume = 100; + unsigned _volume = (volume * base) / PCM_VOLUME_1; + if (_volume > 100) + _volume = 100; GError *error = NULL; - if (!mixer_set_volume(filter->mixer, volume, &error)) { + if (!mixer_set_volume(mixer, _volume, &error)) { g_warning("Failed to update hardware mixer: %s", error->message); g_error_free(error); @@ -113,70 +155,41 @@ replay_gain_filter_update(struct replay_gain_filter *filter) } } -static struct filter * +static Filter * replay_gain_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1); - - filter_init(&filter->filter, &replay_gain_filter_plugin); - filter->mixer = NULL; - - filter->mode = REPLAY_GAIN_OFF; - replay_gain_info_init(&filter->info); - filter->volume = PCM_VOLUME_1; - - return &filter->filter; + return new ReplayGainFilter(); } -static void -replay_gain_filter_finish(struct filter *filter) +const audio_format * +ReplayGainFilter::Open(audio_format &af, gcc_unused GError **error_r) { - g_free(filter); + format = af; + pcm_buffer_init(&buffer); + + return &format; } -static const struct audio_format * -replay_gain_filter_open(struct filter *_filter, - struct audio_format *audio_format, - gcc_unused GError **error_r) +void +ReplayGainFilter::Close() { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - - filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); - - return &filter->audio_format; + pcm_buffer_deinit(&buffer); } -static void -replay_gain_filter_close(struct filter *_filter) +const void * +ReplayGainFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - - pcm_buffer_deinit(&filter->buffer); -} - -static const void * -replay_gain_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) -{ - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - bool success; - void *dest; *dest_size_r = src_size; - if (filter->volume == PCM_VOLUME_1) + if (volume == PCM_VOLUME_1) /* optimized special case: 100% volume = no-op */ return src; - dest = pcm_buffer_get(&filter->buffer, src_size); - - if (filter->volume <= 0) { + void *dest = pcm_buffer_get(&buffer, src_size); + if (volume <= 0) { /* optimized special case: 0% volume = memset(0) */ /* XXX is this valid for all sample formats? What about floating point? */ @@ -186,9 +199,9 @@ replay_gain_filter_filter(struct filter *_filter, memcpy(dest, src, src_size); - success = pcm_volume(dest, src_size, - sample_format(filter->audio_format.format), - filter->volume); + bool success = pcm_volume(dest, src_size, + sample_format(format.format), + volume); if (!success) { g_set_error(error_r, replay_gain_quark(), 0, "pcm_volume() has failed"); @@ -201,55 +214,29 @@ replay_gain_filter_filter(struct filter *_filter, const struct filter_plugin replay_gain_filter_plugin = { "replay_gain", replay_gain_filter_init, - replay_gain_filter_finish, - replay_gain_filter_open, - replay_gain_filter_close, - replay_gain_filter_filter, }; void -replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, +replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer, unsigned base) { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; - assert(mixer == NULL || (base > 0 && base <= 100)); - - filter->mixer = mixer; - filter->base = base; - - replay_gain_filter_update(filter); + filter->SetMixer(mixer, base); } void -replay_gain_filter_set_info(struct filter *_filter, - const struct replay_gain_info *info) +replay_gain_filter_set_info(Filter *_filter, const replay_gain_info *info) { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; - if (info != NULL) { - filter->info = *info; - replay_gain_info_complete(&filter->info); - } else - replay_gain_info_init(&filter->info); - - replay_gain_filter_update(filter); + filter->SetInfo(info); } void -replay_gain_filter_set_mode(struct filter *_filter, enum replay_gain_mode mode) +replay_gain_filter_set_mode(Filter *_filter, enum replay_gain_mode mode) { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; - if (mode == filter->mode) - /* no change */ - return; - - g_debug("replay gain mode has changed %d->%d\n", filter->mode, mode); - - filter->mode = mode; - replay_gain_filter_update(filter); + filter->SetMode(mode); } diff --git a/src/filter/ReplayGainFilterPlugin.hxx b/src/filter/ReplayGainFilterPlugin.hxx index 9b4ffc522..dd8ceb953 100644 --- a/src/filter/ReplayGainFilterPlugin.hxx +++ b/src/filter/ReplayGainFilterPlugin.hxx @@ -22,7 +22,7 @@ #include "replay_gain_info.h" -struct filter; +class Filter; struct mixer; /** @@ -34,7 +34,7 @@ struct mixer; * (including). */ void -replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, +replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer, unsigned base); /** @@ -44,10 +44,9 @@ replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, * gain data is available for the current song */ void -replay_gain_filter_set_info(struct filter *filter, - const struct replay_gain_info *info); +replay_gain_filter_set_info(Filter *filter, const replay_gain_info *info); void -replay_gain_filter_set_mode(struct filter *filter, enum replay_gain_mode mode); +replay_gain_filter_set_mode(Filter *filter, enum replay_gain_mode mode); #endif diff --git a/src/filter/RouteFilterPlugin.cxx b/src/filter/RouteFilterPlugin.cxx index dd24bde9a..559578938 100644 --- a/src/filter/RouteFilterPlugin.cxx +++ b/src/filter/RouteFilterPlugin.cxx @@ -53,14 +53,7 @@ #include #include - -struct route_filter { - - /** - * Inherit (and support cast to/from) filter - */ - struct filter base; - +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 @@ -110,21 +103,31 @@ struct route_filter { */ struct pcm_buffer output_buffer; +public: + RouteFilter():sources(nullptr) {} + ~RouteFilter() { + g_free(sources); + } + + /** + * Parse the "routes" section, a string on the form + * a>b, c>d, e>f, ... + * where a... are non-unique, non-negative integers + * and input channel a gets copied to output channel b, etc. + * @param param the configuration block to read + * @param filter a route_filter whose min_channels and sources[] to set + * @return true on success, false on error + */ + bool Configure(const config_param *param, GError **error_r); + + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; -/** - * Parse the "routes" section, a string on the form - * a>b, c>d, e>f, ... - * where a... are non-unique, non-negative integers - * and input channel a gets copied to output channel b, etc. - * @param param the configuration block to read - * @param filter a route_filter whose min_channels and sources[] to set - * @return true on success, false on error - */ -static bool -route_filter_parse(const struct config_param *param, - struct route_filter *filter, - GError **error_r) { +bool +RouteFilter::Configure(const config_param *param, GError **error_r) { /* TODO: * With a more clever way of marking "don't copy to output N", @@ -139,8 +142,8 @@ route_filter_parse(const struct config_param *param, const char *routes = config_get_block_string(param, "routes", "0>0, 1>1"); - filter->min_input_channels = 0; - filter->min_output_channels = 0; + min_input_channels = 0; + min_output_channels = 0; tokens = g_strsplit(routes, ",", 255); number_of_copies = g_strv_length(tokens); @@ -171,28 +174,28 @@ route_filter_parse(const struct config_param *param, // Keep track of the highest channel numbers seen // as either in- or outputs - if (source >= filter->min_input_channels) - filter->min_input_channels = source + 1; - if (dest >= filter->min_output_channels) - filter->min_output_channels = dest + 1; + if (source >= min_input_channels) + min_input_channels = source + 1; + if (dest >= min_output_channels) + min_output_channels = dest + 1; g_strfreev(sd); } - if (!audio_valid_channel_count(filter->min_output_channels)) { + if (!audio_valid_channel_count(min_output_channels)) { g_strfreev(tokens); g_set_error(error_r, audio_format_quark(), 0, "Invalid number of output channels requested: %d", - filter->min_output_channels); + min_output_channels); return false; } // Allocate a map of "copy nothing to me" - filter->sources = (signed char *) - g_malloc(filter->min_output_channels * sizeof(signed char)); + sources = (signed char *) + g_malloc(min_output_channels * sizeof(signed char)); - for (int i=0; imin_output_channels; ++i) - filter->sources[i] = -1; + for (int i=0; isources[dest] = source; + sources[dest] = source; g_strfreev(sd); } @@ -226,73 +229,53 @@ route_filter_parse(const struct config_param *param, return true; } -static struct filter * -route_filter_init(const struct config_param *param, - gcc_unused GError **error_r) +static Filter * +route_filter_init(const config_param *param, GError **error_r) { - struct route_filter *filter = g_new(struct route_filter, 1); - filter_init(&filter->base, &route_filter_plugin); + RouteFilter *filter = new RouteFilter(); + if (!filter->Configure(param, error_r)) { + delete filter; + return nullptr; + } - // Allocate and set the filter->sources[] array - route_filter_parse(param, filter, error_r); - - return &filter->base; + return filter; } -static void -route_filter_finish(struct filter *_filter) +const struct audio_format * +RouteFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) { - struct route_filter *filter = (struct route_filter *)_filter; - - g_free(filter->sources); - g_free(filter); -} - -static const struct audio_format * -route_filter_open(struct filter *_filter, struct audio_format *audio_format, - gcc_unused GError **error_r) -{ - struct route_filter *filter = (struct route_filter *)_filter; - // Copy the input format for later reference - filter->input_format = *audio_format; - filter->input_frame_size = - audio_format_frame_size(&filter->input_format); + input_format = audio_format; + input_frame_size = audio_format_frame_size(&input_format); // Decide on an output format which has enough channels, // and is otherwise identical - filter->output_format = *audio_format; - filter->output_format.channels = filter->min_output_channels; + output_format = audio_format; + output_format.channels = min_output_channels; // Precalculate this simple value, to speed up allocation later - filter->output_frame_size = - audio_format_frame_size(&filter->output_format); + output_frame_size = audio_format_frame_size(&output_format); // This buffer grows as needed - pcm_buffer_init(&filter->output_buffer); + pcm_buffer_init(&output_buffer); - return &filter->output_format; + return &output_format; } -static void -route_filter_close(struct filter *_filter) +void +RouteFilter::Close() { - struct route_filter *filter = (struct route_filter *)_filter; - - pcm_buffer_deinit(&filter->output_buffer); + pcm_buffer_deinit(&output_buffer); } -static const void * -route_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, gcc_unused GError **error_r) +const void * +RouteFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, gcc_unused GError **error_r) { - struct route_filter *filter = (struct route_filter *)_filter; - - size_t number_of_frames = src_size / filter->input_frame_size; + size_t number_of_frames = src_size / input_frame_size; size_t bytes_per_frame_per_channel = - audio_format_sample_size(&filter->input_format); + audio_format_sample_size(&input_format); // A moving pointer that always refers to channel 0 in the input, at the currently handled frame const uint8_t *base_source = (const uint8_t *)src; @@ -301,18 +284,18 @@ route_filter_filter(struct filter *_filter, uint8_t *chan_destination; // Grow our reusable buffer, if needed, and set the moving pointer - *dest_size_r = number_of_frames * filter->output_frame_size; + *dest_size_r = number_of_frames * output_frame_size; chan_destination = (uint8_t *) - pcm_buffer_get(&filter->output_buffer, *dest_size_r); + pcm_buffer_get(&output_buffer, *dest_size_r); // Perform our copy operations, with N input channels and M output channels for (unsigned int s=0; smin_output_channels; ++c) { - if (filter->sources[c] == -1 || - (unsigned)filter->sources[c] >= filter->input_format.channels) { + for (unsigned int c=0; c= input_format.channels) { // No source for this destination output, // give it zeroes as input memset(chan_destination, @@ -322,7 +305,7 @@ route_filter_filter(struct filter *_filter, // Get the data from channel sources[c] // and copy it to the output const uint8_t *data = base_source + - (filter->sources[c] * bytes_per_frame_per_channel); + (sources[c] * bytes_per_frame_per_channel); memcpy(chan_destination, data, bytes_per_frame_per_channel); @@ -333,18 +316,14 @@ route_filter_filter(struct filter *_filter, // Go on to the next N input samples - base_source += filter->input_frame_size; + base_source += input_frame_size; } // Here it is, ladies and gentlemen! Rerouted data! - return (void *) filter->output_buffer.buffer; + return (void *) output_buffer.buffer; } const struct filter_plugin route_filter_plugin = { "route", route_filter_init, - route_filter_finish, - route_filter_open, - route_filter_close, - route_filter_filter, }; diff --git a/src/filter/VolumeFilterPlugin.cxx b/src/filter/VolumeFilterPlugin.cxx index 86c99b83a..0689f5da5 100644 --- a/src/filter/VolumeFilterPlugin.cxx +++ b/src/filter/VolumeFilterPlugin.cxx @@ -30,17 +30,36 @@ #include #include -struct volume_filter { - struct filter filter; - +class VolumeFilter final : public Filter { /** * The current volume, from 0 to #PCM_VOLUME_1. */ unsigned volume; - struct audio_format audio_format; + struct audio_format format; struct pcm_buffer buffer; + +public: + VolumeFilter() + :volume(PCM_VOLUME_1) {} + + unsigned GetVolume() const { + assert(volume <= PCM_VOLUME_1); + + return volume; + } + + void SetVolume(unsigned _volume) { + assert(_volume <= PCM_VOLUME_1); + + volume = _volume; + } + + virtual const audio_format *Open(audio_format &af, GError **error_r); + virtual void Close(); + virtual const void *FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r); }; static inline GQuark @@ -49,61 +68,41 @@ volume_quark(void) return g_quark_from_static_string("pcm_volume"); } -static struct filter * +static Filter * volume_filter_init(gcc_unused const struct config_param *param, gcc_unused GError **error_r) { - struct volume_filter *filter = g_new(struct volume_filter, 1); - - filter_init(&filter->filter, &volume_filter_plugin); - filter->volume = PCM_VOLUME_1; - - return &filter->filter; + return new VolumeFilter(); } -static void -volume_filter_finish(struct filter *filter) +const struct audio_format * +VolumeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) { - g_free(filter); + format = audio_format; + pcm_buffer_init(&buffer); + + return &format; } -static const struct audio_format * -volume_filter_open(struct filter *_filter, struct audio_format *audio_format, - gcc_unused GError **error_r) +void +VolumeFilter::Close() { - struct volume_filter *filter = (struct volume_filter *)_filter; - - filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); - - return &filter->audio_format; + pcm_buffer_deinit(&buffer); } -static void -volume_filter_close(struct filter *_filter) +const void * +VolumeFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) { - struct volume_filter *filter = (struct volume_filter *)_filter; - - pcm_buffer_deinit(&filter->buffer); -} - -static const void * -volume_filter_filter(struct filter *_filter, const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) -{ - struct volume_filter *filter = (struct volume_filter *)_filter; - bool success; - void *dest; - *dest_size_r = src_size; - if (filter->volume >= PCM_VOLUME_1) + if (volume >= PCM_VOLUME_1) /* optimized special case: 100% volume = no-op */ return src; - dest = pcm_buffer_get(&filter->buffer, src_size); + void *dest = pcm_buffer_get(&buffer, src_size); - if (filter->volume <= 0) { + if (volume <= 0) { /* optimized special case: 0% volume = memset(0) */ /* XXX is this valid for all sample formats? What about floating point? */ @@ -113,9 +112,9 @@ volume_filter_filter(struct filter *_filter, const void *src, size_t src_size, memcpy(dest, src, src_size); - success = pcm_volume(dest, src_size, - sample_format(filter->audio_format.format), - filter->volume); + bool success = pcm_volume(dest, src_size, + sample_format(format.format), + volume); if (!success) { g_set_error(error_r, volume_quark(), 0, "pcm_volume() has failed"); @@ -128,32 +127,22 @@ volume_filter_filter(struct filter *_filter, const void *src, size_t src_size, const struct filter_plugin volume_filter_plugin = { "volume", volume_filter_init, - volume_filter_finish, - volume_filter_open, - volume_filter_close, - volume_filter_filter, }; unsigned -volume_filter_get(const struct filter *_filter) +volume_filter_get(const Filter *_filter) { - const struct volume_filter *filter = - (const struct volume_filter *)_filter; + const VolumeFilter *filter = + (const VolumeFilter *)_filter; - assert(filter->filter.plugin == &volume_filter_plugin); - assert(filter->volume <= PCM_VOLUME_1); - - return filter->volume; + return filter->GetVolume(); } void -volume_filter_set(struct filter *_filter, unsigned volume) +volume_filter_set(Filter *_filter, unsigned volume) { - struct volume_filter *filter = (struct volume_filter *)_filter; + VolumeFilter *filter = (VolumeFilter *)_filter; - assert(filter->filter.plugin == &volume_filter_plugin); - assert(volume <= PCM_VOLUME_1); - - filter->volume = volume; + filter->SetVolume(volume); } diff --git a/src/filter/VolumeFilterPlugin.hxx b/src/filter/VolumeFilterPlugin.hxx index e3d29b87d..822b7e93a 100644 --- a/src/filter/VolumeFilterPlugin.hxx +++ b/src/filter/VolumeFilterPlugin.hxx @@ -20,12 +20,12 @@ #ifndef MPD_VOLUME_FILTER_PLUGIN_HXX #define MPD_VOLUME_FILTER_PLUGIN_HXX -struct filter; +class Filter; unsigned -volume_filter_get(const struct filter *filter); +volume_filter_get(const Filter *filter); void -volume_filter_set(struct filter *filter, unsigned volume); +volume_filter_set(Filter *filter, unsigned volume); #endif diff --git a/src/mixer/SoftwareMixerPlugin.cxx b/src/mixer/SoftwareMixerPlugin.cxx index 9b8f083f4..16463938f 100644 --- a/src/mixer/SoftwareMixerPlugin.cxx +++ b/src/mixer/SoftwareMixerPlugin.cxx @@ -32,7 +32,7 @@ struct software_mixer { /** the base mixer class */ struct mixer base; - struct filter *filter; + Filter *filter; unsigned volume; }; @@ -100,7 +100,7 @@ const struct mixer_plugin software_mixer_plugin = { true, }; -struct filter * +Filter * software_mixer_get_filter(struct mixer *mixer) { struct software_mixer *sm = (struct software_mixer *)mixer; diff --git a/src/mixer/SoftwareMixerPlugin.hxx b/src/mixer/SoftwareMixerPlugin.hxx index 9a625868d..33e9e6c6f 100644 --- a/src/mixer/SoftwareMixerPlugin.hxx +++ b/src/mixer/SoftwareMixerPlugin.hxx @@ -21,13 +21,13 @@ #define MPD_SOFTWARE_MIXER_PLUGIN_HXX struct mixer; -struct filter; +class Filter; /** * Returns the (volume) filter associated with this mixer. All users * of this mixer plugin should install this filter. */ -struct filter * +Filter * software_mixer_get_filter(struct mixer *mixer); #endif diff --git a/src/output_internal.h b/src/output_internal.h index 692233f3c..201962a72 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -27,6 +27,12 @@ #include +#ifdef __cplusplus +class Filter; +#else +typedef void *Filter; +#endif + struct config_param; enum audio_output_command { @@ -156,13 +162,13 @@ struct audio_output { * The filter object of this audio output. This is an * instance of chain_filter_plugin. */ - struct filter *filter; + Filter *filter; /** * The replay_gain_filter_plugin instance of this audio * output. */ - struct filter *replay_gain_filter; + Filter *replay_gain_filter; /** * The serial number of the last replay gain info. 0 means no @@ -175,7 +181,7 @@ struct audio_output { * output, to be applied to the second chunk during * cross-fading. */ - struct filter *other_replay_gain_filter; + Filter *other_replay_gain_filter; /** * The serial number of the last replay gain info by the @@ -189,7 +195,7 @@ struct audio_output { * for converting the input data into the appropriate format * for this audio output. */ - struct filter *convert_filter; + Filter *convert_filter; /** * The thread handle, or NULL if the output thread isn't diff --git a/test/run_filter.cxx b/test/run_filter.cxx index 1800996cb..09d755db2 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -23,6 +23,7 @@ #include "AudioParser.hxx" #include "audio_format.h" #include "FilterPlugin.hxx" +#include "FilterInternal.hxx" #include "PcmVolume.hxx" #include "mixer_control.h" #include "stdbin.h" @@ -66,11 +67,10 @@ find_named_config_block(ConfigOption option, const char *name) return NULL; } -static struct filter * +static Filter * load_filter(const char *name) { const struct config_param *param; - struct filter *filter; GError *error = NULL; param = find_named_config_block(CONF_AUDIO_FILTER, name); @@ -79,7 +79,7 @@ load_filter(const char *name) return nullptr; } - filter = filter_configured_new(param, &error); + Filter *filter = filter_configured_new(param, &error); if (filter == NULL) { g_printerr("Failed to load filter: %s\n", error->message); g_error_free(error); @@ -95,7 +95,6 @@ int main(int argc, char **argv) struct audio_format_string af_string; bool success; GError *error = NULL; - struct filter *filter; const struct audio_format *out_audio_format; char buffer[4096]; @@ -137,17 +136,17 @@ int main(int argc, char **argv) /* initialize the filter */ - filter = load_filter(argv[2]); + Filter *filter = load_filter(argv[2]); if (filter == NULL) return 1; /* open the filter */ - out_audio_format = filter_open(filter, &audio_format, &error); + out_audio_format = filter->Open(audio_format, &error); if (out_audio_format == NULL) { g_printerr("Failed to open filter: %s\n", error->message); g_error_free(error); - filter_free(filter); + delete filter; return 1; } @@ -165,28 +164,28 @@ int main(int argc, char **argv) if (nbytes <= 0) break; - dest = filter_filter(filter, buffer, (size_t)nbytes, - &length, &error); + dest = filter->FilterPCM(buffer, (size_t)nbytes, + &length, &error); if (dest == NULL) { g_printerr("Filter failed: %s\n", error->message); - filter_close(filter); - filter_free(filter); + filter->Close(); + delete filter; return 1; } nbytes = write(1, dest, length); if (nbytes < 0) { g_printerr("Failed to write: %s\n", g_strerror(errno)); - filter_close(filter); - filter_free(filter); + filter->Close(); + delete filter; return 1; } } /* cleanup and exit */ - filter_close(filter); - filter_free(filter); + filter->Close(); + delete filter; config_global_finish();