xref: /MusicPlayer2/MusicPlayer2/AudioTag.cpp (revision 5171fd65328eb0ef2baf72c5bf4229472569c4f3)
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