1 #pragma once
2
3 #include <vector>
4 #include <set>
5 #include <deque>
6 #include <cstdlib>
7 #include <ctime>
8 #include <cassert>
9 #include <algorithm>
10 //#include <iostream>
11
12 #define MOUS_HAS(container, var) \
13 (container.find(var) != container.end())
14
15 #define MOUS_FIND(container, var) \
16 std::find(container.begin(), container.end(), var)
17
18 #define MOUS_CONTAINS(container, var) \
19 (std::find(container.begin(), container.end(), var) != container.end())
20
21 namespace mous {
22
23 enum class PlaylistMode : uint8_t
24 {
25 Normal,
26 Repeat,
27 Shuffle,
28 ShuffleRepeat,
29 RepeatOne,
30 Top,
31 };
32
ToString(PlaylistMode mode)33 inline static std::string ToString(PlaylistMode mode)
34 {
35 switch (mode) {
36 case PlaylistMode::Normal:
37 return "Normal";
38
39 case PlaylistMode::Repeat:
40 return "Repeat";
41
42 case PlaylistMode::Shuffle:
43 return "Shuffle";
44
45 case PlaylistMode::ShuffleRepeat:
46 return "Shuffle Repeat";
47
48 case PlaylistMode::RepeatOne:
49 return "Repeat One";
50
51 default:
52 return "";
53 }
54 }
55
56 template <typename item_t>
57 class PlaylistSerializer;
58
59 /*
60 * NOTE: Playlist<int> won't be compiled!
61 */
62 template <typename item_t>
63 class Playlist
64 {
65 friend class PlaylistSerializer<item_t>;
66
67 public:
68 Playlist() = default;
69 ~Playlist() = default;
70
SetMode(PlaylistMode mode)71 void SetMode(PlaylistMode mode)
72 {
73 if (m_SeqIndex >= 0 && (size_t)m_SeqIndex < m_ItemQueue.size()) {
74 std::set<PlaylistMode> normalSet { PlaylistMode::Normal, PlaylistMode::Repeat, PlaylistMode::RepeatOne };
75 std::set<PlaylistMode> shuffleSet { PlaylistMode::Shuffle, PlaylistMode::ShuffleRepeat };
76
77 // normal <=> shuffle
78 if (MOUS_HAS(normalSet, m_Mode) && MOUS_HAS(shuffleSet, mode)) {
79 m_SeqIndex = MOUS_FIND(m_SeqShuffleQueue, m_SeqIndex) - m_SeqShuffleQueue.begin();
80 } else if (MOUS_HAS(shuffleSet, m_Mode) && MOUS_HAS(normalSet, mode)) {
81 m_SeqIndex = m_SeqShuffleQueue[m_SeqIndex];
82 }
83 }
84
85 m_Mode = mode;
86 }
87
Mode()88 PlaylistMode Mode() const
89 {
90 return m_Mode;
91 }
92
93 /* check whether there is a next item according to current play mode
94 *
95 * offset:
96 * < 0 previous
97 * = 0 current
98 * > 0 next
99 *
100 * NOTE: this method should be called before NextItem()
101 *
102 */
HasNext(int offset)103 bool HasNext(int offset) const
104 {
105 return (OffsetToIndex(offset) >= 0);
106 }
107
NextItem(int offset,bool moveTo)108 const item_t& NextItem(int offset, bool moveTo) const
109 {
110 return (const_cast<Playlist*>(this))->NextItem(offset, moveTo);
111 }
112
NextItem(int offset,bool moveTo)113 item_t& NextItem(int offset, bool moveTo)
114 {
115 int index = OffsetToIndex(offset);
116 assert(index >= 0 && (size_t)index < m_ItemQueue.size());
117
118 if (moveTo)
119 m_SeqIndex = index;
120
121 switch (m_Mode) {
122 case PlaylistMode::Shuffle:
123 case PlaylistMode::ShuffleRepeat:
124 index = m_SeqShuffleQueue[index];
125 break;
126
127 default:
128 break;
129 }
130
131 return m_ItemQueue[index];
132 }
133
134 /* try to set index */
JumpTo(int index)135 bool JumpTo(int index)
136 {
137 if (index >= 0 && (size_t)index < m_ItemQueue.size()) {
138 switch (m_Mode) {
139 case PlaylistMode::Normal:
140 case PlaylistMode::Repeat:
141 case PlaylistMode::RepeatOne:
142 m_SeqIndex = index;
143 break;
144
145 case PlaylistMode::Shuffle:
146 case PlaylistMode::ShuffleRepeat:
147 m_SeqIndex = MOUS_FIND(m_SeqShuffleQueue, index) - m_SeqShuffleQueue.begin();
148 break;
149
150 default:
151 break;
152 }
153 return true;
154 } else {
155 return false;
156 }
157 }
158
159 /* current index */
CurrentIndex()160 int CurrentIndex() const
161 {
162 if (m_SeqIndex < 0)
163 return -1;
164
165 switch (m_Mode) {
166 case PlaylistMode::Normal:
167 case PlaylistMode::Repeat:
168 case PlaylistMode::RepeatOne:
169 return m_SeqIndex;
170
171 case PlaylistMode::Shuffle:
172 case PlaylistMode::ShuffleRepeat:
173 return m_SeqShuffleQueue[m_SeqIndex];
174
175 default:
176 return -1;
177 }
178 }
179
180 template<class Array>
Assign(const Array & items)181 void Assign(const Array& items)
182 {
183 m_ItemQueue.assign(items.begin(), items.end());
184 AdjustSeqPosition();
185 AdjustShuffleRange(true);
186 }
187
Move(int oldPos,int newPos)188 void Move(int oldPos, int newPos)
189 {
190 Move(std::vector<int>(1, oldPos), newPos);
191 }
192
193 template<class Array>
Move(Array oldPos,int newPos)194 void Move(Array oldPos, int newPos)
195 {
196 if (m_ItemQueue.size() < 2)
197 return;
198
199 std::sort(oldPos.begin(), oldPos.end());
200
201 int realNewPos = newPos;
202 for (size_t i = 0; i < oldPos.size(); ++i) {
203 if (oldPos[i] < newPos)
204 --realNewPos;
205 }
206
207 // backup & remove & insert
208 std::deque<item_t> tmpList(oldPos.size());
209 for (size_t i = 0; i < oldPos.size(); ++i) {
210 tmpList[i] = std::move(m_ItemQueue[oldPos[i]]);
211 }
212 Remove(oldPos);
213 Insert(realNewPos, tmpList);
214
215 // calc new seq index
216 int seqIndex = (m_Mode == PlaylistMode::Shuffle
217 || m_Mode == PlaylistMode::ShuffleRepeat) ?
218 m_SeqShuffleQueue[m_SeqIndex] : m_SeqIndex;
219
220 const auto iter = std::find(oldPos.begin(), oldPos.end(), seqIndex);
221 if (iter != oldPos.end()) {
222 seqIndex = realNewPos + (iter - oldPos.begin());
223 } else {
224 int tmp = seqIndex;
225 for (size_t i = 0; i < oldPos.size(); ++i) {
226 if (oldPos[i] < seqIndex)
227 --tmp;
228 else
229 break;
230 }
231 if (realNewPos <= tmp)
232 tmp += oldPos.size();
233 seqIndex = tmp;
234 }
235
236 assert(seqIndex >= 0 && (size_t)seqIndex < m_ItemQueue.size());
237
238 m_SeqIndex = (m_Mode == PlaylistMode::Shuffle
239 || m_Mode == PlaylistMode::ShuffleRepeat) ?
240 MOUS_FIND(m_SeqShuffleQueue, seqIndex) - m_SeqShuffleQueue.begin() : seqIndex;
241 }
242
Insert(int index,const item_t & item)243 void Insert(int index, const item_t& item)
244 {
245 m_ItemQueue.insert(m_ItemQueue.begin()+index, item);
246 AdjustSeqPosition();
247 AdjustShuffleRange();
248 }
249
Insert(int index,item_t && item)250 void Insert(int index, item_t&& item)
251 {
252 m_ItemQueue.insert(m_ItemQueue.begin()+index, std::move(item));
253 AdjustSeqPosition();
254 AdjustShuffleRange();
255 }
256
257 template<class Array>
Insert(int index,const Array & array)258 void Insert(int index, const Array& array)
259 {
260 m_ItemQueue.insert(m_ItemQueue.begin()+index, array.begin(), array.end());
261 AdjustSeqPosition();
262 AdjustShuffleRange();
263 }
264
Append(const item_t & item)265 void Append(const item_t& item)
266 {
267 m_ItemQueue.push_back(item);
268 AdjustSeqPosition();
269 AdjustShuffleRange();
270 }
271
Append(item_t && item)272 void Append(item_t&& item)
273 {
274 m_ItemQueue.push_back(std::move(item));
275 AdjustSeqPosition();
276 AdjustShuffleRange();
277 }
278
279 template<class Array>
Append(const Array & array)280 void Append(const Array& array)
281 {
282 m_ItemQueue.insert(m_ItemQueue.end(), array.begin(), array.end());
283 AdjustSeqPosition();
284 AdjustShuffleRange();
285 }
286
Remove(const item_t & item)287 bool Remove(const item_t& item)
288 {
289 for (size_t i = 0; i < m_ItemQueue.size(); ++i) {
290 if (item == m_ItemQueue[i]) {
291 m_ItemQueue.erase(m_ItemQueue.begin() + i);
292 AdjustSeqPosition();
293 AdjustShuffleRange();
294 return true;
295 }
296 }
297 return false;
298 }
299
Remove(int index)300 void Remove(int index)
301 {
302 m_ItemQueue.erase(m_ItemQueue.begin() + index);
303 AdjustSeqPosition();
304 AdjustShuffleRange();
305 }
306
307 template<class Array>
Remove(const Array & array)308 void Remove(const Array& array)
309 {
310 for (int i = array.size()-1; i >= 0; --i) {
311 m_ItemQueue.erase(m_ItemQueue.begin() + array[i]);
312 }
313 AdjustSeqPosition();
314 AdjustShuffleRange();
315 }
316
Clear()317 void Clear()
318 {
319 m_ItemQueue.clear();
320 m_SeqShuffleQueue.clear();
321 AdjustSeqPosition();
322 }
323
At(int index)324 item_t& At(int index)
325 {
326 return m_ItemQueue.at(index);
327 }
328
At(int index)329 const item_t& At(int index) const
330 {
331 return m_ItemQueue.at(index);
332 }
333
334 item_t& operator[](int index)
335 {
336 return m_ItemQueue[index];
337 }
338
339 const item_t& operator[](int index) const
340 {
341 return m_ItemQueue[index];
342 }
343
Items()344 const std::deque<item_t>& Items() const
345 {
346 return m_ItemQueue;
347 }
348
Items()349 std::deque<item_t>& Items()
350 {
351 return m_ItemQueue;
352 }
353
Count()354 int Count() const
355 {
356 int size = m_ItemQueue.size();
357 return size;
358 }
359
Empty()360 bool Empty() const
361 {
362 bool empty = m_ItemQueue.empty();
363 return empty;
364 }
365
Reverse()366 void Reverse()
367 {
368 reverse(m_ItemQueue.begin(), m_ItemQueue.end());
369 }
370
371 private:
OffsetToIndex(int offset)372 int OffsetToIndex(int offset) const
373 {
374 if (m_ItemQueue.empty())
375 return -1;
376
377 int idx = -1;
378 switch (m_Mode) {
379 case PlaylistMode::Normal:
380 case PlaylistMode::Shuffle:
381 idx = m_SeqIndex + offset;
382 break;
383
384 case PlaylistMode::Repeat:
385 case PlaylistMode::ShuffleRepeat:
386 idx = (m_SeqIndex + offset) % m_ItemQueue.size();
387 break;
388
389 case PlaylistMode::RepeatOne:
390 idx = m_SeqIndex;
391 break;
392
393 default:
394 break;
395 }
396
397 return (idx >= 0 && (size_t)idx < m_ItemQueue.size()) ? idx : -1;
398 }
399
AdjustSeqPosition()400 void AdjustSeqPosition()
401 {
402 if (m_ItemQueue.empty()) {
403 m_SeqIndex = -1;
404 };
405 }
406
407 void AdjustShuffleRange(bool reGenerate = false)
408 {
409 if (m_ItemQueue.empty())
410 return;
411
412 if (reGenerate)
413 m_SeqShuffleQueue.clear();
414
415 int need = m_ItemQueue.size() - m_SeqShuffleQueue.size();
416 if (need > 0) {
417 srandom(time(nullptr));
418 for (int i = 0; i < need; ++i) {
419 int inspos = random() % (m_SeqShuffleQueue.size()+1);
420 m_SeqShuffleQueue.insert(m_SeqShuffleQueue.begin()+inspos, m_SeqShuffleQueue.size());
421 }
422 } else if (need < 0){
423 int top = m_ItemQueue.size();
424 for (int i = m_SeqShuffleQueue.size()-1; i >= 0; --i) {
425 if (m_SeqShuffleQueue[i] >= top)
426 m_SeqShuffleQueue.erase(m_SeqShuffleQueue.begin() + i);
427 }
428 }
429
430 assert(m_ItemQueue.size() == m_SeqShuffleQueue.size());
431
432 // debug
433 /*
434 using namespace std;
435 cout << "shuffle:";
436 for (size_t i = 0; i < m_SeqShuffleQueue.size(); ++i) {
437 cout << m_SeqShuffleQueue[i] << ", ";
438 }
439 cout << endl;
440 */
441 }
442
443 private:
444 PlaylistMode m_Mode = PlaylistMode::Normal;
445
446 std::deque<item_t> m_ItemQueue;
447
448 int m_SeqIndex = -1;
449 std::deque<int> m_SeqShuffleQueue;
450
451 };
452
453 }
454
455 #undef MOUS_HAS
456 #undef MOUS_FIND
457 #undef MOUS_CONTAINS
458