1 #include "stdafx.h"
2 #include "AudioTag.h"
3 #include "TagLibHelper.h"
4 #include "AudioTagOld.h"
5 #include "CueFile.h"
6 #include "FilePathHelper.h"
7
CAudioTag(SongInfo & song_info,HSTREAM hStream)8 CAudioTag::CAudioTag(SongInfo& song_info, HSTREAM hStream)
9 :m_song_info{ song_info }, m_hStream{ hStream }
10 {
11 ASSERT(!m_song_info.file_path.empty());
12
13 //获取通道信息
14 BASS_CHANNELINFO channel_info{};
15 if (hStream != 0)
16 BASS_ChannelGetInfo(hStream, &channel_info);
17 //根据通道信息判断音频文件的类型
18 m_type = CAudioCommon::GetAudioTypeByBassChannel(channel_info.ctype);
19
20 //如果获取不到文件类型,这里根据扩展名再判断
21 if (m_type == AudioType::AU_OTHER)
22 m_type = CAudioCommon::GetAudioTypeByFileName(m_song_info.file_path);
23 }
24
CAudioTag(SongInfo & song_info,AudioType type)25 CAudioTag::CAudioTag(SongInfo& song_info, AudioType type)
26 :m_song_info{ song_info }, m_type{ type }
27 {
28 ASSERT(!m_song_info.file_path.empty());
29 }
30
CAudioTag(const wstring & file_path)31 CAudioTag::CAudioTag(const wstring& file_path)
32 : m_song_info{ m_no_use }
33 {
34 ASSERT(!file_path.empty());
35 m_no_use.file_path = file_path;
36 m_type = CAudioCommon::GetAudioTypeByFileName(m_song_info.file_path);
37 }
38
~CAudioTag()39 CAudioTag::~CAudioTag()
40 {
41 }
42
GetAudioTag()43 bool CAudioTag::GetAudioTag()
44 {
45 bool succeed{ false };
46 m_song_info.tag_type = 0;
47 if (m_song_info.is_cue)
48 {
49 succeed = GetCueTag(m_song_info);
50 }
51 else
52 {
53 switch (m_type)
54 {
55 case AU_MP3:
56 CTagLibHelper::GetMpegTagInfo(m_song_info);
57 succeed = true;
58 break;
59 case AU_WMA_ASF:
60 CTagLibHelper::GetAsfTagInfo(m_song_info);
61 succeed = true;
62 break;
63 case AU_OGG:
64 CTagLibHelper::GetOggTagInfo(m_song_info);
65 succeed = true;
66 break;
67 case AU_MP4:
68 CTagLibHelper::GetM4aTagInfo(m_song_info);
69 succeed = true;
70 break;
71 case AU_APE:
72 CTagLibHelper::GetApeTagInfo(m_song_info);
73 succeed = true;
74 break;
75 case AU_FLAC:
76 CTagLibHelper::GetFlacTagInfo(m_song_info);
77 succeed = true;
78 break;
79 case AU_WAV:
80 CTagLibHelper::GetWavTagInfo(m_song_info);
81 succeed = true;
82 break;
83 case AU_AIFF:
84 CTagLibHelper::GetAiffTagInfo(m_song_info);
85 succeed = true;
86 break;
87 case AU_MPC:
88 CTagLibHelper::GetMpcTagInfo(m_song_info);
89 succeed = true;
90 break;
91 case AU_OPUS:
92 //CTagLibHelper::GetOpusTagInfo(m_song_info);
93 {
94 CAudioTagOld audio_tag_old(m_hStream, m_song_info, m_type);
95 audio_tag_old.GetOggTag();
96 }
97 break;
98 case AU_WV:
99 CTagLibHelper::GetWavPackTagInfo(m_song_info);
100 succeed = true;
101 break;
102 case AU_TTA:
103 CTagLibHelper::GetTtaTagInfo(m_song_info);
104 succeed = true;
105 break;
106 case AudioType::AU_SPX:
107 CTagLibHelper::GetSpxTagInfo(m_song_info);
108 succeed = true;
109 break;
110 case AudioType::AU_AAC:
111 //CTagLibHelper::GetAnyFileTagInfo(m_song_info);
112 {
113 CAudioTagOld audio_tag_old(m_hStream, m_song_info, m_type);
114 audio_tag_old.GetTagDefault();
115 }
116 break;
117 case AU_CUE:
118 break;
119 case AU_MIDI:
120 break;
121 case AU_OTHER:
122 break;
123 default:
124 break;
125 }
126 }
127 CCommon::StringNormalize(m_song_info.title);
128 CCommon::StringNormalize(m_song_info.artist);
129 CCommon::StringNormalize(m_song_info.album);
130 CCommon::StringNormalize(m_song_info.genre);
131 CCommon::StringNormalize(m_song_info.comment);
132 return succeed;
133 }
134
135
GetAudioTagPropertyMap(std::map<wstring,wstring> & property_map)136 void CAudioTag::GetAudioTagPropertyMap(std::map<wstring, wstring>& property_map)
137 {
138 if (m_song_info.is_cue)
139 {
140 GetCuePropertyMap(m_song_info, property_map);
141 }
142 else
143 {
144 switch (m_type)
145 {
146 case AU_MP3:
147 CTagLibHelper::GetMpegPropertyMap(m_song_info.file_path, property_map);
148 break;
149 case AU_WMA_ASF:
150 CTagLibHelper::GetAsfPropertyMap(m_song_info.file_path, property_map);
151 break;
152 case AU_OGG:
153 CTagLibHelper::GetOggPropertyMap(m_song_info.file_path, property_map);
154 break;
155 case AU_MP4:
156 CTagLibHelper::GetM4aPropertyMap(m_song_info.file_path, property_map);
157 break;
158 case AU_APE:
159 CTagLibHelper::GetApePropertyMap(m_song_info.file_path, property_map);
160 break;
161 case AU_FLAC:
162 CTagLibHelper::GetFlacPropertyMap(m_song_info.file_path, property_map);
163 break;
164 case AU_WAV:
165 CTagLibHelper::GetWavPropertyMap(m_song_info.file_path, property_map);
166 break;
167 case AU_AIFF:
168 CTagLibHelper::GetAiffPropertyMap(m_song_info.file_path, property_map);
169 break;
170 case AU_MPC:
171 CTagLibHelper::GetMpcPropertyMap(m_song_info.file_path, property_map);
172 break;
173 case AU_OPUS:
174 //CTagLibHelper::GetOpusPropertyMap(m_song_info.file_path, property_map);
175 break;
176 case AU_WV:
177 CTagLibHelper::GetWavPackPropertyMap(m_song_info.file_path, property_map);
178 break;
179 case AU_TTA:
180 CTagLibHelper::GetTtaPropertyMap(m_song_info.file_path, property_map);
181 break;
182 case AudioType::AU_SPX:
183 CTagLibHelper::GetSpxPropertyMap(m_song_info.file_path, property_map);
184 break;
185 case AudioType::AU_AAC:
186 break;
187 case AU_CUE:
188 break;
189 case AU_MIDI:
190 break;
191 case AU_OTHER:
192 break;
193 default:
194 break;
195 }
196 }
197 }
198
GetAlbumCover(int & image_type,const wchar_t * file_name,size_t * file_size)199 wstring CAudioTag::GetAlbumCover(int& image_type, const wchar_t* file_name, size_t* file_size)
200 {
201 image_type = -1;
202 string image_contents;
203 switch (m_type)
204 {
205 case AU_MP3:
206 image_contents = CTagLibHelper::GetMp3AlbumCover(m_song_info.file_path, image_type);
207 break;
208 case AU_WMA_ASF:
209 image_contents = CTagLibHelper::GetAsfAlbumCover(m_song_info.file_path, image_type);
210 break;
211 case AU_MP4:
212 image_contents = CTagLibHelper::GetM4aAlbumCover(m_song_info.file_path, image_type);
213 break;
214 case AU_APE:
215 image_contents = CTagLibHelper::GetApeAlbumCover(m_song_info.file_path, image_type);
216 break;
217 case AU_FLAC:
218 image_contents = CTagLibHelper::GetFlacAlbumCover(m_song_info.file_path, image_type);
219 break;
220 case AU_OGG:
221 image_contents = CTagLibHelper::GetOggAlbumCover(m_song_info.file_path, image_type);
222 break;
223 case AU_WAV:
224 image_contents = CTagLibHelper::GetWavAlbumCover(m_song_info.file_path, image_type);
225 break;
226 case AU_TTA:
227 image_contents = CTagLibHelper::GetTtaAlbumCover(m_song_info.file_path, image_type);
228 break;
229 case AU_OPUS:
230 //image_contents = CTagLibHelper::GetOpusAlbumCover(m_song_info.file_path, image_type);
231 break;
232 case AU_SPX:
233 image_contents = CTagLibHelper::GetSpxAlbumCover(m_song_info.file_path, image_type);
234 break;
235 case AU_AIFF:
236 image_contents = CTagLibHelper::GetAiffAlbumCover(m_song_info.file_path, image_type);
237 break;
238 case AU_MPC:
239 image_contents = CTagLibHelper::GetMpcAlbumCover(m_song_info.file_path, image_type);
240 break;
241 case AU_WV:
242 image_contents = CTagLibHelper::GetWavePackAlbumCover(m_song_info.file_path, image_type);
243 break;
244 case AU_CUE:
245 break;
246 case AU_MIDI:
247 break;
248 default:
249 {
250 CAudioTagOld audio_tag_old(m_hStream, m_song_info, m_type);
251 image_contents = audio_tag_old.GetAlbumCoverDefault(image_type);
252 }
253 break;
254 }
255
256 if (file_size != nullptr)
257 *file_size = image_contents.size();
258
259 //将专辑封面保存到临时目录
260 wstring file_path{ CCommon::GetTemplatePath() };
261 wstring _file_name;
262 if (file_name == nullptr)
263 _file_name = ALBUM_COVER_NAME;
264 else
265 _file_name = file_name;
266 if (!image_contents.empty())
267 {
268 file_path += _file_name;
269 ofstream out_put{ file_path, std::ios::binary };
270 out_put << image_contents;
271 return file_path;
272 }
273 else
274 {
275 image_type = -1;
276 return wstring();
277 }
278 }
279
GetAudioLyric()280 wstring CAudioTag::GetAudioLyric()
281 {
282 switch (m_type)
283 {
284 case AU_MP3:
285 return CTagLibHelper::GetMpegLyric(m_song_info.file_path);
286 case AU_WMA_ASF:
287 return CTagLibHelper::GetAsfLyric(m_song_info.file_path);
288 case AU_MP4:
289 return CTagLibHelper::GetM4aLyric(m_song_info.file_path);
290 case AU_FLAC:
291 return CTagLibHelper::GetFlacLyric(m_song_info.file_path);
292 case AU_WAV:
293 return CTagLibHelper::GetWavLyric(m_song_info.file_path);
294 default:
295 break;
296 }
297
298 return wstring();
299 }
300
WriteAudioLyric(const wstring & lyric_contents)301 bool CAudioTag::WriteAudioLyric(const wstring& lyric_contents)
302 {
303 switch (m_type)
304 {
305 case AU_MP3:
306 return CTagLibHelper::WriteMpegLyric(m_song_info.file_path, lyric_contents);
307 case AU_FLAC:
308 return CTagLibHelper::WriteFlacLyric(m_song_info.file_path, lyric_contents);
309 case AU_MP4:
310 return CTagLibHelper::WriteM4aLyric(m_song_info.file_path, lyric_contents);
311 case AU_WMA_ASF:
312 return CTagLibHelper::WriteAsfLyric(m_song_info.file_path, lyric_contents);
313 case AU_WAV:
314 return CTagLibHelper::WriteWavLyric(m_song_info.file_path, lyric_contents);
315 default:
316 break;
317 }
318 return false;
319 }
320
WriteAudioTag()321 bool CAudioTag::WriteAudioTag()
322 {
323 if (m_song_info.is_cue)
324 {
325 return WriteCueTag(m_song_info);
326 }
327 else
328 {
329 switch (m_type)
330 {
331 case AU_MP3:
332 return CTagLibHelper::WriteMpegTag(m_song_info);
333 case AU_WMA_ASF:
334 return CTagLibHelper::WriteAsfTag(m_song_info);
335 case AU_OGG:
336 return CTagLibHelper::WriteOggTag(m_song_info);
337 case AU_MP4:
338 return CTagLibHelper::WriteM4aTag(m_song_info);
339 case AU_APE:
340 return CTagLibHelper::WriteApeTag(m_song_info);
341 case AU_AIFF:
342 return CTagLibHelper::WriteAiffTag(m_song_info);
343 case AU_FLAC:
344 return CTagLibHelper::WriteFlacTag(m_song_info);
345 case AU_WAV:
346 return CTagLibHelper::WriteWavTag(m_song_info);
347 case AU_MPC:
348 return CTagLibHelper::WriteMpcTag(m_song_info);
349 case AU_DSD:
350 break;
351 case AU_OPUS:
352 //return CTagLibHelper::WriteOpusTag(m_song_info);
353 case AU_WV:
354 return CTagLibHelper::WriteWavPackTag(m_song_info);
355 case AU_SPX:
356 return CTagLibHelper::WriteSpxTag(m_song_info);
357 case AU_TTA:
358 return CTagLibHelper::WriteTtaTag(m_song_info);
359 case AU_CUE:
360 break;
361 default:
362 break;
363 }
364 return false;
365 }
366 }
367
WriteAlbumCover(const wstring & album_cover_path)368 bool CAudioTag::WriteAlbumCover(const wstring& album_cover_path)
369 {
370 switch (m_type)
371 {
372 case AU_MP3:
373 return CTagLibHelper::WriteMp3AlbumCover(m_song_info.file_path, album_cover_path);
374 case AU_WMA_ASF:
375 return CTagLibHelper::WriteAsfAlbumCover(m_song_info.file_path, album_cover_path);
376 case AU_OGG:
377 return CTagLibHelper::WriteOggAlbumCover(m_song_info.file_path, album_cover_path);
378 case AU_MP4:
379 return CTagLibHelper::WriteM4aAlbumCover(m_song_info.file_path, album_cover_path);
380 case AU_APE:
381 return CTagLibHelper::WriteApeAlbumCover(m_song_info.file_path, album_cover_path);
382 case AU_AIFF:
383 return CTagLibHelper::WriteAiffAlbumCover(m_song_info.file_path, album_cover_path);
384 case AU_FLAC:
385 return CTagLibHelper::WriteFlacAlbumCover(m_song_info.file_path, album_cover_path);
386 case AU_WAV:
387 return CTagLibHelper::WriteWavAlbumCover(m_song_info.file_path, album_cover_path);
388 case AU_MPC:
389 return CTagLibHelper::WriteMpcAlbumCover(m_song_info.file_path, album_cover_path);
390 case AU_DSD:
391 break;
392 case AU_OPUS:
393 //return CTagLibHelper::WriteOpusAlbumCover(m_song_info.file_path, album_cover_path);
394 case AU_WV:
395 return CTagLibHelper::WriteWavePackAlbumCover(m_song_info.file_path, album_cover_path);;
396 case AU_SPX:
397 return CTagLibHelper::WriteSpxAlbumCover(m_song_info.file_path, album_cover_path);
398 case AU_TTA:
399 return CTagLibHelper::WriteTtaAlbumCover(m_song_info.file_path, album_cover_path);
400 default:
401 break;
402 }
403 return false;
404 }
405
GetAudioCue()406 wstring CAudioTag::GetAudioCue()
407 {
408 switch (m_type)
409 {
410 case AU_APE:
411 return CTagLibHelper::GetApeCue(m_song_info.file_path);
412 default:
413 break;
414 }
415 return wstring();
416 }
417
GetAudioRating()418 void CAudioTag::GetAudioRating()
419 {
420 switch (m_type)
421 {
422 case AU_MP3:
423 m_song_info.rating = static_cast<BYTE>(CTagLibHelper::GetMepgRating(m_song_info.file_path));
424 break;
425 case AU_FLAC:
426 m_song_info.rating = static_cast<BYTE>(CTagLibHelper::GetFlacRating(m_song_info.file_path));
427 break;
428 case AU_WMA_ASF:
429 m_song_info.rating = static_cast<BYTE>(CTagLibHelper::GetWmaRating(m_song_info.file_path));
430 break;
431 default:
432 break;
433 }
434 }
435
WriteAudioRating()436 bool CAudioTag::WriteAudioRating()
437 {
438 switch (m_type)
439 {
440 case AU_MP3:
441 return CTagLibHelper::WriteMpegRating(m_song_info.file_path, m_song_info.rating);
442 case AU_FLAC:
443 return CTagLibHelper::WriteFlacRating(m_song_info.file_path, m_song_info.rating);
444 case AU_WMA_ASF:
445 return CTagLibHelper::WriteWmaRating(m_song_info.file_path, m_song_info.rating);
446 default:
447 break;
448 }
449 return false;
450 }
451
IsFileTypeTagWriteSupport(const wstring & ext)452 bool CAudioTag::IsFileTypeTagWriteSupport(const wstring& ext)
453 {
454 wstring _ext = ext;
455 CCommon::StringTransform(_ext, false);
456 AudioType type = CAudioCommon::GetAudioTypeByFileExtension(_ext);
457 return type == AU_MP3 || type == AU_FLAC || type == AU_MP4 || type == AU_WAV || type == AU_OGG || type == AU_APE
458 || type == AU_WV || type == AU_AIFF /*|| type == AU_OPUS*/ || type == AU_TTA || type == AU_WMA_ASF || type == AU_MPC
459 || type == AU_SPX;
460 }
461
IsFileTypeCoverWriteSupport(const wstring & ext)462 bool CAudioTag::IsFileTypeCoverWriteSupport(const wstring& ext)
463 {
464 wstring _ext = ext;
465 CCommon::StringTransform(_ext, false);
466 AudioType type = CAudioCommon::GetAudioTypeByFileExtension(_ext);
467 return type == AU_MP3 || type == AU_FLAC || type == AU_MP4 || type == AU_WMA_ASF || type == AU_WAV || type == AU_APE
468 || type == AU_OGG /*|| type == AU_OPUS*/ || type == AU_SPX || type == AU_AIFF || type == AU_MPC || type == AU_WV
469 || type == AU_TTA;
470 }
471
IsFileTypeLyricWriteSupport(const wstring & ext)472 bool CAudioTag::IsFileTypeLyricWriteSupport(const wstring& ext)
473 {
474 wstring _ext = ext;
475 CCommon::StringTransform(_ext, false);
476 AudioType type = CAudioCommon::GetAudioTypeByFileExtension(_ext);
477 return type == AU_MP3 || type == AU_FLAC || type == AU_MP4 || type == AU_WMA_ASF || type == AU_WAV;
478 }
479
IsFileRatingSupport(const wstring & ext)480 bool CAudioTag::IsFileRatingSupport(const wstring& ext)
481 {
482 wstring _ext = ext;
483 CCommon::StringTransform(_ext, false);
484 AudioType type = CAudioCommon::GetAudioTypeByFileExtension(_ext);
485 return type == AU_MP3 || type == AU_FLAC || type == AU_WMA_ASF;
486 }
487
GetCuePath(SongInfo & song_info)488 std::wstring CAudioTag::GetCuePath(SongInfo& song_info)
489 {
490 //为了兼容以前版本的媒体库中保存了没有cue_file_path的SongInfo,或者如果因为某些其他原因,
491 //song_info中的cue_file_path字段为空,则在这里根据音频文件的路径获取cue文件的路径。
492 //正常情况下,这个if里的代码应该不会被执行了。
493 if (song_info.is_cue && song_info.cue_file_path.empty() && !song_info.file_path.empty())
494 {
495 //获取cue文件的路径
496 CFilePathHelper cue_path(song_info.file_path);
497 cue_path.ReplaceFileExtension(L"cue");
498 if (!CCommon::FileExist(cue_path.GetFilePath()))
499 {
500 cue_path.SetFilePath(song_info.file_path + L".cue");
501 }
502 if (CCommon::FileExist(cue_path.GetFilePath()))
503 song_info.cue_file_path = cue_path.GetFilePath();
504 }
505 return song_info.cue_file_path;
506 }
507
GetCueTag(SongInfo & song_info)508 bool CAudioTag::GetCueTag(SongInfo& song_info)
509 {
510 std::wstring cue_path = GetCuePath(song_info);
511 if (!CCommon::FileExist(cue_path))
512 return false;
513
514 CCueFile cue_file(cue_path);
515 SongInfo cue_track{ cue_file.GetTrackInfo(song_info.file_path, song_info.track) };
516 if (cue_track.IsEmpty())
517 return false;
518 song_info.CopyAudioTag(cue_track);
519 song_info.start_pos = cue_track.start_pos;
520 if (cue_track.end_pos > cue_track.start_pos)
521 song_info.end_pos = cue_track.end_pos;
522
523 return true;
524 }
525
WriteCueTag(SongInfo & song_info)526 bool CAudioTag::WriteCueTag(SongInfo& song_info)
527 {
528 std::wstring cue_path = GetCuePath(song_info);
529 if (!CCommon::FileExist(cue_path))
530 return false;
531
532 CCueFile cue_file(cue_path);
533 SongInfo& cue_track{ cue_file.GetTrackInfo(song_info.file_path, song_info.track) };
534 if (cue_track.IsEmpty())
535 return false;
536 cue_track.CopyAudioTag(song_info);
537 //cue中的第一个音轨
538 SongInfo& first_track{ cue_file.GetAnalysisResult().front() };
539 //由于cue文件中,所有音轨共享“唱片集”、“流派”、“年份”、“注释”属性,因此需要将这些属性复制到第一个音轨的信息中,才能将它们写入到cue文件中
540 first_track.album = song_info.album;
541 first_track.genre = song_info.genre;
542 first_track.year = song_info.year;
543 first_track.comment = song_info.comment;
544 return cue_file.Save();
545 }
546
GetCuePropertyMap(SongInfo & song_info,std::map<wstring,wstring> & property_map)547 bool CAudioTag::GetCuePropertyMap(SongInfo& song_info, std::map<wstring, wstring>& property_map)
548 {
549 std::wstring cue_path = GetCuePath(song_info);
550 if (!CCommon::FileExist(cue_path))
551 return false;
552
553 CCueFile cue_file(cue_path);
554 property_map.clear();
555
556 auto parseCueProperty = [](std::wstring& key, std::wstring& value)
557 {
558 //如果key的前面有“REM”,则将其去掉
559 if (CCommon::StringLeftMatch(key, L"REM "))
560 key = key.substr(4);
561 //去掉value前后的引号
562 if (!value.empty() && value[0] == L'\"')
563 value = value.substr(1);
564 if (!value.empty() && value.back() == L'\"')
565 value.pop_back();
566 };
567
568 for (const auto& item : cue_file.GetCuePropertyMap())
569 {
570 std::wstring key = item.first;
571 std::wstring value = item.second;
572 parseCueProperty(key, value);
573 if (key == L"TITLE")
574 key = L"ALBUM";
575 if (key == L"PERFORMER")
576 key = L"ALBUMARTIST";
577 property_map[key] = value;
578 }
579 const auto& track_property_map = cue_file.GetTrackPropertyMap(song_info.file_path, song_info.track);
580 for (const auto& item : track_property_map)
581 {
582 std::wstring key = item.first;
583 std::wstring value = item.second;
584 parseCueProperty(key, value);
585 if (key == L"PERFORMER")
586 key = L"ARTIST";
587 property_map[key] = value;
588 }
589 return true;
590 }
591