Rework Display Options and File View.
[Rockbox.git] / apps / playlist.c
blob7652e2924d58a7dfe52c59472645e7a75642ecf7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by wavey@wavey.org
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 Dynamic playlist design (based on design originally proposed by ricII)
23 There are two files associated with a dynamic playlist:
24 1. Playlist file : This file contains the initial songs in the playlist.
25 The file is created by the user and stored on the hard
26 drive. NOTE: If we are playing the contents of a
27 directory, there will be no playlist file.
28 2. Control file : This file is automatically created when a playlist is
29 started and contains all the commands done to it.
31 The first non-comment line in a control file must begin with
32 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
33 DIR is the directory where the playlist is located and FILE is the
34 playlist filename. For dirplay, FILE will be empty. An empty playlist
35 will have both entries as null.
37 Control file commands:
38 a. Add track (A:<position>:<last position>:<path to track>)
39 - Insert a track at the specified position in the current
40 playlist. Last position is used to specify where last insertion
41 occurred.
42 b. Queue track (Q:<position>:<last position>:<path to track>)
43 - Queue a track at the specified position in the current
44 playlist. Queued tracks differ from added tracks in that they
45 are deleted from the playlist as soon as they are played and
46 they are not saved to disk as part of the playlist.
47 c. Delete track (D:<position>)
48 - Delete track from specified position in the current playlist.
49 d. Shuffle playlist (S:<seed>:<index>)
50 - Shuffle entire playlist with specified seed. The index
51 identifies the first index in the newly shuffled playlist
52 (needed for repeat mode).
53 e. Unshuffle playlist (U:<index>)
54 - Unshuffle entire playlist. The index identifies the first index
55 in the newly unshuffled playlist.
56 f. Reset last insert position (R)
57 - Needed so that insertions work properly after resume
59 Resume:
60 The only resume info that needs to be saved is the current index in the
61 playlist and the position in the track. When resuming, all the commands
62 in the control file will be reapplied so that the playlist indices are
63 exactly the same as before shutdown. To avoid unnecessary disk
64 accesses, the shuffle mode settings are also saved in settings and only
65 flushed to disk when required.
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include "playlist.h"
72 #include "file.h"
73 #include "dir.h"
74 #include "sprintf.h"
75 #include "debug.h"
76 #include "audio.h"
77 #include "lcd.h"
78 #include "kernel.h"
79 #include "settings.h"
80 #include "status.h"
81 #include "applimits.h"
82 #include "screens.h"
83 #include "buffer.h"
84 #include "atoi.h"
85 #include "misc.h"
86 #include "button.h"
87 #include "filetree.h"
88 #include "abrepeat.h"
89 #include "dircache.h"
90 #include "thread.h"
91 #include "usb.h"
92 #ifdef HAVE_LCD_BITMAP
93 #include "icons.h"
94 #include "widgets.h"
95 #endif
97 #include "lang.h"
98 #include "talk.h"
99 #include "splash.h"
101 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
102 #define PLAYLIST_CONTROL_FILE_VERSION 2
105 Each playlist index has a flag associated with it which identifies what
106 type of track it is. These flags are stored in the 4 high order bits of
107 the index.
109 NOTE: This limits the playlist file size to a max of 256M.
111 Bits 31-30:
112 00 = Playlist track
113 01 = Track was prepended into playlist
114 10 = Track was inserted into playlist
115 11 = Track was appended into playlist
116 Bit 29:
117 0 = Added track
118 1 = Queued track
119 Bit 28:
120 0 = Track entry is valid
121 1 = Track does not exist on disk and should be skipped
123 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
124 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
125 #define PLAYLIST_QUEUE_MASK 0x20000000
127 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
128 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
129 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
131 #define PLAYLIST_QUEUED 0x20000000
132 #define PLAYLIST_SKIPPED 0x10000000
134 #define PLAYLIST_DISPLAY_COUNT 10
136 static bool changing_dir = false;
138 static struct playlist_info current_playlist;
139 static char now_playing[MAX_PATH+1];
141 static void empty_playlist(struct playlist_info* playlist, bool resume);
142 static void new_playlist(struct playlist_info* playlist, const char *dir,
143 const char *file);
144 static void create_control(struct playlist_info* playlist);
145 static int check_control(struct playlist_info* playlist);
146 static int recreate_control(struct playlist_info* playlist);
147 static void update_playlist_filename(struct playlist_info* playlist,
148 const char *dir, const char *file);
149 static int add_indices_to_playlist(struct playlist_info* playlist,
150 char* buffer, int buflen);
151 static int add_track_to_playlist(struct playlist_info* playlist,
152 const char *filename, int position,
153 bool queue, int seek_pos);
154 static int add_directory_to_playlist(struct playlist_info* playlist,
155 const char *dirname, int *position,
156 bool queue, int *count, bool recurse);
157 static int remove_track_from_playlist(struct playlist_info* playlist,
158 int position, bool write);
159 static int randomise_playlist(struct playlist_info* playlist,
160 unsigned int seed, bool start_current,
161 bool write);
162 static int sort_playlist(struct playlist_info* playlist, bool start_current,
163 bool write);
164 static int get_next_index(const struct playlist_info* playlist, int steps,
165 int repeat_mode);
166 static void find_and_set_playlist_index(struct playlist_info* playlist,
167 unsigned int seek);
168 static int compare(const void* p1, const void* p2);
169 static int get_filename(struct playlist_info* playlist, int index, int seek,
170 bool control_file, char *buf, int buf_length);
171 static int get_next_directory(char *dir);
172 static int get_next_dir(char *dir, bool is_forward, bool recursion);
173 static int get_previous_directory(char *dir);
174 static int check_subdir_for_music(char *dir, char *subdir);
175 static int format_track_path(char *dest, char *src, int buf_length, int max,
176 char *dir);
177 static void display_playlist_count(int count, const unsigned char *fmt);
178 static void display_buffer_full(void);
179 static int flush_cached_control(struct playlist_info* playlist);
180 static int update_control(struct playlist_info* playlist,
181 enum playlist_command command, int i1, int i2,
182 const char* s1, const char* s2, void* data);
183 static void sync_control(struct playlist_info* playlist, bool force);
184 static int rotate_index(const struct playlist_info* playlist, int index);
186 #ifdef HAVE_DIRCACHE
187 #define PLAYLIST_LOAD_POINTERS 1
189 static struct event_queue playlist_queue;
190 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x400)/sizeof(long)];
191 static const char playlist_thread_name[] = "playlist cachectrl";
192 #endif
195 * remove any files and indices associated with the playlist
197 static void empty_playlist(struct playlist_info* playlist, bool resume)
199 playlist->filename[0] = '\0';
201 if(playlist->fd >= 0)
202 /* If there is an already open playlist, close it. */
203 close(playlist->fd);
204 playlist->fd = -1;
206 if(playlist->control_fd >= 0)
207 close(playlist->control_fd);
208 playlist->control_fd = -1;
209 playlist->control_created = false;
211 playlist->in_ram = false;
213 if (playlist->buffer)
214 playlist->buffer[0] = 0;
216 playlist->buffer_end_pos = 0;
218 playlist->index = 0;
219 playlist->first_index = 0;
220 playlist->amount = 0;
221 playlist->last_insert_pos = -1;
222 playlist->seed = 0;
223 playlist->shuffle_modified = false;
224 playlist->deleted = false;
225 playlist->num_inserted_tracks = 0;
227 playlist->num_cached = 0;
228 playlist->pending_control_sync = false;
230 if (!resume && playlist->current)
232 /* start with fresh playlist control file when starting new
233 playlist */
234 create_control(playlist);
236 /* Reset resume settings */
237 global_settings.resume_first_index = 0;
238 global_settings.resume_seed = -1;
243 * Initialize a new playlist for viewing/editing/playing. dir is the
244 * directory where the playlist is located and file is the filename.
246 static void new_playlist(struct playlist_info* playlist, const char *dir,
247 const char *file)
249 empty_playlist(playlist, false);
251 if (!file)
253 file = "";
255 if (dir && playlist->current) /* !current cannot be in_ram */
256 playlist->in_ram = true;
257 else
258 dir = ""; /* empty playlist */
261 update_playlist_filename(playlist, dir, file);
263 if (playlist->control_fd >= 0)
265 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
266 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
267 sync_control(playlist, false);
272 * create control file for playlist
274 static void create_control(struct playlist_info* playlist)
276 playlist->control_fd = open(playlist->control_filename,
277 O_CREAT|O_RDWR|O_TRUNC);
278 if (playlist->control_fd < 0)
280 if (check_rockboxdir())
282 gui_syncsplash(HZ*2, true, (unsigned char *)"%s (%d)",
283 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
284 playlist->control_fd);
286 playlist->control_created = false;
288 else
290 playlist->control_created = true;
295 * validate the control file. This may include creating/initializing it if
296 * necessary;
298 static int check_control(struct playlist_info* playlist)
300 if (!playlist->control_created)
302 create_control(playlist);
304 if (playlist->control_fd >= 0)
306 char* dir = playlist->filename;
307 char* file = playlist->filename+playlist->dirlen;
308 char c = playlist->filename[playlist->dirlen-1];
310 playlist->filename[playlist->dirlen-1] = '\0';
312 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
313 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
314 sync_control(playlist, false);
315 playlist->filename[playlist->dirlen-1] = c;
319 if (playlist->control_fd < 0)
320 return -1;
322 return 0;
326 * recreate the control file based on current playlist entries
328 static int recreate_control(struct playlist_info* playlist)
330 char temp_file[MAX_PATH+1];
331 int temp_fd = -1;
332 int i;
333 int result = 0;
335 if(playlist->control_fd >= 0)
337 char* dir = playlist->filename;
338 char* file = playlist->filename+playlist->dirlen;
339 char c = playlist->filename[playlist->dirlen-1];
341 close(playlist->control_fd);
343 snprintf(temp_file, sizeof(temp_file), "%s_temp",
344 playlist->control_filename);
346 if (rename(playlist->control_filename, temp_file) < 0)
347 return -1;
349 temp_fd = open(temp_file, O_RDONLY);
350 if (temp_fd < 0)
351 return -1;
353 playlist->control_fd = open(playlist->control_filename,
354 O_CREAT|O_RDWR|O_TRUNC);
355 if (playlist->control_fd < 0)
356 return -1;
358 playlist->filename[playlist->dirlen-1] = '\0';
360 /* cannot call update_control() because of mutex */
361 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
362 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
364 playlist->filename[playlist->dirlen-1] = c;
366 if (result < 0)
368 close(temp_fd);
369 return result;
373 playlist->seed = 0;
374 playlist->shuffle_modified = false;
375 playlist->deleted = false;
376 playlist->num_inserted_tracks = 0;
378 if (playlist->current)
380 global_settings.resume_seed = -1;
381 settings_save();
384 for (i=0; i<playlist->amount; i++)
386 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
388 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
389 char inserted_file[MAX_PATH+1];
391 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
392 SEEK_SET);
393 read_line(temp_fd, inserted_file, sizeof(inserted_file));
395 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
396 queue?'Q':'A', i, playlist->last_insert_pos);
397 if (result > 0)
399 /* save the position in file where name is written */
400 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
402 result = fdprintf(playlist->control_fd, "%s\n",
403 inserted_file);
405 playlist->indices[i] =
406 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
409 if (result < 0)
410 break;
412 playlist->num_inserted_tracks++;
416 close(temp_fd);
417 remove(temp_file);
418 fsync(playlist->control_fd);
420 if (result < 0)
421 return result;
423 return 0;
427 * store directory and name of playlist file
429 static void update_playlist_filename(struct playlist_info* playlist,
430 const char *dir, const char *file)
432 char *sep="";
433 int dirlen = strlen(dir);
435 /* If the dir does not end in trailing slash, we use a separator.
436 Otherwise we don't. */
437 if('/' != dir[dirlen-1])
439 sep="/";
440 dirlen++;
443 playlist->dirlen = dirlen;
445 snprintf(playlist->filename, sizeof(playlist->filename),
446 "%s%s%s", dir, sep, file);
450 * calculate track offsets within a playlist file
452 static int add_indices_to_playlist(struct playlist_info* playlist,
453 char* buffer, int buflen)
455 unsigned int nread;
456 unsigned int i = 0;
457 unsigned int count = 0;
458 bool store_index;
459 unsigned char *p;
460 int result = 0;
462 if(-1 == playlist->fd)
463 playlist->fd = open(playlist->filename, O_RDONLY);
464 if(playlist->fd < 0)
465 return -1; /* failure */
467 #ifdef HAVE_LCD_BITMAP
468 if(global_settings.statusbar)
469 lcd_setmargins(0, STATUSBAR_HEIGHT);
470 else
471 lcd_setmargins(0, 0);
472 #endif
474 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD));
476 if (!buffer)
478 /* use mp3 buffer for maximum load speed */
479 audio_stop();
480 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
482 buffer = (char *)audiobuf;
483 buflen = (audiobufend - audiobuf);
486 store_index = true;
488 while(1)
490 nread = read(playlist->fd, buffer, buflen);
491 /* Terminate on EOF */
492 if(nread <= 0)
493 break;
495 p = (unsigned char *)buffer;
497 for(count=0; count < nread; count++,p++) {
499 /* Are we on a new line? */
500 if((*p == '\n') || (*p == '\r'))
502 store_index = true;
504 else if(store_index)
506 store_index = false;
508 if(*p != '#')
510 if ( playlist->amount >= playlist->max_playlist_size ) {
511 display_buffer_full();
512 result = -1;
513 goto exit;
516 /* Store a new entry */
517 playlist->indices[ playlist->amount ] = i+count;
518 #ifdef HAVE_DIRCACHE
519 if (playlist->filenames)
520 playlist->filenames[ playlist->amount ] = NULL;
521 #endif
522 playlist->amount++;
527 i+= count;
530 exit:
531 #ifdef HAVE_DIRCACHE
532 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
533 #endif
535 return result;
539 * Add track to playlist at specified position. There are five special
540 * positions that can be specified:
541 * PLAYLIST_PREPEND - Add track at beginning of playlist
542 * PLAYLIST_INSERT - Add track after current song. NOTE: If
543 * there are already inserted tracks then track
544 * is added to the end of the insertion list
545 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
546 * matter what other tracks have been inserted
547 * PLAYLIST_INSERT_LAST - Add track to end of playlist
548 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
549 * current playing track and end of playlist
551 static int add_track_to_playlist(struct playlist_info* playlist,
552 const char *filename, int position,
553 bool queue, int seek_pos)
555 int insert_position = position;
556 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
557 int i;
559 if (playlist->amount >= playlist->max_playlist_size)
561 display_buffer_full();
562 return -1;
565 switch (position)
567 case PLAYLIST_PREPEND:
568 insert_position = playlist->first_index;
569 flags = PLAYLIST_INSERT_TYPE_PREPEND;
570 break;
571 case PLAYLIST_INSERT:
572 /* if there are already inserted tracks then add track to end of
573 insertion list else add after current playing track */
574 if (playlist->last_insert_pos >= 0 &&
575 playlist->last_insert_pos < playlist->amount &&
576 (playlist->indices[playlist->last_insert_pos]&
577 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
578 position = insert_position = playlist->last_insert_pos+1;
579 else if (playlist->amount > 0)
580 position = insert_position = playlist->index + 1;
581 else
582 position = insert_position = 0;
584 playlist->last_insert_pos = position;
585 break;
586 case PLAYLIST_INSERT_FIRST:
587 if (playlist->amount > 0)
588 position = insert_position = playlist->index + 1;
589 else
590 position = insert_position = 0;
592 if (playlist->last_insert_pos < 0)
593 playlist->last_insert_pos = position;
594 break;
595 case PLAYLIST_INSERT_LAST:
596 if (playlist->first_index > 0)
597 insert_position = playlist->first_index;
598 else
599 insert_position = playlist->amount;
601 flags = PLAYLIST_INSERT_TYPE_APPEND;
602 break;
603 case PLAYLIST_INSERT_SHUFFLED:
605 int offset;
606 int n = playlist->amount -
607 rotate_index(playlist, playlist->index);
609 if (n > 0)
610 offset = rand() % n;
611 else
612 offset = 0;
614 position = playlist->index + offset + 1;
615 if (position >= playlist->amount)
616 position -= playlist->amount;
618 insert_position = position;
619 break;
623 if (queue)
624 flags |= PLAYLIST_QUEUED;
626 /* shift indices so that track can be added */
627 for (i=playlist->amount; i>insert_position; i--)
629 playlist->indices[i] = playlist->indices[i-1];
630 #ifdef HAVE_DIRCACHE
631 if (playlist->filenames)
632 playlist->filenames[i] = playlist->filenames[i-1];
633 #endif
636 /* update stored indices if needed */
637 if (playlist->amount > 0 && insert_position <= playlist->index)
638 playlist->index++;
640 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
641 position != PLAYLIST_PREPEND)
643 playlist->first_index++;
645 if (seek_pos < 0 && playlist->current)
647 global_settings.resume_first_index = playlist->first_index;
648 settings_save();
652 if (insert_position < playlist->last_insert_pos ||
653 (insert_position == playlist->last_insert_pos && position < 0))
654 playlist->last_insert_pos++;
656 if (seek_pos < 0 && playlist->control_fd >= 0)
658 int result = update_control(playlist,
659 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
660 playlist->last_insert_pos, filename, NULL, &seek_pos);
662 if (result < 0)
663 return result;
666 playlist->indices[insert_position] = flags | seek_pos;
668 #ifdef HAVE_DIRCACHE
669 if (playlist->filenames)
670 playlist->filenames[insert_position] = NULL;
671 #endif
673 playlist->amount++;
674 playlist->num_inserted_tracks++;
676 return insert_position;
680 * Insert directory into playlist. May be called recursively.
682 static int add_directory_to_playlist(struct playlist_info* playlist,
683 const char *dirname, int *position,
684 bool queue, int *count, bool recurse)
686 char buf[MAX_PATH+1];
687 unsigned char *count_str;
688 int result = 0;
689 int num_files = 0;
690 int i;
691 int dirfilter = global_settings.dirfilter;
692 struct entry *files;
693 struct tree_context* tc = tree_get_context();
695 /* use the tree browser dircache to load files */
696 global_settings.dirfilter = SHOW_ALL;
698 if (ft_load(tc, dirname) < 0)
700 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
701 global_settings.dirfilter = dirfilter;
702 return -1;
705 files = (struct entry*) tc->dircache;
706 num_files = tc->filesindir;
708 /* we've overwritten the dircache so tree browser will need to be
709 reloaded */
710 reload_directory();
712 if (queue)
713 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
714 else
715 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
717 for (i=0; i<num_files; i++)
719 /* user abort */
720 if (button_get(false) == SETTINGS_CANCEL)
722 result = -1;
723 break;
726 if (files[i].attr & ATTR_DIRECTORY)
728 if (recurse)
730 /* recursively add directories */
731 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
732 result = add_directory_to_playlist(playlist, buf, position,
733 queue, count, recurse);
734 if (result < 0)
735 break;
737 /* we now need to reload our current directory */
738 if(ft_load(tc, dirname) < 0)
740 result = -1;
741 break;
744 files = (struct entry*) tc->dircache;
745 num_files = tc->filesindir;
746 if (!num_files)
748 result = -1;
749 break;
752 else
753 continue;
755 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
757 int insert_pos;
759 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
761 insert_pos = add_track_to_playlist(playlist, buf, *position,
762 queue, -1);
763 if (insert_pos < 0)
765 result = -1;
766 break;
769 (*count)++;
771 /* Make sure tracks are inserted in correct order if user requests
772 INSERT_FIRST */
773 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
774 *position = insert_pos + 1;
776 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
778 display_playlist_count(*count, count_str);
780 if (*count == PLAYLIST_DISPLAY_COUNT &&
781 (audio_status() & AUDIO_STATUS_PLAY))
782 audio_flush_and_reload_tracks();
785 /* let the other threads work */
786 yield();
790 /* restore dirfilter */
791 global_settings.dirfilter = dirfilter;
793 return result;
797 * remove track at specified position
799 static int remove_track_from_playlist(struct playlist_info* playlist,
800 int position, bool write)
802 int i;
803 bool inserted;
805 if (playlist->amount <= 0)
806 return -1;
808 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
810 /* shift indices now that track has been removed */
811 for (i=position; i<playlist->amount; i++)
813 playlist->indices[i] = playlist->indices[i+1];
814 #ifdef HAVE_DIRCACHE
815 if (playlist->filenames)
816 playlist->filenames[i] = playlist->filenames[i+1];
817 #endif
820 playlist->amount--;
822 if (inserted)
823 playlist->num_inserted_tracks--;
824 else
825 playlist->deleted = true;
827 /* update stored indices if needed */
828 if (position < playlist->index)
829 playlist->index--;
831 if (position < playlist->first_index)
833 playlist->first_index--;
835 if (write)
837 global_settings.resume_first_index = playlist->first_index;
838 settings_save();
842 if (position <= playlist->last_insert_pos)
843 playlist->last_insert_pos--;
845 if (write && playlist->control_fd >= 0)
847 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
848 position, -1, NULL, NULL, NULL);
850 if (result < 0)
851 return result;
853 sync_control(playlist, false);
856 return 0;
860 * randomly rearrange the array of indices for the playlist. If start_current
861 * is true then update the index to the new index of the current playing track
863 static int randomise_playlist(struct playlist_info* playlist,
864 unsigned int seed, bool start_current,
865 bool write)
867 int count;
868 int candidate;
869 long store;
870 unsigned int current = playlist->indices[playlist->index];
872 /* seed 0 is used to identify sorted playlist for resume purposes */
873 if (seed == 0)
874 seed = 1;
876 /* seed with the given seed */
877 srand(seed);
879 /* randomise entire indices list */
880 for(count = playlist->amount - 1; count >= 0; count--)
882 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
883 candidate = rand() % (count + 1);
885 /* now swap the values at the 'count' and 'candidate' positions */
886 store = playlist->indices[candidate];
887 playlist->indices[candidate] = playlist->indices[count];
888 playlist->indices[count] = store;
889 #ifdef HAVE_DIRCACHE
890 if (playlist->filenames)
892 store = (long)playlist->filenames[candidate];
893 playlist->filenames[candidate] = playlist->filenames[count];
894 playlist->filenames[count] = (struct dircache_entry *)store;
896 #endif
899 if (start_current)
900 find_and_set_playlist_index(playlist, current);
902 /* indices have been moved so last insert position is no longer valid */
903 playlist->last_insert_pos = -1;
905 playlist->seed = seed;
906 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
907 playlist->shuffle_modified = true;
909 if (write)
911 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
912 playlist->first_index, NULL, NULL, NULL);
913 global_settings.resume_seed = seed;
914 settings_save();
917 return 0;
921 * Sort the array of indices for the playlist. If start_current is true then
922 * set the index to the new index of the current song.
924 static int sort_playlist(struct playlist_info* playlist, bool start_current,
925 bool write)
927 unsigned int current = playlist->indices[playlist->index];
929 if (playlist->amount > 0)
930 qsort(playlist->indices, playlist->amount,
931 sizeof(playlist->indices[0]), compare);
933 #ifdef HAVE_DIRCACHE
934 /** We need to re-check the song names from disk because qsort can't
935 * sort two arrays at once :/
936 * FIXME: Please implement a better way to do this. */
937 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
938 #endif
940 if (start_current)
941 find_and_set_playlist_index(playlist, current);
943 /* indices have been moved so last insert position is no longer valid */
944 playlist->last_insert_pos = -1;
946 if (!playlist->num_inserted_tracks && !playlist->deleted)
947 playlist->shuffle_modified = false;
948 if (write && playlist->control_fd >= 0)
950 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
951 playlist->first_index, -1, NULL, NULL, NULL);
952 global_settings.resume_seed = 0;
953 settings_save();
956 return 0;
959 /* Calculate how many steps we have to really step when skipping entries
960 * marked as bad.
962 static int calculate_step_count(const struct playlist_info *playlist, int steps)
964 int i, count, direction;
965 int index;
966 int stepped_count = 0;
968 if (steps < 0)
970 direction = -1;
971 count = -steps;
973 else
975 direction = 1;
976 count = steps;
979 index = playlist->index;
980 i = 0;
981 do {
982 /* Boundary check */
983 if (index < 0)
984 index += playlist->amount;
985 if (index >= playlist->amount)
986 index -= playlist->amount;
988 /* Check if we found a bad entry. */
989 if (playlist->indices[index] & PLAYLIST_SKIPPED)
991 steps += direction;
992 /* Are all entries bad? */
993 if (stepped_count++ > playlist->amount)
994 break ;
996 else
997 i++;
999 index += direction;
1000 } while (i <= count);
1002 return steps;
1005 /* Marks the index of the track to be skipped that is "steps" away from
1006 * current playing track.
1008 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1010 int index;
1012 if (playlist == NULL)
1013 playlist = &current_playlist;
1015 index = rotate_index(playlist, playlist->index);
1016 /* We should also skip already skipped entries before the entry to be skipepd. */
1017 index += calculate_step_count(playlist, steps);
1018 if (index < 0 || index >= playlist->amount)
1019 return ;
1021 index = (index+playlist->first_index) % playlist->amount;
1022 playlist->indices[index] |= PLAYLIST_SKIPPED;
1026 * returns the index of the track that is "steps" away from current playing
1027 * track.
1029 static int get_next_index(const struct playlist_info* playlist, int steps,
1030 int repeat_mode)
1032 int current_index = playlist->index;
1033 int next_index = -1;
1035 if (playlist->amount <= 0)
1036 return -1;
1038 if (repeat_mode == -1)
1039 repeat_mode = global_settings.repeat_mode;
1041 if (repeat_mode == REPEAT_SHUFFLE &&
1042 (!global_settings.playlist_shuffle || playlist->amount <= 1))
1043 repeat_mode = REPEAT_ALL;
1045 steps = calculate_step_count(playlist, steps);
1046 switch (repeat_mode)
1048 case REPEAT_SHUFFLE:
1049 /* Treat repeat shuffle just like repeat off. At end of playlist,
1050 play will be resumed in playlist_next() */
1051 case REPEAT_OFF:
1053 current_index = rotate_index(playlist, current_index);
1054 next_index = current_index+steps;
1055 if ((next_index < 0) || (next_index >= playlist->amount))
1056 next_index = -1;
1057 else
1058 next_index = (next_index+playlist->first_index) %
1059 playlist->amount;
1061 break;
1064 case REPEAT_ONE:
1065 #if (AB_REPEAT_ENABLE == 1)
1066 case REPEAT_AB:
1067 #endif
1068 next_index = current_index;
1069 break;
1071 case REPEAT_ALL:
1072 default:
1074 next_index = (current_index+steps) % playlist->amount;
1075 while (next_index < 0)
1076 next_index += playlist->amount;
1078 if (steps >= playlist->amount)
1080 int i, index;
1082 index = next_index;
1083 next_index = -1;
1085 /* second time around so skip the queued files */
1086 for (i=0; i<playlist->amount; i++)
1088 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1089 index = (index+1) % playlist->amount;
1090 else
1092 next_index = index;
1093 break;
1097 break;
1101 /* No luck if the whole playlist was bad. */
1102 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1103 return -1;
1105 return next_index;
1109 * Search for the seek track and set appropriate indices. Used after shuffle
1110 * to make sure the current index is still pointing to correct track.
1112 static void find_and_set_playlist_index(struct playlist_info* playlist,
1113 unsigned int seek)
1115 int i;
1117 /* Set the index to the current song */
1118 for (i=0; i<playlist->amount; i++)
1120 if (playlist->indices[i] == seek)
1122 playlist->index = playlist->first_index = i;
1124 if (playlist->current)
1126 global_settings.resume_first_index = i;
1127 settings_save();
1130 break;
1136 * used to sort track indices. Sort order is as follows:
1137 * 1. Prepended tracks (in prepend order)
1138 * 2. Playlist/directory tracks (in playlist order)
1139 * 3. Inserted/Appended tracks (in insert order)
1141 static int compare(const void* p1, const void* p2)
1143 unsigned long* e1 = (unsigned long*) p1;
1144 unsigned long* e2 = (unsigned long*) p2;
1145 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1146 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1148 if (flags1 == flags2)
1149 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1150 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1151 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1152 return -1;
1153 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1154 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1155 return 1;
1156 else if (flags1 && flags2)
1157 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1158 else
1159 return *e1 - *e2;
1162 #ifdef HAVE_DIRCACHE
1164 * Thread to update filename pointers to dircache on background
1165 * without affecting playlist load up performance. This thread also flushes
1166 * any pending control commands when the disk spins up.
1168 static void playlist_thread(void)
1170 struct event ev;
1171 bool dirty_pointers = false;
1172 static char tmp[MAX_PATH+1];
1174 struct playlist_info *playlist;
1175 int index;
1176 int seek;
1177 bool control_file;
1179 int sleep_time = 5;
1181 if (global_settings.disk_spindown > 1 &&
1182 global_settings.disk_spindown <= 5)
1183 sleep_time = global_settings.disk_spindown - 1;
1185 while (1)
1187 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1189 switch (ev.id)
1191 case PLAYLIST_LOAD_POINTERS:
1192 dirty_pointers = true;
1193 break ;
1195 /* Start the background scanning after either the disk spindown
1196 timeout or 5s, whichever is less */
1197 case SYS_TIMEOUT:
1198 playlist = &current_playlist;
1200 if (playlist->control_fd >= 0
1201 # ifndef SIMULATOR
1202 && ata_disk_is_active()
1203 # endif
1206 if (playlist->num_cached > 0)
1207 flush_cached_control(playlist);
1209 sync_control(playlist, true);
1212 if (!dirty_pointers)
1213 break ;
1215 if (!dircache_is_enabled() || !playlist->filenames
1216 || playlist->amount <= 0)
1217 break ;
1219 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1220 cpu_boost(true);
1221 #endif
1222 for (index = 0; index < playlist->amount
1223 && queue_empty(&playlist_queue); index++)
1225 /* Process only pointers that are not already loaded. */
1226 if (playlist->filenames[index])
1227 continue ;
1229 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1230 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1232 /* Load the filename from playlist file. */
1233 if (get_filename(playlist, index, seek, control_file, tmp,
1234 sizeof(tmp)) < 0)
1235 break ;
1237 /* Set the dircache entry pointer. */
1238 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1240 /* And be on background so user doesn't notice any delays. */
1241 yield();
1244 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1245 cpu_boost(false);
1246 #endif
1247 dirty_pointers = false;
1248 break ;
1250 #ifndef SIMULATOR
1251 case SYS_USB_CONNECTED:
1252 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1253 usb_wait_for_disconnect(&playlist_queue);
1254 break ;
1255 #endif
1259 #endif
1262 * gets pathname for track at seek index
1264 static int get_filename(struct playlist_info* playlist, int index, int seek,
1265 bool control_file, char *buf, int buf_length)
1267 int fd;
1268 int max = -1;
1269 char tmp_buf[MAX_PATH+1];
1270 char dir_buf[MAX_PATH+1];
1272 if (buf_length > MAX_PATH+1)
1273 buf_length = MAX_PATH+1;
1275 #ifdef HAVE_DIRCACHE
1276 if (dircache_is_enabled() && playlist->filenames)
1278 if (playlist->filenames[index] != NULL)
1280 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1281 max = strlen(tmp_buf) + 1;
1284 #else
1285 (void)index;
1286 #endif
1288 if (playlist->in_ram && !control_file && max < 0)
1290 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1291 tmp_buf[MAX_PATH] = '\0';
1292 max = strlen(tmp_buf) + 1;
1294 else if (max < 0)
1296 mutex_lock(&playlist->control_mutex);
1298 if (control_file)
1299 fd = playlist->control_fd;
1300 else
1302 if(-1 == playlist->fd)
1303 playlist->fd = open(playlist->filename, O_RDONLY);
1305 fd = playlist->fd;
1308 if(-1 != fd)
1311 if (lseek(fd, seek, SEEK_SET) != seek)
1312 max = -1;
1313 else
1314 max = read(fd, tmp_buf, buf_length);
1317 mutex_unlock(&playlist->control_mutex);
1319 if (max < 0)
1321 if (control_file)
1322 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1323 else
1324 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1326 return max;
1330 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1331 dir_buf[playlist->dirlen-1] = 0;
1333 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1336 static int get_next_directory(char *dir){
1337 return get_next_dir(dir,true,false);
1340 static int get_previous_directory(char *dir){
1341 return get_next_dir(dir,false,false);
1345 * search through all the directories (starting with the current) to find
1346 * one that has tracks to play
1348 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1350 struct playlist_info* playlist = &current_playlist;
1351 int result = -1;
1352 int dirfilter = global_settings.dirfilter;
1353 int sort_dir = global_settings.sort_dir;
1354 char *start_dir = NULL;
1355 bool exit = false;
1356 struct tree_context* tc = tree_get_context();
1358 if (recursion){
1359 /* start with root */
1360 dir[0] = '\0';
1362 else{
1363 /* start with current directory */
1364 strncpy(dir, playlist->filename, playlist->dirlen-1);
1365 dir[playlist->dirlen-1] = '\0';
1368 /* use the tree browser dircache to load files */
1369 global_settings.dirfilter = SHOW_ALL;
1371 /* sort in another direction if previous dir is requested */
1372 if(!is_forward){
1373 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1374 global_settings.sort_dir = 4;
1375 else if (global_settings.sort_dir == 1)
1376 global_settings.sort_dir = 2;
1377 else if (global_settings.sort_dir == 2)
1378 global_settings.sort_dir = 1;
1379 else if (global_settings.sort_dir == 4)
1380 global_settings.sort_dir = 0;
1383 while (!exit)
1385 struct entry *files;
1386 int num_files = 0;
1387 int i;
1389 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1391 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1392 exit = true;
1393 result = -1;
1394 break;
1397 files = (struct entry*) tc->dircache;
1398 num_files = tc->filesindir;
1400 for (i=0; i<num_files; i++)
1402 /* user abort */
1403 if (button_get(false) == SETTINGS_CANCEL)
1405 result = -1;
1406 exit = true;
1407 break;
1410 if (files[i].attr & ATTR_DIRECTORY)
1412 if (!start_dir)
1414 result = check_subdir_for_music(dir, files[i].name);
1415 if (result != -1)
1417 exit = true;
1418 break;
1421 else if (!strcmp(start_dir, files[i].name))
1422 start_dir = NULL;
1426 if (!exit)
1428 /* move down to parent directory. current directory name is
1429 stored as the starting point for the search in parent */
1430 start_dir = strrchr(dir, '/');
1431 if (start_dir)
1433 *start_dir = '\0';
1434 start_dir++;
1436 else
1437 break;
1441 /* we've overwritten the dircache so tree browser will need to be
1442 reloaded */
1443 reload_directory();
1445 /* restore dirfilter & sort_dir */
1446 global_settings.dirfilter = dirfilter;
1447 global_settings.sort_dir = sort_dir;
1449 /* special case if nothing found: try start searching again from root */
1450 if (result == -1 && !recursion){
1451 result = get_next_dir(dir,is_forward, true);
1454 return result;
1458 * Checks if there are any music files in the dir or any of its
1459 * subdirectories. May be called recursively.
1461 static int check_subdir_for_music(char *dir, char *subdir)
1463 int result = -1;
1464 int dirlen = strlen(dir);
1465 int num_files = 0;
1466 int i;
1467 struct entry *files;
1468 bool has_music = false;
1469 bool has_subdir = false;
1470 struct tree_context* tc = tree_get_context();
1472 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1474 if (ft_load(tc, dir) < 0)
1476 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1477 return -2;
1480 files = (struct entry*) tc->dircache;
1481 num_files = tc->filesindir;
1483 for (i=0; i<num_files; i++)
1485 if (files[i].attr & ATTR_DIRECTORY)
1486 has_subdir = true;
1487 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
1489 has_music = true;
1490 break;
1494 if (has_music)
1495 return 0;
1497 if (has_subdir)
1499 for (i=0; i<num_files; i++)
1501 if (button_get(false) == SETTINGS_CANCEL)
1503 result = -2;
1504 break;
1507 if (files[i].attr & ATTR_DIRECTORY)
1509 result = check_subdir_for_music(dir, files[i].name);
1510 if (!result)
1511 break;
1516 if (result < 0)
1518 if (dirlen)
1520 dir[dirlen] = '\0';
1522 else
1524 strcpy(dir, "/");
1527 /* we now need to reload our current directory */
1528 if(ft_load(tc, dir) < 0)
1529 gui_syncsplash(HZ*2, true,
1530 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1533 return result;
1537 * Returns absolute path of track
1539 static int format_track_path(char *dest, char *src, int buf_length, int max,
1540 char *dir)
1542 int i = 0;
1543 int j;
1544 char *temp_ptr;
1546 /* Zero-terminate the file name */
1547 while((src[i] != '\n') &&
1548 (src[i] != '\r') &&
1549 (i < max))
1550 i++;
1552 /* Now work back killing white space */
1553 while((src[i-1] == ' ') ||
1554 (src[i-1] == '\t'))
1555 i--;
1557 src[i]=0;
1559 /* replace backslashes with forward slashes */
1560 for ( j=0; j<i; j++ )
1561 if ( src[j] == '\\' )
1562 src[j] = '/';
1564 if('/' == src[0])
1566 strncpy(dest, src, buf_length);
1568 else
1570 /* handle dos style drive letter */
1571 if (':' == src[1])
1572 strncpy(dest, &src[2], buf_length);
1573 else if (!strncmp(src, "../", 3))
1575 /* handle relative paths */
1576 i=3;
1577 while(!strncmp(&src[i], "../", 3))
1578 i += 3;
1579 for (j=0; j<i/3; j++) {
1580 temp_ptr = strrchr(dir, '/');
1581 if (temp_ptr)
1582 *temp_ptr = '\0';
1583 else
1584 break;
1586 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1588 else if ( '.' == src[0] && '/' == src[1] ) {
1589 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1591 else {
1592 snprintf(dest, buf_length, "%s/%s", dir, src);
1596 return 0;
1600 * Display splash message showing progress of playlist/directory insertion or
1601 * save.
1603 static void display_playlist_count(int count, const unsigned char *fmt)
1605 lcd_clear_display();
1607 #ifdef HAVE_LCD_BITMAP
1608 if(global_settings.statusbar)
1609 lcd_setmargins(0, STATUSBAR_HEIGHT);
1610 else
1611 lcd_setmargins(0, 0);
1612 #endif
1614 gui_syncsplash(0, true, fmt, count,
1615 #if CONFIG_KEYPAD == PLAYER_PAD
1616 str(LANG_STOP_ABORT)
1617 #else
1618 str(LANG_OFF_ABORT)
1619 #endif
1624 * Display buffer full message
1626 static void display_buffer_full(void)
1628 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_BUFFER_FULL));
1632 * Flush any cached control commands to disk. Called when playlist is being
1633 * modified. Returns 0 on success and -1 on failure.
1635 static int flush_cached_control(struct playlist_info* playlist)
1637 int result = 0;
1638 int i;
1640 lseek(playlist->control_fd, 0, SEEK_END);
1642 for (i=0; i<playlist->num_cached; i++)
1644 struct playlist_control_cache* cache =
1645 &(playlist->control_cache[i]);
1647 switch (cache->command)
1649 case PLAYLIST_COMMAND_PLAYLIST:
1650 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1651 cache->i1, cache->s1, cache->s2);
1652 break;
1653 case PLAYLIST_COMMAND_ADD:
1654 case PLAYLIST_COMMAND_QUEUE:
1655 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1656 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1657 cache->i1, cache->i2);
1658 if (result > 0)
1660 /* save the position in file where name is written */
1661 int* seek_pos = (int *)cache->data;
1662 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1663 result = fdprintf(playlist->control_fd, "%s\n",
1664 cache->s1);
1666 break;
1667 case PLAYLIST_COMMAND_DELETE:
1668 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1669 break;
1670 case PLAYLIST_COMMAND_SHUFFLE:
1671 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1672 cache->i1, cache->i2);
1673 break;
1674 case PLAYLIST_COMMAND_UNSHUFFLE:
1675 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1676 break;
1677 case PLAYLIST_COMMAND_RESET:
1678 result = fdprintf(playlist->control_fd, "R\n");
1679 break;
1680 default:
1681 break;
1684 if (result <= 0)
1685 break;
1688 if (result > 0)
1690 if (global_settings.resume_seed >= 0)
1692 global_settings.resume_seed = -1;
1693 settings_save();
1696 playlist->num_cached = 0;
1697 playlist->pending_control_sync = true;
1699 result = 0;
1701 else
1702 result = -1;
1704 if (result < 0)
1706 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1707 return result;
1710 return result;
1714 * Update control data with new command. Depending on the command, it may be
1715 * cached or flushed to disk.
1717 static int update_control(struct playlist_info* playlist,
1718 enum playlist_command command, int i1, int i2,
1719 const char* s1, const char* s2, void* data)
1721 int result = 0;
1722 struct playlist_control_cache* cache;
1723 bool flush = false;
1725 mutex_lock(&playlist->control_mutex);
1727 cache = &(playlist->control_cache[playlist->num_cached++]);
1729 cache->command = command;
1730 cache->i1 = i1;
1731 cache->i2 = i2;
1732 cache->s1 = s1;
1733 cache->s2 = s2;
1734 cache->data = data;
1736 switch (command)
1738 case PLAYLIST_COMMAND_PLAYLIST:
1739 case PLAYLIST_COMMAND_ADD:
1740 case PLAYLIST_COMMAND_QUEUE:
1741 #ifndef HAVE_DIRCACHE
1742 case PLAYLIST_COMMAND_DELETE:
1743 case PLAYLIST_COMMAND_RESET:
1744 #endif
1745 flush = true;
1746 break;
1747 case PLAYLIST_COMMAND_SHUFFLE:
1748 case PLAYLIST_COMMAND_UNSHUFFLE:
1749 default:
1750 /* only flush when needed */
1751 break;
1754 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1755 result = flush_cached_control(playlist);
1757 mutex_unlock(&playlist->control_mutex);
1759 return result;
1763 * sync control file to disk
1765 static void sync_control(struct playlist_info* playlist, bool force)
1767 (void) force;
1768 #ifdef HAVE_DIRCACHE
1769 if (force)
1770 #endif
1772 if (playlist->pending_control_sync)
1774 mutex_lock(&playlist->control_mutex);
1775 fsync(playlist->control_fd);
1776 playlist->pending_control_sync = false;
1777 mutex_unlock(&playlist->control_mutex);
1783 * Rotate indices such that first_index is index 0
1785 static int rotate_index(const struct playlist_info* playlist, int index)
1787 index -= playlist->first_index;
1788 if (index < 0)
1789 index += playlist->amount;
1791 return index;
1795 * Initialize playlist entries at startup
1797 void playlist_init(void)
1799 struct playlist_info* playlist = &current_playlist;
1801 playlist->current = true;
1802 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1803 "%s", PLAYLIST_CONTROL_FILE);
1804 playlist->fd = -1;
1805 playlist->control_fd = -1;
1806 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1807 playlist->indices = buffer_alloc(
1808 playlist->max_playlist_size * sizeof(int));
1809 playlist->buffer_size =
1810 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1811 playlist->buffer = buffer_alloc(playlist->buffer_size);
1812 mutex_init(&playlist->control_mutex);
1813 empty_playlist(playlist, true);
1815 #ifdef HAVE_DIRCACHE
1816 playlist->filenames = buffer_alloc(
1817 playlist->max_playlist_size * sizeof(int));
1818 memset(playlist->filenames, 0,
1819 playlist->max_playlist_size * sizeof(int));
1820 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1821 playlist_thread_name);
1822 queue_init(&playlist_queue);
1823 #endif
1827 * Clean playlist at shutdown
1829 void playlist_shutdown(void)
1831 struct playlist_info* playlist = &current_playlist;
1833 if (playlist->control_fd >= 0)
1835 mutex_lock(&playlist->control_mutex);
1837 if (playlist->num_cached > 0)
1838 flush_cached_control(playlist);
1840 close(playlist->control_fd);
1842 mutex_unlock(&playlist->control_mutex);
1847 * Create new playlist
1849 int playlist_create(const char *dir, const char *file)
1851 struct playlist_info* playlist = &current_playlist;
1853 new_playlist(playlist, dir, file);
1855 if (file)
1856 /* load the playlist file */
1857 add_indices_to_playlist(playlist, NULL, 0);
1859 return 0;
1862 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1865 * Restore the playlist state based on control file commands. Called to
1866 * resume playback after shutdown.
1868 int playlist_resume(void)
1870 struct playlist_info* playlist = &current_playlist;
1871 char *buffer;
1872 int buflen;
1873 int nread;
1874 int total_read = 0;
1875 int control_file_size = 0;
1876 bool first = true;
1877 bool sorted = true;
1879 /* use mp3 buffer for maximum load speed */
1880 #if CONFIG_CODEC != SWCODEC
1881 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1882 buflen = (audiobufend - audiobuf);
1883 buffer = (char *)audiobuf;
1884 #else
1885 buflen = (audiobufend - audiobuf - talk_get_bufsize());
1886 buffer = (char *)&audiobuf[talk_get_bufsize()];
1887 #endif
1889 empty_playlist(playlist, true);
1891 gui_syncsplash(0, true, str(LANG_WAIT));
1892 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1893 if (playlist->control_fd < 0)
1895 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1896 return -1;
1898 playlist->control_created = true;
1900 control_file_size = filesize(playlist->control_fd);
1901 if (control_file_size <= 0)
1903 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1904 return -1;
1907 /* read a small amount first to get the header */
1908 nread = read(playlist->control_fd, buffer,
1909 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1910 if(nread <= 0)
1912 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1913 return -1;
1916 while (1)
1918 int result = 0;
1919 int count;
1920 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1921 int last_newline = 0;
1922 int str_count = -1;
1923 bool newline = true;
1924 bool exit_loop = false;
1925 char *p = buffer;
1926 char *str1 = NULL;
1927 char *str2 = NULL;
1928 char *str3 = NULL;
1930 for(count=0; count<nread && !exit_loop; count++,p++)
1932 /* Are we on a new line? */
1933 if((*p == '\n') || (*p == '\r'))
1935 *p = '\0';
1937 /* save last_newline in case we need to load more data */
1938 last_newline = count;
1940 switch (current_command)
1942 case PLAYLIST_COMMAND_PLAYLIST:
1944 /* str1=version str2=dir str3=file */
1945 int version;
1947 if (!str1)
1949 result = -1;
1950 exit_loop = true;
1951 break;
1954 if (!str2)
1955 str2 = "";
1957 if (!str3)
1958 str3 = "";
1960 version = atoi(str1);
1962 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1963 return -1;
1965 update_playlist_filename(playlist, str2, str3);
1967 if (str3[0] != '\0')
1969 /* NOTE: add_indices_to_playlist() overwrites the
1970 audiobuf so we need to reload control file
1971 data */
1972 add_indices_to_playlist(playlist, NULL, 0);
1974 else if (str2[0] != '\0')
1976 playlist->in_ram = true;
1977 resume_directory(str2);
1980 /* load the rest of the data */
1981 first = false;
1982 exit_loop = true;
1984 break;
1986 case PLAYLIST_COMMAND_ADD:
1987 case PLAYLIST_COMMAND_QUEUE:
1989 /* str1=position str2=last_position str3=file */
1990 int position, last_position;
1991 bool queue;
1993 if (!str1 || !str2 || !str3)
1995 result = -1;
1996 exit_loop = true;
1997 break;
2000 position = atoi(str1);
2001 last_position = atoi(str2);
2003 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2004 false:true;
2006 /* seek position is based on str3's position in
2007 buffer */
2008 if (add_track_to_playlist(playlist, str3, position,
2009 queue, total_read+(str3-buffer)) < 0)
2010 return -1;
2012 playlist->last_insert_pos = last_position;
2014 break;
2016 case PLAYLIST_COMMAND_DELETE:
2018 /* str1=position */
2019 int position;
2021 if (!str1)
2023 result = -1;
2024 exit_loop = true;
2025 break;
2028 position = atoi(str1);
2030 if (remove_track_from_playlist(playlist, position,
2031 false) < 0)
2032 return -1;
2034 break;
2036 case PLAYLIST_COMMAND_SHUFFLE:
2038 /* str1=seed str2=first_index */
2039 int seed;
2041 if (!str1 || !str2)
2043 result = -1;
2044 exit_loop = true;
2045 break;
2048 if (!sorted)
2050 /* Always sort list before shuffling */
2051 sort_playlist(playlist, false, false);
2054 seed = atoi(str1);
2055 playlist->first_index = atoi(str2);
2057 if (randomise_playlist(playlist, seed, false,
2058 false) < 0)
2059 return -1;
2061 sorted = false;
2062 break;
2064 case PLAYLIST_COMMAND_UNSHUFFLE:
2066 /* str1=first_index */
2067 if (!str1)
2069 result = -1;
2070 exit_loop = true;
2071 break;
2074 playlist->first_index = atoi(str1);
2076 if (sort_playlist(playlist, false, false) < 0)
2077 return -1;
2079 sorted = true;
2080 break;
2082 case PLAYLIST_COMMAND_RESET:
2084 playlist->last_insert_pos = -1;
2085 break;
2087 case PLAYLIST_COMMAND_COMMENT:
2088 default:
2089 break;
2092 newline = true;
2094 /* to ignore any extra newlines */
2095 current_command = PLAYLIST_COMMAND_COMMENT;
2097 else if(newline)
2099 newline = false;
2101 /* first non-comment line must always specify playlist */
2102 if (first && *p != 'P' && *p != '#')
2104 result = -1;
2105 exit_loop = true;
2106 break;
2109 switch (*p)
2111 case 'P':
2112 /* playlist can only be specified once */
2113 if (!first)
2115 result = -1;
2116 exit_loop = true;
2117 break;
2120 current_command = PLAYLIST_COMMAND_PLAYLIST;
2121 break;
2122 case 'A':
2123 current_command = PLAYLIST_COMMAND_ADD;
2124 break;
2125 case 'Q':
2126 current_command = PLAYLIST_COMMAND_QUEUE;
2127 break;
2128 case 'D':
2129 current_command = PLAYLIST_COMMAND_DELETE;
2130 break;
2131 case 'S':
2132 current_command = PLAYLIST_COMMAND_SHUFFLE;
2133 break;
2134 case 'U':
2135 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2136 break;
2137 case 'R':
2138 current_command = PLAYLIST_COMMAND_RESET;
2139 break;
2140 case '#':
2141 current_command = PLAYLIST_COMMAND_COMMENT;
2142 break;
2143 default:
2144 result = -1;
2145 exit_loop = true;
2146 break;
2149 str_count = -1;
2150 str1 = NULL;
2151 str2 = NULL;
2152 str3 = NULL;
2154 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2156 /* all control file strings are separated with a colon.
2157 Replace the colon with 0 to get proper strings that can be
2158 used by commands above */
2159 if (*p == ':')
2161 *p = '\0';
2162 str_count++;
2164 if ((count+1) < nread)
2166 switch (str_count)
2168 case 0:
2169 str1 = p+1;
2170 break;
2171 case 1:
2172 str2 = p+1;
2173 break;
2174 case 2:
2175 str3 = p+1;
2176 break;
2177 default:
2178 /* allow last string to contain colons */
2179 *p = ':';
2180 break;
2187 if (result < 0)
2189 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2190 return result;
2193 if (!newline || (exit_loop && count<nread))
2195 if ((total_read + count) >= control_file_size)
2197 /* no newline at end of control file */
2198 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2199 return -1;
2202 /* We didn't end on a newline or we exited loop prematurely.
2203 Either way, re-read the remainder. */
2204 count = last_newline;
2205 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2208 total_read += count;
2210 if (first)
2211 /* still looking for header */
2212 nread = read(playlist->control_fd, buffer,
2213 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2214 else
2215 nread = read(playlist->control_fd, buffer, buflen);
2217 /* Terminate on EOF */
2218 if(nread <= 0)
2220 if (global_settings.resume_seed >= 0)
2222 /* Apply shuffle command saved in settings */
2223 if (global_settings.resume_seed == 0)
2224 sort_playlist(playlist, false, true);
2225 else
2227 if (!sorted)
2228 sort_playlist(playlist, false, false);
2230 randomise_playlist(playlist, global_settings.resume_seed,
2231 false, true);
2234 playlist->first_index = global_settings.resume_first_index;
2237 break;
2241 #ifdef HAVE_DIRCACHE
2242 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2243 #endif
2245 return 0;
2249 * Add track to in_ram playlist. Used when playing directories.
2251 int playlist_add(const char *filename)
2253 struct playlist_info* playlist = &current_playlist;
2254 int len = strlen(filename);
2256 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2257 (playlist->amount >= playlist->max_playlist_size))
2259 display_buffer_full();
2260 return -1;
2263 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2264 #ifdef HAVE_DIRCACHE
2265 playlist->filenames[playlist->amount] = NULL;
2266 #endif
2267 playlist->amount++;
2269 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2270 playlist->buffer_end_pos += len;
2271 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2273 return 0;
2276 /* shuffle newly created playlist using random seed. */
2277 int playlist_shuffle(int random_seed, int start_index)
2279 struct playlist_info* playlist = &current_playlist;
2281 unsigned int seek_pos = 0;
2282 bool start_current = false;
2284 if (start_index >= 0 && global_settings.play_selected)
2286 /* store the seek position before the shuffle */
2287 seek_pos = playlist->indices[start_index];
2288 playlist->index = global_settings.resume_first_index =
2289 playlist->first_index = start_index;
2290 start_current = true;
2293 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE));
2295 randomise_playlist(playlist, random_seed, start_current, true);
2297 return playlist->index;
2300 /* start playing current playlist at specified index/offset */
2301 int playlist_start(int start_index, int offset)
2303 struct playlist_info* playlist = &current_playlist;
2305 playlist->index = start_index;
2306 #if CONFIG_CODEC != SWCODEC
2307 talk_buffer_steal(); /* will use the mp3 buffer */
2308 #endif
2309 audio_play(offset);
2311 return 0;
2314 /* Returns false if 'steps' is out of bounds, else true */
2315 bool playlist_check(int steps)
2317 struct playlist_info* playlist = &current_playlist;
2319 /* always allow folder navigation */
2320 if (global_settings.next_folder && playlist->in_ram)
2321 return true;
2323 int index = get_next_index(playlist, steps, -1);
2325 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2326 index = get_next_index(playlist, steps, REPEAT_ALL);
2328 return (index >= 0);
2331 /* get trackname of track that is "steps" away from current playing track.
2332 NULL is used to identify end of playlist */
2333 char* playlist_peek(int steps)
2335 struct playlist_info* playlist = &current_playlist;
2336 int seek;
2337 int fd;
2338 char *temp_ptr;
2339 int index;
2340 bool control_file;
2342 index = get_next_index(playlist, steps, -1);
2343 if (index < 0)
2344 return NULL;
2346 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2347 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2349 if (get_filename(playlist, index, seek, control_file, now_playing,
2350 MAX_PATH+1) < 0)
2351 return NULL;
2353 temp_ptr = now_playing;
2355 if (!playlist->in_ram || control_file)
2357 /* remove bogus dirs from beginning of path
2358 (workaround for buggy playlist creation tools) */
2359 while (temp_ptr)
2361 #ifdef HAVE_DIRCACHE
2362 if (dircache_is_enabled())
2364 if (dircache_get_entry_ptr(temp_ptr))
2365 break;
2367 else
2368 #endif
2370 fd = open(temp_ptr, O_RDONLY);
2371 if (fd >= 0)
2373 close(fd);
2374 break;
2378 temp_ptr = strchr(temp_ptr+1, '/');
2381 if (!temp_ptr)
2383 /* Even though this is an invalid file, we still need to pass a
2384 file name to the caller because NULL is used to indicate end
2385 of playlist */
2386 return now_playing;
2390 return temp_ptr;
2394 * Update indices as track has changed
2396 int playlist_next(int steps)
2398 struct playlist_info* playlist = &current_playlist;
2399 int index;
2401 if ( (steps > 0)
2402 #if (AB_REPEAT_ENABLE == 1)
2403 && (global_settings.repeat_mode != REPEAT_AB)
2404 #endif
2405 && (global_settings.repeat_mode != REPEAT_ONE) )
2407 int i, j;
2409 /* We need to delete all the queued songs */
2410 for (i=0, j=steps; i<j; i++)
2412 index = get_next_index(playlist, i, -1);
2414 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2416 remove_track_from_playlist(playlist, index, true);
2417 steps--; /* one less track */
2422 index = get_next_index(playlist, steps, -1);
2424 if (index < 0)
2426 /* end of playlist... or is it */
2427 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2428 global_settings.playlist_shuffle &&
2429 playlist->amount > 1)
2431 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2432 playlist->first_index = global_settings.resume_first_index = 0;
2433 sort_playlist(playlist, false, false);
2434 randomise_playlist(playlist, current_tick, false, true);
2435 #if CONFIG_CODEC != SWCODEC
2436 playlist_start(0, 0);
2437 #endif
2438 playlist->index = 0;
2439 index = 0;
2441 else if (playlist->in_ram && global_settings.next_folder)
2443 char dir[MAX_PATH+1];
2445 changing_dir = true;
2446 if (steps > 0)
2448 if (!get_next_directory(dir))
2450 /* start playing next directory */
2451 if (playlist_create(dir, NULL) != -1)
2453 ft_build_playlist(tree_get_context(), 0);
2454 if (global_settings.playlist_shuffle)
2455 playlist_shuffle(current_tick, -1);
2456 #if CONFIG_CODEC != SWCODEC
2457 playlist_start(0, 0);
2458 #endif
2459 playlist->index = 0;
2460 index = 0;
2464 else
2466 if (!get_previous_directory(dir))
2468 /* start playing previous directory */
2469 if (playlist_create(dir, NULL) != -1)
2471 ft_build_playlist(tree_get_context(), 0);
2472 if (global_settings.playlist_shuffle)
2473 playlist_shuffle(current_tick, -1);
2474 playlist_start(current_playlist.amount-1,0);
2475 index = current_playlist.amount-1;
2479 changing_dir = false;
2482 return index;
2485 playlist->index = index;
2487 if (playlist->last_insert_pos >= 0 && steps > 0)
2489 /* check to see if we've gone beyond the last inserted track */
2490 int cur = rotate_index(playlist, index);
2491 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2493 if (cur > last_pos)
2495 /* reset last inserted track */
2496 playlist->last_insert_pos = -1;
2498 if (playlist->control_fd >= 0)
2500 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2501 -1, -1, NULL, NULL, NULL);
2503 if (result < 0)
2504 return result;
2506 sync_control(playlist, false);
2511 return index;
2514 /* try playing next or previous folder */
2515 bool playlist_next_dir(int direction)
2517 char dir[MAX_PATH+1];
2518 bool result;
2519 int res;
2521 /* not to mess up real playlists */
2522 if(!current_playlist.in_ram)
2523 return false;
2525 if(changing_dir)
2526 return false;
2528 changing_dir = true;
2529 if(direction > 0)
2530 res = get_next_directory(dir);
2531 else
2532 res = get_previous_directory(dir);
2533 if (!res)
2535 if (playlist_create(dir, NULL) != -1)
2537 ft_build_playlist(tree_get_context(), 0);
2538 if (global_settings.playlist_shuffle)
2539 playlist_shuffle(current_tick, -1);
2540 playlist_start(0,0);
2541 result = true;
2543 else
2544 result = false;
2546 else
2547 result = false;
2549 changing_dir = false;
2551 return result;
2554 /* Get resume info for current playing song. If return value is -1 then
2555 settings shouldn't be saved. */
2556 int playlist_get_resume_info(int *resume_index)
2558 struct playlist_info* playlist = &current_playlist;
2560 *resume_index = playlist->index;
2562 return 0;
2565 /* Update resume info for current playing song. Returns -1 on error. */
2566 int playlist_update_resume_info(const struct mp3entry* id3)
2568 struct playlist_info* playlist = &current_playlist;
2570 if (id3)
2572 if (global_settings.resume_index != playlist->index ||
2573 global_settings.resume_offset != id3->offset)
2575 global_settings.resume_index = playlist->index;
2576 global_settings.resume_offset = id3->offset;
2577 settings_save();
2580 else
2582 global_settings.resume_index = -1;
2583 global_settings.resume_offset = -1;
2584 settings_save();
2587 return 0;
2590 /* Returns index of current playing track for display purposes. This value
2591 should not be used for resume purposes as it doesn't represent the actual
2592 index into the playlist */
2593 int playlist_get_display_index(void)
2595 struct playlist_info* playlist = &current_playlist;
2597 /* first_index should always be index 0 for display purposes */
2598 int index = rotate_index(playlist, playlist->index);
2600 return (index+1);
2603 /* returns number of tracks in current playlist */
2604 int playlist_amount(void)
2606 return playlist_amount_ex(NULL);
2610 * Create a new playlist If playlist is not NULL then we're loading a
2611 * playlist off disk for viewing/editing. The index_buffer is used to store
2612 * playlist indices (required for and only used if !current playlist). The
2613 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2615 int playlist_create_ex(struct playlist_info* playlist,
2616 const char* dir, const char* file,
2617 void* index_buffer, int index_buffer_size,
2618 void* temp_buffer, int temp_buffer_size)
2620 if (!playlist)
2621 playlist = &current_playlist;
2622 else
2624 /* Initialize playlist structure */
2625 int r = rand() % 10;
2626 playlist->current = false;
2628 /* Use random name for control file */
2629 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2630 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2631 playlist->fd = -1;
2632 playlist->control_fd = -1;
2634 if (index_buffer)
2636 int num_indices = index_buffer_size / sizeof(int);
2638 #ifdef HAVE_DIRCACHE
2639 num_indices /= 2;
2640 #endif
2641 if (num_indices > global_settings.max_files_in_playlist)
2642 num_indices = global_settings.max_files_in_playlist;
2644 playlist->max_playlist_size = num_indices;
2645 playlist->indices = index_buffer;
2646 #ifdef HAVE_DIRCACHE
2647 playlist->filenames = (const struct dircache_entry **)
2648 &playlist->indices[num_indices];
2649 #endif
2651 else
2653 playlist->max_playlist_size = current_playlist.max_playlist_size;
2654 playlist->indices = current_playlist.indices;
2655 #ifdef HAVE_DIRCACHE
2656 playlist->filenames = current_playlist.filenames;
2657 #endif
2660 playlist->buffer_size = 0;
2661 playlist->buffer = NULL;
2662 mutex_init(&playlist->control_mutex);
2665 new_playlist(playlist, dir, file);
2667 if (file)
2668 /* load the playlist file */
2669 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2671 return 0;
2675 * Set the specified playlist as the current.
2676 * NOTE: You will get undefined behaviour if something is already playing so
2677 * remember to stop before calling this. Also, this call will
2678 * effectively close your playlist, making it unusable.
2680 int playlist_set_current(struct playlist_info* playlist)
2682 if (!playlist || (check_control(playlist) < 0))
2683 return -1;
2685 empty_playlist(&current_playlist, false);
2687 strncpy(current_playlist.filename, playlist->filename,
2688 sizeof(current_playlist.filename));
2690 current_playlist.fd = playlist->fd;
2692 close(playlist->control_fd);
2693 remove(current_playlist.control_filename);
2694 if (rename(playlist->control_filename,
2695 current_playlist.control_filename) < 0)
2696 return -1;
2697 current_playlist.control_fd = open(current_playlist.control_filename,
2698 O_RDWR);
2699 if (current_playlist.control_fd < 0)
2700 return -1;
2701 current_playlist.control_created = true;
2703 current_playlist.dirlen = playlist->dirlen;
2705 if (playlist->indices && playlist->indices != current_playlist.indices)
2707 memcpy(current_playlist.indices, playlist->indices,
2708 playlist->max_playlist_size*sizeof(int));
2709 #ifdef HAVE_DIRCACHE
2710 memcpy(current_playlist.filenames, playlist->filenames,
2711 playlist->max_playlist_size*sizeof(int));
2712 #endif
2715 current_playlist.first_index = playlist->first_index;
2716 current_playlist.amount = playlist->amount;
2717 current_playlist.last_insert_pos = playlist->last_insert_pos;
2718 current_playlist.seed = playlist->seed;
2719 current_playlist.shuffle_modified = playlist->shuffle_modified;
2720 current_playlist.deleted = playlist->deleted;
2721 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2723 memcpy(current_playlist.control_cache, playlist->control_cache,
2724 sizeof(current_playlist.control_cache));
2725 current_playlist.num_cached = playlist->num_cached;
2726 current_playlist.pending_control_sync = playlist->pending_control_sync;
2728 return 0;
2732 * Close files and delete control file for non-current playlist.
2734 void playlist_close(struct playlist_info* playlist)
2736 if (!playlist)
2737 return;
2739 if (playlist->fd >= 0)
2740 close(playlist->fd);
2742 if (playlist->control_fd >= 0)
2743 close(playlist->control_fd);
2745 if (playlist->control_created)
2746 remove(playlist->control_filename);
2750 * Insert track into playlist at specified position (or one of the special
2751 * positions). Returns position where track was inserted or -1 if error.
2753 int playlist_insert_track(struct playlist_info* playlist,
2754 const char *filename, int position, bool queue)
2756 int result;
2758 if (!playlist)
2759 playlist = &current_playlist;
2761 if (check_control(playlist) < 0)
2763 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2764 return -1;
2767 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2769 if (result != -1)
2771 sync_control(playlist, false);
2772 if (audio_status() & AUDIO_STATUS_PLAY)
2773 audio_flush_and_reload_tracks();
2776 #ifdef HAVE_DIRCACHE
2777 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2778 #endif
2780 return result;
2784 * Insert all tracks from specified directory into playlist.
2786 int playlist_insert_directory(struct playlist_info* playlist,
2787 const char *dirname, int position, bool queue,
2788 bool recurse)
2790 int count = 0;
2791 int result;
2792 unsigned char *count_str;
2794 if (!playlist)
2795 playlist = &current_playlist;
2797 if (check_control(playlist) < 0)
2799 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2800 return -1;
2803 if (queue)
2804 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2805 else
2806 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2808 display_playlist_count(count, count_str);
2810 result = add_directory_to_playlist(playlist, dirname, &position, queue,
2811 &count, recurse);
2813 sync_control(playlist, false);
2815 display_playlist_count(count, count_str);
2817 if (audio_status() & AUDIO_STATUS_PLAY)
2818 audio_flush_and_reload_tracks();
2820 #ifdef HAVE_DIRCACHE
2821 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2822 #endif
2824 return result;
2828 * Insert all tracks from specified playlist into dynamic playlist.
2830 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2831 int position, bool queue)
2833 int fd;
2834 int max;
2835 char *temp_ptr;
2836 char *dir;
2837 unsigned char *count_str;
2838 char temp_buf[MAX_PATH+1];
2839 char trackname[MAX_PATH+1];
2840 int count = 0;
2841 int result = 0;
2843 if (!playlist)
2844 playlist = &current_playlist;
2846 if (check_control(playlist) < 0)
2848 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2849 return -1;
2852 fd = open(filename, O_RDONLY);
2853 if (fd < 0)
2855 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2856 return -1;
2859 /* we need the directory name for formatting purposes */
2860 dir = filename;
2862 temp_ptr = strrchr(filename+1,'/');
2863 if (temp_ptr)
2864 *temp_ptr = 0;
2865 else
2866 dir = "/";
2868 if (queue)
2869 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2870 else
2871 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2873 display_playlist_count(count, count_str);
2875 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2877 /* user abort */
2878 if (button_get(false) == SETTINGS_CANCEL)
2879 break;
2881 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2883 int insert_pos;
2885 /* we need to format so that relative paths are correctly
2886 handled */
2887 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2888 dir) < 0)
2890 result = -1;
2891 break;
2894 insert_pos = add_track_to_playlist(playlist, trackname, position,
2895 queue, -1);
2897 if (insert_pos < 0)
2899 result = -1;
2900 break;
2903 /* Make sure tracks are inserted in correct order if user
2904 requests INSERT_FIRST */
2905 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2906 position = insert_pos + 1;
2908 count++;
2910 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2912 display_playlist_count(count, count_str);
2914 if (count == PLAYLIST_DISPLAY_COUNT &&
2915 (audio_status() & AUDIO_STATUS_PLAY))
2916 audio_flush_and_reload_tracks();
2920 /* let the other threads work */
2921 yield();
2924 close(fd);
2926 if (temp_ptr)
2927 *temp_ptr = '/';
2929 sync_control(playlist, false);
2931 display_playlist_count(count, count_str);
2933 if (audio_status() & AUDIO_STATUS_PLAY)
2934 audio_flush_and_reload_tracks();
2936 #ifdef HAVE_DIRCACHE
2937 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2938 #endif
2940 return result;
2944 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2945 * we want to delete the current playing track.
2947 int playlist_delete(struct playlist_info* playlist, int index)
2949 int result = 0;
2951 if (!playlist)
2952 playlist = &current_playlist;
2954 if (check_control(playlist) < 0)
2956 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2957 return -1;
2960 if (index == PLAYLIST_DELETE_CURRENT)
2961 index = playlist->index;
2963 result = remove_track_from_playlist(playlist, index, true);
2965 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
2966 audio_flush_and_reload_tracks();
2968 return result;
2972 * Move track at index to new_index. Tracks between the two are shifted
2973 * appropriately. Returns 0 on success and -1 on failure.
2975 int playlist_move(struct playlist_info* playlist, int index, int new_index)
2977 int result;
2978 int seek;
2979 bool control_file;
2980 bool queue;
2981 bool current = false;
2982 int r;
2983 char filename[MAX_PATH];
2985 if (!playlist)
2986 playlist = &current_playlist;
2988 if (check_control(playlist) < 0)
2990 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2991 return -1;
2994 if (index == new_index)
2995 return -1;
2997 if (index == playlist->index)
2998 /* Moving the current track */
2999 current = true;
3001 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3002 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3003 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3005 if (get_filename(playlist, index, seek, control_file, filename,
3006 sizeof(filename)) < 0)
3007 return -1;
3009 /* Delete track from original position */
3010 result = remove_track_from_playlist(playlist, index, true);
3012 if (result != -1)
3014 /* We want to insert the track at the position that was specified by
3015 new_index. This may be different then new_index because of the
3016 shifting that occurred after the delete */
3017 r = rotate_index(playlist, new_index);
3019 if (r == 0)
3020 /* First index */
3021 new_index = PLAYLIST_PREPEND;
3022 else if (r == playlist->amount)
3023 /* Append */
3024 new_index = PLAYLIST_INSERT_LAST;
3025 else
3026 /* Calculate index of desired position */
3027 new_index = (r+playlist->first_index)%playlist->amount;
3029 result = add_track_to_playlist(playlist, filename, new_index, queue,
3030 -1);
3032 if (result != -1)
3034 if (current)
3036 /* Moved the current track */
3037 switch (new_index)
3039 case PLAYLIST_PREPEND:
3040 playlist->index = playlist->first_index;
3041 break;
3042 case PLAYLIST_INSERT_LAST:
3043 playlist->index = playlist->first_index - 1;
3044 if (playlist->index < 0)
3045 playlist->index += playlist->amount;
3046 break;
3047 default:
3048 playlist->index = new_index;
3049 break;
3053 if (audio_status() & AUDIO_STATUS_PLAY)
3054 audio_flush_and_reload_tracks();
3058 #ifdef HAVE_DIRCACHE
3059 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3060 #endif
3062 return result;
3065 /* shuffle currently playing playlist */
3066 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3067 bool start_current)
3069 int result;
3071 if (!playlist)
3072 playlist = &current_playlist;
3074 check_control(playlist);
3076 result = randomise_playlist(playlist, seed, start_current, true);
3078 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
3079 audio_flush_and_reload_tracks();
3081 return result;
3084 /* sort currently playing playlist */
3085 int playlist_sort(struct playlist_info* playlist, bool start_current)
3087 int result;
3089 if (!playlist)
3090 playlist = &current_playlist;
3092 check_control(playlist);
3094 result = sort_playlist(playlist, start_current, true);
3096 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
3097 audio_flush_and_reload_tracks();
3099 return result;
3102 /* returns true if playlist has been modified */
3103 bool playlist_modified(const struct playlist_info* playlist)
3105 if (!playlist)
3106 playlist = &current_playlist;
3108 if (playlist->shuffle_modified ||
3109 playlist->deleted ||
3110 playlist->num_inserted_tracks > 0)
3111 return true;
3113 return false;
3116 /* returns index of first track in playlist */
3117 int playlist_get_first_index(const struct playlist_info* playlist)
3119 if (!playlist)
3120 playlist = &current_playlist;
3122 return playlist->first_index;
3125 /* returns shuffle seed of playlist */
3126 int playlist_get_seed(const struct playlist_info* playlist)
3128 if (!playlist)
3129 playlist = &current_playlist;
3131 return playlist->seed;
3134 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3135 int playlist_amount_ex(const struct playlist_info* playlist)
3137 if (!playlist)
3138 playlist = &current_playlist;
3140 return playlist->amount;
3143 /* returns full path of playlist (minus extension) */
3144 char *playlist_name(const struct playlist_info* playlist, char *buf,
3145 int buf_size)
3147 char *sep;
3149 if (!playlist)
3150 playlist = &current_playlist;
3152 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3154 if (!buf[0])
3155 return NULL;
3157 /* Remove extension */
3158 sep = strrchr(buf, '.');
3159 if (sep)
3160 *sep = 0;
3162 return buf;
3165 /* returns the playlist filename */
3166 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3167 int buf_size)
3169 if (!playlist)
3170 playlist = &current_playlist;
3172 snprintf(buf, buf_size, "%s", playlist->filename);
3174 if (!buf[0])
3175 return NULL;
3177 return buf;
3180 /* Fills info structure with information about track at specified index.
3181 Returns 0 on success and -1 on failure */
3182 int playlist_get_track_info(struct playlist_info* playlist, int index,
3183 struct playlist_track_info* info)
3185 int seek;
3186 bool control_file;
3188 if (!playlist)
3189 playlist = &current_playlist;
3191 if (index < 0 || index >= playlist->amount)
3192 return -1;
3194 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3195 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3197 if (get_filename(playlist, index, seek, control_file, info->filename,
3198 sizeof(info->filename)) < 0)
3199 return -1;
3201 info->attr = 0;
3203 if (control_file)
3205 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3206 info->attr |= PLAYLIST_ATTR_QUEUED;
3207 else
3208 info->attr |= PLAYLIST_ATTR_INSERTED;
3212 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3213 info->attr |= PLAYLIST_ATTR_SKIPPED;
3215 info->index = index;
3216 info->display_index = rotate_index(playlist, index) + 1;
3218 return 0;
3221 /* save the current dynamic playlist to specified file */
3222 int playlist_save(struct playlist_info* playlist, char *filename)
3224 int fd;
3225 int i, index;
3226 int count = 0;
3227 char path[MAX_PATH+1];
3228 char tmp_buf[MAX_PATH+1];
3229 int result = 0;
3230 bool overwrite_current = false;
3231 int* index_buf = NULL;
3233 if (!playlist)
3234 playlist = &current_playlist;
3236 if (playlist->amount <= 0)
3237 return -1;
3239 /* use current working directory as base for pathname */
3240 if (format_track_path(path, filename, sizeof(tmp_buf),
3241 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3242 return -1;
3244 if (!strncmp(playlist->filename, path, strlen(path)))
3246 /* Attempting to overwrite current playlist file.*/
3248 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3250 /* not enough buffer space to store updated indices */
3251 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3252 return -1;
3255 /* in_ram buffer is unused for m3u files so we'll use for storing
3256 updated indices */
3257 index_buf = (int*)playlist->buffer;
3259 /* use temporary pathname */
3260 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3261 overwrite_current = true;
3264 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3265 if (fd < 0)
3267 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3268 return -1;
3271 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3273 index = playlist->first_index;
3274 for (i=0; i<playlist->amount; i++)
3276 bool control_file;
3277 bool queue;
3278 int seek;
3280 /* user abort */
3281 if (button_get(false) == SETTINGS_CANCEL)
3283 result = -1;
3284 break;
3287 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3288 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3289 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3291 /* Don't save queued files */
3292 if (!queue)
3294 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3295 MAX_PATH+1) < 0)
3297 result = -1;
3298 break;
3301 if (overwrite_current)
3302 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3304 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3306 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3307 result = -1;
3308 break;
3311 count++;
3313 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3314 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3316 yield();
3319 index = (index+1)%playlist->amount;
3322 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3324 close(fd);
3326 if (overwrite_current && result >= 0)
3328 result = -1;
3330 mutex_lock(&playlist->control_mutex);
3332 /* Replace the current playlist with the new one and update indices */
3333 close(playlist->fd);
3334 if (remove(playlist->filename) >= 0)
3336 if (rename(path, playlist->filename) >= 0)
3338 playlist->fd = open(playlist->filename, O_RDONLY);
3339 if (playlist->fd >= 0)
3341 index = playlist->first_index;
3342 for (i=0, count=0; i<playlist->amount; i++)
3344 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3346 playlist->indices[index] = index_buf[count];
3347 count++;
3349 index = (index+1)%playlist->amount;
3352 /* we need to recreate control because inserted tracks are
3353 now part of the playlist and shuffle has been
3354 invalidated */
3355 result = recreate_control(playlist);
3360 mutex_unlock(&playlist->control_mutex);
3364 return result;