1 #pragma once 2 #include"Common.h" 3 #include"Lyric.h" 4 #include"AudioTag.h" 5 #include "FilePathHelper.h" 6 #include "BASSMidiLibrary.h" 7 #include "GaussBlur.h" 8 #include "IPlayerCore.h" 9 #include "BassCore.h" 10 #include "SpectralDataHelper.h" 11 #include "MediaTransControls.h" 12 #include "MediaLibHelper.h" 13 #include "ListItem.h" 14 15 #define WM_PLAYLIST_INI_START (WM_USER+104) // 播放列表开始加载时的消息 16 #define WM_PLAYLIST_INI_COMPLATE (WM_USER+105) // 播放列表加载完成消息 17 #define WM_AFTER_SET_TRACK (WM_USER+106) // 通知主窗口更新显示的消息 18 #define WM_CONNOT_PLAY_WARNING (WM_USER+108) // 无法播放文件时弹出警告提示框的消息 19 #define WM_MUSIC_STREAM_OPENED (WM_USER+109) // 当音频文件打开时的消息 20 #define WM_POST_MUSIC_STREAM_OPENED (WM_USER+129) // 当音频文件打开前的消息 21 #define WM_AFTER_MUSIC_STREAM_CLOSED (WM_USER+137) // 当音频文件关闭后的消息 22 23 struct AlbumCoverInfo 24 { 25 int width{}; 26 int height{}; 27 int bpp{}; 28 bool size_exceed{}; 29 GetInfoAlbumCoverInfo30 void GetInfo(const CImage& image) 31 { 32 if (image.IsNull()) 33 { 34 width = 0; 35 height = 0; 36 bpp = 0; 37 } 38 else 39 { 40 width = image.GetWidth(); 41 height = image.GetHeight(); 42 bpp = image.GetBPP(); 43 } 44 } 45 }; 46 47 class CPlayer 48 { 49 public: 50 //用于向初始化播放列表传递信息的结构体 51 struct ThreadInfo 52 { 53 MediaLibRefreshMode refresh_mode{}; 54 bool play{}; // 加载完播放列表后是否立即播放 55 bool playlist_mode{}; // 是否为播放列表模式 56 int play_index{}; // 播放索引,播放列表模式下需要在cue解析时维持其指向 57 int process_percent{}; 58 wstring remove_list_path{}; // 进入初始化线程后通知主窗口移除此播放列表/文件夹 59 }; 60 //初始化播放列表的工作线程函数 61 static UINT IniPlaylistThreadFunc(LPVOID lpParam); 62 ThreadInfo m_thread_info; 63 64 enum ErrorState 65 { 66 ES_NO_ERROR, 67 ES_FILE_NOT_EXIST, 68 ES_FILE_CANNOT_BE_OPEN 69 }; 70 71 enum ABRepeatMode //AB循环的模式 72 { 73 AM_NONE, //无AB循环 74 AM_A_SELECTED, //已选择A点 75 AM_AB_REPEAT //AB循环中 76 }; 77 78 private: 79 CWinThread* m_pThread{}; //初始化播放列表的线程 80 81 IPlayerCore* m_pCore{}; 82 83 vector<SongInfo> m_playlist; // 播放列表,储存每个音乐文件的各种信息 84 85 private: 86 87 88 wstring m_path; // 文件夹模式下,当前播放文件的目录 89 wstring m_playlist_path; // 当前播放列表文件的路径 90 CMediaClassifier::ClassificationType m_media_lib_playlist_type; //播放列表模式为PM_MEDIA_LIB时的媒体库项目类型 91 wstring m_media_lib_playlist_name; //播放列表模式为PM_MEDIA_LIB时媒体库项目的名称 92 93 SongInfo m_current_song_tmp; // 临时存储歌曲的信息并在播放列表初始化完成后查找播放 94 int m_current_song_position_tmp; // 临时存储歌曲的播放位置(m_current_song_tmp被找到才会应用) 95 bool m_current_song_playing_tmp; // 临时存储歌曲是否正在播放(m_current_song_tmp被找到才会应用) 96 97 wstring m_current_file_type; 98 99 Time m_song_length; //正在播放的文件的长度 100 Time m_current_position; //当前播放到的位置 101 102 int m_total_time; //播放列表中所有曲目的时间(毫秒) 103 104 int m_index{ 0 }; //当前播放音乐的序号 105 int m_index_tmp{ 0 }; // 播放列表初始化中暂存当前播放音乐序号 106 107 int m_error_code{ 0 }; //储存错误代码 108 ErrorState m_error_state{}; 109 110 //正在播放标志(0:已停止,1:已暂停,2:正在播放)。 111 //注意此标志和IPlayerCore::GetPlayingState()返回值略有不同,比如在按下暂停键时,声音不会立刻停止,而是会有一个淡入淡出的效果, 112 //在声音还未完全停止时,IPlayerCore::GetPlayingState()将返回PS_PLAYING,但是这个值已经是PS_PAUSED了。 113 PlayingState m_playing{}; 114 RepeatMode m_repeat_mode; //循环模式(0:顺序播放,1:随机播放,2:列表循环,3:单曲循环) 115 int m_volume{ 100 }; //音量(百分比) 116 float m_speed{ 1 }; //播放速度 117 int m_pitch{ 0 }; //变调 118 119 float m_fft[FFT_SAMPLE]; //储存频谱分析的数据 120 float m_spectral_data[SPECTRUM_COL]{}; //用于显示的每个频谱柱形的高度 121 float m_spectral_peak[SPECTRUM_COL]{}; //频谱顶端的高度 122 CSpectralDataHelper m_spectrum_data_helper; 123 124 int m_equalizer_gain[EQU_CH_NUM]{}; //用于保存设置好的每个通道的增益 125 bool m_equ_enable{ false }; //指示是否允许均衡器 126 127 int m_equ_style{}; 128 int m_reverb_mix{}; //混响强度(0~100) 129 int m_reverb_time{ 1 }; //混响时间(单位10ms,1~300) 130 bool m_reverb_enable{ false }; //指示是否允许混响 131 132 CImage m_album_cover; //专辑封面 133 CImage m_album_cover_blur; //高斯模糊后的专辑封面 134 wstring m_album_cover_path; //专辑封面文件的路径 135 int m_album_cover_type; //专辑封面的格式 136 bool m_inner_cover{ false }; //如果专辑封面是内嵌图片,则为true 137 bool m_inner_lyric{ false }; //如果是内嵌歌词,则为true 138 bool m_is_osu{ false }; 139 140 SongInfo m_no_use; 141 public: 142 CLyrics m_Lyrics; //歌词 143 144 SortMode m_sort_mode; //排序方式 145 146 bool m_loading{ false }; //如果正在载入播放列表,则为true 147 148 bool m_enable_lastfm; // 当前歌曲是否启用了last.fm支持 149 150 private: 151 152 vector<int> m_shuffle_list; //储存乱序播放过的曲目序号 153 int m_shuffle_index{}; //乱序播放时当前的索引 154 bool m_is_shuffle_list_played{ false }; 155 inline int GetNextShuffleIdx() const; //返回乱序播放下下一曲的序号 156 inline int GetPrevShuffleIdx() const; //返回乱序播放下前一曲的序号 157 std::list<int> m_random_list; //随机播放模式下的历史记录,用于回溯之前的记录 158 deque<int> m_next_tracks{}; //下n首播放的歌曲,用于“下一首播放” 159 160 //播放列表模式 161 enum PlaylistMode 162 { 163 PM_FOLDER, //文件夹模式 164 PM_PLAYLIST, //播放列表模式 165 PM_MEDIA_LIB //媒体库模式 166 }; 167 168 PlaylistMode m_playlist_mode{ PM_FOLDER }; //当前的播放列表模式 169 170 Time m_a_repeat{}; //AB循环中A点的时间 171 Time m_b_repeat{}; //AB循环中B点的时间 172 ABRepeatMode m_ab_repeat_mode{}; 173 174 bool m_file_opend{ false }; //如果打开了一个文件,则为true 175 bool m_player_core_inited{ false }; //播放内核是否初始化 176 bool m_contain_sub_folder{ false }; //文件夹模式是否包含子文件夹 177 178 AlbumCoverInfo m_album_cover_info; 179 180 private: 181 //初始化播放内核 182 void IniPlayerCore(); 183 void UnInitPlayerCore(); 184 185 // 此方法进行重新填充m_playlist以及一些共有操作,最后会启动初始化播放列表线程函数,调用前必须停止播放 186 // 调用完此方法后请尽快返回并且尽量不要执行任何操作,应当提前进行或安排在IniPlaylistComplate中进行 187 // 此方法返回后的任何修改数据的操作都应视为(实际上也是如此)与IniPlaylistThreadFunc及IniPlaylistComplate处于竞争状态 188 // play参数会传递到IniPlaylistComplate指示是否播放,refresh_info指示初始化线程刷新级别 189 void IniPlayList(bool play = false, MediaLibRefreshMode refresh_mode = MR_MIN_REQUIRED, SongKey song = {}); 190 191 //应用一个均衡器通道的增益 192 void ApplyEqualizer(int channel, int gain); 193 194 public: 195 void SaveCurrentPlaylist(); 196 //退出时的处理 197 void OnExit(); 198 //播放列表加载完毕时的处理 199 void IniPlaylistComplate(); 200 void OnPlaylistChange(); //播放列表有修改时的相关操作 201 202 //设置均衡器(channel为通道,取值为0~9,gain为增益,取值为-15~15) 203 void SetEqualizer(int channel, int gain); 204 //获取指定均衡器通道的增益 205 int GeEqualizer(int channel); 206 //将保存好的每个通道的增益(m_equalizer_gain)设置到均衡器 207 void SetAllEqualizer(); 208 //将每个均衡器通道的增益复位 209 void ClearAllEqulizer(); 210 //均衡器开关 211 void EnableEqualizer(bool enable); GetEqualizerEnable()212 bool GetEqualizerEnable() const { return m_equ_enable; } 213 GetReverbMix()214 int GetReverbMix() const { return m_reverb_mix; } GetReverbTime()215 int GetReverbTime() const { return m_reverb_time; } SetReverbMix(int reverb_mix)216 void SetReverbMix(int reverb_mix) { m_reverb_mix = reverb_mix; } SetReverbTime(int reverb_time)217 void SetReverbTime(int reverb_time) { m_reverb_time = reverb_time; } 218 //混响开关 219 void EnableReverb(bool enable); GetReverbEnable()220 bool GetReverbEnable() const { return m_reverb_enable; } 221 GetARepeatPosition()222 Time GetARepeatPosition() const { return m_a_repeat; } GetBRepeatPosition()223 Time GetBRepeatPosition() const { return m_b_repeat; } GetABRepeatMode()224 ABRepeatMode GetABRepeatMode() const { return m_ab_repeat_mode; } 225 226 //设置当前播放位置为重复A点 227 bool SetARepeatPoint(); 228 //设置当前播放位置为重复B点 229 bool SetBRepeatPoint(); 230 //继续下一句AB重复(将当前重复B点设置为下一句重复A点,处于AB重复状态下才有效) 231 bool ContinueABRepeat(); 232 // 切换到下一个m_ab_repeat_mode 233 void DoABRepeat(); 234 //取消AB重复 235 void ResetABRepeat(); 236 GetAlbumCoverInfo()237 const AlbumCoverInfo& GetAlbumCoverInfo() const { return m_album_cover_info; } 238 239 private: 240 CPlayer(); 241 242 private: 243 static CPlayer m_instance; //CPlayer类唯一的对象 244 CCriticalSection m_album_cover_sync; //用于专辑封面的线程同步对象 245 std::timed_mutex m_play_status_sync; // 更改播放状态时加锁,请使用GetPlayStatusMutex获取 246 public: 247 // 在“稳态”(稳定的播放/暂停/停止)之间切换期间请先持有此锁,避免其他线程中途介入以及当前操作发生重入 248 // 持有锁的临界区应尽量长整个切换期间不应当发生解锁再加锁,主窗口定时器回调(TIMER_ELAPSE ms)/UI线程会以try_lock方式获取锁 249 // 启动初始化播放列表线程的方法以try_lock_for获取锁再正式进行,由IniPlaylistComplate解锁 注:更安全的if (m_loading) return; 250 // 以上加锁的操作及其中调用的方法切勿重复加锁会有未定义行为,也不能换成递归锁(防止重入) 251 // 判断与以上操作互斥的操作理论上也应当加锁但我没有检查会不会死锁,可以放弃的操作可以用try_lock_for进行 GetPlayStatusMutex()252 std::timed_mutex& GetPlayStatusMutex() { return m_play_status_sync; } 253 254 public: 255 //获取CPlayer类的唯一的对象 256 static CPlayer& GetInstance(); 257 ~CPlayer(); 258 //初始化CPlayer类 259 void Create(); 260 //使用文件列表初始化CPlayer类(添加到默认播放列表) 261 void CreateWithFiles(const vector<wstring>& files); 262 // 使用指定路径初始化CPlayer类(文件夹模式) 263 void CreateWithPath(const wstring& path); 264 //使用指定播放列表文件来初始化CPlayer类 265 void CreateWithPlaylist(const wstring& playlist_path); 266 //控制音乐播放 267 void MusicControl(Command command, int volume_step = 0); 268 //判断当前音乐是否播放完毕 269 bool SongIsOver() const; 270 //从播放内核获取当前播放到的位置(更新m_current_position),调用需要取得播放状态锁 271 void GetPlayerCoreCurrentPosition(); 272 //用m_volume的值设置音量 273 void SetVolume(); 274 275 //计算频谱分析 276 void CalculateSpectralData(); 277 //计算频谱分析顶端的高度 278 void CalculateSpectralDataPeak(); 279 //判断当前是否正在播放 280 bool IsPlaying() const; 281 282 // 播放指定序号的歌曲,如果是播放结束自动播放下一曲(主定时器),则auto_next为true(其他情况不能为true) 283 // auto_next为false时返回false说明没能取得播放状态锁,应当给出稍后再试的提示 284 bool PlayTrack(int song_track, bool auto_next = false); 285 // 设置指定序号歌曲为下一首播放的歌曲,无效的index会被忽略 286 bool PlayAfterCurrentTrack(const std::vector<int>& tracks_to_play); 287 // 设置指定SongInfo为下一首播放的歌曲,不存在于m_playlist的条目会被忽略 288 bool PlayAfterCurrentTrack(const std::vector<SongInfo>& tracks_to_play); 289 private: 290 void LoopPlaylist(int& song_track); 291 292 // 更新并保存最近播放文件夹/播放列表到文件,PlayTrack不需要保存playlist故设置参数控制 293 void SaveRecentInfoToFiles(bool save_playlist = true); 294 // 启动列表初始化方法前的共有操作 295 bool BeforeIniPlayList(bool continue_play = false, bool force_continue_play = false); 296 297 public: 298 // 以下方法调用后时间上直到IniPlaylistComplate的最后unlock为止 299 // 都是处于与IniPlaylistThreadFunc/IniPlaylistComplate/CMusicPlayerDlg::OnPlaylistIniComplate的数据竞争状态 300 // 这些方法已经尽量包办了所有需要的操作,调用方请尽快返回 301 // 返回false(addtoplaylist返回-1)时调用方需要放弃并提示用户重试,初始化流程需要主线程参与故返回false时等待无意义 302 303 #pragma region 列表初始化方法 304 305 // 切换为播放指定ListItem表示的列表,play表示是否立即播放 306 // force为false时允许continue_when_switch_playlist设置项的覆盖维持当前播放状态 307 // continue_when_switch_playlist条件不满足时会还原此列表上次关闭时的状态(从CRecentList加载) 308 // force为true时会强制播放参数list_item成员last_track指示曲目 309 bool SetList(ListItem list_item, bool play = false, bool force = false); 310 311 // 切换到指定路径的播放列表模式/通过“打开文件夹”来设置路径的处理 312 // (不进行“切换播放列表时继续播放”)(没能取得播放状态锁返回false) 313 bool OpenFolder(wstring path, bool contain_sub_folder = false, bool play = false); 314 315 // 切换到指定播放列表模式(没能取得播放状态锁返回false) 316 // force为true时忽略continue_when_switch_playlist设置播放track指定歌曲 317 bool SetPlaylist(const wstring& playlist_path, int track, int position, bool play = false, bool force = false); 318 // 打开一个播放列表文件(没能取得播放状态锁返回false) 319 // 支持所有支持的播放列表格式,不在默认播放列表目录则以.playlist格式复制到默认播放列表目录,会修改参数file_path为复制后的路径 320 bool OpenPlaylistFile(wstring& file_path); 321 322 // 向默认播放列表添加并打开多个文件,play用来设置是否立即播放(没能取得播放状态锁返回false) 323 // 由于cue解析问题,请在判断需要“添加歌曲”而不是“添加文件”时尽量使用CPlayer::OpenSongsInDefaultPlaylist代替此方法而不是使用path构建SongInfo 324 bool OpenFilesInDefaultPlaylist(const vector<wstring>& files, bool play = true); 325 // 向默认播放列表添加并打开多个歌曲,play用来设置是否立即播放(没能取得播放状态锁返回false) 326 bool OpenSongsInDefaultPlaylist(const vector<SongInfo>& songs, bool play = true); 327 // 打开多个歌曲并覆盖临时播放列表,play用来设置是否立即播放 328 bool OpenSongsInTempPlaylist(const vector<SongInfo>& songs, int play_index = 0, bool play = true); 329 // 切换到此歌曲音频文件目录的文件夹模式并播放此歌曲 330 bool OpenASongInFolderMode(const SongInfo& song, bool play = false); 331 332 // 向当前播放列表添加文件,仅在播放列表模式可用,返回成功添加的数量(拒绝重复曲目) 333 // 由于cue解析问题,请在判断需要“添加歌曲”而不是“添加文件”时尽量使用CPlayer::AddSongs代替此方法而不是使用path构建SongInfo 334 // files内含有cue原始文件时返回值可能不正确(处理在线程函数,无法及时得知是否添加) 335 int AddFilesToPlaylist(const vector<wstring>& files); 336 // 向当前播放列表添加歌曲,仅在播放列表模式可用,返回成功添加的数量(拒绝重复曲目) 337 int AddSongsToPlaylist(const vector<SongInfo>& songs); 338 339 // 重新载入播放列表(没能取得播放状态锁返回false) 340 bool ReloadPlaylist(MediaLibRefreshMode refresh_mode); 341 // 翻转是否包含子文件夹设置,如果当前为文件夹模式则直接重新加载播放列表(没能取得播放状态锁返回false) 342 bool SetContainSubFolder(); 343 344 // 移除当前播放列表(同时删除文件)/文件夹(从最近播放中移除)并切换到默认播放列表(没能取得播放状态锁返回false) 345 bool RemoveCurPlaylistOrFolder(); 346 347 #pragma endregion 列表初始化方法 348 349 //更改循环模式 350 void SetRepeatMode(); 351 //设置循环模式 352 void SetRepeatMode(RepeatMode repeat_mode); 353 RepeatMode GetRepeatMode() const; 354 void SpeedUp(); 355 void SlowDown(); 356 void SetOrignalSpeed(); 357 void SetSpeed(float speed); GetSpeed()358 float GetSpeed() const { return m_speed; } 359 void PitchUp(); 360 void PitchDown(); 361 void SetOrignalPitch(); 362 void SetPitch(int pitch); GetPitch()363 int GetPitch() const { return m_pitch; } 364 365 private: 366 // 获取CPlayer操作播放内核时产生的错误写入错误日志 367 bool GetPlayerCoreError(const wchar_t* function_name); 368 369 public: 370 //有错误时返回ture,否则返回false 371 bool IsError() const; 372 // IsError()为true时获取状态栏显示的错误字符串 373 std::wstring GetErrorInfo(); 374 375 // 通知主窗口当前播放歌曲改变需要更新显示(向主窗口发送消息)(原SetTitle) 376 void AfterSetTrack() const; 377 378 //保存配置到ini文件 379 void SaveConfig() const; 380 //从ini文件读取配置 381 void LoadConfig(); 382 383 //检索外部歌词文件(如果如果refresh为true,则不管媒体库里是否有当前歌曲的文件路径,都从文件重新检索歌词) 384 void SearchLyrics(bool refresh = false); 385 // 初始化歌词(同时读取内嵌歌词并判断是否使用) 386 void IniLyrics(); 387 // 为当前歌曲指定歌词文件并初始化 388 void IniLyrics(const wstring& lyric_path); 389 390 //用资源管理器打开当前路径并选中指定指定文件(当track小于0时选中当前正在播放的文件) 391 void ExplorePath(int track = -1) const; 392 //用资源管理器打开歌词文件所在的位置 393 void ExploreLyric() const; 394 395 //获取播放列表的引用 GetPlayList()396 vector<SongInfo>& GetPlayList() { return m_playlist; } 397 398 // 判断参数中的曲目是否存在于m_playlist,存在返回索引不存在返回-1(IsSameSong) 399 int IsSongInPlayList(const SongInfo& song); 400 // 判断参数中的曲目是否全部存在于m_playlist(IsSameSong) 401 bool IsSongsInPlayList(const vector<SongInfo>& songs_list); 402 //获取歌曲总数 403 int GetSongNum() const; 404 //获取当前播放曲目的目录 405 wstring GetCurrentDir() const; 406 //获取当前目录(文件夹模式下获取文件夹的目录,而不是正在播放曲目的目录) 407 wstring GetCurrentDir2() const; 408 // 获取正在播放文件的路径(涉及cue时不能用来判断是否为当前歌曲,判断请使用SongInfo,旧代码正在修改) 409 wstring GetCurrentFilePath() const; 410 //获取当前播放的曲目序号 GetIndex()411 int GetIndex() const { return m_index; } 412 //获取正在播放文件的显示名称 413 wstring GetDisplayName() const; GetVolume()414 int GetVolume() const { return m_volume; } 415 CImage& GetAlbumCover(); 416 CImage& GetAlbumCoverBlur(); 417 bool AlbumCoverExist(); GetAlbumCoverPath()418 wstring GetAlbumCoverPath() const { return m_album_cover_path; } 419 wstring GetAlbumCoverType() const; 420 //媒体库模式下获取当前播放媒体库项目的名称 GetMedialibItemName()421 wstring GetMedialibItemName() const { return m_media_lib_playlist_name; } 422 423 private: 424 // 下方播放列表移除歌曲方法中的共有部分 425 void AfterRemoveSong(bool is_current); 426 public: 427 //从播放列表中删除指定的项目 428 bool RemoveSong(int index, bool skip_locking = false); 429 //从播放列表中删除多个指定的项目 430 void RemoveSongs(vector<int> indexes, bool skip_locking = false); 431 //从播放列表中移除相同的曲目,返回已移除的曲目数量 432 int RemoveSameSongs(); 433 //从播放列表中移除无效的曲目,返回已移除的曲目数量 434 int RemoveInvalidSongs(); 435 //清空播放列表 436 void ClearPlaylist(); 437 438 //将指定范围内的项目上移 439 bool MoveUp(int first, int last); 440 //将指定范围内的项目下移 441 bool MoveDown(int first, int last); 442 //移动多个项目到dest的位置,返回移动后第1个项目的索引 443 int MoveItems(std::vector<int> indexes, int dest); 444 445 // 定位到指定位置,需要加播放状态锁 446 void SeekTo(int position); 447 // 定位到指定位置(范围0~1),需要加播放状态锁 448 void SeekTo(double position); 449 //static void SeekTo(HSTREAM hStream, int position); 450 451 //清除当前文件的歌词关联 452 void ClearLyric(); 453 454 //返回当前播放到的位置 GetCurrentPosition()455 int GetCurrentPosition() const { return m_current_position.toInt(); } 456 //返回正在播放文件的长度 GetSongLength()457 int GetSongLength() const { return m_song_length.toInt(); } 458 //返回当前播放时间的字符串形式 459 wstring GetTimeString() const; 460 //返回频谱分析每个柱形的高度数据 GetSpectralData()461 const float* GetSpectralData() const { return m_spectral_data; } 462 //返回频谱分析每个柱形顶端的高度数据 GetSpectralPeakData()463 const float* GetSpectralPeakData() const { return m_spectral_peak; } 464 //返回频谱分析的原始数据 GetFFTData()465 const float* GetFFTData() const { return m_fft; } 466 //获取播放状态的字符串 467 wstring GetPlayingState() const; 468 //获取正在播放状态(0:已停止,1:已暂停,2:正在播放) GetPlayingState2()469 int GetPlayingState2() const { return m_playing; } 470 // 获取当前SongInfo常引用,m_index无效时返回m_no_use 471 const SongInfo& GetCurrentSongInfo() const; 472 // 获取当前SongInfo引用(可修改),m_index无效时返回m_no_use 473 SongInfo& GetCurrentSongInfo2(); 474 //获取下一个要播放的曲目。如果返回的是空的SongInfo对象,则说明没有下一个曲目或下一个曲目不确定 475 SongInfo GetNextTrack() const; 476 //为当前歌曲设置“我喜欢”标记 477 void SetFavourite(int index, bool favourite); 478 void SetFavourite(bool favourite); 479 bool IsFavourite(int index); 480 bool IsFavourite(); 481 //判断当前专辑封面是否是内嵌图片 IsInnerCover()482 bool IsInnerCover() const { return m_inner_cover; } 483 //判断当前歌词是否是内嵌歌词 IsInnerLyric()484 bool IsInnerLyric() const { return m_inner_lyric; } 485 486 // 为当前歌曲增加累计已播放时间 487 void AddListenTime(int sec); 488 IsMidi()489 bool IsMidi() const { return m_pCore == nullptr ? false : m_pCore->IsMidi(); } GetMidiInfo()490 MidiInfo GetMidiInfo() const { return m_pCore == nullptr ? MidiInfo() : m_pCore->GetMidiInfo(); } GetMidiLyric()491 wstring GetMidiLyric() const { return m_pCore == nullptr ? wstring() : m_pCore->GetMidiInnerLyric(); } MidiNoLyric()492 bool MidiNoLyric() const { return m_pCore == nullptr ? true : m_pCore->MidiNoLyric(); } GetSoundFontName()493 wstring GetSoundFontName() const { return m_pCore == nullptr ? wstring() : m_pCore->GetSoundFontName(); } 494 495 int GetChannels(); 496 int GetFreq(); 497 unsigned int GetBassHandle() const; 498 499 //重新初始化BASS。当replay为true时,如果原来正在播放,则重新初始化后继续播放 500 void ReIniPlayerCore(bool replay = false); 501 502 //播放列表按照m_sort_mode排序(当is_init为false时,排序后重新查找正在播放的歌曲) 503 void SortPlaylist(bool is_init = false); 504 //获取专辑封面 505 void SearchAlbumCover(); 506 private: 507 //当无法播放时弹出提示信息 508 void ConnotPlayWarning() const; 509 //如果专辑封面过大,将其缩小后再加载 510 void AlbumCoverResize(); 511 //初始化随机播放列表 512 void InitShuffleList(int first_song = -1); 513 514 public: 515 //查找匹配的外部专辑封面,并加载专辑封面 516 void SearchOutAlbumCover(); 517 //专辑封面高斯模糊 518 void AlbumCoverGaussBlur(); GetCurrentFileType()519 wstring GetCurrentFileType() { return m_current_file_type; } 520 bool IsOsuFile() const; 521 bool IsPlaylistMode() const; //是否为播放列表模式 522 bool IsFolderMode() const; //是否为文件夹模式 523 bool IsMediaLibMode() const; //是否为媒体库模式 524 //当前播放列表模式为PM_MEDIA_LIB时,获取当前播放的媒体库项目类型 525 CMediaClassifier::ClassificationType GetMediaLibPlaylistType() const; 526 bool IsPlaylistEmpty() const; 527 528 // 重命名播放列表后使用此方法更新播放实例(不会重新载入播放列表) 529 void SetPlaylistPath(const wstring& playlist_path); 530 wstring GetPlaylistPath() const; GetPlayerCore()531 IPlayerCore* GetPlayerCore() { return m_pCore; } 532 bool IsMciCore() const; 533 bool IsBassCore() const; 534 bool IsFfmpegCore() const; IsFileOpened()535 bool IsFileOpened() const { return m_file_opend; } 536 //播放内核是否初始化完成 IsPlayerCoreInited()537 bool IsPlayerCoreInited() const { return m_player_core_inited; } IsContainSubFolder()538 bool IsContainSubFolder() const { return m_contain_sub_folder; } 539 540 541 MediaTransControls m_controls; 542 void UpdateLastFMCurrentTrack(const SongInfo& info); 543 544 // 更新SMTC封面,从路径为m_album_cover_path的图片文件 545 void MediaTransControlsLoadThumbnail(); 546 private: 547 void MediaTransControlsLoadThumbnailDefaultImage(); 548 549 public: 550 // 用于在执行某些操作时,播放器需要关闭当前播放的歌曲,操作完成后再次打开 551 // 当reopen为true时,在构造函数中关闭,析构时再次打开 552 // 使用后需要先检查IsLockSuccess,如果返回false(极小概率)那么此次操作应当放弃并让出主线程,在主线程等待会死锁 553 struct ReOpen 554 { 555 public: ReOpenReOpen556 ReOpen(bool reopen) 557 : m_reopen{ reopen } 558 { 559 if (m_reopen && !m_instance.m_loading && m_instance.GetPlayStatusMutex().try_lock_for(std::chrono::milliseconds(1000))) 560 { 561 lock_success = true; 562 current_position = m_instance.GetCurrentPosition(); 563 is_playing = m_instance.IsPlaying(); 564 current_song = m_instance.GetCurrentSongInfo(); 565 m_instance.MusicControl(Command::CLOSE); 566 } 567 } 568 ~ReOpenReOpen569 ~ReOpen() 570 { 571 if (lock_success) 572 { 573 m_instance.MusicControl(Command::OPEN); 574 m_instance.SeekTo(current_position); 575 if (is_playing) 576 m_instance.MusicControl(Command::PLAY); 577 m_instance.GetPlayStatusMutex().unlock(); 578 } 579 } 580 // 返回true说明此次取得锁成功或没有进行“reopen”操作,返回false时应放弃此次操作(主线程) IsLockSuccessReOpen581 bool IsLockSuccess() { return !m_reopen || lock_success; } 582 private: 583 int current_position{}; 584 SongInfo current_song; 585 bool is_playing{}; 586 bool m_reopen{}; 587 bool lock_success{}; 588 }; 589 }; 590