Update to 6762
[qball-mpd.git] / src / inputPlugins / wavpack_plugin.c
blob45e7dba78e10823ea86680a770bd024a7f72bf8a
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
4 *
5 * WavPack support added by Laszlo Ashin <kodest@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "../inputPlugin.h"
23 #ifdef HAVE_WAVPACK
25 #include "../utils.h"
26 #include "../audio.h"
27 #include "../log.h"
28 #include "../pcm_utils.h"
29 #include "../playerData.h"
30 #include "../outputBuffer.h"
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <wavpack/wavpack.h>
39 #include <math.h>
41 #define ERRORLEN 80
43 static struct {
44 const char *name;
45 int type;
46 } tagtypes[] = {
47 { "artist", TAG_ITEM_ARTIST },
48 { "album", TAG_ITEM_ALBUM },
49 { "title", TAG_ITEM_TITLE },
50 { "track", TAG_ITEM_TRACK },
51 { "name", TAG_ITEM_NAME },
52 { "genre", TAG_ITEM_GENRE },
53 { "date", TAG_ITEM_DATE },
54 { "composer", TAG_ITEM_COMPOSER },
55 { "performer", TAG_ITEM_PERFORMER },
56 { "comment", TAG_ITEM_COMMENT },
57 { "disc", TAG_ITEM_DISC },
58 { NULL, 0 }
62 * This function has been borrowed from the tiny player found on
63 * wavpack.com. Modifications were required because mpd only handles
64 * max 16 bit samples.
66 static void format_samples_int(int Bps, void *buffer, uint32_t samcnt)
68 int32_t temp;
69 uchar *dst = (uchar *)buffer;
70 int32_t *src = (int32_t *)buffer;
72 switch (Bps) {
73 case 1:
74 while (samcnt--)
75 *dst++ = *src++;
76 break;
77 case 2:
78 while (samcnt--) {
79 temp = *src++;
80 #ifdef WORDS_BIGENDIAN
81 *dst++ = (uchar)(temp >> 8);
82 *dst++ = (uchar)(temp);
83 #else
84 *dst++ = (uchar)(temp);
85 *dst++ = (uchar)(temp >> 8);
86 #endif
88 break;
89 case 3:
90 /* downscale to 16 bits */
91 while (samcnt--) {
92 temp = *src++;
93 *dst++ = (uchar)(temp >> 8);
94 *dst++ = (uchar)(temp >> 16);
95 #ifdef WORDS_BIGENDIAN
96 *dst++ = (uchar)(temp >> 16);
97 *dst++ = (uchar)(temp >> 8);
99 #else
100 *dst++ = (uchar)(temp >> 8);
101 *dst++ = (uchar)(temp >> 16);
102 #endif
104 break;
105 case 4:
106 /* downscale to 16 bits */
107 while (samcnt--) {
108 temp = *src++;
109 #ifdef WORDS_BIGENDIAN
110 *dst++ = (uchar)(temp >> 24);
111 *dst++ = (uchar)(temp >> 16);
113 #else
114 *dst++ = (uchar)(temp >> 16);
115 *dst++ = (uchar)(temp >> 24);
116 #endif
118 break;
123 * This function converts floating point sample data to 16 bit integer.
125 static void format_samples_float(int Bps, void *buffer, uint32_t samcnt)
127 int16_t *dst = (int16_t *)buffer;
128 float *src = (float *)buffer;
130 while (samcnt--) {
131 *dst++ = (int16_t)(*src++);
136 * This does the main decoding thing.
137 * Requires an already opened WavpackContext.
139 static void wavpack_decode(OutputBuffer *cb, DecoderControl *dc,
140 WavpackContext *wpc, int canseek,
141 ReplayGainInfo *replayGainInfo)
143 void (*format_samples)(int Bps, void *buffer, uint32_t samcnt);
144 char chunk[CHUNK_SIZE];
145 float time;
146 int samplesreq, samplesgot;
147 int allsamples;
148 int position, outsamplesize;
149 int Bps;
151 dc->audioFormat.sampleRate = WavpackGetSampleRate(wpc);
152 dc->audioFormat.channels = WavpackGetReducedChannels(wpc);
153 dc->audioFormat.bits = WavpackGetBitsPerSample(wpc);
155 if (dc->audioFormat.bits > 16)
156 dc->audioFormat.bits = 16;
158 if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT)
159 format_samples = format_samples_float;
160 else
161 format_samples = format_samples_int;
163 if ((WavpackGetMode(wpc) & MODE_WVC) == MODE_WVC)
164 ERROR("decoding WITH wvc!!!\n");
165 else
166 ERROR("decoding without wvc\n");
168 allsamples = WavpackGetNumSamples(wpc);
169 Bps = WavpackGetBytesPerSample(wpc);
171 outsamplesize = Bps;
172 if (outsamplesize > 2)
173 outsamplesize = 2;
174 outsamplesize *= dc->audioFormat.channels;
176 samplesreq = sizeof(chunk) / (4 * dc->audioFormat.channels);
178 getOutputAudioFormat(&(dc->audioFormat), &(cb->audioFormat));
180 dc->totalTime = (float)allsamples / dc->audioFormat.sampleRate;
181 dc->state = DECODE_STATE_DECODE;
182 dc->seekable = canseek;
184 position = 0;
186 do {
187 if (dc->seek) {
188 if (canseek) {
189 int where;
191 clearOutputBuffer(cb);
193 where = dc->seekWhere *
194 dc->audioFormat.sampleRate;
195 if (WavpackSeekSample(wpc, where))
196 position = where;
197 else
198 dc->seekError = 1;
199 } else {
200 dc->seekError = 1;
203 dc->seek = 0;
206 if (dc->stop)
207 break;
209 samplesgot = WavpackUnpackSamples(wpc,
210 (int32_t *)chunk, samplesreq);
211 if (samplesgot > 0) {
212 int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
213 1000 + 0.5);
214 position += samplesgot;
215 time = (float)position / dc->audioFormat.sampleRate;
217 format_samples(Bps, chunk,
218 samplesgot * dc->audioFormat.channels);
220 sendDataToOutputBuffer(cb, NULL, dc, 0, chunk,
221 samplesgot * outsamplesize,
222 time, bitrate, replayGainInfo);
224 } while (samplesgot == samplesreq);
226 flushOutputBuffer(cb);
228 dc->state = DECODE_STATE_STOP;
229 dc->stop = 0;
232 static char *wavpack_tag(WavpackContext *wpc, char *key)
234 char *value = NULL;
235 int size;
237 size = WavpackGetTagItem(wpc, key, NULL, 0);
238 if (size > 0) {
239 size++;
240 value = xmalloc(size);
241 if (!value)
242 return NULL;
243 WavpackGetTagItem(wpc, key, value, size);
246 return value;
249 static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
251 ReplayGainInfo *replayGainInfo;
252 int found = 0;
253 char *value;
255 replayGainInfo = newReplayGainInfo();
257 value = wavpack_tag(wpc, "replaygain_track_gain");
258 if (value) {
259 replayGainInfo->trackGain = atof(value);
260 free(value);
261 found = 1;
264 value = wavpack_tag(wpc, "replaygain_album_gain");
265 if (value) {
266 replayGainInfo->albumGain = atof(value);
267 free(value);
268 found = 1;
271 value = wavpack_tag(wpc, "replaygain_track_peak");
272 if (value) {
273 replayGainInfo->trackPeak = atof(value);
274 free(value);
275 found = 1;
278 value = wavpack_tag(wpc, "replaygain_album_peak");
279 if (value) {
280 replayGainInfo->albumPeak = atof(value);
281 free(value);
282 found = 1;
286 if (found)
287 return replayGainInfo;
289 freeReplayGainInfo(replayGainInfo);
291 return NULL;
295 * Reads metainfo from the specified file.
297 static MpdTag *wavpack_tagdup(char *fname)
299 WavpackContext *wpc;
300 MpdTag *tag;
301 char error[ERRORLEN];
302 char *s;
303 int ssize;
304 int i, j;
306 wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0);
307 if (wpc == NULL) {
308 ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
309 return NULL;
312 tag = newMpdTag();
313 if (tag == NULL) {
314 ERROR("failed to newMpdTag()\n");
315 return NULL;
318 tag->time =
319 (float)WavpackGetNumSamples(wpc) / WavpackGetSampleRate(wpc);
321 ssize = 0;
322 s = NULL;
324 for (i = 0; tagtypes[i].name != NULL; ++i) {
325 j = WavpackGetTagItem(wpc, tagtypes[i].name, NULL, 0);
326 if (j > 0) {
327 ++j;
329 if (s == NULL) {
330 s = xmalloc(j);
331 if (s == NULL) break;
332 ssize = j;
333 } else if (j > ssize) {
334 char *t = (char *)xrealloc(s, j);
335 if (t == NULL) break;
336 ssize = j;
337 s = t;
340 WavpackGetTagItem(wpc, tagtypes[i].name, s, j);
341 addItemToMpdTag(tag, tagtypes[i].type, s);
345 if (s != NULL)
346 free(s);
348 WavpackCloseFile(wpc);
350 return tag;
354 * mpd InputStream <=> WavpackStreamReader wrapper callbacks
357 /* This struct is needed for per-stream last_byte storage. */
358 typedef struct {
359 InputStream *is;
360 /* Needed for push_back_byte() */
361 int last_byte;
362 } InputStreamPlus;
364 static int32_t read_bytes(void *id, void *data, int32_t bcount)
366 InputStreamPlus *isp = (InputStreamPlus *)id;
367 uint8_t *buf = (uint8_t *)data;
368 int32_t i = 0;
370 if (isp->last_byte != EOF) {
371 *buf++ = isp->last_byte;
372 isp->last_byte = EOF;
373 --bcount;
374 ++i;
376 return i + readFromInputStream(isp->is, buf, 1, bcount);
379 static uint32_t get_pos(void *id)
381 return ((InputStreamPlus *)id)->is->offset;
384 static int set_pos_abs(void *id, uint32_t pos)
386 return seekInputStream(((InputStreamPlus *)id)->is, pos, SEEK_SET);
389 static int set_pos_rel(void *id, int32_t delta, int mode)
391 return seekInputStream(((InputStreamPlus *)id)->is, delta, mode);
394 static int push_back_byte(void *id, int c)
396 ((InputStreamPlus *)id)->last_byte = c;
397 return 1;
400 static uint32_t get_length(void *id)
402 return ((InputStreamPlus *)id)->is->size;
405 static int can_seek(void *id)
407 return ((InputStreamPlus *)id)->is->seekable;
410 static WavpackStreamReader mpd_is_reader = {
411 .read_bytes = read_bytes,
412 .get_pos = get_pos,
413 .set_pos_abs = set_pos_abs,
414 .set_pos_rel = set_pos_rel,
415 .push_back_byte = push_back_byte,
416 .get_length = get_length,
417 .can_seek = can_seek,
418 .write_bytes = NULL /* no need to write edited tags */
421 static void
422 initInputStreamPlus(InputStreamPlus *isp, InputStream *is)
424 isp->is = is;
425 isp->last_byte = EOF;
429 * Tries to decode the specified stream, and gives true if managed to do it.
431 static unsigned int wavpack_trydecode(InputStream *is)
433 char error[ERRORLEN];
434 WavpackContext *wpc;
435 InputStreamPlus isp;
437 initInputStreamPlus(&isp, is);
438 wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, NULL, error,
439 OPEN_STREAMING, 0);
440 if (wpc == NULL)
441 return 0;
443 WavpackCloseFile(wpc);
444 /* Seek it back in order to play from the first byte. */
445 if (is->seekable)
446 seekInputStream(is, 0, SEEK_SET);
448 return 1;
452 * Decodes a stream.
454 static int wavpack_streamdecode(OutputBuffer *cb, DecoderControl *dc,
455 InputStream *is)
457 char error[ERRORLEN];
458 WavpackContext *wpc;
459 InputStream is_wvc;
460 int open_flags = OPEN_2CH_MAX | OPEN_NORMALIZE;
461 char *wvc_url = NULL;
462 int err;
463 InputStreamPlus isp, isp_wvc;
464 int canseek;
466 /* Try to find wvc */
467 do {
468 size_t len;
469 err = 1;
472 * As we use dc->utf8url, this function will be bad for
473 * single files. utf8url is not absolute file path :\
475 if (dc->utf8url == NULL)
476 break;
478 len = strlen(dc->utf8url);
479 if (!len)
480 break;
482 wvc_url = (char *)xmalloc(len + 2);
483 if (wvc_url == NULL)
484 break;
486 strncpy(wvc_url, dc->utf8url, len);
487 wvc_url[len] = 'c';
488 wvc_url[len + 1] = '\0';
490 if (openInputStream(&is_wvc, wvc_url))
491 break;
492 #if 0 /* this code is moved to http stuff */
494 * And we try to buffer in order to get know
495 * about a possible 404.
497 for (;;) {
498 if (inputStreamAtEOF(&is_wvc))
500 * EOF is reached even without
501 * reading a single byte...
502 * So, this is not good :\
504 break;
506 if (bufferInputStream(&is_wvc) >= 0) {
507 err = 0;
508 break;
511 /* Can this happen?: */
512 if (dc->stop)
513 break;
515 /* Save some CPU */
516 my_usleep(1000);
519 if (err) {
520 closeInputStream(&is_wvc);
521 break;
523 #endif
524 err = 0;
526 } while (0);
528 canseek = can_seek(&isp);
529 if (wvc_url != NULL) {
530 if (err) {
531 free(wvc_url);
532 wvc_url = NULL;
533 } else {
534 initInputStreamPlus(&isp_wvc, &is_wvc);
535 open_flags |= OPEN_WVC;
536 if (!can_seek(&isp_wvc))
537 canseek = 0;
541 if (!canseek)
542 open_flags |= OPEN_STREAMING;
544 initInputStreamPlus(&isp, is);
545 wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, &isp_wvc, error,
546 open_flags, 15);
547 if (wpc == NULL) {
548 ERROR("failed to open WavPack stream: %s\n", error);
549 return -1;
552 wavpack_decode(cb, dc, wpc, canseek, NULL);
554 WavpackCloseFile(wpc);
555 if (wvc_url != NULL) {
556 closeInputStream(&is_wvc);
557 free(wvc_url);
559 closeInputStream(is); /* calling side doesn't do this in mpd 0.13.0 */
561 return 0;
565 * Decodes a file.
567 static int wavpack_filedecode(OutputBuffer *cb, DecoderControl *dc, char *fname)
569 char error[ERRORLEN];
570 WavpackContext *wpc;
571 ReplayGainInfo *replayGainInfo;
573 wpc = WavpackOpenFileInput(fname, error,
574 OPEN_TAGS | OPEN_WVC |
575 OPEN_2CH_MAX | OPEN_NORMALIZE, 15);
576 if (wpc == NULL) {
577 ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
578 return -1;
581 replayGainInfo = wavpack_replaygain(wpc);
583 wavpack_decode(cb, dc, wpc, 1, replayGainInfo);
585 if (replayGainInfo)
586 freeReplayGainInfo(replayGainInfo);
588 WavpackCloseFile(wpc);
590 return 0;
593 static char *wavpackSuffixes[] = { "wv", NULL };
594 static char *wavpackMimeTypes[] = { "audio/x-wavpack", NULL };
596 InputPlugin wavpackPlugin = {
597 "wavpack",
598 NULL,
599 NULL,
600 /* Disabled till the http seek bug is fixed. */
601 wavpack_trydecode,
602 wavpack_streamdecode,
603 wavpack_filedecode,
604 wavpack_tagdup,
605 INPUT_PLUGIN_STREAM_FILE | INPUT_PLUGIN_STREAM_URL,
606 wavpackSuffixes,
607 wavpackMimeTypes
610 #else /* !HAVE_WAVPACK */
612 InputPlugin wavpackPlugin;
614 #endif /* !HAVE_WAVPACK */