xref: /MusicPlayer2/MusicPlayer2/Player.h (revision e84504647172202b2dfd01b2498fed6db80a3db0)
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