~ubuntu-branches/ubuntu/saucy/mpd/saucy

« back to all changes in this revision

Viewing changes to src/decoder/wavpack_decoder_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
#include "config.h"
 
21
#include "decoder_api.h"
 
22
#include "audio_check.h"
 
23
#include "path.h"
 
24
#include "utils.h"
 
25
 
 
26
#include <wavpack/wavpack.h>
 
27
#include <glib.h>
 
28
 
 
29
#include <assert.h>
 
30
#include <unistd.h>
 
31
#include <stdio.h>
 
32
#include <stdlib.h>
 
33
 
 
34
#undef G_LOG_DOMAIN
 
35
#define G_LOG_DOMAIN "wavpack"
 
36
 
 
37
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
 
38
#define CHUNK_SIZE              1020
 
39
 
 
40
#define ERRORLEN 80
 
41
 
 
42
static struct {
 
43
        const char *name;
 
44
        enum tag_type type;
 
45
} tagtypes[] = {
 
46
        { "artist", TAG_ARTIST },
 
47
        { "album", TAG_ALBUM },
 
48
        { "title", TAG_TITLE },
 
49
        { "track", TAG_TRACK },
 
50
        { "name", TAG_NAME },
 
51
        { "genre", TAG_GENRE },
 
52
        { "date", TAG_DATE },
 
53
        { "composer", TAG_COMPOSER },
 
54
        { "performer", TAG_PERFORMER },
 
55
        { "comment", TAG_COMMENT },
 
56
        { "disc", TAG_DISC },
 
57
};
 
58
 
 
59
/** A pointer type for format converter function. */
 
60
typedef void (*format_samples_t)(
 
61
        int bytes_per_sample,
 
62
        void *buffer, uint32_t count
 
63
);
 
64
 
 
65
/*
 
66
 * This function has been borrowed from the tiny player found on
 
67
 * wavpack.com. Modifications were required because mpd only handles
 
68
 * max 24-bit samples.
 
69
 */
 
70
static void
 
71
format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
 
72
{
 
73
        int32_t *src = buffer;
 
74
 
 
75
        switch (bytes_per_sample) {
 
76
        case 1: {
 
77
                int8_t *dst = buffer;
 
78
                /*
 
79
                 * The asserts like the following one are because we do the
 
80
                 * formatting of samples within a single buffer. The size
 
81
                 * of the output samples never can be greater than the size
 
82
                 * of the input ones. Otherwise we would have an overflow.
 
83
                 */
 
84
                assert_static(sizeof(*dst) <= sizeof(*src));
 
85
 
 
86
                /* pass through and align 8-bit samples */
 
87
                while (count--) {
 
88
                        *dst++ = *src++;
 
89
                }
 
90
                break;
 
91
        }
 
92
        case 2: {
 
93
                uint16_t *dst = buffer;
 
94
                assert_static(sizeof(*dst) <= sizeof(*src));
 
95
 
 
96
                /* pass through and align 16-bit samples */
 
97
                while (count--) {
 
98
                        *dst++ = *src++;
 
99
                }
 
100
                break;
 
101
        }
 
102
 
 
103
        case 3:
 
104
        case 4:
 
105
                /* do nothing */
 
106
                break;
 
107
        }
 
108
}
 
109
 
 
110
/*
 
111
 * This function converts floating point sample data to 24-bit integer.
 
112
 */
 
113
static void
 
114
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
 
115
                     uint32_t count)
 
116
{
 
117
        int32_t *dst = buffer;
 
118
        float *src = buffer;
 
119
        assert_static(sizeof(*dst) <= sizeof(*src));
 
120
 
 
121
        while (count--) {
 
122
                *dst++ = (int32_t)(*src++ + 0.5f);
 
123
        }
 
124
}
 
125
 
 
126
/**
 
127
 * Choose a MPD sample format from libwavpacks' number of bits.
 
128
 */
 
129
static enum sample_format
 
130
wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
 
131
{
 
132
        if (is_float)
 
133
                return SAMPLE_FORMAT_S24_P32;
 
134
 
 
135
        switch (bytes_per_sample) {
 
136
        case 1:
 
137
                return SAMPLE_FORMAT_S8;
 
138
 
 
139
        case 2:
 
140
                return SAMPLE_FORMAT_S16;
 
141
 
 
142
        case 3:
 
143
                return SAMPLE_FORMAT_S24_P32;
 
144
 
 
145
        case 4:
 
146
                return SAMPLE_FORMAT_S32;
 
147
 
 
148
        default:
 
149
                return SAMPLE_FORMAT_UNDEFINED;
 
150
        }
 
151
}
 
152
 
 
153
/*
 
154
 * This does the main decoding thing.
 
155
 * Requires an already opened WavpackContext.
 
156
 */
 
157
static void
 
158
wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
 
159
{
 
160
        GError *error = NULL;
 
161
        bool is_float;
 
162
        enum sample_format sample_format;
 
163
        struct audio_format audio_format;
 
164
        format_samples_t format_samples;
 
165
        char chunk[CHUNK_SIZE];
 
166
        int samples_requested, samples_got;
 
167
        float total_time;
 
168
        int bytes_per_sample, output_sample_size;
 
169
 
 
170
        is_float = (WavpackGetMode(wpc) & MODE_FLOAT) != 0;
 
171
        sample_format =
 
172
                wavpack_bits_to_sample_format(is_float,
 
173
                                              WavpackGetBytesPerSample(wpc));
 
174
 
 
175
        if (!audio_format_init_checked(&audio_format,
 
176
                                       WavpackGetSampleRate(wpc),
 
177
                                       sample_format,
 
178
                                       WavpackGetNumChannels(wpc), &error)) {
 
179
                g_warning("%s", error->message);
 
180
                g_error_free(error);
 
181
                return;
 
182
        }
 
183
 
 
184
        if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) {
 
185
                format_samples = format_samples_float;
 
186
        } else {
 
187
                format_samples = format_samples_int;
 
188
        }
 
189
 
 
190
        total_time = WavpackGetNumSamples(wpc);
 
191
        total_time /= audio_format.sample_rate;
 
192
        bytes_per_sample = WavpackGetBytesPerSample(wpc);
 
193
        output_sample_size = audio_format_frame_size(&audio_format);
 
194
 
 
195
        /* wavpack gives us all kind of samples in a 32-bit space */
 
196
        samples_requested = sizeof(chunk) / (4 * audio_format.channels);
 
197
 
 
198
        decoder_initialized(decoder, &audio_format, can_seek, total_time);
 
199
 
 
200
        do {
 
201
                if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) {
 
202
                        if (can_seek) {
 
203
                                unsigned where = decoder_seek_where(decoder) *
 
204
                                        audio_format.sample_rate;
 
205
 
 
206
                                if (WavpackSeekSample(wpc, where)) {
 
207
                                        decoder_command_finished(decoder);
 
208
                                } else {
 
209
                                        decoder_seek_error(decoder);
 
210
                                }
 
211
                        } else {
 
212
                                decoder_seek_error(decoder);
 
213
                        }
 
214
                }
 
215
 
 
216
                if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) {
 
217
                        break;
 
218
                }
 
219
 
 
220
                samples_got = WavpackUnpackSamples(
 
221
                        wpc, (int32_t *)chunk, samples_requested
 
222
                );
 
223
                if (samples_got > 0) {
 
224
                        int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
 
225
                                      1000 + 0.5);
 
226
 
 
227
                        format_samples(
 
228
                                bytes_per_sample, chunk,
 
229
                                samples_got * audio_format.channels
 
230
                        );
 
231
 
 
232
                        decoder_data(
 
233
                                decoder, NULL, chunk,
 
234
                                samples_got * output_sample_size,
 
235
                                bitrate
 
236
                        );
 
237
                }
 
238
        } while (samples_got > 0);
 
239
}
 
240
 
 
241
/**
 
242
 * Locate and parse a floating point tag.  Returns true if it was
 
243
 * found.
 
244
 */
 
245
static bool
 
246
wavpack_tag_float(WavpackContext *wpc, const char *key, float *value_r)
 
247
{
 
248
        char buffer[64];
 
249
        int ret;
 
250
 
 
251
        ret = WavpackGetTagItem(wpc, key, buffer, sizeof(buffer));
 
252
        if (ret <= 0)
 
253
                return false;
 
254
 
 
255
        *value_r = atof(buffer);
 
256
        return true;
 
257
}
 
258
 
 
259
static bool
 
260
wavpack_replaygain(struct replay_gain_info *replay_gain_info,
 
261
                   WavpackContext *wpc)
 
262
{
 
263
        bool found = false;
 
264
 
 
265
        replay_gain_info_init(replay_gain_info);
 
266
 
 
267
        found |= wavpack_tag_float(
 
268
                wpc, "replaygain_track_gain",
 
269
                &replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain
 
270
        );
 
271
        found |= wavpack_tag_float(
 
272
                wpc, "replaygain_track_peak",
 
273
                &replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak
 
274
        );
 
275
        found |= wavpack_tag_float(
 
276
                wpc, "replaygain_album_gain",
 
277
                &replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain
 
278
        );
 
279
        found |= wavpack_tag_float(
 
280
                wpc, "replaygain_album_peak",
 
281
                &replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak
 
282
        );
 
283
 
 
284
        return found;
 
285
}
 
286
 
 
287
/*
 
288
 * Reads metainfo from the specified file.
 
289
 */
 
290
static struct tag *
 
291
wavpack_tagdup(const char *fname)
 
292
{
 
293
        WavpackContext *wpc;
 
294
        struct tag *tag;
 
295
        char error[ERRORLEN];
 
296
        char *s;
 
297
        int size, allocated_size;
 
298
 
 
299
        wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0);
 
300
        if (wpc == NULL) {
 
301
                g_warning(
 
302
                        "failed to open WavPack file \"%s\": %s\n",
 
303
                        fname, error
 
304
                );
 
305
                return NULL;
 
306
        }
 
307
 
 
308
        tag = tag_new();
 
309
        tag->time = WavpackGetNumSamples(wpc);
 
310
        tag->time /= WavpackGetSampleRate(wpc);
 
311
 
 
312
        allocated_size = 0;
 
313
        s = NULL;
 
314
 
 
315
        for (unsigned i = 0; i < G_N_ELEMENTS(tagtypes); ++i) {
 
316
                size = WavpackGetTagItem(wpc, tagtypes[i].name, NULL, 0);
 
317
                if (size > 0) {
 
318
                        ++size; /* EOS */
 
319
 
 
320
                        if (s == NULL) {
 
321
                                s = g_malloc(size);
 
322
                                allocated_size = size;
 
323
                        } else if (size > allocated_size) {
 
324
                                char *t = (char *)g_realloc(s, size);
 
325
                                allocated_size = size;
 
326
                                s = t;
 
327
                        }
 
328
 
 
329
                        WavpackGetTagItem(wpc, tagtypes[i].name, s, size);
 
330
                        tag_add_item(tag, tagtypes[i].type, s);
 
331
                }
 
332
        }
 
333
 
 
334
        g_free(s);
 
335
 
 
336
        WavpackCloseFile(wpc);
 
337
 
 
338
        return tag;
 
339
}
 
340
 
 
341
/*
 
342
 * mpd input_stream <=> WavpackStreamReader wrapper callbacks
 
343
 */
 
344
 
 
345
/* This struct is needed for per-stream last_byte storage. */
 
346
struct wavpack_input {
 
347
        struct decoder *decoder;
 
348
        struct input_stream *is;
 
349
        /* Needed for push_back_byte() */
 
350
        int last_byte;
 
351
};
 
352
 
 
353
/**
 
354
 * Little wrapper for struct wavpack_input to cast from void *.
 
355
 */
 
356
static struct wavpack_input *
 
357
wpin(void *id)
 
358
{
 
359
        assert(id);
 
360
        return id;
 
361
}
 
362
 
 
363
static int32_t
 
364
wavpack_input_read_bytes(void *id, void *data, int32_t bcount)
 
365
{
 
366
        uint8_t *buf = (uint8_t *)data;
 
367
        int32_t i = 0;
 
368
 
 
369
        if (wpin(id)->last_byte != EOF) {
 
370
                *buf++ = wpin(id)->last_byte;
 
371
                wpin(id)->last_byte = EOF;
 
372
                --bcount;
 
373
                ++i;
 
374
        }
 
375
 
 
376
        /* wavpack fails if we return a partial read, so we just wait
 
377
           until the buffer is full */
 
378
        while (bcount > 0) {
 
379
                size_t nbytes = decoder_read(
 
380
                        wpin(id)->decoder, wpin(id)->is, buf, bcount
 
381
                );
 
382
                if (nbytes == 0) {
 
383
                        /* EOF, error or a decoder command */
 
384
                        break;
 
385
                }
 
386
 
 
387
                i += nbytes;
 
388
                bcount -= nbytes;
 
389
                buf += nbytes;
 
390
        }
 
391
 
 
392
        return i;
 
393
}
 
394
 
 
395
static uint32_t
 
396
wavpack_input_get_pos(void *id)
 
397
{
 
398
        return wpin(id)->is->offset;
 
399
}
 
400
 
 
401
static int
 
402
wavpack_input_set_pos_abs(void *id, uint32_t pos)
 
403
{
 
404
        return input_stream_seek(wpin(id)->is, pos, SEEK_SET, NULL) ? 0 : -1;
 
405
}
 
406
 
 
407
static int
 
408
wavpack_input_set_pos_rel(void *id, int32_t delta, int mode)
 
409
{
 
410
        return input_stream_seek(wpin(id)->is, delta, mode, NULL) ? 0 : -1;
 
411
}
 
412
 
 
413
static int
 
414
wavpack_input_push_back_byte(void *id, int c)
 
415
{
 
416
        if (wpin(id)->last_byte == EOF) {
 
417
                wpin(id)->last_byte = c;
 
418
                return c;
 
419
        } else {
 
420
                return EOF;
 
421
        }
 
422
}
 
423
 
 
424
static uint32_t
 
425
wavpack_input_get_length(void *id)
 
426
{
 
427
        if (wpin(id)->is->size < 0)
 
428
                return 0;
 
429
 
 
430
        return wpin(id)->is->size;
 
431
}
 
432
 
 
433
static int
 
434
wavpack_input_can_seek(void *id)
 
435
{
 
436
        return wpin(id)->is->seekable;
 
437
}
 
438
 
 
439
static WavpackStreamReader mpd_is_reader = {
 
440
        .read_bytes = wavpack_input_read_bytes,
 
441
        .get_pos = wavpack_input_get_pos,
 
442
        .set_pos_abs = wavpack_input_set_pos_abs,
 
443
        .set_pos_rel = wavpack_input_set_pos_rel,
 
444
        .push_back_byte = wavpack_input_push_back_byte,
 
445
        .get_length = wavpack_input_get_length,
 
446
        .can_seek = wavpack_input_can_seek,
 
447
        .write_bytes = NULL /* no need to write edited tags */
 
448
};
 
449
 
 
450
static void
 
451
wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder,
 
452
                   struct input_stream *is)
 
453
{
 
454
        isp->decoder = decoder;
 
455
        isp->is = is;
 
456
        isp->last_byte = EOF;
 
457
}
 
458
 
 
459
static struct input_stream *
 
460
wavpack_open_wvc(struct decoder *decoder, const char *uri,
 
461
                 struct wavpack_input *wpi)
 
462
{
 
463
        struct input_stream *is_wvc;
 
464
        char *wvc_url = NULL;
 
465
        char first_byte;
 
466
        size_t nbytes;
 
467
 
 
468
        /*
 
469
         * As we use dc->utf8url, this function will be bad for
 
470
         * single files. utf8url is not absolute file path :/
 
471
         */
 
472
        if (uri == NULL)
 
473
                return false;
 
474
 
 
475
        wvc_url = g_strconcat(uri, "c", NULL);
 
476
        is_wvc = input_stream_open(wvc_url, NULL);
 
477
        g_free(wvc_url);
 
478
 
 
479
        if (is_wvc == NULL)
 
480
                return NULL;
 
481
 
 
482
        /*
 
483
         * And we try to buffer in order to get know
 
484
         * about a possible 404 error.
 
485
         */
 
486
        nbytes = decoder_read(
 
487
                decoder, is_wvc, &first_byte, sizeof(first_byte)
 
488
        );
 
489
        if (nbytes == 0) {
 
490
                input_stream_close(is_wvc);
 
491
                return NULL;
 
492
        }
 
493
 
 
494
        /* push it back */
 
495
        wavpack_input_init(wpi, decoder, is_wvc);
 
496
        wpi->last_byte = first_byte;
 
497
        return is_wvc;
 
498
}
 
499
 
 
500
/*
 
501
 * Decodes a stream.
 
502
 */
 
503
static void
 
504
wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
 
505
{
 
506
        char error[ERRORLEN];
 
507
        WavpackContext *wpc;
 
508
        struct input_stream *is_wvc;
 
509
        int open_flags = OPEN_NORMALIZE;
 
510
        struct wavpack_input isp, isp_wvc;
 
511
        bool can_seek = is->seekable;
 
512
 
 
513
        is_wvc = wavpack_open_wvc(decoder, is->uri, &isp_wvc);
 
514
        if (is_wvc != NULL) {
 
515
                open_flags |= OPEN_WVC;
 
516
                can_seek &= is_wvc->seekable;
 
517
        }
 
518
 
 
519
        if (!can_seek) {
 
520
                open_flags |= OPEN_STREAMING;
 
521
        }
 
522
 
 
523
        wavpack_input_init(&isp, decoder, is);
 
524
        wpc = WavpackOpenFileInputEx(
 
525
                &mpd_is_reader, &isp,
 
526
                open_flags & OPEN_WVC ? &isp_wvc : NULL,
 
527
                error, open_flags, 23
 
528
        );
 
529
 
 
530
        if (wpc == NULL) {
 
531
                g_warning("failed to open WavPack stream: %s\n", error);
 
532
                return;
 
533
        }
 
534
 
 
535
        wavpack_decode(decoder, wpc, can_seek);
 
536
 
 
537
        WavpackCloseFile(wpc);
 
538
        if (open_flags & OPEN_WVC) {
 
539
                input_stream_close(is_wvc);
 
540
        }
 
541
}
 
542
 
 
543
/*
 
544
 * Decodes a file.
 
545
 */
 
546
static void
 
547
wavpack_filedecode(struct decoder *decoder, const char *fname)
 
548
{
 
549
        char error[ERRORLEN];
 
550
        WavpackContext *wpc;
 
551
 
 
552
        wpc = WavpackOpenFileInput(
 
553
                fname, error,
 
554
                OPEN_TAGS | OPEN_WVC | OPEN_NORMALIZE, 23
 
555
        );
 
556
        if (wpc == NULL) {
 
557
                g_warning(
 
558
                        "failed to open WavPack file \"%s\": %s\n",
 
559
                        fname, error
 
560
                );
 
561
                return;
 
562
        }
 
563
 
 
564
        struct replay_gain_info replay_gain_info;
 
565
        if (wavpack_replaygain(&replay_gain_info, wpc))
 
566
                decoder_replay_gain(decoder, &replay_gain_info);
 
567
 
 
568
        wavpack_decode(decoder, wpc, true);
 
569
 
 
570
        WavpackCloseFile(wpc);
 
571
}
 
572
 
 
573
static char const *const wavpack_suffixes[] = {
 
574
        "wv",
 
575
        NULL
 
576
};
 
577
 
 
578
static char const *const wavpack_mime_types[] = {
 
579
        "audio/x-wavpack",
 
580
        NULL
 
581
};
 
582
 
 
583
const struct decoder_plugin wavpack_decoder_plugin = {
 
584
        .name = "wavpack",
 
585
        .stream_decode = wavpack_streamdecode,
 
586
        .file_decode = wavpack_filedecode,
 
587
        .tag_dup = wavpack_tagdup,
 
588
        .suffixes = wavpack_suffixes,
 
589
        .mime_types = wavpack_mime_types
 
590
};