xref: /MusicPlayer2/MusicPlayer2/Lyric.h (revision e51d77514999ce70db10740029cbbfec98e7c854)
1 #pragma once
2 #include"Time.h"
3 #include "Common.h"
4 
5 class CLyrics
6 {
7 public:
8     struct Lyric               // 一句歌词的结构体
9     {
10         int time_start_raw{};   // 行开始时间(初始化时写入之后只读)
11         int time_span_raw{};    // 行持续时间(初始化时写入之后只读)
12         int time_start{};       // 行开始时间(偏移量即时应用)
13         int time_span{};        // 行持续时间(偏移量即时应用)
14         wstring text;           // 歌词的文本
15         wstring translate;      // 歌词的翻译
16         vector<int> split;      // 逐字歌词对text的分割位置
17         vector<int> word_time;  // 分割后各字持续时间(毫秒),未经Normalize仅限GetLyricProgress使用,其他位置不应使用防止出现意料之外的行为
18 
19         // 重载小于号运算符,用于对歌词按时间标签排序
20         bool operator<(const Lyric& lyric) const
21         {
22             return lyric.time_start_raw > time_start_raw;
23         }
24     };
25 
26     enum class LyricType
27     {
28         LY_AUTO,                // 根据扩展名判断
29         LY_LRC,
30         LY_LRC_NETEASE,
31         LY_KSC,
32         LY_VTT,
33     };
34 
35     const static vector<wstring> m_surpported_lyric;            // 支持的歌词格式的扩展名列表
36 
37 private:
38     wstring m_file;                            // 歌词文件的文件名
39     vector<Lyric> m_lyrics;                    // 储存每一句歌词(包含时间标签和文本)
40     vector<wstring> m_lyrics_str;              // 储存未拆分时间标签的每一句歌词
41     CodeType m_code_type{ CodeType::ANSI };    // 歌词文本的编码类型
42     LyricType m_lyric_type{ LyricType::LY_LRC };    // 歌词类型
43 
44     wstring m_id;          // 歌词中的id标签(网易云音乐中的歌曲id,我自己加的,标准的lrc文件没有这个标签)
45     wstring m_ti;          // 歌词中的ti标签
46     wstring m_ar;          // 歌词中的ar标签
47     wstring m_al;          // 歌词中的al标签
48     wstring m_by;          // 歌词中的by标签
49     bool m_id_tag{ false };    // 歌词中是否含有id标签
50     bool m_ti_tag{ false };    // 歌词中是否含有ti标签
51     bool m_ar_tag{ false };    // 歌词中是否含有ar标签
52     bool m_al_tag{ false };    // 歌词中是否含有al标签
53     bool m_by_tag{ false };    // 歌词中是否含有by标签
54 
55     int m_offset{};                            // 歌词偏移量
56     bool m_offset_tag{ false };                // 歌词是否包含偏移量标签
57 
58     bool m_modified{ false };                  // 歌词是否已经修改
59     bool m_translate{ false };                 // 歌词是否包含翻译
60 
61     bool m_text_and_translatein_in_same_line{ true };  //原文和翻译是否在同一行歌词中
62 
63 public:
64     // 判断文件是否为歌词文件
65     static bool FileIsLyric(const wstring& file_name);
66 
67     /**
68      * @brief   解析歌词的时间标签
69      * @param[in]   const wstring & lyric_text 一行歌词文本
70      * @param[out]  Time & time 得到的时间标签
71      * @return  bool 是否成功
72      */
73     static bool ParseLyricTimeTag(const wstring& lyric_text, Time& time, int& pos_start, int& pos_end, wchar_t bracket_left, wchar_t bracket_right);
74 
75 private:
76     // 删除歌词中时间标签超过100分钟的歌词(使用时必须确保歌词已经按时间标签排序),对新下载的歌词使用
77     void DeleteRedundantLyric();
78     // 解析m_lyrics_str并将结果保存在m_lyrics中
79     void DisposeLrc();
80     void DisposeLrcNetease();
81     void DisposeKsc();
82     void DisposeWebVTT();
83     // 将歌词中信息全部填入m_lyrics后或偏移量调整后调用,负责修正/填补信息
84     void NormalizeLyric();
85 
86 public:
87     CLyrics(const wstring& file_name, const LyricType& lyric_type = LyricType::LY_AUTO);
CLyrics()88     CLyrics() {}
89 
90     // 从原始歌词字符串加载解析歌词(与构造函数类似)
91     void LyricsFromRowString(const wstring& lryic_str, const LyricType& lyric_type = LyricType::LY_LRC);
92 
93     // 判断是否有歌词
94     bool IsEmpty() const;
95 
96     LyricType GetLyricType() const;
97 
98     // 根据时间返回该时间对应的原始歌词序号,多行歌词使用
99     // 返回index直接与下标对应,-1为在第一行歌词之前,超过最后一行歌词后保持为m_lyrics.size() - 1
100     int GetLyricIndex(Time time) const;
101     // 根据索引返回一句歌词,索引越界返回空歌词,多行歌词使用
102     Lyric GetLyric(int index) const;
103 private:
104     // 将当前时间对应的index( 必须小于m_lyrics.size() )偏移到非空歌词,is_next为true时返回索引可能越界
105     int GetLyricIndexIgnoreBlank(int index, bool is_next) const;
106     // 获取指定非空歌词之前的空白时长(index无效时返回0)
107     int GetBlankTimeBeforeLyric(int index) const;
108 public:
109     // 提供给单双行歌词使用,ignore_blank为true时忽略空行,若同时blank2mark为true则将空行替换为进度符号,非原始文本
110     // is_next为true时为下一句歌词,否则为当前歌词
111     Lyric GetLyric(Time time, bool is_next, bool ignore_blank, bool blank2mark) const;
112     // 根据时间返回该时间所对应的歌词的进度(0~1000)(用于使歌词以卡拉OK样式显示/长歌词滚动也需要正确的歌词进度)
113     // ignore_blank为true时忽略空行,若同时blank2mark为true则将空行替换为进度符号
114     // 注意进度为1000时表示当前歌词“已结束”,不要进行高亮并应根据需要进行高亮取消操作,由于逐字歌词引入此状态可能维持一段时间
115     int GetLyricProgress(Time time, bool ignore_blank, bool blank2mark, std::function<int(const wstring&)> measure) const;
116 
117     // 获得歌词文本的编码类型
118     CodeType GetCodeType() const;
119     // 获取歌词文件的路径+文件名
GetPathName()120     wstring GetPathName() const { return m_file; }
121     // 返回所有歌词(仅包含全部歌词文本,不含标识标签和时间标签)。with_translate:是否包含翻译(如果有)
122     wstring GetAllLyricText(bool with_translate = false) const;
123     // 返回所有歌词的字符串,原始样式,包含全部标签(提供给歌词编辑使用)
124     wstring GetLyricsString() const;
125     // 返回所有歌词的字符串,以保存的样式,包含全部标签(将歌词偏移保存到每个时间标签中)
126     // lyric_and_traslation_in_same_line:歌词和翻译在同一行中,使用" / "分隔。如果为false,则歌词和翻译为两行具有相同时间标签的歌词
127     // lyric_type:歌词保存的格式,如果为LyricType::LY_AUTO,则使用歌词原本的格式保存
128     wstring GetLyricsString2(bool lyric_and_traslation_in_same_line = true, LyricType lyric_type = LyricType::LY_AUTO) const;
129 
130     // 返回歌词修改标志
IsModified()131     bool IsModified() const { return m_modified; }
132     // 设置歌词更改标志
SetModified(bool modified)133     void SetModified(bool modified) { m_modified = modified; }
134     // 返回歌词是否含有翻译
IsTranslated()135     bool IsTranslated() const { return m_translate; }
136 
137     // 返回歌词总数
GetLyricCount()138     int GetLyricCount() const{ return static_cast<int>(m_lyrics.size()); }
139 
140     // 保存歌词(将歌词偏移保存到每个时间标签中)
141     // lyric_and_traslation_in_same_line:歌词和翻译在同一行中,使用" / "分隔。如果为false,则歌词和翻译为两行具有相同时间标签的歌词
142     void SaveLyric2(bool lyric_and_traslation_in_same_line = true);
143 
144     // 先进行按时间排序,如果歌词中有相同时间标签的歌词,则将第二行视作第一行的翻译进行合并,参数为允许误差(ms)
145     void CombineSameTimeLyric(int error = 0);
146     // 交换歌词文本和翻译
147     void SwapTextAndTranslation();
148     // 时间标签提前一句
149     void TimeTagForward();
150     // 时间标签延后一句
151     void TimeTagDelay();
152     // 从歌词原文的括号中提取翻译,丢弃原有翻译
153     void ExtractTranslationFromBrackets();
154 
155     // 调整歌词的偏移量
156     void AdjustLyric(int offset);
157 
158     // 获取标题
GetTitle()159     wstring GetTitle() const { return m_ti; }
160     // 获取艺术家
GetAritst()161     wstring GetAritst() const { return m_ar; }
162     // 获取专辑
GetAlbum()163     wstring GetAlbum() const { return m_al; }
164     // 获取保存在歌词中的网易云音乐的歌曲ID
GetSongId()165     wstring GetSongId() const { return m_id; }
166 
167     // 中文繁简转换
168     void ChineseConvertion(bool simplified);
169 
IsTextAndTranslationInSameLine()170     bool IsTextAndTranslationInSameLine() const { return m_text_and_translatein_in_same_line; }
171 };
172