~ubuntu-branches/ubuntu/utopic/mpd/utopic-proposed

« back to all changes in this revision

Viewing changes to src/filter/RouteFilterPlugin.cxx

  • Committer: Package Import Robot
  • Author(s): Steve Kowalik
  • Date: 2013-11-12 18:17:40 UTC
  • mfrom: (2.2.36 sid)
  • Revision ID: package-import@ubuntu.com-20131112181740-72aa4zihehoobedp
Tags: 0.18.3-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Add libmp3lame-dev to Build-Depends, and enable LAME.
  - Read the user for the daemon from the config file in the init script.
  - Move avahi-daemon from Suggests to Recommends.
  - Added apport hook to include user configuration file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
 
3
 * http://www.musicpd.org
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
18
 */
 
19
 
 
20
/** \file
 
21
 *
 
22
 * This filter copies audio data between channels. Useful for
 
23
 * upmixing mono/stereo audio to surround speaker configurations.
 
24
 *
 
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.
 
31
 *
 
32
 * Example: \\
 
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).
 
37
 *
 
38
 * If multiple sources are copied to the same destination channel, only
 
39
 * one of them takes effect.
 
40
 */
 
41
 
 
42
#include "config.h"
 
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"
 
53
 
 
54
#include <algorithm>
 
55
 
 
56
#include <assert.h>
 
57
#include <string.h>
 
58
#include <stdint.h>
 
59
 
 
60
class RouteFilter final : public Filter {
 
61
        /**
 
62
         * The minimum number of channels we need for output
 
63
         * to be able to perform all the copies the user has specified
 
64
         */
 
65
        unsigned min_output_channels;
 
66
 
 
67
        /**
 
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.
 
72
         */
 
73
        unsigned min_input_channels;
 
74
 
 
75
        /**
 
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"
 
80
         */
 
81
        int8_t sources[MAX_CHANNELS];
 
82
 
 
83
        /**
 
84
         * The actual input format of our signal, once opened
 
85
         */
 
86
        AudioFormat input_format;
 
87
 
 
88
        /**
 
89
         * The decided upon output format, once opened
 
90
         */
 
91
        AudioFormat output_format;
 
92
 
 
93
        /**
 
94
         * The size, in bytes, of each multichannel frame in the
 
95
         * input buffer
 
96
         */
 
97
        size_t input_frame_size;
 
98
 
 
99
        /**
 
100
         * The size, in bytes, of each multichannel frame in the
 
101
         * output buffer
 
102
         */
 
103
        size_t output_frame_size;
 
104
 
 
105
        /**
 
106
         * The output buffer used last time around, can be reused if the size doesn't differ.
 
107
         */
 
108
        PcmBuffer output_buffer;
 
109
 
 
110
public:
 
111
        /**
 
112
         * Parse the "routes" section, a string on the form
 
113
         *  a>b, c>d, e>f, ...
 
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
 
119
         */
 
120
        bool Configure(const config_param &param, Error &error);
 
121
 
 
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);
 
126
};
 
127
 
 
128
bool
 
129
RouteFilter::Configure(const config_param &param, Error &error) {
 
130
 
 
131
        /* TODO:
 
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().
 
135
         */
 
136
 
 
137
        std::fill_n(sources, MAX_CHANNELS, -1);
 
138
 
 
139
        min_input_channels = 0;
 
140
        min_output_channels = 0;
 
141
 
 
142
        // A cowardly default, just passthrough stereo
 
143
        const char *routes = param.GetBlockValue("routes", "0>0, 1>1");
 
144
        while (true) {
 
145
                routes = strchug_fast(routes);
 
146
 
 
147
                char *endptr;
 
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");
 
153
                        return false;
 
154
                }
 
155
 
 
156
                if (source >= MAX_CHANNELS) {
 
157
                        error.Format(config_domain,
 
158
                                     "Invalid source channel number: %u",
 
159
                                     source);
 
160
                        return false;
 
161
                }
 
162
 
 
163
                if (source >= min_input_channels)
 
164
                        min_input_channels = source + 1;
 
165
 
 
166
                routes = strchug_fast(endptr + 1);
 
167
 
 
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");
 
173
                        return false;
 
174
                }
 
175
 
 
176
                if (dest >= MAX_CHANNELS) {
 
177
                        error.Format(config_domain,
 
178
                                     "Invalid destination channel number: %u",
 
179
                                     dest);
 
180
                        return false;
 
181
                }
 
182
 
 
183
                if (dest >= min_output_channels)
 
184
                        min_output_channels = dest + 1;
 
185
 
 
186
                sources[dest] = source;
 
187
 
 
188
                routes = endptr;
 
189
 
 
190
                if (*routes == 0)
 
191
                        break;
 
192
 
 
193
                if (*routes != ',') {
 
194
                        error.Set(config_domain,
 
195
                                  "Malformed 'routes' specification");
 
196
                        return false;
 
197
                }
 
198
 
 
199
                ++routes;
 
200
        }
 
201
 
 
202
        return true;
 
203
}
 
204
 
 
205
static Filter *
 
206
route_filter_init(const config_param &param, Error &error)
 
207
{
 
208
        RouteFilter *filter = new RouteFilter();
 
209
        if (!filter->Configure(param, error)) {
 
210
                delete filter;
 
211
                return nullptr;
 
212
        }
 
213
 
 
214
        return filter;
 
215
}
 
216
 
 
217
AudioFormat
 
218
RouteFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
 
219
{
 
220
        // Copy the input format for later reference
 
221
        input_format = audio_format;
 
222
        input_frame_size = input_format.GetFrameSize();
 
223
 
 
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;
 
228
 
 
229
        // Precalculate this simple value, to speed up allocation later
 
230
        output_frame_size = output_format.GetFrameSize();
 
231
 
 
232
        return output_format;
 
233
}
 
234
 
 
235
void
 
236
RouteFilter::Close()
 
237
{
 
238
        output_buffer.Clear();
 
239
}
 
240
 
 
241
const void *
 
242
RouteFilter::FilterPCM(const void *src, size_t src_size,
 
243
                       size_t *dest_size_r, gcc_unused Error &error)
 
244
{
 
245
        size_t number_of_frames = src_size / input_frame_size;
 
246
 
 
247
        const size_t bytes_per_frame_per_channel = input_format.GetSampleSize();
 
248
 
 
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;
 
251
 
 
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);
 
255
 
 
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;
 
258
 
 
259
        // Perform our copy operations, with N input channels and M output channels
 
260
        for (unsigned int s=0; s<number_of_frames; ++s) {
 
261
 
 
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,
 
269
                                        0x00,
 
270
                                        bytes_per_frame_per_channel);
 
271
                        } else {
 
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,
 
277
                                          data,
 
278
                                          bytes_per_frame_per_channel);
 
279
                        }
 
280
                        // Move on to the next output channel
 
281
                        chan_destination += bytes_per_frame_per_channel;
 
282
                }
 
283
 
 
284
 
 
285
                // Go on to the next N input samples
 
286
                base_source += input_frame_size;
 
287
        }
 
288
 
 
289
        // Here it is, ladies and gentlemen! Rerouted data!
 
290
        return result;
 
291
}
 
292
 
 
293
const struct filter_plugin route_filter_plugin = {
 
294
        "route",
 
295
        route_filter_init,
 
296
};