summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2022-12-03 06:21:52 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2022-12-03 06:28:48 -0500
commit03c225fe54a2ae4699f92473ea21da11ba043148 (patch)
treeef9513da39eb6a75905516d6633223be5116cbb0
parent90dc64da322f428b058740a9e1847cf469a3bdea (diff)
downloadrockbox-03c225fe54a2ae4699f92473ea21da11ba043148.tar.gz
rockbox-03c225fe54a2ae4699f92473ea21da11ba043148.zip
playlist.c clean-up and organize
No functional changes Change-Id: I5c7a4a63c54bc867b7d6c2ca6cbc8ef135e01a90
-rw-r--r--apps/playlist.c3753
-rw-r--r--apps/playlist.h37
2 files changed, 1881 insertions, 1909 deletions
diff --git a/apps/playlist.c b/apps/playlist.c
index d71257a515..23195b4417 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -29,7 +29,7 @@
29 directory, there will be no playlist file. 29 directory, there will be no playlist file.
30 2. Control file : This file is automatically created when a playlist is 30 2. Control file : This file is automatically created when a playlist is
31 started and contains all the commands done to it. 31 started and contains all the commands done to it.
32 32
33 The first non-comment line in a control file must begin with 33 The first non-comment line in a control file must begin with
34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version, 34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
35 DIR is the directory where the playlist is located and FILE is the 35 DIR is the directory where the playlist is located and FILE is the
@@ -68,8 +68,8 @@
68 */ 68 */
69 69
70#include <stdio.h> 70#include <stdio.h>
71#include <stdlib.h> 71#include <stdlib.h>
72#include <ctype.h> 72#include <ctype.h>
73#include "string-extra.h" 73#include "string-extra.h"
74#include "playlist.h" 74#include "playlist.h"
75#include "ata_idle_notify.h" 75#include "ata_idle_notify.h"
@@ -127,14 +127,15 @@
127/* default load buffer size (should be at least 1 KiB) */ 127/* default load buffer size (should be at least 1 KiB) */
128#define PLAYLIST_LOAD_BUFLEN (32*1024) 128#define PLAYLIST_LOAD_BUFLEN (32*1024)
129 129
130
131#define PLAYLIST_CONTROL_FILE_VERSION 2 130#define PLAYLIST_CONTROL_FILE_VERSION 2
132 131
132#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
133
133/* 134/*
134 Each playlist index has a flag associated with it which identifies what 135 Each playlist index has a flag associated with it which identifies what
135 type of track it is. These flags are stored in the 4 high order bits of 136 type of track it is. These flags are stored in the 4 high order bits of
136 the index. 137 the index.
137 138
138 NOTE: This limits the playlist file size to a max of 256M. 139 NOTE: This limits the playlist file size to a max of 256M.
139 140
140 Bits 31-30: 141 Bits 31-30:
@@ -169,56 +170,12 @@ struct directory_search_context {
169 170
170static struct playlist_info current_playlist; 171static struct playlist_info current_playlist;
171 172
172static void empty_playlist(struct playlist_info* playlist, bool resume);
173static void new_playlist(struct playlist_info* playlist, const char *dir,
174 const char *file);
175static void create_control(struct playlist_info* playlist);
176static int check_control(struct playlist_info* playlist);
177static int recreate_control(struct playlist_info* playlist);
178static void update_playlist_filename(struct playlist_info* playlist,
179 const char *dir, const char *file);
180static int add_indices_to_playlist(struct playlist_info* playlist,
181 char* buffer, size_t buflen);
182static int add_track_to_playlist(struct playlist_info* playlist,
183 const char *filename, int position,
184 bool queue, int seek_pos);
185static int directory_search_callback(char* filename, void* context);
186static int remove_track_from_playlist(struct playlist_info* playlist,
187 int position, bool write);
188static int randomise_playlist(struct playlist_info* playlist,
189 unsigned int seed, bool start_current,
190 bool write);
191static int sort_playlist(struct playlist_info* playlist, bool start_current,
192 bool write);
193static int get_next_index(const struct playlist_info* playlist, int steps,
194 int repeat_mode);
195static void find_and_set_playlist_index(struct playlist_info* playlist,
196 unsigned int seek);
197static int compare(const void* p1, const void* p2);
198static int get_filename(struct playlist_info* playlist, int index, int seek,
199 bool control_file, char *buf, int buf_length);
200static int get_next_directory(char *dir);
201static int get_next_dir(char *dir, bool is_forward);
202static int get_previous_directory(char *dir);
203static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
204static ssize_t format_track_path(char *dest, char *src, int buf_length,
205 const char *dir);
206static void display_playlist_count(int count, const unsigned char *fmt,
207 bool final);
208static void display_buffer_full(void);
209static int flush_cached_control(struct playlist_info* playlist);
210static int update_control(struct playlist_info* playlist,
211 enum playlist_command command, int i1, int i2,
212 const char* s1, const char* s2, void* data);
213static void sync_control(struct playlist_info* playlist, bool force);
214static int rotate_index(const struct playlist_info* playlist, int index);
215
216#ifdef HAVE_DIRCACHE 173#ifdef HAVE_DIRCACHE
217#define PLAYLIST_LOAD_POINTERS 1 174#define PLAYLIST_LOAD_POINTERS 1
218 175
219static struct event_queue playlist_queue SHAREDBSS_ATTR; 176static struct event_queue playlist_queue SHAREDBSS_ATTR;
220static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; 177static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
221static const char playlist_thread_name[] = "playlist cachectrl"; 178static const char thread_playlist_name[] = "playlist cachectrl";
222#endif 179#endif
223 180
224static struct mutex current_playlist_mutex SHAREDBSS_ATTR; 181static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
@@ -253,14 +210,14 @@ static bool is_m3u8(const char* filename)
253} 210}
254 211
255 212
256/* Convert a filename in an M3U playlist to UTF-8. 213/* Convert a filename in an M3U playlist to UTF-8.
257 * 214 *
258 * buf - the filename to convert; can contain more than one line from the 215 * buf - the filename to convert; can contain more than one line from the
259 * playlist. 216 * playlist.
260 * buf_len - amount of buf that is used. 217 * buf_len - amount of buf that is used.
261 * buf_max - total size of buf. 218 * buf_max - total size of buf.
262 * temp - temporary conversion buffer, at least buf_max bytes. 219 * temp - temporary conversion buffer, at least buf_max bytes.
263 * 220 *
264 * Returns the length of the converted filename. 221 * Returns the length of the converted filename.
265 */ 222 */
266static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp) 223static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
@@ -279,10 +236,10 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
279 { 236 {
280 i--; 237 i--;
281 } 238 }
282 239
283 buf_len = i; 240 buf_len = i;
284 dest = temp; 241 dest = temp;
285 242
286 /* Convert char by char, so as to not overflow temp (iso_decode should 243 /* Convert char by char, so as to not overflow temp (iso_decode should
287 * preferably handle this). No more than 4 bytes should be generated for 244 * preferably handle this). No more than 4 bytes should be generated for
288 * each input char. 245 * each input char.
@@ -291,13 +248,222 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
291 { 248 {
292 dest = iso_decode(&buf[i], dest, -1, 1); 249 dest = iso_decode(&buf[i], dest, -1, 1);
293 } 250 }
294 251
295 *dest = 0; 252 *dest = 0;
296 strcpy(buf, temp); 253 strcpy(buf, temp);
297 return dest - temp; 254 return dest - temp;
298} 255}
299 256
300/* 257/*
258 * create control file for playlist
259 */
260static void create_control(struct playlist_info* playlist)
261{
262 playlist->control_fd = open(playlist->control_filename,
263 O_CREAT|O_RDWR|O_TRUNC, 0666);
264 if (playlist->control_fd < 0)
265 {
266 if (check_rockboxdir())
267 {
268 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
269 splashf(HZ*2, (unsigned char *)"%s (%d)",
270 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
271 playlist->control_fd);
272 }
273 playlist->control_created = false;
274 }
275 else
276 {
277 playlist->control_created = true;
278 }
279}
280
281/*
282 * Rotate indices such that first_index is index 0
283 */
284static int rotate_index(const struct playlist_info* playlist, int index)
285{
286 index -= playlist->first_index;
287 if (index < 0)
288 index += playlist->amount;
289
290 return index;
291}
292
293/*
294 * sync control file to disk
295 */
296static void sync_control(struct playlist_info* playlist, bool force)
297{
298#ifdef HAVE_DIRCACHE
299 if (playlist->started && force)
300#else
301 (void) force;
302
303 if (playlist->started)
304#endif
305 {
306 if (playlist->pending_control_sync)
307 {
308 mutex_lock(playlist->control_mutex);
309 fsync(playlist->control_fd);
310 playlist->pending_control_sync = false;
311 mutex_unlock(playlist->control_mutex);
312 }
313 }
314}
315
316/*
317 * Flush any cached control commands to disk. Called when playlist is being
318 * modified. Returns 0 on success and -1 on failure.
319 */
320static int flush_cached_control(struct playlist_info* playlist)
321{
322 int result = 0;
323 int i;
324
325 if (!playlist->num_cached)
326 return 0;
327
328 lseek(playlist->control_fd, 0, SEEK_END);
329
330 for (i=0; i<playlist->num_cached; i++)
331 {
332 struct playlist_control_cache* cache =
333 &(playlist->control_cache[i]);
334
335 switch (cache->command)
336 {
337 case PLAYLIST_COMMAND_PLAYLIST:
338 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
339 cache->i1, cache->s1, cache->s2);
340 break;
341 case PLAYLIST_COMMAND_ADD:
342 case PLAYLIST_COMMAND_QUEUE:
343 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
344 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
345 cache->i1, cache->i2);
346 if (result > 0)
347 {
348 /* save the position in file where name is written */
349 int* seek_pos = (int *)cache->data;
350 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
351 result = fdprintf(playlist->control_fd, "%s\n",
352 cache->s1);
353 }
354 break;
355 case PLAYLIST_COMMAND_DELETE:
356 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
357 break;
358 case PLAYLIST_COMMAND_SHUFFLE:
359 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
360 cache->i1, cache->i2);
361 break;
362 case PLAYLIST_COMMAND_UNSHUFFLE:
363 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
364 break;
365 case PLAYLIST_COMMAND_RESET:
366 result = fdprintf(playlist->control_fd, "R\n");
367 break;
368 default:
369 break;
370 }
371
372 if (result <= 0)
373 break;
374 }
375
376 if (result > 0)
377 {
378 playlist->num_cached = 0;
379 playlist->pending_control_sync = true;
380
381 result = 0;
382 }
383 else
384 {
385 result = -1;
386 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
387 }
388
389 return result;
390}
391
392/*
393 * Update control data with new command. Depending on the command, it may be
394 * cached or flushed to disk.
395 */
396static int update_control(struct playlist_info* playlist,
397 enum playlist_command command, int i1, int i2,
398 const char* s1, const char* s2, void* data)
399{
400 int result = 0;
401 struct playlist_control_cache* cache;
402 bool flush = false;
403
404 mutex_lock(playlist->control_mutex);
405
406 cache = &(playlist->control_cache[playlist->num_cached++]);
407
408 cache->command = command;
409 cache->i1 = i1;
410 cache->i2 = i2;
411 cache->s1 = s1;
412 cache->s2 = s2;
413 cache->data = data;
414
415 switch (command)
416 {
417 case PLAYLIST_COMMAND_PLAYLIST:
418 case PLAYLIST_COMMAND_ADD:
419 case PLAYLIST_COMMAND_QUEUE:
420#ifndef HAVE_DIRCACHE
421 case PLAYLIST_COMMAND_DELETE:
422 case PLAYLIST_COMMAND_RESET:
423#endif
424 flush = true;
425 break;
426 case PLAYLIST_COMMAND_SHUFFLE:
427 case PLAYLIST_COMMAND_UNSHUFFLE:
428 default:
429 /* only flush when needed */
430 break;
431 }
432
433 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
434 result = flush_cached_control(playlist);
435
436 mutex_unlock(playlist->control_mutex);
437
438 return result;
439}
440
441/*
442 * store directory and name of playlist file
443 */
444static void update_playlist_filename(struct playlist_info* playlist,
445 const char *dir, const char *file)
446{
447 char *sep="";
448 int dirlen = strlen(dir);
449
450 playlist->utf8 = is_m3u8(file);
451
452 /* If the dir does not end in trailing slash, we use a separator.
453 Otherwise we don't. */
454 if(!dirlen || '/' != dir[dirlen-1])
455 {
456 sep="/";
457 dirlen++;
458 }
459
460 playlist->dirlen = dirlen;
461
462 snprintf(playlist->filename, sizeof(playlist->filename),
463 "%s%s%s", dir, sep, file);
464}
465
466/*
301 * remove any files and indices associated with the playlist 467 * remove any files and indices associated with the playlist
302 */ 468 */
303static void empty_playlist(struct playlist_info* playlist, bool resume) 469static void empty_playlist(struct playlist_info* playlist, bool resume)
@@ -344,6 +510,73 @@ static void empty_playlist(struct playlist_info* playlist, bool resume)
344} 510}
345 511
346/* 512/*
513 * Returns absolute path of track
514 *
515 * dest: output buffer
516 * src: the file name from the playlist
517 * dir: the absolute path to the directory where the playlist resides
518 *
519 * The type of path in "src" determines what will be written to "dest":
520 *
521 * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
522 * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
523 * absolute path by replacing the drive letter with the volume that the
524 * *playlist* resides on, ie. the volume in "dir"
525 * 3. Relative paths are converted to absolute paths by prepending "dir".
526 * This also applies to Windows-style relative paths "C:foo/bar" where
527 * the drive letter is accepted but ignored.
528 */
529static ssize_t format_track_path(char *dest, char *src, int buf_length,
530 const char *dir)
531{
532 size_t len = 0;
533
534 /* Look for the end of the string */
535 while (1)
536 {
537 int c = src[len];
538 if (c == '\n' || c == '\r' || c == '\0')
539 break;
540 len++;
541 }
542
543 /* Now work back killing white space */
544 while (len > 0)
545 {
546 int c = src[len - 1];
547 if (c != '\t' && c != ' ')
548 break;
549 len--;
550 }
551
552 src[len] = '\0';
553
554 /* Replace backslashes with forward slashes */
555 path_correct_separators(src, src);
556
557 /* Handle Windows-style absolute paths */
558 if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
559 src[-1] == PATH_SEPCH)
560 {
561 #ifdef HAVE_MULTIVOLUME
562 const char *p;
563 path_strip_last_volume(dir, &p, false);
564 dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
565 #else
566 dir = ""; /* only volume is root */
567 #endif
568 }
569
570 len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
571 if (len >= (size_t)buf_length)
572 return -1; /* buffer too small */
573
574 path_remove_dot_segments (dest, dest);
575
576 return strlen (dest);
577}
578
579/*
347 * Initialize a new playlist for viewing/editing/playing. dir is the 580 * Initialize a new playlist for viewing/editing/playing. dir is the
348 * directory where the playlist is located and file is the filename. 581 * directory where the playlist is located and file is the filename.
349 */ 582 */
@@ -363,7 +596,7 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
363 else 596 else
364 dirused = ""; /* empty playlist */ 597 dirused = ""; /* empty playlist */
365 } 598 }
366 599
367 update_playlist_filename(playlist, dirused, fileused); 600 update_playlist_filename(playlist, dirused, fileused);
368 601
369 if (playlist->control_fd >= 0) 602 if (playlist->control_fd >= 0)
@@ -375,30 +608,6 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
375} 608}
376 609
377/* 610/*
378 * create control file for playlist
379 */
380static void create_control(struct playlist_info* playlist)
381{
382 playlist->control_fd = open(playlist->control_filename,
383 O_CREAT|O_RDWR|O_TRUNC, 0666);
384 if (playlist->control_fd < 0)
385 {
386 if (check_rockboxdir())
387 {
388 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
389 splashf(HZ*2, (unsigned char *)"%s (%d)",
390 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
391 playlist->control_fd);
392 }
393 playlist->control_created = false;
394 }
395 else
396 {
397 playlist->control_created = true;
398 }
399}
400
401/*
402 * validate the control file. This may include creating/initializing it if 611 * validate the control file. This may include creating/initializing it if
403 * necessary; 612 * necessary;
404 */ 613 */
@@ -429,6 +638,40 @@ static int check_control(struct playlist_info* playlist)
429 return 0; 638 return 0;
430} 639}
431 640
641
642/*
643 * Display buffer full message
644 */
645static void display_buffer_full(void)
646{
647 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
648}
649
650/*
651 * Display splash message showing progress of playlist/directory insertion or
652 * save.
653 */
654static void display_playlist_count(int count, const unsigned char *fmt,
655 bool final)
656{
657 static long talked_tick = 0;
658 long id = P2ID(fmt);
659 if(global_settings.talk_menu && id>=0)
660 {
661 if(final || (count && (talked_tick == 0
662 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
663 {
664 talked_tick = current_tick;
665 talk_number(count, false);
666 talk_id(id, true);
667 }
668 }
669 fmt = P2STR(fmt);
670
671 splashf(0, fmt, count, str(LANG_OFF_ABORT));
672}
673
674
432/* 675/*
433 * recreate the control file based on current playlist entries 676 * recreate the control file based on current playlist entries
434 */ 677 */
@@ -532,31 +775,6 @@ static int recreate_control(struct playlist_info* playlist)
532} 775}
533 776
534/* 777/*
535 * store directory and name of playlist file
536 */
537static void update_playlist_filename(struct playlist_info* playlist,
538 const char *dir, const char *file)
539{
540 char *sep="";
541 int dirlen = strlen(dir);
542
543 playlist->utf8 = is_m3u8(file);
544
545 /* If the dir does not end in trailing slash, we use a separator.
546 Otherwise we don't. */
547 if(!dirlen || '/' != dir[dirlen-1])
548 {
549 sep="/";
550 dirlen++;
551 }
552
553 playlist->dirlen = dirlen;
554
555 snprintf(playlist->filename, sizeof(playlist->filename),
556 "%s%s%s", dir, sep, file);
557}
558
559/*
560 * calculate track offsets within a playlist file 778 * calculate track offsets within a playlist file
561 */ 779 */
562static int add_indices_to_playlist(struct playlist_info* playlist, 780static int add_indices_to_playlist(struct playlist_info* playlist,
@@ -588,7 +806,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
588 i = lseek(playlist->fd, playlist->utf8 ? BOM_UTF_8_SIZE : 0, SEEK_SET); 806 i = lseek(playlist->fd, playlist->utf8 ? BOM_UTF_8_SIZE : 0, SEEK_SET);
589 807
590 playlist_fd = playlist->fd; 808 playlist_fd = playlist->fd;
591 playlist->fd = -2; /* DEBUGGING Remove me! */ 809 playlist->fd = -2; /* DEBUGGING Remove me! */
592 810
593 splash(0, ID2P(LANG_WAIT)); 811 splash(0, ID2P(LANG_WAIT));
594 812
@@ -642,11 +860,11 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
642 } 860 }
643 861
644exit: 862exit:
645/* v DEBUGGING Remove me! */ 863/* v DEBUGGING Remove me! */
646 if (playlist->fd != -2) 864 if (playlist->fd != -2)
647 splashf(HZ, "Error playlist fd"); 865 splashf(HZ, "Error playlist fd");
648 playlist->fd = playlist_fd; 866 playlist->fd = playlist_fd;
649/* ^ DEBUGGING Remove me! */ 867/* ^ DEBUGGING Remove me! */
650 playlist->amount = amount; 868 playlist->amount = amount;
651 playlist->max_playlist_size = max_playlist_size; 869 playlist->max_playlist_size = max_playlist_size;
652 mutex_unlock(playlist->control_mutex); 870 mutex_unlock(playlist->control_mutex);
@@ -657,6 +875,372 @@ exit:
657 return result; 875 return result;
658} 876}
659 877
878
879/*
880 * Checks if there are any music files in the dir or any of its
881 * subdirectories. May be called recursively.
882 */
883static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
884{
885 int result = -1;
886 size_t dirlen = strlen(dir);
887 int num_files = 0;
888 int i;
889 struct entry *files;
890 bool has_music = false;
891 bool has_subdir = false;
892 struct tree_context* tc = tree_get_context();
893
894 if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
895 MAX_PATH - dirlen)
896 {
897 return 0;
898 }
899
900 if (ft_load(tc, dir) < 0)
901 {
902 return -2;
903 }
904
905 tree_lock_cache(tc);
906 files = tree_get_entries(tc);
907 num_files = tc->filesindir;
908
909 for (i=0; i<num_files; i++)
910 {
911 if (files[i].attr & ATTR_DIRECTORY)
912 has_subdir = true;
913 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
914 {
915 has_music = true;
916 break;
917 }
918 }
919
920 if (has_music)
921 {
922 tree_unlock_cache(tc);
923 return 0;
924 }
925
926 if (has_subdir && recurse)
927 {
928 for (i=0; i<num_files; i++)
929 {
930 if (action_userabort(TIMEOUT_NOBLOCK))
931 {
932 result = -2;
933 break;
934 }
935
936 if (files[i].attr & ATTR_DIRECTORY)
937 {
938 result = check_subdir_for_music(dir, files[i].name, true);
939 if (!result)
940 break;
941 }
942 }
943 }
944 tree_unlock_cache(tc);
945
946 if (result < 0)
947 {
948 if (dirlen)
949 {
950 dir[dirlen] = '\0';
951 }
952 else
953 {
954 strcpy(dir, PATH_ROOTSTR);
955 }
956
957 /* we now need to reload our current directory */
958 if(ft_load(tc, dir) < 0)
959 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
960 }
961 return result;
962}
963
964/*
965 * search through all the directories (starting with the current) to find
966 * one that has tracks to play
967 */
968static int get_next_dir(char *dir, bool is_forward)
969{
970 struct playlist_info* playlist = &current_playlist;
971 int result = -1;
972 char *start_dir = NULL;
973 bool exit = false;
974 struct tree_context* tc = tree_get_context();
975 int saved_dirfilter = *(tc->dirfilter);
976 unsigned int base_len;
977
978 if (global_settings.constrain_next_folder)
979 {
980 /* constrain results to directories below user's start directory */
981 strcpy(dir, global_settings.start_directory);
982 base_len = strlen(dir);
983
984 /* strip any trailing slash from base directory */
985 if (base_len > 0 && dir[base_len - 1] == '/')
986 {
987 base_len--;
988 dir[base_len] = '\0';
989 }
990 }
991 else
992 {
993 /* start from root directory */
994 dir[0] = '\0';
995 base_len = 0;
996 }
997
998 /* process random folder advance */
999 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1000 {
1001 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
1002 if (fd >= 0)
1003 {
1004 int folder_count = 0;
1005 ssize_t nread = read(fd,&folder_count,sizeof(int));
1006 if ((nread == sizeof(int)) && folder_count)
1007 {
1008 char buffer[MAX_PATH];
1009 /* give up looking for a directory after we've had four
1010 times as many tries as there are directories. */
1011 unsigned long allowed_tries = folder_count * 4;
1012 int i;
1013 srand(current_tick);
1014 *(tc->dirfilter) = SHOW_MUSIC;
1015 tc->sort_dir = global_settings.sort_dir;
1016 while (!exit && allowed_tries--)
1017 {
1018 i = rand() % folder_count;
1019 lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
1020 read(fd, buffer, MAX_PATH);
1021 /* is the current dir within our base dir and has music? */
1022 if ((base_len == 0 || !strncmp(buffer, dir, base_len))
1023 && check_subdir_for_music(buffer, "", false) == 0)
1024 exit = true;
1025 }
1026 close(fd);
1027 *(tc->dirfilter) = saved_dirfilter;
1028 tc->sort_dir = global_settings.sort_dir;
1029 reload_directory();
1030 if (exit)
1031 {
1032 strcpy(dir,buffer);
1033 return 0;
1034 }
1035 }
1036 else
1037 close(fd);
1038 }
1039 }
1040
1041 /* if the current file is within our base dir, use its dir instead */
1042 if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
1043 strmemccpy(dir, playlist->filename, playlist->dirlen);
1044
1045 /* use the tree browser dircache to load files */
1046 *(tc->dirfilter) = SHOW_ALL;
1047
1048 /* set up sorting/direction */
1049 tc->sort_dir = global_settings.sort_dir;
1050 if (!is_forward)
1051 {
1052 static const char sortpairs[] =
1053 {
1054 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1055 [SORT_DATE] = SORT_DATE_REVERSED,
1056 [SORT_TYPE] = SORT_TYPE_REVERSED,
1057 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1058 [SORT_DATE_REVERSED] = SORT_DATE,
1059 [SORT_TYPE_REVERSED] = SORT_TYPE,
1060 };
1061
1062 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1063 tc->sort_dir = sortpairs[tc->sort_dir];
1064 }
1065
1066 while (!exit)
1067 {
1068 struct entry *files;
1069 int num_files = 0;
1070 int i;
1071
1072 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1073 {
1074 exit = true;
1075 result = -1;
1076 break;
1077 }
1078
1079 tree_lock_cache(tc);
1080 files = tree_get_entries(tc);
1081 num_files = tc->filesindir;
1082
1083 for (i=0; i<num_files; i++)
1084 {
1085 /* user abort */
1086 if (action_userabort(TIMEOUT_NOBLOCK))
1087 {
1088 result = -1;
1089 exit = true;
1090 break;
1091 }
1092
1093 if (files[i].attr & ATTR_DIRECTORY)
1094 {
1095 if (!start_dir)
1096 {
1097 result = check_subdir_for_music(dir, files[i].name, true);
1098 if (result != -1)
1099 {
1100 exit = true;
1101 break;
1102 }
1103 }
1104 else if (!strcmp(start_dir, files[i].name))
1105 start_dir = NULL;
1106 }
1107 }
1108 tree_unlock_cache(tc);
1109
1110 if (!exit)
1111 {
1112 /* we've already descended to the base dir with nothing found,
1113 check whether that contains music */
1114 if (strlen(dir) <= base_len)
1115 {
1116 result = check_subdir_for_music(dir, "", true);
1117 if (result == -1)
1118 /* there's no music files in the base directory,
1119 treat as a fatal error */
1120 result = -2;
1121 break;
1122 }
1123 else
1124 {
1125 /* move down to parent directory. current directory name is
1126 stored as the starting point for the search in parent */
1127 start_dir = strrchr(dir, '/');
1128 if (start_dir)
1129 {
1130 *start_dir = '\0';
1131 start_dir++;
1132 }
1133 else
1134 break;
1135 }
1136 }
1137 }
1138
1139 /* restore dirfilter */
1140 *(tc->dirfilter) = saved_dirfilter;
1141 tc->sort_dir = global_settings.sort_dir;
1142
1143 return result;
1144}
1145
1146static int get_next_directory(char *dir){
1147 return get_next_dir(dir, true);
1148}
1149
1150static int get_previous_directory(char *dir){
1151 return get_next_dir(dir, false);
1152}
1153
1154
1155/*
1156 * gets pathname for track at seek index
1157 */
1158static int get_filename(struct playlist_info* playlist, int index, int seek,
1159 bool control_file, char *buf, int buf_length)
1160{
1161 int fd;
1162 int max = -1;
1163 char tmp_buf[MAX_PATH+1];
1164 char dir_buf[MAX_PATH+1];
1165 bool utf8 = playlist->utf8;
1166
1167 if (buf_length > MAX_PATH+1)
1168 buf_length = MAX_PATH+1;
1169
1170#ifdef HAVE_DIRCACHE
1171 if (playlist->dcfrefs)
1172 {
1173 max = dircache_get_fileref_path(&playlist->dcfrefs[index],
1174 tmp_buf, sizeof(tmp_buf));
1175 }
1176#endif /* HAVE_DIRCACHE */
1177
1178 if (playlist->in_ram && !control_file && max < 0)
1179 {
1180 strmemccpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
1181 }
1182 else if (max < 0)
1183 {
1184 mutex_lock(playlist->control_mutex);
1185
1186 if (control_file)
1187 {
1188 fd = playlist->control_fd;
1189 utf8 = true;
1190 }
1191 else
1192 {
1193 if(-1 == playlist->fd)
1194 playlist->fd = open(playlist->filename, O_RDONLY);
1195
1196 fd = playlist->fd;
1197 }
1198
1199 if(-1 != fd)
1200 {
1201
1202 if (lseek(fd, seek, SEEK_SET) != seek)
1203 max = -1;
1204 else
1205 {
1206 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1207
1208 if (max > 0)
1209 {
1210 /* playlist file may end without a new line - terminate buffer */
1211 tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
1212
1213 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1214 * be as large as tmp_buf.
1215 */
1216 if (!utf8)
1217 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1218 }
1219 }
1220 }
1221
1222 mutex_unlock(playlist->control_mutex);
1223
1224 if (max < 0)
1225 {
1226 if (usb_detect() == USB_INSERTED)
1227 ; /* ignore error on usb plug */
1228 else if (control_file)
1229 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1230 else
1231 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1232
1233 return max;
1234 }
1235 }
1236
1237 strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
1238
1239 return format_track_path(buf, tmp_buf, buf_length, dir_buf);
1240
1241 (void)index;
1242}
1243
660/* 1244/*
661 * Utility function to create a new playlist, fill it with the next or 1245 * Utility function to create a new playlist, fill it with the next or
662 * previous directory, shuffle it if needed, and start playback. 1246 * previous directory, shuffle it if needed, and start playback.
@@ -701,33 +1285,6 @@ static int create_and_play_dir(int direction, bool play_last)
701} 1285}
702 1286
703/* 1287/*
704 * Removes all tracks, from the playlist, leaving the presently playing
705 * track queued.
706 */
707int playlist_remove_all_tracks(struct playlist_info *playlist)
708{
709 int result;
710
711 if (playlist == NULL)
712 playlist = &current_playlist;
713
714 while (playlist->index > 0)
715 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
716 return result;
717
718 while (playlist->amount > 1)
719 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
720 return result;
721
722 if (playlist->amount == 1) {
723 playlist->indices[0] |= PLAYLIST_QUEUED;
724 }
725
726 return 0;
727}
728
729
730/*
731 * Add track to playlist at specified position. There are seven special 1288 * Add track to playlist at specified position. There are seven special
732 * positions that can be specified: 1289 * positions that can be specified:
733 * PLAYLIST_PREPEND - Add track at beginning of playlist 1290 * PLAYLIST_PREPEND - Add track at beginning of playlist
@@ -803,16 +1360,16 @@ static int add_track_to_playlist(struct playlist_info* playlist,
803 int offset; 1360 int offset;
804 int n = playlist->amount - 1361 int n = playlist->amount -
805 rotate_index(playlist, playlist->index); 1362 rotate_index(playlist, playlist->index);
806 1363
807 if (n > 0) 1364 if (n > 0)
808 offset = rand() % n; 1365 offset = rand() % n;
809 else 1366 else
810 offset = 0; 1367 offset = 0;
811 1368
812 position = playlist->index + offset + 1; 1369 position = playlist->index + offset + 1;
813 if (position >= playlist->amount) 1370 if (position >= playlist->amount)
814 position -= playlist->amount; 1371 position -= playlist->amount;
815 1372
816 insert_position = position; 1373 insert_position = position;
817 } 1374 }
818 else 1375 else
@@ -828,11 +1385,11 @@ static int add_track_to_playlist(struct playlist_info* playlist,
828 case PLAYLIST_REPLACE: 1385 case PLAYLIST_REPLACE:
829 if (playlist_remove_all_tracks(playlist) < 0) 1386 if (playlist_remove_all_tracks(playlist) < 0)
830 return -1; 1387 return -1;
831 1388
832 playlist->last_insert_pos = position = insert_position = playlist->index + 1; 1389 playlist->last_insert_pos = position = insert_position = playlist->index + 1;
833 break; 1390 break;
834 } 1391 }
835 1392
836 if (queue) 1393 if (queue)
837 flags |= PLAYLIST_QUEUED; 1394 flags |= PLAYLIST_QUEUED;
838 1395
@@ -845,7 +1402,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
845 playlist->dcfrefs[i] = playlist->dcfrefs[i-1]; 1402 playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
846#endif 1403#endif
847 } 1404 }
848 1405
849 /* update stored indices if needed */ 1406 /* update stored indices if needed */
850 1407
851 if (orig_position < 0) 1408 if (orig_position < 0)
@@ -881,7 +1438,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
881 1438
882 playlist->amount++; 1439 playlist->amount++;
883 playlist->num_inserted_tracks++; 1440 playlist->num_inserted_tracks++;
884 1441
885 return insert_position; 1442 return insert_position;
886} 1443}
887 1444
@@ -918,7 +1475,7 @@ static int directory_search_callback(char* filename, void* context)
918 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT); 1475 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
919 1476
920 display_playlist_count(c->count, count_str, false); 1477 display_playlist_count(c->count, count_str, false);
921 1478
922 if ((c->count) == PLAYLIST_DISPLAY_COUNT && 1479 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
923 (audio_status() & AUDIO_STATUS_PLAY) && 1480 (audio_status() & AUDIO_STATUS_PLAY) &&
924 c->playlist->started) 1481 c->playlist->started)
@@ -981,11 +1538,32 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
981 1538
982 sync_control(playlist, false); 1539 sync_control(playlist, false);
983 } 1540 }
984 1541
985 return 0; 1542 return 0;
986} 1543}
987 1544
988/* 1545/*
1546 * Search for the seek track and set appropriate indices. Used after shuffle
1547 * to make sure the current index is still pointing to correct track.
1548 */
1549static void find_and_set_playlist_index(struct playlist_info* playlist,
1550 unsigned int seek)
1551{
1552 int i;
1553
1554 /* Set the index to the current song */
1555 for (i=0; i<playlist->amount; i++)
1556 {
1557 if (playlist->indices[i] == seek)
1558 {
1559 playlist->index = playlist->first_index = i;
1560
1561 break;
1562 }
1563 }
1564}
1565
1566/*
989 * randomly rearrange the array of indices for the playlist. If start_current 1567 * randomly rearrange the array of indices for the playlist. If start_current
990 * is true then update the index to the new index of the current playing track 1568 * is true then update the index to the new index of the current playing track
991 */ 1569 */
@@ -996,7 +1574,7 @@ static int randomise_playlist(struct playlist_info* playlist,
996 int count; 1574 int count;
997 int candidate; 1575 int candidate;
998 unsigned int current = playlist->indices[playlist->index]; 1576 unsigned int current = playlist->indices[playlist->index];
999 1577
1000 /* seed 0 is used to identify sorted playlist for resume purposes */ 1578 /* seed 0 is used to identify sorted playlist for resume purposes */
1001 if (seed == 0) 1579 if (seed == 0)
1002 seed = 1; 1580 seed = 1;
@@ -1039,11 +1617,38 @@ static int randomise_playlist(struct playlist_info* playlist,
1039 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed, 1617 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
1040 playlist->first_index, NULL, NULL, NULL); 1618 playlist->first_index, NULL, NULL, NULL);
1041 } 1619 }
1042 1620
1043 return 0; 1621 return 0;
1044} 1622}
1045 1623
1046/* 1624/*
1625 * used to sort track indices. Sort order is as follows:
1626 * 1. Prepended tracks (in prepend order)
1627 * 2. Playlist/directory tracks (in playlist order)
1628 * 3. Inserted/Appended tracks (in insert order)
1629 */
1630static int compare(const void* p1, const void* p2)
1631{
1632 unsigned long* e1 = (unsigned long*) p1;
1633 unsigned long* e2 = (unsigned long*) p2;
1634 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1635 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1636
1637 if (flags1 == flags2)
1638 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1639 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1640 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1641 return -1;
1642 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1643 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1644 return 1;
1645 else if (flags1 && flags2)
1646 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1647 else
1648 return *e1 - *e2;
1649}
1650
1651/*
1047 * Sort the array of indices for the playlist. If start_current is true then 1652 * Sort the array of indices for the playlist. If start_current is true then
1048 * set the index to the new index of the current song. 1653 * set the index to the new index of the current song.
1049 * Also while going to unshuffled mode set the first_index to 0. 1654 * Also while going to unshuffled mode set the first_index to 0.
@@ -1079,7 +1684,7 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
1079 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE, 1684 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1080 playlist->first_index, -1, NULL, NULL, NULL); 1685 playlist->first_index, -1, NULL, NULL, NULL);
1081 } 1686 }
1082 1687
1083 return 0; 1688 return 0;
1084} 1689}
1085 1690
@@ -1091,7 +1696,7 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
1091 int i, count, direction; 1696 int i, count, direction;
1092 int index; 1697 int index;
1093 int stepped_count = 0; 1698 int stepped_count = 0;
1094 1699
1095 if (steps < 0) 1700 if (steps < 0)
1096 { 1701 {
1097 direction = -1; 1702 direction = -1;
@@ -1129,28 +1734,6 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
1129 return steps; 1734 return steps;
1130} 1735}
1131 1736
1132/* Marks the index of the track to be skipped that is "steps" away from
1133 * current playing track.
1134 */
1135void playlist_skip_entry(struct playlist_info *playlist, int steps)
1136{
1137 int index;
1138
1139 if (playlist == NULL)
1140 playlist = &current_playlist;
1141
1142 /* need to account for already skipped tracks */
1143 steps = calculate_step_count(playlist, steps);
1144
1145 index = playlist->index + steps;
1146 if (index < 0)
1147 index += playlist->amount;
1148 else if (index >= playlist->amount)
1149 index -= playlist->amount;
1150
1151 playlist->indices[index] |= PLAYLIST_SKIPPED;
1152}
1153
1154/* 1737/*
1155 * returns the index of the track that is "steps" away from current playing 1738 * returns the index of the track that is "steps" away from current playing
1156 * track. 1739 * track.
@@ -1229,56 +1812,8 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
1229 /* No luck if the whole playlist was bad. */ 1812 /* No luck if the whole playlist was bad. */
1230 if (playlist->indices[next_index] & PLAYLIST_SKIPPED) 1813 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1231 return -1; 1814 return -1;
1232
1233 return next_index;
1234}
1235
1236/*
1237 * Search for the seek track and set appropriate indices. Used after shuffle
1238 * to make sure the current index is still pointing to correct track.
1239 */
1240static void find_and_set_playlist_index(struct playlist_info* playlist,
1241 unsigned int seek)
1242{
1243 int i;
1244
1245 /* Set the index to the current song */
1246 for (i=0; i<playlist->amount; i++)
1247 {
1248 if (playlist->indices[i] == seek)
1249 {
1250 playlist->index = playlist->first_index = i;
1251
1252 break;
1253 }
1254 }
1255}
1256
1257/*
1258 * used to sort track indices. Sort order is as follows:
1259 * 1. Prepended tracks (in prepend order)
1260 * 2. Playlist/directory tracks (in playlist order)
1261 * 3. Inserted/Appended tracks (in insert order)
1262 */
1263static int compare(const void* p1, const void* p2)
1264{
1265 unsigned long* e1 = (unsigned long*) p1;
1266 unsigned long* e2 = (unsigned long*) p2;
1267 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1268 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1269 1815
1270 if (flags1 == flags2) 1816 return next_index;
1271 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1272 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1273 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1274 return -1;
1275 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1276 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1277 return 1;
1278 else if (flags1 && flags2)
1279 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1280 else
1281 return *e1 - *e2;
1282} 1817}
1283 1818
1284#ifdef HAVE_DIRCACHE 1819#ifdef HAVE_DIRCACHE
@@ -1287,7 +1822,7 @@ static int compare(const void* p1, const void* p2)
1287 * without affecting playlist load up performance. This thread also flushes 1822 * without affecting playlist load up performance. This thread also flushes
1288 * any pending control commands when the disk spins up. 1823 * any pending control commands when the disk spins up.
1289 */ 1824 */
1290static void playlist_flush_callback(void) 1825static void flush_playlist_callback(void)
1291{ 1826{
1292 struct playlist_info *playlist; 1827 struct playlist_info *playlist;
1293 playlist = &current_playlist; 1828 playlist = &current_playlist;
@@ -1303,7 +1838,7 @@ static void playlist_flush_callback(void)
1303 } 1838 }
1304} 1839}
1305 1840
1306static void playlist_thread(void) 1841static void thread_playlist(void)
1307{ 1842{
1308 struct queue_event ev; 1843 struct queue_event ev;
1309 bool dirty_pointers = false; 1844 bool dirty_pointers = false;
@@ -1340,12 +1875,12 @@ static void playlist_thread(void)
1340 if (playlist->control_fd >= 0) 1875 if (playlist->control_fd >= 0)
1341 { 1876 {
1342 if (playlist->num_cached > 0) 1877 if (playlist->num_cached > 0)
1343 register_storage_idle_func(playlist_flush_callback); 1878 register_storage_idle_func(flush_playlist_callback);
1344 } 1879 }
1345 1880
1346 if (!playlist->dcfrefs || playlist->amount <= 0) 1881 if (!playlist->dcfrefs || playlist->amount <= 0)
1347 break ; 1882 break ;
1348 1883
1349 /* Check if previously loaded pointers are intact. */ 1884 /* Check if previously loaded pointers are intact. */
1350 if (!dirty_pointers) 1885 if (!dirty_pointers)
1351 break ; 1886 break ;
@@ -1364,7 +1899,7 @@ static void playlist_thread(void)
1364 /* Process only pointers that are superficially stale. */ 1899 /* Process only pointers that are superficially stale. */
1365 if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0) 1900 if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
1366 continue ; 1901 continue ;
1367 1902
1368 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; 1903 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1369 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; 1904 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1370 1905
@@ -1387,10 +1922,10 @@ static void playlist_thread(void)
1387 1922
1388 if (index == playlist->amount) 1923 if (index == playlist->amount)
1389 dirty_pointers = false; 1924 dirty_pointers = false;
1390 1925
1391 break ; 1926 break ;
1392 } 1927 }
1393 1928
1394 case SYS_USB_CONNECTED: 1929 case SYS_USB_CONNECTED:
1395 usb_acknowledge(SYS_USB_CONNECTED_ACK); 1930 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1396 usb_wait_for_disconnect(&playlist_queue); 1931 usb_wait_for_disconnect(&playlist_queue);
@@ -1401,629 +1936,6 @@ static void playlist_thread(void)
1401#endif 1936#endif
1402 1937
1403/* 1938/*
1404 * gets pathname for track at seek index
1405 */
1406static int get_filename(struct playlist_info* playlist, int index, int seek,
1407 bool control_file, char *buf, int buf_length)
1408{
1409 int fd;
1410 int max = -1;
1411 char tmp_buf[MAX_PATH+1];
1412 char dir_buf[MAX_PATH+1];
1413 bool utf8 = playlist->utf8;
1414
1415 if (buf_length > MAX_PATH+1)
1416 buf_length = MAX_PATH+1;
1417
1418#ifdef HAVE_DIRCACHE
1419 if (playlist->dcfrefs)
1420 {
1421 max = dircache_get_fileref_path(&playlist->dcfrefs[index],
1422 tmp_buf, sizeof(tmp_buf));
1423 }
1424#endif /* HAVE_DIRCACHE */
1425
1426 if (playlist->in_ram && !control_file && max < 0)
1427 {
1428 strmemccpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
1429 }
1430 else if (max < 0)
1431 {
1432 mutex_lock(playlist->control_mutex);
1433
1434 if (control_file)
1435 {
1436 fd = playlist->control_fd;
1437 utf8 = true;
1438 }
1439 else
1440 {
1441 if(-1 == playlist->fd)
1442 playlist->fd = open(playlist->filename, O_RDONLY);
1443
1444 fd = playlist->fd;
1445 }
1446
1447 if(-1 != fd)
1448 {
1449
1450 if (lseek(fd, seek, SEEK_SET) != seek)
1451 max = -1;
1452 else
1453 {
1454 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1455
1456 if (max > 0)
1457 {
1458 /* playlist file may end without a new line - terminate buffer */
1459 tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
1460
1461 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1462 * be as large as tmp_buf.
1463 */
1464 if (!utf8)
1465 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1466 }
1467 }
1468 }
1469
1470 mutex_unlock(playlist->control_mutex);
1471
1472 if (max < 0)
1473 {
1474 if (usb_detect() == USB_INSERTED)
1475 ; /* ignore error on usb plug */
1476 else if (control_file)
1477 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1478 else
1479 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1480
1481 return max;
1482 }
1483 }
1484
1485 strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
1486
1487 return format_track_path(buf, tmp_buf, buf_length, dir_buf);
1488
1489 (void)index;
1490}
1491
1492static int get_next_directory(char *dir){
1493 return get_next_dir(dir, true);
1494}
1495
1496static int get_previous_directory(char *dir){
1497 return get_next_dir(dir, false);
1498}
1499
1500/*
1501 * search through all the directories (starting with the current) to find
1502 * one that has tracks to play
1503 */
1504static int get_next_dir(char *dir, bool is_forward)
1505{
1506 struct playlist_info* playlist = &current_playlist;
1507 int result = -1;
1508 char *start_dir = NULL;
1509 bool exit = false;
1510 struct tree_context* tc = tree_get_context();
1511 int saved_dirfilter = *(tc->dirfilter);
1512 unsigned int base_len;
1513
1514 if (global_settings.constrain_next_folder)
1515 {
1516 /* constrain results to directories below user's start directory */
1517 strcpy(dir, global_settings.start_directory);
1518 base_len = strlen(dir);
1519
1520 /* strip any trailing slash from base directory */
1521 if (base_len > 0 && dir[base_len - 1] == '/')
1522 {
1523 base_len--;
1524 dir[base_len] = '\0';
1525 }
1526 }
1527 else
1528 {
1529 /* start from root directory */
1530 dir[0] = '\0';
1531 base_len = 0;
1532 }
1533
1534 /* process random folder advance */
1535 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1536 {
1537 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
1538 if (fd >= 0)
1539 {
1540 int folder_count = 0;
1541 ssize_t nread = read(fd,&folder_count,sizeof(int));
1542 if ((nread == sizeof(int)) && folder_count)
1543 {
1544 char buffer[MAX_PATH];
1545 /* give up looking for a directory after we've had four
1546 times as many tries as there are directories. */
1547 unsigned long allowed_tries = folder_count * 4;
1548 int i;
1549 srand(current_tick);
1550 *(tc->dirfilter) = SHOW_MUSIC;
1551 tc->sort_dir = global_settings.sort_dir;
1552 while (!exit && allowed_tries--)
1553 {
1554 i = rand() % folder_count;
1555 lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
1556 read(fd, buffer, MAX_PATH);
1557 /* is the current dir within our base dir and has music? */
1558 if ((base_len == 0 || !strncmp(buffer, dir, base_len))
1559 && check_subdir_for_music(buffer, "", false) == 0)
1560 exit = true;
1561 }
1562 close(fd);
1563 *(tc->dirfilter) = saved_dirfilter;
1564 tc->sort_dir = global_settings.sort_dir;
1565 reload_directory();
1566 if (exit)
1567 {
1568 strcpy(dir,buffer);
1569 return 0;
1570 }
1571 }
1572 else
1573 close(fd);
1574 }
1575 }
1576
1577 /* if the current file is within our base dir, use its dir instead */
1578 if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
1579 strmemccpy(dir, playlist->filename, playlist->dirlen);
1580
1581 /* use the tree browser dircache to load files */
1582 *(tc->dirfilter) = SHOW_ALL;
1583
1584 /* set up sorting/direction */
1585 tc->sort_dir = global_settings.sort_dir;
1586 if (!is_forward)
1587 {
1588 static const char sortpairs[] =
1589 {
1590 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1591 [SORT_DATE] = SORT_DATE_REVERSED,
1592 [SORT_TYPE] = SORT_TYPE_REVERSED,
1593 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1594 [SORT_DATE_REVERSED] = SORT_DATE,
1595 [SORT_TYPE_REVERSED] = SORT_TYPE,
1596 };
1597
1598 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1599 tc->sort_dir = sortpairs[tc->sort_dir];
1600 }
1601
1602 while (!exit)
1603 {
1604 struct entry *files;
1605 int num_files = 0;
1606 int i;
1607
1608 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1609 {
1610 exit = true;
1611 result = -1;
1612 break;
1613 }
1614
1615 tree_lock_cache(tc);
1616 files = tree_get_entries(tc);
1617 num_files = tc->filesindir;
1618
1619 for (i=0; i<num_files; i++)
1620 {
1621 /* user abort */
1622 if (action_userabort(TIMEOUT_NOBLOCK))
1623 {
1624 result = -1;
1625 exit = true;
1626 break;
1627 }
1628
1629 if (files[i].attr & ATTR_DIRECTORY)
1630 {
1631 if (!start_dir)
1632 {
1633 result = check_subdir_for_music(dir, files[i].name, true);
1634 if (result != -1)
1635 {
1636 exit = true;
1637 break;
1638 }
1639 }
1640 else if (!strcmp(start_dir, files[i].name))
1641 start_dir = NULL;
1642 }
1643 }
1644 tree_unlock_cache(tc);
1645
1646 if (!exit)
1647 {
1648 /* we've already descended to the base dir with nothing found,
1649 check whether that contains music */
1650 if (strlen(dir) <= base_len)
1651 {
1652 result = check_subdir_for_music(dir, "", true);
1653 if (result == -1)
1654 /* there's no music files in the base directory,
1655 treat as a fatal error */
1656 result = -2;
1657 break;
1658 }
1659 else
1660 {
1661 /* move down to parent directory. current directory name is
1662 stored as the starting point for the search in parent */
1663 start_dir = strrchr(dir, '/');
1664 if (start_dir)
1665 {
1666 *start_dir = '\0';
1667 start_dir++;
1668 }
1669 else
1670 break;
1671 }
1672 }
1673 }
1674
1675 /* restore dirfilter */
1676 *(tc->dirfilter) = saved_dirfilter;
1677 tc->sort_dir = global_settings.sort_dir;
1678
1679 return result;
1680}
1681
1682/*
1683 * Checks if there are any music files in the dir or any of its
1684 * subdirectories. May be called recursively.
1685 */
1686static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1687{
1688 int result = -1;
1689 size_t dirlen = strlen(dir);
1690 int num_files = 0;
1691 int i;
1692 struct entry *files;
1693 bool has_music = false;
1694 bool has_subdir = false;
1695 struct tree_context* tc = tree_get_context();
1696
1697 if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
1698 MAX_PATH - dirlen)
1699 {
1700 return 0;
1701 }
1702
1703 if (ft_load(tc, dir) < 0)
1704 {
1705 return -2;
1706 }
1707
1708 tree_lock_cache(tc);
1709 files = tree_get_entries(tc);
1710 num_files = tc->filesindir;
1711
1712 for (i=0; i<num_files; i++)
1713 {
1714 if (files[i].attr & ATTR_DIRECTORY)
1715 has_subdir = true;
1716 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1717 {
1718 has_music = true;
1719 break;
1720 }
1721 }
1722
1723 if (has_music)
1724 {
1725 tree_unlock_cache(tc);
1726 return 0;
1727 }
1728
1729 if (has_subdir && recurse)
1730 {
1731 for (i=0; i<num_files; i++)
1732 {
1733 if (action_userabort(TIMEOUT_NOBLOCK))
1734 {
1735 result = -2;
1736 break;
1737 }
1738
1739 if (files[i].attr & ATTR_DIRECTORY)
1740 {
1741 result = check_subdir_for_music(dir, files[i].name, true);
1742 if (!result)
1743 break;
1744 }
1745 }
1746 }
1747 tree_unlock_cache(tc);
1748
1749 if (result < 0)
1750 {
1751 if (dirlen)
1752 {
1753 dir[dirlen] = '\0';
1754 }
1755 else
1756 {
1757 strcpy(dir, PATH_ROOTSTR);
1758 }
1759
1760 /* we now need to reload our current directory */
1761 if(ft_load(tc, dir) < 0)
1762 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1763 }
1764 return result;
1765}
1766
1767/*
1768 * Returns absolute path of track
1769 *
1770 * dest: output buffer
1771 * src: the file name from the playlist
1772 * dir: the absolute path to the directory where the playlist resides
1773 *
1774 * The type of path in "src" determines what will be written to "dest":
1775 *
1776 * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
1777 * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
1778 * absolute path by replacing the drive letter with the volume that the
1779 * *playlist* resides on, ie. the volume in "dir"
1780 * 3. Relative paths are converted to absolute paths by prepending "dir".
1781 * This also applies to Windows-style relative paths "C:foo/bar" where
1782 * the drive letter is accepted but ignored.
1783 */
1784static ssize_t format_track_path(char *dest, char *src, int buf_length,
1785 const char *dir)
1786{
1787 size_t len = 0;
1788
1789 /* Look for the end of the string */
1790 while (1)
1791 {
1792 int c = src[len];
1793 if (c == '\n' || c == '\r' || c == '\0')
1794 break;
1795 len++;
1796 }
1797
1798 /* Now work back killing white space */
1799 while (len > 0)
1800 {
1801 int c = src[len - 1];
1802 if (c != '\t' && c != ' ')
1803 break;
1804 len--;
1805 }
1806
1807 src[len] = '\0';
1808
1809 /* Replace backslashes with forward slashes */
1810 path_correct_separators(src, src);
1811
1812 /* Handle Windows-style absolute paths */
1813 if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
1814 src[-1] == PATH_SEPCH)
1815 {
1816 #ifdef HAVE_MULTIVOLUME
1817 const char *p;
1818 path_strip_last_volume(dir, &p, false);
1819 dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
1820 #else
1821 dir = ""; /* only volume is root */
1822 #endif
1823 }
1824
1825 len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
1826 if (len >= (size_t)buf_length)
1827 return -1; /* buffer too small */
1828
1829 path_remove_dot_segments (dest, dest);
1830
1831 return strlen (dest);
1832}
1833
1834/*
1835 * Display splash message showing progress of playlist/directory insertion or
1836 * save.
1837 */
1838static void display_playlist_count(int count, const unsigned char *fmt,
1839 bool final)
1840{
1841 static long talked_tick = 0;
1842 long id = P2ID(fmt);
1843 if(global_settings.talk_menu && id>=0)
1844 {
1845 if(final || (count && (talked_tick == 0
1846 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1847 {
1848 talked_tick = current_tick;
1849 talk_number(count, false);
1850 talk_id(id, true);
1851 }
1852 }
1853 fmt = P2STR(fmt);
1854
1855 splashf(0, fmt, count, str(LANG_OFF_ABORT));
1856}
1857
1858/*
1859 * Display buffer full message
1860 */
1861static void display_buffer_full(void)
1862{
1863 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1864}
1865
1866/*
1867 * Flush any cached control commands to disk. Called when playlist is being
1868 * modified. Returns 0 on success and -1 on failure.
1869 */
1870static int flush_cached_control(struct playlist_info* playlist)
1871{
1872 int result = 0;
1873 int i;
1874
1875 if (!playlist->num_cached)
1876 return 0;
1877
1878 lseek(playlist->control_fd, 0, SEEK_END);
1879
1880 for (i=0; i<playlist->num_cached; i++)
1881 {
1882 struct playlist_control_cache* cache =
1883 &(playlist->control_cache[i]);
1884
1885 switch (cache->command)
1886 {
1887 case PLAYLIST_COMMAND_PLAYLIST:
1888 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1889 cache->i1, cache->s1, cache->s2);
1890 break;
1891 case PLAYLIST_COMMAND_ADD:
1892 case PLAYLIST_COMMAND_QUEUE:
1893 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1894 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1895 cache->i1, cache->i2);
1896 if (result > 0)
1897 {
1898 /* save the position in file where name is written */
1899 int* seek_pos = (int *)cache->data;
1900 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1901 result = fdprintf(playlist->control_fd, "%s\n",
1902 cache->s1);
1903 }
1904 break;
1905 case PLAYLIST_COMMAND_DELETE:
1906 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1907 break;
1908 case PLAYLIST_COMMAND_SHUFFLE:
1909 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1910 cache->i1, cache->i2);
1911 break;
1912 case PLAYLIST_COMMAND_UNSHUFFLE:
1913 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1914 break;
1915 case PLAYLIST_COMMAND_RESET:
1916 result = fdprintf(playlist->control_fd, "R\n");
1917 break;
1918 default:
1919 break;
1920 }
1921
1922 if (result <= 0)
1923 break;
1924 }
1925
1926 if (result > 0)
1927 {
1928 playlist->num_cached = 0;
1929 playlist->pending_control_sync = true;
1930
1931 result = 0;
1932 }
1933 else
1934 {
1935 result = -1;
1936 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1937 }
1938
1939 return result;
1940}
1941
1942/*
1943 * Update control data with new command. Depending on the command, it may be
1944 * cached or flushed to disk.
1945 */
1946static int update_control(struct playlist_info* playlist,
1947 enum playlist_command command, int i1, int i2,
1948 const char* s1, const char* s2, void* data)
1949{
1950 int result = 0;
1951 struct playlist_control_cache* cache;
1952 bool flush = false;
1953
1954 mutex_lock(playlist->control_mutex);
1955
1956 cache = &(playlist->control_cache[playlist->num_cached++]);
1957
1958 cache->command = command;
1959 cache->i1 = i1;
1960 cache->i2 = i2;
1961 cache->s1 = s1;
1962 cache->s2 = s2;
1963 cache->data = data;
1964
1965 switch (command)
1966 {
1967 case PLAYLIST_COMMAND_PLAYLIST:
1968 case PLAYLIST_COMMAND_ADD:
1969 case PLAYLIST_COMMAND_QUEUE:
1970#ifndef HAVE_DIRCACHE
1971 case PLAYLIST_COMMAND_DELETE:
1972 case PLAYLIST_COMMAND_RESET:
1973#endif
1974 flush = true;
1975 break;
1976 case PLAYLIST_COMMAND_SHUFFLE:
1977 case PLAYLIST_COMMAND_UNSHUFFLE:
1978 default:
1979 /* only flush when needed */
1980 break;
1981 }
1982
1983 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1984 result = flush_cached_control(playlist);
1985
1986 mutex_unlock(playlist->control_mutex);
1987
1988 return result;
1989}
1990
1991/*
1992 * sync control file to disk
1993 */
1994static void sync_control(struct playlist_info* playlist, bool force)
1995{
1996#ifdef HAVE_DIRCACHE
1997 if (playlist->started && force)
1998#else
1999 (void) force;
2000
2001 if (playlist->started)
2002#endif
2003 {
2004 if (playlist->pending_control_sync)
2005 {
2006 mutex_lock(playlist->control_mutex);
2007 fsync(playlist->control_fd);
2008 playlist->pending_control_sync = false;
2009 mutex_unlock(playlist->control_mutex);
2010 }
2011 }
2012}
2013
2014/*
2015 * Rotate indices such that first_index is index 0
2016 */
2017static int rotate_index(const struct playlist_info* playlist, int index)
2018{
2019 index -= playlist->first_index;
2020 if (index < 0)
2021 index += playlist->amount;
2022
2023 return index;
2024}
2025
2026/*
2027 * Allocate a temporary buffer for loading playlists 1939 * Allocate a temporary buffer for loading playlists
2028 */ 1940 */
2029static int alloc_tempbuf(size_t* buflen) 1941static int alloc_tempbuf(size_t* buflen)
@@ -2064,6 +1976,14 @@ static struct buflib_callbacks ops = {
2064 .move_callback = move_callback, 1976 .move_callback = move_callback,
2065 .shrink_callback = NULL, 1977 .shrink_callback = NULL,
2066}; 1978};
1979
1980/******************************************************************************/
1981/******************************************************************************/
1982/* ************************************************************************** */
1983/* * PUBLIC INTERFACE FUNCTIONS * *********************************************/
1984/* ************************************************************************** */
1985/******************************************************************************/
1986/******************************************************************************/
2067/* 1987/*
2068 * Initialize playlist entries at startup 1988 * Initialize playlist entries at startup
2069 */ 1989 */
@@ -2099,8 +2019,8 @@ void playlist_init(void)
2099 playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops); 2019 playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops);
2100 playlist->dcfrefs = core_get_data(handle); 2020 playlist->dcfrefs = core_get_data(handle);
2101 copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size); 2021 copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
2102 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), 2022 create_thread(thread_playlist, playlist_stack, sizeof(playlist_stack),
2103 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) 2023 0, thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND)
2104 IF_COP(, CPU)); 2024 IF_COP(, CPU));
2105 2025
2106 queue_init(&playlist_queue, true); 2026 queue_init(&playlist_queue, true);
@@ -2129,6 +2049,113 @@ void playlist_shutdown(void)
2129} 2049}
2130 2050
2131/* 2051/*
2052 * Add track to in_ram playlist. Used when playing directories.
2053 */
2054int playlist_add(const char *filename)
2055{
2056 struct playlist_info* playlist = &current_playlist;
2057 int len = strlen(filename);
2058
2059 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2060 (playlist->amount >= playlist->max_playlist_size))
2061 {
2062 display_buffer_full();
2063 return -1;
2064 }
2065
2066 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2067#ifdef HAVE_DIRCACHE
2068 copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
2069#endif
2070
2071 playlist->amount++;
2072
2073 strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
2074 playlist->buffer_end_pos += len;
2075 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2076
2077 return 0;
2078}
2079
2080/* returns number of tracks in playlist (includes queued/inserted tracks) */
2081int playlist_amount_ex(const struct playlist_info* playlist)
2082{
2083 if (!playlist)
2084 playlist = &current_playlist;
2085
2086 return playlist->amount;
2087}
2088
2089/* returns number of tracks in current playlist */
2090int playlist_amount(void)
2091{
2092 return playlist_amount_ex(NULL);
2093}
2094
2095/*
2096 * Create a new playlist If playlist is not NULL then we're loading a
2097 * playlist off disk for viewing/editing. The index_buffer is used to store
2098 * playlist indices (required for and only used if !current playlist). The
2099 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2100 */
2101int playlist_create_ex(struct playlist_info* playlist,
2102 const char* dir, const char* file,
2103 void* index_buffer, int index_buffer_size,
2104 void* temp_buffer, int temp_buffer_size)
2105{
2106 if (!playlist)
2107 playlist = &current_playlist;
2108 else
2109 {
2110 /* Initialize playlist structure */
2111 int r = rand() % 10;
2112 playlist->current = false;
2113
2114 /* Use random name for control file */
2115 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2116 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2117 playlist->fd = -1;
2118 playlist->control_fd = -1;
2119
2120 if (index_buffer)
2121 {
2122 int num_indices = index_buffer_size /
2123 playlist_get_required_bufsz(playlist, false, 1);
2124
2125 if (num_indices > global_settings.max_files_in_playlist)
2126 num_indices = global_settings.max_files_in_playlist;
2127
2128 playlist->max_playlist_size = num_indices;
2129 playlist->indices = index_buffer;
2130#ifdef HAVE_DIRCACHE
2131 playlist->dcfrefs = (void *)&playlist->indices[num_indices];
2132#endif
2133 }
2134 else
2135 {
2136 playlist->max_playlist_size = current_playlist.max_playlist_size;
2137 playlist->indices = current_playlist.indices;
2138#ifdef HAVE_DIRCACHE
2139 playlist->dcfrefs = current_playlist.dcfrefs;
2140#endif
2141 }
2142
2143 playlist->buffer_size = 0;
2144 playlist->buffer_handle = -1;
2145 playlist->buffer = NULL;
2146 playlist->control_mutex = &created_playlist_mutex;
2147 }
2148
2149 new_playlist(playlist, dir, file);
2150
2151 if (file)
2152 /* load the playlist file */
2153 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2154
2155 return 0;
2156}
2157
2158/*
2132 * Create new playlist 2159 * Create new playlist
2133 */ 2160 */
2134int playlist_create(const char *dir, const char *file) 2161int playlist_create(const char *dir, const char *file)
@@ -2162,474 +2189,196 @@ int playlist_create(const char *dir, const char *file)
2162 return 0; 2189 return 0;
2163} 2190}
2164 2191
2165#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12) 2192/* Returns false if 'steps' is out of bounds, else true */
2166 2193bool playlist_check(int steps)
2167/*
2168 * Restore the playlist state based on control file commands. Called to
2169 * resume playback after shutdown.
2170 */
2171int playlist_resume(void)
2172{ 2194{
2173 struct playlist_info* playlist = &current_playlist; 2195 struct playlist_info* playlist = &current_playlist;
2174 char *buffer;
2175 size_t buflen;
2176 int handle;
2177 int nread;
2178 int total_read = 0;
2179 int control_file_size = 0;
2180 bool first = true;
2181 bool sorted = true;
2182 int result = -1;
2183 2196
2184 splash(0, ID2P(LANG_WAIT)); 2197 /* always allow folder navigation */
2185 if (core_allocatable() < (1 << 10)) 2198 if (global_settings.next_folder && playlist->in_ram)
2186 talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */ 2199 return true;
2187 2200
2188#ifdef HAVE_DIRCACHE 2201 int index = get_next_index(playlist, steps, -1);
2189 dircache_wait(); /* we need the dircache to use the files in the playlist */
2190#endif
2191 2202
2192 handle = alloc_tempbuf(&buflen); 2203 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2193 if (handle < 0) 2204 index = get_next_index(playlist, steps, REPEAT_ALL);
2194 {
2195 splashf(HZ * 2, "%s(): OOM", __func__);
2196 return -1;
2197 }
2198 2205
2199 /* align buffer for faster load times */ 2206 return (index >= 0);
2200 buffer = core_get_data(handle); 2207}
2201 STORAGE_ALIGN_BUFFER(buffer, buflen);
2202 buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
2203 2208
2204 playlist_shutdown(); /* flush any cached control commands to disk */ 2209/*
2205 empty_playlist(playlist, true); 2210 * Close files and delete control file for non-current playlist.
2211 */
2212void playlist_close(struct playlist_info* playlist)
2213{
2214 if (!playlist)
2215 return;
2206 2216
2207 playlist->control_fd = open(playlist->control_filename, O_RDWR); 2217 if (playlist->fd >= 0) {
2208 if (playlist->control_fd < 0) 2218 close(playlist->fd);
2209 { 2219 playlist->fd = -1;
2210 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2211 goto out;
2212 } 2220 }
2213 playlist->control_created = true;
2214 2221
2215 control_file_size = filesize(playlist->control_fd); 2222 if (playlist->control_fd >= 0) {
2216 if (control_file_size <= 0) 2223 close(playlist->control_fd);
2217 { 2224 playlist->control_fd = -1;
2218 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2219 goto out;
2220 } 2225 }
2221 2226
2222 /* read a small amount first to get the header */ 2227 if (playlist->control_created) {
2223 nread = read(playlist->control_fd, buffer, 2228 remove(playlist->control_filename);
2224 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen); 2229 playlist->control_created = false;
2225 if(nread <= 0)
2226 {
2227 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2228 goto out;
2229 } 2230 }
2231}
2230 2232
2231 playlist->started = true; 2233/*
2234 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2235 * we want to delete the current playing track.
2236 */
2237int playlist_delete(struct playlist_info* playlist, int index)
2238{
2239 int result = 0;
2232 2240
2233 while (1) 2241 if (!playlist)
2242 playlist = &current_playlist;
2243
2244 if (check_control(playlist) < 0)
2234 { 2245 {
2235 result = 0; 2246 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2236 int count; 2247 return -1;
2237 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT; 2248 }
2238 int last_newline = 0;
2239 int str_count = -1;
2240 bool newline = true;
2241 bool exit_loop = false;
2242 char *p = buffer;
2243 char *str1 = NULL;
2244 char *str2 = NULL;
2245 char *str3 = NULL;
2246 unsigned long last_tick = current_tick;
2247 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
2248 bool useraborted = false;
2249
2250 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2251 {
2252 /* Show a splash while we are loading. */
2253 splash_progress((total_read + count), control_file_size,
2254 "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
2255 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2256 {
2257 if (action_userabort(TIMEOUT_NOBLOCK))
2258 {
2259 useraborted = true;
2260 break;
2261 }
2262 last_tick = current_tick;
2263 }
2264 /* Are we on a new line? */
2265 if((*p == '\n') || (*p == '\r'))
2266 {
2267 *p = '\0';
2268 2249
2269 /* save last_newline in case we need to load more data */ 2250 if (index == PLAYLIST_DELETE_CURRENT)
2270 last_newline = count; 2251 index = playlist->index;
2271 2252
2272 switch (current_command) 2253 result = remove_track_from_playlist(playlist, index, true);
2273 {
2274 case PLAYLIST_COMMAND_PLAYLIST:
2275 {
2276 /* str1=version str2=dir str3=file */
2277 int version;
2278 2254
2279 if (!str1) 2255 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
2280 { 2256 playlist->started)
2281 result = -2; 2257 audio_flush_and_reload_tracks();
2282 exit_loop = true;
2283 break;
2284 }
2285
2286 if (!str2)
2287 str2 = "";
2288
2289 if (!str3)
2290 str3 = "";
2291
2292 version = atoi(str1);
2293
2294 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2295 {
2296 result = -3;
2297 goto out;
2298 }
2299
2300 update_playlist_filename(playlist, str2, str3);
2301
2302 if (str3[0] != '\0')
2303 {
2304 /* NOTE: add_indices_to_playlist() overwrites the
2305 audiobuf so we need to reload control file
2306 data */
2307 add_indices_to_playlist(playlist, buffer, buflen);
2308 }
2309 else if (str2[0] != '\0')
2310 {
2311 playlist->in_ram = true;
2312 resume_directory(str2);
2313 }
2314
2315 /* load the rest of the data */
2316 first = false;
2317 exit_loop = true;
2318 2258
2319 break; 2259 return result;
2320 } 2260}
2321 case PLAYLIST_COMMAND_ADD:
2322 case PLAYLIST_COMMAND_QUEUE:
2323 {
2324 /* str1=position str2=last_position str3=file */
2325 int position, last_position;
2326 bool queue;
2327
2328 if (!str1 || !str2 || !str3)
2329 {
2330 result = -4;
2331 exit_loop = true;
2332 break;
2333 }
2334
2335 position = atoi(str1);
2336 last_position = atoi(str2);
2337
2338 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2339 false:true;
2340 2261
2341 /* seek position is based on str3's position in 2262/*
2342 buffer */ 2263 * Search specified directory for tracks and notify via callback. May be
2343 if (add_track_to_playlist(playlist, str3, position, 2264 * called recursively.
2344 queue, total_read+(str3-buffer)) < 0) 2265 */
2345 { 2266int playlist_directory_tracksearch(const char* dirname, bool recurse,
2346 result = -5; 2267 int (*callback)(char*, void*),
2347 goto out; 2268 void* context)
2348 } 2269{
2349 2270 char buf[MAX_PATH+1];
2350 playlist->last_insert_pos = last_position; 2271 int result = 0;
2272 int num_files = 0;
2273 int i;;
2274 struct tree_context* tc = tree_get_context();
2275 struct tree_cache* cache = &tc->cache;
2276 int old_dirfilter = *(tc->dirfilter);
2351 2277
2352 break; 2278 if (!callback)
2353 } 2279 return -1;
2354 case PLAYLIST_COMMAND_DELETE:
2355 {
2356 /* str1=position */
2357 int position;
2358
2359 if (!str1)
2360 {
2361 result = -6;
2362 exit_loop = true;
2363 break;
2364 }
2365
2366 position = atoi(str1);
2367
2368 if (remove_track_from_playlist(playlist, position,
2369 false) < 0)
2370 {
2371 result = -7;
2372 goto out;
2373 }
2374 2280
2375 break; 2281 /* use the tree browser dircache to load files */
2376 } 2282 *(tc->dirfilter) = SHOW_ALL;
2377 case PLAYLIST_COMMAND_SHUFFLE:
2378 {
2379 /* str1=seed str2=first_index */
2380 int seed;
2381
2382 if (!str1 || !str2)
2383 {
2384 result = -8;
2385 exit_loop = true;
2386 break;
2387 }
2388
2389 if (!sorted)
2390 {
2391 /* Always sort list before shuffling */
2392 sort_playlist(playlist, false, false);
2393 }
2394 2283
2395 seed = atoi(str1); 2284 if (ft_load(tc, dirname) < 0)
2396 playlist->first_index = atoi(str2); 2285 {
2397 2286 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
2398 if (randomise_playlist(playlist, seed, false, 2287 *(tc->dirfilter) = old_dirfilter;
2399 false) < 0) 2288 return -1;
2400 { 2289 }
2401 result = -9;
2402 goto out;
2403 }
2404 sorted = false;
2405 break;
2406 }
2407 case PLAYLIST_COMMAND_UNSHUFFLE:
2408 {
2409 /* str1=first_index */
2410 if (!str1)
2411 {
2412 result = -10;
2413 exit_loop = true;
2414 break;
2415 }
2416
2417 playlist->first_index = atoi(str1);
2418
2419 if (sort_playlist(playlist, false, false) < 0)
2420 {
2421 result = -11;
2422 goto out;
2423 }
2424 2290
2425 sorted = true; 2291 num_files = tc->filesindir;
2426 break;
2427 }
2428 case PLAYLIST_COMMAND_RESET:
2429 {
2430 playlist->last_insert_pos = -1;
2431 break;
2432 }
2433 case PLAYLIST_COMMAND_COMMENT:
2434 default:
2435 break;
2436 }
2437 2292
2438 newline = true; 2293 /* we've overwritten the dircache so tree browser will need to be
2294 reloaded */
2295 reload_directory();
2439 2296
2440 /* to ignore any extra newlines */ 2297 for (i=0; i<num_files; i++)
2441 current_command = PLAYLIST_COMMAND_COMMENT; 2298 {
2442 } 2299 /* user abort */
2443 else if(newline) 2300 if (action_userabort(TIMEOUT_NOBLOCK))
2444 { 2301 {
2445 newline = false; 2302 result = -1;
2303 break;
2304 }
2446 2305
2447 /* first non-comment line must always specify playlist */ 2306 struct entry *files = core_get_data(cache->entries_handle);
2448 if (first && *p != 'P' && *p != '#') 2307 if (files[i].attr & ATTR_DIRECTORY)
2308 {
2309 if (recurse)
2310 {
2311 /* recursively add directories */
2312 if (path_append(buf, dirname, files[i].name, sizeof(buf))
2313 >= sizeof(buf))
2449 { 2314 {
2450 result = -12; 2315 continue;
2451 exit_loop = true;
2452 break;
2453 } 2316 }
2454 2317
2455 switch (*p) 2318 result = playlist_directory_tracksearch(buf, recurse,
2456 { 2319 callback, context);
2457 case 'P': 2320 if (result < 0)
2458 /* playlist can only be specified once */ 2321 break;
2459 if (!first)
2460 {
2461 result = -13;
2462 exit_loop = true;
2463 break;
2464 }
2465 2322
2466 current_command = PLAYLIST_COMMAND_PLAYLIST; 2323 /* we now need to reload our current directory */
2467 break; 2324 if(ft_load(tc, dirname) < 0)
2468 case 'A': 2325 {
2469 current_command = PLAYLIST_COMMAND_ADD; 2326 result = -1;
2470 break; 2327 break;
2471 case 'Q':
2472 current_command = PLAYLIST_COMMAND_QUEUE;
2473 break;
2474 case 'D':
2475 current_command = PLAYLIST_COMMAND_DELETE;
2476 break;
2477 case 'S':
2478 current_command = PLAYLIST_COMMAND_SHUFFLE;
2479 break;
2480 case 'U':
2481 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2482 break;
2483 case 'R':
2484 current_command = PLAYLIST_COMMAND_RESET;
2485 break;
2486 case '#':
2487 current_command = PLAYLIST_COMMAND_COMMENT;
2488 break;
2489 default:
2490 result = -14;
2491 exit_loop = true;
2492 break;
2493 } 2328 }
2494 2329
2495 str_count = -1; 2330 num_files = tc->filesindir;
2496 str1 = NULL; 2331 if (!num_files)
2497 str2 = NULL;
2498 str3 = NULL;
2499 }
2500 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2501 {
2502 /* all control file strings are separated with a colon.
2503 Replace the colon with 0 to get proper strings that can be
2504 used by commands above */
2505 if (*p == ':')
2506 { 2332 {
2507 *p = '\0'; 2333 result = -1;
2508 str_count++; 2334 break;
2509
2510 if ((count+1) < nread)
2511 {
2512 switch (str_count)
2513 {
2514 case 0:
2515 str1 = p+1;
2516 break;
2517 case 1:
2518 str2 = p+1;
2519 break;
2520 case 2:
2521 str3 = p+1;
2522 break;
2523 default:
2524 /* allow last string to contain colons */
2525 *p = ':';
2526 break;
2527 }
2528 }
2529 } 2335 }
2530 } 2336 }
2337 else
2338 continue;
2531 } 2339 }
2532 2340 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
2533 if (result < 0)
2534 {
2535 splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
2536 goto out;
2537 }
2538
2539 if (useraborted)
2540 {
2541 splash(HZ*2, ID2P(LANG_CANCEL));
2542 result = -1;
2543 goto out;
2544 }
2545 if (!newline || (exit_loop && count<nread))
2546 { 2341 {
2547 if ((total_read + count) >= control_file_size) 2342 if (path_append(buf, dirname, files[i].name, sizeof(buf))
2343 >= sizeof(buf))
2548 { 2344 {
2549 /* no newline at end of control file */ 2345 continue;
2550 splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
2551 result = -15;
2552 goto out;
2553 } 2346 }
2554 2347
2555 /* We didn't end on a newline or we exited loop prematurely. 2348 if (callback(buf, context) != 0)
2556 Either way, re-read the remainder. */ 2349 {
2557 count = last_newline; 2350 result = -1;
2558 lseek(playlist->control_fd, total_read+count, SEEK_SET); 2351 break;
2559 } 2352 }
2560
2561 total_read += count;
2562
2563 if (first)
2564 /* still looking for header */
2565 nread = read(playlist->control_fd, buffer,
2566 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2567 else
2568 nread = read(playlist->control_fd, buffer, buflen);
2569 2353
2570 /* Terminate on EOF */ 2354 /* let the other threads work */
2571 if(nread <= 0) 2355 yield();
2572 {
2573 break;
2574 } 2356 }
2575 } 2357 }
2576 2358
2577#ifdef HAVE_DIRCACHE 2359 /* restore dirfilter */
2578 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); 2360 *(tc->dirfilter) = old_dirfilter;
2579#endif
2580 2361
2581out:
2582 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
2583 core_free(handle);
2584 return result; 2362 return result;
2585} 2363}
2586 2364
2587/*
2588 * Add track to in_ram playlist. Used when playing directories.
2589 */
2590int playlist_add(const char *filename)
2591{
2592 struct playlist_info* playlist = &current_playlist;
2593 int len = strlen(filename);
2594
2595 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2596 (playlist->amount >= playlist->max_playlist_size))
2597 {
2598 display_buffer_full();
2599 return -1;
2600 }
2601
2602 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2603#ifdef HAVE_DIRCACHE
2604 copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
2605#endif
2606
2607 playlist->amount++;
2608
2609 strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
2610 playlist->buffer_end_pos += len;
2611 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2612 2365
2613 return 0; 2366struct playlist_info *playlist_get_current(void)
2367{
2368 return &current_playlist;
2614} 2369}
2615 2370
2616/* shuffle newly created playlist using random seed. */ 2371/* Returns index of current playing track for display purposes. This value
2617int playlist_shuffle(int random_seed, int start_index) 2372 should not be used for resume purposes as it doesn't represent the actual
2373 index into the playlist */
2374int playlist_get_display_index(void)
2618{ 2375{
2619 struct playlist_info* playlist = &current_playlist; 2376 struct playlist_info* playlist = &current_playlist;
2620 2377
2621 bool start_current = false; 2378 /* first_index should always be index 0 for display purposes */
2622 2379 int index = rotate_index(playlist, playlist->index);
2623 if (start_index >= 0 && global_settings.play_selected)
2624 {
2625 /* store the seek position before the shuffle */
2626 playlist->index = playlist->first_index = start_index;
2627 start_current = true;
2628 }
2629
2630 randomise_playlist(playlist, random_seed, start_current, true);
2631 2380
2632 return playlist->index; 2381 return (index+1);
2633} 2382}
2634 2383
2635/* returns the crc32 of the filename of the track at the specified index */ 2384/* returns the crc32 of the filename of the track at the specified index */
@@ -2650,212 +2399,48 @@ unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
2650 return crc_32(basename, strlen(basename), -1); 2399 return crc_32(basename, strlen(basename), -1);
2651} 2400}
2652 2401
2653/* resume a playlist track with the given crc_32 of the track name. */ 2402/* returns index of first track in playlist */
2654void playlist_resume_track(int start_index, unsigned int crc, 2403int playlist_get_first_index(const struct playlist_info* playlist)
2655 unsigned long elapsed, unsigned long offset)
2656{
2657 int i;
2658 unsigned int tmp_crc;
2659 struct playlist_info* playlist = &current_playlist;
2660 tmp_crc = playlist_get_filename_crc32(playlist, start_index);
2661 if (tmp_crc == crc)
2662 {
2663 playlist_start(start_index, elapsed, offset);
2664 return;
2665 }
2666
2667 for (i = 0 ; i < playlist->amount; i++)
2668 {
2669 tmp_crc = playlist_get_filename_crc32(playlist, i);
2670 if (tmp_crc == crc)
2671 {
2672 playlist_start(i, elapsed, offset);
2673 return;
2674 }
2675 }
2676
2677 /* If we got here the file wasnt found, so start from the beginning */
2678 playlist_start(0, 0, 0);
2679}
2680
2681/* start playing current playlist at specified index/offset */
2682void playlist_start(int start_index, unsigned long elapsed,
2683 unsigned long offset)
2684{
2685 struct playlist_info* playlist = &current_playlist;
2686
2687 playlist->index = start_index;
2688
2689 playlist->started = true;
2690 sync_control(playlist, false);
2691 audio_play(elapsed, offset);
2692 audio_resume();
2693}
2694
2695/* Returns false if 'steps' is out of bounds, else true */
2696bool playlist_check(int steps)
2697{ 2404{
2698 struct playlist_info* playlist = &current_playlist; 2405 if (!playlist)
2699 2406 playlist = &current_playlist;
2700 /* always allow folder navigation */
2701 if (global_settings.next_folder && playlist->in_ram)
2702 return true;
2703
2704 int index = get_next_index(playlist, steps, -1);
2705
2706 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2707 index = get_next_index(playlist, steps, REPEAT_ALL);
2708 2407
2709 return (index >= 0); 2408 return playlist->first_index;
2710} 2409}
2711 2410
2712/* get trackname of track that is "steps" away from current playing track. 2411/* returns the playlist filename */
2713 NULL is used to identify end of playlist */ 2412char *playlist_get_name(const struct playlist_info* playlist, char *buf,
2714const char* playlist_peek(int steps, char* buf, size_t buf_size) 2413 int buf_size)
2715{ 2414{
2716 struct playlist_info* playlist = &current_playlist; 2415 if (!playlist)
2717 int seek; 2416 playlist = &current_playlist;
2718 char *temp_ptr;
2719 int index;
2720 bool control_file;
2721
2722 index = get_next_index(playlist, steps, -1);
2723 if (index < 0)
2724 return NULL;
2725
2726 /* Just testing - don't care about the file name */
2727 if (!buf || !buf_size)
2728 return "";
2729 2417
2730 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; 2418 strmemccpy(buf, playlist->filename, buf_size);
2731 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2732 2419
2733 if (get_filename(playlist, index, seek, control_file, buf, 2420 if (!buf[0])
2734 buf_size) < 0)
2735 return NULL; 2421 return NULL;
2736 2422
2737 temp_ptr = buf; 2423 return buf;
2738
2739 if (!playlist->in_ram || control_file)
2740 {
2741 /* remove bogus dirs from beginning of path
2742 (workaround for buggy playlist creation tools) */
2743 while (temp_ptr)
2744 {
2745 if (file_exists(temp_ptr))
2746 break;
2747
2748 temp_ptr = strchr(temp_ptr+1, '/');
2749 }
2750
2751 if (!temp_ptr)
2752 {
2753 /* Even though this is an invalid file, we still need to pass a
2754 file name to the caller because NULL is used to indicate end
2755 of playlist */
2756 return buf;
2757 }
2758 }
2759
2760 return temp_ptr;
2761} 2424}
2762 2425
2763/* 2426/* return size of buffer needed for playlist to initialize num_indices entries */
2764 * Update indices as track has changed 2427size_t playlist_get_required_bufsz(struct playlist_info* playlist,
2765 */ 2428 bool include_namebuf,
2766int playlist_next(int steps) 2429 int num_indices)
2767{ 2430{
2768 struct playlist_info* playlist = &current_playlist; 2431 size_t namebuf = 0;
2769 int index;
2770
2771 if ( (steps > 0)
2772#ifdef AB_REPEAT_ENABLE
2773 && (global_settings.repeat_mode != REPEAT_AB)
2774#endif
2775 && (global_settings.repeat_mode != REPEAT_ONE) )
2776 {
2777 int i, j;
2778
2779 /* We need to delete all the queued songs */
2780 for (i=0, j=steps; i<j; i++)
2781 {
2782 index = get_next_index(playlist, i, -1);
2783
2784 if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2785 {
2786 remove_track_from_playlist(playlist, index, true);
2787 steps--; /* one less track */
2788 }
2789 }
2790 }
2791
2792 index = get_next_index(playlist, steps, -1);
2793
2794 if (index < 0)
2795 {
2796 /* end of playlist... or is it */
2797 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2798 playlist->amount > 1)
2799 {
2800 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2801 playlist->first_index = 0;
2802 sort_playlist(playlist, false, false);
2803 randomise_playlist(playlist, current_tick, false, true);
2804
2805 playlist->started = true;
2806 playlist->index = 0;
2807 index = 0;
2808 }
2809 else if (playlist->in_ram && global_settings.next_folder)
2810 {
2811 index = create_and_play_dir(steps, true);
2812
2813 if (index >= 0)
2814 {
2815 playlist->index = index;
2816 }
2817 }
2818
2819 return index;
2820 }
2821
2822 playlist->index = index;
2823
2824 if (playlist->last_insert_pos >= 0 && steps > 0)
2825 {
2826 /* check to see if we've gone beyond the last inserted track */
2827 int cur = rotate_index(playlist, index);
2828 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2829
2830 if (cur > last_pos)
2831 {
2832 /* reset last inserted track */
2833 playlist->last_insert_pos = -1;
2834
2835 if (playlist->control_fd >= 0)
2836 {
2837 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2838 -1, -1, NULL, NULL, NULL);
2839
2840 if (result < 0)
2841 return result;
2842
2843 sync_control(playlist, false);
2844 }
2845 }
2846 }
2847 2432
2848 return index; 2433 if (!playlist)
2849} 2434 playlist = &current_playlist;
2850 2435
2851/* try playing next or previous folder */ 2436 size_t unit_size = sizeof (*playlist->indices);
2852bool playlist_next_dir(int direction) 2437 #ifdef HAVE_DIRCACHE
2853{ 2438 unit_size += sizeof (*playlist->dcfrefs);
2854 /* not to mess up real playlists */ 2439 #endif
2855 if(!current_playlist.in_ram) 2440 if (include_namebuf)
2856 return false; 2441 namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
2857 2442
2858 return create_and_play_dir(direction, false) >= 0; 2443 return (num_indices * unit_size) + namebuf;
2859} 2444}
2860 2445
2861/* Get resume info for current playing song. If return value is -1 then 2446/* Get resume info for current playing song. If return value is -1 then
@@ -2869,256 +2454,54 @@ int playlist_get_resume_info(int *resume_index)
2869 return 0; 2454 return 0;
2870} 2455}
2871 2456
2872/* Update resume info for current playing song. Returns -1 on error. */ 2457/* returns shuffle seed of playlist */
2873int playlist_update_resume_info(const struct mp3entry* id3) 2458int playlist_get_seed(const struct playlist_info* playlist)
2874{ 2459{
2875 struct playlist_info* playlist = &current_playlist; 2460 if (!playlist)
2876 2461 playlist = &current_playlist;
2877 if (id3)
2878 {
2879 if (global_status.resume_index != playlist->index ||
2880 global_status.resume_elapsed != id3->elapsed ||
2881 global_status.resume_offset != id3->offset)
2882 {
2883 unsigned int crc = playlist_get_filename_crc32(playlist,
2884 playlist->index);
2885 global_status.resume_index = playlist->index;
2886 global_status.resume_crc32 = crc;
2887 global_status.resume_elapsed = id3->elapsed;
2888 global_status.resume_offset = id3->offset;
2889 status_save();
2890 }
2891 }
2892 else
2893 {
2894 global_status.resume_index = -1;
2895 global_status.resume_crc32 = -1;
2896 global_status.resume_elapsed = -1;
2897 global_status.resume_offset = -1;
2898 status_save();
2899 }
2900 2462
2901 return 0; 2463 return playlist->seed;
2902} 2464}
2903 2465
2904/* Returns index of current playing track for display purposes. This value 2466/* Fills info structure with information about track at specified index.
2905 should not be used for resume purposes as it doesn't represent the actual 2467 Returns 0 on success and -1 on failure */
2906 index into the playlist */ 2468int playlist_get_track_info(struct playlist_info* playlist, int index,
2907int playlist_get_display_index(void) 2469 struct playlist_track_info* info)
2908{ 2470{
2909 struct playlist_info* playlist = &current_playlist; 2471 int seek;
2910 2472 bool control_file;
2911 /* first_index should always be index 0 for display purposes */
2912 int index = rotate_index(playlist, playlist->index);
2913
2914 return (index+1);
2915}
2916 2473
2917/* returns number of tracks in current playlist */
2918int playlist_amount(void)
2919{
2920 return playlist_amount_ex(NULL);
2921}
2922/* set playlist->last_shuffle_start to playlist->amount for
2923 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2924void playlist_set_last_shuffled_start(void)
2925{
2926 struct playlist_info* playlist = &current_playlist;
2927 playlist->last_shuffled_start = playlist->amount;
2928}
2929/*
2930 * Create a new playlist If playlist is not NULL then we're loading a
2931 * playlist off disk for viewing/editing. The index_buffer is used to store
2932 * playlist indices (required for and only used if !current playlist). The
2933 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2934 */
2935int playlist_create_ex(struct playlist_info* playlist,
2936 const char* dir, const char* file,
2937 void* index_buffer, int index_buffer_size,
2938 void* temp_buffer, int temp_buffer_size)
2939{
2940 if (!playlist) 2474 if (!playlist)
2941 playlist = &current_playlist; 2475 playlist = &current_playlist;
2942 else
2943 {
2944 /* Initialize playlist structure */
2945 int r = rand() % 10;
2946 playlist->current = false;
2947
2948 /* Use random name for control file */
2949 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2950 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2951 playlist->fd = -1;
2952 playlist->control_fd = -1;
2953
2954 if (index_buffer)
2955 {
2956 int num_indices = index_buffer_size /
2957 playlist_get_required_bufsz(playlist, false, 1);
2958
2959 if (num_indices > global_settings.max_files_in_playlist)
2960 num_indices = global_settings.max_files_in_playlist;
2961
2962 playlist->max_playlist_size = num_indices;
2963 playlist->indices = index_buffer;
2964#ifdef HAVE_DIRCACHE
2965 playlist->dcfrefs = (void *)&playlist->indices[num_indices];
2966#endif
2967 }
2968 else
2969 {
2970 playlist->max_playlist_size = current_playlist.max_playlist_size;
2971 playlist->indices = current_playlist.indices;
2972#ifdef HAVE_DIRCACHE
2973 playlist->dcfrefs = current_playlist.dcfrefs;
2974#endif
2975 }
2976
2977 playlist->buffer_size = 0;
2978 playlist->buffer_handle = -1;
2979 playlist->buffer = NULL;
2980 playlist->control_mutex = &created_playlist_mutex;
2981 }
2982
2983 new_playlist(playlist, dir, file);
2984 2476
2985 if (file) 2477 if (index < 0 || index >= playlist->amount)
2986 /* load the playlist file */
2987 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2988
2989 return 0;
2990}
2991
2992/*
2993 * Set the specified playlist as the current.
2994 * NOTE: You will get undefined behaviour if something is already playing so
2995 * remember to stop before calling this. Also, this call will
2996 * effectively close your playlist, making it unusable.
2997 */
2998int playlist_set_current(struct playlist_info* playlist)
2999{
3000 if (!playlist || (check_control(playlist) < 0))
3001 return -1; 2478 return -1;
3002 2479
3003 empty_playlist(&current_playlist, false); 2480 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3004 2481 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3005 strmemccpy(current_playlist.filename, playlist->filename,
3006 sizeof(current_playlist.filename));
3007
3008 current_playlist.utf8 = playlist->utf8;
3009 current_playlist.fd = playlist->fd;
3010 2482
3011 close(playlist->control_fd); 2483 if (get_filename(playlist, index, seek, control_file, info->filename,
3012 playlist->control_fd = -1; 2484 sizeof(info->filename)) < 0)
3013 close(current_playlist.control_fd);
3014 current_playlist.control_fd = -1;
3015 remove(current_playlist.control_filename);
3016 current_playlist.control_created = false;
3017 if (rename(playlist->control_filename,
3018 current_playlist.control_filename) < 0)
3019 return -1;
3020 current_playlist.control_fd = open(current_playlist.control_filename,
3021 O_RDWR);
3022 if (current_playlist.control_fd < 0)
3023 return -1; 2485 return -1;
3024 current_playlist.control_created = true;
3025 2486
3026 current_playlist.dirlen = playlist->dirlen; 2487 info->attr = 0;
3027 2488
3028 if (playlist->indices && playlist->indices != current_playlist.indices) 2489 if (control_file)
3029 { 2490 {
3030 memcpy((void*)current_playlist.indices, (void*)playlist->indices, 2491 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3031 playlist->max_playlist_size*sizeof(*playlist->indices)); 2492 info->attr |= PLAYLIST_ATTR_QUEUED;
3032#ifdef HAVE_DIRCACHE 2493 else
3033 copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs, 2494 info->attr |= PLAYLIST_ATTR_INSERTED;
3034 playlist->max_playlist_size);
3035#endif
3036 }
3037
3038 current_playlist.first_index = playlist->first_index;
3039 current_playlist.amount = playlist->amount;
3040 current_playlist.last_insert_pos = playlist->last_insert_pos;
3041 current_playlist.seed = playlist->seed;
3042 current_playlist.shuffle_modified = playlist->shuffle_modified;
3043 current_playlist.deleted = playlist->deleted;
3044 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
3045
3046 memcpy(current_playlist.control_cache, playlist->control_cache,
3047 sizeof(current_playlist.control_cache));
3048 current_playlist.num_cached = playlist->num_cached;
3049 current_playlist.pending_control_sync = playlist->pending_control_sync;
3050
3051 return 0;
3052}
3053struct playlist_info *playlist_get_current(void)
3054{
3055 return &current_playlist;
3056}
3057/*
3058 * Close files and delete control file for non-current playlist.
3059 */
3060void playlist_close(struct playlist_info* playlist)
3061{
3062 if (!playlist)
3063 return;
3064
3065 if (playlist->fd >= 0) {
3066 close(playlist->fd);
3067 playlist->fd = -1;
3068 }
3069
3070 if (playlist->control_fd >= 0) {
3071 close(playlist->control_fd);
3072 playlist->control_fd = -1;
3073 }
3074
3075 if (playlist->control_created) {
3076 remove(playlist->control_filename);
3077 playlist->control_created = false;
3078 }
3079}
3080
3081void playlist_sync(struct playlist_info* playlist)
3082{
3083 if (!playlist)
3084 playlist = &current_playlist;
3085
3086 sync_control(playlist, false);
3087 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3088 audio_flush_and_reload_tracks();
3089
3090#ifdef HAVE_DIRCACHE
3091 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3092#endif
3093}
3094
3095/*
3096 * Insert track into playlist at specified position (or one of the special
3097 * positions). Returns position where track was inserted or -1 if error.
3098 */
3099int playlist_insert_track(struct playlist_info* playlist, const char *filename,
3100 int position, bool queue, bool sync)
3101{
3102 int result;
3103
3104 if (!playlist)
3105 playlist = &current_playlist;
3106 2495
3107 if (check_control(playlist) < 0)
3108 {
3109 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3110 return -1;
3111 } 2496 }
3112 2497
3113 result = add_track_to_playlist(playlist, filename, position, queue, -1); 2498 if (playlist->indices[index] & PLAYLIST_SKIPPED)
2499 info->attr |= PLAYLIST_ATTR_SKIPPED;
3114 2500
3115 /* Check if we want manually sync later. For example when adding 2501 info->index = index;
3116 * bunch of files from tagcache, syncing after every file wouldn't be 2502 info->display_index = rotate_index(playlist, index) + 1;
3117 * a good thing to do. */
3118 if (sync && result >= 0)
3119 playlist_sync(playlist);
3120 2503
3121 return result; 2504 return 0;
3122} 2505}
3123 2506
3124/* 2507/*
@@ -3160,7 +2543,7 @@ int playlist_insert_directory(struct playlist_info* playlist,
3160 context.position = position; 2543 context.position = position;
3161 context.queue = queue; 2544 context.queue = queue;
3162 context.count = 0; 2545 context.count = 0;
3163 2546
3164 cpu_boost(true); 2547 cpu_boost(true);
3165 2548
3166 result = playlist_directory_tracksearch(dirname, recurse, 2549 result = playlist_directory_tracksearch(dirname, recurse,
@@ -3243,11 +2626,11 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3243 /* user abort */ 2626 /* user abort */
3244 if (action_userabort(TIMEOUT_NOBLOCK)) 2627 if (action_userabort(TIMEOUT_NOBLOCK))
3245 break; 2628 break;
3246 2629
3247 if (temp_buf[0] != '#' && temp_buf[0] != '\0') 2630 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3248 { 2631 {
3249 int insert_pos; 2632 int insert_pos;
3250 2633
3251 if (!utf8) 2634 if (!utf8)
3252 { 2635 {
3253 /* Use trackname as a temporay buffer. Note that trackname must 2636 /* Use trackname as a temporay buffer. Note that trackname must
@@ -3315,12 +2698,13 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3315} 2698}
3316 2699
3317/* 2700/*
3318 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then 2701 * Insert track into playlist at specified position (or one of the special
3319 * we want to delete the current playing track. 2702 * positions). Returns position where track was inserted or -1 if error.
3320 */ 2703 */
3321int playlist_delete(struct playlist_info* playlist, int index) 2704int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2705 int position, bool queue, bool sync)
3322{ 2706{
3323 int result = 0; 2707 int result;
3324 2708
3325 if (!playlist) 2709 if (!playlist)
3326 playlist = &current_playlist; 2710 playlist = &current_playlist;
@@ -3331,18 +2715,32 @@ int playlist_delete(struct playlist_info* playlist, int index)
3331 return -1; 2715 return -1;
3332 } 2716 }
3333 2717
3334 if (index == PLAYLIST_DELETE_CURRENT) 2718 result = add_track_to_playlist(playlist, filename, position, queue, -1);
3335 index = playlist->index;
3336 2719
3337 result = remove_track_from_playlist(playlist, index, true); 2720 /* Check if we want manually sync later. For example when adding
3338 2721 * bunch of files from tagcache, syncing after every file wouldn't be
3339 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && 2722 * a good thing to do. */
3340 playlist->started) 2723 if (sync && result >= 0)
3341 audio_flush_and_reload_tracks(); 2724 playlist_sync(playlist);
3342 2725
3343 return result; 2726 return result;
3344} 2727}
3345 2728
2729
2730/* returns true if playlist has been modified */
2731bool playlist_modified(const struct playlist_info* playlist)
2732{
2733 if (!playlist)
2734 playlist = &current_playlist;
2735
2736 if (playlist->shuffle_modified ||
2737 playlist->deleted ||
2738 playlist->num_inserted_tracks > 0)
2739 return true;
2740
2741 return false;
2742}
2743
3346/* 2744/*
3347 * Move track at index to new_index. Tracks between the two are shifted 2745 * Move track at index to new_index. Tracks between the two are shifted
3348 * appropriately. Returns 0 on success and -1 on failure. 2746 * appropriately. Returns 0 on success and -1 on failure.
@@ -3475,182 +2873,669 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index)
3475 return result; 2873 return result;
3476} 2874}
3477 2875
3478/* shuffle currently playing playlist */ 2876/* returns full path of playlist (minus extension) */
3479int playlist_randomise(struct playlist_info* playlist, unsigned int seed, 2877char *playlist_name(const struct playlist_info* playlist, char *buf,
3480 bool start_current) 2878 int buf_size)
3481{ 2879{
3482 int result; 2880 char *sep;
3483 2881
3484 if (!playlist) 2882 if (!playlist)
3485 playlist = &current_playlist; 2883 playlist = &current_playlist;
3486 2884
3487 check_control(playlist); 2885 strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size);
3488 2886
3489 result = randomise_playlist(playlist, seed, start_current, true); 2887 if (!buf[0])
2888 return NULL;
3490 2889
3491 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && 2890 /* Remove extension */
3492 playlist->started) 2891 sep = strrchr(buf, '.');
3493 audio_flush_and_reload_tracks(); 2892 if (sep)
2893 *sep = 0;
3494 2894
3495 return result; 2895 return buf;
3496} 2896}
3497 2897
3498/* sort currently playing playlist */ 2898/*
3499int playlist_sort(struct playlist_info* playlist, bool start_current) 2899 * Update indices as track has changed
2900 */
2901int playlist_next(int steps)
3500{ 2902{
3501 int result; 2903 struct playlist_info* playlist = &current_playlist;
2904 int index;
3502 2905
3503 if (!playlist) 2906 if ( (steps > 0)
3504 playlist = &current_playlist; 2907#ifdef AB_REPEAT_ENABLE
2908 && (global_settings.repeat_mode != REPEAT_AB)
2909#endif
2910 && (global_settings.repeat_mode != REPEAT_ONE) )
2911 {
2912 int i, j;
3505 2913
3506 check_control(playlist); 2914 /* We need to delete all the queued songs */
2915 for (i=0, j=steps; i<j; i++)
2916 {
2917 index = get_next_index(playlist, i, -1);
3507 2918
3508 result = sort_playlist(playlist, start_current, true); 2919 if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2920 {
2921 remove_track_from_playlist(playlist, index, true);
2922 steps--; /* one less track */
2923 }
2924 }
2925 }
3509 2926
3510 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && 2927 index = get_next_index(playlist, steps, -1);
3511 playlist->started)
3512 audio_flush_and_reload_tracks();
3513 2928
3514 return result; 2929 if (index < 0)
3515} 2930 {
2931 /* end of playlist... or is it */
2932 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2933 playlist->amount > 1)
2934 {
2935 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2936 playlist->first_index = 0;
2937 sort_playlist(playlist, false, false);
2938 randomise_playlist(playlist, current_tick, false, true);
3516 2939
3517/* returns true if playlist has been modified */ 2940 playlist->started = true;
3518bool playlist_modified(const struct playlist_info* playlist) 2941 playlist->index = 0;
3519{ 2942 index = 0;
3520 if (!playlist) 2943 }
3521 playlist = &current_playlist; 2944 else if (playlist->in_ram && global_settings.next_folder)
2945 {
2946 index = create_and_play_dir(steps, true);
3522 2947
3523 if (playlist->shuffle_modified || 2948 if (index >= 0)
3524 playlist->deleted || 2949 {
3525 playlist->num_inserted_tracks > 0) 2950 playlist->index = index;
3526 return true; 2951 }
2952 }
3527 2953
3528 return false; 2954 return index;
3529} 2955 }
3530 2956
3531/* returns index of first track in playlist */ 2957 playlist->index = index;
3532int playlist_get_first_index(const struct playlist_info* playlist)
3533{
3534 if (!playlist)
3535 playlist = &current_playlist;
3536 2958
3537 return playlist->first_index; 2959 if (playlist->last_insert_pos >= 0 && steps > 0)
3538} 2960 {
2961 /* check to see if we've gone beyond the last inserted track */
2962 int cur = rotate_index(playlist, index);
2963 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
3539 2964
3540/* returns shuffle seed of playlist */ 2965 if (cur > last_pos)
3541int playlist_get_seed(const struct playlist_info* playlist) 2966 {
3542{ 2967 /* reset last inserted track */
3543 if (!playlist) 2968 playlist->last_insert_pos = -1;
3544 playlist = &current_playlist;
3545 2969
3546 return playlist->seed; 2970 if (playlist->control_fd >= 0)
2971 {
2972 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2973 -1, -1, NULL, NULL, NULL);
2974
2975 if (result < 0)
2976 return result;
2977
2978 sync_control(playlist, false);
2979 }
2980 }
2981 }
2982
2983 return index;
3547} 2984}
3548 2985
3549/* returns number of tracks in playlist (includes queued/inserted tracks) */ 2986/* try playing next or previous folder */
3550int playlist_amount_ex(const struct playlist_info* playlist) 2987bool playlist_next_dir(int direction)
3551{ 2988{
3552 if (!playlist) 2989 /* not to mess up real playlists */
3553 playlist = &current_playlist; 2990 if(!current_playlist.in_ram)
2991 return false;
3554 2992
3555 return playlist->amount; 2993 return create_and_play_dir(direction, false) >= 0;
3556} 2994}
3557 2995
3558/* returns full path of playlist (minus extension) */ 2996/* get trackname of track that is "steps" away from current playing track.
3559char *playlist_name(const struct playlist_info* playlist, char *buf, 2997 NULL is used to identify end of playlist */
3560 int buf_size) 2998const char* playlist_peek(int steps, char* buf, size_t buf_size)
3561{ 2999{
3562 char *sep; 3000 struct playlist_info* playlist = &current_playlist;
3001 int seek;
3002 char *temp_ptr;
3003 int index;
3004 bool control_file;
3563 3005
3564 if (!playlist) 3006 index = get_next_index(playlist, steps, -1);
3565 playlist = &current_playlist; 3007 if (index < 0)
3008 return NULL;
3566 3009
3567 strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size); 3010 /* Just testing - don't care about the file name */
3568 3011 if (!buf || !buf_size)
3569 if (!buf[0]) 3012 return "";
3013
3014 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3015 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3016
3017 if (get_filename(playlist, index, seek, control_file, buf,
3018 buf_size) < 0)
3570 return NULL; 3019 return NULL;
3571 3020
3572 /* Remove extension */ 3021 temp_ptr = buf;
3573 sep = strrchr(buf, '.');
3574 if (sep)
3575 *sep = 0;
3576 3022
3577 return buf; 3023 if (!playlist->in_ram || control_file)
3024 {
3025 /* remove bogus dirs from beginning of path
3026 (workaround for buggy playlist creation tools) */
3027 while (temp_ptr)
3028 {
3029 if (file_exists(temp_ptr))
3030 break;
3031
3032 temp_ptr = strchr(temp_ptr+1, '/');
3033 }
3034
3035 if (!temp_ptr)
3036 {
3037 /* Even though this is an invalid file, we still need to pass a
3038 file name to the caller because NULL is used to indicate end
3039 of playlist */
3040 return buf;
3041 }
3042 }
3043
3044 return temp_ptr;
3578} 3045}
3579 3046
3580/* returns the playlist filename */ 3047/* shuffle currently playing playlist */
3581char *playlist_get_name(const struct playlist_info* playlist, char *buf, 3048int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3582 int buf_size) 3049 bool start_current)
3583{ 3050{
3051 int result;
3052
3584 if (!playlist) 3053 if (!playlist)
3585 playlist = &current_playlist; 3054 playlist = &current_playlist;
3586 3055
3587 strmemccpy(buf, playlist->filename, buf_size); 3056 check_control(playlist);
3588 3057
3589 if (!buf[0]) 3058 result = randomise_playlist(playlist, seed, start_current, true);
3590 return NULL;
3591 3059
3592 return buf; 3060 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3061 playlist->started)
3062 audio_flush_and_reload_tracks();
3063
3064 return result;
3593} 3065}
3594 3066
3595/* return size of buffer needed for playlist to initialize num_indices entries */ 3067/*
3596size_t playlist_get_required_bufsz(struct playlist_info* playlist, 3068 * Removes all tracks, from the playlist, leaving the presently playing
3597 bool include_namebuf, 3069 * track queued.
3598 int num_indices) 3070 */
3071int playlist_remove_all_tracks(struct playlist_info *playlist)
3599{ 3072{
3600 size_t namebuf = 0; 3073 int result;
3601 3074
3602 if (!playlist) 3075 if (playlist == NULL)
3603 playlist = &current_playlist; 3076 playlist = &current_playlist;
3604 3077
3605 size_t unit_size = sizeof (*playlist->indices); 3078 while (playlist->index > 0)
3606 #ifdef HAVE_DIRCACHE 3079 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
3607 unit_size += sizeof (*playlist->dcfrefs); 3080 return result;
3608 #endif
3609 if (include_namebuf)
3610 namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
3611 3081
3612 return (num_indices * unit_size) + namebuf; 3082 while (playlist->amount > 1)
3083 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
3084 return result;
3085
3086 if (playlist->amount == 1) {
3087 playlist->indices[0] |= PLAYLIST_QUEUED;
3088 }
3089
3090 return 0;
3613} 3091}
3614 3092
3615/* Fills info structure with information about track at specified index. 3093/*
3616 Returns 0 on success and -1 on failure */ 3094 * Restore the playlist state based on control file commands. Called to
3617int playlist_get_track_info(struct playlist_info* playlist, int index, 3095 * resume playback after shutdown.
3618 struct playlist_track_info* info) 3096 */
3097int playlist_resume(void)
3619{ 3098{
3620 int seek; 3099 struct playlist_info* playlist = &current_playlist;
3621 bool control_file; 3100 char *buffer;
3101 size_t buflen;
3102 int handle;
3103 int nread;
3104 int total_read = 0;
3105 int control_file_size = 0;
3106 bool first = true;
3107 bool sorted = true;
3108 int result = -1;
3622 3109
3623 if (!playlist) 3110 splash(0, ID2P(LANG_WAIT));
3624 playlist = &current_playlist; 3111 if (core_allocatable() < (1 << 10))
3112 talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
3625 3113
3626 if (index < 0 || index >= playlist->amount) 3114#ifdef HAVE_DIRCACHE
3115 dircache_wait(); /* we need the dircache to use the files in the playlist */
3116#endif
3117
3118 handle = alloc_tempbuf(&buflen);
3119 if (handle < 0)
3120 {
3121 splashf(HZ * 2, "%s(): OOM", __func__);
3627 return -1; 3122 return -1;
3123 }
3628 3124
3629 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; 3125 /* align buffer for faster load times */
3630 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; 3126 buffer = core_get_data(handle);
3127 STORAGE_ALIGN_BUFFER(buffer, buflen);
3128 buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
3631 3129
3632 if (get_filename(playlist, index, seek, control_file, info->filename, 3130 playlist_shutdown(); /* flush any cached control commands to disk */
3633 sizeof(info->filename)) < 0) 3131 empty_playlist(playlist, true);
3634 return -1;
3635 3132
3636 info->attr = 0; 3133 playlist->control_fd = open(playlist->control_filename, O_RDWR);
3134 if (playlist->control_fd < 0)
3135 {
3136 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3137 goto out;
3138 }
3139 playlist->control_created = true;
3637 3140
3638 if (control_file) 3141 control_file_size = filesize(playlist->control_fd);
3142 if (control_file_size <= 0)
3639 { 3143 {
3640 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK) 3144 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3641 info->attr |= PLAYLIST_ATTR_QUEUED; 3145 goto out;
3146 }
3147
3148 /* read a small amount first to get the header */
3149 nread = read(playlist->control_fd, buffer,
3150 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
3151 if(nread <= 0)
3152 {
3153 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3154 goto out;
3155 }
3156
3157 playlist->started = true;
3158
3159 while (1)
3160 {
3161 result = 0;
3162 int count;
3163 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
3164 int last_newline = 0;
3165 int str_count = -1;
3166 bool newline = true;
3167 bool exit_loop = false;
3168 char *p = buffer;
3169 char *str1 = NULL;
3170 char *str2 = NULL;
3171 char *str3 = NULL;
3172 unsigned long last_tick = current_tick;
3173 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
3174 bool useraborted = false;
3175
3176 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
3177 {
3178 /* Show a splash while we are loading. */
3179 splash_progress((total_read + count), control_file_size,
3180 "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
3181 if (TIME_AFTER(current_tick, last_tick + HZ/4))
3182 {
3183 if (action_userabort(TIMEOUT_NOBLOCK))
3184 {
3185 useraborted = true;
3186 break;
3187 }
3188 last_tick = current_tick;
3189 }
3190 /* Are we on a new line? */
3191 if((*p == '\n') || (*p == '\r'))
3192 {
3193 *p = '\0';
3194
3195 /* save last_newline in case we need to load more data */
3196 last_newline = count;
3197
3198 switch (current_command)
3199 {
3200 case PLAYLIST_COMMAND_PLAYLIST:
3201 {
3202 /* str1=version str2=dir str3=file */
3203 int version;
3204
3205 if (!str1)
3206 {
3207 result = -2;
3208 exit_loop = true;
3209 break;
3210 }
3211
3212 if (!str2)
3213 str2 = "";
3214
3215 if (!str3)
3216 str3 = "";
3217
3218 version = atoi(str1);
3219
3220 if (version != PLAYLIST_CONTROL_FILE_VERSION)
3221 {
3222 result = -3;
3223 goto out;
3224 }
3225
3226 update_playlist_filename(playlist, str2, str3);
3227
3228 if (str3[0] != '\0')
3229 {
3230 /* NOTE: add_indices_to_playlist() overwrites the
3231 audiobuf so we need to reload control file
3232 data */
3233 add_indices_to_playlist(playlist, buffer, buflen);
3234 }
3235 else if (str2[0] != '\0')
3236 {
3237 playlist->in_ram = true;
3238 resume_directory(str2);
3239 }
3240
3241 /* load the rest of the data */
3242 first = false;
3243 exit_loop = true;
3244
3245 break;
3246 }
3247 case PLAYLIST_COMMAND_ADD:
3248 case PLAYLIST_COMMAND_QUEUE:
3249 {
3250 /* str1=position str2=last_position str3=file */
3251 int position, last_position;
3252 bool queue;
3253
3254 if (!str1 || !str2 || !str3)
3255 {
3256 result = -4;
3257 exit_loop = true;
3258 break;
3259 }
3260
3261 position = atoi(str1);
3262 last_position = atoi(str2);
3263
3264 queue = (current_command == PLAYLIST_COMMAND_ADD)?
3265 false:true;
3266
3267 /* seek position is based on str3's position in
3268 buffer */
3269 if (add_track_to_playlist(playlist, str3, position,
3270 queue, total_read+(str3-buffer)) < 0)
3271 {
3272 result = -5;
3273 goto out;
3274 }
3275
3276 playlist->last_insert_pos = last_position;
3277
3278 break;
3279 }
3280 case PLAYLIST_COMMAND_DELETE:
3281 {
3282 /* str1=position */
3283 int position;
3284
3285 if (!str1)
3286 {
3287 result = -6;
3288 exit_loop = true;
3289 break;
3290 }
3291
3292 position = atoi(str1);
3293
3294 if (remove_track_from_playlist(playlist, position,
3295 false) < 0)
3296 {
3297 result = -7;
3298 goto out;
3299 }
3300
3301 break;
3302 }
3303 case PLAYLIST_COMMAND_SHUFFLE:
3304 {
3305 /* str1=seed str2=first_index */
3306 int seed;
3307
3308 if (!str1 || !str2)
3309 {
3310 result = -8;
3311 exit_loop = true;
3312 break;
3313 }
3314
3315 if (!sorted)
3316 {
3317 /* Always sort list before shuffling */
3318 sort_playlist(playlist, false, false);
3319 }
3320
3321 seed = atoi(str1);
3322 playlist->first_index = atoi(str2);
3323
3324 if (randomise_playlist(playlist, seed, false,
3325 false) < 0)
3326 {
3327 result = -9;
3328 goto out;
3329 }
3330 sorted = false;
3331 break;
3332 }
3333 case PLAYLIST_COMMAND_UNSHUFFLE:
3334 {
3335 /* str1=first_index */
3336 if (!str1)
3337 {
3338 result = -10;
3339 exit_loop = true;
3340 break;
3341 }
3342
3343 playlist->first_index = atoi(str1);
3344
3345 if (sort_playlist(playlist, false, false) < 0)
3346 {
3347 result = -11;
3348 goto out;
3349 }
3350
3351 sorted = true;
3352 break;
3353 }
3354 case PLAYLIST_COMMAND_RESET:
3355 {
3356 playlist->last_insert_pos = -1;
3357 break;
3358 }
3359 case PLAYLIST_COMMAND_COMMENT:
3360 default:
3361 break;
3362 }
3363
3364 newline = true;
3365
3366 /* to ignore any extra newlines */
3367 current_command = PLAYLIST_COMMAND_COMMENT;
3368 }
3369 else if(newline)
3370 {
3371 newline = false;
3372
3373 /* first non-comment line must always specify playlist */
3374 if (first && *p != 'P' && *p != '#')
3375 {
3376 result = -12;
3377 exit_loop = true;
3378 break;
3379 }
3380
3381 switch (*p)
3382 {
3383 case 'P':
3384 /* playlist can only be specified once */
3385 if (!first)
3386 {
3387 result = -13;
3388 exit_loop = true;
3389 break;
3390 }
3391
3392 current_command = PLAYLIST_COMMAND_PLAYLIST;
3393 break;
3394 case 'A':
3395 current_command = PLAYLIST_COMMAND_ADD;
3396 break;
3397 case 'Q':
3398 current_command = PLAYLIST_COMMAND_QUEUE;
3399 break;
3400 case 'D':
3401 current_command = PLAYLIST_COMMAND_DELETE;
3402 break;
3403 case 'S':
3404 current_command = PLAYLIST_COMMAND_SHUFFLE;
3405 break;
3406 case 'U':
3407 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
3408 break;
3409 case 'R':
3410 current_command = PLAYLIST_COMMAND_RESET;
3411 break;
3412 case '#':
3413 current_command = PLAYLIST_COMMAND_COMMENT;
3414 break;
3415 default:
3416 result = -14;
3417 exit_loop = true;
3418 break;
3419 }
3420
3421 str_count = -1;
3422 str1 = NULL;
3423 str2 = NULL;
3424 str3 = NULL;
3425 }
3426 else if(current_command != PLAYLIST_COMMAND_COMMENT)
3427 {
3428 /* all control file strings are separated with a colon.
3429 Replace the colon with 0 to get proper strings that can be
3430 used by commands above */
3431 if (*p == ':')
3432 {
3433 *p = '\0';
3434 str_count++;
3435
3436 if ((count+1) < nread)
3437 {
3438 switch (str_count)
3439 {
3440 case 0:
3441 str1 = p+1;
3442 break;
3443 case 1:
3444 str2 = p+1;
3445 break;
3446 case 2:
3447 str3 = p+1;
3448 break;
3449 default:
3450 /* allow last string to contain colons */
3451 *p = ':';
3452 break;
3453 }
3454 }
3455 }
3456 }
3457 }
3458
3459 if (result < 0)
3460 {
3461 splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
3462 goto out;
3463 }
3464
3465 if (useraborted)
3466 {
3467 splash(HZ*2, ID2P(LANG_CANCEL));
3468 result = -1;
3469 goto out;
3470 }
3471 if (!newline || (exit_loop && count<nread))
3472 {
3473 if ((total_read + count) >= control_file_size)
3474 {
3475 /* no newline at end of control file */
3476 splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
3477 result = -15;
3478 goto out;
3479 }
3480
3481 /* We didn't end on a newline or we exited loop prematurely.
3482 Either way, re-read the remainder. */
3483 count = last_newline;
3484 lseek(playlist->control_fd, total_read+count, SEEK_SET);
3485 }
3486
3487 total_read += count;
3488
3489 if (first)
3490 /* still looking for header */
3491 nread = read(playlist->control_fd, buffer,
3492 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
3642 else 3493 else
3643 info->attr |= PLAYLIST_ATTR_INSERTED; 3494 nread = read(playlist->control_fd, buffer, buflen);
3644 3495
3496 /* Terminate on EOF */
3497 if(nread <= 0)
3498 {
3499 break;
3500 }
3645 } 3501 }
3646 3502
3647 if (playlist->indices[index] & PLAYLIST_SKIPPED) 3503#ifdef HAVE_DIRCACHE
3648 info->attr |= PLAYLIST_ATTR_SKIPPED; 3504 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3649 3505#endif
3650 info->index = index;
3651 info->display_index = rotate_index(playlist, index) + 1;
3652 3506
3653 return 0; 3507out:
3508 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
3509 core_free(handle);
3510 return result;
3511}
3512
3513/* resume a playlist track with the given crc_32 of the track name. */
3514void playlist_resume_track(int start_index, unsigned int crc,
3515 unsigned long elapsed, unsigned long offset)
3516{
3517 int i;
3518 unsigned int tmp_crc;
3519 struct playlist_info* playlist = &current_playlist;
3520 tmp_crc = playlist_get_filename_crc32(playlist, start_index);
3521 if (tmp_crc == crc)
3522 {
3523 playlist_start(start_index, elapsed, offset);
3524 return;
3525 }
3526
3527 for (i = 0 ; i < playlist->amount; i++)
3528 {
3529 tmp_crc = playlist_get_filename_crc32(playlist, i);
3530 if (tmp_crc == crc)
3531 {
3532 playlist_start(i, elapsed, offset);
3533 return;
3534 }
3535 }
3536
3537 /* If we got here the file wasnt found, so start from the beginning */
3538 playlist_start(0, 0, 0);
3654} 3539}
3655 3540
3656/* save the current dynamic playlist to specified file. The 3541/* save the current dynamic playlist to specified file. The
@@ -3838,104 +3723,194 @@ reset_old_buffer:
3838} 3723}
3839 3724
3840/* 3725/*
3841 * Search specified directory for tracks and notify via callback. May be 3726 * Set the specified playlist as the current.
3842 * called recursively. 3727 * NOTE: You will get undefined behaviour if something is already playing so
3728 * remember to stop before calling this. Also, this call will
3729 * effectively close your playlist, making it unusable.
3843 */ 3730 */
3844int playlist_directory_tracksearch(const char* dirname, bool recurse, 3731int playlist_set_current(struct playlist_info* playlist)
3845 int (*callback)(char*, void*),
3846 void* context)
3847{ 3732{
3848 char buf[MAX_PATH+1]; 3733 if (!playlist || (check_control(playlist) < 0))
3849 int result = 0; 3734 return -1;
3850 int num_files = 0;
3851 int i;;
3852 struct tree_context* tc = tree_get_context();
3853 struct tree_cache* cache = &tc->cache;
3854 int old_dirfilter = *(tc->dirfilter);
3855 3735
3856 if (!callback) 3736 empty_playlist(&current_playlist, false);
3737
3738 strmemccpy(current_playlist.filename, playlist->filename,
3739 sizeof(current_playlist.filename));
3740
3741 current_playlist.utf8 = playlist->utf8;
3742 current_playlist.fd = playlist->fd;
3743
3744 close(playlist->control_fd);
3745 playlist->control_fd = -1;
3746 close(current_playlist.control_fd);
3747 current_playlist.control_fd = -1;
3748 remove(current_playlist.control_filename);
3749 current_playlist.control_created = false;
3750 if (rename(playlist->control_filename,
3751 current_playlist.control_filename) < 0)
3857 return -1; 3752 return -1;
3753 current_playlist.control_fd = open(current_playlist.control_filename,
3754 O_RDWR);
3755 if (current_playlist.control_fd < 0)
3756 return -1;
3757 current_playlist.control_created = true;
3858 3758
3859 /* use the tree browser dircache to load files */ 3759 current_playlist.dirlen = playlist->dirlen;
3860 *(tc->dirfilter) = SHOW_ALL;
3861 3760
3862 if (ft_load(tc, dirname) < 0) 3761 if (playlist->indices && playlist->indices != current_playlist.indices)
3863 { 3762 {
3864 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); 3763 memcpy((void*)current_playlist.indices, (void*)playlist->indices,
3865 *(tc->dirfilter) = old_dirfilter; 3764 playlist->max_playlist_size*sizeof(*playlist->indices));
3866 return -1; 3765#ifdef HAVE_DIRCACHE
3766 copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
3767 playlist->max_playlist_size);
3768#endif
3867 } 3769 }
3868 3770
3869 num_files = tc->filesindir; 3771 current_playlist.first_index = playlist->first_index;
3772 current_playlist.amount = playlist->amount;
3773 current_playlist.last_insert_pos = playlist->last_insert_pos;
3774 current_playlist.seed = playlist->seed;
3775 current_playlist.shuffle_modified = playlist->shuffle_modified;
3776 current_playlist.deleted = playlist->deleted;
3777 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
3870 3778
3871 /* we've overwritten the dircache so tree browser will need to be 3779 memcpy(current_playlist.control_cache, playlist->control_cache,
3872 reloaded */ 3780 sizeof(current_playlist.control_cache));
3873 reload_directory(); 3781 current_playlist.num_cached = playlist->num_cached;
3782 current_playlist.pending_control_sync = playlist->pending_control_sync;
3874 3783
3875 for (i=0; i<num_files; i++) 3784 return 0;
3785}
3786
3787/* set playlist->last_shuffle_start to playlist->amount for
3788 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
3789void playlist_set_last_shuffled_start(void)
3790{
3791 struct playlist_info* playlist = &current_playlist;
3792 playlist->last_shuffled_start = playlist->amount;
3793}
3794
3795/* shuffle newly created playlist using random seed. */
3796int playlist_shuffle(int random_seed, int start_index)
3797{
3798 struct playlist_info* playlist = &current_playlist;
3799
3800 bool start_current = false;
3801
3802 if (start_index >= 0 && global_settings.play_selected)
3876 { 3803 {
3877 /* user abort */ 3804 /* store the seek position before the shuffle */
3878 if (action_userabort(TIMEOUT_NOBLOCK)) 3805 playlist->index = playlist->first_index = start_index;
3879 { 3806 start_current = true;
3880 result = -1; 3807 }
3881 break;
3882 }
3883 3808
3884 struct entry *files = core_get_data(cache->entries_handle); 3809 randomise_playlist(playlist, random_seed, start_current, true);
3885 if (files[i].attr & ATTR_DIRECTORY)
3886 {
3887 if (recurse)
3888 {
3889 /* recursively add directories */
3890 if (path_append(buf, dirname, files[i].name, sizeof(buf))
3891 >= sizeof(buf))
3892 {
3893 continue;
3894 }
3895 3810
3896 result = playlist_directory_tracksearch(buf, recurse, 3811 return playlist->index;
3897 callback, context); 3812}
3898 if (result < 0)
3899 break;
3900 3813
3901 /* we now need to reload our current directory */ 3814/* Marks the index of the track to be skipped that is "steps" away from
3902 if(ft_load(tc, dirname) < 0) 3815 * current playing track.
3903 { 3816 */
3904 result = -1; 3817void playlist_skip_entry(struct playlist_info *playlist, int steps)
3905 break; 3818{
3906 } 3819 int index;
3907 3820
3908 num_files = tc->filesindir; 3821 if (playlist == NULL)
3909 if (!num_files) 3822 playlist = &current_playlist;
3910 {
3911 result = -1;
3912 break;
3913 }
3914 }
3915 else
3916 continue;
3917 }
3918 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3919 {
3920 if (path_append(buf, dirname, files[i].name, sizeof(buf))
3921 >= sizeof(buf))
3922 {
3923 continue;
3924 }
3925 3823
3926 if (callback(buf, context) != 0) 3824 /* need to account for already skipped tracks */
3927 { 3825 steps = calculate_step_count(playlist, steps);
3928 result = -1;
3929 break;
3930 }
3931 3826
3932 /* let the other threads work */ 3827 index = playlist->index + steps;
3933 yield(); 3828 if (index < 0)
3934 } 3829 index += playlist->amount;
3935 } 3830 else if (index >= playlist->amount)
3831 index -= playlist->amount;
3936 3832
3937 /* restore dirfilter */ 3833 playlist->indices[index] |= PLAYLIST_SKIPPED;
3938 *(tc->dirfilter) = old_dirfilter; 3834}
3835
3836/* sort currently playing playlist */
3837int playlist_sort(struct playlist_info* playlist, bool start_current)
3838{
3839 int result;
3840
3841 if (!playlist)
3842 playlist = &current_playlist;
3843
3844 check_control(playlist);
3845
3846 result = sort_playlist(playlist, start_current, true);
3847
3848 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3849 playlist->started)
3850 audio_flush_and_reload_tracks();
3939 3851
3940 return result; 3852 return result;
3941} 3853}
3854
3855/* start playing current playlist at specified index/offset */
3856void playlist_start(int start_index, unsigned long elapsed,
3857 unsigned long offset)
3858{
3859 struct playlist_info* playlist = &current_playlist;
3860
3861 playlist->index = start_index;
3862
3863 playlist->started = true;
3864 sync_control(playlist, false);
3865 audio_play(elapsed, offset);
3866 audio_resume();
3867}
3868
3869void playlist_sync(struct playlist_info* playlist)
3870{
3871 if (!playlist)
3872 playlist = &current_playlist;
3873
3874 sync_control(playlist, false);
3875 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3876 audio_flush_and_reload_tracks();
3877
3878#ifdef HAVE_DIRCACHE
3879 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3880#endif
3881}
3882
3883/* Update resume info for current playing song. Returns -1 on error. */
3884int playlist_update_resume_info(const struct mp3entry* id3)
3885{
3886 struct playlist_info* playlist = &current_playlist;
3887
3888 if (id3)
3889 {
3890 if (global_status.resume_index != playlist->index ||
3891 global_status.resume_elapsed != id3->elapsed ||
3892 global_status.resume_offset != id3->offset)
3893 {
3894 unsigned int crc = playlist_get_filename_crc32(playlist,
3895 playlist->index);
3896 global_status.resume_index = playlist->index;
3897 global_status.resume_crc32 = crc;
3898 global_status.resume_elapsed = id3->elapsed;
3899 global_status.resume_offset = id3->offset;
3900 status_save();
3901 }
3902 }
3903 else
3904 {
3905 global_status.resume_index = -1;
3906 global_status.resume_crc32 = -1;
3907 global_status.resume_elapsed = -1;
3908 global_status.resume_offset = -1;
3909 status_save();
3910 }
3911
3912 return 0;
3913}
3914
3915
3916
diff --git a/apps/playlist.h b/apps/playlist.h
index 2eca7355e4..0ecc7ccf77 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -74,22 +74,16 @@ struct playlist_control_cache {
74struct playlist_info 74struct playlist_info
75{ 75{
76 bool current; /* current playing playlist */ 76 bool current; /* current playing playlist */
77 char filename[MAX_PATH]; /* path name of m3u playlist on disk */
78 char control_filename[MAX_PATH]; /* full path of control file */
79 bool utf8; /* playlist is in .m3u8 format */ 77 bool utf8; /* playlist is in .m3u8 format */
78 bool control_created; /* has control file been created? */
79 bool in_ram; /* playlist stored in ram (dirplay) */
80 int fd; /* descriptor of the open playlist file */ 80 int fd; /* descriptor of the open playlist file */
81 int control_fd; /* descriptor of the open control file */ 81 int control_fd; /* descriptor of the open control file */
82 bool control_created; /* has control file been created? */
83 int dirlen; /* Length of the path to the playlist file */
84 volatile unsigned long *indices; /* array of indices */
85#ifdef HAVE_DIRCACHE
86 struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
87#endif
88 int max_playlist_size; /* Max number of files in playlist. Mirror of 82 int max_playlist_size; /* Max number of files in playlist. Mirror of
89 global_settings.max_files_in_playlist */ 83 global_settings.max_files_in_playlist */
90 bool in_ram; /* playlist stored in ram (dirplay) */ 84 int num_inserted_tracks; /* number of tracks inserted */
85 volatile unsigned long *indices; /* array of indices */
91 int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */ 86 int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
92
93 volatile char *buffer;/* buffer for in-ram playlists */ 87 volatile char *buffer;/* buffer for in-ram playlists */
94 int buffer_size; /* size of buffer */ 88 int buffer_size; /* size of buffer */
95 int buffer_end_pos; /* last position where buffer was written */ 89 int buffer_end_pos; /* last position where buffer was written */
@@ -97,22 +91,25 @@ struct playlist_info
97 int first_index; /* index of first song in playlist */ 91 int first_index; /* index of first song in playlist */
98 int amount; /* number of tracks in the index */ 92 int amount; /* number of tracks in the index */
99 int last_insert_pos; /* last position we inserted a track */ 93 int last_insert_pos; /* last position we inserted a track */
100 int seed; /* shuffle seed */
101 bool shuffle_modified; /* has playlist been shuffled with
102 inserted tracks? */
103 bool deleted; /* have any tracks been deleted? */ 94 bool deleted; /* have any tracks been deleted? */
104 int num_inserted_tracks; /* number of tracks inserted */
105 bool started; /* has playlist been started? */ 95 bool started; /* has playlist been started? */
106 96 bool pending_control_sync; /* control file needs to be synced */
97 bool shuffle_modified; /* has playlist been shuffled with
98 inserted tracks? */
99 int last_shuffled_start; /* number of tracks when insert last
100 shuffled command start */
101 int seed; /* shuffle seed */
107 /* cache of playlist control commands waiting to be flushed to 102 /* cache of playlist control commands waiting to be flushed to
108 to disk */ 103 to disk */
109 struct playlist_control_cache control_cache[PLAYLIST_MAX_CACHE]; 104 struct playlist_control_cache control_cache[PLAYLIST_MAX_CACHE];
110 int num_cached; /* number of cached entries */ 105 int num_cached; /* number of cached entries */
111 bool pending_control_sync; /* control file needs to be synced */ 106 struct mutex mutex; /* mutex for control file access */
112 107#ifdef HAVE_DIRCACHE
113 struct mutex *control_mutex; /* mutex for control file access */ 108 struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
114 int last_shuffled_start; /* number of tracks when insert last 109#endif
115 shuffled command start */ 110 int dirlen; /* Length of the path to the playlist file */
111 char filename[MAX_PATH]; /* path name of m3u playlist on disk */
112 char control_filename[MAX_PATH]; /* full path of control file */
116}; 113};
117 114
118struct playlist_track_info 115struct playlist_track_info