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

« back to all changes in this revision

Viewing changes to src/filter/route_filter_plugin.c

  • 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-2011 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
 
};