1 //=========================================================================
2 // FILENAME : tagutils.c
3 // DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
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.
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.
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/>.
22 /* This file is derived from mt-daapd project */
34 #include <netinet/in.h>
36 #include <vorbis/codec.h>
37 #include <FLAC/metadata.h>
39 #include "../config.h"
47 #include "textutils.h"
48 #include "../metadata.h"
53 unsigned char version
[2];
55 unsigned char size
[4];
56 } __attribute((packed
));
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",
99 #define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1)
105 #include "tagutils-mp3.h"
106 #include "tagutils-aac.h"
107 #include "tagutils-ogg.h"
108 #include "tagutils-flc.h"
109 #include "tagutils-asf.h"
110 #include "tagutils-wav.h"
111 #include "tagutils-pcm.h"
113 static int _get_tags(char *file
, struct song_metadata
*psong
);
114 static int _get_fileinfo(char *file
, struct song_metadata
*psong
);
123 int (*get_tags
)(char* file
, struct song_metadata
* psong
);
124 int (*get_fileinfo
)(char* file
, struct song_metadata
* psong
);
127 static taghandler taghandlers
[] = {
128 { "aac", _get_aactags
, _get_aacfileinfo
},
129 { "mp3", _get_mp3tags
, _get_mp3fileinfo
},
130 { "flc", _get_flctags
, _get_flcfileinfo
},
131 { "ogg", 0, _get_oggfileinfo
},
132 { "asf", 0, _get_asffileinfo
},
133 { "wav", _get_wavtags
, _get_wavfileinfo
},
134 { "pcm", 0, _get_pcmfileinfo
},
140 //*********************************************************************************
141 #include "tagutils-misc.c"
142 #include "tagutils-mp3.c"
143 #include "tagutils-aac.c"
144 #include "tagutils-ogg.c"
145 #include "tagutils-flc.c"
146 #include "tagutils-asf.c"
147 #include "tagutils-wav.c"
148 #include "tagutils-pcm.c"
149 #include "tagutils-plist.c"
151 //*********************************************************************************
153 #define MAYBEFREE(a) { if((a)) free((a)); };
155 freetags(struct song_metadata
*psong
)
159 MAYBEFREE(psong
->path
);
160 MAYBEFREE(psong
->image
);
161 MAYBEFREE(psong
->title
);
162 MAYBEFREE(psong
->album
);
163 MAYBEFREE(psong
->genre
);
164 MAYBEFREE(psong
->comment
);
165 for(role
= ROLE_START
; role
<= ROLE_LAST
; role
++)
167 MAYBEFREE(psong
->contributor
[role
]);
168 MAYBEFREE(psong
->contributor_sort
[role
]);
170 MAYBEFREE(psong
->grouping
);
171 MAYBEFREE(psong
->mime
);
172 MAYBEFREE(psong
->dlna_pn
);
173 MAYBEFREE(psong
->tagversion
);
174 MAYBEFREE(psong
->musicbrainz_albumid
);
175 MAYBEFREE(psong
->musicbrainz_trackid
);
176 MAYBEFREE(psong
->musicbrainz_artistid
);
177 MAYBEFREE(psong
->musicbrainz_albumartistid
);
182 _get_fileinfo(char *file
, struct song_metadata
*psong
)
186 // dispatch to appropriate tag handler
187 for(hdl
= taghandlers
; hdl
->type
; ++hdl
)
188 if(!strcmp(hdl
->type
, psong
->type
))
191 if(hdl
->get_fileinfo
)
192 return hdl
->get_fileinfo(file
, psong
);
199 _make_composite_tags(struct song_metadata
*psong
)
205 if(!psong
->contributor
[ROLE_ARTIST
] &&
206 (psong
->contributor
[ROLE_BAND
] || psong
->contributor
[ROLE_CONDUCTOR
]))
208 if(psong
->contributor
[ROLE_BAND
])
209 len
+= strlen(psong
->contributor
[ROLE_BAND
]);
210 if(psong
->contributor
[ROLE_CONDUCTOR
])
211 len
+= strlen(psong
->contributor
[ROLE_CONDUCTOR
]);
215 psong
->contributor
[ROLE_ARTIST
] = (char*)calloc(len
, 1);
216 if(psong
->contributor
[ROLE_ARTIST
])
218 if(psong
->contributor
[ROLE_BAND
])
219 strcat(psong
->contributor
[ROLE_ARTIST
], psong
->contributor
[ROLE_BAND
]);
221 if(psong
->contributor
[ROLE_BAND
] && psong
->contributor
[ROLE_CONDUCTOR
])
222 strcat(psong
->contributor
[ROLE_ARTIST
], " - ");
224 if(psong
->contributor
[ROLE_CONDUCTOR
])
225 strcat(psong
->contributor
[ROLE_ARTIST
], psong
->contributor
[ROLE_CONDUCTOR
]);
229 #if 0 // already taken care of by scanner.c
233 psong
->title
= strdup(psong
->basename
);
234 suffix
= strrchr(psong
->title
, '.');
235 if(suffix
) *suffix
= '\0';
241 /*****************************************************************************/
244 _get_tags(char *file
, struct song_metadata
*psong
)
249 for(hdl
= taghandlers
; hdl
->type
; ++hdl
)
250 if(!strcasecmp(hdl
->type
, psong
->type
))
255 return hdl
->get_tags(file
, psong
);
261 /*****************************************************************************/
264 readtags(char *path
, struct song_metadata
*psong
, struct stat
*stat
, char *lang
, char *type
)
269 lang_index
= _lang2cp(lang
);
271 memset((void*)psong
, 0, sizeof(struct song_metadata
));
272 psong
->path
= strdup(path
);
275 fname
= strrchr(psong
->path
, '/');
276 psong
->basename
= fname
? fname
+ 1 : psong
->path
;
280 if(!psong
->time_modified
)
281 psong
->time_modified
= stat
->st_mtime
;
282 psong
->file_size
= stat
->st_size
;
286 if( _get_tags(path
, psong
) == 0 )
288 _make_composite_tags(psong
);
292 return _get_fileinfo(path
, psong
);