1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2004-2005 Timo Hirvonen
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 "file.h"
21
#include "xmalloc.h"
22
#include "debug.h"
23
#include "utils.h"
24
#include "comment.h"
25
26
#include <stdio.h>
27
#include <string.h>
28
#include <strings.h>
29
#include <errno.h>
30
31
#include <sys/types.h>
32
#include <unistd.h>
33
34
#define WAVE_FORMAT_PCM        0x0001U
35
#define WAVE_FORMAT_EXTENSIBLE 0xfffeU
36
37
struct wav_private {
38
	unsigned int pcm_start;
39
	unsigned int pcm_size;
40
	unsigned int pos;
41
42
	/* size of one second of data */
43
	unsigned int sec_size;
44
45
	unsigned int frame_size;
46
};
47
48
static int read_chunk_header(int fd, char *name, unsigned int *size)
49
{
50
	int rc;
51
	char buf[8];
52
53
	rc = read_all(fd, buf, 8);
54
	if (rc == -1)
55
		return -IP_ERROR_ERRNO;
56
	if (rc != 8)
57
		return -IP_ERROR_FILE_FORMAT;
58
	*size = read_le32(buf + 4);
59
	memmove(name, buf, 4);
60
	return 0;
61
}
62
63
static int read_named_chunk_header(int fd, const char *name, unsigned int *size)
64
{
65
	int rc;
66
	char buf[4];
67
68
	rc = read_chunk_header(fd, buf, size);
69
	if (rc)
70
		return rc;
71
	if (memcmp(buf, name, 4))
72
		return -IP_ERROR_FILE_FORMAT;
73
	return 0;
74
}
75
76
static int find_chunk(int fd, const char *name, unsigned int *size)
77
{
78
	int rc;
79
80
	do {
81
		rc = read_named_chunk_header(fd, name, size);
82
		if (rc == 0)
83
			return 0;
84
		if (rc != -IP_ERROR_FILE_FORMAT)
85
			return rc;
86
		d_print("seeking %d\n", *size);
87
		if (lseek(fd, *size, SEEK_CUR) == -1) {
88
			d_print("seek failed\n");
89
			return -IP_ERROR_ERRNO;
90
		}
91
	} while (1);
92
}
93
94
static int wav_open(struct input_plugin_data *ip_data)
95
{
96
	struct wav_private *priv;
97
	char buf[4];
98
	char *fmt;
99
	int rc;
100
	unsigned int riff_size, fmt_size;
101
	int save;
102
103
	d_print("file: %s\n", ip_data->filename);
104
	priv = xnew(struct wav_private, 1);
105
	ip_data->private = priv;
106
	rc = read_named_chunk_header(ip_data->fd, "RIFF", &riff_size);
107
	if (rc)
108
		goto error_exit;
109
	rc = read_all(ip_data->fd, buf, 4);
110
	if (rc == -1) {
111
		rc = -IP_ERROR_ERRNO;
112
		goto error_exit;
113
	}
114
	if (rc != 4 || memcmp(buf, "WAVE", 4) != 0) {
115
		rc = -IP_ERROR_FILE_FORMAT;
116
		goto error_exit;
117
	}
118
119
	rc = find_chunk(ip_data->fd, "fmt ", &fmt_size);
120
	if (rc)
121
		goto error_exit;
122
	if (fmt_size < 16) {
123
		d_print("size of \"fmt \" chunk is invalid (%u)\n", fmt_size);
124
		rc = -IP_ERROR_FILE_FORMAT;
125
		goto error_exit;
126
	}
127
	fmt = xnew(char, fmt_size);
128
	rc = read_all(ip_data->fd, fmt, fmt_size);
129
	if (rc == -1) {
130
		save = errno;
131
		free(fmt);
132
		errno = save;
133
		rc = -IP_ERROR_ERRNO;
134
		goto error_exit;
135
	}
136
	if (rc != fmt_size) {
137
		save = errno;
138
		free(fmt);
139
		errno = save;
140
		rc = -IP_ERROR_FILE_FORMAT;
141
		goto error_exit;
142
	}
143
	{
144
		unsigned int format_tag, channels, rate, bits, channel_mask = 0;
145
146
		format_tag = read_le16(fmt + 0);
147
		channels = read_le16(fmt + 2);
148
		rate = read_le32(fmt + 4);
149
		/* 4 bytes, bytes per second */
150
		/* 2 bytes, bytes per sample */
151
		bits = read_le16(fmt + 14);
152
		if (format_tag == WAVE_FORMAT_EXTENSIBLE) {
153
			unsigned int ext_size, valid_bits;
154
			if (fmt_size < 18) {
155
				free(fmt);
156
				d_print("size of \"fmt \" chunk is invalid (%u)\n", fmt_size);
157
				rc = -IP_ERROR_FILE_FORMAT;
158
				goto error_exit;
159
			}
160
			ext_size = read_le16(fmt + 16);
161
			if (ext_size < 22) {
162
				free(fmt);
163
				d_print("size of \"fmt \" chunk extension is invalid (%u)\n", ext_size);
164
				rc = -IP_ERROR_FILE_FORMAT;
165
				goto error_exit;
166
			}
167
			valid_bits = read_le16(fmt + 18);
168
			if (valid_bits != bits) {
169
				free(fmt);
170
				d_print("padded samples are not supported (%u != %u)\n", bits, valid_bits);
171
				rc = -IP_ERROR_FILE_FORMAT;
172
				goto error_exit;
173
			}
174
			channel_mask = read_le32(fmt + 20);
175
			format_tag = read_le16(fmt + 24);
176
			/* ignore rest of extension tag */
177
		}
178
		free(fmt);
179
180
		if (format_tag != WAVE_FORMAT_PCM) {
181
			d_print("unsupported format tag %u, should be 1\n", format_tag);
182
			rc = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
183
			goto error_exit;
184
		}
185
		if ((bits != 8 && bits != 16 && bits != 24 && bits != 32) || channels < 1) {
186
			rc = -IP_ERROR_SAMPLE_FORMAT;
187
			goto error_exit;
188
		}
189
		ip_data->sf = sf_channels(channels) | sf_rate(rate) | sf_bits(bits) |
190
			sf_signed(bits > 8);
191
		channel_map_init_waveex(channels, channel_mask, ip_data->channel_map);
192
	}
193
194
	rc = find_chunk(ip_data->fd, "data", &priv->pcm_size);
195
	if (rc)
196
		goto error_exit;
197
	if (lseek(ip_data->fd, 0, SEEK_CUR) == -1) {
198
		rc = -IP_ERROR_ERRNO;
199
		goto error_exit;
200
	}
201
	priv->pcm_start = rc;
202
203
	priv->sec_size = sf_get_second_size(ip_data->sf);
204
	priv->frame_size = sf_get_frame_size(ip_data->sf);
205
	priv->pos = 0;
206
207
	d_print("pcm start: %u\n", priv->pcm_start);
208
	d_print("pcm size: %u\n", priv->pcm_size);
209
	d_print("\n");
210
	d_print("sr: %d, ch: %d, bits: %d, signed: %d\n", sf_get_rate(ip_data->sf),
211
			sf_get_channels(ip_data->sf), sf_get_bits(ip_data->sf),
212
			sf_get_signed(ip_data->sf));
213
214
	/* clamp pcm_size to full frames (file might be corrupt or truncated) */
215
	priv->pcm_size = priv->pcm_size & ~((unsigned int)sf_get_frame_size(ip_data->sf) - 1U);
216
	return 0;
217
error_exit:
218
	save = errno;
219
	free(priv);
220
	errno = save;
221
	return rc;
222
}
223
224
static int wav_close(struct input_plugin_data *ip_data)
225
{
226
	struct wav_private *priv;
227
228
	priv = ip_data->private;
229
	free(priv);
230
	ip_data->private = NULL;
231
	return 0;
232
}
233
234
static int wav_read(struct input_plugin_data *ip_data, char *buffer, int _count)
235
{
236
	struct wav_private *priv = ip_data->private;
237
	unsigned int count = _count;
238
	int rc;
239
240
	if (priv->pos == priv->pcm_size) {
241
		/* eof */
242
		return 0;
243
	}
244
	if (count > priv->pcm_size - priv->pos)
245
		count = priv->pcm_size - priv->pos;
246
	rc = read(ip_data->fd, buffer, count);
247
	if (rc == -1) {
248
		d_print("read error\n");
249
		return -IP_ERROR_ERRNO;
250
	}
251
	if (rc == 0) {
252
		d_print("eof\n");
253
		return 0;
254
	}
255
	priv->pos += rc;
256
	return rc;
257
}
258
259
static int wav_seek(struct input_plugin_data *ip_data, double _offset)
260
{
261
	struct wav_private *priv = ip_data->private;
262
	unsigned int offset;
263
264
	offset = (unsigned int)(_offset * (double)priv->sec_size + 0.5);
265
	/* align to frame size */
266
	offset -= offset % priv->frame_size;
267
	priv->pos = offset;
268
	if (lseek(ip_data->fd, priv->pcm_start + offset, SEEK_SET) == -1)
269
		return -1;
270
	return 0;
271
}
272
273
static struct {
274
	const char *old;
275
	const char *new;
276
} key_map[] = {
277
	{ "IART", "artist" },
278
	{ "ICMT", "comment" },
279
	{ "ICOP", "copyright" },
280
	{ "ICRD", "date" },
281
	{ "IGNR", "genre" },
282
	{ "INAM", "title" },
283
	{ "IPRD", "album" },
284
	{ "IPRT", "tracknumber" },
285
	{ "ISFT", "software" },
286
	{ NULL, NULL }
287
};
288
289
static const char *lookup_key(const char *key)
290
{
291
	int i;
292
	for (i = 0; key_map[i].old; i++) {
293
		if (!strcasecmp(key, key_map[i].old))
294
			return key_map[i].new;
295
	}
296
	return NULL;
297
}
298
299
static int wav_read_comments(struct input_plugin_data *ip_data,
300
		struct keyval **comments)
301
{
302
	GROWING_KEYVALS(c);
303
	struct wav_private *priv;
304
	unsigned int size;
305
	char id[4+1];
306
	int rc = 0;
307
308
	priv = ip_data->private;
309
	id[4] = '\0';
310
311
	if (lseek(ip_data->fd, 12, SEEK_SET) == -1) {
312
		rc = -1;
313
		goto out;
314
	}
315
316
	while (1) {
317
		rc = read_chunk_header(ip_data->fd, id, &size);
318
		if (rc)
319
			break;
320
		if (strcmp(id, "data") == 0) {
321
			rc = 0;
322
			break;
323
		} else if (strcmp(id, "LIST") == 0) {
324
			char buf[4];
325
			rc = read_all(ip_data->fd, buf, 4);
326
			if (rc == -1)
327
				break;
328
			if (memcmp(buf, "INFO", 4) == 0)
329
				continue;
330
			size -= 4;
331
		} else {
332
			const char *key = lookup_key(id);
333
			if (key) {
334
				char *val = xnew(char, size + 1);
335
				rc = read_all(ip_data->fd, val, size);
336
				if (rc == -1) {
337
					free(val);
338
					break;
339
				}
340
				val[rc] = '\0';
341
				comments_add(&c, key, val);
342
				continue;
343
			}
344
		}
345
346
		if (lseek(ip_data->fd, size, SEEK_CUR) == -1) {
347
			rc = -1;
348
			break;
349
		}
350
	}
351
352
out:
353
	lseek(ip_data->fd, priv->pcm_start, SEEK_SET);
354
355
	keyvals_terminate(&c);
356
357
	if (rc && c.count == 0) {
358
		keyvals_free(c.keyvals);
359
		return -1;
360
	}
361
362
	*comments = c.keyvals;
363
	return 0;
364
}
365
366
static int wav_duration(struct input_plugin_data *ip_data)
367
{
368
	struct wav_private *priv;
369
	int duration;
370
371
	priv = ip_data->private;
372
	duration = priv->pcm_size / priv->sec_size;
373
	return duration;
374
}
375
376
static long wav_bitrate(struct input_plugin_data *ip_data)
377
{
378
	sample_format_t sf = ip_data->sf;
379
	return sf_get_bits(sf) * sf_get_rate(sf) * sf_get_channels(sf);
380
}
381
382
static char *wav_codec(struct input_plugin_data *ip_data)
383
{
384
	char buf[16];
385
	snprintf(buf, 16, "pcm_%c%u%s",
386
			sf_get_signed(ip_data->sf) ? 's' : 'u',
387
			sf_get_bits(ip_data->sf),
388
			sf_get_bigendian(ip_data->sf) ? "be" : "le");
389
390
	return xstrdup(buf);
391
}
392
393
static char *wav_codec_profile(struct input_plugin_data *ip_data)
394
{
395
	return NULL;
396
}
397
398
const struct input_plugin_ops ip_ops = {
399
	.open = wav_open,
400
	.close = wav_close,
401
	.read = wav_read,
402
	.seek = wav_seek,
403
	.read_comments = wav_read_comments,
404
	.duration = wav_duration,
405
	.bitrate = wav_bitrate,
406
	.bitrate_current = wav_bitrate,
407
	.codec = wav_codec,
408
	.codec_profile = wav_codec_profile
409
};
410
411
const int ip_priority = 50;
412
const char * const ip_extensions[] = { "wav", NULL };
413
const char * const ip_mime_types[] = { NULL };
414
const char * const ip_options[] = { NULL };