2
* Copyright (C) 2003-2013 The Music Player Daemon Project
3
* http://www.musicpd.org
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
* This filter copies audio data between channels. Useful for
23
* upmixing mono/stereo audio to surround speaker configurations.
25
* Its configuration consists of a "filter" section with a single
26
* "routes" entry, formatted as: \\
27
* routes "0>1, 1>0, 2>2, 3>3, 3>4" \\
28
* where each pair of numbers signifies a set of channels.
29
* Each source>dest pair leads to the data from channel #source
30
* being copied to channel #dest in the output.
33
* routes "0>0, 1>1, 0>2, 1>3"\\
34
* upmixes stereo audio to a 4-speaker system, copying the front-left
35
* (0) to front left (0) and rear left (2), copying front-right (1) to
36
* front-right (1) and rear-right (3).
38
* If multiple sources are copied to the same destination channel, only
39
* one of them takes effect.
43
#include "ConfigError.hxx"
44
#include "ConfigData.hxx"
45
#include "AudioFormat.hxx"
46
#include "CheckAudioFormat.hxx"
47
#include "FilterPlugin.hxx"
48
#include "FilterInternal.hxx"
49
#include "FilterRegistry.hxx"
50
#include "pcm/PcmBuffer.hxx"
51
#include "util/StringUtil.hxx"
52
#include "util/Error.hxx"
60
class RouteFilter final : public Filter {
62
* The minimum number of channels we need for output
63
* to be able to perform all the copies the user has specified
65
unsigned min_output_channels;
68
* The minimum number of input channels we need to
69
* copy all the data the user has requested. If fewer
70
* than this many are supplied by the input, undefined
71
* copy operations are given zeroed sources in stead.
73
unsigned min_input_channels;
76
* The set of copy operations to perform on each sample
77
* The index is an output channel to use, the value is
78
* a corresponding input channel from which to take the
79
* data. A -1 means "no source"
81
int8_t sources[MAX_CHANNELS];
84
* The actual input format of our signal, once opened
86
AudioFormat input_format;
89
* The decided upon output format, once opened
91
AudioFormat output_format;
94
* The size, in bytes, of each multichannel frame in the
97
size_t input_frame_size;
100
* The size, in bytes, of each multichannel frame in the
103
size_t output_frame_size;
106
* The output buffer used last time around, can be reused if the size doesn't differ.
108
PcmBuffer output_buffer;
112
* Parse the "routes" section, a string on the form
114
* where a... are non-unique, non-negative integers
115
* and input channel a gets copied to output channel b, etc.
116
* @param param the configuration block to read
117
* @param filter a route_filter whose min_channels and sources[] to set
118
* @return true on success, false on error
120
bool Configure(const config_param ¶m, Error &error);
122
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
123
virtual void Close();
124
virtual const void *FilterPCM(const void *src, size_t src_size,
125
size_t *dest_size_r, Error &error);
129
RouteFilter::Configure(const config_param ¶m, Error &error) {
132
* With a more clever way of marking "don't copy to output N",
133
* This could easily be merged into a single loop with some
134
* dynamic realloc() instead of one count run and one malloc().
137
std::fill_n(sources, MAX_CHANNELS, -1);
139
min_input_channels = 0;
140
min_output_channels = 0;
142
// A cowardly default, just passthrough stereo
143
const char *routes = param.GetBlockValue("routes", "0>0, 1>1");
145
routes = strchug_fast(routes);
148
const unsigned source = strtoul(routes, &endptr, 10);
149
endptr = strchug_fast(endptr);
150
if (endptr == routes || *endptr != '>') {
151
error.Set(config_domain,
152
"Malformed 'routes' specification");
156
if (source >= MAX_CHANNELS) {
157
error.Format(config_domain,
158
"Invalid source channel number: %u",
163
if (source >= min_input_channels)
164
min_input_channels = source + 1;
166
routes = strchug_fast(endptr + 1);
168
unsigned dest = strtoul(routes, &endptr, 10);
169
endptr = strchug_fast(endptr);
170
if (endptr == routes) {
171
error.Set(config_domain,
172
"Malformed 'routes' specification");
176
if (dest >= MAX_CHANNELS) {
177
error.Format(config_domain,
178
"Invalid destination channel number: %u",
183
if (dest >= min_output_channels)
184
min_output_channels = dest + 1;
186
sources[dest] = source;
193
if (*routes != ',') {
194
error.Set(config_domain,
195
"Malformed 'routes' specification");
206
route_filter_init(const config_param ¶m, Error &error)
208
RouteFilter *filter = new RouteFilter();
209
if (!filter->Configure(param, error)) {
218
RouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
220
// Copy the input format for later reference
221
input_format = audio_format;
222
input_frame_size = input_format.GetFrameSize();
224
// Decide on an output format which has enough channels,
225
// and is otherwise identical
226
output_format = audio_format;
227
output_format.channels = min_output_channels;
229
// Precalculate this simple value, to speed up allocation later
230
output_frame_size = output_format.GetFrameSize();
232
return output_format;
238
output_buffer.Clear();
242
RouteFilter::FilterPCM(const void *src, size_t src_size,
243
size_t *dest_size_r, gcc_unused Error &error)
245
size_t number_of_frames = src_size / input_frame_size;
247
const size_t bytes_per_frame_per_channel = input_format.GetSampleSize();
249
// A moving pointer that always refers to channel 0 in the input, at the currently handled frame
250
const uint8_t *base_source = (const uint8_t *)src;
252
// Grow our reusable buffer, if needed, and set the moving pointer
253
*dest_size_r = number_of_frames * output_frame_size;
254
void *const result = output_buffer.Get(*dest_size_r);
256
// A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output
257
uint8_t *chan_destination = (uint8_t *)result;
259
// Perform our copy operations, with N input channels and M output channels
260
for (unsigned int s=0; s<number_of_frames; ++s) {
262
// Need to perform one copy per output channel
263
for (unsigned int c=0; c<min_output_channels; ++c) {
264
if (sources[c] == -1 ||
265
(unsigned)sources[c] >= input_format.channels) {
266
// No source for this destination output,
267
// give it zeroes as input
268
memset(chan_destination,
270
bytes_per_frame_per_channel);
272
// Get the data from channel sources[c]
273
// and copy it to the output
274
const uint8_t *data = base_source +
275
(sources[c] * bytes_per_frame_per_channel);
276
memcpy(chan_destination,
278
bytes_per_frame_per_channel);
280
// Move on to the next output channel
281
chan_destination += bytes_per_frame_per_channel;
285
// Go on to the next N input samples
286
base_source += input_frame_size;
289
// Here it is, ladies and gentlemen! Rerouted data!
293
const struct filter_plugin route_filter_plugin = {