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