1use symphonia_core::errors::{unsupported_error, Result};
11use symphonia_core::io::ReadBytes;
12use symphonia_core::meta::{MetadataBuilder, StandardTagKey, Tag, Value};
13
14const GENRES: &[&str] = &[
15 "Blues",
17 "Classic rock",
18 "Country",
19 "Dance",
20 "Disco",
21 "Funk",
22 "Grunge",
23 "Hip-Hop",
24 "Jazz",
25 "Metal",
26 "New Age",
27 "Oldies",
28 "Other",
29 "Pop",
30 "Rhythm and Blues",
31 "Rap",
32 "Reggae",
33 "Rock",
34 "Techno",
35 "Industrial",
36 "Alternative",
37 "Ska",
38 "Death metal",
39 "Pranks",
40 "Soundtrack",
41 "Euro-Techno",
42 "Ambient",
43 "Trip-Hop",
44 "Vocal",
45 "Jazz & Funk",
46 "Fusion",
47 "Trance",
48 "Classical",
49 "Instrumental",
50 "Acid",
51 "House",
52 "Game",
53 "Sound clip",
54 "Gospel",
55 "Noise",
56 "Alternative Rock",
57 "Bass",
58 "Soul",
59 "Punk",
60 "Space",
61 "Meditative",
62 "Instrumental Pop",
63 "Instrumental Rock",
64 "Ethnic",
65 "Gothic",
66 "Darkwave",
67 "Techno-Industrial",
68 "Electronic",
69 "Pop-Folk",
70 "Eurodance",
71 "Dream",
72 "Southern Rock",
73 "Comedy",
74 "Cult",
75 "Gangsta",
76 "Top 40",
77 "Christian Rap",
78 "Pop/Funk",
79 "Jungle",
80 "Native US",
81 "Cabaret",
82 "New Wave",
83 "Psychedelic",
84 "Rave",
85 "Show tunes",
86 "Trailer",
87 "Lo-Fi",
88 "Tribal",
89 "Acid Punk",
90 "Acid Jazz",
91 "Polka",
92 "Retro",
93 "Musical",
94 "Rock 'n Roll",
95 "Hard Rock",
96 "Folk",
98 "Folk-Rock",
99 "National Folk",
100 "Swing",
101 "Fast Fusion",
102 "Bebop",
103 "Latin",
104 "Revival",
105 "Celtic",
106 "Bluegrass",
107 "Avantgarde",
108 "Gothic Rock",
109 "Progressive Rock",
110 "Psychedelic Rock",
111 "Symphonic Rock",
112 "Slow rock",
113 "Big Band",
114 "Chorus",
115 "Easy Listening",
116 "Acoustic",
117 "Humour",
118 "Speech",
119 "Chanson",
120 "Opera",
121 "Chamber music",
122 "Symphonia",
123 "Symphony",
124 "Booty bass",
125 "Primus",
126 "Porn groove",
127 "Satire",
128 "Slow jam",
129 "Club",
130 "Tango",
131 "Samba",
132 "Folklore",
133 "Ballad",
134 "Power ballad",
135 "Rhythmic Soul",
136 "Freestyle",
137 "Duet",
138 "Punk Rock",
139 "Drum solo",
140 "A cappella",
141 "Euro-House",
142 "Dance Hall",
143 "Goa",
144 "Drum & Bass",
145 "Club-House",
146 "Hardcore Techno",
147 "Terror",
148 "Indie",
149 "BritPop",
150 "(133)",
151 "Polsk Punk",
152 "Beat",
153 "Christian Gangsta Rap",
154 "Heavy Metal",
155 "Black Metal",
156 "Crossover",
157 "Contemporary Christian",
158 "Christian rock",
159 "Merengue",
160 "Salsa",
161 "Thrash Metal",
162 "Anime",
163 "Jpop",
164 "Synthpop",
165 "Abstract",
167 "Art Rock",
168 "Baroque",
169 "Bhangra",
170 "Big beat",
171 "Breakbeat",
172 "Chillout",
173 "Downtempo",
174 "Dub",
175 "EBM",
176 "Eclectic",
177 "Electro",
178 "Electroclash",
179 "Emo",
180 "Experimental",
181 "Garage",
182 "Global",
183 "IDM",
184 "Illbient",
185 "Industro-Goth",
186 "Jam Band",
187 "Krautrock",
188 "Leftfield",
189 "Lounge",
190 "Math Rock",
191 "New Romantic",
192 "Nu-Breakz",
193 "Post-Punk",
194 "Post-Rock",
195 "Psytrance",
196 "Shoegaze",
197 "Space Rock",
198 "Trop Rock",
199 "World Music",
200 "Neoclassical",
201 "Audiobook",
202 "Audio theatre",
203 "Neue Deutsche Welle",
204 "Podcast",
205 "Indie-Rock",
206 "G-Funk",
207 "Dubstep",
208 "Garage Rock",
209 "Psybient",
210];
211
212pub fn read_id3v1<B: ReadBytes>(reader: &mut B, metadata: &mut MetadataBuilder) -> Result<()> {
213 let marker = reader.read_triple_bytes()?;
215
216 if marker != *b"TAG" {
217 return unsupported_error("id3v1: Not an ID3v1 tag");
218 }
219
220 let buf = reader.read_boxed_slice_exact(125)?;
221
222 let title = decode_iso8859_text(&buf[0..30]);
223 if !title.is_empty() {
224 metadata.add_tag(Tag::new(Some(StandardTagKey::TrackTitle), "TITLE", Value::from(title)));
225 }
226
227 let artist = decode_iso8859_text(&buf[30..60]);
228 if !artist.is_empty() {
229 metadata.add_tag(Tag::new(Some(StandardTagKey::Artist), "ARTIST", Value::from(artist)));
230 }
231
232 let album = decode_iso8859_text(&buf[60..90]);
233 if !album.is_empty() {
234 metadata.add_tag(Tag::new(Some(StandardTagKey::Album), "ALBUM", Value::from(album)));
235 }
236
237 let year = decode_iso8859_text(&buf[90..94]);
238 if !year.is_empty() {
239 metadata.add_tag(Tag::new(Some(StandardTagKey::Date), "DATE", Value::from(year)));
240 }
241
242 let comment = if buf[122] == 0 {
243 let track = buf[123];
244
245 metadata.add_tag(Tag::new(Some(StandardTagKey::TrackNumber), "TRACK", Value::from(track)));
246
247 decode_iso8859_text(&buf[94..122])
248 }
249 else {
250 decode_iso8859_text(&buf[94..124])
251 };
252
253 if !comment.is_empty() {
254 metadata.add_tag(Tag::new(Some(StandardTagKey::Comment), "COMMENT", Value::from(comment)));
255 }
256
257 let genre_idx = buf[124] as usize;
258
259 if genre_idx < GENRES.len() && genre_idx != 133 {
262 metadata.add_tag(Tag::new(
263 Some(StandardTagKey::Genre),
264 "GENRE",
265 Value::from(GENRES[genre_idx]),
266 ));
267 }
268
269 Ok(())
270}
271
272fn decode_iso8859_text(data: &[u8]) -> String {
273 data.iter().filter(|&b| *b > 0x1f).map(|&b| b as char).collect()
274}
275
276pub mod util {
277 use super::GENRES;
278
279 pub fn genre_name(index: u8) -> Option<&'static &'static str> {
281 GENRES.get(usize::from(index))
282 }
283}