1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2007 Johannes Weißl
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License as
7
 * published by the Free Software Foundation; either version 2 of the
8
 * License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful, but
11
 * WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
#include "ip.h"
20
#include "ape.h"
21
#include "id3.h"
22
#include "xmalloc.h"
23
#include "read_wrapper.h"
24
#include "debug.h"
25
#include "buffer.h"
26
#include "comment.h"
27
28
#include <wavpack/wavpack.h>
29
30
#include <errno.h>
31
#include <stdio.h>
32
#include <string.h>
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <unistd.h>
36
#include <fcntl.h>
37
38
#define WV_CHANNEL_MAX 2
39
40
struct wavpack_file {
41
	int fd;
42
	off_t len;
43
	int push_back_byte;
44
};
45
46
struct wavpack_private {
47
	WavpackContext *wpc;
48
	int32_t samples[CHUNK_SIZE * WV_CHANNEL_MAX];
49
	struct wavpack_file wv_file;
50
	struct wavpack_file wvc_file;
51
	unsigned int has_wvc : 1;
52
};
53
54
/* http://www.wavpack.com/lib_use.txt */
55
56
static int32_t read_bytes(void *data, void *ptr, int32_t count)
57
{
58
	struct wavpack_file *file = data;
59
	int32_t rc, n = 0;
60
61
	if (file->push_back_byte != EOF) {
62
		char *p = ptr;
63
		*p = (char) file->push_back_byte;
64
		ptr = p + 1;
65
		file->push_back_byte = EOF;
66
		count--;
67
		n++;
68
	}
69
70
	rc = read(file->fd, ptr, count);
71
	if (rc == -1) {
72
		d_print("error: %s\n", strerror(errno));
73
		return 0;
74
	}
75
	if (rc == 0) {
76
		errno = 0;
77
		return 0;
78
	}
79
	return rc + n;
80
}
81
82
static uint32_t get_pos(void *data)
83
{
84
	struct wavpack_file *file = data;
85
86
	return lseek(file->fd, 0, SEEK_CUR);
87
}
88
89
static int set_pos_rel(void *data, int32_t delta, int mode)
90
{
91
	struct wavpack_file *file = data;
92
93
	if (lseek(file->fd, delta, mode) == -1)
94
		return -1;
95
96
	file->push_back_byte = EOF;
97
	return 0;
98
}
99
100
static int set_pos_abs(void *data, uint32_t pos)
101
{
102
	return set_pos_rel(data, pos, SEEK_SET);
103
}
104
105
static int push_back_byte(void *data, int c)
106
{
107
	struct wavpack_file *file = data;
108
109
	if (file->push_back_byte != EOF) {
110
		d_print("error: only one byte push back possible!\n");
111
		return EOF;
112
	}
113
	file->push_back_byte = c;
114
	return c;
115
}
116
117
static uint32_t get_length(void *data)
118
{
119
	struct wavpack_file *file = data;
120
	return file->len;
121
}
122
123
static int can_seek(void *data)
124
{
125
	struct wavpack_file *file = data;
126
	return file->len != -1;
127
}
128
129
static int32_t write_bytes(void *data, void *ptr, int32_t count)
130
{
131
	/* we shall not write any bytes */
132
	return 0;
133
}
134
135
136
/*
137
 * typedef struct {
138
 *     int32_t (*read_bytes)(void *id, void *data, int32_t bcount);
139
 *     uint32_t (*get_pos)(void *id);
140
 *     int (*set_pos_abs)(void *id, uint32_t pos);
141
 *     int (*set_pos_rel)(void *id, int32_t delta, int mode);
142
 *     int (*push_back_byte)(void *id, int c);
143
 *     uint32_t (*get_length)(void *id);
144
 *     int (*can_seek)(void *id);
145
 *
146
 *     // this callback is for writing edited tags only
147
 *     int32_t (*write_bytes)(void *id, void *data, int32_t bcount);
148
 * } WavpackStreamReader;
149
 */
150
static WavpackStreamReader callbacks = {
151
	.read_bytes = read_bytes,
152
	.get_pos = get_pos,
153
	.set_pos_abs = set_pos_abs,
154
	.set_pos_rel = set_pos_rel,
155
	.push_back_byte = push_back_byte,
156
	.get_length = get_length,
157
	.can_seek = can_seek,
158
	.write_bytes = write_bytes
159
};
160
161
static int wavpack_open(struct input_plugin_data *ip_data)
162
{
163
	struct wavpack_private *priv;
164
	struct stat st;
165
	char msg[80];
166
	int channel_mask = 0;
167
168
	const struct wavpack_private priv_init = {
169
		.wv_file = {
170
			.fd = ip_data->fd,
171
			.push_back_byte = EOF
172
		}
173
	};
174
175
	priv = xnew(struct wavpack_private, 1);
176
	*priv = priv_init;
177
	if (!ip_data->remote && fstat(ip_data->fd, &st) == 0) {
178
		char *filename_wvc;
179
180
		priv->wv_file.len = st.st_size;
181
182
		filename_wvc = xnew(char, strlen(ip_data->filename) + 2);
183
		sprintf(filename_wvc, "%sc", ip_data->filename);
184
		if (stat(filename_wvc, &st) == 0) {
185
			priv->wvc_file.fd = open(filename_wvc, O_RDONLY);
186
			if (priv->wvc_file.fd != -1) {
187
				priv->wvc_file.len = st.st_size;
188
				priv->wvc_file.push_back_byte = EOF;
189
				priv->has_wvc = 1;
190
				d_print("use correction file: %s\n", filename_wvc);
191
			}
192
		}
193
		free(filename_wvc);
194
	} else
195
		priv->wv_file.len = -1;
196
	ip_data->private = priv;
197
198
	*msg = '\0';
199
200
	priv->wpc = WavpackOpenFileInputEx(&callbacks, &priv->wv_file,
201
			priv->has_wvc ? &priv->wvc_file : NULL, msg,
202
			OPEN_NORMALIZE, 0);
203
204
	if (!priv->wpc) {
205
		d_print("WavpackOpenFileInputEx failed: %s\n", msg);
206
		free(priv);
207
		return -IP_ERROR_FILE_FORMAT;
208
	}
209
210
	ip_data->sf = sf_rate(WavpackGetSampleRate(priv->wpc))
211
		| sf_channels(WavpackGetReducedChannels(priv->wpc))
212
		| sf_bits(WavpackGetBitsPerSample(priv->wpc))
213
		| sf_signed(1);
214
#if CUR_STREAM_VERS > 0x404
215
	channel_mask = WavpackGetChannelMask(priv->wpc);
216
#endif
217
	channel_map_init_waveex(sf_get_channels(ip_data->sf), channel_mask, ip_data->channel_map);
218
	return 0;
219
}
220
221
static int wavpack_close(struct input_plugin_data *ip_data)
222
{
223
	struct wavpack_private *priv;
224
225
	priv = ip_data->private;
226
	priv->wpc = WavpackCloseFile(priv->wpc);
227
	if (priv->has_wvc)
228
		close(priv->wvc_file.fd);
229
	free(priv);
230
	ip_data->private = NULL;
231
	return 0;
232
}
233
234
/* from wv_engine.cpp (C) 2006 by Peter Lemenkov <lemenkov@newmail.ru> */
235
static char *format_samples(int bps, char *dst, int32_t *src, uint32_t count)
236
{
237
	int32_t temp;
238
239
	switch (bps) {
240
	case 1:
241
		while (count--)
242
			*dst++ = *src++ + 128;
243
		break;
244
	case 2:
245
		while (count--) {
246
			*dst++ = (char) (temp = *src++);
247
			*dst++ = (char) (temp >> 8);
248
		}
249
		break;
250
	case 3:
251
		while (count--) {
252
			*dst++ = (char) (temp = *src++);
253
			*dst++ = (char) (temp >> 8);
254
			*dst++ = (char) (temp >> 16);
255
		}
256
		break;
257
	case 4:
258
		while (count--) {
259
			*dst++ = (char) (temp = *src++);
260
			*dst++ = (char) (temp >> 8);
261
			*dst++ = (char) (temp >> 16);
262
			*dst++ = (char) (temp >> 24);
263
		}
264
		break;
265
	}
266
267
	return dst;
268
}
269
270
static int wavpack_read(struct input_plugin_data *ip_data, char *buffer, int count)
271
{
272
	struct wavpack_private *priv;
273
	int rc, bps, sample_count, channels;
274
275
	priv = ip_data->private;
276
	channels = sf_get_channels(ip_data->sf);
277
	bps = WavpackGetBytesPerSample(priv->wpc);
278
279
	sample_count = count / bps;
280
281
	rc = WavpackUnpackSamples(priv->wpc, priv->samples, sample_count / channels);
282
	format_samples(bps, buffer, priv->samples, rc * channels);
283
	return rc * channels * bps;
284
}
285
286
static int wavpack_seek(struct input_plugin_data *ip_data, double offset)
287
{
288
	struct wavpack_private *priv = ip_data->private;
289
290
	if (!WavpackSeekSample(priv->wpc, WavpackGetSampleRate(priv->wpc) * offset))
291
		return -IP_ERROR_INTERNAL;
292
	return 0;
293
}
294
295
static int wavpack_read_comments(struct input_plugin_data *ip_data,
296
		struct keyval **comments)
297
{
298
	struct id3tag id3;
299
	APETAG(ape);
300
	GROWING_KEYVALS(c);
301
	int fd, rc, save, i;
302
303
	fd = open(ip_data->filename, O_RDONLY);
304
	if (fd == -1)
305
		return -1;
306
	d_print("filename: %s\n", ip_data->filename);
307
308
	id3_init(&id3);
309
	rc = id3_read_tags(&id3, fd, ID3_V1);
310
	save = errno;
311
	close(fd);
312
	errno = save;
313
	if (rc) {
314
		if (rc == -1) {
315
			d_print("error: %s\n", strerror(errno));
316
			return -1;
317
		}
318
		d_print("corrupted tag?\n");
319
		goto next;
320
	}
321
322
	for (i = 0; i < NUM_ID3_KEYS; i++) {
323
		char *val = id3_get_comment(&id3, i);
324
		if (val)
325
			comments_add(&c, id3_key_names[i], val);
326
	}
327
328
next:
329
	id3_free(&id3);
330
331
	rc = ape_read_tags(&ape, ip_data->fd, 1);
332
	if (rc < 0)
333
		goto out;
334
335
	for (i = 0; i < rc; i++) {
336
		char *k, *v;
337
		k = ape_get_comment(&ape, &v);
338
		if (!k)
339
			break;
340
		comments_add(&c, k, v);
341
		free(k);
342
	}
343
344
out:
345
	ape_free(&ape);
346
347
	keyvals_terminate(&c);
348
	*comments = c.keyvals;
349
	return 0;
350
}
351
352
static int wavpack_duration(struct input_plugin_data *ip_data)
353
{
354
	struct wavpack_private *priv;
355
	int duration;
356
357
	priv = ip_data->private;
358
	duration = WavpackGetNumSamples(priv->wpc) /
359
		WavpackGetSampleRate(priv->wpc);
360
361
	return duration;
362
}
363
364
static long wavpack_bitrate(struct input_plugin_data *ip_data)
365
{
366
	struct wavpack_private *priv = ip_data->private;
367
	double bitrate = WavpackGetAverageBitrate(priv->wpc, 1);
368
	if (!bitrate)
369
		return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
370
	return (long) (bitrate + 0.5);
371
}
372
373
static long wavpack_current_bitrate(struct input_plugin_data *ip_data)
374
{
375
	struct wavpack_private *priv = ip_data->private;
376
	return WavpackGetInstantBitrate(priv->wpc);
377
}
378
379
static char *wavpack_codec(struct input_plugin_data *ip_data)
380
{
381
	return xstrdup("wavpack");
382
}
383
384
static char *wavpack_codec_profile(struct input_plugin_data *ip_data)
385
{
386
	struct wavpack_private *priv = ip_data->private;
387
	int m = WavpackGetMode(priv->wpc);
388
	char buf[32];
389
390
	buf[0] = '\0';
391
392
	if (m & MODE_FAST)
393
		strcat(buf, "fast");
394
#ifdef MODE_VERY_HIGH
395
	else if (m & MODE_VERY_HIGH)
396
		strcat(buf, "very high");
397
#endif
398
	else if (m & MODE_HIGH)
399
		strcat(buf, "high");
400
	else
401
		strcat(buf, "normal");
402
403
	if (m & MODE_HYBRID)
404
		strcat(buf, " hybrid");
405
406
#ifdef MODE_XMODE
407
	if ((m & MODE_EXTRA) && (m & MODE_XMODE)) {
408
		char xmode[] = " x0";
409
		xmode[2] = ((m & MODE_XMODE) >> 12) + '0';
410
		strcat(buf, xmode);
411
	}
412
#endif
413
414
	return xstrdup(buf);
415
}
416
417
const struct input_plugin_ops ip_ops = {
418
	.open = wavpack_open,
419
	.close = wavpack_close,
420
	.read = wavpack_read,
421
	.seek = wavpack_seek,
422
	.read_comments = wavpack_read_comments,
423
	.duration = wavpack_duration,
424
	.bitrate = wavpack_bitrate,
425
	.bitrate_current = wavpack_current_bitrate,
426
	.codec = wavpack_codec,
427
	.codec_profile = wavpack_codec_profile
428
};
429
430
const int ip_priority = 50;
431
const char * const ip_extensions[] = { "wv", NULL };
432
const char * const ip_mime_types[] = { "audio/x-wavpack", NULL };
433
const char * const ip_options[] = { NULL };