ChainFilterPlugin.cxx 4.15 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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.
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21
#include "ChainFilterPlugin.hxx"
22 23 24
#include "filter/FilterPlugin.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
25
#include "AudioFormat.hxx"
26 27
#include "util/Error.hxx"
#include "util/Domain.hxx"
28
#include "util/ConstBuffer.hxx"
29

30 31
#include <list>

32 33
#include <assert.h>

34 35 36 37 38 39 40 41 42 43
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;
		}
44

45 46 47
		Child(const Child &) = delete;
		Child &operator=(const Child &) = delete;
	};
Max Kellermann's avatar
Max Kellermann committed
48

49
	std::list<Child> children;
50

51 52 53
public:
	void Append(const char *name, Filter *filter) {
		children.emplace_back(name, filter);
54
	}
55

56 57 58
	/* virtual methods from class Filter */
	AudioFormat Open(AudioFormat &af, Error &error) override;
	void Close() override;
59 60
	ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
					    Error &error) override;
61 62 63 64 65 66 67

private:
	/**
	 * Close all filters in the chain until #until is reached.
	 * #until itself is not closed.
	 */
	void CloseUntil(const Filter *until);
68 69
};

70
static constexpr Domain chain_filter_domain("chain_filter");
71

72
static Filter *
73
chain_filter_init(gcc_unused const ConfigBlock &block,
74
		  gcc_unused Error &error)
75
{
76
	return new ChainFilter();
77 78
}

79 80
void
ChainFilter::CloseUntil(const Filter *until)
81
{
82 83
	for (auto &child : children) {
		if (child.filter == until)
84
			/* don't close this filter */
85
			return;
86 87

		/* close this filter */
88
		child.filter->Close();
89
	}
90 91 92

	/* this assertion fails if #until does not exist (anymore) */
	assert(false);
93
	gcc_unreachable();
94 95
}

96
static AudioFormat
97
chain_open_child(const char *name, Filter *filter,
98
		 const AudioFormat &prev_audio_format,
99
		 Error &error)
100
{
101 102
	AudioFormat conv_audio_format = prev_audio_format;
	const AudioFormat next_audio_format =
103
		filter->Open(conv_audio_format, error);
104 105
	if (!next_audio_format.IsDefined())
		return next_audio_format;
106

107
	if (conv_audio_format != prev_audio_format) {
108 109
		struct audio_format_string s;

110
		filter->Close();
111 112 113 114 115

		error.Format(chain_filter_domain,
			     "Audio format not supported by filter '%s': %s",
			     name,
			     audio_format_to_string(prev_audio_format, &s));
116
		return AudioFormat::Undefined();
117 118 119 120 121
	}

	return next_audio_format;
}

122
AudioFormat
123
ChainFilter::Open(AudioFormat &in_audio_format, Error &error)
124
{
125
	AudioFormat audio_format = in_audio_format;
126

127 128
	for (auto &child : children) {
		audio_format = chain_open_child(child.name, child.filter,
129
						audio_format, error);
130
		if (!audio_format.IsDefined()) {
131
			/* rollback, close all children */
132
			CloseUntil(child.filter);
133
			break;
134 135 136 137 138 139 140
		}
	}

	/* return the output format of the last filter */
	return audio_format;
}

141 142
void
ChainFilter::Close()
143
{
144 145
	for (auto &child : children)
		child.filter->Close();
146 147
}

148 149
ConstBuffer<void>
ChainFilter::FilterPCM(ConstBuffer<void> src, Error &error)
150
{
151
	for (auto &child : children) {
152 153
		/* feed the output of the previous filter as input
		   into the current one */
154 155
		src = child.filter->FilterPCM(src, error);
		if (src.IsNull())
156
			return nullptr;
157 158 159 160 161 162 163
	}

	/* return the output of the last filter */
	return src;
}

const struct filter_plugin chain_filter_plugin = {
Max Kellermann's avatar
Max Kellermann committed
164 165
	"chain",
	chain_filter_init,
166 167
};

168
Filter *
169 170
filter_chain_new(void)
{
171
	return new ChainFilter();
172 173 174
}

void
175
filter_chain_append(Filter &_chain, const char *name, Filter *filter)
176
{
177
	ChainFilter &chain = (ChainFilter &)_chain;
178

179
	chain.Append(name, filter);
180
}