1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2004 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 "nomad.h"
21
#include "id3.h"
22
#include "ape.h"
23
#include "xmalloc.h"
24
#include "read_wrapper.h"
25
#include "debug.h"
26
#include "utils.h"
27
#include "comment.h"
28
29
#include <stdio.h>
30
#include <math.h>
31
#include <errno.h>
32
#include <sys/types.h>
33
#include <unistd.h>
34
#include <sys/types.h>
35
#include <sys/stat.h>
36
#include <fcntl.h>
37
38
/* ------------------------------------------------------------------------- */
39
40
static ssize_t read_func(void *datasource, void *buffer, size_t count)
41
{
42
	struct input_plugin_data *ip_data = datasource;
43
44
	return read_wrapper(ip_data, buffer, count);
45
}
46
47
static off_t lseek_func(void *datasource, off_t offset, int whence)
48
{
49
	struct input_plugin_data *ip_data = datasource;
50
51
	return lseek(ip_data->fd, offset, whence);
52
}
53
54
static int close_func(void *datasource)
55
{
56
	struct input_plugin_data *ip_data = datasource;
57
58
	return close(ip_data->fd);
59
}
60
61
static struct nomad_callbacks callbacks = {
62
	.read = read_func,
63
	.lseek = lseek_func,
64
	.close = close_func
65
};
66
67
/* ------------------------------------------------------------------------- */
68
69
static int mad_open(struct input_plugin_data *ip_data)
70
{
71
	struct nomad *nomad;
72
	const struct nomad_info *info;
73
	int rc;
74
75
	rc = nomad_open_callbacks(&nomad, ip_data, &callbacks);
76
	switch (rc) {
77
	case -NOMAD_ERROR_ERRNO:
78
		return -1;
79
	case -NOMAD_ERROR_FILE_FORMAT:
80
		return -IP_ERROR_FILE_FORMAT;
81
	}
82
	ip_data->private = nomad;
83
84
	info = nomad_info(nomad);
85
86
	/* always 16-bit signed little-endian */
87
	ip_data->sf = sf_rate(info->sample_rate) | sf_channels(info->channels) |
88
		sf_bits(16) | sf_signed(1);
89
	channel_map_init_waveex(info->channels, 0, ip_data->channel_map);
90
	return 0;
91
}
92
93
static int mad_close(struct input_plugin_data *ip_data)
94
{
95
	struct nomad *nomad;
96
97
	nomad = ip_data->private;
98
	nomad_close(nomad);
99
	ip_data->fd = -1;
100
	ip_data->private = NULL;
101
	return 0;
102
}
103
104
static int mad_read(struct input_plugin_data *ip_data, char *buffer, int count)
105
{
106
	struct nomad *nomad;
107
	
108
	nomad = ip_data->private;
109
	return nomad_read(nomad, buffer, count);
110
}
111
112
static int mad_seek(struct input_plugin_data *ip_data, double offset)
113
{
114
	struct nomad *nomad;
115
	
116
	nomad = ip_data->private;
117
	return nomad_time_seek(nomad, offset);
118
}
119
120
static int mad_read_comments(struct input_plugin_data *ip_data,
121
		struct keyval **comments)
122
{
123
	struct nomad *nomad = ip_data->private;
124
	const struct nomad_lame *lame = nomad_lame(nomad);
125
	struct id3tag id3;
126
	int fd, rc, save, i;
127
	APETAG(ape);
128
	GROWING_KEYVALS(c);
129
130
	fd = open(ip_data->filename, O_RDONLY);
131
	if (fd == -1) {
132
		return -1;
133
	}
134
	d_print("filename: %s\n", ip_data->filename);
135
136
	id3_init(&id3);
137
	rc = id3_read_tags(&id3, fd, ID3_V1 | ID3_V2);
138
	save = errno;
139
	close(fd);
140
	errno = save;
141
	if (rc) {
142
		if (rc == -1) {
143
			d_print("error: %s\n", strerror(errno));
144
			return -1;
145
		}
146
		d_print("corrupted tag?\n");
147
		goto next;
148
	}
149
150
	for (i = 0; i < NUM_ID3_KEYS; i++) {
151
		char *val = id3_get_comment(&id3, i);
152
153
		if (val)
154
			comments_add(&c, id3_key_names[i], val);
155
	}
156
157
next:
158
	id3_free(&id3);
159
160
	rc = ape_read_tags(&ape, ip_data->fd, 0);
161
	if (rc < 0)
162
		goto out;
163
164
	for (i = 0; i < rc; i++) {
165
		char *k, *v;
166
		k = ape_get_comment(&ape, &v);
167
		if (!k)
168
			break;
169
		comments_add(&c, k, v);
170
		free(k);
171
	}
172
173
out:
174
	ape_free(&ape);
175
176
	/* add last so the other tags get preference */
177
	if (lame && !isnan(lame->trackGain)) {
178
		char buf[64];
179
180
		if (!isnan(lame->peak)) {
181
			sprintf(buf, "%f", lame->peak);
182
			comments_add_const(&c, "replaygain_track_peak", buf);
183
		}
184
		sprintf(buf, "%+.1f dB", lame->trackGain);
185
		comments_add_const(&c, "replaygain_track_gain", buf);
186
	}
187
188
	keyvals_terminate(&c);
189
	*comments = c.keyvals;
190
	return 0;
191
}
192
193
static int mad_duration(struct input_plugin_data *ip_data)
194
{
195
	struct nomad *nomad = ip_data->private;
196
197
	return nomad_info(nomad)->duration;
198
}
199
200
static long mad_bitrate(struct input_plugin_data *ip_data)
201
{
202
	struct nomad *nomad = ip_data->private;
203
	long bitrate = nomad_info(nomad)->avg_bitrate;
204
205
	return bitrate != -1 ? bitrate : -IP_ERROR_FUNCTION_NOT_SUPPORTED;
206
}
207
208
static long mad_current_bitrate(struct input_plugin_data *ip_data)
209
{
210
	struct nomad *nomad = ip_data->private;
211
	return nomad_current_bitrate(nomad);
212
}
213
214
static char *mad_codec(struct input_plugin_data *ip_data)
215
{
216
	struct nomad *nomad = ip_data->private;
217
218
	switch (nomad_info(nomad)->layer) {
219
	case 3:
220
		return xstrdup("mp3");
221
	case 2:
222
		return xstrdup("mp2");
223
	case 1:
224
		return xstrdup("mp1");
225
	}
226
	return NULL;
227
}
228
229
static char *mad_codec_profile(struct input_plugin_data *ip_data)
230
{
231
	struct nomad *nomad = ip_data->private;
232
	const struct nomad_lame *lame = nomad_lame(nomad);
233
	const char *mode = nomad_info(nomad)->vbr ? "VBR" : "CBR";
234
235
	if (lame) {
236
		/* LAME:
237
		 * 0: unknown
238
		 * 1: cbr
239
		 * 2: abr
240
		 * 3: vbr rh (--vbr-old)
241
		 * 4: vbr mtrh (--vbr-new)
242
		 * 5: vbr mt (obsolete)
243
		 */
244
		int method = lame->vbr_method;
245
		if (method == 2)
246
			mode = "ABR";
247
		else if (method >= 3 && method <= 5) {
248
			const struct nomad_xing *xing = nomad_xing(nomad);
249
250
			if (xing && xing->flags & XING_SCALE && xing->scale && xing->scale <= 100) {
251
				char buf[16];
252
				int v = 10 - (xing->scale + 9) / 10;
253
				/* quality (-q): 10 - (xing->scale - ((9 - v) * 10)) */
254
255
				sprintf(buf, "VBR V%d", v);
256
				return xstrdup(buf);
257
258
			}
259
		}
260
	}
261
262
	return xstrdup(mode);
263
}
264
265
const struct input_plugin_ops ip_ops = {
266
	.open = mad_open,
267
	.close = mad_close,
268
	.read = mad_read,
269
	.seek = mad_seek,
270
	.read_comments = mad_read_comments,
271
	.duration = mad_duration,
272
	.bitrate = mad_bitrate,
273
	.bitrate_current = mad_current_bitrate,
274
	.codec = mad_codec,
275
	.codec_profile = mad_codec_profile
276
};
277
278
const int ip_priority = 55;
279
const char * const ip_extensions[] = { "mp3", "mp2", NULL };
280
const char * const ip_mime_types[] = {
281
	"audio/mpeg", "audio/x-mp3", "audio/x-mpeg", NULL
282
};
283
const char * const ip_options[] = { NULL };