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
5 * WavPack support added by Laszlo Ashin <kodest@gmail.com>
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"
28 #include "../pcm_utils.h"
29 #include "../playerData.h"
30 #include "../outputBuffer.h"
36 #include <sys/types.h>
38 #include <wavpack/wavpack.h>
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 },
62 * This function has been borrowed from the tiny player found on
63 * wavpack.com. Modifications were required because mpd only handles
66 static void format_samples_int(int Bps, void *buffer, uint32_t samcnt)
69 uchar *dst = (uchar *)buffer;
70 int32_t *src = (int32_t *)buffer;
79 *dst++ = (uchar)(temp = *src++);
80 *dst++ = (uchar)(temp >> 8);
84 /* downscale to 16 bits */
87 *dst++ = (uchar)(temp >> 8);
88 *dst++ = (uchar)(temp >> 16);
92 /* downscale to 16 bits */
95 *dst++ = (uchar)(temp >> 16);
96 *dst++ = (uchar)(temp >> 24);
103 * This function converts floating point sample data to 16 bit integer.
105 static void format_samples_float(int Bps, void *buffer, uint32_t samcnt)
107 int16_t *dst = (int16_t *)buffer;
108 float *src = (float *)buffer;
111 *dst++ = (int16_t)(*src++);
116 * This does the main decoding thing.
117 * Requires an already opened WavpackContext.
119 static void wavpack_decode(OutputBuffer *cb, DecoderControl *dc,
120 WavpackContext *wpc, int canseek,
121 ReplayGainInfo *replayGainInfo)
123 void (*format_samples)(int Bps, void *buffer, uint32_t samcnt);
124 char chunk[CHUNK_SIZE];
126 int samplesreq, samplesgot;
128 int position, outsamplesize;
131 dc->audioFormat.sampleRate = WavpackGetSampleRate(wpc);
132 dc->audioFormat.channels = WavpackGetReducedChannels(wpc);
133 dc->audioFormat.bits = WavpackGetBitsPerSample(wpc);
135 if (dc->audioFormat.bits > 16)
136 dc->audioFormat.bits = 16;
138 if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT)
139 format_samples = format_samples_float;
141 format_samples = format_samples_int;
143 if ((WavpackGetMode(wpc) & MODE_WVC) == MODE_WVC)
144 ERROR("decoding WITH wvc!!!\n");
146 ERROR("decoding without wvc\n");
148 allsamples = WavpackGetNumSamples(wpc);
149 Bps = WavpackGetBytesPerSample(wpc);
152 if (outsamplesize > 2)
154 outsamplesize *= dc->audioFormat.channels;
156 samplesreq = sizeof(chunk) / (4 * dc->audioFormat.channels);
158 getOutputAudioFormat(&(dc->audioFormat), &(cb->audioFormat));
160 dc->totalTime = (float)allsamples / dc->audioFormat.sampleRate;
161 dc->state = DECODE_STATE_DECODE;
162 dc->seekable = canseek;
171 clearOutputBuffer(cb);
173 where = dc->seekWhere *
174 dc->audioFormat.sampleRate;
175 if (WavpackSeekSample(wpc, where))
189 samplesgot = WavpackUnpackSamples(wpc,
190 (int32_t *)chunk, samplesreq);
191 if (samplesgot > 0) {
192 int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
194 position += samplesgot;
195 time = (float)position / dc->audioFormat.sampleRate;
197 format_samples(Bps, chunk,
198 samplesgot * dc->audioFormat.channels);
200 sendDataToOutputBuffer(cb, NULL, dc, 0, chunk,
201 samplesgot * outsamplesize,
202 time, bitrate, replayGainInfo);
204 } while (samplesgot == samplesreq);
206 flushOutputBuffer(cb);
208 dc->state = DECODE_STATE_STOP;
212 static char *wavpack_tag(WavpackContext *wpc, char *key)
217 size = WavpackGetTagItem(wpc, key, NULL, 0);
220 value = xmalloc(size);
223 WavpackGetTagItem(wpc, key, value, size);
229 static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
231 ReplayGainInfo *replayGainInfo;
235 replayGainInfo = newReplayGainInfo();
237 value = wavpack_tag(wpc, "replaygain_track_gain");
239 replayGainInfo->trackGain = atof(value);
244 value = wavpack_tag(wpc, "replaygain_album_gain");
246 replayGainInfo->albumGain = atof(value);
251 value = wavpack_tag(wpc, "replaygain_track_peak");
253 replayGainInfo->trackPeak = atof(value);
258 value = wavpack_tag(wpc, "replaygain_album_peak");
260 replayGainInfo->albumPeak = atof(value);
267 return replayGainInfo;
269 freeReplayGainInfo(replayGainInfo);
275 * Reads metainfo from the specified file.
277 static MpdTag *wavpack_tagdup(char *fname)
281 char error[ERRORLEN];
286 wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0);
288 ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
294 ERROR("failed to newMpdTag()\n");
299 (float)WavpackGetNumSamples(wpc) / WavpackGetSampleRate(wpc);
304 for (i = 0; tagtypes[i].name != NULL; ++i) {
305 j = WavpackGetTagItem(wpc, tagtypes[i].name, NULL, 0);
311 if (s == NULL) break;
313 } else if (j > ssize) {
314 char *t = (char *)xrealloc(s, j);
315 if (t == NULL) break;
320 WavpackGetTagItem(wpc, tagtypes[i].name, s, j);
321 addItemToMpdTag(tag, tagtypes[i].type, s);
328 WavpackCloseFile(wpc);
334 * mpd InputStream <=> WavpackStreamReader wrapper callbacks
337 /* This struct is needed for per-stream last_byte storage. */
340 /* Needed for push_back_byte() */
344 static int32_t read_bytes(void *id, void *data, int32_t bcount)
346 InputStreamPlus *isp = (InputStreamPlus *)id;
347 uint8_t *buf = (uint8_t *)data;
350 if (isp->last_byte != EOF) {
351 *buf++ = isp->last_byte;
352 isp->last_byte = EOF;
356 return i + readFromInputStream(isp->is, buf, 1, bcount);
359 static uint32_t get_pos(void *id)
361 return ((InputStreamPlus *)id)->is->offset;
364 static int set_pos_abs(void *id, uint32_t pos)
366 return seekInputStream(((InputStreamPlus *)id)->is, pos, SEEK_SET);
369 static int set_pos_rel(void *id, int32_t delta, int mode)
371 return seekInputStream(((InputStreamPlus *)id)->is, delta, mode);
374 static int push_back_byte(void *id, int c)
376 ((InputStreamPlus *)id)->last_byte = c;
380 static uint32_t get_length(void *id)
382 return ((InputStreamPlus *)id)->is->size;
385 static int can_seek(void *id)
387 return ((InputStreamPlus *)id)->is->seekable;
390 static WavpackStreamReader mpd_is_reader = {
391 .read_bytes = read_bytes,
393 .set_pos_abs = set_pos_abs,
394 .set_pos_rel = set_pos_rel,
395 .push_back_byte = push_back_byte,
396 .get_length = get_length,
397 .can_seek = can_seek,
398 .write_bytes = NULL /* no need to write edited tags */
402 initInputStreamPlus(InputStreamPlus *isp, InputStream *is)
405 isp->last_byte = EOF;
409 * Tries to decode the specified stream, and gives true if managed to do it.
411 static unsigned int wavpack_trydecode(InputStream *is)
413 char error[ERRORLEN];
417 initInputStreamPlus(&isp, is);
418 wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, NULL, error,
423 WavpackCloseFile(wpc);
424 /* Seek it back in order to play from the first byte. */
426 seekInputStream(is, 0, SEEK_SET);
434 static int wavpack_streamdecode(OutputBuffer *cb, DecoderControl *dc,
437 char error[ERRORLEN];
440 int open_flags = OPEN_2CH_MAX | OPEN_NORMALIZE;
441 char *wvc_url = NULL;
443 InputStreamPlus isp, isp_wvc;
446 /* Try to find wvc */
452 * As we use dc->utf8url, this function will be bad for
453 * single files. utf8url is not absolute file path :\
455 if (dc->utf8url == NULL)
458 len = strlen(dc->utf8url);
462 wvc_url = (char *)xmalloc(len + 2);
466 strncpy(wvc_url, dc->utf8url, len);
468 wvc_url[len + 1] = '\0';
470 if (openInputStream(&is_wvc, wvc_url))
472 #if 0 /* this code is moved to http stuff */
474 * And we try to buffer in order to get know
475 * about a possible 404.
478 if (inputStreamAtEOF(&is_wvc))
480 * EOF is reached even without
481 * reading a single byte...
482 * So, this is not good :\
486 if (bufferInputStream(&is_wvc) >= 0) {
491 /* Can this happen?: */
500 closeInputStream(&is_wvc);
508 canseek = can_seek(&isp);
509 if (wvc_url != NULL) {
514 initInputStreamPlus(&isp_wvc, &is_wvc);
515 open_flags |= OPEN_WVC;
516 if (!can_seek(&isp_wvc))
522 open_flags |= OPEN_STREAMING;
524 initInputStreamPlus(&isp, is);
525 wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, &isp_wvc, error,
528 ERROR("failed to open WavPack stream: %s\n", error);
532 wavpack_decode(cb, dc, wpc, canseek, NULL);
534 WavpackCloseFile(wpc);
535 if (wvc_url != NULL) {
536 closeInputStream(&is_wvc);
539 closeInputStream(is); /* calling side doesn't do this in mpd 0.13.0 */
547 static int wavpack_filedecode(OutputBuffer *cb, DecoderControl *dc, char *fname)
549 char error[ERRORLEN];
551 ReplayGainInfo *replayGainInfo;
553 wpc = WavpackOpenFileInput(fname, error,
554 OPEN_TAGS | OPEN_WVC |
555 OPEN_2CH_MAX | OPEN_NORMALIZE, 15);
557 ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
561 replayGainInfo = wavpack_replaygain(wpc);
563 wavpack_decode(cb, dc, wpc, 1, replayGainInfo);
566 freeReplayGainInfo(replayGainInfo);
568 WavpackCloseFile(wpc);
573 static char *wavpackSuffixes[] = { "wv", NULL };
574 static char *wavpackMimeTypes[] = { "audio/x-wavpack", NULL };
576 InputPlugin wavpackPlugin = {
580 /* Disabled till the http seek bug is fixed. */
582 wavpack_streamdecode,
585 INPUT_PLUGIN_STREAM_FILE | INPUT_PLUGIN_STREAM_URL,
590 #else /* !HAVE_WAVPACK */
592 InputPlugin wavpackPlugin;
594 #endif /* !HAVE_WAVPACK */