diff options
Diffstat (limited to 'src/filter')
20 files changed, 1080 insertions, 1265 deletions
diff --git a/src/filter/AutoConvertFilterPlugin.cxx b/src/filter/AutoConvertFilterPlugin.cxx new file mode 100644 index 000000000..55ee46948 --- /dev/null +++ b/src/filter/AutoConvertFilterPlugin.cxx @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "AutoConvertFilterPlugin.hxx" +#include "ConvertFilterPlugin.hxx" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "audio_format.h" + +#include <assert.h> + +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. + */ + audio_format child_audio_format; + + /** + * The underlying filter. + */ + Filter *filter; + + /** + * A convert_filter, just in case conversion is needed. nullptr + * if unused. + */ + Filter *convert; + +public: + AutoConvertFilter(Filter *_filter):filter(_filter) {} + ~AutoConvertFilter() { + 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); +}; + +const struct audio_format * +AutoConvertFilter::Open(audio_format &in_audio_format, GError **error_r) +{ + assert(audio_format_valid(&in_audio_format)); + + /* open the "real" filter */ + + 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(&child_audio_format, &in_audio_format)) { + /* yes - create a convert_filter */ + + convert = filter_new(&convert_filter_plugin, nullptr, error_r); + if (convert == nullptr) { + filter->Close(); + return nullptr; + } + + audio_format audio_format2 = in_audio_format; + const audio_format *audio_format3 = + convert->Open(audio_format2, error_r); + if (audio_format3 == nullptr) { + delete convert; + filter->Close(); + return nullptr; + } + + assert(audio_format_equals(&audio_format2, &in_audio_format)); + + convert_filter_set(convert, child_audio_format); + } else + /* no */ + convert = nullptr; + + return out_audio_format; +} + +void +AutoConvertFilter::Close() +{ + if (convert != nullptr) { + convert->Close(); + delete convert; + } + + filter->Close(); +} + +const void * +AutoConvertFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) +{ + if (convert != nullptr) { + src = convert->FilterPCM(src, src_size, &src_size, error_r); + if (src == nullptr) + return nullptr; + } + + return filter->FilterPCM(src, src_size, dest_size_r, error_r); +} + +Filter * +autoconvert_filter_new(Filter *filter) +{ + return new AutoConvertFilter(filter); +} diff --git a/src/filter/autoconvert_filter_plugin.h b/src/filter/AutoConvertFilterPlugin.hxx index def08ab7e..7db72a345 100644 --- a/src/filter/autoconvert_filter_plugin.h +++ b/src/filter/AutoConvertFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,10 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef AUTOCONVERT_FILTER_PLUGIN_H -#define AUTOCONVERT_FILTER_PLUGIN_H +#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 new file mode 100644 index 000000000..c8666615f --- /dev/null +++ b/src/filter/ChainFilterPlugin.cxx @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ChainFilterPlugin.hxx" +#include "conf.h" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "audio_format.h" + +#include <glib.h> + +#include <list> + +#include <assert.h> + +class ChainFilter final : public Filter { + struct Child { + const char *name; + Filter *filter; + + Child(const char *_name, Filter *_filter) + :name(_name), filter(_filter) {} + ~Child() { + delete filter; + } + + Child(const Child &) = delete; + Child &operator=(const Child &) = delete; + }; + + 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 +filter_quark(void) +{ + return g_quark_from_static_string("filter"); +} + +static Filter * +chain_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new ChainFilter(); +} + +void +ChainFilter::CloseUntil(const Filter *until) +{ + for (auto &child : children) { + if (child.filter == until) + /* don't close this filter */ + return; + + /* close this filter */ + child.filter->Close(); + } + + /* this assertion fails if #until does not exist (anymore) */ + assert(false); +} + +static const struct audio_format * +chain_open_child(const char *name, Filter *filter, + const audio_format &prev_audio_format, + GError **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)) { + struct audio_format_string s; + + filter->Close(); + g_set_error(error_r, filter_quark(), 0, + "Audio format not supported by filter '%s': %s", + name, + audio_format_to_string(&prev_audio_format, &s)); + return NULL; + } + + return next_audio_format; +} + +const audio_format * +ChainFilter::Open(audio_format &in_audio_format, GError **error_r) +{ + const audio_format *audio_format = &in_audio_format; + + 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 */ + CloseUntil(child.filter); + return NULL; + } + } + + /* return the output format of the last filter */ + return audio_format; +} + +void +ChainFilter::Close() +{ + for (auto &child : children) + child.filter->Close(); +} + +const void * +ChainFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) +{ + for (auto &child : children) { + /* feed the output of the previous filter as input + into the current one */ + src = child.filter->FilterPCM(src, src_size, &src_size, + error_r); + if (src == NULL) + return NULL; + } + + /* return the output of the last filter */ + *dest_size_r = src_size; + return src; +} + +const struct filter_plugin chain_filter_plugin = { + "chain", + chain_filter_init, +}; + +Filter * +filter_chain_new(void) +{ + return new ChainFilter(); +} + +void +filter_chain_append(Filter &_chain, const char *name, Filter *filter) +{ + ChainFilter &chain = (ChainFilter &)_chain; + + chain.Append(name, filter); +} diff --git a/src/filter/chain_filter_plugin.h b/src/filter/ChainFilterPlugin.hxx index 1dba46667..884c7ca19 100644 --- a/src/filter/chain_filter_plugin.h +++ b/src/filter/ChainFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,15 +24,15 @@ * output to the next one. */ -#ifndef MPD_FILTER_CHAIN_H -#define MPD_FILTER_CHAIN_H +#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 new file mode 100644 index 000000000..2c6907655 --- /dev/null +++ b/src/filter/ConvertFilterPlugin.cxx @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ConvertFilterPlugin.hxx" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "conf.h" +#include "PcmConvert.hxx" +#include "util/Manual.hxx" +#include "audio_format.h" +#include "poison.h" + +#include <assert.h> +#include <string.h> + +class ConvertFilter final : public Filter { + /** + * The input audio format; PCM data is passed to the filter() + * method in this 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(). + */ + audio_format out_audio_format; + + Manual<PcmConvert> state; + +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 Filter * +convert_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new ConvertFilter(); +} + +const struct audio_format * +ConvertFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) +{ + assert(audio_format_valid(&audio_format)); + + in_audio_format = out_audio_format = audio_format; + state.Construct(); + + return &in_audio_format; +} + +void +ConvertFilter::Close() +{ + state.Destruct(); + + poison_undefined(&in_audio_format, sizeof(in_audio_format)); + poison_undefined(&out_audio_format, sizeof(out_audio_format)); +} + +const void * +ConvertFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) +{ + if (audio_format_equals(&in_audio_format, &out_audio_format)) { + /* optimized special case: no-op */ + *dest_size_r = src_size; + return src; + } + + 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, +}; + +void +convert_filter_set(Filter *_filter, const audio_format &out_audio_format) +{ + ConvertFilter *filter = (ConvertFilter *)_filter; + + filter->Set(out_audio_format); +} diff --git a/src/filter/convert_filter_plugin.h b/src/filter/ConvertFilterPlugin.hxx index 156adf8e3..840bf496f 100644 --- a/src/filter/convert_filter_plugin.h +++ b/src/filter/ConvertFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,10 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef CONVERT_FILTER_PLUGIN_H -#define CONVERT_FILTER_PLUGIN_H +#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 struct 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 new file mode 100644 index 000000000..e18c5cdf9 --- /dev/null +++ b/src/filter/NormalizeFilterPlugin.cxx @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "pcm_buffer.h" +#include "audio_format.h" +#include "AudioCompress/compress.h" + +#include <assert.h> +#include <string.h> + +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 Filter * +normalize_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new NormalizeFilter(); +} + +const struct audio_format * +NormalizeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) +{ + audio_format.format = SAMPLE_FORMAT_S16; + + compressor = Compressor_new(0); + pcm_buffer_init(&buffer); + + return &audio_format; +} + +void +NormalizeFilter::Close() +{ + pcm_buffer_deinit(&buffer); + Compressor_delete(compressor); +} + +const void * +NormalizeFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, gcc_unused GError **error_r) +{ + int16_t *dest = (int16_t *)pcm_buffer_get(&buffer, src_size); + memcpy(dest, src, src_size); + + Compressor_Process_int16(compressor, dest, src_size / 2); + + *dest_size_r = src_size; + return dest; +} + +const struct filter_plugin normalize_filter_plugin = { + "normalize", + normalize_filter_init, +}; diff --git a/src/filter/NullFilterPlugin.cxx b/src/filter/NullFilterPlugin.cxx new file mode 100644 index 000000000..d68065a39 --- /dev/null +++ b/src/filter/NullFilterPlugin.cxx @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** \file + * + * This filter plugin does nothing. That is not quite useful, except + * for testing the filter core, or as a template for new filter + * plugins. + */ + +#include "config.h" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "gcc.h" + +class NullFilter final : public Filter { +public: + virtual const audio_format *Open(audio_format &af, + gcc_unused GError **error_r) { + return ⁡ + } + + 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 Filter * +null_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new NullFilter(); +} + +const struct filter_plugin null_filter_plugin = { + "null", + null_filter_init, +}; diff --git a/src/filter/ReplayGainFilterPlugin.cxx b/src/filter/ReplayGainFilterPlugin.cxx new file mode 100644 index 000000000..1fa2269b4 --- /dev/null +++ b/src/filter/ReplayGainFilterPlugin.cxx @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ReplayGainFilterPlugin.hxx" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "audio_format.h" +#include "replay_gain_info.h" +#include "replay_gain_config.h" +#include "MixerControl.hxx" +#include "PcmVolume.hxx" + +extern "C" { +#include "pcm_buffer.h" +} + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "replay_gain" + +class ReplayGainFilter final : public Filter { + /** + * If set, then this hardware mixer is used for applying + * replay gain, instead of the software volume library. + */ + struct mixer *mixer; + + /** + * The base volume level for scale=1.0, between 1 and 100 + * (including). + */ + unsigned base; + + enum replay_gain_mode mode; + + struct replay_gain_info info; + + /** + * The current volume, between 0 and a value that may or may not exceed + * #PCM_VOLUME_1. + * + * If the default value of true is used for replaygain_limit, the + * application of the volume to the signal will never cause clipping. + * + * On the other hand, if the user has set replaygain_limit to false, + * the chance of clipping is explicitly preferred if that's required to + * maintain a consistent audio level. Whether clipping will actually + * occur depends on what value the user is using for replaygain_preamp. + */ + unsigned volume; + + 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 +replay_gain_quark(void) +{ + return g_quark_from_static_string("replay_gain"); +} + +void +ReplayGainFilter::Update() +{ + 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); + + volume = pcm_float_to_volume(scale); + } else + volume = PCM_VOLUME_1; + + if (mixer != NULL) { + /* update the hardware mixer volume */ + + unsigned _volume = (volume * base) / PCM_VOLUME_1; + if (_volume > 100) + _volume = 100; + + GError *error = NULL; + if (!mixer_set_volume(mixer, _volume, &error)) { + g_warning("Failed to update hardware mixer: %s", + error->message); + g_error_free(error); + } + } +} + +static Filter * +replay_gain_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new ReplayGainFilter(); +} + +const audio_format * +ReplayGainFilter::Open(audio_format &af, gcc_unused GError **error_r) +{ + format = af; + pcm_buffer_init(&buffer); + + return &format; +} + +void +ReplayGainFilter::Close() +{ + pcm_buffer_deinit(&buffer); +} + +const void * +ReplayGainFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) +{ + + *dest_size_r = src_size; + + if (volume == PCM_VOLUME_1) + /* optimized special case: 100% volume = no-op */ + return src; + + 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? */ + memset(dest, 0, src_size); + return dest; + } + + memcpy(dest, src, src_size); + + 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"); + return NULL; + } + + return dest; +} + +const struct filter_plugin replay_gain_filter_plugin = { + "replay_gain", + replay_gain_filter_init, +}; + +void +replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer, + unsigned base) +{ + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; + + filter->SetMixer(mixer, base); +} + +void +replay_gain_filter_set_info(Filter *_filter, const replay_gain_info *info) +{ + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; + + filter->SetInfo(info); +} + +void +replay_gain_filter_set_mode(Filter *_filter, enum replay_gain_mode mode) +{ + ReplayGainFilter *filter = (ReplayGainFilter *)_filter; + + filter->SetMode(mode); +} diff --git a/src/filter/replay_gain_filter_plugin.h b/src/filter/ReplayGainFilterPlugin.hxx index 45b738e40..dd8ceb953 100644 --- a/src/filter/replay_gain_filter_plugin.h +++ b/src/filter/ReplayGainFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,12 +17,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef REPLAY_GAIN_FILTER_PLUGIN_H -#define REPLAY_GAIN_FILTER_PLUGIN_H +#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX +#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX #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,7 +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(Filter *filter, enum replay_gain_mode mode); #endif diff --git a/src/filter/route_filter_plugin.c b/src/filter/RouteFilterPlugin.cxx index 3bf8677e5..559578938 100644 --- a/src/filter/route_filter_plugin.c +++ b/src/filter/RouteFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,25 +41,19 @@ #include "config.h" #include "conf.h" +#include "ConfigQuark.hxx" #include "audio_format.h" #include "audio_check.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" #include "pcm_buffer.h" #include <assert.h> #include <string.h> #include <stdlib.h> - -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 @@ -109,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", @@ -138,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); @@ -170,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 = - g_malloc(filter->min_output_channels * sizeof(signed char)); + sources = (signed char *) + g_malloc(min_output_channels * sizeof(signed char)); - for (int i=0; i<filter->min_output_channels; ++i) - filter->sources[i] = -1; + for (int i=0; i<min_output_channels; ++i) + sources[i] = -1; // Run through the spec again, and save the // actual mapping output <- input @@ -215,7 +219,7 @@ route_filter_parse(const struct config_param *param, source = strtol(sd[0], NULL, 10); dest = strtol(sd[1], NULL, 10); - filter->sources[dest] = source; + sources[dest] = source; g_strfreev(sd); } @@ -225,92 +229,73 @@ route_filter_parse(const struct config_param *param, return true; } -static struct filter * -route_filter_init(const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct route_filter *filter = g_new(struct route_filter, 1); - filter_init(&filter->base, &route_filter_plugin); - - // Allocate and set the filter->sources[] array - route_filter_parse(param, filter, error_r); - - return &filter->base; -} - -static void -route_filter_finish(struct filter *_filter) +static Filter * +route_filter_init(const config_param *param, GError **error_r) { - struct route_filter *filter = (struct route_filter *)_filter; + RouteFilter *filter = new RouteFilter(); + if (!filter->Configure(param, error_r)) { + delete filter; + return nullptr; + } - g_free(filter->sources); - g_free(filter); + return filter; } -static const struct audio_format * -route_filter_open(struct filter *_filter, struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) +const struct audio_format * +RouteFilter::Open(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, G_GNUC_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 = src; + const uint8_t *base_source = (const uint8_t *)src; // A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output 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; - chan_destination = pcm_buffer_get(&filter->output_buffer, *dest_size_r); + *dest_size_r = number_of_frames * output_frame_size; + chan_destination = (uint8_t *) + 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; s<number_of_frames; ++s) { // Need to perform one copy per output channel - for (unsigned int c=0; c<filter->min_output_channels; ++c) { - if (filter->sources[c] == -1 || - (unsigned)filter->sources[c] >= filter->input_format.channels) { + for (unsigned int c=0; c<min_output_channels; ++c) { + if (sources[c] == -1 || + (unsigned)sources[c] >= input_format.channels) { // No source for this destination output, // give it zeroes as input memset(chan_destination, @@ -320,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); @@ -331,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 = { - .name = "route", - .init = route_filter_init, - .finish = route_filter_finish, - .open = route_filter_open, - .close = route_filter_close, - .filter = route_filter_filter, + "route", + route_filter_init, }; diff --git a/src/filter/VolumeFilterPlugin.cxx b/src/filter/VolumeFilterPlugin.cxx new file mode 100644 index 000000000..0689f5da5 --- /dev/null +++ b/src/filter/VolumeFilterPlugin.cxx @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "VolumeFilterPlugin.hxx" +#include "FilterPlugin.hxx" +#include "FilterInternal.hxx" +#include "FilterRegistry.hxx" +#include "conf.h" +#include "pcm_buffer.h" +#include "PcmVolume.hxx" +#include "audio_format.h" + +#include <assert.h> +#include <string.h> + +class VolumeFilter final : public Filter { + /** + * The current volume, from 0 to #PCM_VOLUME_1. + */ + unsigned volume; + + 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 +volume_quark(void) +{ + return g_quark_from_static_string("pcm_volume"); +} + +static Filter * +volume_filter_init(gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + return new VolumeFilter(); +} + +const struct audio_format * +VolumeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r) +{ + format = audio_format; + pcm_buffer_init(&buffer); + + return &format; +} + +void +VolumeFilter::Close() +{ + pcm_buffer_deinit(&buffer); +} + +const void * +VolumeFilter::FilterPCM(const void *src, size_t src_size, + size_t *dest_size_r, GError **error_r) +{ + *dest_size_r = src_size; + + if (volume >= PCM_VOLUME_1) + /* optimized special case: 100% volume = no-op */ + return src; + + 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? */ + memset(dest, 0, src_size); + return dest; + } + + memcpy(dest, src, src_size); + + 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"); + return NULL; + } + + return dest; +} + +const struct filter_plugin volume_filter_plugin = { + "volume", + volume_filter_init, +}; + +unsigned +volume_filter_get(const Filter *_filter) +{ + const VolumeFilter *filter = + (const VolumeFilter *)_filter; + + return filter->GetVolume(); +} + +void +volume_filter_set(Filter *_filter, unsigned volume) +{ + VolumeFilter *filter = (VolumeFilter *)_filter; + + filter->SetVolume(volume); +} + diff --git a/src/filter/volume_filter_plugin.h b/src/filter/VolumeFilterPlugin.hxx index 5b16f7e57..822b7e93a 100644 --- a/src/filter/volume_filter_plugin.h +++ b/src/filter/VolumeFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,15 +17,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef VOLUME_FILTER_PLUGIN_H -#define VOLUME_FILTER_PLUGIN_H +#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/filter/autoconvert_filter_plugin.c b/src/filter/autoconvert_filter_plugin.c deleted file mode 100644 index 3826a0fb3..000000000 --- a/src/filter/autoconvert_filter_plugin.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "filter/autoconvert_filter_plugin.h" -#include "filter/convert_filter_plugin.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "conf.h" -#include "pcm_convert.h" -#include "audio_format.h" -#include "poison.h" - -#include <assert.h> -#include <string.h> - -struct autoconvert_filter { - struct filter base; - - /** - * 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; - - /** - * The underlying filter. - */ - struct filter *filter; - - /** - * A convert_filter, just in case conversion is needed. NULL - * if unused. - */ - struct filter *convert; -}; - -static void -autoconvert_filter_finish(struct filter *_filter) -{ - struct autoconvert_filter *filter = - (struct autoconvert_filter *)_filter; - - filter_free(filter->filter); - g_free(filter); -} - -static const struct audio_format * -autoconvert_filter_open(struct filter *_filter, - struct audio_format *in_audio_format, - GError **error_r) -{ - struct autoconvert_filter *filter = - (struct autoconvert_filter *)_filter; - const struct audio_format *out_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); - if (out_audio_format == NULL) - return NULL; - - /* need to convert? */ - - if (!audio_format_equals(&filter->in_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, NULL, - error_r); - if (filter->convert == NULL) { - filter_close(filter->filter); - return NULL; - } - - audio_format3 = filter_open(filter->convert, &audio_format2, - error_r); - if (audio_format3 == NULL) { - filter_free(filter->convert); - filter_close(filter->filter); - return NULL; - } - - assert(audio_format_equals(&audio_format2, in_audio_format)); - - convert_filter_set(filter->convert, &filter->in_audio_format); - } else - /* no */ - filter->convert = NULL; - - return out_audio_format; -} - -static void -autoconvert_filter_close(struct filter *_filter) -{ - struct autoconvert_filter *filter = - (struct autoconvert_filter *)_filter; - - if (filter->convert != NULL) { - filter_close(filter->convert); - filter_free(filter->convert); - } - - filter_close(filter->filter); -} - -static const void * -autoconvert_filter_filter(struct filter *_filter, const void *src, - size_t src_size, size_t *dest_size_r, - GError **error_r) -{ - struct autoconvert_filter *filter = - (struct autoconvert_filter *)_filter; - - if (filter->convert != NULL) { - src = filter_filter(filter->convert, src, src_size, &src_size, - error_r); - if (src == NULL) - return NULL; - } - - return filter_filter(filter->filter, src, src_size, dest_size_r, - error_r); -} - -static const struct filter_plugin autoconvert_filter_plugin = { - .name = "convert", - .finish = autoconvert_filter_finish, - .open = autoconvert_filter_open, - .close = autoconvert_filter_close, - .filter = autoconvert_filter_filter, -}; - -struct filter * -autoconvert_filter_new(struct filter *_filter) -{ - struct autoconvert_filter *filter = - g_new(struct autoconvert_filter, 1); - - filter_init(&filter->base, &autoconvert_filter_plugin); - filter->filter = _filter; - - return &filter->base; -} diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c deleted file mode 100644 index 2c785a36f..000000000 --- a/src/filter/chain_filter_plugin.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "conf.h" -#include "filter/chain_filter_plugin.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "audio_format.h" - -#include <assert.h> - -struct filter_chain { - /** the base class */ - struct filter base; - - GSList *children; -}; - -static inline GQuark -filter_quark(void) -{ - return g_quark_from_static_string("filter"); -} - -static struct filter * -chain_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct filter_chain *chain = g_new(struct filter_chain, 1); - - filter_init(&chain->base, &chain_filter_plugin); - chain->children = NULL; - - return &chain->base; -} - -static void -chain_free_child(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct filter *filter = data; - - filter_free(filter); -} - -static void -chain_filter_finish(struct filter *_filter) -{ - struct filter_chain *chain = (struct filter_chain *)_filter; - - g_slist_foreach(chain->children, chain_free_child, NULL); - g_slist_free(chain->children); - - g_free(chain); -} - -/** - * Close all filters in the chain until #until is reached. #until - * itself is not closed. - */ -static void -chain_close_until(struct filter_chain *chain, const struct filter *until) -{ - GSList *i = chain->children; - struct filter *filter; - - while (true) { - /* this assertion fails if #until does not exist - (anymore) */ - assert(i != NULL); - - if (i->data == until) - /* don't close this filter */ - break; - - /* close this filter */ - filter = i->data; - filter_close(filter); - - i = g_slist_next(i); - } -} - -static const struct audio_format * -chain_open_child(struct filter *filter, - const struct 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); - if (next_audio_format == NULL) - return NULL; - - if (!audio_format_equals(&conv_audio_format, prev_audio_format)) { - struct audio_format_string s; - - filter_close(filter); - 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)); - 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) -{ - struct filter_chain *chain = (struct filter_chain *)_filter; - const struct audio_format *audio_format = in_audio_format; - - for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { - struct filter *filter = i->data; - - audio_format = chain_open_child(filter, audio_format, error_r); - if (audio_format == NULL) { - /* rollback, close all children */ - chain_close_until(chain, filter); - return NULL; - } - } - - /* return the output format of the last filter */ - return audio_format; -} - -static void -chain_close_child(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct filter *filter = data; - - filter_close(filter); -} - -static void -chain_filter_close(struct filter *_filter) -{ - struct filter_chain *chain = (struct filter_chain *)_filter; - - g_slist_foreach(chain->children, chain_close_child, NULL); -} - -static const void * -chain_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) -{ - struct filter_chain *chain = (struct filter_chain *)_filter; - - for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { - struct filter *filter = i->data; - - /* feed the output of the previous filter as input - into the current one */ - src = filter_filter(filter, src, src_size, &src_size, error_r); - if (src == NULL) - return NULL; - } - - /* return the output of the last filter */ - *dest_size_r = src_size; - return src; -} - -const struct filter_plugin chain_filter_plugin = { - .name = "chain", - .init = chain_filter_init, - .finish = chain_filter_finish, - .open = chain_filter_open, - .close = chain_filter_close, - .filter = chain_filter_filter, -}; - -struct 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; -} - -void -filter_chain_append(struct filter *_chain, struct filter *filter) -{ - struct filter_chain *chain = (struct filter_chain *)_chain; - - chain->children = g_slist_append(chain->children, filter); -} - diff --git a/src/filter/convert_filter_plugin.c b/src/filter/convert_filter_plugin.c deleted file mode 100644 index c55b69af2..000000000 --- a/src/filter/convert_filter_plugin.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "filter/convert_filter_plugin.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "conf.h" -#include "pcm_convert.h" -#include "audio_format.h" -#include "poison.h" - -#include <assert.h> -#include <string.h> - -struct convert_filter { - struct filter base; - - /** - * The current convert, from 0 to #PCM_CONVERT_1. - */ - unsigned convert; - - /** - * The input audio format; PCM data is passed to the filter() - * method in this format. - */ - struct 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; - - struct pcm_convert_state state; -}; - -static struct filter * -convert_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct convert_filter *filter = g_new(struct convert_filter, 1); - - filter_init(&filter->base, &convert_filter_plugin); - return &filter->base; -} - -static void -convert_filter_finish(struct filter *filter) -{ - g_free(filter); -} - -static const struct audio_format * -convert_filter_open(struct filter *_filter, struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) -{ - struct convert_filter *filter = (struct convert_filter *)_filter; - - assert(audio_format_valid(audio_format)); - - filter->in_audio_format = filter->out_audio_format = *audio_format; - pcm_convert_init(&filter->state); - - return &filter->in_audio_format; -} - -static void -convert_filter_close(struct filter *_filter) -{ - struct convert_filter *filter = (struct convert_filter *)_filter; - - pcm_convert_deinit(&filter->state); - - 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) -{ - struct convert_filter *filter = (struct convert_filter *)_filter; - const void *dest; - - if (audio_format_equals(&filter->in_audio_format, - &filter->out_audio_format)) { - /* optimized special case: no-op */ - *dest_size_r = src_size; - return src; - } - - dest = pcm_convert(&filter->state, &filter->in_audio_format, - src, src_size, - &filter->out_audio_format, dest_size_r, - error_r); - if (dest == NULL) - return NULL; - - return dest; -} - -const struct filter_plugin convert_filter_plugin = { - .name = "convert", - .init = convert_filter_init, - .finish = convert_filter_finish, - .open = convert_filter_open, - .close = convert_filter_close, - .filter = convert_filter_filter, -}; - -void -convert_filter_set(struct filter *_filter, - const struct audio_format *out_audio_format) -{ - struct convert_filter *filter = (struct convert_filter *)_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; -} diff --git a/src/filter/normalize_filter_plugin.c b/src/filter/normalize_filter_plugin.c deleted file mode 100644 index 2151482e4..000000000 --- a/src/filter/normalize_filter_plugin.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "pcm_buffer.h" -#include "audio_format.h" -#include "AudioCompress/compress.h" - -#include <assert.h> -#include <string.h> - -struct normalize_filter { - struct filter filter; - - struct Compressor *compressor; - - struct pcm_buffer buffer; -}; - -static inline GQuark -normalize_quark(void) -{ - return g_quark_from_static_string("normalize"); -} - -static struct filter * -normalize_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct normalize_filter *filter = g_new(struct normalize_filter, 1); - - filter_init(&filter->filter, &normalize_filter_plugin); - - return &filter->filter; -} - -static void -normalize_filter_finish(struct filter *filter) -{ - g_free(filter); -} - -static const struct audio_format * -normalize_filter_open(struct filter *_filter, - struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) -{ - 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; -} - -static void -normalize_filter_close(struct filter *_filter) -{ - 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, - G_GNUC_UNUSED GError **error_r) -{ - struct normalize_filter *filter = (struct normalize_filter *)_filter; - void *dest; - - dest = pcm_buffer_get(&filter->buffer, src_size); - - memcpy(dest, src, src_size); - - Compressor_Process_int16(filter->compressor, dest, src_size / 2); - - *dest_size_r = src_size; - return dest; -} - -const struct filter_plugin normalize_filter_plugin = { - .name = "normalize", - .init = normalize_filter_init, - .finish = normalize_filter_finish, - .open = normalize_filter_open, - .close = normalize_filter_close, - .filter = normalize_filter_filter, -}; diff --git a/src/filter/null_filter_plugin.c b/src/filter/null_filter_plugin.c deleted file mode 100644 index e7c998827..000000000 --- a/src/filter/null_filter_plugin.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** \file - * - * This filter plugin does nothing. That is not quite useful, except - * for testing the filter core, or as a template for new filter - * plugins. - */ - -#include "config.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" - -#include <assert.h> - -struct null_filter { - struct filter filter; -}; - -static struct filter * -null_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_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, - G_GNUC_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, G_GNUC_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 = { - .name = "null", - .init = null_filter_init, - .finish = null_filter_finish, - .open = null_filter_open, - .close = null_filter_close, - .filter = null_filter_filter, -}; diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c deleted file mode 100644 index 583a09f90..000000000 --- a/src/filter/replay_gain_filter_plugin.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "filter/replay_gain_filter_plugin.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "audio_format.h" -#include "pcm_buffer.h" -#include "pcm_volume.h" -#include "replay_gain_info.h" -#include "replay_gain_config.h" -#include "mixer_control.h" - -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "replay_gain" - -struct replay_gain_filter { - struct filter filter; - - /** - * If set, then this hardware mixer is used for applying - * replay gain, instead of the software volume library. - */ - struct mixer *mixer; - - /** - * The base volume level for scale=1.0, between 1 and 100 - * (including). - */ - unsigned base; - - enum replay_gain_mode mode; - - struct replay_gain_info info; - - /** - * The current volume, between 0 and a value that may or may not exceed - * #PCM_VOLUME_1. - * - * If the default value of true is used for replaygain_limit, the - * application of the volume to the signal will never cause clipping. - * - * On the other hand, if the user has set replaygain_limit to false, - * the chance of clipping is explicitly preferred if that's required to - * maintain a consistent audio level. Whether clipping will actually - * occur depends on what value the user is using for replaygain_preamp. - */ - unsigned volume; - - struct audio_format audio_format; - - struct pcm_buffer buffer; -}; - -static inline GQuark -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) -{ - if (filter->mode != REPLAY_GAIN_OFF) { - float scale = replay_gain_tuple_scale(&filter->info.tuples[filter->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); - } else - filter->volume = PCM_VOLUME_1; - - if (filter->mixer != NULL) { - /* update the hardware mixer volume */ - - unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1; - if (volume > 100) - volume = 100; - - GError *error = NULL; - if (!mixer_set_volume(filter->mixer, volume, &error)) { - g_warning("Failed to update hardware mixer: %s", - error->message); - g_error_free(error); - } - } -} - -static struct filter * -replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_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_get_real_mode(); - replay_gain_info_init(&filter->info); - filter->volume = PCM_VOLUME_1; - - return &filter->filter; -} - -static void -replay_gain_filter_finish(struct filter *filter) -{ - g_free(filter); -} - -static const struct audio_format * -replay_gain_filter_open(struct filter *_filter, - struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) -{ - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - - filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); - - return &filter->audio_format; -} - -static void -replay_gain_filter_close(struct filter *_filter) -{ - 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; - enum replay_gain_mode rg_mode; - - /* check if the mode has been changed since the last call */ - rg_mode = replay_gain_get_real_mode(); - - if (filter->mode != rg_mode) { - g_debug("replay gain mode has changed %d->%d\n", filter->mode, rg_mode); - filter->mode = rg_mode; - replay_gain_filter_update(filter); - } - - *dest_size_r = src_size; - - if (filter->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) { - /* optimized special case: 0% volume = memset(0) */ - /* XXX is this valid for all sample formats? What - about floating point? */ - memset(dest, 0, src_size); - return dest; - } - - memcpy(dest, src, src_size); - - success = pcm_volume(dest, src_size, filter->audio_format.format, - filter->volume); - if (!success) { - g_set_error(error_r, replay_gain_quark(), 0, - "pcm_volume() has failed"); - return NULL; - } - - return dest; -} - -const struct filter_plugin replay_gain_filter_plugin = { - .name = "replay_gain", - .init = replay_gain_filter_init, - .finish = replay_gain_filter_finish, - .open = replay_gain_filter_open, - .close = replay_gain_filter_close, - .filter = replay_gain_filter_filter, -}; - -void -replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer, - unsigned base) -{ - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - - assert(mixer == NULL || (base > 0 && base <= 100)); - - filter->mixer = mixer; - filter->base = base; - - replay_gain_filter_update(filter); -} - -void -replay_gain_filter_set_info(struct filter *_filter, - const struct replay_gain_info *info) -{ - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_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); -} diff --git a/src/filter/volume_filter_plugin.c b/src/filter/volume_filter_plugin.c deleted file mode 100644 index 3260e8989..000000000 --- a/src/filter/volume_filter_plugin.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "filter/volume_filter_plugin.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "conf.h" -#include "pcm_buffer.h" -#include "pcm_volume.h" -#include "audio_format.h" - -#include <assert.h> -#include <string.h> - -struct volume_filter { - struct filter filter; - - /** - * The current volume, from 0 to #PCM_VOLUME_1. - */ - unsigned volume; - - struct audio_format audio_format; - - struct pcm_buffer buffer; -}; - -static inline GQuark -volume_quark(void) -{ - return g_quark_from_static_string("pcm_volume"); -} - -static struct filter * -volume_filter_init(G_GNUC_UNUSED const struct config_param *param, - G_GNUC_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; -} - -static void -volume_filter_finish(struct filter *filter) -{ - g_free(filter); -} - -static const struct audio_format * -volume_filter_open(struct filter *_filter, struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) -{ - struct volume_filter *filter = (struct volume_filter *)_filter; - - filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); - - return &filter->audio_format; -} - -static void -volume_filter_close(struct filter *_filter) -{ - 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) - /* optimized special case: 100% volume = no-op */ - return src; - - dest = pcm_buffer_get(&filter->buffer, src_size); - - if (filter->volume <= 0) { - /* optimized special case: 0% volume = memset(0) */ - /* XXX is this valid for all sample formats? What - about floating point? */ - memset(dest, 0, src_size); - return dest; - } - - memcpy(dest, src, src_size); - - success = pcm_volume(dest, src_size, filter->audio_format.format, - filter->volume); - if (!success) { - g_set_error(error_r, volume_quark(), 0, - "pcm_volume() has failed"); - return NULL; - } - - return dest; -} - -const struct filter_plugin volume_filter_plugin = { - .name = "volume", - .init = volume_filter_init, - .finish = volume_filter_finish, - .open = volume_filter_open, - .close = volume_filter_close, - .filter = volume_filter_filter, -}; - -unsigned -volume_filter_get(const struct filter *_filter) -{ - const struct volume_filter *filter = - (const struct volume_filter *)_filter; - - assert(filter->filter.plugin == &volume_filter_plugin); - assert(filter->volume <= PCM_VOLUME_1); - - return filter->volume; -} - -void -volume_filter_set(struct filter *_filter, unsigned volume) -{ - struct volume_filter *filter = (struct volume_filter *)_filter; - - assert(filter->filter.plugin == &volume_filter_plugin); - assert(volume <= PCM_VOLUME_1); - - filter->volume = volume; -} - |