~ubuntu-branches/ubuntu/trusty/mpd/trusty

« back to all changes in this revision

Viewing changes to src/filter/route_filter_plugin.c

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad
  • Date: 2011-02-02 12:26:30 UTC
  • mfrom: (1.5.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110202122630-bdyx8w4k94doz4fs
Tags: 0.16.1-1ubuntu1
* Merge from debian unstable. Remaining changes:
  - debian/control:
    + Don't build-depend on libmikmod2-dev (Debian bug #510675).
    + Move avahi-daemon from Suggests field to Recommends field.
  - debian/mpd.init.d:
    + Read mpd user from mpd.conf.
  - debian/control, debian/rules:
    + Add libmp3lame-dev to the build dependencies and enable lame.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2003-2010 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 "conf.h"
 
44
#include "audio_format.h"
 
45
#include "audio_check.h"
 
46
#include "filter_plugin.h"
 
47
#include "filter_internal.h"
 
48
#include "filter_registry.h"
 
49
#include "pcm_buffer.h"
 
50
 
 
51
#include <assert.h>
 
52
#include <string.h>
 
53
#include <stdlib.h>
 
54
 
 
55
 
 
56
struct route_filter {
 
57
 
 
58
        /**
 
59
         * Inherit (and support cast to/from) filter
 
60
         */
 
61
        struct filter base;
 
62
 
 
63
        /**
 
64
         * The minimum number of channels we need for output
 
65
         * to be able to perform all the copies the user has specified
 
66
         */
 
67
        unsigned char min_output_channels;
 
68
 
 
69
        /**
 
70
         * The minimum number of input channels we need to
 
71
         * copy all the data the user has requested. If fewer
 
72
         * than this many are supplied by the input, undefined
 
73
         * copy operations are given zeroed sources in stead.
 
74
         */
 
75
        unsigned char min_input_channels;
 
76
 
 
77
        /**
 
78
         * The set of copy operations to perform on each sample
 
79
         * The index is an output channel to use, the value is
 
80
         * a corresponding input channel from which to take the
 
81
         * data. A -1 means "no source"
 
82
         */
 
83
        signed char* sources;
 
84
 
 
85
        /**
 
86
         * The actual input format of our signal, once opened
 
87
         */
 
88
        struct audio_format input_format;
 
89
 
 
90
        /**
 
91
         * The decided upon output format, once opened
 
92
         */
 
93
        struct audio_format output_format;
 
94
 
 
95
        /**
 
96
         * The size, in bytes, of each multichannel frame in the
 
97
         * input buffer
 
98
         */
 
99
        size_t input_frame_size;
 
100
 
 
101
        /**
 
102
         * The size, in bytes, of each multichannel frame in the
 
103
         * output buffer
 
104
         */
 
105
        size_t output_frame_size;
 
106
 
 
107
        /**
 
108
         * The output buffer used last time around, can be reused if the size doesn't differ.
 
109
         */
 
110
        struct pcm_buffer output_buffer;
 
111
 
 
112
};
 
113
 
 
114
/**
 
115
 * Parse the "routes" section, a string on the form
 
116
 *  a>b, c>d, e>f, ...
 
117
 * where a... are non-unique, non-negative integers
 
118
 * and input channel a gets copied to output channel b, etc.
 
119
 * @param param the configuration block to read
 
120
 * @param filter a route_filter whose min_channels and sources[] to set
 
121
 * @return true on success, false on error
 
122
 */
 
123
static bool
 
124
route_filter_parse(const struct config_param *param,
 
125
                   struct route_filter *filter,
 
126
                   GError **error_r) {
 
127
 
 
128
        /* TODO:
 
129
         * With a more clever way of marking "don't copy to output N",
 
130
         * This could easily be merged into a single loop with some
 
131
         * dynamic g_realloc() instead of one count run and one g_malloc().
 
132
         */
 
133
 
 
134
        gchar **tokens;
 
135
        int number_of_copies;
 
136
 
 
137
        // A cowardly default, just passthrough stereo
 
138
        const char *routes =
 
139
                config_get_block_string(param, "routes", "0>0, 1>1");
 
140
 
 
141
        filter->min_input_channels = 0;
 
142
        filter->min_output_channels = 0;
 
143
 
 
144
        tokens = g_strsplit(routes, ",", 255);
 
145
        number_of_copies = g_strv_length(tokens);
 
146
 
 
147
        // Start by figuring out a few basic things about the routing set
 
148
        for (int c=0; c<number_of_copies; ++c) {
 
149
 
 
150
                // String and int representations of the source/destination
 
151
                gchar **sd;
 
152
                int source, dest;
 
153
 
 
154
                // Squeeze whitespace
 
155
                g_strstrip(tokens[c]);
 
156
 
 
157
                // Split the a>b string into source and destination
 
158
                sd = g_strsplit(tokens[c], ">", 2);
 
159
                if (g_strv_length(sd) != 2) {
 
160
                        g_set_error(error_r, config_quark(), 1,
 
161
                                "Invalid copy around %d in routes spec: %s",
 
162
                                param->line, tokens[c]);
 
163
                        g_strfreev(sd);
 
164
                        g_strfreev(tokens);
 
165
                        return false;
 
166
                }
 
167
 
 
168
                source = strtol(sd[0], NULL, 10);
 
169
                dest = strtol(sd[1], NULL, 10);
 
170
 
 
171
                // Keep track of the highest channel numbers seen
 
172
                // as either in- or outputs
 
173
                if (source >= filter->min_input_channels)
 
174
                        filter->min_input_channels = source + 1;
 
175
                if (dest   >= filter->min_output_channels)
 
176
                        filter->min_output_channels = dest + 1;
 
177
 
 
178
                g_strfreev(sd);
 
179
        }
 
180
 
 
181
        if (!audio_valid_channel_count(filter->min_output_channels)) {
 
182
                g_strfreev(tokens);
 
183
                g_set_error(error_r, audio_format_quark(), 0,
 
184
                            "Invalid number of output channels requested: %d",
 
185
                            filter->min_output_channels);
 
186
                return false;
 
187
        }
 
188
 
 
189
        // Allocate a map of "copy nothing to me"
 
190
        filter->sources =
 
191
                g_malloc(filter->min_output_channels * sizeof(signed char));
 
192
 
 
193
        for (int i=0; i<filter->min_output_channels; ++i)
 
194
                filter->sources[i] = -1;
 
195
 
 
196
        // Run through the spec again, and save the
 
197
        // actual mapping output <- input
 
198
        for (int c=0; c<number_of_copies; ++c) {
 
199
 
 
200
                // String and int representations of the source/destination
 
201
                gchar **sd;
 
202
                int source, dest;
 
203
 
 
204
                // Split the a>b string into source and destination
 
205
                sd = g_strsplit(tokens[c], ">", 2);
 
206
                if (g_strv_length(sd) != 2) {
 
207
                        g_set_error(error_r, config_quark(), 1,
 
208
                                "Invalid copy around %d in routes spec: %s",
 
209
                                param->line, tokens[c]);
 
210
                        g_strfreev(sd);
 
211
                        g_strfreev(tokens);
 
212
                        return false;
 
213
                }
 
214
 
 
215
                source = strtol(sd[0], NULL, 10);
 
216
                dest = strtol(sd[1], NULL, 10);
 
217
 
 
218
                filter->sources[dest] = source;
 
219
 
 
220
                g_strfreev(sd);
 
221
        }
 
222
 
 
223
        g_strfreev(tokens);
 
224
 
 
225
        return true;
 
226
}
 
227
 
 
228
static struct filter *
 
229
route_filter_init(const struct config_param *param,
 
230
                 G_GNUC_UNUSED GError **error_r)
 
231
{
 
232
        struct route_filter *filter = g_new(struct route_filter, 1);
 
233
        filter_init(&filter->base, &route_filter_plugin);
 
234
 
 
235
        // Allocate and set the filter->sources[] array
 
236
        route_filter_parse(param, filter, error_r);
 
237
 
 
238
        return &filter->base;
 
239
}
 
240
 
 
241
static void
 
242
route_filter_finish(struct filter *_filter)
 
243
{
 
244
        struct route_filter *filter = (struct route_filter *)_filter;
 
245
 
 
246
        g_free(filter->sources);
 
247
        g_free(filter);
 
248
}
 
249
 
 
250
static const struct audio_format *
 
251
route_filter_open(struct filter *_filter, struct audio_format *audio_format,
 
252
                  G_GNUC_UNUSED GError **error_r)
 
253
{
 
254
        struct route_filter *filter = (struct route_filter *)_filter;
 
255
 
 
256
        // Copy the input format for later reference
 
257
        filter->input_format = *audio_format;
 
258
        filter->input_frame_size =
 
259
                audio_format_frame_size(&filter->input_format);
 
260
 
 
261
        // Decide on an output format which has enough channels,
 
262
        // and is otherwise identical
 
263
        filter->output_format = *audio_format;
 
264
        filter->output_format.channels = filter->min_output_channels;
 
265
 
 
266
        // Precalculate this simple value, to speed up allocation later
 
267
        filter->output_frame_size =
 
268
                audio_format_frame_size(&filter->output_format);
 
269
 
 
270
        // This buffer grows as needed
 
271
        pcm_buffer_init(&filter->output_buffer);
 
272
 
 
273
        return &filter->output_format;
 
274
}
 
275
 
 
276
static void
 
277
route_filter_close(struct filter *_filter)
 
278
{
 
279
        struct route_filter *filter = (struct route_filter *)_filter;
 
280
 
 
281
        pcm_buffer_deinit(&filter->output_buffer);
 
282
}
 
283
 
 
284
static const void *
 
285
route_filter_filter(struct filter *_filter,
 
286
                   const void *src, size_t src_size,
 
287
                   size_t *dest_size_r, G_GNUC_UNUSED GError **error_r)
 
288
{
 
289
        struct route_filter *filter = (struct route_filter *)_filter;
 
290
 
 
291
        size_t number_of_frames = src_size / filter->input_frame_size;
 
292
 
 
293
        size_t bytes_per_frame_per_channel =
 
294
                audio_format_sample_size(&filter->input_format);
 
295
 
 
296
        // A moving pointer that always refers to channel 0 in the input, at the currently handled frame
 
297
        const uint8_t *base_source = src;
 
298
 
 
299
        // A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output
 
300
        uint8_t *chan_destination;
 
301
 
 
302
        // Grow our reusable buffer, if needed, and set the moving pointer
 
303
        *dest_size_r = number_of_frames * filter->output_frame_size;
 
304
        chan_destination = pcm_buffer_get(&filter->output_buffer, *dest_size_r);
 
305
 
 
306
 
 
307
        // Perform our copy operations, with N input channels and M output channels
 
308
        for (unsigned int s=0; s<number_of_frames; ++s) {
 
309
 
 
310
                // Need to perform one copy per output channel
 
311
                for (unsigned int c=0; c<filter->min_output_channels; ++c) {
 
312
                        if (filter->sources[c] == -1 ||
 
313
                            (unsigned)filter->sources[c] >= filter->input_format.channels) {
 
314
                                // No source for this destination output,
 
315
                                // give it zeroes as input
 
316
                                memset(chan_destination,
 
317
                                        0x00,
 
318
                                        bytes_per_frame_per_channel);
 
319
                        } else {
 
320
                                // Get the data from channel sources[c]
 
321
                                // and copy it to the output
 
322
                                const uint8_t *data = base_source +
 
323
                                        (filter->sources[c] * bytes_per_frame_per_channel);
 
324
                                memcpy(chan_destination,
 
325
                                          data,
 
326
                                          bytes_per_frame_per_channel);
 
327
                        }
 
328
                        // Move on to the next output channel
 
329
                        chan_destination += bytes_per_frame_per_channel;
 
330
                }
 
331
 
 
332
 
 
333
                // Go on to the next N input samples
 
334
                base_source += filter->input_frame_size;
 
335
        }
 
336
 
 
337
        // Here it is, ladies and gentlemen! Rerouted data!
 
338
        return (void *) filter->output_buffer.buffer;
 
339
}
 
340
 
 
341
const struct filter_plugin route_filter_plugin = {
 
342
        .name = "route",
 
343
        .init = route_filter_init,
 
344
        .finish = route_filter_finish,
 
345
        .open = route_filter_open,
 
346
        .close = route_filter_close,
 
347
        .filter = route_filter_filter,
 
348
};