Filter*: move to filter/
This commit is contained in:
131
src/filter/plugins/AutoConvertFilterPlugin.cxx
Normal file
131
src/filter/plugins/AutoConvertFilterPlugin.cxx
Normal 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);
|
||||
}
|
34
src/filter/plugins/AutoConvertFilterPlugin.hxx
Normal file
34
src/filter/plugins/AutoConvertFilterPlugin.hxx
Normal 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
|
181
src/filter/plugins/ChainFilterPlugin.cxx
Normal file
181
src/filter/plugins/ChainFilterPlugin.cxx
Normal 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 ¶m,
|
||||
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);
|
||||
}
|
48
src/filter/plugins/ChainFilterPlugin.hxx
Normal file
48
src/filter/plugins/ChainFilterPlugin.hxx
Normal 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
|
149
src/filter/plugins/ConvertFilterPlugin.cxx
Normal file
149
src/filter/plugins/ConvertFilterPlugin.cxx
Normal 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 ¶m,
|
||||
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);
|
||||
}
|
37
src/filter/plugins/ConvertFilterPlugin.hxx
Normal file
37
src/filter/plugins/ConvertFilterPlugin.hxx
Normal 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
|
82
src/filter/plugins/NormalizeFilterPlugin.cxx
Normal file
82
src/filter/plugins/NormalizeFilterPlugin.cxx
Normal 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 ¶m,
|
||||
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,
|
||||
};
|
61
src/filter/plugins/NullFilterPlugin.cxx
Normal file
61
src/filter/plugins/NullFilterPlugin.cxx
Normal 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 ¶m,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new NullFilter();
|
||||
}
|
||||
|
||||
const struct filter_plugin null_filter_plugin = {
|
||||
"null",
|
||||
null_filter_init,
|
||||
};
|
210
src/filter/plugins/ReplayGainFilterPlugin.cxx
Normal file
210
src/filter/plugins/ReplayGainFilterPlugin.cxx
Normal 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 ¶m,
|
||||
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);
|
||||
}
|
52
src/filter/plugins/ReplayGainFilterPlugin.hxx
Normal file
52
src/filter/plugins/ReplayGainFilterPlugin.hxx
Normal 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
|
296
src/filter/plugins/RouteFilterPlugin.cxx
Normal file
296
src/filter/plugins/RouteFilterPlugin.cxx
Normal 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 ¶m, 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 ¶m, 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 ¶m, 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,
|
||||
};
|
106
src/filter/plugins/VolumeFilterPlugin.cxx
Normal file
106
src/filter/plugins/VolumeFilterPlugin.cxx
Normal 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 ¶m,
|
||||
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);
|
||||
}
|
||||
|
31
src/filter/plugins/VolumeFilterPlugin.hxx
Normal file
31
src/filter/plugins/VolumeFilterPlugin.hxx
Normal 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
|
Reference in New Issue
Block a user