Add platform file for Ipod 1G / 2G. Now only the front image is missing for building...
[Rockbox.git] / apps / playlist.c
blobf0ac29d1ef0c8ccc957c848ac4630a5727aa7e47
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 <ctype.h>
72 #include "playlist.h"
73 #include "file.h"
74 #include "action.h"
75 #include "dir.h"
76 #include "sprintf.h"
77 #include "debug.h"
78 #include "audio.h"
79 #include "lcd.h"
80 #include "kernel.h"
81 #include "settings.h"
82 #include "status.h"
83 #include "applimits.h"
84 #include "screens.h"
85 #include "buffer.h"
86 #include "atoi.h"
87 #include "misc.h"
88 #include "button.h"
89 #include "filetree.h"
90 #include "abrepeat.h"
91 #include "thread.h"
92 #include "usb.h"
93 #include "filetypes.h"
94 #ifdef HAVE_LCD_BITMAP
95 #include "icons.h"
96 #endif
98 #include "lang.h"
99 #include "talk.h"
100 #include "splash.h"
101 #include "rbunicode.h"
103 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
104 #define PLAYLIST_CONTROL_FILE_VERSION 2
107 Each playlist index has a flag associated with it which identifies what
108 type of track it is. These flags are stored in the 4 high order bits of
109 the index.
111 NOTE: This limits the playlist file size to a max of 256M.
113 Bits 31-30:
114 00 = Playlist track
115 01 = Track was prepended into playlist
116 10 = Track was inserted into playlist
117 11 = Track was appended into playlist
118 Bit 29:
119 0 = Added track
120 1 = Queued track
121 Bit 28:
122 0 = Track entry is valid
123 1 = Track does not exist on disk and should be skipped
125 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
126 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
127 #define PLAYLIST_QUEUE_MASK 0x20000000
129 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
130 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
131 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
133 #define PLAYLIST_QUEUED 0x20000000
134 #define PLAYLIST_SKIPPED 0x10000000
136 #define PLAYLIST_DISPLAY_COUNT 10
138 struct directory_search_context {
139 struct playlist_info* playlist;
140 int position;
141 bool queue;
142 int count;
145 static bool changing_dir = false;
147 static struct playlist_info current_playlist;
148 static char now_playing[MAX_PATH+1];
150 static void empty_playlist(struct playlist_info* playlist, bool resume);
151 static void new_playlist(struct playlist_info* playlist, const char *dir,
152 const char *file);
153 static void create_control(struct playlist_info* playlist);
154 static int check_control(struct playlist_info* playlist);
155 static int recreate_control(struct playlist_info* playlist);
156 static void update_playlist_filename(struct playlist_info* playlist,
157 const char *dir, const char *file);
158 static int add_indices_to_playlist(struct playlist_info* playlist,
159 char* buffer, size_t buflen);
160 static int add_track_to_playlist(struct playlist_info* playlist,
161 const char *filename, int position,
162 bool queue, int seek_pos);
163 static int directory_search_callback(char* filename, void* context);
164 static int remove_track_from_playlist(struct playlist_info* playlist,
165 int position, bool write);
166 static int randomise_playlist(struct playlist_info* playlist,
167 unsigned int seed, bool start_current,
168 bool write);
169 static int sort_playlist(struct playlist_info* playlist, bool start_current,
170 bool write);
171 static int get_next_index(const struct playlist_info* playlist, int steps,
172 int repeat_mode);
173 static void find_and_set_playlist_index(struct playlist_info* playlist,
174 unsigned int seek);
175 static int compare(const void* p1, const void* p2);
176 static int get_filename(struct playlist_info* playlist, int index, int seek,
177 bool control_file, char *buf, int buf_length);
178 static int get_next_directory(char *dir);
179 static int get_next_dir(char *dir, bool is_forward, bool recursion);
180 static int get_previous_directory(char *dir);
181 static int check_subdir_for_music(char *dir, char *subdir);
182 static int format_track_path(char *dest, char *src, int buf_length, int max,
183 char *dir);
184 static void display_playlist_count(int count, const unsigned char *fmt);
185 static void display_buffer_full(void);
186 static int flush_cached_control(struct playlist_info* playlist);
187 static int update_control(struct playlist_info* playlist,
188 enum playlist_command command, int i1, int i2,
189 const char* s1, const char* s2, void* data);
190 static void sync_control(struct playlist_info* playlist, bool force);
191 static int rotate_index(const struct playlist_info* playlist, int index);
193 #ifdef HAVE_DIRCACHE
194 #define PLAYLIST_LOAD_POINTERS 1
196 static struct event_queue playlist_queue;
197 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
198 static const char playlist_thread_name[] = "playlist cachectrl";
199 #endif
202 * remove any files and indices associated with the playlist
204 static void empty_playlist(struct playlist_info* playlist, bool resume)
206 playlist->filename[0] = '\0';
207 playlist->utf8 = true;
209 if(playlist->fd >= 0)
210 /* If there is an already open playlist, close it. */
211 close(playlist->fd);
212 playlist->fd = -1;
214 if(playlist->control_fd >= 0)
215 close(playlist->control_fd);
216 playlist->control_fd = -1;
217 playlist->control_created = false;
219 playlist->in_ram = false;
221 if (playlist->buffer)
222 playlist->buffer[0] = 0;
224 playlist->buffer_end_pos = 0;
226 playlist->index = 0;
227 playlist->first_index = 0;
228 playlist->amount = 0;
229 playlist->last_insert_pos = -1;
230 playlist->seed = 0;
231 playlist->shuffle_modified = false;
232 playlist->deleted = false;
233 playlist->num_inserted_tracks = 0;
234 playlist->started = false;
236 playlist->num_cached = 0;
237 playlist->pending_control_sync = false;
239 if (!resume && playlist->current)
241 /* start with fresh playlist control file when starting new
242 playlist */
243 create_control(playlist);
245 /* Reset resume settings */
246 global_status.resume_first_index = 0;
247 global_status.resume_seed = -1;
252 * Initialize a new playlist for viewing/editing/playing. dir is the
253 * directory where the playlist is located and file is the filename.
255 static void new_playlist(struct playlist_info* playlist, const char *dir,
256 const char *file)
258 empty_playlist(playlist, false);
260 if (!file)
262 file = "";
264 if (dir && playlist->current) /* !current cannot be in_ram */
265 playlist->in_ram = true;
266 else
267 dir = ""; /* empty playlist */
270 update_playlist_filename(playlist, dir, file);
272 if (playlist->control_fd >= 0)
274 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
275 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
276 sync_control(playlist, false);
281 * create control file for playlist
283 static void create_control(struct playlist_info* playlist)
285 playlist->control_fd = open(playlist->control_filename,
286 O_CREAT|O_RDWR|O_TRUNC);
287 if (playlist->control_fd < 0)
289 if (check_rockboxdir())
291 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
292 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
293 playlist->control_fd);
295 playlist->control_created = false;
297 else
299 playlist->control_created = true;
304 * validate the control file. This may include creating/initializing it if
305 * necessary;
307 static int check_control(struct playlist_info* playlist)
309 if (!playlist->control_created)
311 create_control(playlist);
313 if (playlist->control_fd >= 0)
315 char* dir = playlist->filename;
316 char* file = playlist->filename+playlist->dirlen;
317 char c = playlist->filename[playlist->dirlen-1];
319 playlist->filename[playlist->dirlen-1] = '\0';
321 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
322 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
323 sync_control(playlist, false);
324 playlist->filename[playlist->dirlen-1] = c;
328 if (playlist->control_fd < 0)
329 return -1;
331 return 0;
335 * recreate the control file based on current playlist entries
337 static int recreate_control(struct playlist_info* playlist)
339 char temp_file[MAX_PATH+1];
340 int temp_fd = -1;
341 int i;
342 int result = 0;
344 if(playlist->control_fd >= 0)
346 char* dir = playlist->filename;
347 char* file = playlist->filename+playlist->dirlen;
348 char c = playlist->filename[playlist->dirlen-1];
350 close(playlist->control_fd);
352 snprintf(temp_file, sizeof(temp_file), "%s_temp",
353 playlist->control_filename);
355 if (rename(playlist->control_filename, temp_file) < 0)
356 return -1;
358 temp_fd = open(temp_file, O_RDONLY);
359 if (temp_fd < 0)
360 return -1;
362 playlist->control_fd = open(playlist->control_filename,
363 O_CREAT|O_RDWR|O_TRUNC);
364 if (playlist->control_fd < 0)
365 return -1;
367 playlist->filename[playlist->dirlen-1] = '\0';
369 /* cannot call update_control() because of mutex */
370 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
371 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
373 playlist->filename[playlist->dirlen-1] = c;
375 if (result < 0)
377 close(temp_fd);
378 return result;
382 playlist->seed = 0;
383 playlist->shuffle_modified = false;
384 playlist->deleted = false;
385 playlist->num_inserted_tracks = 0;
387 if (playlist->current)
389 global_status.resume_seed = -1;
390 status_save();
393 for (i=0; i<playlist->amount; i++)
395 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
397 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
398 char inserted_file[MAX_PATH+1];
400 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
401 SEEK_SET);
402 read_line(temp_fd, inserted_file, sizeof(inserted_file));
404 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
405 queue?'Q':'A', i, playlist->last_insert_pos);
406 if (result > 0)
408 /* save the position in file where name is written */
409 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
411 result = fdprintf(playlist->control_fd, "%s\n",
412 inserted_file);
414 playlist->indices[i] =
415 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
418 if (result < 0)
419 break;
421 playlist->num_inserted_tracks++;
425 close(temp_fd);
426 remove(temp_file);
427 fsync(playlist->control_fd);
429 if (result < 0)
430 return result;
432 return 0;
436 * store directory and name of playlist file
438 static void update_playlist_filename(struct playlist_info* playlist,
439 const char *dir, const char *file)
441 char *sep="";
442 int dirlen = strlen(dir);
443 int filelen = strlen(file);
445 /* Default to utf8 unless explicitly told otherwise. */
446 playlist->utf8 = !(filelen > 4 && strcasecmp(&file[filelen - 4], ".m3u") == 0);
448 /* If the dir does not end in trailing slash, we use a separator.
449 Otherwise we don't. */
450 if('/' != dir[dirlen-1])
452 sep="/";
453 dirlen++;
456 playlist->dirlen = dirlen;
458 snprintf(playlist->filename, sizeof(playlist->filename),
459 "%s%s%s", dir, sep, file);
463 * calculate track offsets within a playlist file
465 static int add_indices_to_playlist(struct playlist_info* playlist,
466 char* buffer, size_t buflen)
468 unsigned int nread;
469 unsigned int i = 0;
470 unsigned int count = 0;
471 bool store_index;
472 unsigned char *p;
473 int result = 0;
475 if(-1 == playlist->fd)
476 playlist->fd = open(playlist->filename, O_RDONLY);
477 if(playlist->fd < 0)
478 return -1; /* failure */
480 #ifdef HAVE_LCD_BITMAP
481 if(global_settings.statusbar)
482 lcd_setmargins(0, STATUSBAR_HEIGHT);
483 else
484 lcd_setmargins(0, 0);
485 #endif
487 gui_syncsplash(0, str(LANG_PLAYLIST_LOAD));
489 if (!buffer)
491 /* use mp3 buffer for maximum load speed */
492 audio_stop();
493 #if CONFIG_CODEC != SWCODEC
494 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
495 buflen = (audiobufend - audiobuf);
496 buffer = (char *)audiobuf;
497 #else
498 buffer = (char *)audio_get_buffer(false, &buflen);
499 #endif
502 store_index = true;
504 while(1)
506 nread = read(playlist->fd, buffer, buflen);
507 /* Terminate on EOF */
508 if(nread <= 0)
509 break;
511 p = (unsigned char *)buffer;
513 /* utf8 BOM at beginning of file? */
514 if(i == 0 && nread > 3
515 && *p == 0xef && *(p+1) == 0xbb && *(p+2) == 0xbf) {
516 nread -= 3;
517 p += 3;
518 i += 3;
519 playlist->utf8 = true; /* Override any earlier indication. */
522 for(count=0; count < nread; count++,p++) {
524 /* Are we on a new line? */
525 if((*p == '\n') || (*p == '\r'))
527 store_index = true;
529 else if(store_index)
531 store_index = false;
533 if(*p != '#')
535 if ( playlist->amount >= playlist->max_playlist_size ) {
536 display_buffer_full();
537 result = -1;
538 goto exit;
541 /* Store a new entry */
542 playlist->indices[ playlist->amount ] = i+count;
543 #ifdef HAVE_DIRCACHE
544 if (playlist->filenames)
545 playlist->filenames[ playlist->amount ] = NULL;
546 #endif
547 playlist->amount++;
552 i+= count;
555 exit:
556 #ifdef HAVE_DIRCACHE
557 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
558 #endif
560 return result;
564 * Removes all tracks, from the playlist, leaving the presently playing
565 * track queued.
567 int remove_all_tracks(struct playlist_info *playlist)
569 int result;
571 if (playlist == NULL)
572 playlist = &current_playlist;
574 while (playlist->index > 0)
575 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
576 return result;
578 while (playlist->amount > 1)
579 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
580 return result;
582 if (playlist->amount == 1) {
583 playlist->indices[0] |= PLAYLIST_QUEUED;
586 return 0;
591 * Add track to playlist at specified position. There are five special
592 * positions that can be specified:
593 * PLAYLIST_PREPEND - Add track at beginning of playlist
594 * PLAYLIST_INSERT - Add track after current song. NOTE: If
595 * there are already inserted tracks then track
596 * is added to the end of the insertion list
597 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
598 * matter what other tracks have been inserted
599 * PLAYLIST_INSERT_LAST - Add track to end of playlist
600 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
601 * current playing track and end of playlist
602 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
603 * and inster this track at the end.
605 static int add_track_to_playlist(struct playlist_info* playlist,
606 const char *filename, int position,
607 bool queue, int seek_pos)
609 int insert_position, orig_position;
610 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
611 int i;
613 insert_position = orig_position = position;
615 if (playlist->amount >= playlist->max_playlist_size)
617 display_buffer_full();
618 return -1;
621 switch (position)
623 case PLAYLIST_PREPEND:
624 position = insert_position = playlist->first_index;
625 break;
626 case PLAYLIST_INSERT:
627 /* if there are already inserted tracks then add track to end of
628 insertion list else add after current playing track */
629 if (playlist->last_insert_pos >= 0 &&
630 playlist->last_insert_pos < playlist->amount &&
631 (playlist->indices[playlist->last_insert_pos]&
632 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
633 position = insert_position = playlist->last_insert_pos+1;
634 else if (playlist->amount > 0)
635 position = insert_position = playlist->index + 1;
636 else
637 position = insert_position = 0;
639 if (playlist->started)
640 playlist->last_insert_pos = position;
641 break;
642 case PLAYLIST_INSERT_FIRST:
643 if (playlist->amount > 0)
644 position = insert_position = playlist->index + 1;
645 else
646 position = insert_position = 0;
648 if (playlist->last_insert_pos < 0 && playlist->started)
649 playlist->last_insert_pos = position;
650 break;
651 case PLAYLIST_INSERT_LAST:
652 if (playlist->first_index > 0)
653 position = insert_position = playlist->first_index;
654 else
655 position = insert_position = playlist->amount;
656 break;
657 case PLAYLIST_INSERT_SHUFFLED:
659 if (playlist->started)
661 int offset;
662 int n = playlist->amount -
663 rotate_index(playlist, playlist->index);
665 if (n > 0)
666 offset = rand() % n;
667 else
668 offset = 0;
670 position = playlist->index + offset + 1;
671 if (position >= playlist->amount)
672 position -= playlist->amount;
674 insert_position = position;
676 else
677 position = insert_position = (rand() % (playlist->amount+1));
678 break;
680 case PLAYLIST_REPLACE:
681 if (remove_all_tracks(playlist) < 0)
682 return -1;
684 position = insert_position = playlist->index + 1;
685 break;
688 if (queue)
689 flags |= PLAYLIST_QUEUED;
691 /* shift indices so that track can be added */
692 for (i=playlist->amount; i>insert_position; i--)
694 playlist->indices[i] = playlist->indices[i-1];
695 #ifdef HAVE_DIRCACHE
696 if (playlist->filenames)
697 playlist->filenames[i] = playlist->filenames[i-1];
698 #endif
701 /* update stored indices if needed */
702 if (playlist->amount > 0 && insert_position <= playlist->index &&
703 playlist->started)
704 playlist->index++;
706 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
707 orig_position != PLAYLIST_PREPEND && playlist->started)
709 playlist->first_index++;
711 if (seek_pos < 0 && playlist->current)
713 global_status.resume_first_index = playlist->first_index;
714 status_save();
718 if (insert_position < playlist->last_insert_pos ||
719 (insert_position == playlist->last_insert_pos && position < 0))
720 playlist->last_insert_pos++;
722 if (seek_pos < 0 && playlist->control_fd >= 0)
724 int result = update_control(playlist,
725 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
726 playlist->last_insert_pos, filename, NULL, &seek_pos);
728 if (result < 0)
729 return result;
732 playlist->indices[insert_position] = flags | seek_pos;
734 #ifdef HAVE_DIRCACHE
735 if (playlist->filenames)
736 playlist->filenames[insert_position] = NULL;
737 #endif
739 playlist->amount++;
740 playlist->num_inserted_tracks++;
742 return insert_position;
746 * Callback for playlist_directory_tracksearch to insert track into
747 * playlist.
749 static int directory_search_callback(char* filename, void* context)
751 struct directory_search_context* c =
752 (struct directory_search_context*) context;
753 int insert_pos;
755 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
756 c->queue, -1);
758 if (insert_pos < 0)
759 return -1;
761 (c->count)++;
763 /* Make sure tracks are inserted in correct order if user requests
764 INSERT_FIRST */
765 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
766 c->position = insert_pos + 1;
768 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
770 unsigned char* count_str;
772 if (c->queue)
773 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
774 else
775 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
777 display_playlist_count(c->count, count_str);
779 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
780 (audio_status() & AUDIO_STATUS_PLAY) &&
781 c->playlist->started)
782 audio_flush_and_reload_tracks();
785 return 0;
789 * remove track at specified position
791 static int remove_track_from_playlist(struct playlist_info* playlist,
792 int position, bool write)
794 int i;
795 bool inserted;
797 if (playlist->amount <= 0)
798 return -1;
800 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
802 /* shift indices now that track has been removed */
803 for (i=position; i<playlist->amount; i++)
805 playlist->indices[i] = playlist->indices[i+1];
806 #ifdef HAVE_DIRCACHE
807 if (playlist->filenames)
808 playlist->filenames[i] = playlist->filenames[i+1];
809 #endif
812 playlist->amount--;
814 if (inserted)
815 playlist->num_inserted_tracks--;
816 else
817 playlist->deleted = true;
819 /* update stored indices if needed */
820 if (position < playlist->index)
821 playlist->index--;
823 if (position < playlist->first_index)
825 playlist->first_index--;
827 if (write)
829 global_status.resume_first_index = playlist->first_index;
830 status_save();
834 if (position <= playlist->last_insert_pos)
835 playlist->last_insert_pos--;
837 if (write && playlist->control_fd >= 0)
839 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
840 position, -1, NULL, NULL, NULL);
842 if (result < 0)
843 return result;
845 sync_control(playlist, false);
848 return 0;
852 * randomly rearrange the array of indices for the playlist. If start_current
853 * is true then update the index to the new index of the current playing track
855 static int randomise_playlist(struct playlist_info* playlist,
856 unsigned int seed, bool start_current,
857 bool write)
859 int count;
860 int candidate;
861 long store;
862 unsigned int current = playlist->indices[playlist->index];
864 /* seed 0 is used to identify sorted playlist for resume purposes */
865 if (seed == 0)
866 seed = 1;
868 /* seed with the given seed */
869 srand(seed);
871 /* randomise entire indices list */
872 for(count = playlist->amount - 1; count >= 0; count--)
874 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
875 candidate = rand() % (count + 1);
877 /* now swap the values at the 'count' and 'candidate' positions */
878 store = playlist->indices[candidate];
879 playlist->indices[candidate] = playlist->indices[count];
880 playlist->indices[count] = store;
881 #ifdef HAVE_DIRCACHE
882 if (playlist->filenames)
884 store = (long)playlist->filenames[candidate];
885 playlist->filenames[candidate] = playlist->filenames[count];
886 playlist->filenames[count] = (struct dircache_entry *)store;
888 #endif
891 if (start_current)
892 find_and_set_playlist_index(playlist, current);
894 /* indices have been moved so last insert position is no longer valid */
895 playlist->last_insert_pos = -1;
897 playlist->seed = seed;
898 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
899 playlist->shuffle_modified = true;
901 if (write)
903 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
904 playlist->first_index, NULL, NULL, NULL);
905 global_status.resume_seed = seed;
906 status_save();
909 return 0;
913 * Sort the array of indices for the playlist. If start_current is true then
914 * set the index to the new index of the current song.
916 static int sort_playlist(struct playlist_info* playlist, bool start_current,
917 bool write)
919 unsigned int current = playlist->indices[playlist->index];
921 if (playlist->amount > 0)
922 qsort(playlist->indices, playlist->amount,
923 sizeof(playlist->indices[0]), compare);
925 #ifdef HAVE_DIRCACHE
926 /** We need to re-check the song names from disk because qsort can't
927 * sort two arrays at once :/
928 * FIXME: Please implement a better way to do this. */
929 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
930 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
931 #endif
933 if (start_current)
934 find_and_set_playlist_index(playlist, current);
936 /* indices have been moved so last insert position is no longer valid */
937 playlist->last_insert_pos = -1;
939 if (!playlist->num_inserted_tracks && !playlist->deleted)
940 playlist->shuffle_modified = false;
941 if (write && playlist->control_fd >= 0)
943 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
944 playlist->first_index, -1, NULL, NULL, NULL);
945 global_status.resume_seed = 0;
946 status_save();
949 return 0;
952 /* Calculate how many steps we have to really step when skipping entries
953 * marked as bad.
955 static int calculate_step_count(const struct playlist_info *playlist, int steps)
957 int i, count, direction;
958 int index;
959 int stepped_count = 0;
961 if (steps < 0)
963 direction = -1;
964 count = -steps;
966 else
968 direction = 1;
969 count = steps;
972 index = playlist->index;
973 i = 0;
974 do {
975 /* Boundary check */
976 if (index < 0)
977 index += playlist->amount;
978 if (index >= playlist->amount)
979 index -= playlist->amount;
981 /* Check if we found a bad entry. */
982 if (playlist->indices[index] & PLAYLIST_SKIPPED)
984 steps += direction;
985 /* Are all entries bad? */
986 if (stepped_count++ > playlist->amount)
987 break ;
989 else
990 i++;
992 index += direction;
993 } while (i <= count);
995 return steps;
998 /* Marks the index of the track to be skipped that is "steps" away from
999 * current playing track.
1001 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1003 int index;
1005 if (playlist == NULL)
1006 playlist = &current_playlist;
1008 /* need to account for already skipped tracks */
1009 steps = calculate_step_count(playlist, steps);
1011 index = playlist->index + steps;
1012 if (index < 0)
1013 index += playlist->amount;
1014 else if (index >= playlist->amount)
1015 index -= playlist->amount;
1017 playlist->indices[index] |= PLAYLIST_SKIPPED;
1021 * returns the index of the track that is "steps" away from current playing
1022 * track.
1024 static int get_next_index(const struct playlist_info* playlist, int steps,
1025 int repeat_mode)
1027 int current_index = playlist->index;
1028 int next_index = -1;
1030 if (playlist->amount <= 0)
1031 return -1;
1033 if (repeat_mode == -1)
1034 repeat_mode = global_settings.repeat_mode;
1036 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1037 repeat_mode = REPEAT_ALL;
1039 steps = calculate_step_count(playlist, steps);
1040 switch (repeat_mode)
1042 case REPEAT_SHUFFLE:
1043 /* Treat repeat shuffle just like repeat off. At end of playlist,
1044 play will be resumed in playlist_next() */
1045 case REPEAT_OFF:
1047 current_index = rotate_index(playlist, current_index);
1048 next_index = current_index+steps;
1049 if ((next_index < 0) || (next_index >= playlist->amount))
1050 next_index = -1;
1051 else
1052 next_index = (next_index+playlist->first_index) %
1053 playlist->amount;
1055 break;
1058 case REPEAT_ONE:
1059 #ifdef AB_REPEAT_ENABLE
1060 case REPEAT_AB:
1061 #endif
1062 next_index = current_index;
1063 break;
1065 case REPEAT_ALL:
1066 default:
1068 next_index = (current_index+steps) % playlist->amount;
1069 while (next_index < 0)
1070 next_index += playlist->amount;
1072 if (steps >= playlist->amount)
1074 int i, index;
1076 index = next_index;
1077 next_index = -1;
1079 /* second time around so skip the queued files */
1080 for (i=0; i<playlist->amount; i++)
1082 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1083 index = (index+1) % playlist->amount;
1084 else
1086 next_index = index;
1087 break;
1091 break;
1095 /* No luck if the whole playlist was bad. */
1096 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1097 return -1;
1099 return next_index;
1103 * Search for the seek track and set appropriate indices. Used after shuffle
1104 * to make sure the current index is still pointing to correct track.
1106 static void find_and_set_playlist_index(struct playlist_info* playlist,
1107 unsigned int seek)
1109 int i;
1111 /* Set the index to the current song */
1112 for (i=0; i<playlist->amount; i++)
1114 if (playlist->indices[i] == seek)
1116 playlist->index = playlist->first_index = i;
1118 if (playlist->current)
1120 global_status.resume_first_index = i;
1121 status_save();
1124 break;
1130 * used to sort track indices. Sort order is as follows:
1131 * 1. Prepended tracks (in prepend order)
1132 * 2. Playlist/directory tracks (in playlist order)
1133 * 3. Inserted/Appended tracks (in insert order)
1135 static int compare(const void* p1, const void* p2)
1137 unsigned long* e1 = (unsigned long*) p1;
1138 unsigned long* e2 = (unsigned long*) p2;
1139 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1140 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1142 if (flags1 == flags2)
1143 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1144 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1145 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1146 return -1;
1147 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1148 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1149 return 1;
1150 else if (flags1 && flags2)
1151 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1152 else
1153 return *e1 - *e2;
1156 #ifdef HAVE_DIRCACHE
1158 * Thread to update filename pointers to dircache on background
1159 * without affecting playlist load up performance. This thread also flushes
1160 * any pending control commands when the disk spins up.
1162 static void playlist_thread(void)
1164 struct event ev;
1165 bool dirty_pointers = false;
1166 static char tmp[MAX_PATH+1];
1168 struct playlist_info *playlist;
1169 int index;
1170 int seek;
1171 bool control_file;
1173 int sleep_time = 5;
1175 if (global_settings.disk_spindown > 1 &&
1176 global_settings.disk_spindown <= 5)
1177 sleep_time = global_settings.disk_spindown - 1;
1179 while (1)
1181 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1183 switch (ev.id)
1185 case PLAYLIST_LOAD_POINTERS:
1186 dirty_pointers = true;
1187 break ;
1189 /* Start the background scanning after either the disk spindown
1190 timeout or 5s, whichever is less */
1191 case SYS_TIMEOUT:
1192 playlist = &current_playlist;
1194 if (playlist->control_fd >= 0
1195 # ifndef SIMULATOR
1196 && ata_disk_is_active()
1197 # endif
1200 if (playlist->num_cached > 0)
1202 mutex_lock(&playlist->control_mutex);
1203 flush_cached_control(playlist);
1204 mutex_unlock(&playlist->control_mutex);
1207 sync_control(playlist, true);
1210 if (!dirty_pointers)
1211 break ;
1213 if (!dircache_is_enabled() || !playlist->filenames
1214 || playlist->amount <= 0)
1215 break ;
1217 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1218 cpu_boost(true);
1219 #endif
1220 for (index = 0; index < playlist->amount
1221 && queue_empty(&playlist_queue); index++)
1223 /* Process only pointers that are not already loaded. */
1224 if (playlist->filenames[index])
1225 continue ;
1227 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1228 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1230 /* Load the filename from playlist file. */
1231 if (get_filename(playlist, index, seek, control_file, tmp,
1232 sizeof(tmp)) < 0)
1233 break ;
1235 /* Set the dircache entry pointer. */
1236 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1238 /* And be on background so user doesn't notice any delays. */
1239 yield();
1242 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1243 cpu_boost(false);
1244 #endif
1245 dirty_pointers = false;
1246 break ;
1248 #ifndef SIMULATOR
1249 case SYS_USB_CONNECTED:
1250 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1251 usb_wait_for_disconnect(&playlist_queue);
1252 break ;
1253 #endif
1257 #endif
1260 * gets pathname for track at seek index
1262 static int get_filename(struct playlist_info* playlist, int index, int seek,
1263 bool control_file, char *buf, int buf_length)
1265 int fd;
1266 int max = -1;
1267 char tmp_buf[MAX_PATH+1];
1268 char dir_buf[MAX_PATH+1];
1270 if (buf_length > MAX_PATH+1)
1271 buf_length = MAX_PATH+1;
1273 #ifdef HAVE_DIRCACHE
1274 if (dircache_is_enabled() && playlist->filenames)
1276 if (playlist->filenames[index] != NULL)
1278 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1279 max = strlen(tmp_buf) + 1;
1282 #else
1283 (void)index;
1284 #endif
1286 if (playlist->in_ram && !control_file && max < 0)
1288 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1289 tmp_buf[MAX_PATH] = '\0';
1290 max = strlen(tmp_buf) + 1;
1292 else if (max < 0)
1294 mutex_lock(&playlist->control_mutex);
1296 if (control_file)
1297 fd = playlist->control_fd;
1298 else
1300 if(-1 == playlist->fd)
1301 playlist->fd = open(playlist->filename, O_RDONLY);
1303 fd = playlist->fd;
1306 if(-1 != fd)
1309 if (lseek(fd, seek, SEEK_SET) != seek)
1310 max = -1;
1311 else
1313 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1315 if ((max > 0) && !playlist->utf8)
1317 char* end;
1318 int i = 0;
1320 /* Locate EOL. */
1321 while ((tmp_buf[i] != '\n') && (tmp_buf[i] != '\r')
1322 && (i < max))
1324 i++;
1327 /* Now work back killing white space. */
1328 while ((i > 0) && isspace(tmp_buf[i - 1]))
1330 i--;
1333 /* Borrow dir_buf a little... */
1334 /* TODO: iso_decode can overflow dir_buf; it really
1335 * should take a dest size argument.
1337 end = iso_decode(tmp_buf, dir_buf, -1, i);
1338 *end = 0;
1339 strncpy(tmp_buf, dir_buf, sizeof(tmp_buf));
1340 tmp_buf[sizeof(tmp_buf) - 1] = 0;
1341 max = strlen(tmp_buf);
1346 mutex_unlock(&playlist->control_mutex);
1348 if (max < 0)
1350 if (control_file)
1351 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1352 else
1353 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR));
1355 return max;
1359 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1360 dir_buf[playlist->dirlen-1] = 0;
1362 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1365 static int get_next_directory(char *dir){
1366 return get_next_dir(dir,true,false);
1369 static int get_previous_directory(char *dir){
1370 return get_next_dir(dir,false,false);
1374 * search through all the directories (starting with the current) to find
1375 * one that has tracks to play
1377 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1379 struct playlist_info* playlist = &current_playlist;
1380 int result = -1;
1381 int sort_dir = global_settings.sort_dir;
1382 char *start_dir = NULL;
1383 bool exit = false;
1384 struct tree_context* tc = tree_get_context();
1385 int dirfilter = *(tc->dirfilter);
1387 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1389 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1390 char buffer[MAX_PATH];
1391 int folder_count = 0,i;
1392 srand(current_tick);
1393 *(tc->dirfilter) = SHOW_MUSIC;
1394 if (fd >= 0)
1396 read(fd,&folder_count,sizeof(int));
1397 while (!exit)
1399 i = rand()%folder_count;
1400 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1401 read(fd,buffer,MAX_PATH);
1402 if (check_subdir_for_music(buffer,"") ==0)
1403 exit = true;
1405 strcpy(dir,buffer);
1406 close(fd);
1407 *(tc->dirfilter) = dirfilter;
1408 reload_directory();
1409 return 0;
1412 /* not random folder advance */
1413 if (recursion){
1414 /* start with root */
1415 dir[0] = '\0';
1417 else{
1418 /* start with current directory */
1419 strncpy(dir, playlist->filename, playlist->dirlen-1);
1420 dir[playlist->dirlen-1] = '\0';
1423 /* use the tree browser dircache to load files */
1424 *(tc->dirfilter) = SHOW_ALL;
1426 /* sort in another direction if previous dir is requested */
1427 if(!is_forward){
1428 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1429 global_settings.sort_dir = 4;
1430 else if (global_settings.sort_dir == 1)
1431 global_settings.sort_dir = 2;
1432 else if (global_settings.sort_dir == 2)
1433 global_settings.sort_dir = 1;
1434 else if (global_settings.sort_dir == 4)
1435 global_settings.sort_dir = 0;
1438 while (!exit)
1440 struct entry *files;
1441 int num_files = 0;
1442 int i;
1444 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1446 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1447 exit = true;
1448 result = -1;
1449 break;
1452 files = (struct entry*) tc->dircache;
1453 num_files = tc->filesindir;
1455 for (i=0; i<num_files; i++)
1457 /* user abort */
1458 if (action_userabort(TIMEOUT_NOBLOCK))
1460 result = -1;
1461 exit = true;
1462 break;
1465 if (files[i].attr & ATTR_DIRECTORY)
1467 if (!start_dir)
1469 result = check_subdir_for_music(dir, files[i].name);
1470 if (result != -1)
1472 exit = true;
1473 break;
1476 else if (!strcmp(start_dir, files[i].name))
1477 start_dir = NULL;
1481 if (!exit)
1483 /* move down to parent directory. current directory name is
1484 stored as the starting point for the search in parent */
1485 start_dir = strrchr(dir, '/');
1486 if (start_dir)
1488 *start_dir = '\0';
1489 start_dir++;
1491 else
1492 break;
1496 /* we've overwritten the dircache so tree browser will need to be
1497 reloaded */
1498 reload_directory();
1500 /* restore dirfilter & sort_dir */
1501 *(tc->dirfilter) = dirfilter;
1502 global_settings.sort_dir = sort_dir;
1504 /* special case if nothing found: try start searching again from root */
1505 if (result == -1 && !recursion){
1506 result = get_next_dir(dir,is_forward, true);
1509 return result;
1513 * Checks if there are any music files in the dir or any of its
1514 * subdirectories. May be called recursively.
1516 static int check_subdir_for_music(char *dir, char *subdir)
1518 int result = -1;
1519 int dirlen = strlen(dir);
1520 int num_files = 0;
1521 int i;
1522 struct entry *files;
1523 bool has_music = false;
1524 bool has_subdir = false;
1525 struct tree_context* tc = tree_get_context();
1527 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1529 if (ft_load(tc, dir) < 0)
1531 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1532 return -2;
1535 files = (struct entry*) tc->dircache;
1536 num_files = tc->filesindir;
1538 for (i=0; i<num_files; i++)
1540 if (files[i].attr & ATTR_DIRECTORY)
1541 has_subdir = true;
1542 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1544 has_music = true;
1545 break;
1549 if (has_music)
1550 return 0;
1552 if (has_subdir)
1554 for (i=0; i<num_files; i++)
1556 if (action_userabort(TIMEOUT_NOBLOCK))
1558 result = -2;
1559 break;
1562 if (files[i].attr & ATTR_DIRECTORY)
1564 result = check_subdir_for_music(dir, files[i].name);
1565 if (!result)
1566 break;
1571 if (result < 0)
1573 if (dirlen)
1575 dir[dirlen] = '\0';
1577 else
1579 strcpy(dir, "/");
1582 /* we now need to reload our current directory */
1583 if(ft_load(tc, dir) < 0)
1584 gui_syncsplash(HZ*2,
1585 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1588 return result;
1592 * Returns absolute path of track
1594 static int format_track_path(char *dest, char *src, int buf_length, int max,
1595 char *dir)
1597 int i = 0;
1598 int j;
1599 char *temp_ptr;
1601 /* Zero-terminate the file name */
1602 while((src[i] != '\n') &&
1603 (src[i] != '\r') &&
1604 (i < max))
1605 i++;
1607 /* Now work back killing white space */
1608 while((src[i-1] == ' ') ||
1609 (src[i-1] == '\t'))
1610 i--;
1612 src[i]=0;
1614 /* replace backslashes with forward slashes */
1615 for ( j=0; j<i; j++ )
1616 if ( src[j] == '\\' )
1617 src[j] = '/';
1619 if('/' == src[0])
1621 strncpy(dest, src, buf_length);
1623 else
1625 /* handle dos style drive letter */
1626 if (':' == src[1])
1627 strncpy(dest, &src[2], buf_length);
1628 else if (!strncmp(src, "../", 3))
1630 /* handle relative paths */
1631 i=3;
1632 while(!strncmp(&src[i], "../", 3))
1633 i += 3;
1634 for (j=0; j<i/3; j++) {
1635 temp_ptr = strrchr(dir, '/');
1636 if (temp_ptr)
1637 *temp_ptr = '\0';
1638 else
1639 break;
1641 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1643 else if ( '.' == src[0] && '/' == src[1] ) {
1644 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1646 else {
1647 snprintf(dest, buf_length, "%s/%s", dir, src);
1651 return 0;
1655 * Display splash message showing progress of playlist/directory insertion or
1656 * save.
1658 static void display_playlist_count(int count, const unsigned char *fmt)
1660 lcd_clear_display();
1662 #ifdef HAVE_LCD_BITMAP
1663 if(global_settings.statusbar)
1664 lcd_setmargins(0, STATUSBAR_HEIGHT);
1665 else
1666 lcd_setmargins(0, 0);
1667 #endif
1669 gui_syncsplash(0, fmt, count,
1670 #if CONFIG_KEYPAD == PLAYER_PAD
1671 str(LANG_STOP_ABORT)
1672 #else
1673 str(LANG_OFF_ABORT)
1674 #endif
1679 * Display buffer full message
1681 static void display_buffer_full(void)
1683 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_BUFFER_FULL));
1687 * Flush any cached control commands to disk. Called when playlist is being
1688 * modified. Returns 0 on success and -1 on failure.
1690 static int flush_cached_control(struct playlist_info* playlist)
1692 int result = 0;
1693 int i;
1695 if (!playlist->num_cached)
1696 return 0;
1698 lseek(playlist->control_fd, 0, SEEK_END);
1700 for (i=0; i<playlist->num_cached; i++)
1702 struct playlist_control_cache* cache =
1703 &(playlist->control_cache[i]);
1705 switch (cache->command)
1707 case PLAYLIST_COMMAND_PLAYLIST:
1708 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1709 cache->i1, cache->s1, cache->s2);
1710 break;
1711 case PLAYLIST_COMMAND_ADD:
1712 case PLAYLIST_COMMAND_QUEUE:
1713 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1714 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1715 cache->i1, cache->i2);
1716 if (result > 0)
1718 /* save the position in file where name is written */
1719 int* seek_pos = (int *)cache->data;
1720 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1721 result = fdprintf(playlist->control_fd, "%s\n",
1722 cache->s1);
1724 break;
1725 case PLAYLIST_COMMAND_DELETE:
1726 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1727 break;
1728 case PLAYLIST_COMMAND_SHUFFLE:
1729 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1730 cache->i1, cache->i2);
1731 break;
1732 case PLAYLIST_COMMAND_UNSHUFFLE:
1733 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1734 break;
1735 case PLAYLIST_COMMAND_RESET:
1736 result = fdprintf(playlist->control_fd, "R\n");
1737 break;
1738 default:
1739 break;
1742 if (result <= 0)
1743 break;
1746 if (result > 0)
1748 if (global_status.resume_seed >= 0)
1750 global_status.resume_seed = -1;
1751 status_save();
1754 playlist->num_cached = 0;
1755 playlist->pending_control_sync = true;
1757 result = 0;
1759 else
1761 result = -1;
1762 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1765 return result;
1769 * Update control data with new command. Depending on the command, it may be
1770 * cached or flushed to disk.
1772 static int update_control(struct playlist_info* playlist,
1773 enum playlist_command command, int i1, int i2,
1774 const char* s1, const char* s2, void* data)
1776 int result = 0;
1777 struct playlist_control_cache* cache;
1778 bool flush = false;
1780 mutex_lock(&playlist->control_mutex);
1782 cache = &(playlist->control_cache[playlist->num_cached++]);
1784 cache->command = command;
1785 cache->i1 = i1;
1786 cache->i2 = i2;
1787 cache->s1 = s1;
1788 cache->s2 = s2;
1789 cache->data = data;
1791 switch (command)
1793 case PLAYLIST_COMMAND_PLAYLIST:
1794 case PLAYLIST_COMMAND_ADD:
1795 case PLAYLIST_COMMAND_QUEUE:
1796 #ifndef HAVE_DIRCACHE
1797 case PLAYLIST_COMMAND_DELETE:
1798 case PLAYLIST_COMMAND_RESET:
1799 #endif
1800 flush = true;
1801 break;
1802 case PLAYLIST_COMMAND_SHUFFLE:
1803 case PLAYLIST_COMMAND_UNSHUFFLE:
1804 default:
1805 /* only flush when needed */
1806 break;
1809 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1810 result = flush_cached_control(playlist);
1812 mutex_unlock(&playlist->control_mutex);
1814 return result;
1818 * sync control file to disk
1820 static void sync_control(struct playlist_info* playlist, bool force)
1822 #ifdef HAVE_DIRCACHE
1823 if (playlist->started && force)
1824 #else
1825 (void) force;
1827 if (playlist->started)
1828 #endif
1830 if (playlist->pending_control_sync)
1832 mutex_lock(&playlist->control_mutex);
1833 fsync(playlist->control_fd);
1834 playlist->pending_control_sync = false;
1835 mutex_unlock(&playlist->control_mutex);
1841 * Rotate indices such that first_index is index 0
1843 static int rotate_index(const struct playlist_info* playlist, int index)
1845 index -= playlist->first_index;
1846 if (index < 0)
1847 index += playlist->amount;
1849 return index;
1853 * Initialize playlist entries at startup
1855 void playlist_init(void)
1857 struct playlist_info* playlist = &current_playlist;
1859 playlist->current = true;
1860 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1861 "%s", PLAYLIST_CONTROL_FILE);
1862 playlist->fd = -1;
1863 playlist->control_fd = -1;
1864 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1865 playlist->indices = buffer_alloc(
1866 playlist->max_playlist_size * sizeof(int));
1867 playlist->buffer_size =
1868 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1869 playlist->buffer = buffer_alloc(playlist->buffer_size);
1870 mutex_init(&playlist->control_mutex);
1871 empty_playlist(playlist, true);
1873 #ifdef HAVE_DIRCACHE
1874 playlist->filenames = buffer_alloc(
1875 playlist->max_playlist_size * sizeof(int));
1876 memset(playlist->filenames, 0,
1877 playlist->max_playlist_size * sizeof(int));
1878 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1879 playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1880 IF_COP(, CPU, false));
1881 queue_init(&playlist_queue, true);
1882 #endif
1886 * Clean playlist at shutdown
1888 void playlist_shutdown(void)
1890 struct playlist_info* playlist = &current_playlist;
1892 if (playlist->control_fd >= 0)
1894 mutex_lock(&playlist->control_mutex);
1896 if (playlist->num_cached > 0)
1897 flush_cached_control(playlist);
1899 close(playlist->control_fd);
1901 mutex_unlock(&playlist->control_mutex);
1906 * Create new playlist
1908 int playlist_create(const char *dir, const char *file)
1910 struct playlist_info* playlist = &current_playlist;
1912 new_playlist(playlist, dir, file);
1914 if (file)
1915 /* load the playlist file */
1916 add_indices_to_playlist(playlist, NULL, 0);
1918 return 0;
1921 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1924 * Restore the playlist state based on control file commands. Called to
1925 * resume playback after shutdown.
1927 int playlist_resume(void)
1929 struct playlist_info* playlist = &current_playlist;
1930 char *buffer;
1931 size_t buflen;
1932 int nread;
1933 int total_read = 0;
1934 int control_file_size = 0;
1935 bool first = true;
1936 bool sorted = true;
1938 /* use mp3 buffer for maximum load speed */
1939 #if CONFIG_CODEC != SWCODEC
1940 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1941 buflen = (audiobufend - audiobuf);
1942 buffer = (char *)audiobuf;
1943 #else
1944 buffer = (char *)audio_get_buffer(false, &buflen);
1945 #endif
1947 empty_playlist(playlist, true);
1949 gui_syncsplash(0, str(LANG_WAIT));
1950 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1951 if (playlist->control_fd < 0)
1953 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1954 return -1;
1956 playlist->control_created = true;
1958 control_file_size = filesize(playlist->control_fd);
1959 if (control_file_size <= 0)
1961 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1962 return -1;
1965 /* read a small amount first to get the header */
1966 nread = read(playlist->control_fd, buffer,
1967 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1968 if(nread <= 0)
1970 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1971 return -1;
1974 playlist->started = true;
1976 while (1)
1978 int result = 0;
1979 int count;
1980 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1981 int last_newline = 0;
1982 int str_count = -1;
1983 bool newline = true;
1984 bool exit_loop = false;
1985 char *p = buffer;
1986 char *str1 = NULL;
1987 char *str2 = NULL;
1988 char *str3 = NULL;
1989 unsigned long last_tick = current_tick;
1991 for(count=0; count<nread && !exit_loop; count++,p++)
1993 /* So a splash while we are loading. */
1994 if (current_tick - last_tick > HZ/4)
1996 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
1997 (total_read+count)*100/control_file_size,
1998 #if CONFIG_KEYPAD == PLAYER_PAD
1999 str(LANG_STOP_ABORT)
2000 #else
2001 str(LANG_OFF_ABORT)
2002 #endif
2004 if (action_userabort(TIMEOUT_NOBLOCK))
2006 /* FIXME:
2007 * Not sure how to implement this, somebody more familiar
2008 * with the code, please fix this. */
2010 last_tick = current_tick;
2013 /* Are we on a new line? */
2014 if((*p == '\n') || (*p == '\r'))
2016 *p = '\0';
2018 /* save last_newline in case we need to load more data */
2019 last_newline = count;
2021 switch (current_command)
2023 case PLAYLIST_COMMAND_PLAYLIST:
2025 /* str1=version str2=dir str3=file */
2026 int version;
2028 if (!str1)
2030 result = -1;
2031 exit_loop = true;
2032 break;
2035 if (!str2)
2036 str2 = "";
2038 if (!str3)
2039 str3 = "";
2041 version = atoi(str1);
2043 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2044 return -1;
2046 update_playlist_filename(playlist, str2, str3);
2048 if (str3[0] != '\0')
2050 /* NOTE: add_indices_to_playlist() overwrites the
2051 audiobuf so we need to reload control file
2052 data */
2053 add_indices_to_playlist(playlist, NULL, 0);
2055 else if (str2[0] != '\0')
2057 playlist->in_ram = true;
2058 resume_directory(str2);
2061 /* load the rest of the data */
2062 first = false;
2063 exit_loop = true;
2065 break;
2067 case PLAYLIST_COMMAND_ADD:
2068 case PLAYLIST_COMMAND_QUEUE:
2070 /* str1=position str2=last_position str3=file */
2071 int position, last_position;
2072 bool queue;
2074 if (!str1 || !str2 || !str3)
2076 result = -1;
2077 exit_loop = true;
2078 break;
2081 position = atoi(str1);
2082 last_position = atoi(str2);
2084 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2085 false:true;
2087 /* seek position is based on str3's position in
2088 buffer */
2089 if (add_track_to_playlist(playlist, str3, position,
2090 queue, total_read+(str3-buffer)) < 0)
2091 return -1;
2093 playlist->last_insert_pos = last_position;
2095 break;
2097 case PLAYLIST_COMMAND_DELETE:
2099 /* str1=position */
2100 int position;
2102 if (!str1)
2104 result = -1;
2105 exit_loop = true;
2106 break;
2109 position = atoi(str1);
2111 if (remove_track_from_playlist(playlist, position,
2112 false) < 0)
2113 return -1;
2115 break;
2117 case PLAYLIST_COMMAND_SHUFFLE:
2119 /* str1=seed str2=first_index */
2120 int seed;
2122 if (!str1 || !str2)
2124 result = -1;
2125 exit_loop = true;
2126 break;
2129 if (!sorted)
2131 /* Always sort list before shuffling */
2132 sort_playlist(playlist, false, false);
2135 seed = atoi(str1);
2136 playlist->first_index = atoi(str2);
2138 if (randomise_playlist(playlist, seed, false,
2139 false) < 0)
2140 return -1;
2142 sorted = false;
2143 break;
2145 case PLAYLIST_COMMAND_UNSHUFFLE:
2147 /* str1=first_index */
2148 if (!str1)
2150 result = -1;
2151 exit_loop = true;
2152 break;
2155 playlist->first_index = atoi(str1);
2157 if (sort_playlist(playlist, false, false) < 0)
2158 return -1;
2160 sorted = true;
2161 break;
2163 case PLAYLIST_COMMAND_RESET:
2165 playlist->last_insert_pos = -1;
2166 break;
2168 case PLAYLIST_COMMAND_COMMENT:
2169 default:
2170 break;
2173 newline = true;
2175 /* to ignore any extra newlines */
2176 current_command = PLAYLIST_COMMAND_COMMENT;
2178 else if(newline)
2180 newline = false;
2182 /* first non-comment line must always specify playlist */
2183 if (first && *p != 'P' && *p != '#')
2185 result = -1;
2186 exit_loop = true;
2187 break;
2190 switch (*p)
2192 case 'P':
2193 /* playlist can only be specified once */
2194 if (!first)
2196 result = -1;
2197 exit_loop = true;
2198 break;
2201 current_command = PLAYLIST_COMMAND_PLAYLIST;
2202 break;
2203 case 'A':
2204 current_command = PLAYLIST_COMMAND_ADD;
2205 break;
2206 case 'Q':
2207 current_command = PLAYLIST_COMMAND_QUEUE;
2208 break;
2209 case 'D':
2210 current_command = PLAYLIST_COMMAND_DELETE;
2211 break;
2212 case 'S':
2213 current_command = PLAYLIST_COMMAND_SHUFFLE;
2214 break;
2215 case 'U':
2216 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2217 break;
2218 case 'R':
2219 current_command = PLAYLIST_COMMAND_RESET;
2220 break;
2221 case '#':
2222 current_command = PLAYLIST_COMMAND_COMMENT;
2223 break;
2224 default:
2225 result = -1;
2226 exit_loop = true;
2227 break;
2230 str_count = -1;
2231 str1 = NULL;
2232 str2 = NULL;
2233 str3 = NULL;
2235 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2237 /* all control file strings are separated with a colon.
2238 Replace the colon with 0 to get proper strings that can be
2239 used by commands above */
2240 if (*p == ':')
2242 *p = '\0';
2243 str_count++;
2245 if ((count+1) < nread)
2247 switch (str_count)
2249 case 0:
2250 str1 = p+1;
2251 break;
2252 case 1:
2253 str2 = p+1;
2254 break;
2255 case 2:
2256 str3 = p+1;
2257 break;
2258 default:
2259 /* allow last string to contain colons */
2260 *p = ':';
2261 break;
2268 if (result < 0)
2270 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_INVALID));
2271 return result;
2274 if (!newline || (exit_loop && count<nread))
2276 if ((total_read + count) >= control_file_size)
2278 /* no newline at end of control file */
2279 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_INVALID));
2280 return -1;
2283 /* We didn't end on a newline or we exited loop prematurely.
2284 Either way, re-read the remainder. */
2285 count = last_newline;
2286 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2289 total_read += count;
2291 if (first)
2292 /* still looking for header */
2293 nread = read(playlist->control_fd, buffer,
2294 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2295 else
2296 nread = read(playlist->control_fd, buffer, buflen);
2298 /* Terminate on EOF */
2299 if(nread <= 0)
2301 if (global_status.resume_seed >= 0)
2303 /* Apply shuffle command saved in settings */
2304 if (global_status.resume_seed == 0)
2305 sort_playlist(playlist, false, true);
2306 else
2308 if (!sorted)
2309 sort_playlist(playlist, false, false);
2311 randomise_playlist(playlist, global_status.resume_seed,
2312 false, true);
2316 playlist->first_index = global_status.resume_first_index;
2317 break;
2321 #ifdef HAVE_DIRCACHE
2322 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2323 #endif
2325 return 0;
2329 * Add track to in_ram playlist. Used when playing directories.
2331 int playlist_add(const char *filename)
2333 struct playlist_info* playlist = &current_playlist;
2334 int len = strlen(filename);
2336 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2337 (playlist->amount >= playlist->max_playlist_size))
2339 display_buffer_full();
2340 return -1;
2343 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2344 #ifdef HAVE_DIRCACHE
2345 playlist->filenames[playlist->amount] = NULL;
2346 #endif
2347 playlist->amount++;
2349 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2350 playlist->buffer_end_pos += len;
2351 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2353 return 0;
2356 /* shuffle newly created playlist using random seed. */
2357 int playlist_shuffle(int random_seed, int start_index)
2359 struct playlist_info* playlist = &current_playlist;
2361 unsigned int seek_pos = 0;
2362 bool start_current = false;
2364 if (start_index >= 0 && global_settings.play_selected)
2366 /* store the seek position before the shuffle */
2367 seek_pos = playlist->indices[start_index];
2368 playlist->index = global_status.resume_first_index =
2369 playlist->first_index = start_index;
2370 start_current = true;
2373 gui_syncsplash(0, str(LANG_PLAYLIST_SHUFFLE));
2375 randomise_playlist(playlist, random_seed, start_current, true);
2377 return playlist->index;
2380 /* start playing current playlist at specified index/offset */
2381 int playlist_start(int start_index, int offset)
2383 struct playlist_info* playlist = &current_playlist;
2385 playlist->index = start_index;
2387 #if CONFIG_CODEC != SWCODEC
2388 talk_buffer_steal(); /* will use the mp3 buffer */
2389 #endif
2391 playlist->started = true;
2392 sync_control(playlist, false);
2393 audio_play(offset);
2395 return 0;
2398 /* Returns false if 'steps' is out of bounds, else true */
2399 bool playlist_check(int steps)
2401 struct playlist_info* playlist = &current_playlist;
2403 /* always allow folder navigation */
2404 if (global_settings.next_folder && playlist->in_ram)
2405 return true;
2407 int index = get_next_index(playlist, steps, -1);
2409 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2410 index = get_next_index(playlist, steps, REPEAT_ALL);
2412 return (index >= 0);
2415 /* get trackname of track that is "steps" away from current playing track.
2416 NULL is used to identify end of playlist */
2417 char* playlist_peek(int steps)
2419 struct playlist_info* playlist = &current_playlist;
2420 int seek;
2421 int fd;
2422 char *temp_ptr;
2423 int index;
2424 bool control_file;
2426 index = get_next_index(playlist, steps, -1);
2427 if (index < 0)
2428 return NULL;
2430 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2431 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2433 if (get_filename(playlist, index, seek, control_file, now_playing,
2434 MAX_PATH+1) < 0)
2435 return NULL;
2437 temp_ptr = now_playing;
2439 if (!playlist->in_ram || control_file)
2441 /* remove bogus dirs from beginning of path
2442 (workaround for buggy playlist creation tools) */
2443 while (temp_ptr)
2445 #ifdef HAVE_DIRCACHE
2446 if (dircache_is_enabled())
2448 if (dircache_get_entry_ptr(temp_ptr))
2449 break;
2451 else
2452 #endif
2454 fd = open(temp_ptr, O_RDONLY);
2455 if (fd >= 0)
2457 close(fd);
2458 break;
2462 temp_ptr = strchr(temp_ptr+1, '/');
2465 if (!temp_ptr)
2467 /* Even though this is an invalid file, we still need to pass a
2468 file name to the caller because NULL is used to indicate end
2469 of playlist */
2470 return now_playing;
2474 return temp_ptr;
2478 * Update indices as track has changed
2480 int playlist_next(int steps)
2482 struct playlist_info* playlist = &current_playlist;
2483 int index;
2485 if ( (steps > 0)
2486 #ifdef AB_REPEAT_ENABLE
2487 && (global_settings.repeat_mode != REPEAT_AB)
2488 #endif
2489 && (global_settings.repeat_mode != REPEAT_ONE) )
2491 int i, j;
2493 /* We need to delete all the queued songs */
2494 for (i=0, j=steps; i<j; i++)
2496 index = get_next_index(playlist, i, -1);
2498 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2500 remove_track_from_playlist(playlist, index, true);
2501 steps--; /* one less track */
2506 index = get_next_index(playlist, steps, -1);
2508 if (index < 0)
2510 /* end of playlist... or is it */
2511 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2512 playlist->amount > 1)
2514 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2515 playlist->first_index = global_status.resume_first_index = 0;
2516 sort_playlist(playlist, false, false);
2517 randomise_playlist(playlist, current_tick, false, true);
2518 #if CONFIG_CODEC != SWCODEC
2519 playlist_start(0, 0);
2520 #endif
2521 playlist->index = 0;
2522 index = 0;
2524 else if (playlist->in_ram && global_settings.next_folder)
2526 char dir[MAX_PATH+1];
2528 changing_dir = true;
2529 if (steps > 0)
2531 if (!get_next_directory(dir))
2533 /* start playing next directory */
2534 if (playlist_create(dir, NULL) != -1)
2536 ft_build_playlist(tree_get_context(), 0);
2537 if (global_settings.playlist_shuffle)
2538 playlist_shuffle(current_tick, -1);
2539 #if CONFIG_CODEC != SWCODEC
2540 playlist_start(0, 0);
2541 #endif
2542 playlist->index = index = 0;
2546 else
2548 if (!get_previous_directory(dir))
2550 /* start playing previous directory */
2551 if (playlist_create(dir, NULL) != -1)
2553 ft_build_playlist(tree_get_context(), 0);
2554 if (global_settings.playlist_shuffle)
2555 playlist_shuffle(current_tick, -1);
2556 #if CONFIG_CODEC != SWCODEC
2557 playlist_start(current_playlist.amount-1, 0);
2558 #endif
2559 playlist->index = index = current_playlist.amount - 1;
2563 changing_dir = false;
2566 return index;
2569 playlist->index = index;
2571 if (playlist->last_insert_pos >= 0 && steps > 0)
2573 /* check to see if we've gone beyond the last inserted track */
2574 int cur = rotate_index(playlist, index);
2575 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2577 if (cur > last_pos)
2579 /* reset last inserted track */
2580 playlist->last_insert_pos = -1;
2582 if (playlist->control_fd >= 0)
2584 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2585 -1, -1, NULL, NULL, NULL);
2587 if (result < 0)
2588 return result;
2590 sync_control(playlist, false);
2595 return index;
2598 /* try playing next or previous folder */
2599 bool playlist_next_dir(int direction)
2601 char dir[MAX_PATH+1];
2602 bool result;
2603 int res;
2605 /* not to mess up real playlists */
2606 if(!current_playlist.in_ram)
2607 return false;
2609 if(changing_dir)
2610 return false;
2612 changing_dir = true;
2613 if(direction > 0)
2614 res = get_next_directory(dir);
2615 else
2616 res = get_previous_directory(dir);
2617 if (!res)
2619 if (playlist_create(dir, NULL) != -1)
2621 ft_build_playlist(tree_get_context(), 0);
2622 if (global_settings.playlist_shuffle)
2623 playlist_shuffle(current_tick, -1);
2624 #if (CONFIG_CODEC != SWCODEC)
2625 playlist_start(0,0);
2626 #endif
2627 result = true;
2629 else
2630 result = false;
2632 else
2633 result = false;
2635 changing_dir = false;
2637 return result;
2640 /* Get resume info for current playing song. If return value is -1 then
2641 settings shouldn't be saved. */
2642 int playlist_get_resume_info(int *resume_index)
2644 struct playlist_info* playlist = &current_playlist;
2646 *resume_index = playlist->index;
2648 return 0;
2651 /* Update resume info for current playing song. Returns -1 on error. */
2652 int playlist_update_resume_info(const struct mp3entry* id3)
2654 struct playlist_info* playlist = &current_playlist;
2656 if (id3)
2658 if (global_status.resume_index != playlist->index ||
2659 global_status.resume_offset != id3->offset)
2661 global_status.resume_index = playlist->index;
2662 global_status.resume_offset = id3->offset;
2663 status_save();
2666 else
2668 global_status.resume_index = -1;
2669 global_status.resume_offset = -1;
2670 status_save();
2673 return 0;
2676 /* Returns index of current playing track for display purposes. This value
2677 should not be used for resume purposes as it doesn't represent the actual
2678 index into the playlist */
2679 int playlist_get_display_index(void)
2681 struct playlist_info* playlist = &current_playlist;
2683 /* first_index should always be index 0 for display purposes */
2684 int index = rotate_index(playlist, playlist->index);
2686 return (index+1);
2689 /* returns number of tracks in current playlist */
2690 int playlist_amount(void)
2692 return playlist_amount_ex(NULL);
2696 * Create a new playlist If playlist is not NULL then we're loading a
2697 * playlist off disk for viewing/editing. The index_buffer is used to store
2698 * playlist indices (required for and only used if !current playlist). The
2699 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2701 int playlist_create_ex(struct playlist_info* playlist,
2702 const char* dir, const char* file,
2703 void* index_buffer, int index_buffer_size,
2704 void* temp_buffer, int temp_buffer_size)
2706 if (!playlist)
2707 playlist = &current_playlist;
2708 else
2710 /* Initialize playlist structure */
2711 int r = rand() % 10;
2712 playlist->current = false;
2714 /* Use random name for control file */
2715 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2716 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2717 playlist->fd = -1;
2718 playlist->control_fd = -1;
2720 if (index_buffer)
2722 int num_indices = index_buffer_size / sizeof(int);
2724 #ifdef HAVE_DIRCACHE
2725 num_indices /= 2;
2726 #endif
2727 if (num_indices > global_settings.max_files_in_playlist)
2728 num_indices = global_settings.max_files_in_playlist;
2730 playlist->max_playlist_size = num_indices;
2731 playlist->indices = index_buffer;
2732 #ifdef HAVE_DIRCACHE
2733 playlist->filenames = (const struct dircache_entry **)
2734 &playlist->indices[num_indices];
2735 #endif
2737 else
2739 playlist->max_playlist_size = current_playlist.max_playlist_size;
2740 playlist->indices = current_playlist.indices;
2741 #ifdef HAVE_DIRCACHE
2742 playlist->filenames = current_playlist.filenames;
2743 #endif
2746 playlist->buffer_size = 0;
2747 playlist->buffer = NULL;
2748 mutex_init(&playlist->control_mutex);
2751 new_playlist(playlist, dir, file);
2753 if (file)
2754 /* load the playlist file */
2755 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2757 return 0;
2761 * Set the specified playlist as the current.
2762 * NOTE: You will get undefined behaviour if something is already playing so
2763 * remember to stop before calling this. Also, this call will
2764 * effectively close your playlist, making it unusable.
2766 int playlist_set_current(struct playlist_info* playlist)
2768 if (!playlist || (check_control(playlist) < 0))
2769 return -1;
2771 empty_playlist(&current_playlist, false);
2773 strncpy(current_playlist.filename, playlist->filename,
2774 sizeof(current_playlist.filename));
2776 current_playlist.utf8 = playlist->utf8;
2777 current_playlist.fd = playlist->fd;
2779 close(playlist->control_fd);
2780 close(current_playlist.control_fd);
2781 remove(current_playlist.control_filename);
2782 if (rename(playlist->control_filename,
2783 current_playlist.control_filename) < 0)
2784 return -1;
2785 current_playlist.control_fd = open(current_playlist.control_filename,
2786 O_RDWR);
2787 if (current_playlist.control_fd < 0)
2788 return -1;
2789 current_playlist.control_created = true;
2791 current_playlist.dirlen = playlist->dirlen;
2793 if (playlist->indices && playlist->indices != current_playlist.indices)
2795 memcpy(current_playlist.indices, playlist->indices,
2796 playlist->max_playlist_size*sizeof(int));
2797 #ifdef HAVE_DIRCACHE
2798 memcpy(current_playlist.filenames, playlist->filenames,
2799 playlist->max_playlist_size*sizeof(int));
2800 #endif
2803 current_playlist.first_index = playlist->first_index;
2804 current_playlist.amount = playlist->amount;
2805 current_playlist.last_insert_pos = playlist->last_insert_pos;
2806 current_playlist.seed = playlist->seed;
2807 current_playlist.shuffle_modified = playlist->shuffle_modified;
2808 current_playlist.deleted = playlist->deleted;
2809 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2811 memcpy(current_playlist.control_cache, playlist->control_cache,
2812 sizeof(current_playlist.control_cache));
2813 current_playlist.num_cached = playlist->num_cached;
2814 current_playlist.pending_control_sync = playlist->pending_control_sync;
2816 return 0;
2820 * Close files and delete control file for non-current playlist.
2822 void playlist_close(struct playlist_info* playlist)
2824 if (!playlist)
2825 return;
2827 if (playlist->fd >= 0)
2828 close(playlist->fd);
2830 if (playlist->control_fd >= 0)
2831 close(playlist->control_fd);
2833 if (playlist->control_created)
2834 remove(playlist->control_filename);
2837 void playlist_sync(struct playlist_info* playlist)
2839 if (!playlist)
2840 playlist = &current_playlist;
2842 sync_control(playlist, false);
2843 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2844 audio_flush_and_reload_tracks();
2846 #ifdef HAVE_DIRCACHE
2847 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2848 #endif
2852 * Insert track into playlist at specified position (or one of the special
2853 * positions). Returns position where track was inserted or -1 if error.
2855 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2856 int position, bool queue, bool sync)
2858 int result;
2860 if (!playlist)
2861 playlist = &current_playlist;
2863 if (check_control(playlist) < 0)
2865 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2866 return -1;
2869 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2871 /* Check if we want manually sync later. For example when adding
2872 * bunch of files from tagcache, syncing after every file wouldn't be
2873 * a good thing to do. */
2874 if (sync && result >= 0)
2875 playlist_sync(playlist);
2877 return result;
2881 * Insert all tracks from specified directory into playlist.
2883 int playlist_insert_directory(struct playlist_info* playlist,
2884 const char *dirname, int position, bool queue,
2885 bool recurse)
2887 int result;
2888 unsigned char *count_str;
2889 struct directory_search_context context;
2891 if (!playlist)
2892 playlist = &current_playlist;
2894 if (check_control(playlist) < 0)
2896 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2897 return -1;
2900 if (position == PLAYLIST_REPLACE)
2902 if (remove_all_tracks(playlist) == 0)
2903 position = PLAYLIST_INSERT_LAST;
2904 else
2905 return -1;
2908 if (queue)
2909 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2910 else
2911 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2913 display_playlist_count(0, count_str);
2915 context.playlist = playlist;
2916 context.position = position;
2917 context.queue = queue;
2918 context.count = 0;
2920 cpu_boost(true);
2922 result = playlist_directory_tracksearch(dirname, recurse,
2923 directory_search_callback, &context);
2925 sync_control(playlist, false);
2927 cpu_boost(false);
2929 display_playlist_count(context.count, count_str);
2931 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2932 audio_flush_and_reload_tracks();
2934 #ifdef HAVE_DIRCACHE
2935 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2936 #endif
2938 return result;
2942 * Insert all tracks from specified playlist into dynamic playlist.
2944 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2945 int position, bool queue)
2947 int fd;
2948 int max;
2949 char *temp_ptr;
2950 char *dir;
2951 unsigned char *count_str;
2952 char temp_buf[MAX_PATH+1];
2953 char trackname[MAX_PATH+1];
2954 int count = 0;
2955 int result = 0;
2957 if (!playlist)
2958 playlist = &current_playlist;
2960 if (check_control(playlist) < 0)
2962 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2963 return -1;
2966 fd = open(filename, O_RDONLY);
2967 if (fd < 0)
2969 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR));
2970 return -1;
2973 /* we need the directory name for formatting purposes */
2974 dir = filename;
2976 temp_ptr = strrchr(filename+1,'/');
2977 if (temp_ptr)
2978 *temp_ptr = 0;
2979 else
2980 dir = "/";
2982 if (queue)
2983 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2984 else
2985 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2987 display_playlist_count(count, count_str);
2989 if (position == PLAYLIST_REPLACE)
2991 if (remove_all_tracks(playlist) == 0)
2992 position = PLAYLIST_INSERT_LAST;
2993 else return -1;
2996 cpu_boost(true);
2998 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3000 /* user abort */
3001 if (action_userabort(TIMEOUT_NOBLOCK))
3002 break;
3004 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3006 int insert_pos;
3008 /* we need to format so that relative paths are correctly
3009 handled */
3010 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3011 dir) < 0)
3013 result = -1;
3014 break;
3017 insert_pos = add_track_to_playlist(playlist, trackname, position,
3018 queue, -1);
3020 if (insert_pos < 0)
3022 result = -1;
3023 break;
3026 /* Make sure tracks are inserted in correct order if user
3027 requests INSERT_FIRST */
3028 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3029 position = insert_pos + 1;
3031 count++;
3033 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3035 display_playlist_count(count, count_str);
3037 if (count == PLAYLIST_DISPLAY_COUNT &&
3038 (audio_status() & AUDIO_STATUS_PLAY) &&
3039 playlist->started)
3040 audio_flush_and_reload_tracks();
3044 /* let the other threads work */
3045 yield();
3048 close(fd);
3050 if (temp_ptr)
3051 *temp_ptr = '/';
3053 sync_control(playlist, false);
3055 cpu_boost(false);
3057 display_playlist_count(count, count_str);
3059 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3060 audio_flush_and_reload_tracks();
3062 #ifdef HAVE_DIRCACHE
3063 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3064 #endif
3066 return result;
3070 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3071 * we want to delete the current playing track.
3073 int playlist_delete(struct playlist_info* playlist, int index)
3075 int result = 0;
3077 if (!playlist)
3078 playlist = &current_playlist;
3080 if (check_control(playlist) < 0)
3082 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3083 return -1;
3086 if (index == PLAYLIST_DELETE_CURRENT)
3087 index = playlist->index;
3089 result = remove_track_from_playlist(playlist, index, true);
3091 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3092 playlist->started)
3093 audio_flush_and_reload_tracks();
3095 return result;
3099 * Move track at index to new_index. Tracks between the two are shifted
3100 * appropriately. Returns 0 on success and -1 on failure.
3102 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3104 int result;
3105 int seek;
3106 bool control_file;
3107 bool queue;
3108 bool current = false;
3109 int r;
3110 char filename[MAX_PATH];
3112 if (!playlist)
3113 playlist = &current_playlist;
3115 if (check_control(playlist) < 0)
3117 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3118 return -1;
3121 if (index == new_index)
3122 return -1;
3124 if (index == playlist->index)
3125 /* Moving the current track */
3126 current = true;
3128 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3129 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3130 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3132 if (get_filename(playlist, index, seek, control_file, filename,
3133 sizeof(filename)) < 0)
3134 return -1;
3136 /* Delete track from original position */
3137 result = remove_track_from_playlist(playlist, index, true);
3139 if (result != -1)
3141 /* We want to insert the track at the position that was specified by
3142 new_index. This may be different then new_index because of the
3143 shifting that occurred after the delete */
3144 r = rotate_index(playlist, new_index);
3146 if (r == 0)
3147 /* First index */
3148 new_index = PLAYLIST_PREPEND;
3149 else if (r == playlist->amount)
3150 /* Append */
3151 new_index = PLAYLIST_INSERT_LAST;
3152 else
3153 /* Calculate index of desired position */
3154 new_index = (r+playlist->first_index)%playlist->amount;
3156 result = add_track_to_playlist(playlist, filename, new_index, queue,
3157 -1);
3159 if (result != -1)
3161 if (current)
3163 /* Moved the current track */
3164 switch (new_index)
3166 case PLAYLIST_PREPEND:
3167 playlist->index = playlist->first_index;
3168 break;
3169 case PLAYLIST_INSERT_LAST:
3170 playlist->index = playlist->first_index - 1;
3171 if (playlist->index < 0)
3172 playlist->index += playlist->amount;
3173 break;
3174 default:
3175 playlist->index = new_index;
3176 break;
3180 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3181 audio_flush_and_reload_tracks();
3185 #ifdef HAVE_DIRCACHE
3186 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3187 #endif
3189 return result;
3192 /* shuffle currently playing playlist */
3193 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3194 bool start_current)
3196 int result;
3198 if (!playlist)
3199 playlist = &current_playlist;
3201 check_control(playlist);
3203 result = randomise_playlist(playlist, seed, start_current, true);
3205 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3206 playlist->started)
3207 audio_flush_and_reload_tracks();
3209 return result;
3212 /* sort currently playing playlist */
3213 int playlist_sort(struct playlist_info* playlist, bool start_current)
3215 int result;
3217 if (!playlist)
3218 playlist = &current_playlist;
3220 check_control(playlist);
3222 result = sort_playlist(playlist, start_current, true);
3224 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3225 playlist->started)
3226 audio_flush_and_reload_tracks();
3228 return result;
3231 /* returns true if playlist has been modified */
3232 bool playlist_modified(const struct playlist_info* playlist)
3234 if (!playlist)
3235 playlist = &current_playlist;
3237 if (playlist->shuffle_modified ||
3238 playlist->deleted ||
3239 playlist->num_inserted_tracks > 0)
3240 return true;
3242 return false;
3245 /* returns index of first track in playlist */
3246 int playlist_get_first_index(const struct playlist_info* playlist)
3248 if (!playlist)
3249 playlist = &current_playlist;
3251 return playlist->first_index;
3254 /* returns shuffle seed of playlist */
3255 int playlist_get_seed(const struct playlist_info* playlist)
3257 if (!playlist)
3258 playlist = &current_playlist;
3260 return playlist->seed;
3263 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3264 int playlist_amount_ex(const struct playlist_info* playlist)
3266 if (!playlist)
3267 playlist = &current_playlist;
3269 return playlist->amount;
3272 /* returns full path of playlist (minus extension) */
3273 char *playlist_name(const struct playlist_info* playlist, char *buf,
3274 int buf_size)
3276 char *sep;
3278 if (!playlist)
3279 playlist = &current_playlist;
3281 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3283 if (!buf[0])
3284 return NULL;
3286 /* Remove extension */
3287 sep = strrchr(buf, '.');
3288 if (sep)
3289 *sep = 0;
3291 return buf;
3294 /* returns the playlist filename */
3295 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3296 int buf_size)
3298 if (!playlist)
3299 playlist = &current_playlist;
3301 snprintf(buf, buf_size, "%s", playlist->filename);
3303 if (!buf[0])
3304 return NULL;
3306 return buf;
3309 /* Fills info structure with information about track at specified index.
3310 Returns 0 on success and -1 on failure */
3311 int playlist_get_track_info(struct playlist_info* playlist, int index,
3312 struct playlist_track_info* info)
3314 int seek;
3315 bool control_file;
3317 if (!playlist)
3318 playlist = &current_playlist;
3320 if (index < 0 || index >= playlist->amount)
3321 return -1;
3323 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3324 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3326 if (get_filename(playlist, index, seek, control_file, info->filename,
3327 sizeof(info->filename)) < 0)
3328 return -1;
3330 info->attr = 0;
3332 if (control_file)
3334 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3335 info->attr |= PLAYLIST_ATTR_QUEUED;
3336 else
3337 info->attr |= PLAYLIST_ATTR_INSERTED;
3341 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3342 info->attr |= PLAYLIST_ATTR_SKIPPED;
3344 info->index = index;
3345 info->display_index = rotate_index(playlist, index) + 1;
3347 return 0;
3350 /* save the current dynamic playlist to specified file */
3351 int playlist_save(struct playlist_info* playlist, char *filename)
3353 int fd;
3354 int i, index;
3355 int count = 0;
3356 char path[MAX_PATH+1];
3357 char tmp_buf[MAX_PATH+1];
3358 int result = 0;
3359 bool overwrite_current = false;
3360 int* index_buf = NULL;
3362 if (!playlist)
3363 playlist = &current_playlist;
3365 if (playlist->amount <= 0)
3366 return -1;
3368 /* use current working directory as base for pathname */
3369 if (format_track_path(path, filename, sizeof(tmp_buf),
3370 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3371 return -1;
3373 if (!strncmp(playlist->filename, path, strlen(path)))
3375 /* Attempting to overwrite current playlist file.*/
3377 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3379 /* not enough buffer space to store updated indices */
3380 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR));
3381 return -1;
3384 /* in_ram buffer is unused for m3u files so we'll use for storing
3385 updated indices */
3386 index_buf = (int*)playlist->buffer;
3388 /* use temporary pathname */
3389 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3390 overwrite_current = true;
3393 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3394 if (fd < 0)
3396 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR));
3397 return -1;
3400 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3402 cpu_boost(true);
3404 index = playlist->first_index;
3405 for (i=0; i<playlist->amount; i++)
3407 bool control_file;
3408 bool queue;
3409 int seek;
3411 /* user abort */
3412 if (action_userabort(TIMEOUT_NOBLOCK))
3414 result = -1;
3415 break;
3418 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3419 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3420 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3422 /* Don't save queued files */
3423 if (!queue)
3425 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3426 MAX_PATH+1) < 0)
3428 result = -1;
3429 break;
3432 if (overwrite_current)
3433 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3435 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3437 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR));
3438 result = -1;
3439 break;
3442 count++;
3444 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3445 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3447 yield();
3450 index = (index+1)%playlist->amount;
3453 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3455 close(fd);
3457 if (overwrite_current && result >= 0)
3459 result = -1;
3461 mutex_lock(&playlist->control_mutex);
3463 /* Replace the current playlist with the new one and update indices */
3464 close(playlist->fd);
3465 if (remove(playlist->filename) >= 0)
3467 if (rename(path, playlist->filename) >= 0)
3469 playlist->fd = open(playlist->filename, O_RDONLY);
3470 if (playlist->fd >= 0)
3472 index = playlist->first_index;
3473 for (i=0, count=0; i<playlist->amount; i++)
3475 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3477 playlist->indices[index] = index_buf[count];
3478 count++;
3480 index = (index+1)%playlist->amount;
3483 /* we need to recreate control because inserted tracks are
3484 now part of the playlist and shuffle has been
3485 invalidated */
3486 result = recreate_control(playlist);
3491 mutex_unlock(&playlist->control_mutex);
3495 cpu_boost(false);
3497 return result;
3501 * Search specified directory for tracks and notify via callback. May be
3502 * called recursively.
3504 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3505 int (*callback)(char*, void*),
3506 void* context)
3508 char buf[MAX_PATH+1];
3509 int result = 0;
3510 int num_files = 0;
3511 int i;
3512 struct entry *files;
3513 struct tree_context* tc = tree_get_context();
3514 int old_dirfilter = *(tc->dirfilter);
3516 if (!callback)
3517 return -1;
3519 /* use the tree browser dircache to load files */
3520 *(tc->dirfilter) = SHOW_ALL;
3522 if (ft_load(tc, dirname) < 0)
3524 gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3525 *(tc->dirfilter) = old_dirfilter;
3526 return -1;
3529 files = (struct entry*) tc->dircache;
3530 num_files = tc->filesindir;
3532 /* we've overwritten the dircache so tree browser will need to be
3533 reloaded */
3534 reload_directory();
3536 for (i=0; i<num_files; i++)
3538 /* user abort */
3539 if (action_userabort(TIMEOUT_NOBLOCK))
3541 result = -1;
3542 break;
3545 if (files[i].attr & ATTR_DIRECTORY)
3547 if (recurse)
3549 /* recursively add directories */
3550 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3551 result = playlist_directory_tracksearch(buf, recurse,
3552 callback, context);
3553 if (result < 0)
3554 break;
3556 /* we now need to reload our current directory */
3557 if(ft_load(tc, dirname) < 0)
3559 result = -1;
3560 break;
3563 files = (struct entry*) tc->dircache;
3564 num_files = tc->filesindir;
3565 if (!num_files)
3567 result = -1;
3568 break;
3571 else
3572 continue;
3574 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3576 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3578 if (callback(buf, context) != 0)
3580 result = -1;
3581 break;
3584 /* let the other threads work */
3585 yield();
3589 /* restore dirfilter */
3590 *(tc->dirfilter) = old_dirfilter;
3592 return result;