最近在做MPlayer的Windows版本,就播放器核心设计有了一些小小的探索,但是还是有不完善的地方,遂写下来已有的设计,理一下思路。为了保证未来的各种奇怪用例的可能性,本文将尽可能地考虑各种可能性并为之进行预防设计,所述的某些用例可能也不会作进一步的阐述和设计(因为它们都是未来的可能设计,现在并不想做)。
名词定义
- 音乐清单、歌单(MusicList):包含了一组歌曲的列表,仅作为歌曲来源,内部无重复,但有序
- 播放列表(PlaybackList):播放器当前可能会播放的所有歌曲的集合
- 播放模式(PlayMode):指示当前歌曲播放完毕后如何从播放列表中取下一首歌曲播放,此外还会决定如何生成播放列表
场景描述
用户在浏览各种音乐列表时,可能产生播放音乐的行为。这些音乐列表可能来自曲库、用户创建的歌单、历史播放、专辑歌曲、歌手歌曲等各种场景。不同场景呈现出的一组音乐统称为一个音乐清单或歌单(MusicList)。在这些清单中,要求音乐不能重复且必须保持一定的顺序。
不能重复的含义是指,每首音乐的ID不一致,即使它们播放出来是一样的效果,但是由于用户的要求,它们会被当做不同的音乐并被分配不同的ID,要让用户如何区别这些音乐则需要标记、别名等其它形式配合,不在本文内容中。
当用户要求播放音乐时,通行的做法是,假定用户默认的要求是,播放指定的某歌曲清单中的某一音乐,当该音乐播放完毕后,根据播放模式继续播放该清单中的其它音乐。
当音乐正在播放中时,用户需要能够看到当前播放的清单。由于用户可能会想要在播放当前清单的途中添加其它清单中的一些音乐到当前播放的清单中,以希冀未来在播放当前清单的同时也能播放一些其它的音乐,所以用户看到的当前播放的清单实际上不能等同于播放清单,因此将其称之为播放列表(PlaybackList)。
在播放列表中,用户可以自由调整播放顺序,增删其中的音乐,甚至将某一首音乐重复多次。进一步的,如果用户采用的播放模式为随机播放模式,则会将当前播放列表打乱,而用户可能想要查看原始的播放列表,并希望能够在原始顺序和实际播放顺序之间进行切换。
特别的,在打乱播放列表时,可选是否需要剔除重复歌曲。
设计实现
播放核心分为播放器以及播放列表两个部分,前者负责当前音乐播放及控制功能,后者负责歌曲提供功能。播放器持有播放列表的引用并提供一些便利代理属性如当前播放音乐、播放模式。
播放列表维护一个实际播放列表、当前播放歌曲下标以及当前播放歌曲。注意,当前播放歌曲改变不一定意味着当前播放歌曲下标改变,例如一曲播放完毕后从播放列表中移除,继续播放下一首歌曲;当前歌曲下标改变不一定意味着当前歌曲改变,例如,用户拖拽当前播放歌曲到播放列表的最前方。提供增加、删除、移动、插入现有歌曲功能。提供下一曲、上一曲功能。提供播放模式查改及播放模式轮换功能。
播放列表同时维护一个原始播放列表,这在非随机模式下与当前播放列表一致。当播放模式从非随机模式转换到随机模式时,先将当前未被打乱的列表复制一份为原始播放列表,当前列表应用随机算法进行打乱。当用户要求查看原始列表时,UI层可以查询原始播放列表并执行相应的展示。当播放模式从随机模式转换到非随机模式时,将播放列表转换为原始播放列表,并更新下标。当前播放歌曲下标始终指向播放列表中当前播放歌曲的下标,而不与原始播放列表有任何关系,UI层在切换展示原始列表时需要自行处理下标。
在随机模式下对播放列表进行删除操作时,同步对原始播放列表进行操作。此处有两种策略:1、当从播放列表中移除一首歌曲的最后一份实例时,删除原始播放列表中的所有该歌曲,除此之外不对原始播放列表进行改动;2、当从播放列表中移除一首歌曲时,寻找并删除原始播放列表中该歌曲第一次出现的实例,若考虑不同来源的同一首歌曲(ID相同)也是不一样的,则需要做进一步的处理。
在随机模式下对播放列表进行增加或插入操作时,同步对原始播放列表进行操作。同样两种不同的策略,增加或插入歌曲时,若原始播放列表中已存在,则再次增加到末尾,或者不再增加,若原始列表中不存在则正常增加到末尾。
播放列表必须提供相应的集合修改事件暴露给外界,以允许UI层能够响应实际播放列表的改变。UI层对播放列表的操作全部要通过调用播放列表的各种操作接口来进行,即单向数据流动(由播放列表→UI层)。