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