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
;
80 #ifdef WORDS_BIGENDIAN
81 *dst
++ = (uchar
)(temp
>> 8);
82 *dst
++ = (uchar
)(temp
);
84 *dst
++ = (uchar
)(temp
);
85 *dst
++ = (uchar
)(temp
>> 8);
90 /* downscale to 16 bits */
93 *dst
++ = (uchar
)(temp
>> 8);
94 *dst
++ = (uchar
)(temp
>> 16);
95 #ifdef WORDS_BIGENDIAN
96 *dst
++ = (uchar
)(temp
>> 16);
97 *dst
++ = (uchar
)(temp
>> 8);
100 *dst
++ = (uchar
)(temp
>> 8);
101 *dst
++ = (uchar
)(temp
>> 16);
106 /* downscale to 16 bits */
109 #ifdef WORDS_BIGENDIAN
110 *dst
++ = (uchar
)(temp
>> 24);
111 *dst
++ = (uchar
)(temp
>> 16);
114 *dst
++ = (uchar
)(temp
>> 16);
115 *dst
++ = (uchar
)(temp
>> 24);
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
;
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
];
146 int samplesreq
, samplesgot
;
148 int position
, outsamplesize
;
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
;
161 format_samples
= format_samples_int
;
163 if ((WavpackGetMode(wpc) & MODE_WVC) == MODE_WVC)
164 ERROR("decoding WITH wvc!!!\n");
166 ERROR("decoding without wvc\n");
168 allsamples
= WavpackGetNumSamples(wpc
);
169 Bps
= WavpackGetBytesPerSample(wpc
);
172 if (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
;
191 clearOutputBuffer(cb
);
193 where
= dc
->seekWhere
*
194 dc
->audioFormat
.sampleRate
;
195 if (WavpackSeekSample(wpc
, where
))
209 samplesgot
= WavpackUnpackSamples(wpc
,
210 (int32_t *)chunk
, samplesreq
);
211 if (samplesgot
> 0) {
212 int bitrate
= (int)(WavpackGetInstantBitrate(wpc
) /
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
;
232 static char *wavpack_tag(WavpackContext
*wpc
, char *key
)
237 size
= WavpackGetTagItem(wpc
, key
, NULL
, 0);
240 value
= xmalloc(size
);
243 WavpackGetTagItem(wpc
, key
, value
, size
);
249 static ReplayGainInfo
*wavpack_replaygain(WavpackContext
*wpc
)
251 ReplayGainInfo
*replayGainInfo
;
255 replayGainInfo
= newReplayGainInfo();
257 value
= wavpack_tag(wpc
, "replaygain_track_gain");
259 replayGainInfo
->trackGain
= atof(value
);
264 value
= wavpack_tag(wpc
, "replaygain_album_gain");
266 replayGainInfo
->albumGain
= atof(value
);
271 value
= wavpack_tag(wpc
, "replaygain_track_peak");
273 replayGainInfo
->trackPeak
= atof(value
);
278 value
= wavpack_tag(wpc
, "replaygain_album_peak");
280 replayGainInfo
->albumPeak
= atof(value
);
287 return replayGainInfo
;
289 freeReplayGainInfo(replayGainInfo
);
295 * Reads metainfo from the specified file.
297 static MpdTag
*wavpack_tagdup(char *fname
)
301 char error
[ERRORLEN
];
306 wpc
= WavpackOpenFileInput(fname
, error
, OPEN_TAGS
, 0);
308 ERROR("failed to open WavPack file \"%s\": %s\n", fname
, error
);
314 ERROR("failed to newMpdTag()\n");
319 (float)WavpackGetNumSamples(wpc
) / WavpackGetSampleRate(wpc
);
324 for (i
= 0; tagtypes
[i
].name
!= NULL
; ++i
) {
325 j
= WavpackGetTagItem(wpc
, tagtypes
[i
].name
, NULL
, 0);
331 if (s
== NULL
) break;
333 } else if (j
> ssize
) {
334 char *t
= (char *)xrealloc(s
, j
);
335 if (t
== NULL
) break;
340 WavpackGetTagItem(wpc
, tagtypes
[i
].name
, s
, j
);
341 addItemToMpdTag(tag
, tagtypes
[i
].type
, s
);
348 WavpackCloseFile(wpc
);
354 * mpd InputStream <=> WavpackStreamReader wrapper callbacks
357 /* This struct is needed for per-stream last_byte storage. */
360 /* Needed for push_back_byte() */
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
;
370 if (isp
->last_byte
!= EOF
) {
371 *buf
++ = isp
->last_byte
;
372 isp
->last_byte
= EOF
;
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
;
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
,
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 */
422 initInputStreamPlus(InputStreamPlus
*isp
, InputStream
*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
];
437 initInputStreamPlus(&isp
, is
);
438 wpc
= WavpackOpenFileInputEx(&mpd_is_reader
, &isp
, NULL
, error
,
443 WavpackCloseFile(wpc
);
444 /* Seek it back in order to play from the first byte. */
446 seekInputStream(is
, 0, SEEK_SET
);
454 static int wavpack_streamdecode(OutputBuffer
*cb
, DecoderControl
*dc
,
457 char error
[ERRORLEN
];
460 int open_flags
= OPEN_2CH_MAX
| OPEN_NORMALIZE
;
461 char *wvc_url
= NULL
;
463 InputStreamPlus isp
, isp_wvc
;
466 /* Try to find wvc */
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
)
478 len
= strlen(dc
->utf8url
);
482 wvc_url
= (char *)xmalloc(len
+ 2);
486 strncpy(wvc_url
, dc
->utf8url
, len
);
488 wvc_url
[len
+ 1] = '\0';
490 if (openInputStream(&is_wvc
, wvc_url
))
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.
498 if (inputStreamAtEOF(&is_wvc
))
500 * EOF is reached even without
501 * reading a single byte...
502 * So, this is not good :\
506 if (bufferInputStream(&is_wvc
) >= 0) {
511 /* Can this happen?: */
520 closeInputStream(&is_wvc
);
528 canseek
= can_seek(&isp
);
529 if (wvc_url
!= NULL
) {
534 initInputStreamPlus(&isp_wvc
, &is_wvc
);
535 open_flags
|= OPEN_WVC
;
536 if (!can_seek(&isp_wvc
))
542 open_flags
|= OPEN_STREAMING
;
544 initInputStreamPlus(&isp
, is
);
545 wpc
= WavpackOpenFileInputEx(&mpd_is_reader
, &isp
, &isp_wvc
, error
,
548 ERROR("failed to open WavPack stream: %s\n", error
);
552 wavpack_decode(cb
, dc
, wpc
, canseek
, NULL
);
554 WavpackCloseFile(wpc
);
555 if (wvc_url
!= NULL
) {
556 closeInputStream(&is_wvc
);
559 closeInputStream(is
); /* calling side doesn't do this in mpd 0.13.0 */
567 static int wavpack_filedecode(OutputBuffer
*cb
, DecoderControl
*dc
, char *fname
)
569 char error
[ERRORLEN
];
571 ReplayGainInfo
*replayGainInfo
;
573 wpc
= WavpackOpenFileInput(fname
, error
,
574 OPEN_TAGS
| OPEN_WVC
|
575 OPEN_2CH_MAX
| OPEN_NORMALIZE
, 15);
577 ERROR("failed to open WavPack file \"%s\": %s\n", fname
, error
);
581 replayGainInfo
= wavpack_replaygain(wpc
);
583 wavpack_decode(cb
, dc
, wpc
, 1, replayGainInfo
);
586 freeReplayGainInfo(replayGainInfo
);
588 WavpackCloseFile(wpc
);
593 static char *wavpackSuffixes
[] = { "wv", NULL
};
594 static char *wavpackMimeTypes
[] = { "audio/x-wavpack", NULL
};
596 InputPlugin wavpackPlugin
= {
600 /* Disabled till the http seek bug is fixed. */
602 wavpack_streamdecode
,
605 INPUT_PLUGIN_STREAM_FILE
| INPUT_PLUGIN_STREAM_URL
,
610 #else /* !HAVE_WAVPACK */
612 InputPlugin wavpackPlugin
;
614 #endif /* !HAVE_WAVPACK */