1 //=========================================================================
2 // FILENAME	: tagutils.c
3 // DESCRIPTION	: MP3/MP4/Ogg/FLAC metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
7 
8 /* This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 /* This file is derived from mt-daapd project */
23 
24 #include "config.h"
25 #include <ctype.h>
26 #include <errno.h>
27 #include <id3tag.h>
28 #include <stdlib.h>
29 #include <stddef.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include <sys/time.h>
35 #include <netinet/in.h>
36 #ifdef HAVE_VORBISFILE
37 #include <ogg/ogg.h>
38 #include <vorbis/codec.h>
39 #endif
40 #include <FLAC/metadata.h>
41 
42 #ifdef HAVE_ICONV
43 #include <iconv.h>
44 #endif
45 #include <sqlite3.h>
46 #include "tagutils.h"
47 #include "../metadata.h"
48 #include "../utils.h"
49 #include "../log.h"
50 
51 struct id3header {
52 	unsigned char id[3];
53 	unsigned char version[2];
54 	unsigned char flags;
55 	unsigned char size[4];
56 } __attribute((packed));
57 
58 char *winamp_genre[] = {
59 	/*00*/ "Blues",             "Classic Rock",     "Country",           "Dance",
60 	       "Disco",             "Funk",             "Grunge",            "Hip-Hop",
61 	/*08*/ "Jazz",              "Metal",            "New Age",           "Oldies",
62 	       "Other",             "Pop",              "R&B",               "Rap",
63 	/*10*/ "Reggae",            "Rock",             "Techno",            "Industrial",
64 	       "Alternative",       "Ska",              "Death Metal",       "Pranks",
65 	/*18*/ "Soundtrack",        "Euro-Techno",	"Ambient",           "Trip-Hop",
66 	       "Vocal",             "Jazz+Funk",        "Fusion",            "Trance",
67 	/*20*/ "Classical",         "Instrumental",     "Acid",              "House",
68 	       "Game",              "Sound Clip",       "Gospel",            "Noise",
69 	/*28*/ "AlternRock",        "Bass",             "Soul",              "Punk",
70 	       "Space",             "Meditative",       "Instrumental Pop",  "Instrumental Rock",
71 	/*30*/ "Ethnic",            "Gothic",		"Darkwave",          "Techno-Industrial",
72 	       "Electronic",        "Pop-Folk",         "Eurodance",         "Dream",
73 	/*38*/ "Southern Rock",     "Comedy",           "Cult",              "Gangsta",
74 	       "Top 40",            "Christian Rap",    "Pop/Funk",          "Jungle",
75 	/*40*/ "Native American",   "Cabaret",          "New Wave",          "Psychedelic",
76 	       "Rave",              "Showtunes",        "Trailer",           "Lo-Fi",
77 	/*48*/ "Tribal",            "Acid Punk",        "Acid Jazz",         "Polka",
78 	       "Retro",             "Musical",          "Rock & Roll",       "Hard Rock",
79 	/*50*/ "Folk",              "Folk/Rock",        "National folk",     "Swing",
80 	       "Fast-fusion",       "Bebob",            "Latin",             "Revival",
81 	/*58*/ "Celtic",            "Bluegrass",        "Avantgarde",        "Gothic Rock",
82 	       "Progressive Rock",  "Psychedelic Rock", "Symphonic Rock",    "Slow Rock",
83 	/*60*/ "Big Band",          "Chorus",           "Easy Listening",    "Acoustic",
84 	       "Humour",            "Speech",           "Chanson",           "Opera",
85 	/*68*/ "Chamber Music",     "Sonata",           "Symphony",          "Booty Bass",
86 	       "Primus",            "Porn Groove",      "Satire",            "Slow Jam",
87 	/*70*/ "Club",              "Tango",            "Samba",             "Folklore",
88 	       "Ballad",            "Powder Ballad",    "Rhythmic Soul",     "Freestyle",
89 	/*78*/ "Duet",              "Punk Rock",        "Drum Solo",         "A Capella",
90 	       "Euro-House",        "Dance Hall",       "Goa",               "Drum & Bass",
91 	/*80*/ "Club House",        "Hardcore",         "Terror",            "Indie",
92 	       "BritPop",           "NegerPunk",        "Polsk Punk",        "Beat",
93 	/*88*/ "Christian Gangsta", "Heavy Metal",      "Black Metal",       "Crossover",
94 	       "Contemporary C",    "Christian Rock",   "Merengue",          "Salsa",
95 	/*90*/ "Thrash Metal",      "Anime",            "JPop",              "SynthPop",
96 	       "Unknown"
97 };
98 
99 #define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1)
100 
101 
102 /*
103  * Prototype
104  */
105 #include "tagutils-mp3.h"
106 #include "tagutils-aac.h"
107 #ifdef HAVE_VORBISFILE
108 #include "tagutils-ogg.h"
109 #endif
110 #include "tagutils-flc.h"
111 #include "tagutils-asf.h"
112 #include "tagutils-wav.h"
113 #include "tagutils-pcm.h"
114 #include "tagutils-dsf.h"
115 #include "tagutils-dff.h"
116 
117 static int _get_tags(char *file, struct song_metadata *psong);
118 static int _get_fileinfo(char *file, struct song_metadata *psong);
119 
120 
121 /*
122  * Typedefs
123  */
124 
125 typedef struct {
126 	char* type;
127 	int (*get_tags)(char* file, struct song_metadata* psong);
128 	int (*get_fileinfo)(char* file, struct song_metadata* psong);
129 } taghandler;
130 
131 static taghandler taghandlers[] = {
132 	{ "aac", _get_aactags,	_get_aacfileinfo },
133 	{ "mp3", _get_mp3tags,	_get_mp3fileinfo },
134 	{ "flc", _get_flctags,	_get_flcfileinfo },
135 #ifdef HAVE_VORBISFILE
136 	{ "ogg", NULL,		_get_oggfileinfo },
137 #endif
138 	{ "asf", NULL,		_get_asffileinfo },
139 	{ "wav", _get_wavtags,	_get_wavfileinfo },
140 	{ "pcm", NULL,		_get_pcmfileinfo },
141 	{ "dsf", _get_dsftags,	_get_dsffileinfo },
142 	{ "dff", NULL,		_get_dfffileinfo },
143 	{ NULL,  NULL, NULL }
144 };
145 
146 
147 
148 //*********************************************************************************
149 #include "tagutils-misc.c"
150 #include "tagutils-mp3.c"
151 #include "tagutils-aac.c"
152 #ifdef HAVE_VORBISFILE
153 #include "tagutils-ogg.c"
154 #endif
155 #include "tagutils-flc.c"
156 #include "tagutils-asf.c"
157 #include "tagutils-wav.c"
158 #include "tagutils-pcm.c"
159 #include "tagutils-plist.c"
160 #include "tagutils-dsf.c"
161 #include "tagutils-dff.c"
162 
163 //*********************************************************************************
164 // freetags()
165 #define MAYBEFREE(a) { if((a)) free((a)); };
166 void
freetags(struct song_metadata * psong)167 freetags(struct song_metadata *psong)
168 {
169 	int role;
170 
171 	MAYBEFREE(psong->path);
172 	MAYBEFREE(psong->image);
173 	MAYBEFREE(psong->title);
174 	MAYBEFREE(psong->album);
175 	MAYBEFREE(psong->genre);
176 	MAYBEFREE(psong->comment);
177 	for(role = ROLE_START; role <= ROLE_LAST; role++)
178 	{
179 		MAYBEFREE(psong->contributor[role]);
180 		MAYBEFREE(psong->contributor_sort[role]);
181 	}
182 	MAYBEFREE(psong->grouping);
183 	MAYBEFREE(psong->mime);
184 	MAYBEFREE(psong->dlna_pn);
185 	MAYBEFREE(psong->tagversion);
186 	MAYBEFREE(psong->musicbrainz_albumid);
187 	MAYBEFREE(psong->musicbrainz_trackid);
188 	MAYBEFREE(psong->musicbrainz_artistid);
189 	MAYBEFREE(psong->musicbrainz_albumartistid);
190 }
191 
192 // _get_fileinfo
193 static int
_get_fileinfo(char * file,struct song_metadata * psong)194 _get_fileinfo(char *file, struct song_metadata *psong)
195 {
196 	taghandler *hdl;
197 
198 	// dispatch to appropriate tag handler
199 	for(hdl = taghandlers; hdl->type; ++hdl)
200 		if(!strcmp(hdl->type, psong->type))
201 			break;
202 
203 	if(hdl->get_fileinfo)
204 		return hdl->get_fileinfo(file, psong);
205 
206 	return 0;
207 }
208 
209 
210 static void
_make_composite_tags(struct song_metadata * psong)211 _make_composite_tags(struct song_metadata *psong)
212 {
213 	int len;
214 
215 	len = 1;
216 
217 	if(!psong->contributor[ROLE_ARTIST] &&
218 	   (psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR]))
219 	{
220 		if(psong->contributor[ROLE_BAND])
221 			len += strlen(psong->contributor[ROLE_BAND]);
222 		if(psong->contributor[ROLE_CONDUCTOR])
223 			len += strlen(psong->contributor[ROLE_CONDUCTOR]);
224 
225 		len += 3;
226 
227 		psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1);
228 		if(psong->contributor[ROLE_ARTIST])
229 		{
230 			if(psong->contributor[ROLE_BAND])
231 				strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]);
232 
233 			if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR])
234 				strcat(psong->contributor[ROLE_ARTIST], " - ");
235 
236 			if(psong->contributor[ROLE_CONDUCTOR])
237 				strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]);
238 		}
239 	}
240 
241 #if 0 // already taken care of by scanner.c
242 	if(!psong->title)
243 	{
244 		char *suffix;
245 		psong->title = strdup(psong->basename);
246 		suffix = strrchr(psong->title, '.');
247 		if(suffix) *suffix = '\0';
248 	}
249 #endif
250 }
251 
252 
253 /*****************************************************************************/
254 // _get_tags
255 static int
_get_tags(char * file,struct song_metadata * psong)256 _get_tags(char *file, struct song_metadata *psong)
257 {
258 	taghandler *hdl;
259 
260 	// dispatch
261 	for(hdl = taghandlers ; hdl->type ; ++hdl)
262 		if(!strcasecmp(hdl->type, psong->type))
263 			break;
264 
265 	if(hdl->get_tags)
266 	{
267 		return hdl->get_tags(file, psong);
268 	}
269 
270 	return 0;
271 }
272 
273 /*****************************************************************************/
274 // readtags
275 int
readtags(char * path,struct song_metadata * psong,struct stat * stat,char * lang,char * type)276 readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type)
277 {
278 	char *fname;
279 
280 	if(lang_index == -1)
281 		lang_index = _lang2cp(lang);
282 
283 	memset((void*)psong, 0, sizeof(struct song_metadata));
284 	psong->path = strdup(path);
285 	psong->type = type;
286 
287 	fname = strrchr(psong->path, '/');
288 	psong->basename = fname ? fname + 1 : psong->path;
289 
290 	if(stat)
291 	{
292 		if(!psong->time_modified)
293 			psong->time_modified = stat->st_mtime;
294 		psong->file_size = stat->st_size;
295 	}
296 
297 	// get tag
298 	if( _get_tags(path, psong) == 0 )
299 	{
300 		_make_composite_tags(psong);
301 	}
302 
303 	// get fileinfo
304 	return _get_fileinfo(path, psong);
305 }
306