FilterInternal: convert struct filter to a OO interface

This commit is contained in:
Max Kellermann 2013-02-01 18:40:36 +01:00
parent 7bb5a960fd
commit a9ce0218c1
25 changed files with 534 additions and 807 deletions

View File

@ -78,7 +78,7 @@ filter_plugin_config(const char *filter_template_name, GError **error_r)
* @return the number of filters which were successfully added * @return the number of filters which were successfully added
*/ */
unsigned int 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 // 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 // Add each name to the filter chain by instantiating an actual filter
char **template_names = tokens; char **template_names = tokens;
while (*template_names != NULL) { while (*template_names != NULL) {
struct filter *f;
const struct config_param *cfg;
// Squeeze whitespace // Squeeze whitespace
g_strstrip(*template_names); 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) { if (cfg == NULL) {
// The error has already been set, just stop. // The error has already been set, just stop.
break; break;
} }
// Instantiate one of those filter plugins with the template name as a hint // 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) { if (f == NULL) {
// The error has already been set, just stop. // The error has already been set, just stop.
break; 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; ++added_filters;
++template_names; ++template_names;

View File

@ -27,7 +27,7 @@
#include "gerror.h" #include "gerror.h"
struct filter; class Filter;
/** /**
* Builds a filter chain from a configuration string on the form * 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 * @return the number of filters which were successfully added
*/ */
unsigned int 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 #endif

View File

@ -25,14 +25,47 @@
#ifndef MPD_FILTER_INTERNAL_HXX #ifndef MPD_FILTER_INTERNAL_HXX
#define MPD_FILTER_INTERNAL_HXX #define MPD_FILTER_INTERNAL_HXX
struct filter { struct audio_format;
const struct filter_plugin *plugin;
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 #endif

View File

@ -24,13 +24,9 @@
#include "conf.h" #include "conf.h"
#include "ConfigQuark.hxx" #include "ConfigQuark.hxx"
#ifndef NDEBUG
#include "audio_format.h"
#endif
#include <assert.h> #include <assert.h>
struct filter * Filter *
filter_new(const struct filter_plugin *plugin, filter_new(const struct filter_plugin *plugin,
const struct config_param *param, GError **error_r) 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); return plugin->init(param, error_r);
} }
struct filter * Filter *
filter_configured_new(const struct config_param *param, GError **error_r) filter_configured_new(const struct config_param *param, GError **error_r)
{ {
const char *plugin_name; 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); 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);
}

View File

@ -31,7 +31,7 @@
#include <stddef.h> #include <stddef.h>
struct config_param; struct config_param;
struct filter; class Filter;
struct filter_plugin { struct filter_plugin {
const char *name; const char *name;
@ -39,38 +39,7 @@ struct filter_plugin {
/** /**
* Allocates and configures a filter. * Allocates and configures a filter.
*/ */
struct filter *(*init)(const struct config_param *param, Filter *(*init)(const struct config_param *param, GError **error_r);
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);
}; };
/** /**
@ -82,7 +51,7 @@ struct filter_plugin {
* ignore errors. * ignore errors.
* @return a new filter object, or NULL on error * @return a new filter object, or NULL on error
*/ */
struct filter * Filter *
filter_new(const struct filter_plugin *plugin, filter_new(const struct filter_plugin *plugin,
const struct config_param *param, GError **error_r); const struct config_param *param, GError **error_r);
@ -95,55 +64,7 @@ filter_new(const struct filter_plugin *plugin,
* ignore errors. * ignore errors.
* @return a new filter object, or NULL on error * @return a new filter object, or NULL on error
*/ */
struct filter * Filter *
filter_configured_new(const struct config_param *param, GError **error_r); 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 #endif

View File

@ -25,7 +25,7 @@ extern "C" {
#include "mixer_control.h" #include "mixer_control.h"
} }
#include "FilterPlugin.hxx" #include "FilterInternal.hxx"
#include <assert.h> #include <assert.h>
@ -42,13 +42,9 @@ ao_base_finish(struct audio_output *ao)
g_cond_free(ao->cond); g_cond_free(ao->cond);
g_mutex_free(ao->mutex); g_mutex_free(ao->mutex);
if (ao->replay_gain_filter != NULL) delete ao->replay_gain_filter;
filter_free(ao->replay_gain_filter); delete ao->other_replay_gain_filter;
delete ao->filter;
if (ao->other_replay_gain_filter != NULL)
filter_free(ao->other_replay_gain_filter);
filter_free(ao->filter);
pcm_buffer_deinit(&ao->cross_fade_buffer); pcm_buffer_deinit(&ao->cross_fade_buffer);
} }

View File

@ -100,7 +100,7 @@ static struct mixer *
audio_output_load_mixer(struct audio_output *ao, audio_output_load_mixer(struct audio_output *ao,
const struct config_param *param, const struct config_param *param,
const struct mixer_plugin *plugin, const struct mixer_plugin *plugin,
struct filter *filter_chain, Filter &filter_chain,
GError **error_r) GError **error_r)
{ {
struct mixer *mixer; struct mixer *mixer;
@ -120,7 +120,7 @@ audio_output_load_mixer(struct audio_output *ao,
mixer = mixer_new(&software_mixer_plugin, NULL, NULL, NULL); mixer = mixer_new(&software_mixer_plugin, NULL, NULL, NULL);
assert(mixer != NULL); assert(mixer != NULL);
filter_chain_append(filter_chain, filter_chain_append(filter_chain, "software_mixer",
software_mixer_get_filter(mixer)); software_mixer_get_filter(mixer));
return mixer; return mixer;
} }
@ -190,15 +190,15 @@ ao_base_init(struct audio_output *ao,
/* create the normalization filter (if configured) */ /* create the normalization filter (if configured) */
if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) { if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
struct filter *normalize_filter = Filter *normalize_filter =
filter_new(&normalize_filter_plugin, NULL, NULL); filter_new(&normalize_filter_plugin, NULL, NULL);
assert(normalize_filter != NULL); assert(normalize_filter != NULL);
filter_chain_append(ao->filter, filter_chain_append(*ao->filter, "normalize",
autoconvert_filter_new(normalize_filter)); autoconvert_filter_new(normalize_filter));
} }
filter_chain_parse(ao->filter, filter_chain_parse(*ao->filter,
config_get_block_string(param, AUDIO_FILTERS, ""), config_get_block_string(param, AUDIO_FILTERS, ""),
&error &error
); );
@ -258,7 +258,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param,
GError *error = NULL; GError *error = NULL;
ao->mixer = audio_output_load_mixer(ao, param, ao->mixer = audio_output_load_mixer(ao, param,
ao->plugin->mixer_plugin, ao->plugin->mixer_plugin,
ao->filter, &error); *ao->filter, &error);
if (ao->mixer == NULL && error != NULL) { if (ao->mixer == NULL && error != NULL) {
g_warning("Failed to initialize hardware mixer for '%s': %s", g_warning("Failed to initialize hardware mixer for '%s': %s",
ao->name, error->message); 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); ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
assert(ao->convert_filter != 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; return true;
} }

View File

@ -27,7 +27,7 @@ extern "C" {
} }
#include "notify.hxx" #include "notify.hxx"
#include "FilterPlugin.hxx" #include "FilterInternal.hxx"
#include "filter/ConvertFilterPlugin.hxx" #include "filter/ConvertFilterPlugin.hxx"
#include "filter/ReplayGainFilterPlugin.hxx" #include "filter/ReplayGainFilterPlugin.hxx"
#include "PlayerControl.hxx" #include "PlayerControl.hxx"
@ -98,26 +98,24 @@ ao_disable(struct audio_output *ao)
} }
static const struct audio_format * static const struct audio_format *
ao_filter_open(struct audio_output *ao, ao_filter_open(struct audio_output *ao, audio_format &format,
struct audio_format *audio_format,
GError **error_r) GError **error_r)
{ {
assert(audio_format_valid(audio_format)); assert(audio_format_valid(&format));
/* the replay_gain filter cannot fail here */ /* the replay_gain filter cannot fail here */
if (ao->replay_gain_filter != NULL) 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) if (ao->other_replay_gain_filter != NULL)
filter_open(ao->other_replay_gain_filter, audio_format, ao->other_replay_gain_filter->Open(format, error_r);
error_r);
const struct audio_format *af const struct audio_format *af
= filter_open(ao->filter, audio_format, error_r); = ao->filter->Open(format, error_r);
if (af == NULL) { if (af == NULL) {
if (ao->replay_gain_filter != 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) if (ao->other_replay_gain_filter != NULL)
filter_close(ao->other_replay_gain_filter); ao->other_replay_gain_filter->Close();
} }
return af; return af;
@ -127,11 +125,11 @@ static void
ao_filter_close(struct audio_output *ao) ao_filter_close(struct audio_output *ao)
{ {
if (ao->replay_gain_filter != 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) 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 static void
@ -139,7 +137,6 @@ ao_open(struct audio_output *ao)
{ {
bool success; bool success;
GError *error = NULL; GError *error = NULL;
const struct audio_format *filter_audio_format;
struct audio_format_string af_string; struct audio_format_string af_string;
assert(!ao->open); assert(!ao->open);
@ -164,7 +161,8 @@ ao_open(struct audio_output *ao)
/* open the filter */ /* 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) { if (filter_audio_format == NULL) {
g_warning("Failed to open filter for \"%s\" [%s]: %s", g_warning("Failed to open filter for \"%s\" [%s]: %s",
ao->name, ao->plugin->name, error->message); ao->name, ao->plugin->name, error->message);
@ -196,7 +194,7 @@ ao_open(struct audio_output *ao)
return; return;
} }
convert_filter_set(ao->convert_filter, &ao->out_audio_format); convert_filter_set(ao->convert_filter, ao->out_audio_format);
ao->open = true; ao->open = true;
@ -244,7 +242,7 @@ ao_reopen_filter(struct audio_output *ao)
GError *error = NULL; GError *error = NULL;
ao_filter_close(ao); 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) { if (filter_audio_format == NULL) {
g_warning("Failed to open filter for \"%s\" [%s]: %s", g_warning("Failed to open filter for \"%s\" [%s]: %s",
ao->name, ao->plugin->name, error->message); ao->name, ao->plugin->name, error->message);
@ -267,7 +265,7 @@ ao_reopen_filter(struct audio_output *ao)
return; return;
} }
convert_filter_set(ao->convert_filter, &ao->out_audio_format); convert_filter_set(ao->convert_filter, ao->out_audio_format);
} }
static void static void
@ -322,7 +320,7 @@ ao_wait(struct audio_output *ao)
static const void * static const void *
ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, 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, unsigned *replay_gain_serial_p,
size_t *length_r) size_t *length_r)
{ {
@ -347,7 +345,7 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
} }
GError *error = NULL; GError *error = NULL;
data = filter_filter(replay_gain_filter, data, length, data = replay_gain_filter->FilterPCM(data, length,
&length, &error); &length, &error);
if (data == NULL) { if (data == NULL) {
g_warning("\"%s\" [%s] failed to filter: %s", g_warning("\"%s\" [%s] failed to filter: %s",
@ -421,7 +419,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
/* apply filter chain */ /* apply filter chain */
data = filter_filter(ao->filter, data, length, &length, &error); data = ao->filter->FilterPCM(data, length, &length, &error);
if (data == NULL) { if (data == NULL) {
g_warning("\"%s\" [%s] failed to filter: %s", g_warning("\"%s\" [%s] failed to filter: %s",
ao->name, ao->plugin->name, error->message); ao->name, ao->plugin->name, error->message);

View File

@ -27,143 +27,106 @@
#include <assert.h> #include <assert.h>
struct AutoConvertFilter { class AutoConvertFilter final : public Filter {
struct filter base;
/** /**
* The audio format being fed to the underlying filter. This * The audio format being fed to the underlying filter. This
* plugin actually doesn't need this variable, we have it here * plugin actually doesn't need this variable, we have it here
* just so our open() method doesn't return a stack pointer. * 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. * The underlying filter.
*/ */
struct filter *filter; Filter *filter;
/** /**
* A convert_filter, just in case conversion is needed. nullptr * A convert_filter, just in case conversion is needed. nullptr
* if unused. * if unused.
*/ */
struct filter *convert; Filter *convert;
AutoConvertFilter(const filter_plugin &plugin, struct filter *_filter)
:filter(_filter) {
filter_init(&base, &plugin);
}
public:
AutoConvertFilter(Filter *_filter):filter(_filter) {}
~AutoConvertFilter() { ~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 const struct audio_format *
autoconvert_filter_finish(struct filter *_filter) AutoConvertFilter::Open(audio_format &in_audio_format, GError **error_r)
{ {
AutoConvertFilter *filter = (AutoConvertFilter *)_filter; assert(audio_format_valid(&in_audio_format));
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));
/* open the "real" filter */ /* open the "real" filter */
filter->in_audio_format = *in_audio_format; child_audio_format = in_audio_format;
const audio_format *out_audio_format =
out_audio_format = filter_open(filter->filter, filter->Open(child_audio_format, error_r);
&filter->in_audio_format, error_r);
if (out_audio_format == nullptr) if (out_audio_format == nullptr)
return nullptr; return nullptr;
/* need to convert? */ /* 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 */ /* 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, convert = filter_new(&convert_filter_plugin, nullptr, error_r);
error_r); if (convert == nullptr) {
if (filter->convert == nullptr) { filter->Close();
filter_close(filter->filter);
return nullptr; return nullptr;
} }
audio_format3 = filter_open(filter->convert, &audio_format2, audio_format audio_format2 = in_audio_format;
error_r); const audio_format *audio_format3 =
convert->Open(audio_format2, error_r);
if (audio_format3 == nullptr) { if (audio_format3 == nullptr) {
filter_free(filter->convert); delete convert;
filter_close(filter->filter); filter->Close();
return nullptr; 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 } else
/* no */ /* no */
filter->convert = nullptr; convert = nullptr;
return out_audio_format; return out_audio_format;
} }
static void void
autoconvert_filter_close(struct filter *_filter) AutoConvertFilter::Close()
{ {
AutoConvertFilter *filter = if (convert != nullptr) {
(AutoConvertFilter *)_filter; convert->Close();
delete convert;
if (filter->convert != nullptr) {
filter_close(filter->convert);
filter_free(filter->convert);
} }
filter_close(filter->filter); filter->Close();
} }
static const void * const void *
autoconvert_filter_filter(struct filter *_filter, const void *src, AutoConvertFilter::FilterPCM(const void *src, size_t src_size,
size_t src_size, size_t *dest_size_r, size_t *dest_size_r, GError **error_r)
GError **error_r)
{ {
AutoConvertFilter *filter = (AutoConvertFilter *)_filter; if (convert != nullptr) {
src = convert->FilterPCM(src, src_size, &src_size, error_r);
if (filter->convert != nullptr) {
src = filter_filter(filter->convert, src, src_size, &src_size,
error_r);
if (src == nullptr) if (src == nullptr)
return nullptr; return nullptr;
} }
return filter_filter(filter->filter, src, src_size, dest_size_r, return filter->FilterPCM(src, src_size, dest_size_r, error_r);
error_r);
} }
static const struct filter_plugin autoconvert_filter_plugin = { Filter *
"convert", autoconvert_filter_new(Filter *filter)
nullptr,
autoconvert_filter_finish,
autoconvert_filter_open,
autoconvert_filter_close,
autoconvert_filter_filter,
};
struct filter *
autoconvert_filter_new(struct filter *_filter)
{ {
AutoConvertFilter *filter = return new AutoConvertFilter(filter);
new AutoConvertFilter(autoconvert_filter_plugin,
_filter);
return &filter->base;
} }

View File

@ -20,7 +20,7 @@
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX #ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
#define 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 * Creates a new "autoconvert" filter. When opened, it ensures that
@ -28,7 +28,7 @@ struct filter;
* requests a different format, it automatically creates a * requests a different format, it automatically creates a
* convert_filter. * convert_filter.
*/ */
struct filter * Filter *
autoconvert_filter_new(struct filter *filter); autoconvert_filter_new(Filter *filter);
#endif #endif

View File

@ -31,20 +31,39 @@
#include <assert.h> #include <assert.h>
struct ChainFilter { class ChainFilter final : public Filter {
/** the base class */ struct Child {
struct filter base; const char *name;
Filter *filter;
std::list<struct filter *> children; Child(const char *_name, Filter *_filter)
:name(_name), filter(_filter) {}
ChainFilter() { ~Child() {
filter_init(&base, &chain_filter_plugin); delete filter;
} }
~ChainFilter() { Child(const Child &) = delete;
for (auto i : children) Child &operator=(const Child &) = delete;
filter_free(i); };
std::list<Child> children;
public:
void Append(const char *name, Filter *filter) {
children.emplace_back(name, 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);
private:
/**
* Close all filters in the chain until #until is reached.
* #until itself is not closed.
*/
void CloseUntil(const Filter *until);
}; };
static inline GQuark static inline GQuark
@ -53,37 +72,23 @@ filter_quark(void)
return g_quark_from_static_string("filter"); return g_quark_from_static_string("filter");
} }
static struct filter * static Filter *
chain_filter_init(gcc_unused const struct config_param *param, chain_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
ChainFilter *chain = new ChainFilter(); return new ChainFilter();
return &chain->base;
} }
static void void
chain_filter_finish(struct filter *_filter) ChainFilter::CloseUntil(const Filter *until)
{ {
ChainFilter *chain = (ChainFilter *)_filter; for (auto &child : children) {
if (child.filter == until)
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)
/* don't close this filter */ /* don't close this filter */
return; return;
/* close this filter */ /* close this filter */
filter_close(filter); child.filter->Close();
} }
/* this assertion fails if #until does not exist (anymore) */ /* 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 * static const struct audio_format *
chain_open_child(struct filter *filter, chain_open_child(const char *name, Filter *filter,
const struct audio_format *prev_audio_format, const audio_format &prev_audio_format,
GError **error_r) GError **error_r)
{ {
struct audio_format conv_audio_format = *prev_audio_format; audio_format conv_audio_format = prev_audio_format;
const struct audio_format *next_audio_format; const audio_format *next_audio_format =
filter->Open(conv_audio_format, error_r);
next_audio_format = filter_open(filter, &conv_audio_format, error_r);
if (next_audio_format == NULL) if (next_audio_format == NULL)
return 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; struct audio_format_string s;
filter_close(filter); filter->Close();
g_set_error(error_r, filter_quark(), 0, g_set_error(error_r, filter_quark(), 0,
"Audio format not supported by filter '%s': %s", "Audio format not supported by filter '%s': %s",
filter->plugin->name, name,
audio_format_to_string(prev_audio_format, &s)); audio_format_to_string(&prev_audio_format, &s));
return NULL; return NULL;
} }
return next_audio_format; return next_audio_format;
} }
static const struct audio_format * const audio_format *
chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format, ChainFilter::Open(audio_format &in_audio_format, GError **error_r)
GError **error_r)
{ {
ChainFilter *chain = (ChainFilter *)_filter; const audio_format *audio_format = &in_audio_format;
const struct audio_format *audio_format = in_audio_format;
for (auto filter : chain->children) { for (auto &child : children) {
audio_format = chain_open_child(filter, audio_format, error_r); audio_format = chain_open_child(child.name, child.filter,
*audio_format, error_r);
if (audio_format == NULL) { if (audio_format == NULL) {
/* rollback, close all children */ /* rollback, close all children */
chain_close_until(chain, filter); CloseUntil(child.filter);
return NULL; return NULL;
} }
} }
@ -136,26 +139,22 @@ chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format,
return audio_format; return audio_format;
} }
static void void
chain_filter_close(struct filter *_filter) ChainFilter::Close()
{ {
ChainFilter *chain = (ChainFilter *)_filter; for (auto &child : children)
child.filter->Close();
for (auto filter : chain->children)
filter_close(filter);
} }
static const void * const void *
chain_filter_filter(struct filter *_filter, ChainFilter::FilterPCM(const void *src, size_t src_size,
const void *src, size_t src_size,
size_t *dest_size_r, GError **error_r) size_t *dest_size_r, GError **error_r)
{ {
ChainFilter *chain = (ChainFilter *)_filter; for (auto &child : children) {
for (auto filter : chain->children) {
/* feed the output of the previous filter as input /* feed the output of the previous filter as input
into the current one */ 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) if (src == NULL)
return NULL; return NULL;
} }
@ -168,26 +167,18 @@ chain_filter_filter(struct filter *_filter,
const struct filter_plugin chain_filter_plugin = { const struct filter_plugin chain_filter_plugin = {
"chain", "chain",
chain_filter_init, chain_filter_init,
chain_filter_finish,
chain_filter_open,
chain_filter_close,
chain_filter_filter,
}; };
struct filter * Filter *
filter_chain_new(void) filter_chain_new(void)
{ {
struct filter *filter = filter_new(&chain_filter_plugin, NULL, NULL); return new ChainFilter();
/* chain_filter_init() never fails */
assert(filter != NULL);
return filter;
} }
void 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);
} }

View File

@ -27,12 +27,12 @@
#ifndef MPD_FILTER_CHAIN_HXX #ifndef MPD_FILTER_CHAIN_HXX
#define MPD_FILTER_CHAIN_HXX #define MPD_FILTER_CHAIN_HXX
struct filter; class Filter;
/** /**
* Creates a new filter chain. * Creates a new filter chain.
*/ */
struct filter * Filter *
filter_chain_new(void); filter_chain_new(void);
/** /**
@ -43,6 +43,6 @@ filter_chain_new(void);
* @param filter the filter to be appended to #chain * @param filter the filter to be appended to #chain
*/ */
void void
filter_chain_append(struct filter *chain, struct filter *filter); filter_chain_append(Filter &chain, const char *name, Filter *filter);
#endif #endif

View File

@ -31,114 +31,89 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
struct ConvertFilter { class ConvertFilter final : public Filter {
struct filter base;
/** /**
* The input audio format; PCM data is passed to the filter() * The input audio format; PCM data is passed to the filter()
* method in this format. * method in this format.
*/ */
struct audio_format in_audio_format; audio_format in_audio_format;
/** /**
* The output audio format; the consumer of this plugin * The output audio format; the consumer of this plugin
* expects PCM data in this format. This defaults to * expects PCM data in this format. This defaults to
* #in_audio_format, and can be set with convert_filter_set(). * #in_audio_format, and can be set with convert_filter_set().
*/ */
struct audio_format out_audio_format; audio_format out_audio_format;
Manual<PcmConvert> state; Manual<PcmConvert> state;
ConvertFilter() { public:
filter_init(&base, &convert_filter_plugin); 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, convert_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
ConvertFilter *filter = new ConvertFilter(); return new ConvertFilter();
return &filter->base;
} }
static void const struct audio_format *
convert_filter_finish(struct filter *filter) 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 * void
convert_filter_open(struct filter *_filter, struct audio_format *audio_format, ConvertFilter::Close()
gcc_unused GError **error_r)
{ {
ConvertFilter *filter = (ConvertFilter *)_filter; state.Destruct();
assert(audio_format_valid(audio_format)); poison_undefined(&in_audio_format, sizeof(in_audio_format));
poison_undefined(&out_audio_format, sizeof(out_audio_format));
filter->in_audio_format = filter->out_audio_format = *audio_format;
filter->state.Construct();
return &filter->in_audio_format;
} }
static void const void *
convert_filter_close(struct filter *_filter) ConvertFilter::FilterPCM(const void *src, size_t src_size,
{
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) size_t *dest_size_r, GError **error_r)
{ {
ConvertFilter *filter = (ConvertFilter *)_filter; if (audio_format_equals(&in_audio_format, &out_audio_format)) {
const void *dest;
if (audio_format_equals(&filter->in_audio_format,
&filter->out_audio_format)) {
/* optimized special case: no-op */ /* optimized special case: no-op */
*dest_size_r = src_size; *dest_size_r = src_size;
return src; return src;
} }
dest = filter->state->Convert(&filter->in_audio_format, return state->Convert(&in_audio_format,
src, src_size, src, src_size,
&filter->out_audio_format, dest_size_r, &out_audio_format, dest_size_r,
error_r); error_r);
if (dest == NULL)
return NULL;
return dest;
} }
const struct filter_plugin convert_filter_plugin = { const struct filter_plugin convert_filter_plugin = {
"convert", "convert",
convert_filter_init, convert_filter_init,
convert_filter_finish,
convert_filter_open,
convert_filter_close,
convert_filter_filter,
}; };
void void
convert_filter_set(struct filter *_filter, convert_filter_set(Filter *_filter, const audio_format &out_audio_format)
const struct audio_format *out_audio_format)
{ {
ConvertFilter *filter = (ConvertFilter *)_filter; ConvertFilter *filter = (ConvertFilter *)_filter;
assert(filter != NULL); filter->Set(out_audio_format);
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;
} }

View File

@ -20,7 +20,7 @@
#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX #ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
#define MPD_CONVERT_FILTER_PLUGIN_HXX #define MPD_CONVERT_FILTER_PLUGIN_HXX
struct filter; class Filter;
struct audio_format; struct audio_format;
/** /**
@ -30,7 +30,6 @@ struct audio_format;
* the last in a chain. * the last in a chain.
*/ */
void void
convert_filter_set(struct filter *filter, convert_filter_set(Filter *filter, const audio_format &out_audio_format);
const audio_format *out_audio_format);
#endif #endif

View File

@ -28,68 +28,51 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
struct normalize_filter { class NormalizeFilter final : public Filter {
struct filter filter;
struct Compressor *compressor; struct Compressor *compressor;
struct pcm_buffer buffer; 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, normalize_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
struct normalize_filter *filter = g_new(struct normalize_filter, 1); return new NormalizeFilter();
filter_init(&filter->filter, &normalize_filter_plugin);
return &filter->filter;
} }
static void const struct audio_format *
normalize_filter_finish(struct filter *filter) 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 * void
normalize_filter_open(struct filter *_filter, NormalizeFilter::Close()
struct audio_format *audio_format,
gcc_unused GError **error_r)
{ {
struct normalize_filter *filter = (struct normalize_filter *)_filter; pcm_buffer_deinit(&buffer);
Compressor_delete(compressor);
audio_format->format = SAMPLE_FORMAT_S16;
filter->compressor = Compressor_new(0);
pcm_buffer_init(&filter->buffer);
return audio_format;
} }
static void const void *
normalize_filter_close(struct filter *_filter) 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; int16_t *dest = (int16_t *)pcm_buffer_get(&buffer, src_size);
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);
memcpy(dest, src, 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; *dest_size_r = src_size;
return dest; return dest;
@ -98,8 +81,4 @@ normalize_filter_filter(struct filter *_filter,
const struct filter_plugin normalize_filter_plugin = { const struct filter_plugin normalize_filter_plugin = {
"normalize", "normalize",
normalize_filter_init, normalize_filter_init,
normalize_filter_finish,
normalize_filter_open,
normalize_filter_close,
normalize_filter_filter,
}; };

View File

@ -30,65 +30,31 @@
#include "FilterRegistry.hxx" #include "FilterRegistry.hxx"
#include "gcc.h" #include "gcc.h"
#include <glib.h> class NullFilter final : public Filter {
public:
virtual const audio_format *Open(audio_format &af,
gcc_unused GError **error_r) {
return &af;
}
struct null_filter { virtual void Close() {}
struct filter filter;
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, null_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
struct null_filter *filter = g_new(struct null_filter, 1); return new NullFilter();
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;
} }
const struct filter_plugin null_filter_plugin = { const struct filter_plugin null_filter_plugin = {
"null", "null",
null_filter_init, null_filter_init,
null_filter_finish,
null_filter_open,
null_filter_close,
null_filter_filter,
}; };

View File

@ -38,9 +38,7 @@ extern "C" {
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "replay_gain" #define G_LOG_DOMAIN "replay_gain"
struct replay_gain_filter { class ReplayGainFilter final : public Filter {
struct filter filter;
/** /**
* If set, then this hardware mixer is used for applying * If set, then this hardware mixer is used for applying
* replay gain, instead of the software volume library. * replay gain, instead of the software volume library.
@ -71,9 +69,56 @@ struct replay_gain_filter {
*/ */
unsigned volume; unsigned volume;
struct audio_format audio_format; struct audio_format format;
struct pcm_buffer buffer; 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 static inline GQuark
@ -82,30 +127,27 @@ replay_gain_quark(void)
return g_quark_from_static_string("replay_gain"); return g_quark_from_static_string("replay_gain");
} }
/** void
* Recalculates the new volume after a property was changed. ReplayGainFilter::Update()
*/
static void
replay_gain_filter_update(struct replay_gain_filter *filter)
{ {
if (filter->mode != REPLAY_GAIN_OFF) { if (mode != REPLAY_GAIN_OFF) {
float scale = replay_gain_tuple_scale(&filter->info.tuples[filter->mode], float scale = replay_gain_tuple_scale(&info.tuples[mode],
replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit); replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit);
g_debug("scale=%f\n", (double)scale); g_debug("scale=%f\n", (double)scale);
filter->volume = pcm_float_to_volume(scale); volume = pcm_float_to_volume(scale);
} else } else
filter->volume = PCM_VOLUME_1; volume = PCM_VOLUME_1;
if (filter->mixer != NULL) { if (mixer != NULL) {
/* update the hardware mixer volume */ /* update the hardware mixer volume */
unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1; unsigned _volume = (volume * base) / PCM_VOLUME_1;
if (volume > 100) if (_volume > 100)
volume = 100; _volume = 100;
GError *error = NULL; 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", g_warning("Failed to update hardware mixer: %s",
error->message); error->message);
g_error_free(error); 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, replay_gain_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1); return new ReplayGainFilter();
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;
} }
static void const audio_format *
replay_gain_filter_finish(struct filter *filter) 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 * void
replay_gain_filter_open(struct filter *_filter, ReplayGainFilter::Close()
struct audio_format *audio_format,
gcc_unused GError **error_r)
{ {
struct replay_gain_filter *filter = pcm_buffer_deinit(&buffer);
(struct replay_gain_filter *)_filter;
filter->audio_format = *audio_format;
pcm_buffer_init(&filter->buffer);
return &filter->audio_format;
} }
static void const void *
replay_gain_filter_close(struct filter *_filter) ReplayGainFilter::FilterPCM(const void *src, size_t src_size,
{
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) 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; *dest_size_r = src_size;
if (filter->volume == PCM_VOLUME_1) if (volume == PCM_VOLUME_1)
/* optimized special case: 100% volume = no-op */ /* optimized special case: 100% volume = no-op */
return src; return src;
dest = pcm_buffer_get(&filter->buffer, src_size); void *dest = pcm_buffer_get(&buffer, src_size);
if (volume <= 0) {
if (filter->volume <= 0) {
/* optimized special case: 0% volume = memset(0) */ /* optimized special case: 0% volume = memset(0) */
/* XXX is this valid for all sample formats? What /* XXX is this valid for all sample formats? What
about floating point? */ about floating point? */
@ -186,9 +199,9 @@ replay_gain_filter_filter(struct filter *_filter,
memcpy(dest, src, src_size); memcpy(dest, src, src_size);
success = pcm_volume(dest, src_size, bool success = pcm_volume(dest, src_size,
sample_format(filter->audio_format.format), sample_format(format.format),
filter->volume); volume);
if (!success) { if (!success) {
g_set_error(error_r, replay_gain_quark(), 0, g_set_error(error_r, replay_gain_quark(), 0,
"pcm_volume() has failed"); "pcm_volume() has failed");
@ -201,55 +214,29 @@ replay_gain_filter_filter(struct filter *_filter,
const struct filter_plugin replay_gain_filter_plugin = { const struct filter_plugin replay_gain_filter_plugin = {
"replay_gain", "replay_gain",
replay_gain_filter_init, replay_gain_filter_init,
replay_gain_filter_finish,
replay_gain_filter_open,
replay_gain_filter_close,
replay_gain_filter_filter,
}; };
void void
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
unsigned base) unsigned base)
{ {
struct replay_gain_filter *filter = ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
(struct replay_gain_filter *)_filter;
assert(mixer == NULL || (base > 0 && base <= 100)); filter->SetMixer(mixer, base);
filter->mixer = mixer;
filter->base = base;
replay_gain_filter_update(filter);
} }
void void
replay_gain_filter_set_info(struct filter *_filter, replay_gain_filter_set_info(Filter *_filter, const replay_gain_info *info)
const struct replay_gain_info *info)
{ {
struct replay_gain_filter *filter = ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
(struct replay_gain_filter *)_filter;
if (info != NULL) { filter->SetInfo(info);
filter->info = *info;
replay_gain_info_complete(&filter->info);
} else
replay_gain_info_init(&filter->info);
replay_gain_filter_update(filter);
} }
void 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 = ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
(struct replay_gain_filter *)_filter;
if (mode == filter->mode) filter->SetMode(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);
} }

View File

@ -22,7 +22,7 @@
#include "replay_gain_info.h" #include "replay_gain_info.h"
struct filter; class Filter;
struct mixer; struct mixer;
/** /**
@ -34,7 +34,7 @@ struct mixer;
* (including). * (including).
*/ */
void void
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
unsigned base); 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 * gain data is available for the current song
*/ */
void void
replay_gain_filter_set_info(struct filter *filter, replay_gain_filter_set_info(Filter *filter, const replay_gain_info *info);
const struct replay_gain_info *info);
void 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 #endif

View File

@ -53,14 +53,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
class RouteFilter final : public Filter {
struct route_filter {
/**
* Inherit (and support cast to/from) filter
*/
struct filter base;
/** /**
* The minimum number of channels we need for output * The minimum number of channels we need for output
* to be able to perform all the copies the user has specified * to be able to perform all the copies the user has specified
@ -110,9 +103,13 @@ struct route_filter {
*/ */
struct pcm_buffer output_buffer; struct pcm_buffer output_buffer;
}; public:
RouteFilter():sources(nullptr) {}
~RouteFilter() {
g_free(sources);
}
/** /**
* Parse the "routes" section, a string on the form * Parse the "routes" section, a string on the form
* a>b, c>d, e>f, ... * a>b, c>d, e>f, ...
* where a... are non-unique, non-negative integers * where a... are non-unique, non-negative integers
@ -121,10 +118,16 @@ struct route_filter {
* @param filter a route_filter whose min_channels and sources[] to set * @param filter a route_filter whose min_channels and sources[] to set
* @return true on success, false on error * @return true on success, false on error
*/ */
static bool bool Configure(const config_param *param, GError **error_r);
route_filter_parse(const struct config_param *param,
struct route_filter *filter, virtual const audio_format *Open(audio_format &af, GError **error_r);
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);
};
bool
RouteFilter::Configure(const config_param *param, GError **error_r) {
/* TODO: /* TODO:
* With a more clever way of marking "don't copy to output N", * 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 = const char *routes =
config_get_block_string(param, "routes", "0>0, 1>1"); config_get_block_string(param, "routes", "0>0, 1>1");
filter->min_input_channels = 0; min_input_channels = 0;
filter->min_output_channels = 0; min_output_channels = 0;
tokens = g_strsplit(routes, ",", 255); tokens = g_strsplit(routes, ",", 255);
number_of_copies = g_strv_length(tokens); 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 // Keep track of the highest channel numbers seen
// as either in- or outputs // as either in- or outputs
if (source >= filter->min_input_channels) if (source >= min_input_channels)
filter->min_input_channels = source + 1; min_input_channels = source + 1;
if (dest >= filter->min_output_channels) if (dest >= min_output_channels)
filter->min_output_channels = dest + 1; min_output_channels = dest + 1;
g_strfreev(sd); g_strfreev(sd);
} }
if (!audio_valid_channel_count(filter->min_output_channels)) { if (!audio_valid_channel_count(min_output_channels)) {
g_strfreev(tokens); g_strfreev(tokens);
g_set_error(error_r, audio_format_quark(), 0, g_set_error(error_r, audio_format_quark(), 0,
"Invalid number of output channels requested: %d", "Invalid number of output channels requested: %d",
filter->min_output_channels); min_output_channels);
return false; return false;
} }
// Allocate a map of "copy nothing to me" // Allocate a map of "copy nothing to me"
filter->sources = (signed char *) sources = (signed char *)
g_malloc(filter->min_output_channels * sizeof(signed char)); g_malloc(min_output_channels * sizeof(signed char));
for (int i=0; i<filter->min_output_channels; ++i) for (int i=0; i<min_output_channels; ++i)
filter->sources[i] = -1; sources[i] = -1;
// Run through the spec again, and save the // Run through the spec again, and save the
// actual mapping output <- input // actual mapping output <- input
@ -216,7 +219,7 @@ route_filter_parse(const struct config_param *param,
source = strtol(sd[0], NULL, 10); source = strtol(sd[0], NULL, 10);
dest = strtol(sd[1], NULL, 10); dest = strtol(sd[1], NULL, 10);
filter->sources[dest] = source; sources[dest] = source;
g_strfreev(sd); g_strfreev(sd);
} }
@ -226,73 +229,53 @@ route_filter_parse(const struct config_param *param,
return true; return true;
} }
static struct filter * static Filter *
route_filter_init(const struct config_param *param, route_filter_init(const config_param *param, GError **error_r)
gcc_unused GError **error_r)
{ {
struct route_filter *filter = g_new(struct route_filter, 1); RouteFilter *filter = new RouteFilter();
filter_init(&filter->base, &route_filter_plugin); if (!filter->Configure(param, error_r)) {
delete filter;
return nullptr;
}
// Allocate and set the filter->sources[] array return filter;
route_filter_parse(param, filter, error_r);
return &filter->base;
} }
static void const struct audio_format *
route_filter_finish(struct filter *_filter) 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 // Copy the input format for later reference
filter->input_format = *audio_format; input_format = audio_format;
filter->input_frame_size = input_frame_size = audio_format_frame_size(&input_format);
audio_format_frame_size(&filter->input_format);
// Decide on an output format which has enough channels, // Decide on an output format which has enough channels,
// and is otherwise identical // and is otherwise identical
filter->output_format = *audio_format; output_format = audio_format;
filter->output_format.channels = filter->min_output_channels; output_format.channels = min_output_channels;
// Precalculate this simple value, to speed up allocation later // Precalculate this simple value, to speed up allocation later
filter->output_frame_size = output_frame_size = audio_format_frame_size(&output_format);
audio_format_frame_size(&filter->output_format);
// This buffer grows as needed // 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 void
route_filter_close(struct filter *_filter) RouteFilter::Close()
{ {
struct route_filter *filter = (struct route_filter *)_filter; pcm_buffer_deinit(&output_buffer);
pcm_buffer_deinit(&filter->output_buffer);
} }
static const void * const void *
route_filter_filter(struct filter *_filter, RouteFilter::FilterPCM(const void *src, size_t src_size,
const void *src, size_t src_size,
size_t *dest_size_r, gcc_unused GError **error_r) 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 / input_frame_size;
size_t number_of_frames = src_size / filter->input_frame_size;
size_t bytes_per_frame_per_channel = 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 // 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; const uint8_t *base_source = (const uint8_t *)src;
@ -301,18 +284,18 @@ route_filter_filter(struct filter *_filter,
uint8_t *chan_destination; uint8_t *chan_destination;
// Grow our reusable buffer, if needed, and set the moving pointer // 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 *) 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 // Perform our copy operations, with N input channels and M output channels
for (unsigned int s=0; s<number_of_frames; ++s) { for (unsigned int s=0; s<number_of_frames; ++s) {
// Need to perform one copy per output channel // Need to perform one copy per output channel
for (unsigned int c=0; c<filter->min_output_channels; ++c) { for (unsigned int c=0; c<min_output_channels; ++c) {
if (filter->sources[c] == -1 || if (sources[c] == -1 ||
(unsigned)filter->sources[c] >= filter->input_format.channels) { (unsigned)sources[c] >= input_format.channels) {
// No source for this destination output, // No source for this destination output,
// give it zeroes as input // give it zeroes as input
memset(chan_destination, memset(chan_destination,
@ -322,7 +305,7 @@ route_filter_filter(struct filter *_filter,
// Get the data from channel sources[c] // Get the data from channel sources[c]
// and copy it to the output // and copy it to the output
const uint8_t *data = base_source + const uint8_t *data = base_source +
(filter->sources[c] * bytes_per_frame_per_channel); (sources[c] * bytes_per_frame_per_channel);
memcpy(chan_destination, memcpy(chan_destination,
data, data,
bytes_per_frame_per_channel); bytes_per_frame_per_channel);
@ -333,18 +316,14 @@ route_filter_filter(struct filter *_filter,
// Go on to the next N input samples // 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! // 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 = { const struct filter_plugin route_filter_plugin = {
"route", "route",
route_filter_init, route_filter_init,
route_filter_finish,
route_filter_open,
route_filter_close,
route_filter_filter,
}; };

View File

@ -30,17 +30,36 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
struct volume_filter { class VolumeFilter final : public Filter {
struct filter filter;
/** /**
* The current volume, from 0 to #PCM_VOLUME_1. * The current volume, from 0 to #PCM_VOLUME_1.
*/ */
unsigned volume; unsigned volume;
struct audio_format audio_format; struct audio_format format;
struct pcm_buffer buffer; 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 static inline GQuark
@ -49,61 +68,41 @@ volume_quark(void)
return g_quark_from_static_string("pcm_volume"); return g_quark_from_static_string("pcm_volume");
} }
static struct filter * static Filter *
volume_filter_init(gcc_unused const struct config_param *param, volume_filter_init(gcc_unused const struct config_param *param,
gcc_unused GError **error_r) gcc_unused GError **error_r)
{ {
struct volume_filter *filter = g_new(struct volume_filter, 1); return new VolumeFilter();
filter_init(&filter->filter, &volume_filter_plugin);
filter->volume = PCM_VOLUME_1;
return &filter->filter;
} }
static void const struct audio_format *
volume_filter_finish(struct filter *filter) 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 * void
volume_filter_open(struct filter *_filter, struct audio_format *audio_format, VolumeFilter::Close()
gcc_unused GError **error_r)
{ {
struct volume_filter *filter = (struct volume_filter *)_filter; pcm_buffer_deinit(&buffer);
filter->audio_format = *audio_format;
pcm_buffer_init(&filter->buffer);
return &filter->audio_format;
} }
static void const void *
volume_filter_close(struct filter *_filter) VolumeFilter::FilterPCM(const void *src, size_t src_size,
{
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) 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; *dest_size_r = src_size;
if (filter->volume >= PCM_VOLUME_1) if (volume >= PCM_VOLUME_1)
/* optimized special case: 100% volume = no-op */ /* optimized special case: 100% volume = no-op */
return src; 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) */ /* optimized special case: 0% volume = memset(0) */
/* XXX is this valid for all sample formats? What /* XXX is this valid for all sample formats? What
about floating point? */ 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); memcpy(dest, src, src_size);
success = pcm_volume(dest, src_size, bool success = pcm_volume(dest, src_size,
sample_format(filter->audio_format.format), sample_format(format.format),
filter->volume); volume);
if (!success) { if (!success) {
g_set_error(error_r, volume_quark(), 0, g_set_error(error_r, volume_quark(), 0,
"pcm_volume() has failed"); "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 = { const struct filter_plugin volume_filter_plugin = {
"volume", "volume",
volume_filter_init, volume_filter_init,
volume_filter_finish,
volume_filter_open,
volume_filter_close,
volume_filter_filter,
}; };
unsigned unsigned
volume_filter_get(const struct filter *_filter) volume_filter_get(const Filter *_filter)
{ {
const struct volume_filter *filter = const VolumeFilter *filter =
(const struct volume_filter *)_filter; (const VolumeFilter *)_filter;
assert(filter->filter.plugin == &volume_filter_plugin); return filter->GetVolume();
assert(filter->volume <= PCM_VOLUME_1);
return filter->volume;
} }
void 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); filter->SetVolume(volume);
assert(volume <= PCM_VOLUME_1);
filter->volume = volume;
} }

View File

@ -20,12 +20,12 @@
#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX #ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
#define MPD_VOLUME_FILTER_PLUGIN_HXX #define MPD_VOLUME_FILTER_PLUGIN_HXX
struct filter; class Filter;
unsigned unsigned
volume_filter_get(const struct filter *filter); volume_filter_get(const Filter *filter);
void void
volume_filter_set(struct filter *filter, unsigned volume); volume_filter_set(Filter *filter, unsigned volume);
#endif #endif

View File

@ -32,7 +32,7 @@ struct software_mixer {
/** the base mixer class */ /** the base mixer class */
struct mixer base; struct mixer base;
struct filter *filter; Filter *filter;
unsigned volume; unsigned volume;
}; };
@ -100,7 +100,7 @@ const struct mixer_plugin software_mixer_plugin = {
true, true,
}; };
struct filter * Filter *
software_mixer_get_filter(struct mixer *mixer) software_mixer_get_filter(struct mixer *mixer)
{ {
struct software_mixer *sm = (struct software_mixer *)mixer; struct software_mixer *sm = (struct software_mixer *)mixer;

View File

@ -21,13 +21,13 @@
#define MPD_SOFTWARE_MIXER_PLUGIN_HXX #define MPD_SOFTWARE_MIXER_PLUGIN_HXX
struct mixer; struct mixer;
struct filter; class Filter;
/** /**
* Returns the (volume) filter associated with this mixer. All users * Returns the (volume) filter associated with this mixer. All users
* of this mixer plugin should install this filter. * of this mixer plugin should install this filter.
*/ */
struct filter * Filter *
software_mixer_get_filter(struct mixer *mixer); software_mixer_get_filter(struct mixer *mixer);
#endif #endif

View File

@ -27,6 +27,12 @@
#include <time.h> #include <time.h>
#ifdef __cplusplus
class Filter;
#else
typedef void *Filter;
#endif
struct config_param; struct config_param;
enum audio_output_command { enum audio_output_command {
@ -156,13 +162,13 @@ struct audio_output {
* The filter object of this audio output. This is an * The filter object of this audio output. This is an
* instance of chain_filter_plugin. * instance of chain_filter_plugin.
*/ */
struct filter *filter; Filter *filter;
/** /**
* The replay_gain_filter_plugin instance of this audio * The replay_gain_filter_plugin instance of this audio
* output. * output.
*/ */
struct filter *replay_gain_filter; Filter *replay_gain_filter;
/** /**
* The serial number of the last replay gain info. 0 means no * 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 * output, to be applied to the second chunk during
* cross-fading. * cross-fading.
*/ */
struct filter *other_replay_gain_filter; Filter *other_replay_gain_filter;
/** /**
* The serial number of the last replay gain info by the * 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 converting the input data into the appropriate format
* for this audio output. * for this audio output.
*/ */
struct filter *convert_filter; Filter *convert_filter;
/** /**
* The thread handle, or NULL if the output thread isn't * The thread handle, or NULL if the output thread isn't

View File

@ -23,6 +23,7 @@
#include "AudioParser.hxx" #include "AudioParser.hxx"
#include "audio_format.h" #include "audio_format.h"
#include "FilterPlugin.hxx" #include "FilterPlugin.hxx"
#include "FilterInternal.hxx"
#include "PcmVolume.hxx" #include "PcmVolume.hxx"
#include "mixer_control.h" #include "mixer_control.h"
#include "stdbin.h" #include "stdbin.h"
@ -66,11 +67,10 @@ find_named_config_block(ConfigOption option, const char *name)
return NULL; return NULL;
} }
static struct filter * static Filter *
load_filter(const char *name) load_filter(const char *name)
{ {
const struct config_param *param; const struct config_param *param;
struct filter *filter;
GError *error = NULL; GError *error = NULL;
param = find_named_config_block(CONF_AUDIO_FILTER, name); param = find_named_config_block(CONF_AUDIO_FILTER, name);
@ -79,7 +79,7 @@ load_filter(const char *name)
return nullptr; return nullptr;
} }
filter = filter_configured_new(param, &error); Filter *filter = filter_configured_new(param, &error);
if (filter == NULL) { if (filter == NULL) {
g_printerr("Failed to load filter: %s\n", error->message); g_printerr("Failed to load filter: %s\n", error->message);
g_error_free(error); g_error_free(error);
@ -95,7 +95,6 @@ int main(int argc, char **argv)
struct audio_format_string af_string; struct audio_format_string af_string;
bool success; bool success;
GError *error = NULL; GError *error = NULL;
struct filter *filter;
const struct audio_format *out_audio_format; const struct audio_format *out_audio_format;
char buffer[4096]; char buffer[4096];
@ -137,17 +136,17 @@ int main(int argc, char **argv)
/* initialize the filter */ /* initialize the filter */
filter = load_filter(argv[2]); Filter *filter = load_filter(argv[2]);
if (filter == NULL) if (filter == NULL)
return 1; return 1;
/* open the filter */ /* 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) { if (out_audio_format == NULL) {
g_printerr("Failed to open filter: %s\n", error->message); g_printerr("Failed to open filter: %s\n", error->message);
g_error_free(error); g_error_free(error);
filter_free(filter); delete filter;
return 1; return 1;
} }
@ -165,28 +164,28 @@ int main(int argc, char **argv)
if (nbytes <= 0) if (nbytes <= 0)
break; break;
dest = filter_filter(filter, buffer, (size_t)nbytes, dest = filter->FilterPCM(buffer, (size_t)nbytes,
&length, &error); &length, &error);
if (dest == NULL) { if (dest == NULL) {
g_printerr("Filter failed: %s\n", error->message); g_printerr("Filter failed: %s\n", error->message);
filter_close(filter); filter->Close();
filter_free(filter); delete filter;
return 1; return 1;
} }
nbytes = write(1, dest, length); nbytes = write(1, dest, length);
if (nbytes < 0) { if (nbytes < 0) {
g_printerr("Failed to write: %s\n", g_strerror(errno)); g_printerr("Failed to write: %s\n", g_strerror(errno));
filter_close(filter); filter->Close();
filter_free(filter); delete filter;
return 1; return 1;
} }
} }
/* cleanup and exit */ /* cleanup and exit */
filter_close(filter); filter->Close();
filter_free(filter); delete filter;
config_global_finish(); config_global_finish();