FilterInternal: convert struct filter to a OO interface
This commit is contained in:
@@ -27,143 +27,106 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct AutoConvertFilter {
|
||||
struct filter base;
|
||||
|
||||
class AutoConvertFilter final : public Filter {
|
||||
/**
|
||||
* The audio format being fed to the underlying filter. This
|
||||
* plugin actually doesn't need this variable, we have it here
|
||||
* just so our open() method doesn't return a stack pointer.
|
||||
*/
|
||||
struct audio_format in_audio_format;
|
||||
audio_format child_audio_format;
|
||||
|
||||
/**
|
||||
* The underlying filter.
|
||||
*/
|
||||
struct filter *filter;
|
||||
Filter *filter;
|
||||
|
||||
/**
|
||||
* A convert_filter, just in case conversion is needed. nullptr
|
||||
* if unused.
|
||||
*/
|
||||
struct filter *convert;
|
||||
|
||||
AutoConvertFilter(const filter_plugin &plugin, struct filter *_filter)
|
||||
:filter(_filter) {
|
||||
filter_init(&base, &plugin);
|
||||
}
|
||||
Filter *convert;
|
||||
|
||||
public:
|
||||
AutoConvertFilter(Filter *_filter):filter(_filter) {}
|
||||
~AutoConvertFilter() {
|
||||
filter_free(filter);
|
||||
delete filter;
|
||||
}
|
||||
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
static void
|
||||
autoconvert_filter_finish(struct filter *_filter)
|
||||
const struct audio_format *
|
||||
AutoConvertFilter::Open(audio_format &in_audio_format, GError **error_r)
|
||||
{
|
||||
AutoConvertFilter *filter = (AutoConvertFilter *)_filter;
|
||||
|
||||
delete filter;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
autoconvert_filter_open(struct filter *_filter,
|
||||
struct audio_format *in_audio_format,
|
||||
GError **error_r)
|
||||
{
|
||||
AutoConvertFilter *filter = (AutoConvertFilter *)_filter;
|
||||
const struct audio_format *out_audio_format;
|
||||
|
||||
assert(audio_format_valid(in_audio_format));
|
||||
assert(audio_format_valid(&in_audio_format));
|
||||
|
||||
/* open the "real" filter */
|
||||
|
||||
filter->in_audio_format = *in_audio_format;
|
||||
|
||||
out_audio_format = filter_open(filter->filter,
|
||||
&filter->in_audio_format, error_r);
|
||||
child_audio_format = in_audio_format;
|
||||
const audio_format *out_audio_format =
|
||||
filter->Open(child_audio_format, error_r);
|
||||
if (out_audio_format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
/* need to convert? */
|
||||
|
||||
if (!audio_format_equals(&filter->in_audio_format, in_audio_format)) {
|
||||
if (!audio_format_equals(&child_audio_format, &in_audio_format)) {
|
||||
/* yes - create a convert_filter */
|
||||
struct audio_format audio_format2 = *in_audio_format;
|
||||
const struct audio_format *audio_format3;
|
||||
|
||||
filter->convert = filter_new(&convert_filter_plugin, nullptr,
|
||||
error_r);
|
||||
if (filter->convert == nullptr) {
|
||||
filter_close(filter->filter);
|
||||
convert = filter_new(&convert_filter_plugin, nullptr, error_r);
|
||||
if (convert == nullptr) {
|
||||
filter->Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audio_format3 = filter_open(filter->convert, &audio_format2,
|
||||
error_r);
|
||||
audio_format audio_format2 = in_audio_format;
|
||||
const audio_format *audio_format3 =
|
||||
convert->Open(audio_format2, error_r);
|
||||
if (audio_format3 == nullptr) {
|
||||
filter_free(filter->convert);
|
||||
filter_close(filter->filter);
|
||||
delete convert;
|
||||
filter->Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(audio_format_equals(&audio_format2, in_audio_format));
|
||||
assert(audio_format_equals(&audio_format2, &in_audio_format));
|
||||
|
||||
convert_filter_set(filter->convert, &filter->in_audio_format);
|
||||
convert_filter_set(convert, child_audio_format);
|
||||
} else
|
||||
/* no */
|
||||
filter->convert = nullptr;
|
||||
convert = nullptr;
|
||||
|
||||
return out_audio_format;
|
||||
}
|
||||
|
||||
static void
|
||||
autoconvert_filter_close(struct filter *_filter)
|
||||
void
|
||||
AutoConvertFilter::Close()
|
||||
{
|
||||
AutoConvertFilter *filter =
|
||||
(AutoConvertFilter *)_filter;
|
||||
|
||||
if (filter->convert != nullptr) {
|
||||
filter_close(filter->convert);
|
||||
filter_free(filter->convert);
|
||||
if (convert != nullptr) {
|
||||
convert->Close();
|
||||
delete convert;
|
||||
}
|
||||
|
||||
filter_close(filter->filter);
|
||||
filter->Close();
|
||||
}
|
||||
|
||||
static const void *
|
||||
autoconvert_filter_filter(struct filter *_filter, const void *src,
|
||||
size_t src_size, size_t *dest_size_r,
|
||||
GError **error_r)
|
||||
const void *
|
||||
AutoConvertFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
AutoConvertFilter *filter = (AutoConvertFilter *)_filter;
|
||||
|
||||
if (filter->convert != nullptr) {
|
||||
src = filter_filter(filter->convert, src, src_size, &src_size,
|
||||
error_r);
|
||||
if (convert != nullptr) {
|
||||
src = convert->FilterPCM(src, src_size, &src_size, error_r);
|
||||
if (src == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return filter_filter(filter->filter, src, src_size, dest_size_r,
|
||||
error_r);
|
||||
return filter->FilterPCM(src, src_size, dest_size_r, error_r);
|
||||
}
|
||||
|
||||
static const struct filter_plugin autoconvert_filter_plugin = {
|
||||
"convert",
|
||||
nullptr,
|
||||
autoconvert_filter_finish,
|
||||
autoconvert_filter_open,
|
||||
autoconvert_filter_close,
|
||||
autoconvert_filter_filter,
|
||||
};
|
||||
|
||||
struct filter *
|
||||
autoconvert_filter_new(struct filter *_filter)
|
||||
Filter *
|
||||
autoconvert_filter_new(Filter *filter)
|
||||
{
|
||||
AutoConvertFilter *filter =
|
||||
new AutoConvertFilter(autoconvert_filter_plugin,
|
||||
_filter);
|
||||
|
||||
return &filter->base;
|
||||
return new AutoConvertFilter(filter);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
|
||||
#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
|
||||
|
||||
struct filter;
|
||||
class Filter;
|
||||
|
||||
/**
|
||||
* Creates a new "autoconvert" filter. When opened, it ensures that
|
||||
@@ -28,7 +28,7 @@ struct filter;
|
||||
* requests a different format, it automatically creates a
|
||||
* convert_filter.
|
||||
*/
|
||||
struct filter *
|
||||
autoconvert_filter_new(struct filter *filter);
|
||||
Filter *
|
||||
autoconvert_filter_new(Filter *filter);
|
||||
|
||||
#endif
|
||||
|
@@ -31,20 +31,39 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct ChainFilter {
|
||||
/** the base class */
|
||||
struct filter base;
|
||||
class ChainFilter final : public Filter {
|
||||
struct Child {
|
||||
const char *name;
|
||||
Filter *filter;
|
||||
|
||||
std::list<struct filter *> children;
|
||||
Child(const char *_name, Filter *_filter)
|
||||
:name(_name), filter(_filter) {}
|
||||
~Child() {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
ChainFilter() {
|
||||
filter_init(&base, &chain_filter_plugin);
|
||||
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);
|
||||
}
|
||||
|
||||
~ChainFilter() {
|
||||
for (auto i : children)
|
||||
filter_free(i);
|
||||
}
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Close all filters in the chain until #until is reached.
|
||||
* #until itself is not closed.
|
||||
*/
|
||||
void CloseUntil(const Filter *until);
|
||||
};
|
||||
|
||||
static inline GQuark
|
||||
@@ -53,37 +72,23 @@ filter_quark(void)
|
||||
return g_quark_from_static_string("filter");
|
||||
}
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
chain_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
ChainFilter *chain = new ChainFilter();
|
||||
|
||||
return &chain->base;
|
||||
return new ChainFilter();
|
||||
}
|
||||
|
||||
static void
|
||||
chain_filter_finish(struct filter *_filter)
|
||||
void
|
||||
ChainFilter::CloseUntil(const Filter *until)
|
||||
{
|
||||
ChainFilter *chain = (ChainFilter *)_filter;
|
||||
|
||||
delete chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all filters in the chain until #until is reached. #until
|
||||
* itself is not closed.
|
||||
*/
|
||||
static void
|
||||
chain_close_until(ChainFilter *chain, const struct filter *until)
|
||||
{
|
||||
for (auto filter : chain->children) {
|
||||
if (filter == until)
|
||||
for (auto &child : children) {
|
||||
if (child.filter == until)
|
||||
/* don't close this filter */
|
||||
return;
|
||||
|
||||
/* close this filter */
|
||||
filter_close(filter);
|
||||
child.filter->Close();
|
||||
}
|
||||
|
||||
/* this assertion fails if #until does not exist (anymore) */
|
||||
@@ -91,43 +96,41 @@ chain_close_until(ChainFilter *chain, const struct filter *until)
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
chain_open_child(struct filter *filter,
|
||||
const struct audio_format *prev_audio_format,
|
||||
chain_open_child(const char *name, Filter *filter,
|
||||
const audio_format &prev_audio_format,
|
||||
GError **error_r)
|
||||
{
|
||||
struct audio_format conv_audio_format = *prev_audio_format;
|
||||
const struct audio_format *next_audio_format;
|
||||
|
||||
next_audio_format = filter_open(filter, &conv_audio_format, error_r);
|
||||
audio_format conv_audio_format = prev_audio_format;
|
||||
const audio_format *next_audio_format =
|
||||
filter->Open(conv_audio_format, error_r);
|
||||
if (next_audio_format == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!audio_format_equals(&conv_audio_format, prev_audio_format)) {
|
||||
if (!audio_format_equals(&conv_audio_format, &prev_audio_format)) {
|
||||
struct audio_format_string s;
|
||||
|
||||
filter_close(filter);
|
||||
filter->Close();
|
||||
g_set_error(error_r, filter_quark(), 0,
|
||||
"Audio format not supported by filter '%s': %s",
|
||||
filter->plugin->name,
|
||||
audio_format_to_string(prev_audio_format, &s));
|
||||
name,
|
||||
audio_format_to_string(&prev_audio_format, &s));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return next_audio_format;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format,
|
||||
GError **error_r)
|
||||
const audio_format *
|
||||
ChainFilter::Open(audio_format &in_audio_format, GError **error_r)
|
||||
{
|
||||
ChainFilter *chain = (ChainFilter *)_filter;
|
||||
const struct audio_format *audio_format = in_audio_format;
|
||||
const audio_format *audio_format = &in_audio_format;
|
||||
|
||||
for (auto filter : chain->children) {
|
||||
audio_format = chain_open_child(filter, audio_format, error_r);
|
||||
for (auto &child : children) {
|
||||
audio_format = chain_open_child(child.name, child.filter,
|
||||
*audio_format, error_r);
|
||||
if (audio_format == NULL) {
|
||||
/* rollback, close all children */
|
||||
chain_close_until(chain, filter);
|
||||
CloseUntil(child.filter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -136,26 +139,22 @@ chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format,
|
||||
return audio_format;
|
||||
}
|
||||
|
||||
static void
|
||||
chain_filter_close(struct filter *_filter)
|
||||
void
|
||||
ChainFilter::Close()
|
||||
{
|
||||
ChainFilter *chain = (ChainFilter *)_filter;
|
||||
|
||||
for (auto filter : chain->children)
|
||||
filter_close(filter);
|
||||
for (auto &child : children)
|
||||
child.filter->Close();
|
||||
}
|
||||
|
||||
static const void *
|
||||
chain_filter_filter(struct filter *_filter,
|
||||
const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
const void *
|
||||
ChainFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
ChainFilter *chain = (ChainFilter *)_filter;
|
||||
|
||||
for (auto filter : chain->children) {
|
||||
for (auto &child : children) {
|
||||
/* feed the output of the previous filter as input
|
||||
into the current one */
|
||||
src = filter_filter(filter, src, src_size, &src_size, error_r);
|
||||
src = child.filter->FilterPCM(src, src_size, &src_size,
|
||||
error_r);
|
||||
if (src == NULL)
|
||||
return NULL;
|
||||
}
|
||||
@@ -168,26 +167,18 @@ chain_filter_filter(struct filter *_filter,
|
||||
const struct filter_plugin chain_filter_plugin = {
|
||||
"chain",
|
||||
chain_filter_init,
|
||||
chain_filter_finish,
|
||||
chain_filter_open,
|
||||
chain_filter_close,
|
||||
chain_filter_filter,
|
||||
};
|
||||
|
||||
struct filter *
|
||||
Filter *
|
||||
filter_chain_new(void)
|
||||
{
|
||||
struct filter *filter = filter_new(&chain_filter_plugin, NULL, NULL);
|
||||
/* chain_filter_init() never fails */
|
||||
assert(filter != NULL);
|
||||
|
||||
return filter;
|
||||
return new ChainFilter();
|
||||
}
|
||||
|
||||
void
|
||||
filter_chain_append(struct filter *_chain, struct filter *filter)
|
||||
filter_chain_append(Filter &_chain, const char *name, Filter *filter)
|
||||
{
|
||||
ChainFilter *chain = (ChainFilter *)_chain;
|
||||
ChainFilter &chain = (ChainFilter &)_chain;
|
||||
|
||||
chain->children.push_back(filter);
|
||||
chain.Append(name, filter);
|
||||
}
|
||||
|
@@ -27,12 +27,12 @@
|
||||
#ifndef MPD_FILTER_CHAIN_HXX
|
||||
#define MPD_FILTER_CHAIN_HXX
|
||||
|
||||
struct filter;
|
||||
class Filter;
|
||||
|
||||
/**
|
||||
* Creates a new filter chain.
|
||||
*/
|
||||
struct filter *
|
||||
Filter *
|
||||
filter_chain_new(void);
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,6 @@ filter_chain_new(void);
|
||||
* @param filter the filter to be appended to #chain
|
||||
*/
|
||||
void
|
||||
filter_chain_append(struct filter *chain, struct filter *filter);
|
||||
filter_chain_append(Filter &chain, const char *name, Filter *filter);
|
||||
|
||||
#endif
|
||||
|
@@ -31,114 +31,89 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
struct ConvertFilter {
|
||||
struct filter base;
|
||||
|
||||
class ConvertFilter final : public Filter {
|
||||
/**
|
||||
* The input audio format; PCM data is passed to the filter()
|
||||
* method in this format.
|
||||
*/
|
||||
struct audio_format in_audio_format;
|
||||
audio_format in_audio_format;
|
||||
|
||||
/**
|
||||
* The output audio format; the consumer of this plugin
|
||||
* expects PCM data in this format. This defaults to
|
||||
* #in_audio_format, and can be set with convert_filter_set().
|
||||
*/
|
||||
struct audio_format out_audio_format;
|
||||
audio_format out_audio_format;
|
||||
|
||||
Manual<PcmConvert> state;
|
||||
|
||||
ConvertFilter() {
|
||||
filter_init(&base, &convert_filter_plugin);
|
||||
public:
|
||||
void Set(const audio_format &_out_audio_format) {
|
||||
assert(audio_format_valid(&in_audio_format));
|
||||
assert(audio_format_valid(&out_audio_format));
|
||||
assert(audio_format_valid(&_out_audio_format));
|
||||
|
||||
out_audio_format = _out_audio_format;
|
||||
}
|
||||
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
convert_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
ConvertFilter *filter = new ConvertFilter();
|
||||
return &filter->base;
|
||||
return new ConvertFilter();
|
||||
}
|
||||
|
||||
static void
|
||||
convert_filter_finish(struct filter *filter)
|
||||
const struct audio_format *
|
||||
ConvertFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
|
||||
{
|
||||
delete filter;
|
||||
assert(audio_format_valid(&audio_format));
|
||||
|
||||
in_audio_format = out_audio_format = audio_format;
|
||||
state.Construct();
|
||||
|
||||
return &in_audio_format;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
convert_filter_open(struct filter *_filter, struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
void
|
||||
ConvertFilter::Close()
|
||||
{
|
||||
ConvertFilter *filter = (ConvertFilter *)_filter;
|
||||
state.Destruct();
|
||||
|
||||
assert(audio_format_valid(audio_format));
|
||||
|
||||
filter->in_audio_format = filter->out_audio_format = *audio_format;
|
||||
filter->state.Construct();
|
||||
|
||||
return &filter->in_audio_format;
|
||||
poison_undefined(&in_audio_format, sizeof(in_audio_format));
|
||||
poison_undefined(&out_audio_format, sizeof(out_audio_format));
|
||||
}
|
||||
|
||||
static void
|
||||
convert_filter_close(struct filter *_filter)
|
||||
const void *
|
||||
ConvertFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
ConvertFilter *filter = (ConvertFilter *)_filter;
|
||||
|
||||
filter->state.Destruct();
|
||||
|
||||
poison_undefined(&filter->in_audio_format,
|
||||
sizeof(filter->in_audio_format));
|
||||
poison_undefined(&filter->out_audio_format,
|
||||
sizeof(filter->out_audio_format));
|
||||
}
|
||||
|
||||
static const void *
|
||||
convert_filter_filter(struct filter *_filter, const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
ConvertFilter *filter = (ConvertFilter *)_filter;
|
||||
const void *dest;
|
||||
|
||||
if (audio_format_equals(&filter->in_audio_format,
|
||||
&filter->out_audio_format)) {
|
||||
if (audio_format_equals(&in_audio_format, &out_audio_format)) {
|
||||
/* optimized special case: no-op */
|
||||
*dest_size_r = src_size;
|
||||
return src;
|
||||
}
|
||||
|
||||
dest = filter->state->Convert(&filter->in_audio_format,
|
||||
src, src_size,
|
||||
&filter->out_audio_format, dest_size_r,
|
||||
error_r);
|
||||
if (dest == NULL)
|
||||
return NULL;
|
||||
|
||||
return dest;
|
||||
return state->Convert(&in_audio_format,
|
||||
src, src_size,
|
||||
&out_audio_format, dest_size_r,
|
||||
error_r);
|
||||
}
|
||||
|
||||
const struct filter_plugin convert_filter_plugin = {
|
||||
"convert",
|
||||
convert_filter_init,
|
||||
convert_filter_finish,
|
||||
convert_filter_open,
|
||||
convert_filter_close,
|
||||
convert_filter_filter,
|
||||
};
|
||||
|
||||
void
|
||||
convert_filter_set(struct filter *_filter,
|
||||
const struct audio_format *out_audio_format)
|
||||
convert_filter_set(Filter *_filter, const audio_format &out_audio_format)
|
||||
{
|
||||
ConvertFilter *filter = (ConvertFilter *)_filter;
|
||||
|
||||
assert(filter != NULL);
|
||||
assert(audio_format_valid(&filter->in_audio_format));
|
||||
assert(audio_format_valid(&filter->out_audio_format));
|
||||
assert(out_audio_format != NULL);
|
||||
assert(audio_format_valid(out_audio_format));
|
||||
|
||||
filter->out_audio_format = *out_audio_format;
|
||||
filter->Set(out_audio_format);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
|
||||
#define MPD_CONVERT_FILTER_PLUGIN_HXX
|
||||
|
||||
struct filter;
|
||||
class Filter;
|
||||
struct audio_format;
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,6 @@ struct audio_format;
|
||||
* the last in a chain.
|
||||
*/
|
||||
void
|
||||
convert_filter_set(struct filter *filter,
|
||||
const audio_format *out_audio_format);
|
||||
convert_filter_set(Filter *filter, const audio_format &out_audio_format);
|
||||
|
||||
#endif
|
||||
|
@@ -28,68 +28,51 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
struct normalize_filter {
|
||||
struct filter filter;
|
||||
|
||||
class NormalizeFilter final : public Filter {
|
||||
struct Compressor *compressor;
|
||||
|
||||
struct pcm_buffer buffer;
|
||||
|
||||
public:
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
normalize_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct normalize_filter *filter = g_new(struct normalize_filter, 1);
|
||||
|
||||
filter_init(&filter->filter, &normalize_filter_plugin);
|
||||
|
||||
return &filter->filter;
|
||||
return new NormalizeFilter();
|
||||
}
|
||||
|
||||
static void
|
||||
normalize_filter_finish(struct filter *filter)
|
||||
const struct audio_format *
|
||||
NormalizeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
|
||||
{
|
||||
g_free(filter);
|
||||
audio_format.format = SAMPLE_FORMAT_S16;
|
||||
|
||||
compressor = Compressor_new(0);
|
||||
pcm_buffer_init(&buffer);
|
||||
|
||||
return &audio_format;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
normalize_filter_open(struct filter *_filter,
|
||||
struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
void
|
||||
NormalizeFilter::Close()
|
||||
{
|
||||
struct normalize_filter *filter = (struct normalize_filter *)_filter;
|
||||
|
||||
audio_format->format = SAMPLE_FORMAT_S16;
|
||||
|
||||
filter->compressor = Compressor_new(0);
|
||||
|
||||
pcm_buffer_init(&filter->buffer);
|
||||
|
||||
return audio_format;
|
||||
pcm_buffer_deinit(&buffer);
|
||||
Compressor_delete(compressor);
|
||||
}
|
||||
|
||||
static void
|
||||
normalize_filter_close(struct filter *_filter)
|
||||
const void *
|
||||
NormalizeFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, gcc_unused GError **error_r)
|
||||
{
|
||||
struct normalize_filter *filter = (struct normalize_filter *)_filter;
|
||||
|
||||
pcm_buffer_deinit(&filter->buffer);
|
||||
Compressor_delete(filter->compressor);
|
||||
}
|
||||
|
||||
static const void *
|
||||
normalize_filter_filter(struct filter *_filter,
|
||||
const void *src, size_t src_size, size_t *dest_size_r,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct normalize_filter *filter = (struct normalize_filter *)_filter;
|
||||
|
||||
int16_t *dest = (int16_t *)pcm_buffer_get(&filter->buffer, src_size);
|
||||
|
||||
int16_t *dest = (int16_t *)pcm_buffer_get(&buffer, src_size);
|
||||
memcpy(dest, src, src_size);
|
||||
|
||||
Compressor_Process_int16(filter->compressor, dest, src_size / 2);
|
||||
Compressor_Process_int16(compressor, dest, src_size / 2);
|
||||
|
||||
*dest_size_r = src_size;
|
||||
return dest;
|
||||
@@ -98,8 +81,4 @@ normalize_filter_filter(struct filter *_filter,
|
||||
const struct filter_plugin normalize_filter_plugin = {
|
||||
"normalize",
|
||||
normalize_filter_init,
|
||||
normalize_filter_finish,
|
||||
normalize_filter_open,
|
||||
normalize_filter_close,
|
||||
normalize_filter_filter,
|
||||
};
|
||||
|
@@ -30,65 +30,31 @@
|
||||
#include "FilterRegistry.hxx"
|
||||
#include "gcc.h"
|
||||
|
||||
#include <glib.h>
|
||||
class NullFilter final : public Filter {
|
||||
public:
|
||||
virtual const audio_format *Open(audio_format &af,
|
||||
gcc_unused GError **error_r) {
|
||||
return ⁡
|
||||
}
|
||||
|
||||
struct null_filter {
|
||||
struct filter filter;
|
||||
virtual void Close() {}
|
||||
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r,
|
||||
gcc_unused GError **error_r) {
|
||||
*dest_size_r = src_size;
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
null_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct null_filter *filter = g_new(struct null_filter, 1);
|
||||
|
||||
filter_init(&filter->filter, &null_filter_plugin);
|
||||
return &filter->filter;
|
||||
}
|
||||
|
||||
static void
|
||||
null_filter_finish(struct filter *_filter)
|
||||
{
|
||||
struct null_filter *filter = (struct null_filter *)_filter;
|
||||
|
||||
g_free(filter);
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
null_filter_open(struct filter *_filter, struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct null_filter *filter = (struct null_filter *)_filter;
|
||||
(void)filter;
|
||||
|
||||
return audio_format;
|
||||
}
|
||||
|
||||
static void
|
||||
null_filter_close(struct filter *_filter)
|
||||
{
|
||||
struct null_filter *filter = (struct null_filter *)_filter;
|
||||
(void)filter;
|
||||
}
|
||||
|
||||
static const void *
|
||||
null_filter_filter(struct filter *_filter,
|
||||
const void *src, size_t src_size,
|
||||
size_t *dest_size_r, gcc_unused GError **error_r)
|
||||
{
|
||||
struct null_filter *filter = (struct null_filter *)_filter;
|
||||
(void)filter;
|
||||
|
||||
/* return the unmodified source buffer */
|
||||
*dest_size_r = src_size;
|
||||
return src;
|
||||
return new NullFilter();
|
||||
}
|
||||
|
||||
const struct filter_plugin null_filter_plugin = {
|
||||
"null",
|
||||
null_filter_init,
|
||||
null_filter_finish,
|
||||
null_filter_open,
|
||||
null_filter_close,
|
||||
null_filter_filter,
|
||||
};
|
||||
|
@@ -38,9 +38,7 @@ extern "C" {
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "replay_gain"
|
||||
|
||||
struct replay_gain_filter {
|
||||
struct filter filter;
|
||||
|
||||
class ReplayGainFilter final : public Filter {
|
||||
/**
|
||||
* If set, then this hardware mixer is used for applying
|
||||
* replay gain, instead of the software volume library.
|
||||
@@ -71,9 +69,56 @@ struct replay_gain_filter {
|
||||
*/
|
||||
unsigned volume;
|
||||
|
||||
struct audio_format audio_format;
|
||||
struct audio_format format;
|
||||
|
||||
struct pcm_buffer buffer;
|
||||
|
||||
public:
|
||||
ReplayGainFilter()
|
||||
:mixer(nullptr), mode(REPLAY_GAIN_OFF),
|
||||
volume(PCM_VOLUME_1) {
|
||||
replay_gain_info_init(&info);
|
||||
}
|
||||
|
||||
void SetMixer(struct mixer *_mixer, unsigned _base) {
|
||||
assert(_mixer == NULL || (_base > 0 && _base <= 100));
|
||||
|
||||
mixer = _mixer;
|
||||
base = _base;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void SetInfo(const struct replay_gain_info *_info) {
|
||||
if (_info != NULL) {
|
||||
info = *_info;
|
||||
replay_gain_info_complete(&info);
|
||||
} else
|
||||
replay_gain_info_init(&info);
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void SetMode(enum replay_gain_mode _mode) {
|
||||
if (_mode == mode)
|
||||
/* no change */
|
||||
return;
|
||||
|
||||
g_debug("replay gain mode has changed %d->%d\n", mode, _mode);
|
||||
|
||||
mode = _mode;
|
||||
Update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the new volume after a property was changed.
|
||||
*/
|
||||
void Update();
|
||||
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
static inline GQuark
|
||||
@@ -82,30 +127,27 @@ replay_gain_quark(void)
|
||||
return g_quark_from_static_string("replay_gain");
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the new volume after a property was changed.
|
||||
*/
|
||||
static void
|
||||
replay_gain_filter_update(struct replay_gain_filter *filter)
|
||||
void
|
||||
ReplayGainFilter::Update()
|
||||
{
|
||||
if (filter->mode != REPLAY_GAIN_OFF) {
|
||||
float scale = replay_gain_tuple_scale(&filter->info.tuples[filter->mode],
|
||||
if (mode != REPLAY_GAIN_OFF) {
|
||||
float scale = replay_gain_tuple_scale(&info.tuples[mode],
|
||||
replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit);
|
||||
g_debug("scale=%f\n", (double)scale);
|
||||
|
||||
filter->volume = pcm_float_to_volume(scale);
|
||||
volume = pcm_float_to_volume(scale);
|
||||
} else
|
||||
filter->volume = PCM_VOLUME_1;
|
||||
volume = PCM_VOLUME_1;
|
||||
|
||||
if (filter->mixer != NULL) {
|
||||
if (mixer != NULL) {
|
||||
/* update the hardware mixer volume */
|
||||
|
||||
unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1;
|
||||
if (volume > 100)
|
||||
volume = 100;
|
||||
unsigned _volume = (volume * base) / PCM_VOLUME_1;
|
||||
if (_volume > 100)
|
||||
_volume = 100;
|
||||
|
||||
GError *error = NULL;
|
||||
if (!mixer_set_volume(filter->mixer, volume, &error)) {
|
||||
if (!mixer_set_volume(mixer, _volume, &error)) {
|
||||
g_warning("Failed to update hardware mixer: %s",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
@@ -113,70 +155,41 @@ replay_gain_filter_update(struct replay_gain_filter *filter)
|
||||
}
|
||||
}
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
replay_gain_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1);
|
||||
|
||||
filter_init(&filter->filter, &replay_gain_filter_plugin);
|
||||
filter->mixer = NULL;
|
||||
|
||||
filter->mode = REPLAY_GAIN_OFF;
|
||||
replay_gain_info_init(&filter->info);
|
||||
filter->volume = PCM_VOLUME_1;
|
||||
|
||||
return &filter->filter;
|
||||
return new ReplayGainFilter();
|
||||
}
|
||||
|
||||
static void
|
||||
replay_gain_filter_finish(struct filter *filter)
|
||||
const audio_format *
|
||||
ReplayGainFilter::Open(audio_format &af, gcc_unused GError **error_r)
|
||||
{
|
||||
g_free(filter);
|
||||
format = af;
|
||||
pcm_buffer_init(&buffer);
|
||||
|
||||
return &format;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
replay_gain_filter_open(struct filter *_filter,
|
||||
struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
void
|
||||
ReplayGainFilter::Close()
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
|
||||
filter->audio_format = *audio_format;
|
||||
pcm_buffer_init(&filter->buffer);
|
||||
|
||||
return &filter->audio_format;
|
||||
pcm_buffer_deinit(&buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
replay_gain_filter_close(struct filter *_filter)
|
||||
const void *
|
||||
ReplayGainFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
|
||||
pcm_buffer_deinit(&filter->buffer);
|
||||
}
|
||||
|
||||
static const void *
|
||||
replay_gain_filter_filter(struct filter *_filter,
|
||||
const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
bool success;
|
||||
void *dest;
|
||||
|
||||
*dest_size_r = src_size;
|
||||
|
||||
if (filter->volume == PCM_VOLUME_1)
|
||||
if (volume == PCM_VOLUME_1)
|
||||
/* optimized special case: 100% volume = no-op */
|
||||
return src;
|
||||
|
||||
dest = pcm_buffer_get(&filter->buffer, src_size);
|
||||
|
||||
if (filter->volume <= 0) {
|
||||
void *dest = pcm_buffer_get(&buffer, src_size);
|
||||
if (volume <= 0) {
|
||||
/* optimized special case: 0% volume = memset(0) */
|
||||
/* XXX is this valid for all sample formats? What
|
||||
about floating point? */
|
||||
@@ -186,9 +199,9 @@ replay_gain_filter_filter(struct filter *_filter,
|
||||
|
||||
memcpy(dest, src, src_size);
|
||||
|
||||
success = pcm_volume(dest, src_size,
|
||||
sample_format(filter->audio_format.format),
|
||||
filter->volume);
|
||||
bool success = pcm_volume(dest, src_size,
|
||||
sample_format(format.format),
|
||||
volume);
|
||||
if (!success) {
|
||||
g_set_error(error_r, replay_gain_quark(), 0,
|
||||
"pcm_volume() has failed");
|
||||
@@ -201,55 +214,29 @@ replay_gain_filter_filter(struct filter *_filter,
|
||||
const struct filter_plugin replay_gain_filter_plugin = {
|
||||
"replay_gain",
|
||||
replay_gain_filter_init,
|
||||
replay_gain_filter_finish,
|
||||
replay_gain_filter_open,
|
||||
replay_gain_filter_close,
|
||||
replay_gain_filter_filter,
|
||||
};
|
||||
|
||||
void
|
||||
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
|
||||
replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
|
||||
unsigned base)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
|
||||
|
||||
assert(mixer == NULL || (base > 0 && base <= 100));
|
||||
|
||||
filter->mixer = mixer;
|
||||
filter->base = base;
|
||||
|
||||
replay_gain_filter_update(filter);
|
||||
filter->SetMixer(mixer, base);
|
||||
}
|
||||
|
||||
void
|
||||
replay_gain_filter_set_info(struct filter *_filter,
|
||||
const struct replay_gain_info *info)
|
||||
replay_gain_filter_set_info(Filter *_filter, const replay_gain_info *info)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
|
||||
|
||||
if (info != NULL) {
|
||||
filter->info = *info;
|
||||
replay_gain_info_complete(&filter->info);
|
||||
} else
|
||||
replay_gain_info_init(&filter->info);
|
||||
|
||||
replay_gain_filter_update(filter);
|
||||
filter->SetInfo(info);
|
||||
}
|
||||
|
||||
void
|
||||
replay_gain_filter_set_mode(struct filter *_filter, enum replay_gain_mode mode)
|
||||
replay_gain_filter_set_mode(Filter *_filter, enum replay_gain_mode mode)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
|
||||
|
||||
if (mode == filter->mode)
|
||||
/* no change */
|
||||
return;
|
||||
|
||||
g_debug("replay gain mode has changed %d->%d\n", filter->mode, mode);
|
||||
|
||||
filter->mode = mode;
|
||||
replay_gain_filter_update(filter);
|
||||
filter->SetMode(mode);
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "replay_gain_info.h"
|
||||
|
||||
struct filter;
|
||||
class Filter;
|
||||
struct mixer;
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ struct mixer;
|
||||
* (including).
|
||||
*/
|
||||
void
|
||||
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
|
||||
replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
|
||||
unsigned base);
|
||||
|
||||
/**
|
||||
@@ -44,10 +44,9 @@ replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
|
||||
* gain data is available for the current song
|
||||
*/
|
||||
void
|
||||
replay_gain_filter_set_info(struct filter *filter,
|
||||
const struct replay_gain_info *info);
|
||||
replay_gain_filter_set_info(Filter *filter, const replay_gain_info *info);
|
||||
|
||||
void
|
||||
replay_gain_filter_set_mode(struct filter *filter, enum replay_gain_mode mode);
|
||||
replay_gain_filter_set_mode(Filter *filter, enum replay_gain_mode mode);
|
||||
|
||||
#endif
|
||||
|
@@ -53,14 +53,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
struct route_filter {
|
||||
|
||||
/**
|
||||
* Inherit (and support cast to/from) filter
|
||||
*/
|
||||
struct filter base;
|
||||
|
||||
class RouteFilter final : public Filter {
|
||||
/**
|
||||
* The minimum number of channels we need for output
|
||||
* to be able to perform all the copies the user has specified
|
||||
@@ -110,21 +103,31 @@ struct route_filter {
|
||||
*/
|
||||
struct pcm_buffer output_buffer;
|
||||
|
||||
public:
|
||||
RouteFilter():sources(nullptr) {}
|
||||
~RouteFilter() {
|
||||
g_free(sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the "routes" section, a string on the form
|
||||
* a>b, c>d, e>f, ...
|
||||
* where a... are non-unique, non-negative integers
|
||||
* and input channel a gets copied to output channel b, etc.
|
||||
* @param param the configuration block to read
|
||||
* @param filter a route_filter whose min_channels and sources[] to set
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool Configure(const config_param *param, GError **error_r);
|
||||
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the "routes" section, a string on the form
|
||||
* a>b, c>d, e>f, ...
|
||||
* where a... are non-unique, non-negative integers
|
||||
* and input channel a gets copied to output channel b, etc.
|
||||
* @param param the configuration block to read
|
||||
* @param filter a route_filter whose min_channels and sources[] to set
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
static bool
|
||||
route_filter_parse(const struct config_param *param,
|
||||
struct route_filter *filter,
|
||||
GError **error_r) {
|
||||
bool
|
||||
RouteFilter::Configure(const config_param *param, GError **error_r) {
|
||||
|
||||
/* TODO:
|
||||
* With a more clever way of marking "don't copy to output N",
|
||||
@@ -139,8 +142,8 @@ route_filter_parse(const struct config_param *param,
|
||||
const char *routes =
|
||||
config_get_block_string(param, "routes", "0>0, 1>1");
|
||||
|
||||
filter->min_input_channels = 0;
|
||||
filter->min_output_channels = 0;
|
||||
min_input_channels = 0;
|
||||
min_output_channels = 0;
|
||||
|
||||
tokens = g_strsplit(routes, ",", 255);
|
||||
number_of_copies = g_strv_length(tokens);
|
||||
@@ -171,28 +174,28 @@ route_filter_parse(const struct config_param *param,
|
||||
|
||||
// Keep track of the highest channel numbers seen
|
||||
// as either in- or outputs
|
||||
if (source >= filter->min_input_channels)
|
||||
filter->min_input_channels = source + 1;
|
||||
if (dest >= filter->min_output_channels)
|
||||
filter->min_output_channels = dest + 1;
|
||||
if (source >= min_input_channels)
|
||||
min_input_channels = source + 1;
|
||||
if (dest >= min_output_channels)
|
||||
min_output_channels = dest + 1;
|
||||
|
||||
g_strfreev(sd);
|
||||
}
|
||||
|
||||
if (!audio_valid_channel_count(filter->min_output_channels)) {
|
||||
if (!audio_valid_channel_count(min_output_channels)) {
|
||||
g_strfreev(tokens);
|
||||
g_set_error(error_r, audio_format_quark(), 0,
|
||||
"Invalid number of output channels requested: %d",
|
||||
filter->min_output_channels);
|
||||
min_output_channels);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate a map of "copy nothing to me"
|
||||
filter->sources = (signed char *)
|
||||
g_malloc(filter->min_output_channels * sizeof(signed char));
|
||||
sources = (signed char *)
|
||||
g_malloc(min_output_channels * sizeof(signed char));
|
||||
|
||||
for (int i=0; i<filter->min_output_channels; ++i)
|
||||
filter->sources[i] = -1;
|
||||
for (int i=0; i<min_output_channels; ++i)
|
||||
sources[i] = -1;
|
||||
|
||||
// Run through the spec again, and save the
|
||||
// actual mapping output <- input
|
||||
@@ -216,7 +219,7 @@ route_filter_parse(const struct config_param *param,
|
||||
source = strtol(sd[0], NULL, 10);
|
||||
dest = strtol(sd[1], NULL, 10);
|
||||
|
||||
filter->sources[dest] = source;
|
||||
sources[dest] = source;
|
||||
|
||||
g_strfreev(sd);
|
||||
}
|
||||
@@ -226,73 +229,53 @@ route_filter_parse(const struct config_param *param,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct filter *
|
||||
route_filter_init(const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
static Filter *
|
||||
route_filter_init(const config_param *param, GError **error_r)
|
||||
{
|
||||
struct route_filter *filter = g_new(struct route_filter, 1);
|
||||
filter_init(&filter->base, &route_filter_plugin);
|
||||
RouteFilter *filter = new RouteFilter();
|
||||
if (!filter->Configure(param, error_r)) {
|
||||
delete filter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate and set the filter->sources[] array
|
||||
route_filter_parse(param, filter, error_r);
|
||||
|
||||
return &filter->base;
|
||||
return filter;
|
||||
}
|
||||
|
||||
static void
|
||||
route_filter_finish(struct filter *_filter)
|
||||
const struct audio_format *
|
||||
RouteFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
|
||||
{
|
||||
struct route_filter *filter = (struct route_filter *)_filter;
|
||||
|
||||
g_free(filter->sources);
|
||||
g_free(filter);
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
route_filter_open(struct filter *_filter, struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct route_filter *filter = (struct route_filter *)_filter;
|
||||
|
||||
// Copy the input format for later reference
|
||||
filter->input_format = *audio_format;
|
||||
filter->input_frame_size =
|
||||
audio_format_frame_size(&filter->input_format);
|
||||
input_format = audio_format;
|
||||
input_frame_size = audio_format_frame_size(&input_format);
|
||||
|
||||
// Decide on an output format which has enough channels,
|
||||
// and is otherwise identical
|
||||
filter->output_format = *audio_format;
|
||||
filter->output_format.channels = filter->min_output_channels;
|
||||
output_format = audio_format;
|
||||
output_format.channels = min_output_channels;
|
||||
|
||||
// Precalculate this simple value, to speed up allocation later
|
||||
filter->output_frame_size =
|
||||
audio_format_frame_size(&filter->output_format);
|
||||
output_frame_size = audio_format_frame_size(&output_format);
|
||||
|
||||
// This buffer grows as needed
|
||||
pcm_buffer_init(&filter->output_buffer);
|
||||
pcm_buffer_init(&output_buffer);
|
||||
|
||||
return &filter->output_format;
|
||||
return &output_format;
|
||||
}
|
||||
|
||||
static void
|
||||
route_filter_close(struct filter *_filter)
|
||||
void
|
||||
RouteFilter::Close()
|
||||
{
|
||||
struct route_filter *filter = (struct route_filter *)_filter;
|
||||
|
||||
pcm_buffer_deinit(&filter->output_buffer);
|
||||
pcm_buffer_deinit(&output_buffer);
|
||||
}
|
||||
|
||||
static const void *
|
||||
route_filter_filter(struct filter *_filter,
|
||||
const void *src, size_t src_size,
|
||||
size_t *dest_size_r, gcc_unused GError **error_r)
|
||||
const void *
|
||||
RouteFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, gcc_unused GError **error_r)
|
||||
{
|
||||
struct route_filter *filter = (struct route_filter *)_filter;
|
||||
|
||||
size_t number_of_frames = src_size / filter->input_frame_size;
|
||||
size_t number_of_frames = src_size / input_frame_size;
|
||||
|
||||
size_t bytes_per_frame_per_channel =
|
||||
audio_format_sample_size(&filter->input_format);
|
||||
audio_format_sample_size(&input_format);
|
||||
|
||||
// A moving pointer that always refers to channel 0 in the input, at the currently handled frame
|
||||
const uint8_t *base_source = (const uint8_t *)src;
|
||||
@@ -301,18 +284,18 @@ route_filter_filter(struct filter *_filter,
|
||||
uint8_t *chan_destination;
|
||||
|
||||
// Grow our reusable buffer, if needed, and set the moving pointer
|
||||
*dest_size_r = number_of_frames * filter->output_frame_size;
|
||||
*dest_size_r = number_of_frames * output_frame_size;
|
||||
chan_destination = (uint8_t *)
|
||||
pcm_buffer_get(&filter->output_buffer, *dest_size_r);
|
||||
pcm_buffer_get(&output_buffer, *dest_size_r);
|
||||
|
||||
|
||||
// Perform our copy operations, with N input channels and M output channels
|
||||
for (unsigned int s=0; s<number_of_frames; ++s) {
|
||||
|
||||
// Need to perform one copy per output channel
|
||||
for (unsigned int c=0; c<filter->min_output_channels; ++c) {
|
||||
if (filter->sources[c] == -1 ||
|
||||
(unsigned)filter->sources[c] >= filter->input_format.channels) {
|
||||
for (unsigned int c=0; c<min_output_channels; ++c) {
|
||||
if (sources[c] == -1 ||
|
||||
(unsigned)sources[c] >= input_format.channels) {
|
||||
// No source for this destination output,
|
||||
// give it zeroes as input
|
||||
memset(chan_destination,
|
||||
@@ -322,7 +305,7 @@ route_filter_filter(struct filter *_filter,
|
||||
// Get the data from channel sources[c]
|
||||
// and copy it to the output
|
||||
const uint8_t *data = base_source +
|
||||
(filter->sources[c] * bytes_per_frame_per_channel);
|
||||
(sources[c] * bytes_per_frame_per_channel);
|
||||
memcpy(chan_destination,
|
||||
data,
|
||||
bytes_per_frame_per_channel);
|
||||
@@ -333,18 +316,14 @@ route_filter_filter(struct filter *_filter,
|
||||
|
||||
|
||||
// Go on to the next N input samples
|
||||
base_source += filter->input_frame_size;
|
||||
base_source += input_frame_size;
|
||||
}
|
||||
|
||||
// Here it is, ladies and gentlemen! Rerouted data!
|
||||
return (void *) filter->output_buffer.buffer;
|
||||
return (void *) output_buffer.buffer;
|
||||
}
|
||||
|
||||
const struct filter_plugin route_filter_plugin = {
|
||||
"route",
|
||||
route_filter_init,
|
||||
route_filter_finish,
|
||||
route_filter_open,
|
||||
route_filter_close,
|
||||
route_filter_filter,
|
||||
};
|
||||
|
@@ -30,17 +30,36 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
struct volume_filter {
|
||||
struct filter filter;
|
||||
|
||||
class VolumeFilter final : public Filter {
|
||||
/**
|
||||
* The current volume, from 0 to #PCM_VOLUME_1.
|
||||
*/
|
||||
unsigned volume;
|
||||
|
||||
struct audio_format audio_format;
|
||||
struct audio_format format;
|
||||
|
||||
struct pcm_buffer buffer;
|
||||
|
||||
public:
|
||||
VolumeFilter()
|
||||
:volume(PCM_VOLUME_1) {}
|
||||
|
||||
unsigned GetVolume() const {
|
||||
assert(volume <= PCM_VOLUME_1);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
void SetVolume(unsigned _volume) {
|
||||
assert(_volume <= PCM_VOLUME_1);
|
||||
|
||||
volume = _volume;
|
||||
}
|
||||
|
||||
virtual const audio_format *Open(audio_format &af, GError **error_r);
|
||||
virtual void Close();
|
||||
virtual const void *FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r);
|
||||
};
|
||||
|
||||
static inline GQuark
|
||||
@@ -49,61 +68,41 @@ volume_quark(void)
|
||||
return g_quark_from_static_string("pcm_volume");
|
||||
}
|
||||
|
||||
static struct filter *
|
||||
static Filter *
|
||||
volume_filter_init(gcc_unused const struct config_param *param,
|
||||
gcc_unused GError **error_r)
|
||||
{
|
||||
struct volume_filter *filter = g_new(struct volume_filter, 1);
|
||||
|
||||
filter_init(&filter->filter, &volume_filter_plugin);
|
||||
filter->volume = PCM_VOLUME_1;
|
||||
|
||||
return &filter->filter;
|
||||
return new VolumeFilter();
|
||||
}
|
||||
|
||||
static void
|
||||
volume_filter_finish(struct filter *filter)
|
||||
const struct audio_format *
|
||||
VolumeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
|
||||
{
|
||||
g_free(filter);
|
||||
format = audio_format;
|
||||
pcm_buffer_init(&buffer);
|
||||
|
||||
return &format;
|
||||
}
|
||||
|
||||
static const struct audio_format *
|
||||
volume_filter_open(struct filter *_filter, struct audio_format *audio_format,
|
||||
gcc_unused GError **error_r)
|
||||
void
|
||||
VolumeFilter::Close()
|
||||
{
|
||||
struct volume_filter *filter = (struct volume_filter *)_filter;
|
||||
|
||||
filter->audio_format = *audio_format;
|
||||
pcm_buffer_init(&filter->buffer);
|
||||
|
||||
return &filter->audio_format;
|
||||
pcm_buffer_deinit(&buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
volume_filter_close(struct filter *_filter)
|
||||
const void *
|
||||
VolumeFilter::FilterPCM(const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
struct volume_filter *filter = (struct volume_filter *)_filter;
|
||||
|
||||
pcm_buffer_deinit(&filter->buffer);
|
||||
}
|
||||
|
||||
static const void *
|
||||
volume_filter_filter(struct filter *_filter, const void *src, size_t src_size,
|
||||
size_t *dest_size_r, GError **error_r)
|
||||
{
|
||||
struct volume_filter *filter = (struct volume_filter *)_filter;
|
||||
bool success;
|
||||
void *dest;
|
||||
|
||||
*dest_size_r = src_size;
|
||||
|
||||
if (filter->volume >= PCM_VOLUME_1)
|
||||
if (volume >= PCM_VOLUME_1)
|
||||
/* optimized special case: 100% volume = no-op */
|
||||
return src;
|
||||
|
||||
dest = pcm_buffer_get(&filter->buffer, src_size);
|
||||
void *dest = pcm_buffer_get(&buffer, src_size);
|
||||
|
||||
if (filter->volume <= 0) {
|
||||
if (volume <= 0) {
|
||||
/* optimized special case: 0% volume = memset(0) */
|
||||
/* XXX is this valid for all sample formats? What
|
||||
about floating point? */
|
||||
@@ -113,9 +112,9 @@ volume_filter_filter(struct filter *_filter, const void *src, size_t src_size,
|
||||
|
||||
memcpy(dest, src, src_size);
|
||||
|
||||
success = pcm_volume(dest, src_size,
|
||||
sample_format(filter->audio_format.format),
|
||||
filter->volume);
|
||||
bool success = pcm_volume(dest, src_size,
|
||||
sample_format(format.format),
|
||||
volume);
|
||||
if (!success) {
|
||||
g_set_error(error_r, volume_quark(), 0,
|
||||
"pcm_volume() has failed");
|
||||
@@ -128,32 +127,22 @@ volume_filter_filter(struct filter *_filter, const void *src, size_t src_size,
|
||||
const struct filter_plugin volume_filter_plugin = {
|
||||
"volume",
|
||||
volume_filter_init,
|
||||
volume_filter_finish,
|
||||
volume_filter_open,
|
||||
volume_filter_close,
|
||||
volume_filter_filter,
|
||||
};
|
||||
|
||||
unsigned
|
||||
volume_filter_get(const struct filter *_filter)
|
||||
volume_filter_get(const Filter *_filter)
|
||||
{
|
||||
const struct volume_filter *filter =
|
||||
(const struct volume_filter *)_filter;
|
||||
const VolumeFilter *filter =
|
||||
(const VolumeFilter *)_filter;
|
||||
|
||||
assert(filter->filter.plugin == &volume_filter_plugin);
|
||||
assert(filter->volume <= PCM_VOLUME_1);
|
||||
|
||||
return filter->volume;
|
||||
return filter->GetVolume();
|
||||
}
|
||||
|
||||
void
|
||||
volume_filter_set(struct filter *_filter, unsigned volume)
|
||||
volume_filter_set(Filter *_filter, unsigned volume)
|
||||
{
|
||||
struct volume_filter *filter = (struct volume_filter *)_filter;
|
||||
VolumeFilter *filter = (VolumeFilter *)_filter;
|
||||
|
||||
assert(filter->filter.plugin == &volume_filter_plugin);
|
||||
assert(volume <= PCM_VOLUME_1);
|
||||
|
||||
filter->volume = volume;
|
||||
filter->SetVolume(volume);
|
||||
}
|
||||
|
||||
|
@@ -20,12 +20,12 @@
|
||||
#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
|
||||
#define MPD_VOLUME_FILTER_PLUGIN_HXX
|
||||
|
||||
struct filter;
|
||||
class Filter;
|
||||
|
||||
unsigned
|
||||
volume_filter_get(const struct filter *filter);
|
||||
volume_filter_get(const Filter *filter);
|
||||
|
||||
void
|
||||
volume_filter_set(struct filter *filter, unsigned volume);
|
||||
volume_filter_set(Filter *filter, unsigned volume);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user