2007-06-24 22:40:04 +02:00
|
|
|
/* the Music Player Daemon (MPD)
|
|
|
|
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
|
|
|
* This project's homepage is: http://www.musicpd.org
|
|
|
|
*
|
2007-06-25 14:13:45 +02:00
|
|
|
* WavPack support added by Laszlo Ashin <kodest@gmail.com>
|
2007-06-24 22:40:04 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2008-08-26 08:27:04 +02:00
|
|
|
#include "../decoder_api.h"
|
2007-06-24 22:40:04 +02:00
|
|
|
#include "../utils.h"
|
|
|
|
#include "../log.h"
|
2008-04-12 06:19:26 +02:00
|
|
|
#include "../path.h"
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
#include <wavpack/wavpack.h>
|
|
|
|
|
2008-08-26 08:41:05 +02:00
|
|
|
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
|
|
|
|
#define CHUNK_SIZE 1020
|
|
|
|
|
2007-06-24 22:40:04 +02:00
|
|
|
#define ERRORLEN 80
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
const char *name;
|
|
|
|
int type;
|
|
|
|
} tagtypes[] = {
|
|
|
|
{ "artist", TAG_ITEM_ARTIST },
|
|
|
|
{ "album", TAG_ITEM_ALBUM },
|
|
|
|
{ "title", TAG_ITEM_TITLE },
|
|
|
|
{ "track", TAG_ITEM_TRACK },
|
|
|
|
{ "name", TAG_ITEM_NAME },
|
|
|
|
{ "genre", TAG_ITEM_GENRE },
|
|
|
|
{ "date", TAG_ITEM_DATE },
|
|
|
|
{ "composer", TAG_ITEM_COMPOSER },
|
|
|
|
{ "performer", TAG_ITEM_PERFORMER },
|
|
|
|
{ "comment", TAG_ITEM_COMMENT },
|
|
|
|
{ "disc", TAG_ITEM_DISC },
|
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function has been borrowed from the tiny player found on
|
|
|
|
* wavpack.com. Modifications were required because mpd only handles
|
|
|
|
* max 16 bit samples.
|
|
|
|
*/
|
|
|
|
static void format_samples_int(int Bps, void *buffer, uint32_t samcnt)
|
|
|
|
{
|
|
|
|
int32_t temp;
|
|
|
|
uchar *dst = (uchar *)buffer;
|
|
|
|
int32_t *src = (int32_t *)buffer;
|
|
|
|
|
|
|
|
switch (Bps) {
|
|
|
|
case 1:
|
|
|
|
while (samcnt--)
|
|
|
|
*dst++ = *src++;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
while (samcnt--) {
|
2007-10-03 18:11:01 +02:00
|
|
|
temp = *src++;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
*dst++ = (uchar)(temp >> 8);
|
|
|
|
*dst++ = (uchar)(temp);
|
|
|
|
#else
|
|
|
|
*dst++ = (uchar)(temp);
|
2007-06-24 22:40:04 +02:00
|
|
|
*dst++ = (uchar)(temp >> 8);
|
2007-10-03 18:11:01 +02:00
|
|
|
#endif
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* downscale to 16 bits */
|
|
|
|
while (samcnt--) {
|
|
|
|
temp = *src++;
|
2007-10-03 18:11:01 +02:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
*dst++ = (uchar)(temp >> 16);
|
|
|
|
*dst++ = (uchar)(temp >> 8);
|
|
|
|
#else
|
2007-06-24 22:40:04 +02:00
|
|
|
*dst++ = (uchar)(temp >> 8);
|
|
|
|
*dst++ = (uchar)(temp >> 16);
|
2007-10-03 18:11:01 +02:00
|
|
|
#endif
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* downscale to 16 bits */
|
|
|
|
while (samcnt--) {
|
|
|
|
temp = *src++;
|
2007-10-03 18:11:01 +02:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
*dst++ = (uchar)(temp >> 24);
|
|
|
|
*dst++ = (uchar)(temp >> 16);
|
|
|
|
|
|
|
|
#else
|
2007-06-24 22:40:04 +02:00
|
|
|
*dst++ = (uchar)(temp >> 16);
|
|
|
|
*dst++ = (uchar)(temp >> 24);
|
2007-10-03 18:11:01 +02:00
|
|
|
#endif
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function converts floating point sample data to 16 bit integer.
|
|
|
|
*/
|
2008-08-26 08:27:02 +02:00
|
|
|
static void format_samples_float(mpd_unused int Bps, void *buffer,
|
|
|
|
uint32_t samcnt)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
|
|
|
int16_t *dst = (int16_t *)buffer;
|
|
|
|
float *src = (float *)buffer;
|
|
|
|
|
|
|
|
while (samcnt--) {
|
|
|
|
*dst++ = (int16_t)(*src++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This does the main decoding thing.
|
|
|
|
* Requires an already opened WavpackContext.
|
|
|
|
*/
|
2008-08-26 08:27:04 +02:00
|
|
|
static void wavpack_decode(struct decoder * decoder,
|
2008-08-26 08:27:04 +02:00
|
|
|
WavpackContext *wpc, int canseek,
|
2007-06-25 15:37:21 +02:00
|
|
|
ReplayGainInfo *replayGainInfo)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
2008-09-07 19:19:55 +02:00
|
|
|
struct audio_format audio_format;
|
2007-06-24 22:40:04 +02:00
|
|
|
void (*format_samples)(int Bps, void *buffer, uint32_t samcnt);
|
|
|
|
char chunk[CHUNK_SIZE];
|
2008-04-12 06:16:32 +02:00
|
|
|
float file_time;
|
2007-06-24 22:40:04 +02:00
|
|
|
int samplesreq, samplesgot;
|
|
|
|
int allsamples;
|
|
|
|
int position, outsamplesize;
|
|
|
|
int Bps;
|
|
|
|
|
2008-10-10 14:40:54 +02:00
|
|
|
audio_format.sample_rate = WavpackGetSampleRate(wpc);
|
2008-08-26 08:27:05 +02:00
|
|
|
audio_format.channels = WavpackGetReducedChannels(wpc);
|
|
|
|
audio_format.bits = WavpackGetBitsPerSample(wpc);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:05 +02:00
|
|
|
if (audio_format.bits > 16)
|
|
|
|
audio_format.bits = 16;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT)
|
|
|
|
format_samples = format_samples_float;
|
|
|
|
else
|
|
|
|
format_samples = format_samples_int;
|
2008-03-26 11:38:30 +01:00
|
|
|
/*
|
|
|
|
if ((WavpackGetMode(wpc) & MODE_WVC) == MODE_WVC)
|
|
|
|
ERROR("decoding WITH wvc!!!\n");
|
|
|
|
else
|
|
|
|
ERROR("decoding without wvc\n");
|
|
|
|
*/
|
2007-06-24 22:40:04 +02:00
|
|
|
allsamples = WavpackGetNumSamples(wpc);
|
|
|
|
Bps = WavpackGetBytesPerSample(wpc);
|
|
|
|
|
|
|
|
outsamplesize = Bps;
|
|
|
|
if (outsamplesize > 2)
|
|
|
|
outsamplesize = 2;
|
2008-08-26 08:27:05 +02:00
|
|
|
outsamplesize *= audio_format.channels;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:05 +02:00
|
|
|
samplesreq = sizeof(chunk) / (4 * audio_format.channels);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:05 +02:00
|
|
|
decoder_initialized(decoder, &audio_format,
|
2008-10-10 14:40:54 +02:00
|
|
|
(float)allsamples / audio_format.sample_rate);
|
2008-08-26 08:27:04 +02:00
|
|
|
|
2007-06-24 22:40:04 +02:00
|
|
|
position = 0;
|
|
|
|
|
|
|
|
do {
|
2008-08-26 08:27:07 +02:00
|
|
|
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) {
|
2007-06-24 22:40:04 +02:00
|
|
|
if (canseek) {
|
|
|
|
int where;
|
|
|
|
|
2008-08-26 08:27:07 +02:00
|
|
|
where = decoder_seek_where(decoder) *
|
2008-10-10 14:40:54 +02:00
|
|
|
audio_format.sample_rate;
|
2008-08-26 08:27:07 +02:00
|
|
|
if (WavpackSeekSample(wpc, where)) {
|
2007-06-24 22:40:04 +02:00
|
|
|
position = where;
|
2008-08-26 08:27:07 +02:00
|
|
|
decoder_command_finished(decoder);
|
|
|
|
} else
|
|
|
|
decoder_seek_error(decoder);
|
2007-06-24 22:40:04 +02:00
|
|
|
} else {
|
2008-08-26 08:27:07 +02:00
|
|
|
decoder_seek_error(decoder);
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-26 08:27:07 +02:00
|
|
|
if (decoder_get_command(decoder) == DECODE_COMMAND_STOP)
|
2007-06-24 22:40:04 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
samplesgot = WavpackUnpackSamples(wpc,
|
|
|
|
(int32_t *)chunk, samplesreq);
|
|
|
|
if (samplesgot > 0) {
|
|
|
|
int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
|
|
|
|
1000 + 0.5);
|
|
|
|
position += samplesgot;
|
2008-10-10 14:40:54 +02:00
|
|
|
file_time = (float)position / audio_format.sample_rate;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
format_samples(Bps, chunk,
|
2008-08-26 08:27:05 +02:00
|
|
|
samplesgot * audio_format.channels);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:05 +02:00
|
|
|
decoder_data(decoder, NULL, 0, chunk,
|
|
|
|
samplesgot * outsamplesize,
|
|
|
|
file_time, bitrate,
|
|
|
|
replayGainInfo);
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
} while (samplesgot == samplesreq);
|
|
|
|
}
|
|
|
|
|
2007-06-25 15:37:21 +02:00
|
|
|
static char *wavpack_tag(WavpackContext *wpc, char *key)
|
|
|
|
{
|
|
|
|
char *value = NULL;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
size = WavpackGetTagItem(wpc, key, NULL, 0);
|
|
|
|
if (size > 0) {
|
|
|
|
size++;
|
|
|
|
value = xmalloc(size);
|
|
|
|
if (!value)
|
|
|
|
return NULL;
|
|
|
|
WavpackGetTagItem(wpc, key, value, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
|
|
|
|
{
|
2008-09-07 19:14:39 +02:00
|
|
|
static char replaygain_track_gain[] = "replaygain_track_gain";
|
|
|
|
static char replaygain_album_gain[] = "replaygain_album_gain";
|
|
|
|
static char replaygain_track_peak[] = "replaygain_track_peak";
|
|
|
|
static char replaygain_album_peak[] = "replaygain_album_peak";
|
2007-06-25 15:37:21 +02:00
|
|
|
ReplayGainInfo *replayGainInfo;
|
2008-10-30 08:38:54 +01:00
|
|
|
bool found = false;
|
2007-06-25 15:37:21 +02:00
|
|
|
char *value;
|
|
|
|
|
|
|
|
replayGainInfo = newReplayGainInfo();
|
|
|
|
|
2008-09-07 19:14:39 +02:00
|
|
|
value = wavpack_tag(wpc, replaygain_track_gain);
|
2007-06-25 15:37:21 +02:00
|
|
|
if (value) {
|
|
|
|
replayGainInfo->trackGain = atof(value);
|
|
|
|
free(value);
|
2008-10-30 08:38:54 +01:00
|
|
|
found = true;
|
2007-06-25 15:37:21 +02:00
|
|
|
}
|
|
|
|
|
2008-09-07 19:14:39 +02:00
|
|
|
value = wavpack_tag(wpc, replaygain_album_gain);
|
2007-06-25 15:37:21 +02:00
|
|
|
if (value) {
|
|
|
|
replayGainInfo->albumGain = atof(value);
|
|
|
|
free(value);
|
2008-10-30 08:38:54 +01:00
|
|
|
found = true;
|
2007-06-25 15:37:21 +02:00
|
|
|
}
|
|
|
|
|
2008-09-07 19:14:39 +02:00
|
|
|
value = wavpack_tag(wpc, replaygain_track_peak);
|
2007-06-25 15:37:21 +02:00
|
|
|
if (value) {
|
|
|
|
replayGainInfo->trackPeak = atof(value);
|
|
|
|
free(value);
|
2008-10-30 08:38:54 +01:00
|
|
|
found = true;
|
2007-06-25 15:37:21 +02:00
|
|
|
}
|
|
|
|
|
2008-09-07 19:14:39 +02:00
|
|
|
value = wavpack_tag(wpc, replaygain_album_peak);
|
2007-06-25 15:37:21 +02:00
|
|
|
if (value) {
|
|
|
|
replayGainInfo->albumPeak = atof(value);
|
|
|
|
free(value);
|
2008-10-30 08:38:54 +01:00
|
|
|
found = true;
|
2007-06-25 15:37:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
return replayGainInfo;
|
|
|
|
|
|
|
|
freeReplayGainInfo(replayGainInfo);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-06-24 22:40:04 +02:00
|
|
|
/*
|
|
|
|
* Reads metainfo from the specified file.
|
|
|
|
*/
|
2008-10-31 15:56:43 +01:00
|
|
|
static struct tag *wavpack_tagdup(const char *fname)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
|
|
|
WavpackContext *wpc;
|
2008-08-29 09:38:11 +02:00
|
|
|
struct tag *tag;
|
2007-06-24 22:40:04 +02:00
|
|
|
char error[ERRORLEN];
|
|
|
|
char *s;
|
|
|
|
int ssize;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0);
|
|
|
|
if (wpc == NULL) {
|
|
|
|
ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-29 09:38:21 +02:00
|
|
|
tag = tag_new();
|
2007-06-24 22:40:04 +02:00
|
|
|
tag->time =
|
|
|
|
(float)WavpackGetNumSamples(wpc) / WavpackGetSampleRate(wpc);
|
|
|
|
|
|
|
|
ssize = 0;
|
|
|
|
s = NULL;
|
|
|
|
|
|
|
|
for (i = 0; tagtypes[i].name != NULL; ++i) {
|
|
|
|
j = WavpackGetTagItem(wpc, tagtypes[i].name, NULL, 0);
|
|
|
|
if (j > 0) {
|
|
|
|
++j;
|
|
|
|
|
|
|
|
if (s == NULL) {
|
|
|
|
s = xmalloc(j);
|
|
|
|
if (s == NULL) break;
|
|
|
|
ssize = j;
|
|
|
|
} else if (j > ssize) {
|
|
|
|
char *t = (char *)xrealloc(s, j);
|
|
|
|
if (t == NULL) break;
|
|
|
|
ssize = j;
|
|
|
|
s = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
WavpackGetTagItem(wpc, tagtypes[i].name, s, j);
|
2008-08-29 09:38:21 +02:00
|
|
|
tag_add_item(tag, tagtypes[i].type, s);
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s != NULL)
|
|
|
|
free(s);
|
|
|
|
|
|
|
|
WavpackCloseFile(wpc);
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-10-26 19:54:57 +01:00
|
|
|
* mpd input_stream <=> WavpackStreamReader wrapper callbacks
|
2007-06-24 22:40:04 +02:00
|
|
|
*/
|
|
|
|
|
2008-03-26 11:38:30 +01:00
|
|
|
/* This struct is needed for per-stream last_byte storage. */
|
|
|
|
typedef struct {
|
2008-08-26 08:27:14 +02:00
|
|
|
struct decoder *decoder;
|
2008-10-26 19:54:57 +01:00
|
|
|
struct input_stream *is;
|
2008-03-26 11:38:30 +01:00
|
|
|
/* Needed for push_back_byte() */
|
|
|
|
int last_byte;
|
|
|
|
} InputStreamPlus;
|
|
|
|
|
2007-06-24 22:40:04 +02:00
|
|
|
static int32_t read_bytes(void *id, void *data, int32_t bcount)
|
|
|
|
{
|
2008-03-26 11:38:30 +01:00
|
|
|
InputStreamPlus *isp = (InputStreamPlus *)id;
|
2007-06-24 22:40:04 +02:00
|
|
|
uint8_t *buf = (uint8_t *)data;
|
|
|
|
int32_t i = 0;
|
|
|
|
|
2008-03-26 11:38:30 +01:00
|
|
|
if (isp->last_byte != EOF) {
|
|
|
|
*buf++ = isp->last_byte;
|
|
|
|
isp->last_byte = EOF;
|
2007-06-24 22:40:04 +02:00
|
|
|
--bcount;
|
|
|
|
++i;
|
|
|
|
}
|
2008-08-26 08:27:14 +02:00
|
|
|
return i + decoder_read(isp->decoder, isp->is, buf, bcount);
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t get_pos(void *id)
|
|
|
|
{
|
2008-03-26 11:38:30 +01:00
|
|
|
return ((InputStreamPlus *)id)->is->offset;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int set_pos_abs(void *id, uint32_t pos)
|
|
|
|
{
|
2008-10-26 20:56:46 +01:00
|
|
|
return input_stream_seek(((InputStreamPlus *)id)->is, pos, SEEK_SET)
|
|
|
|
? 0 : -1;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int set_pos_rel(void *id, int32_t delta, int mode)
|
|
|
|
{
|
2008-10-26 20:56:46 +01:00
|
|
|
return input_stream_seek(((InputStreamPlus *)id)->is, delta, mode)
|
|
|
|
? 0 : -1;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int push_back_byte(void *id, int c)
|
|
|
|
{
|
2008-03-26 11:38:30 +01:00
|
|
|
((InputStreamPlus *)id)->last_byte = c;
|
2007-06-24 22:40:04 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t get_length(void *id)
|
|
|
|
{
|
2008-03-26 11:38:30 +01:00
|
|
|
return ((InputStreamPlus *)id)->is->size;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int can_seek(void *id)
|
|
|
|
{
|
2008-03-26 11:38:30 +01:00
|
|
|
return ((InputStreamPlus *)id)->is->seekable;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static WavpackStreamReader mpd_is_reader = {
|
|
|
|
.read_bytes = read_bytes,
|
|
|
|
.get_pos = get_pos,
|
|
|
|
.set_pos_abs = set_pos_abs,
|
|
|
|
.set_pos_rel = set_pos_rel,
|
|
|
|
.push_back_byte = push_back_byte,
|
|
|
|
.get_length = get_length,
|
|
|
|
.can_seek = can_seek,
|
|
|
|
.write_bytes = NULL /* no need to write edited tags */
|
|
|
|
};
|
|
|
|
|
2008-03-26 11:38:30 +01:00
|
|
|
static void
|
2008-08-26 08:27:14 +02:00
|
|
|
initInputStreamPlus(InputStreamPlus *isp, struct decoder *decoder,
|
2008-10-26 19:54:57 +01:00
|
|
|
struct input_stream *is)
|
2008-03-26 11:38:30 +01:00
|
|
|
{
|
2008-08-26 08:27:14 +02:00
|
|
|
isp->decoder = decoder;
|
2008-03-26 11:38:30 +01:00
|
|
|
isp->is = is;
|
|
|
|
isp->last_byte = EOF;
|
|
|
|
}
|
|
|
|
|
2007-06-24 22:40:04 +02:00
|
|
|
/*
|
|
|
|
* Tries to decode the specified stream, and gives true if managed to do it.
|
|
|
|
*/
|
2008-10-26 19:54:57 +01:00
|
|
|
static bool wavpack_trydecode(struct input_stream *is)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
|
|
|
char error[ERRORLEN];
|
|
|
|
WavpackContext *wpc;
|
2008-03-26 11:38:30 +01:00
|
|
|
InputStreamPlus isp;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
initInputStreamPlus(&isp, NULL, is);
|
2008-03-26 11:38:30 +01:00
|
|
|
wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, NULL, error,
|
2007-06-24 22:40:04 +02:00
|
|
|
OPEN_STREAMING, 0);
|
|
|
|
if (wpc == NULL)
|
2008-10-08 11:03:39 +02:00
|
|
|
return false;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
WavpackCloseFile(wpc);
|
|
|
|
/* Seek it back in order to play from the first byte. */
|
2008-10-26 20:34:47 +01:00
|
|
|
input_stream_seek(is, 0, SEEK_SET);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-10-08 11:03:39 +02:00
|
|
|
return true;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
static int wavpack_open_wvc(struct decoder *decoder,
|
2008-10-26 19:54:57 +01:00
|
|
|
struct input_stream *is_wvc)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
2008-08-26 08:27:14 +02:00
|
|
|
char tmp[MPD_PATH_MAX];
|
|
|
|
const char *utf8url;
|
|
|
|
size_t len;
|
2008-03-26 11:38:30 +01:00
|
|
|
char *wvc_url = NULL;
|
2008-10-26 20:56:46 +01:00
|
|
|
bool ret;
|
2008-08-26 08:27:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* As we use dc->utf8url, this function will be bad for
|
|
|
|
* single files. utf8url is not absolute file path :/
|
|
|
|
*/
|
|
|
|
utf8url = decoder_get_url(decoder, tmp);
|
|
|
|
if (utf8url == NULL)
|
|
|
|
return 0;
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
len = strlen(utf8url);
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
wvc_url = (char *)xmalloc(len + 2); /* +2: 'c' and EOS */
|
|
|
|
if (wvc_url == NULL)
|
|
|
|
return 0;
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
memcpy(wvc_url, utf8url, len);
|
|
|
|
wvc_url[len] = 'c';
|
|
|
|
wvc_url[len + 1] = '\0';
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-10-26 20:34:47 +01:00
|
|
|
ret = input_stream_open(is_wvc, wvc_url);
|
2008-08-26 08:27:14 +02:00
|
|
|
free(wvc_url);
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-10-26 20:56:46 +01:00
|
|
|
if (!ret)
|
2008-08-26 08:27:14 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And we try to buffer in order to get know
|
|
|
|
* about a possible 404 error.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
2008-10-26 20:34:47 +01:00
|
|
|
if (input_stream_eof(is_wvc)) {
|
2008-08-26 08:27:14 +02:00
|
|
|
/*
|
|
|
|
* EOF is reached even without
|
|
|
|
* a single byte is read...
|
|
|
|
* So, this is not good :/
|
|
|
|
*/
|
2008-10-26 20:34:47 +01:00
|
|
|
input_stream_close(is_wvc);
|
2008-08-26 08:27:14 +02:00
|
|
|
return 0;
|
2008-03-26 11:38:30 +01:00
|
|
|
}
|
|
|
|
|
2008-10-26 20:34:47 +01:00
|
|
|
if (input_stream_buffer(is_wvc) >= 0)
|
2008-08-26 08:27:14 +02:00
|
|
|
return 1;
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:15 +02:00
|
|
|
if (decoder_get_command(decoder) != DECODE_COMMAND_NONE) {
|
2008-10-26 20:34:47 +01:00
|
|
|
input_stream_close(is_wvc);
|
2008-08-26 08:27:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
/* Save some CPU */
|
|
|
|
my_usleep(1000);
|
|
|
|
}
|
|
|
|
}
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
/*
|
|
|
|
* Decodes a stream.
|
|
|
|
*/
|
2008-10-30 08:38:54 +01:00
|
|
|
static bool
|
2008-10-26 19:54:57 +01:00
|
|
|
wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
|
2008-08-26 08:27:14 +02:00
|
|
|
{
|
|
|
|
char error[ERRORLEN];
|
|
|
|
WavpackContext *wpc;
|
2008-10-26 19:54:57 +01:00
|
|
|
struct input_stream is_wvc;
|
2008-08-26 08:27:14 +02:00
|
|
|
int open_flags = OPEN_2CH_MAX | OPEN_NORMALIZE /*| OPEN_STREAMING*/;
|
|
|
|
InputStreamPlus isp, isp_wvc;
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
if (wavpack_open_wvc(decoder, &is_wvc)) {
|
|
|
|
initInputStreamPlus(&isp_wvc, decoder, &is_wvc);
|
2008-03-26 11:38:30 +01:00
|
|
|
open_flags |= OPEN_WVC;
|
2008-08-26 08:27:14 +02:00
|
|
|
}
|
2008-03-26 11:38:30 +01:00
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
initInputStreamPlus(&isp, decoder, is);
|
2008-03-26 11:38:30 +01:00
|
|
|
wpc = WavpackOpenFileInputEx(&mpd_is_reader, &isp, &isp_wvc, error,
|
|
|
|
open_flags, 15);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
if (wpc == NULL) {
|
|
|
|
ERROR("failed to open WavPack stream: %s\n", error);
|
2008-10-30 08:38:54 +01:00
|
|
|
return false;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2008-08-26 08:27:14 +02:00
|
|
|
wavpack_decode(decoder, wpc, can_seek(&isp), NULL);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
WavpackCloseFile(wpc);
|
2008-08-26 08:27:14 +02:00
|
|
|
if (open_flags & OPEN_WVC)
|
2008-10-26 20:34:47 +01:00
|
|
|
input_stream_close(&is_wvc);
|
|
|
|
input_stream_close(is);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-10-30 08:38:54 +01:00
|
|
|
return true;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-03-26 11:38:30 +01:00
|
|
|
* Decodes a file.
|
2007-06-24 22:40:04 +02:00
|
|
|
*/
|
2008-10-30 08:38:54 +01:00
|
|
|
static bool
|
2008-10-31 15:56:43 +01:00
|
|
|
wavpack_filedecode(struct decoder *decoder, const char *fname)
|
2007-06-24 22:40:04 +02:00
|
|
|
{
|
|
|
|
char error[ERRORLEN];
|
|
|
|
WavpackContext *wpc;
|
2007-06-25 15:37:21 +02:00
|
|
|
ReplayGainInfo *replayGainInfo;
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
wpc = WavpackOpenFileInput(fname, error,
|
2007-06-25 16:24:30 +02:00
|
|
|
OPEN_TAGS | OPEN_WVC |
|
|
|
|
OPEN_2CH_MAX | OPEN_NORMALIZE, 15);
|
2007-06-24 22:40:04 +02:00
|
|
|
if (wpc == NULL) {
|
|
|
|
ERROR("failed to open WavPack file \"%s\": %s\n", fname, error);
|
2008-10-30 08:38:54 +01:00
|
|
|
return false;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2007-06-25 16:24:30 +02:00
|
|
|
replayGainInfo = wavpack_replaygain(wpc);
|
2007-06-25 15:37:21 +02:00
|
|
|
|
2008-08-26 08:27:04 +02:00
|
|
|
wavpack_decode(decoder, wpc, 1, replayGainInfo);
|
2007-06-25 15:37:21 +02:00
|
|
|
|
|
|
|
if (replayGainInfo)
|
|
|
|
freeReplayGainInfo(replayGainInfo);
|
2007-06-24 22:40:04 +02:00
|
|
|
|
|
|
|
WavpackCloseFile(wpc);
|
|
|
|
|
2008-10-30 08:38:54 +01:00
|
|
|
return true;
|
2007-06-24 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2008-11-01 14:55:23 +01:00
|
|
|
static char const *const wavpackSuffixes[] = { "wv", NULL };
|
|
|
|
static char const *const wavpackMimeTypes[] = { "audio/x-wavpack", NULL };
|
2007-06-24 22:40:04 +02:00
|
|
|
|
2008-11-01 14:54:09 +01:00
|
|
|
const struct decoder_plugin wavpackPlugin = {
|
2008-09-29 15:55:17 +02:00
|
|
|
.name = "wavpack",
|
|
|
|
.try_decode = wavpack_trydecode,
|
|
|
|
.stream_decode = wavpack_streamdecode,
|
|
|
|
.file_decode = wavpack_filedecode,
|
|
|
|
.tag_dup = wavpack_tagdup,
|
|
|
|
.stream_types = INPUT_PLUGIN_STREAM_FILE | INPUT_PLUGIN_STREAM_URL,
|
|
|
|
.suffixes = wavpackSuffixes,
|
|
|
|
.mime_types = wavpackMimeTypes
|
2007-06-24 22:40:04 +02:00
|
|
|
};
|