filter/FilterInternal: split class Filter, add class PreparedFilter
For easier state management inside filter plugins.
This commit is contained in:
@@ -24,7 +24,6 @@
|
||||
#include "filter/FilterInternal.hxx"
|
||||
#include "filter/FilterRegistry.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -33,82 +32,70 @@ class AutoConvertFilter final : public Filter {
|
||||
/**
|
||||
* The underlying filter.
|
||||
*/
|
||||
Filter *filter;
|
||||
Filter *const filter;
|
||||
|
||||
/**
|
||||
* A convert_filter, just in case conversion is needed. nullptr
|
||||
* if unused.
|
||||
*/
|
||||
Filter *convert;
|
||||
Filter *const convert;
|
||||
|
||||
public:
|
||||
AutoConvertFilter(Filter *_filter):filter(_filter) {}
|
||||
AutoConvertFilter(Filter *_filter, Filter *_convert)
|
||||
:filter(_filter), convert(_convert) {}
|
||||
|
||||
~AutoConvertFilter() {
|
||||
delete convert;
|
||||
delete filter;
|
||||
}
|
||||
|
||||
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
virtual void Close() override;
|
||||
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
AudioFormat
|
||||
AutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error)
|
||||
class PreparedAutoConvertFilter final : public PreparedFilter {
|
||||
/**
|
||||
* The underlying filter.
|
||||
*/
|
||||
PreparedFilter *const filter;
|
||||
|
||||
public:
|
||||
PreparedAutoConvertFilter(PreparedFilter *_filter):filter(_filter) {}
|
||||
~PreparedAutoConvertFilter() {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
virtual Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
Filter *
|
||||
PreparedAutoConvertFilter::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;
|
||||
auto *new_filter = filter->Open(child_audio_format, error);
|
||||
if (new_filter == nullptr)
|
||||
return nullptr;
|
||||
|
||||
/* need to convert? */
|
||||
|
||||
Filter *convert = nullptr;
|
||||
if (in_audio_format != child_audio_format) {
|
||||
/* yes - create a convert_filter */
|
||||
|
||||
const ConfigBlock empty;
|
||||
convert = filter_new(&convert_filter_plugin, empty, error);
|
||||
convert = convert_filter_new(in_audio_format,
|
||||
child_audio_format,
|
||||
error);
|
||||
if (convert == nullptr) {
|
||||
filter->Close();
|
||||
return AudioFormat::Undefined();
|
||||
delete new_filter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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();
|
||||
return new AutoConvertFilter(new_filter, convert);
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -123,8 +110,8 @@ AutoConvertFilter::FilterPCM(ConstBuffer<void> src, Error &error)
|
||||
return filter->FilterPCM(src, error);
|
||||
}
|
||||
|
||||
Filter *
|
||||
autoconvert_filter_new(Filter *filter)
|
||||
PreparedFilter *
|
||||
autoconvert_filter_new(PreparedFilter *filter)
|
||||
{
|
||||
return new AutoConvertFilter(filter);
|
||||
return new PreparedAutoConvertFilter(filter);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
|
||||
#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
|
||||
|
||||
class Filter;
|
||||
class PreparedFilter;
|
||||
|
||||
/**
|
||||
* Creates a new "autoconvert" filter. When opened, it ensures that
|
||||
@@ -28,7 +28,7 @@ class Filter;
|
||||
* requests a different format, it automatically creates a
|
||||
* convert_filter.
|
||||
*/
|
||||
Filter *
|
||||
autoconvert_filter_new(Filter *filter);
|
||||
PreparedFilter *
|
||||
autoconvert_filter_new(PreparedFilter *filter);
|
||||
|
||||
#endif
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include <assert.h>
|
||||
@@ -49,100 +50,98 @@ class ChainFilter final : public Filter {
|
||||
std::list<Child> children;
|
||||
|
||||
public:
|
||||
explicit ChainFilter(AudioFormat _audio_format)
|
||||
:Filter(_audio_format) {}
|
||||
|
||||
void Append(const char *name, Filter *filter) {
|
||||
assert(out_audio_format.IsValid());
|
||||
out_audio_format = filter->GetOutAudioFormat();
|
||||
assert(out_audio_format.IsValid());
|
||||
|
||||
children.emplace_back(name, filter);
|
||||
}
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
void Close() override;
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Close all filters in the chain until #until is reached.
|
||||
* #until itself is not closed.
|
||||
*/
|
||||
void CloseUntil(const Filter *until);
|
||||
class PreparedChainFilter final : public PreparedFilter {
|
||||
struct Child {
|
||||
const char *name;
|
||||
PreparedFilter *filter;
|
||||
|
||||
Child(const char *_name, PreparedFilter *_filter)
|
||||
:name(_name), filter(_filter) {}
|
||||
~Child() {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
Child(const Child &) = delete;
|
||||
Child &operator=(const Child &) = delete;
|
||||
|
||||
Filter *Open(const AudioFormat &prev_audio_format,
|
||||
Error &error);
|
||||
};
|
||||
|
||||
std::list<Child> children;
|
||||
|
||||
public:
|
||||
void Append(const char *name, PreparedFilter *filter) {
|
||||
children.emplace_back(name, filter);
|
||||
}
|
||||
|
||||
/* virtual methods from class PreparedFilter */
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
static constexpr Domain chain_filter_domain("chain_filter");
|
||||
|
||||
static Filter *
|
||||
static PreparedFilter *
|
||||
chain_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new ChainFilter();
|
||||
return new PreparedChainFilter();
|
||||
}
|
||||
|
||||
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)
|
||||
Filter *
|
||||
PreparedChainFilter::Child::Open(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;
|
||||
Filter *new_filter = filter->Open(conv_audio_format, error);
|
||||
if (new_filter == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (conv_audio_format != prev_audio_format) {
|
||||
delete new_filter;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
return next_audio_format;
|
||||
return new_filter;
|
||||
}
|
||||
|
||||
AudioFormat
|
||||
ChainFilter::Open(AudioFormat &in_audio_format, Error &error)
|
||||
Filter *
|
||||
PreparedChainFilter::Open(AudioFormat &in_audio_format, Error &error)
|
||||
{
|
||||
AudioFormat audio_format = in_audio_format;
|
||||
std::unique_ptr<ChainFilter> chain(new ChainFilter(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;
|
||||
}
|
||||
AudioFormat audio_format = chain->GetOutAudioFormat();
|
||||
auto *filter = child.Open(audio_format, error);
|
||||
if (filter == nullptr)
|
||||
return nullptr;
|
||||
|
||||
chain->Append(child.name, filter);
|
||||
}
|
||||
|
||||
/* return the output format of the last filter */
|
||||
return audio_format;
|
||||
}
|
||||
|
||||
void
|
||||
ChainFilter::Close()
|
||||
{
|
||||
for (auto &child : children)
|
||||
child.filter->Close();
|
||||
return chain.release();
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -165,16 +164,17 @@ const struct filter_plugin chain_filter_plugin = {
|
||||
chain_filter_init,
|
||||
};
|
||||
|
||||
Filter *
|
||||
PreparedFilter *
|
||||
filter_chain_new(void)
|
||||
{
|
||||
return new ChainFilter();
|
||||
return new PreparedChainFilter();
|
||||
}
|
||||
|
||||
void
|
||||
filter_chain_append(Filter &_chain, const char *name, Filter *filter)
|
||||
filter_chain_append(PreparedFilter &_chain, const char *name,
|
||||
PreparedFilter *filter)
|
||||
{
|
||||
ChainFilter &chain = (ChainFilter &)_chain;
|
||||
PreparedChainFilter &chain = (PreparedChainFilter &)_chain;
|
||||
|
||||
chain.Append(name, filter);
|
||||
}
|
||||
|
@@ -27,12 +27,12 @@
|
||||
#ifndef MPD_FILTER_CHAIN_HXX
|
||||
#define MPD_FILTER_CHAIN_HXX
|
||||
|
||||
class Filter;
|
||||
class PreparedFilter;
|
||||
|
||||
/**
|
||||
* Creates a new filter chain.
|
||||
*/
|
||||
Filter *
|
||||
PreparedFilter *
|
||||
filter_chain_new();
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,7 @@ filter_chain_new();
|
||||
* @param filter the filter to be appended to #chain
|
||||
*/
|
||||
void
|
||||
filter_chain_append(Filter &chain, const char *name, Filter *filter);
|
||||
filter_chain_append(PreparedFilter &chain, const char *name,
|
||||
PreparedFilter *filter);
|
||||
|
||||
#endif
|
||||
|
@@ -38,31 +38,33 @@ class ConvertFilter final : public Filter {
|
||||
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.
|
||||
* This object is only "open" if #in_audio_format !=
|
||||
* #out_audio_format.
|
||||
*/
|
||||
AudioFormat out_audio_format;
|
||||
|
||||
Manual<PcmConvert> state;
|
||||
PcmConvert state;
|
||||
|
||||
public:
|
||||
ConvertFilter(const AudioFormat &audio_format);
|
||||
~ConvertFilter();
|
||||
|
||||
bool Set(const AudioFormat &_out_audio_format, Error &error);
|
||||
|
||||
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
virtual void Close() override;
|
||||
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
static Filter *
|
||||
class PreparedConvertFilter final : public PreparedFilter {
|
||||
public:
|
||||
bool Set(const AudioFormat &_out_audio_format, Error &error);
|
||||
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
static PreparedFilter *
|
||||
convert_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new ConvertFilter();
|
||||
return new PreparedConvertFilter();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -75,47 +77,41 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format, Error &error)
|
||||
/* no change */
|
||||
return true;
|
||||
|
||||
if (out_audio_format.IsValid()) {
|
||||
out_audio_format.Clear();
|
||||
state->Close();
|
||||
if (out_audio_format != in_audio_format) {
|
||||
out_audio_format = in_audio_format;
|
||||
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))
|
||||
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)
|
||||
ConvertFilter::ConvertFilter(const AudioFormat &audio_format)
|
||||
:Filter(audio_format), in_audio_format(audio_format)
|
||||
{
|
||||
}
|
||||
|
||||
Filter *
|
||||
PreparedConvertFilter::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;
|
||||
return new ConvertFilter(audio_format);
|
||||
}
|
||||
|
||||
void
|
||||
ConvertFilter::Close()
|
||||
ConvertFilter::~ConvertFilter()
|
||||
{
|
||||
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));
|
||||
state.Close();
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -127,7 +123,7 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src, Error &error)
|
||||
/* optimized special case: no-op */
|
||||
return src;
|
||||
|
||||
return state->Convert(src, error);
|
||||
return state.Convert(src, error);
|
||||
}
|
||||
|
||||
const struct filter_plugin convert_filter_plugin = {
|
||||
@@ -135,6 +131,20 @@ const struct filter_plugin convert_filter_plugin = {
|
||||
convert_filter_init,
|
||||
};
|
||||
|
||||
Filter *
|
||||
convert_filter_new(const AudioFormat in_audio_format,
|
||||
const AudioFormat out_audio_format,
|
||||
Error &error)
|
||||
{
|
||||
auto *filter = new ConvertFilter(in_audio_format);
|
||||
if (!filter->Set(out_audio_format, error)) {
|
||||
delete filter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
bool
|
||||
convert_filter_set(Filter *_filter, AudioFormat out_audio_format,
|
||||
Error &error)
|
||||
|
@@ -24,6 +24,11 @@ class Filter;
|
||||
class Error;
|
||||
struct AudioFormat;
|
||||
|
||||
Filter *
|
||||
convert_filter_new(AudioFormat in_audio_format,
|
||||
AudioFormat out_audio_format,
|
||||
Error &error);
|
||||
|
||||
/**
|
||||
* Sets the output audio format for the specified filter. You must
|
||||
* call this after the filter has been opened. Since this audio
|
||||
|
@@ -29,40 +29,43 @@
|
||||
#include <string.h>
|
||||
|
||||
class NormalizeFilter final : public Filter {
|
||||
struct Compressor *compressor;
|
||||
Compressor *const compressor;
|
||||
|
||||
PcmBuffer buffer;
|
||||
|
||||
public:
|
||||
NormalizeFilter(const AudioFormat &audio_format)
|
||||
:Filter(audio_format), compressor(Compressor_new(0)) {
|
||||
}
|
||||
|
||||
~NormalizeFilter() {
|
||||
Compressor_delete(compressor);
|
||||
}
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
void Close() override;
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
static Filter *
|
||||
class PreparedNormalizeFilter final : public PreparedFilter {
|
||||
public:
|
||||
/* virtual methods from class PreparedFilter */
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
static PreparedFilter *
|
||||
normalize_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new NormalizeFilter();
|
||||
return new PreparedNormalizeFilter();
|
||||
}
|
||||
|
||||
AudioFormat
|
||||
NormalizeFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
|
||||
Filter *
|
||||
PreparedNormalizeFilter::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);
|
||||
return new NormalizeFilter(audio_format);
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
|
@@ -34,12 +34,7 @@
|
||||
|
||||
class NullFilter final : public Filter {
|
||||
public:
|
||||
virtual AudioFormat Open(AudioFormat &af,
|
||||
gcc_unused Error &error) override {
|
||||
return af;
|
||||
}
|
||||
|
||||
virtual void Close() override {}
|
||||
explicit NullFilter(const AudioFormat &af):Filter(af) {}
|
||||
|
||||
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
gcc_unused Error &error) override {
|
||||
@@ -47,11 +42,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static Filter *
|
||||
class PreparedNullFilter final : public PreparedFilter {
|
||||
public:
|
||||
virtual Filter *Open(AudioFormat &af,
|
||||
gcc_unused Error &error) override {
|
||||
return new NullFilter(af);
|
||||
}
|
||||
};
|
||||
|
||||
static PreparedFilter *
|
||||
null_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new NullFilter();
|
||||
return new PreparedNullFilter();
|
||||
}
|
||||
|
||||
const struct filter_plugin null_filter_plugin = {
|
||||
|
@@ -41,13 +41,13 @@ 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 = nullptr;
|
||||
Mixer *const mixer;
|
||||
|
||||
/**
|
||||
* The base volume level for scale=1.0, between 1 and 100
|
||||
* (including).
|
||||
*/
|
||||
unsigned base;
|
||||
const unsigned base;
|
||||
|
||||
ReplayGainMode mode = REPLAY_GAIN_OFF;
|
||||
|
||||
@@ -68,17 +68,15 @@ class ReplayGainFilter final : public Filter {
|
||||
PcmVolume pv;
|
||||
|
||||
public:
|
||||
ReplayGainFilter() {
|
||||
ReplayGainFilter(const AudioFormat &audio_format,
|
||||
Mixer *_mixer, unsigned _base)
|
||||
:Filter(audio_format),
|
||||
mixer(_mixer), base(_base), mode(REPLAY_GAIN_OFF) {
|
||||
info.Clear();
|
||||
}
|
||||
|
||||
void SetMixer(Mixer *_mixer, unsigned _base) {
|
||||
assert(_mixer == nullptr || (_base > 0 && _base <= 100));
|
||||
|
||||
mixer = _mixer;
|
||||
base = _base;
|
||||
|
||||
Update();
|
||||
bool Open(Error &error) {
|
||||
return pv.Open(out_audio_format.format, error);
|
||||
}
|
||||
|
||||
void SetInfo(const ReplayGainInfo *_info) {
|
||||
@@ -110,12 +108,35 @@ public:
|
||||
void Update();
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
void Close() override;
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
class PreparedReplayGainFilter final : public PreparedFilter {
|
||||
/**
|
||||
* If set, then this hardware mixer is used for applying
|
||||
* replay gain, instead of the software volume library.
|
||||
*/
|
||||
Mixer *mixer = nullptr;
|
||||
|
||||
/**
|
||||
* The base volume level for scale=1.0, between 1 and 100
|
||||
* (including).
|
||||
*/
|
||||
unsigned base;
|
||||
|
||||
public:
|
||||
void SetMixer(Mixer *_mixer, unsigned _base) {
|
||||
assert(_mixer == nullptr || (_base > 0 && _base <= 100));
|
||||
|
||||
mixer = _mixer;
|
||||
base = _base;
|
||||
}
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
void
|
||||
ReplayGainFilter::Update()
|
||||
{
|
||||
@@ -146,26 +167,23 @@ ReplayGainFilter::Update()
|
||||
}
|
||||
}
|
||||
|
||||
static Filter *
|
||||
static PreparedFilter *
|
||||
replay_gain_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new ReplayGainFilter();
|
||||
return new PreparedReplayGainFilter();
|
||||
}
|
||||
|
||||
AudioFormat
|
||||
ReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error)
|
||||
Filter *
|
||||
PreparedReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error)
|
||||
{
|
||||
if (!pv.Open(af.format, error))
|
||||
return AudioFormat::Undefined();
|
||||
auto *filter = new ReplayGainFilter(af, mixer, base);
|
||||
if (!filter->Open(error)) {
|
||||
delete filter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return af;
|
||||
}
|
||||
|
||||
void
|
||||
ReplayGainFilter::Close()
|
||||
{
|
||||
pv.Close();
|
||||
return filter;
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -180,10 +198,10 @@ const struct filter_plugin replay_gain_filter_plugin = {
|
||||
};
|
||||
|
||||
void
|
||||
replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
|
||||
replay_gain_filter_set_mixer(PreparedFilter *_filter, Mixer *mixer,
|
||||
unsigned base)
|
||||
{
|
||||
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
|
||||
PreparedReplayGainFilter *filter = (PreparedReplayGainFilter *)_filter;
|
||||
|
||||
filter->SetMixer(mixer, base);
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "ReplayGainInfo.hxx"
|
||||
|
||||
class Filter;
|
||||
class PreparedFilter;
|
||||
class Mixer;
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,7 @@ class Mixer;
|
||||
* (including).
|
||||
*/
|
||||
void
|
||||
replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer,
|
||||
replay_gain_filter_set_mixer(PreparedFilter *_filter, Mixer *mixer,
|
||||
unsigned base);
|
||||
|
||||
/**
|
||||
|
@@ -59,6 +59,46 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
class RouteFilter final : public Filter {
|
||||
/**
|
||||
* 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"
|
||||
*/
|
||||
const std::array<int8_t, MAX_CHANNELS> sources;
|
||||
|
||||
/**
|
||||
* The actual input format of our signal, once opened
|
||||
*/
|
||||
const AudioFormat input_format;
|
||||
|
||||
/**
|
||||
* The size, in bytes, of each multichannel frame in the
|
||||
* input buffer
|
||||
*/
|
||||
const 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:
|
||||
RouteFilter(const AudioFormat &audio_format, unsigned out_channels,
|
||||
const std::array<int8_t, MAX_CHANNELS> &_sources);
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
class PreparedRouteFilter final : public PreparedFilter {
|
||||
/**
|
||||
* The minimum number of channels we need for output
|
||||
* to be able to perform all the copies the user has specified
|
||||
@@ -81,33 +121,6 @@ class RouteFilter final : public Filter {
|
||||
*/
|
||||
std::array<int8_t, MAX_CHANNELS> sources;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -120,16 +133,13 @@ public:
|
||||
*/
|
||||
bool Configure(const ConfigBlock &block, Error &error);
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
void Close() override;
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
/* virtual methods from class PreparedFilter */
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
bool
|
||||
RouteFilter::Configure(const ConfigBlock &block, Error &error) {
|
||||
|
||||
PreparedRouteFilter::Configure(const ConfigBlock &block, 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
|
||||
@@ -204,10 +214,10 @@ RouteFilter::Configure(const ConfigBlock &block, Error &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Filter *
|
||||
static PreparedFilter *
|
||||
route_filter_init(const ConfigBlock &block, Error &error)
|
||||
{
|
||||
RouteFilter *filter = new RouteFilter();
|
||||
auto *filter = new PreparedRouteFilter();
|
||||
if (!filter->Configure(block, error)) {
|
||||
delete filter;
|
||||
return nullptr;
|
||||
@@ -216,28 +226,24 @@ route_filter_init(const ConfigBlock &block, Error &error)
|
||||
return filter;
|
||||
}
|
||||
|
||||
AudioFormat
|
||||
RouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
|
||||
RouteFilter::RouteFilter(const AudioFormat &audio_format,
|
||||
unsigned out_channels,
|
||||
const std::array<int8_t, MAX_CHANNELS> &_sources)
|
||||
:Filter(audio_format), sources(_sources), input_format(audio_format),
|
||||
input_frame_size(input_format.GetFrameSize())
|
||||
{
|
||||
// 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;
|
||||
out_audio_format.channels = out_channels;
|
||||
|
||||
// Precalculate this simple value, to speed up allocation later
|
||||
output_frame_size = output_format.GetFrameSize();
|
||||
|
||||
return output_format;
|
||||
output_frame_size = out_audio_format.GetFrameSize();
|
||||
}
|
||||
|
||||
void
|
||||
RouteFilter::Close()
|
||||
Filter *
|
||||
PreparedRouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
|
||||
{
|
||||
output_buffer.Clear();
|
||||
return new RouteFilter(audio_format, min_output_channels, sources);
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -261,7 +267,7 @@ RouteFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error)
|
||||
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) {
|
||||
for (unsigned c = 0; c < out_audio_format.channels; ++c) {
|
||||
if (sources[c] == -1 ||
|
||||
(unsigned)sources[c] >= input_format.channels) {
|
||||
// No source for this destination output,
|
||||
|
@@ -30,6 +30,13 @@ class VolumeFilter final : public Filter {
|
||||
PcmVolume pv;
|
||||
|
||||
public:
|
||||
explicit VolumeFilter(const AudioFormat &audio_format)
|
||||
:Filter(audio_format) {}
|
||||
|
||||
bool Open(Error &error) {
|
||||
return pv.Open(out_audio_format.format, error);
|
||||
}
|
||||
|
||||
unsigned GetVolume() const {
|
||||
return pv.GetVolume();
|
||||
}
|
||||
@@ -39,32 +46,35 @@ public:
|
||||
}
|
||||
|
||||
/* virtual methods from class Filter */
|
||||
AudioFormat Open(AudioFormat &af, Error &error) override;
|
||||
void Close() override;
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
static Filter *
|
||||
class PreparedVolumeFilter final : public PreparedFilter {
|
||||
PcmVolume pv;
|
||||
|
||||
public:
|
||||
/* virtual methods from class Filter */
|
||||
Filter *Open(AudioFormat &af, Error &error) override;
|
||||
};
|
||||
|
||||
static PreparedFilter *
|
||||
volume_filter_init(gcc_unused const ConfigBlock &block,
|
||||
gcc_unused Error &error)
|
||||
{
|
||||
return new VolumeFilter();
|
||||
return new PreparedVolumeFilter();
|
||||
}
|
||||
|
||||
AudioFormat
|
||||
VolumeFilter::Open(AudioFormat &audio_format, Error &error)
|
||||
Filter *
|
||||
PreparedVolumeFilter::Open(AudioFormat &audio_format, Error &error)
|
||||
{
|
||||
if (!pv.Open(audio_format.format, error))
|
||||
return AudioFormat::Undefined();
|
||||
auto *filter = new VolumeFilter(audio_format);
|
||||
if (!filter->Open(error)) {
|
||||
delete filter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audio_format;
|
||||
}
|
||||
|
||||
void
|
||||
VolumeFilter::Close()
|
||||
{
|
||||
pv.Close();
|
||||
return filter;
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
|
Reference in New Issue
Block a user