Filter*: move to filter/

This commit is contained in:
Max Kellermann
2014-01-24 16:31:52 +01:00
parent 9d34fc394c
commit 7c52a1c04b
29 changed files with 67 additions and 67 deletions

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2003-2014 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 "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "AudioFormat.hxx"
#include "config/ConfigData.hxx"
#include <assert.h>
class AutoConvertFilter final : public Filter {
/**
* 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 AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close() override;
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r,
Error &error) override;
};
AudioFormat
AutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error)
{
assert(in_audio_format.IsValid());
/* open the "real" filter */
AudioFormat child_audio_format = in_audio_format;
AudioFormat out_audio_format = filter->Open(child_audio_format, error);
if (!out_audio_format.IsDefined())
return out_audio_format;
/* need to convert? */
if (in_audio_format != child_audio_format) {
/* yes - create a convert_filter */
const config_param empty;
convert = filter_new(&convert_filter_plugin, empty, error);
if (convert == nullptr) {
filter->Close();
return AudioFormat::Undefined();
}
AudioFormat audio_format2 = in_audio_format;
AudioFormat audio_format3 =
convert->Open(audio_format2, error);
if (!audio_format3.IsDefined()) {
delete convert;
filter->Close();
return AudioFormat::Undefined();
}
assert(audio_format2 == in_audio_format);
if (!convert_filter_set(convert, child_audio_format, error)) {
delete convert;
filter->Close();
return AudioFormat::Undefined();
}
} 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, Error &error)
{
if (convert != nullptr) {
src = convert->FilterPCM(src, src_size, &src_size, error);
if (src == nullptr)
return nullptr;
}
return filter->FilterPCM(src, src_size, dest_size_r, error);
}
Filter *
autoconvert_filter_new(Filter *filter)
{
return new AutoConvertFilter(filter);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
class Filter;
/**
* Creates a new "autoconvert" filter. When opened, it ensures that
* the input audio format isn't changed. If the underlying filter
* requests a different format, it automatically creates a
* convert_filter.
*/
Filter *
autoconvert_filter_new(Filter *filter);
#endif

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2003-2014 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 "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "AudioFormat.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#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 AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error);
private:
/**
* Close all filters in the chain until #until is reached.
* #until itself is not closed.
*/
void CloseUntil(const Filter *until);
};
static constexpr Domain chain_filter_domain("chain_filter");
static Filter *
chain_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
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);
gcc_unreachable();
}
static AudioFormat
chain_open_child(const char *name, Filter *filter,
const AudioFormat &prev_audio_format,
Error &error)
{
AudioFormat conv_audio_format = prev_audio_format;
const AudioFormat next_audio_format =
filter->Open(conv_audio_format, error);
if (!next_audio_format.IsDefined())
return next_audio_format;
if (conv_audio_format != prev_audio_format) {
struct audio_format_string s;
filter->Close();
error.Format(chain_filter_domain,
"Audio format not supported by filter '%s': %s",
name,
audio_format_to_string(prev_audio_format, &s));
return AudioFormat::Undefined();
}
return next_audio_format;
}
AudioFormat
ChainFilter::Open(AudioFormat &in_audio_format, Error &error)
{
AudioFormat audio_format = in_audio_format;
for (auto &child : children) {
audio_format = chain_open_child(child.name, child.filter,
audio_format, error);
if (!audio_format.IsDefined()) {
/* rollback, close all children */
CloseUntil(child.filter);
break;
}
}
/* 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, Error &error)
{
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);
if (src == nullptr)
return nullptr;
}
/* 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);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2003-2014 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
*
* A filter chain is a container for several filters. They are
* chained together, i.e. called in a row, one filter passing its
* output to the next one.
*/
#ifndef MPD_FILTER_CHAIN_HXX
#define MPD_FILTER_CHAIN_HXX
class Filter;
/**
* Creates a new filter chain.
*/
Filter *
filter_chain_new(void);
/**
* Appends a new filter at the end of the filter chain. You must call
* this function before the first filter_open() call.
*
* @param chain the filter chain created with filter_chain_new()
* @param filter the filter to be appended to #chain
*/
void
filter_chain_append(Filter &chain, const char *name, Filter *filter);
#endif

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2003-2014 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 "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "pcm/PcmConvert.hxx"
#include "util/Manual.hxx"
#include "AudioFormat.hxx"
#include "poison.h"
#include <assert.h>
class ConvertFilter final : public Filter {
/**
* The input audio format; PCM data is passed to the filter()
* method in this format.
*/
AudioFormat in_audio_format;
/**
* The output audio format; the consumer of this plugin
* expects PCM data in this format.
*
* If this is AudioFormat::Undefined(), then the #PcmConvert
* attribute is not open. This can mean that Set() has failed
* or that no conversion is necessary.
*/
AudioFormat out_audio_format;
Manual<PcmConvert> state;
public:
bool Set(const AudioFormat &_out_audio_format, Error &error);
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close() override;
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r,
Error &error) override;
};
static Filter *
convert_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
return new ConvertFilter();
}
bool
ConvertFilter::Set(const AudioFormat &_out_audio_format, Error &error)
{
assert(in_audio_format.IsValid());
assert(_out_audio_format.IsValid());
if (_out_audio_format == out_audio_format)
/* no change */
return true;
if (out_audio_format.IsValid()) {
out_audio_format.Clear();
state->Close();
}
if (_out_audio_format == in_audio_format)
/* optimized special case: no-op */
return true;
if (!state->Open(in_audio_format, _out_audio_format, error))
return false;
out_audio_format = _out_audio_format;
return true;
}
AudioFormat
ConvertFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
assert(audio_format.IsValid());
in_audio_format = audio_format;
out_audio_format.Clear();
state.Construct();
return in_audio_format;
}
void
ConvertFilter::Close()
{
assert(in_audio_format.IsValid());
if (out_audio_format.IsValid())
state->Close();
state.Destruct();
poison_undefined(&in_audio_format, sizeof(in_audio_format));
poison_undefined(&out_audio_format, sizeof(out_audio_format));
}
const void *
ConvertFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error)
{
assert(in_audio_format.IsValid());
if (!out_audio_format.IsValid()) {
/* optimized special case: no-op */
*dest_size_r = src_size;
return src;
}
return state->Convert(src, src_size, dest_size_r,
error);
}
const struct filter_plugin convert_filter_plugin = {
"convert",
convert_filter_init,
};
bool
convert_filter_set(Filter *_filter, AudioFormat out_audio_format,
Error &error)
{
ConvertFilter *filter = (ConvertFilter *)_filter;
return filter->Set(out_audio_format, error);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
#define MPD_CONVERT_FILTER_PLUGIN_HXX
class Filter;
class Error;
struct AudioFormat;
/**
* Sets the output audio format for the specified filter. You must
* call this after the filter has been opened. Since this audio
* format switch is a violation of the filter API, this filter must be
* the last in a chain.
*/
bool
convert_filter_set(Filter *filter, AudioFormat out_audio_format,
Error &error);
#endif

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2003-2014 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/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "pcm/PcmBuffer.hxx"
#include "AudioFormat.hxx"
#include "AudioCompress/compress.h"
#include <string.h>
class NormalizeFilter final : public Filter {
struct Compressor *compressor;
PcmBuffer buffer;
public:
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error);
};
static Filter *
normalize_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
return new NormalizeFilter();
}
AudioFormat
NormalizeFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
audio_format.format = SampleFormat::S16;
compressor = Compressor_new(0);
return audio_format;
}
void
NormalizeFilter::Close()
{
buffer.Clear();
Compressor_delete(compressor);
}
const void *
NormalizeFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, gcc_unused Error &error)
{
int16_t *dest = (int16_t *)buffer.Get(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,
};

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2003-2014 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/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "AudioFormat.hxx"
#include "Compiler.h"
class NullFilter final : public Filter {
public:
virtual AudioFormat Open(AudioFormat &af,
gcc_unused Error &error) override {
return af;
}
virtual void Close() override {}
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r,
gcc_unused Error &error) override {
*dest_size_r = src_size;
return src;
}
};
static Filter *
null_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
return new NullFilter();
}
const struct filter_plugin null_filter_plugin = {
"null",
null_filter_init,
};

View File

@@ -0,0 +1,210 @@
/*
* Copyright (C) 2003-2014 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 "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "AudioFormat.hxx"
#include "ReplayGainInfo.hxx"
#include "ReplayGainConfig.hxx"
#include "MixerControl.hxx"
#include "pcm/Volume.hxx"
#include "pcm/PcmBuffer.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include <assert.h>
#include <string.h>
static constexpr Domain replay_gain_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.
*/
Mixer *mixer;
/**
* The base volume level for scale=1.0, between 1 and 100
* (including).
*/
unsigned base;
ReplayGainMode mode;
ReplayGainInfo info;
/**
* About the current volume: it is 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.
*/
PcmVolume pv;
public:
ReplayGainFilter()
:mixer(nullptr), mode(REPLAY_GAIN_OFF) {
info.Clear();
}
void SetMixer(Mixer *_mixer, unsigned _base) {
assert(_mixer == nullptr || (_base > 0 && _base <= 100));
mixer = _mixer;
base = _base;
Update();
}
void SetInfo(const ReplayGainInfo *_info) {
if (_info != nullptr) {
info = *_info;
info.Complete();
} else
info.Clear();
Update();
}
void SetMode(ReplayGainMode _mode) {
if (_mode == mode)
/* no change */
return;
FormatDebug(replay_gain_domain,
"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 AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error);
};
void
ReplayGainFilter::Update()
{
unsigned volume = PCM_VOLUME_1;
if (mode != REPLAY_GAIN_OFF) {
const auto &tuple = info.tuples[mode];
float scale = tuple.CalculateScale(replay_gain_preamp,
replay_gain_missing_preamp,
replay_gain_limit);
FormatDebug(replay_gain_domain,
"scale=%f\n", (double)scale);
volume = pcm_float_to_volume(scale);
}
pv.SetVolume(volume);
if (mixer != nullptr) {
/* update the hardware mixer volume */
unsigned _volume = (volume * base) / PCM_VOLUME_1;
if (_volume > 100)
_volume = 100;
Error error;
if (!mixer_set_volume(mixer, _volume, error))
LogError(error, "Failed to update hardware mixer");
}
}
static Filter *
replay_gain_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
return new ReplayGainFilter();
}
AudioFormat
ReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error)
{
if (!pv.Open(af.format, error))
return AudioFormat::Undefined();
return af;
}
void
ReplayGainFilter::Close()
{
pv.Close();
}
const void *
ReplayGainFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, gcc_unused Error &error)
{
const auto dest = pv.Apply({src, src_size});
*dest_size_r = dest.size;
return dest.data;
}
const struct filter_plugin replay_gain_filter_plugin = {
"replay_gain",
replay_gain_filter_init,
};
void
replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
unsigned base)
{
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
filter->SetMixer(mixer, base);
}
void
replay_gain_filter_set_info(Filter *_filter, const ReplayGainInfo *info)
{
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
filter->SetInfo(info);
}
void
replay_gain_filter_set_mode(Filter *_filter, ReplayGainMode mode)
{
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
filter->SetMode(mode);
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
#include "ReplayGainInfo.hxx"
class Filter;
class Mixer;
/**
* Enables or disables the hardware mixer for applying replay gain.
*
* @param mixer the hardware mixer, or nullptr to fall back to software
* volume
* @param base the base volume level for scale=1.0, between 1 and 100
* (including).
*/
void
replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
unsigned base);
/**
* Sets a new #replay_gain_info at the beginning of a new song.
*
* @param info the new #replay_gain_info value, or nullptr if no replay
* gain data is available for the current song
*/
void
replay_gain_filter_set_info(Filter *filter, const ReplayGainInfo *info);
void
replay_gain_filter_set_mode(Filter *filter, ReplayGainMode mode);
#endif

View File

@@ -0,0 +1,296 @@
/*
* Copyright (C) 2003-2014 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 copies audio data between channels. Useful for
* upmixing mono/stereo audio to surround speaker configurations.
*
* Its configuration consists of a "filter" section with a single
* "routes" entry, formatted as: \\
* routes "0>1, 1>0, 2>2, 3>3, 3>4" \\
* where each pair of numbers signifies a set of channels.
* Each source>dest pair leads to the data from channel #source
* being copied to channel #dest in the output.
*
* Example: \\
* routes "0>0, 1>1, 0>2, 1>3"\\
* upmixes stereo audio to a 4-speaker system, copying the front-left
* (0) to front left (0) and rear left (2), copying front-right (1) to
* front-right (1) and rear-right (3).
*
* If multiple sources are copied to the same destination channel, only
* one of them takes effect.
*/
#include "config.h"
#include "config/ConfigError.hxx"
#include "config/ConfigData.hxx"
#include "AudioFormat.hxx"
#include "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "pcm/PcmBuffer.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include <algorithm>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
class RouteFilter final : public Filter {
/**
* The minimum number of channels we need for output
* to be able to perform all the copies the user has specified
*/
unsigned min_output_channels;
/**
* The minimum number of input channels we need to
* copy all the data the user has requested. If fewer
* than this many are supplied by the input, undefined
* copy operations are given zeroed sources in stead.
*/
unsigned min_input_channels;
/**
* The set of copy operations to perform on each sample
* The index is an output channel to use, the value is
* a corresponding input channel from which to take the
* data. A -1 means "no source"
*/
int8_t sources[MAX_CHANNELS];
/**
* The actual input format of our signal, once opened
*/
AudioFormat input_format;
/**
* The decided upon output format, once opened
*/
AudioFormat output_format;
/**
* The size, in bytes, of each multichannel frame in the
* input buffer
*/
size_t input_frame_size;
/**
* The size, in bytes, of each multichannel frame in the
* output buffer
*/
size_t output_frame_size;
/**
* The output buffer used last time around, can be reused if the size doesn't differ.
*/
PcmBuffer output_buffer;
public:
/**
* 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, Error &error);
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error);
};
bool
RouteFilter::Configure(const config_param &param, Error &error) {
/* TODO:
* With a more clever way of marking "don't copy to output N",
* This could easily be merged into a single loop with some
* dynamic realloc() instead of one count run and one malloc().
*/
std::fill_n(sources, MAX_CHANNELS, -1);
min_input_channels = 0;
min_output_channels = 0;
// A cowardly default, just passthrough stereo
const char *routes = param.GetBlockValue("routes", "0>0, 1>1");
while (true) {
routes = strchug_fast(routes);
char *endptr;
const unsigned source = strtoul(routes, &endptr, 10);
endptr = strchug_fast(endptr);
if (endptr == routes || *endptr != '>') {
error.Set(config_domain,
"Malformed 'routes' specification");
return false;
}
if (source >= MAX_CHANNELS) {
error.Format(config_domain,
"Invalid source channel number: %u",
source);
return false;
}
if (source >= min_input_channels)
min_input_channels = source + 1;
routes = strchug_fast(endptr + 1);
unsigned dest = strtoul(routes, &endptr, 10);
endptr = strchug_fast(endptr);
if (endptr == routes) {
error.Set(config_domain,
"Malformed 'routes' specification");
return false;
}
if (dest >= MAX_CHANNELS) {
error.Format(config_domain,
"Invalid destination channel number: %u",
dest);
return false;
}
if (dest >= min_output_channels)
min_output_channels = dest + 1;
sources[dest] = source;
routes = endptr;
if (*routes == 0)
break;
if (*routes != ',') {
error.Set(config_domain,
"Malformed 'routes' specification");
return false;
}
++routes;
}
return true;
}
static Filter *
route_filter_init(const config_param &param, Error &error)
{
RouteFilter *filter = new RouteFilter();
if (!filter->Configure(param, error)) {
delete filter;
return nullptr;
}
return filter;
}
AudioFormat
RouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
// Copy the input format for later reference
input_format = audio_format;
input_frame_size = input_format.GetFrameSize();
// Decide on an output format which has enough channels,
// and is otherwise identical
output_format = audio_format;
output_format.channels = min_output_channels;
// Precalculate this simple value, to speed up allocation later
output_frame_size = output_format.GetFrameSize();
return output_format;
}
void
RouteFilter::Close()
{
output_buffer.Clear();
}
const void *
RouteFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, gcc_unused Error &error)
{
size_t number_of_frames = src_size / input_frame_size;
const size_t bytes_per_frame_per_channel = input_format.GetSampleSize();
// 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;
// Grow our reusable buffer, if needed, and set the moving pointer
*dest_size_r = number_of_frames * output_frame_size;
void *const result = output_buffer.Get(*dest_size_r);
// A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output
uint8_t *chan_destination = (uint8_t *)result;
// 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<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,
0x00,
bytes_per_frame_per_channel);
} else {
// Get the data from channel sources[c]
// and copy it to the output
const uint8_t *data = base_source +
(sources[c] * bytes_per_frame_per_channel);
memcpy(chan_destination,
data,
bytes_per_frame_per_channel);
}
// Move on to the next output channel
chan_destination += bytes_per_frame_per_channel;
}
// Go on to the next N input samples
base_source += input_frame_size;
}
// Here it is, ladies and gentlemen! Rerouted data!
return result;
}
const struct filter_plugin route_filter_plugin = {
"route",
route_filter_init,
};

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2003-2014 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 "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "pcm/Volume.hxx"
#include "AudioFormat.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include <assert.h>
#include <string.h>
class VolumeFilter final : public Filter {
PcmVolume pv;
public:
unsigned GetVolume() const {
return pv.GetVolume();
}
void SetVolume(unsigned _volume) {
pv.SetVolume(_volume);
}
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual const void *FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error);
};
static constexpr Domain volume_domain("pcm_volume");
static Filter *
volume_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)
{
return new VolumeFilter();
}
AudioFormat
VolumeFilter::Open(AudioFormat &audio_format, Error &error)
{
if (!pv.Open(audio_format.format, error))
return AudioFormat::Undefined();
return audio_format;
}
void
VolumeFilter::Close()
{
pv.Close();
}
const void *
VolumeFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, gcc_unused Error &error)
{
const auto dest = pv.Apply({src, src_size});
*dest_size_r = dest.size;
return dest.data;
}
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);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
#define MPD_VOLUME_FILTER_PLUGIN_HXX
class Filter;
unsigned
volume_filter_get(const Filter *filter);
void
volume_filter_set(Filter *filter, unsigned volume);
#endif