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 */