MusicPlayer2 icon indicating copy to clipboard operation
MusicPlayer2 copied to clipboard

添加“下一首播放”功能

Open VictoriousRaptor opened this issue 2 years ago • 37 comments

参考#411 #280 #367 #145等issue的意见添加了“下一首播放”功能。该功能在顺序播放、乱序播放、随机播放、列表循环、单曲循环模式下有效。以及修改了#280中提到的,随机播放会随机到同一首歌的问题。

VictoriousRaptor avatar Aug 16 '22 19:08 VictoriousRaptor

以及修改了#280中提到的,随机播放会随机到同一首歌的问题。

随机播放会随机到同一首歌是正常的,不用修改,如果不想播放到同一首歌曲,可以使用无序播放模式。

zhongyang219 avatar Aug 17 '22 03:08 zhongyang219

我个人认为随机播放没有必要避免连续播放同一首歌 这样会失去其随机性,对于不想连续播放同一首歌的人应当使用无序播放 如果随机播放不随机那么和无序播放就没有任何区别了

下一首播放应该是可以设置多首的,在切换播放列表时复位(void CPlayer::IniPlaylistComplate()后面几行) 使用vector并以size等于0判断是否为原来播放模式 我原来的想法是在CPlayer::PlayTrack中的switch语句前塞一段处理下一曲播放,并跳过switch语句 我当时没有考虑同时维护无序列表的问题,按照无序播放定义这里应当处理(只是会大幅提升复杂度)

手动播放时清空下一首播放, 我没有想好的是下一曲播放的vector是先进先出还是后进先出,是队列还是栈 以及要不要去重(是否允许重复添加同一首歌)

lrisora avatar Aug 17 '22 03:08 lrisora

随机播放应不应该允许随机到同一首这个可以有不同的理解,确实如果不允许就和无序播放没太大区别了。“下一首播放可以设置多首”这个我不明白是什么意思?是允许多选吗?

lrisora @.***> 于 2022年8月17日周三 11:44写道:

我个人认为随机播放没有必要避免连续播放同一首歌 这样会失去其随机性,对于不想连续播放同一首歌的人应当使用无序播放 如果随机播放不随机那么和无序播放就没有任何区别了

下一首播放应该是可以设置多首的,在切换播放列表时复位(void CPlayer::IniPlaylistComplate()后面几行) 使用vector并以size等于0判断是否为原来播放模式 我原来的想法是在CPlayer::PlayTrack中的switch语句前塞一段处理下一曲播放,并跳过switch语句 我当时没有考虑同时维护无序列表的问题,按照无序播放定义这里应当处理(只是会大幅提升复杂度)

手动播放时清空下一首播放, 我没有想好的是下一曲播放的vector是先进先出还是后进先出,是队列还是栈 以及要不要去重(是否允许重复添加同一首歌)

— Reply to this email directly, view it on GitHub https://github.com/zhongyang219/MusicPlayer2/pull/412#issuecomment-1217425591, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACOUUSMJZBQYCOPFP3Y44I3VZRNYVANCNFSM56XAXUEA . You are receiving this because you authored the thread.Message ID: @.***>

VictoriousRaptor avatar Aug 17 '22 05:08 VictoriousRaptor

随机播放应不应该允许随机到同一首这个可以有不同的理解,确实如果不允许就和无序播放没太大区别了。“下一首播放可以设置多首”这个我不明白是什么意思?是允许多选吗?

感觉可以学一下PowerAMP的逻辑,它的下一首播放是一个列表,点下一首播放会往那个列表的最后加入。

lifegpc avatar Aug 17 '22 05:08 lifegpc

仔细看了一下要改的东西,比我之前想象的要多。是不是对于顺序和列表循环也应该使用一个列表记录播放过的歌曲?毕竟用户选择了“下一首”之后,下一首的“上一首”就不一定是m_index-1了。 观察了一下网易云UWP版,它应该是这么做的。

VictoriousRaptor avatar Aug 18 '22 07:08 VictoriousRaptor

网易云UWP实际播放的不是歌单,而是右下角的播放列表 这个播放列表全局单例,可以办到诸如“设定另一个歌单中的一首歌下一曲播放”之类的操作 平时播放歌单时程序实际的操作是“将歌单前999首加入播放列表并播放播放列表” 网易云的循环模式实现及下一曲播放都直接依赖这个播放列表结构(而不是歌单)

MusicPlayer2使用的是另一套结构,播放的就是看到的播放列表 循环模式的设计实现也不同,没必要实现和网易云同样的逻辑

我原来的构想是设置一次下一曲播放就push_back一首, 在CPlayer::PlayTrack的switch前判断有下一曲播放就拿出一曲播放并跳过switch(单曲播放时不进行) 不过没打算维护无序播放列表(这个有点复杂但是是可以实现的) 也就是设置下一曲播放就会忽略除单曲播放外的循环模式进行指定下一曲 按照这个设计添加下一曲播放最大的坑是播放列表重新排序后存储的index就会失效 (可以通过存储SongInfo或key(在另一个pr)解决)

lrisora avatar Aug 18 '22 09:08 lrisora

这个我比较喜欢PowerAMP的处理方式。 PowerAMP 是直接存一个单独的列表,专门就是存放下一首播放的列表。 如果列表放完了,下次加的时候清零再push_back,它存的应该是位置,所以换播放列表是不会影响下一首播放的。

lifegpc avatar Aug 18 '22 09:08 lifegpc

这个我比较喜欢PowerAMP的处理方式。 PowerAMP 是直接存一个单独的列表,专门就是存放下一首播放的列表。 如果列表放完了,下次加的时候清零再push_back,它存的应该是位置,所以换播放列表是不会影响下一首播放的。

以目前的设计,MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲的 我没有用过,不过按照这个描述这样做类似网易云音乐UWP的实现 我认为这样做最大的问题是当前播放列表无法确定(或者说是动态生成的),这样的结构无法实现随机播放,只能是无序播放 现在这样的基于明确的播放列表的实现也很好用 只要加一个vector垫在CPlayer::PlayTrack前来实现下一曲播放就好

lrisora avatar Aug 18 '22 09:08 lrisora

这个我比较喜欢PowerAMP的处理方式。 PowerAMP 是直接存一个单独的列表,专门就是存放下一首播放的列表。 如果列表放完了,下次加的时候清零再push_back,它存的应该是位置,所以换播放列表是不会影响下一首播放的。

以目前的设计,MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲的 我没有用过,不过按照这个描述这样做类似网易云音乐UWP的实现 我认为这样做最大的问题是当前播放列表无法确定(或者说是动态生成的),这样的结构无法实现随机播放,只能是无序播放 现在这样的基于明确的播放列表的实现也很好用 只要加一个vector垫在CPlayer::PlayTrack前来实现下一曲播放就好

我的意思就很简单,保存一个单独的vector和vector的播放进度,然后播放下一首的时候检查这个vector的大小和播放进度,小就放vector里的歌,反之就之前的行为。这样不会对现有的播放列表造成影响。

lifegpc avatar Aug 18 '22 10:08 lifegpc

下一首播放这个vector里的歌就定死顺序播放就好了

lifegpc avatar Aug 18 '22 10:08 lifegpc

我没有理解,是如何做到跨播放列表维持下一曲播放的 MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲这一点是难以更改的,比换媒体库的键要难得多 我设想的存储下一曲播放的vector会在设置下一曲播放时size+1 播放下一曲时size-1直到size==0(即播放会消耗其中存储),等于size等于0后恢复正常循环模式

lrisora avatar Aug 18 '22 10:08 lrisora

我没有理解,是如何做到跨播放列表维持下一曲播放的 MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲这一点是难以更改的,比换媒体库的键要难得多 我设想的存储下一曲播放的vector会在设置下一曲播放时size+1 播放下一曲时size-1直到size==0(即播放会消耗其中存储),等于size等于0后恢复正常循环模式

那不妨换个想法,存到同一个vector,然后设置个变量来区分当前列表和下一首播放列表(

lifegpc avatar Aug 18 '22 10:08 lifegpc

随机播放的话,只要确保落在当前列表的范围内就可以了

lifegpc avatar Aug 18 '22 10:08 lifegpc

感觉没必要跨播放列表维持下一曲播放的

VictoriousRaptor avatar Aug 18 '22 10:08 VictoriousRaptor

感觉没必要跨播放列表维持下一曲播放的

有必要的,比如说从媒体库加下一曲播放(

lifegpc avatar Aug 18 '22 10:08 lifegpc

我没有理解,是如何做到跨播放列表维持下一曲播放的 MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲这一点是难以更改的,比换媒体库的键要难得多 我设想的存储下一曲播放的vector会在设置下一曲播放时size+1 播放下一曲时size-1直到size==0(即播放会消耗其中存储),等于size等于0后恢复正常循环模式

那不妨换个想法,存到同一个vector,然后设置个变量来区分当前列表和下一首播放列表(

这种级别的播放列表对用户不可见我觉得是不可接受的,网易云的那个虽然烂不过至少如实告诉用户就添加了999首 这样做把背后的逻辑隐藏起来对用户来说实在难以理解, 现在MusicPlayer2的播放列表用网易云举例就是兼具“歌单”和“播放列表”两个功能 这个功能的添加用下一曲列表不合适,其实就是字面意义的“播放列表”, 就像是把现在的播放列表改为歌单,之后又加了一个“真播放列表”

lrisora avatar Aug 18 '22 10:08 lrisora

这个我比较喜欢PowerAMP的处理方式。 PowerAMP 是直接存一个单独的列表,专门就是存放下一首播放的列表。 如果列表放完了,下次加的时候清零再push_back,它存的应该是位置,所以换播放列表是不会影响下一首播放的。

以目前的设计,MusicPlayer2是没办法播放CPlayer::m_playlist之外的歌曲的 我没有用过,不过按照这个描述这样做类似网易云音乐UWP的实现 我认为这样做最大的问题是当前播放列表无法确定(或者说是动态生成的),这样的结构无法实现随机播放,只能是无序播放 现在这样的基于明确的播放列表的实现也很好用 只要加一个vector垫在CPlayer::PlayTrack前来实现下一曲播放就好

我的意思就很简单,保存一个单独的vector和vector的播放进度,然后播放下一首的时候检查这个vector的大小和播放进度,小就放vector里的歌,反之就之前的行为。这样不会对现有的播放列表造成影响。

要不就这种 就代码可能得大改(

lifegpc avatar Aug 18 '22 11:08 lifegpc

暂时先用我那个下一曲播放方案吧

这个加“真·播放列表”的方案落实的话MusicPlayer2可以升大版本号了 这里会涉及的代码从初版就没变过,许多功能都在其基础上实现 UI也需要不小的改变

lrisora avatar Aug 18 '22 11:08 lrisora

~~能用std::queue吗,要不然用vector的话,插入在头部的开销大,在尾部的话违反直觉~~之前没看到已经用过,没事了

VictoriousRaptor avatar Aug 18 '22 11:08 VictoriousRaptor

我的想法是能不能之后只存储key,数据全部存数据库里(考虑引入sqlite3

lifegpc avatar Aug 18 '22 11:08 lifegpc

当前歌曲的信息单独存一份,当前播放列表只存储key,同时还能节省内存占用。 就是搜索感觉麻烦点(需要去调用数据库)

lifegpc avatar Aug 18 '22 12:08 lifegpc

我的想法是能不能之后只存储key,数据全部存数据库里(考虑引入sqlite3

当前歌曲的信息单独存一份,当前播放列表只存储key,这个以后有打算,不过可能只存时长信息用于维持内核播放就足够 重新初始化播放列表时m_playlist[m_index]会失效,目前是用停止播放的方法度过这段时间 切换播放列表保持播放/播放列表添加歌曲时 不重新打开歌曲都需要这个 ~key也不算小吧~ 媒体库内容的更改不会自动触发各种窗口的更新,还是复制SongInfo安全, 用媒体库独一份的SongInfo可以预见会发生各种不同步的bug

lrisora avatar Aug 18 '22 12:08 lrisora

有个问题我不太清楚,想讨论一下: 随机播放模式下,如果用户指定了下一首播放,在播放下下一首时点击“上一首”,需要回到用户指定的那一首吗?如果是那就不能简单地 “在CPlayer::PlayTrack的switch前判断有下一曲播放就拿出一曲播放并跳过switch(单曲播放时不进行)”了

VictoriousRaptor avatar Aug 18 '22 12:08 VictoriousRaptor

我的想法是能不能之后只存储key,数据全部存数据库里(考虑引入sqlite3

当前歌曲的信息单独存一份,当前播放列表只存储key,这个以后有打算,不过可能只存时长信息用于维持内核播放就足够 重新初始化播放列表时m_playlist[m_index]会失效,目前是用停止播放的方法度过这段时间 切换播放列表保持播放/播放列表添加歌曲时 不重新打开歌曲都需要这个 ~key也不算小吧~ 媒体库内容的更改不会自动触发各种窗口的更新,还是复制SongInfo安全, 用媒体库独一份的SongInfo可以预见会发生各种不同步的bug

key直接使用一个数字id即可,具体的路径全部可以从sqlite数据库查询

lifegpc avatar Aug 18 '22 12:08 lifegpc

同步问题在C++是挺头疼的,sqlite是提供了数据库级别的同步,也许可以在修改元数据后发一个事件,然后通知其他窗口,然后再从数据库取一份就好了,最主要的还是现在每秒都会更改一下SongInfo来实现统计信息,这里肯定是要复制一份的,不然更新数据库太频繁了 ~(Rust倒是无脑Mutex/RwLock 叠Arc就好了,还肯定不会死锁)~

lifegpc avatar Aug 18 '22 12:08 lifegpc

有个问题我不太清楚,想讨论一下: 随机播放模式下,如果用户指定了下一首播放,在播放下下一首时点击“上一首”,需要回到用户指定的那一首吗?如果是那就不能简单地 “在CPlayer::PlayTrack的switch前判断有下一曲播放就拿出一曲播放并跳过switch(单曲播放时不进行)”了

我觉得保留当前的逻辑好了,就是播放之前播放的歌曲。

lifegpc avatar Aug 18 '22 12:08 lifegpc

主要做了以下改动:

  1. 添加了一个dequem_next_tracks,用于存储下n首播放的歌曲
  2. 添加了下一首播放相关的函数
  3. 添加了“下一首播放”菜单选项,仅在主界面播放列表有效
  4. 开启多核编译选项

已知问题: ~~1. 未选中歌曲时,菜单选项没有变灰(找不到在哪里改)~~ 2. 不能从媒体库中当前播放列表选择下一首播放 ~~3. 播放列表重新排序后存储的index就会失效?(讨论中大佬提出的)~~ ~~4. 播放列表变动时的处理(查找了m_random_list.clear()出现的位置,发现在相似情况下有很多处,建议新建一个函数统合处理这类需要考虑播放列表变动的变量)~~

VictoriousRaptor avatar Aug 18 '22 13:08 VictoriousRaptor

在MusicPlayerDlg.cpp的1408行 pMenu->EnableMenuItem(ID_PLAY_AS_NEXT, MF_BYCOMMAND | (selete_valid ? MF_ENABLED : MF_GRAYED));

重新排序也是播放列表变动,所以在那些地方有m_random_list.clear(),因为排序后m_random_list也失效了 同理m_next_tracks也需要清空 这部分计划以后换成存储键就不会因为排序而失效了 手动播放时复位, 仅在下一曲时从m_next_tracks中提取, 按照随机播放的设计,随机播放存储的是字面意义的播放历史

bool CPlayer::PlayTrack(int song_track, bool auto_next)
{
    if (song_track != NEXT && song_track != PREVIOUS)
        m_next_tracks.clear();
    if (song_track == NEXT && !m_next_tracks.empty()) {
        song_track = m_next_tracks.front();
        m_next_tracks.pop_front();
        if (m_repeat_mode == RM_PLAY_RANDOM)
            m_random_list.push_back(song_track);
    }
//......
 }

这部分代码我读的不多,可能有问题,仅参考

lrisora avatar Aug 18 '22 14:08 lrisora

已知问题: ~1. 未选中歌曲时,菜单选项没有变灰(找不到在哪里改)~ 2. 不能从媒体库中当前播放列表选择下一首播放 ~3. 播放列表重新排序后存储的index就会失效?(讨论中大佬提出的)~ ~4. 播放列表变动时的处理(查找了m_random_list.clear()出现的位置,发现在相似情况下有很多处,建议新建一个函数统合处理这类需要考虑播放列表变动的变量)~

问题基本都改了,随机播放下点击上一首确实应该回到实际上播放的上一首,那么对于其他模式呢?

VictoriousRaptor avatar Aug 18 '22 17:08 VictoriousRaptor

这里的循环模式除新增的单曲播放外是在 #89 改的, 最后定下来的是 无序播放上一曲=无序列表的上一曲 无序播放下一曲=无序列表的下一曲 无序播放完成一遍重置,手动播放重置,播放列表变动重置 随机播放上一曲=如果随机历史不为空则取出一个播放否则停止 随机播放下一曲=随机选取 随机历史仅存储随机播放的历史并去重 ~虽然我觉得存储所有模式的历史也问题不大~ 随机历史存储手动播放 随机历史播放列表变动重置

其他模式直接就是字面意义,简单易懂

lrisora avatar Aug 18 '22 18:08 lrisora