1 /*
2  * Copyright 2008-2013 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 
read_func(void * datasource,void * buffer,size_t count)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 
lseek_func(void * datasource,off_t offset,int whence)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 
close_func(void * datasource)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 
mad_open(struct input_plugin_data * ip_data)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 
mad_close(struct input_plugin_data * ip_data)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 
mad_read(struct input_plugin_data * ip_data,char * buffer,int count)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 
mad_seek(struct input_plugin_data * ip_data,double offset)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 
mad_read_comments(struct input_plugin_data * ip_data,struct keyval ** comments)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 
mad_duration(struct input_plugin_data * ip_data)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 
mad_bitrate(struct input_plugin_data * ip_data)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 
mad_current_bitrate(struct input_plugin_data * ip_data)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 
mad_codec(struct input_plugin_data * ip_data)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 
mad_codec_profile(struct input_plugin_data * ip_data)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 struct input_plugin_opt ip_options[] = { { NULL } };
284 const unsigned ip_abi_version = IP_ABI_VERSION;
285