iPod 5G: Split lcd_bcm_finishup() function into two halves, and incorporate into...
[Rockbox.git] / apps / playlist.c
blob84160195b761c331862fbfca62f1367b5d17cb48
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;
461 if(-1 == playlist->fd)
462 playlist->fd = open(playlist->filename, O_RDONLY);
463 if(playlist->fd < 0)
464 return -1; /* failure */
466 #ifdef HAVE_LCD_BITMAP
467 if(global_settings.statusbar)
468 lcd_setmargins(0, STATUSBAR_HEIGHT);
469 else
470 lcd_setmargins(0, 0);
471 #endif
473 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD));
475 if (!buffer)
477 /* use mp3 buffer for maximum load speed */
478 audio_stop();
479 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
481 buffer = (char *)audiobuf;
482 buflen = (audiobufend - audiobuf);
485 store_index = true;
487 while(1)
489 nread = read(playlist->fd, buffer, buflen);
490 /* Terminate on EOF */
491 if(nread <= 0)
492 break;
494 p = (unsigned char *)buffer;
496 for(count=0; count < nread; count++,p++) {
498 /* Are we on a new line? */
499 if((*p == '\n') || (*p == '\r'))
501 store_index = true;
503 else if(store_index)
505 store_index = false;
507 if(*p != '#')
509 /* Store a new entry */
510 playlist->indices[ playlist->amount ] = i+count;
511 #ifdef HAVE_DIRCACHE
512 if (playlist->filenames)
513 playlist->filenames[ playlist->amount ] = NULL;
514 #endif
515 if ( playlist->amount >= playlist->max_playlist_size ) {
516 display_buffer_full();
517 return -1;
519 playlist->amount++;
524 i+= count;
527 #ifdef HAVE_DIRCACHE
528 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
529 #endif
531 return 0;
535 * Add track to playlist at specified position. There are five special
536 * positions that can be specified:
537 * PLAYLIST_PREPEND - Add track at beginning of playlist
538 * PLAYLIST_INSERT - Add track after current song. NOTE: If
539 * there are already inserted tracks then track
540 * is added to the end of the insertion list
541 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
542 * matter what other tracks have been inserted
543 * PLAYLIST_INSERT_LAST - Add track to end of playlist
544 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
545 * current playing track and end of playlist
547 static int add_track_to_playlist(struct playlist_info* playlist,
548 const char *filename, int position,
549 bool queue, int seek_pos)
551 int insert_position = position;
552 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
553 int i;
555 if (playlist->amount >= playlist->max_playlist_size)
557 display_buffer_full();
558 return -1;
561 switch (position)
563 case PLAYLIST_PREPEND:
564 insert_position = playlist->first_index;
565 flags = PLAYLIST_INSERT_TYPE_PREPEND;
566 break;
567 case PLAYLIST_INSERT:
568 /* if there are already inserted tracks then add track to end of
569 insertion list else add after current playing track */
570 if (playlist->last_insert_pos >= 0 &&
571 playlist->last_insert_pos < playlist->amount &&
572 (playlist->indices[playlist->last_insert_pos]&
573 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
574 position = insert_position = playlist->last_insert_pos+1;
575 else if (playlist->amount > 0)
576 position = insert_position = playlist->index + 1;
577 else
578 position = insert_position = 0;
580 playlist->last_insert_pos = position;
581 break;
582 case PLAYLIST_INSERT_FIRST:
583 if (playlist->amount > 0)
584 position = insert_position = playlist->index + 1;
585 else
586 position = insert_position = 0;
588 if (playlist->last_insert_pos < 0)
589 playlist->last_insert_pos = position;
590 break;
591 case PLAYLIST_INSERT_LAST:
592 if (playlist->first_index > 0)
593 insert_position = playlist->first_index;
594 else
595 insert_position = playlist->amount;
597 flags = PLAYLIST_INSERT_TYPE_APPEND;
598 break;
599 case PLAYLIST_INSERT_SHUFFLED:
601 int offset;
602 int n = playlist->amount -
603 rotate_index(playlist, playlist->index);
605 if (n > 0)
606 offset = rand() % n;
607 else
608 offset = 0;
610 position = playlist->index + offset + 1;
611 if (position >= playlist->amount)
612 position -= playlist->amount;
614 insert_position = position;
615 break;
619 if (queue)
620 flags |= PLAYLIST_QUEUED;
622 /* shift indices so that track can be added */
623 for (i=playlist->amount; i>insert_position; i--)
625 playlist->indices[i] = playlist->indices[i-1];
626 #ifdef HAVE_DIRCACHE
627 if (playlist->filenames)
628 playlist->filenames[i] = playlist->filenames[i-1];
629 #endif
632 /* update stored indices if needed */
633 if (playlist->amount > 0 && insert_position <= playlist->index)
634 playlist->index++;
636 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
637 position != PLAYLIST_PREPEND)
639 playlist->first_index++;
641 if (seek_pos < 0 && playlist->current)
643 global_settings.resume_first_index = playlist->first_index;
644 settings_save();
648 if (insert_position < playlist->last_insert_pos ||
649 (insert_position == playlist->last_insert_pos && position < 0))
650 playlist->last_insert_pos++;
652 if (seek_pos < 0 && playlist->control_fd >= 0)
654 int result = update_control(playlist,
655 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
656 playlist->last_insert_pos, filename, NULL, &seek_pos);
658 if (result < 0)
659 return result;
662 playlist->indices[insert_position] = flags | seek_pos;
664 #ifdef HAVE_DIRCACHE
665 if (playlist->filenames)
666 playlist->filenames[insert_position] = NULL;
667 #endif
669 playlist->amount++;
670 playlist->num_inserted_tracks++;
672 return insert_position;
676 * Insert directory into playlist. May be called recursively.
678 static int add_directory_to_playlist(struct playlist_info* playlist,
679 const char *dirname, int *position,
680 bool queue, int *count, bool recurse)
682 char buf[MAX_PATH+1];
683 unsigned char *count_str;
684 int result = 0;
685 int num_files = 0;
686 int i;
687 int dirfilter = global_settings.dirfilter;
688 struct entry *files;
689 struct tree_context* tc = tree_get_context();
691 /* use the tree browser dircache to load files */
692 global_settings.dirfilter = SHOW_ALL;
694 if (ft_load(tc, dirname) < 0)
696 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
697 global_settings.dirfilter = dirfilter;
698 return -1;
701 files = (struct entry*) tc->dircache;
702 num_files = tc->filesindir;
704 /* we've overwritten the dircache so tree browser will need to be
705 reloaded */
706 reload_directory();
708 if (queue)
709 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
710 else
711 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
713 for (i=0; i<num_files; i++)
715 /* user abort */
716 if (button_get(false) == SETTINGS_CANCEL)
718 result = -1;
719 break;
722 if (files[i].attr & ATTR_DIRECTORY)
724 if (recurse)
726 /* recursively add directories */
727 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
728 result = add_directory_to_playlist(playlist, buf, position,
729 queue, count, recurse);
730 if (result < 0)
731 break;
733 /* we now need to reload our current directory */
734 if(ft_load(tc, dirname) < 0)
736 result = -1;
737 break;
740 files = (struct entry*) tc->dircache;
741 num_files = tc->filesindir;
742 if (!num_files)
744 result = -1;
745 break;
748 else
749 continue;
751 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
753 int insert_pos;
755 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
757 insert_pos = add_track_to_playlist(playlist, buf, *position,
758 queue, -1);
759 if (insert_pos < 0)
761 result = -1;
762 break;
765 (*count)++;
767 /* Make sure tracks are inserted in correct order if user requests
768 INSERT_FIRST */
769 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
770 *position = insert_pos + 1;
772 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
774 display_playlist_count(*count, count_str);
776 if (*count == PLAYLIST_DISPLAY_COUNT &&
777 (audio_status() & AUDIO_STATUS_PLAY))
778 audio_flush_and_reload_tracks();
781 /* let the other threads work */
782 yield();
786 /* restore dirfilter */
787 global_settings.dirfilter = dirfilter;
789 return result;
793 * remove track at specified position
795 static int remove_track_from_playlist(struct playlist_info* playlist,
796 int position, bool write)
798 int i;
799 bool inserted;
801 if (playlist->amount <= 0)
802 return -1;
804 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
806 /* shift indices now that track has been removed */
807 for (i=position; i<playlist->amount; i++)
809 playlist->indices[i] = playlist->indices[i+1];
810 #ifdef HAVE_DIRCACHE
811 if (playlist->filenames)
812 playlist->filenames[i] = playlist->filenames[i+1];
813 #endif
816 playlist->amount--;
818 if (inserted)
819 playlist->num_inserted_tracks--;
820 else
821 playlist->deleted = true;
823 /* update stored indices if needed */
824 if (position < playlist->index)
825 playlist->index--;
827 if (position < playlist->first_index)
829 playlist->first_index--;
831 if (write)
833 global_settings.resume_first_index = playlist->first_index;
834 settings_save();
838 if (position <= playlist->last_insert_pos)
839 playlist->last_insert_pos--;
841 if (write && playlist->control_fd >= 0)
843 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
844 position, -1, NULL, NULL, NULL);
846 if (result < 0)
847 return result;
849 sync_control(playlist, false);
852 return 0;
856 * randomly rearrange the array of indices for the playlist. If start_current
857 * is true then update the index to the new index of the current playing track
859 static int randomise_playlist(struct playlist_info* playlist,
860 unsigned int seed, bool start_current,
861 bool write)
863 int count;
864 int candidate;
865 int store;
866 unsigned int current = playlist->indices[playlist->index];
868 /* seed 0 is used to identify sorted playlist for resume purposes */
869 if (seed == 0)
870 seed = 1;
872 /* seed with the given seed */
873 srand(seed);
875 /* randomise entire indices list */
876 for(count = playlist->amount - 1; count >= 0; count--)
878 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
879 candidate = rand() % (count + 1);
881 /* now swap the values at the 'count' and 'candidate' positions */
882 store = playlist->indices[candidate];
883 playlist->indices[candidate] = playlist->indices[count];
884 playlist->indices[count] = store;
885 #ifdef HAVE_DIRCACHE
886 if (playlist->filenames)
888 store = (int)playlist->filenames[candidate];
889 playlist->filenames[candidate] = playlist->filenames[count];
890 playlist->filenames[count] = (struct dircache_entry *)store;
892 #endif
895 if (start_current)
896 find_and_set_playlist_index(playlist, current);
898 /* indices have been moved so last insert position is no longer valid */
899 playlist->last_insert_pos = -1;
901 playlist->seed = seed;
902 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
903 playlist->shuffle_modified = true;
905 if (write)
907 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
908 playlist->first_index, NULL, NULL, NULL);
909 global_settings.resume_seed = seed;
910 settings_save();
913 return 0;
917 * Sort the array of indices for the playlist. If start_current is true then
918 * set the index to the new index of the current song.
920 static int sort_playlist(struct playlist_info* playlist, bool start_current,
921 bool write)
923 unsigned int current = playlist->indices[playlist->index];
925 if (playlist->amount > 0)
926 qsort(playlist->indices, playlist->amount,
927 sizeof(playlist->indices[0]), compare);
929 #ifdef HAVE_DIRCACHE
930 /** We need to re-check the song names from disk because qsort can't
931 * sort two arrays at once :/
932 * FIXME: Please implement a better way to do this. */
933 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
934 #endif
936 if (start_current)
937 find_and_set_playlist_index(playlist, current);
939 /* indices have been moved so last insert position is no longer valid */
940 playlist->last_insert_pos = -1;
942 if (!playlist->num_inserted_tracks && !playlist->deleted)
943 playlist->shuffle_modified = false;
944 if (write && playlist->control_fd >= 0)
946 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
947 playlist->first_index, -1, NULL, NULL, NULL);
948 global_settings.resume_seed = 0;
949 settings_save();
952 return 0;
955 /* Calculate how many steps we have to really step when skipping entries
956 * marked as bad.
958 static int calculate_step_count(const struct playlist_info *playlist, int steps)
960 int i, count, direction;
961 int index;
962 int stepped_count = 0;
964 if (steps < 0)
966 direction = -1;
967 count = -steps;
969 else
971 direction = 1;
972 count = steps;
975 index = playlist->index;
976 i = 0;
977 do {
978 /* Boundary check */
979 if (index < 0)
980 index += playlist->amount;
981 if (index >= playlist->amount)
982 index -= playlist->amount;
984 /* Check if we found a bad entry. */
985 if (playlist->indices[index] & PLAYLIST_SKIPPED)
987 steps += direction;
988 /* Are all entries bad? */
989 if (stepped_count++ > playlist->amount)
990 break ;
992 else
993 i++;
995 index += direction;
996 } while (i <= count);
998 return steps;
1001 /* Marks the index of the track to be skipped that is "steps" away from
1002 * current playing track.
1004 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1006 int index;
1008 if (playlist == NULL)
1009 playlist = &current_playlist;
1011 index = rotate_index(playlist, playlist->index);
1012 /* We should also skip already skipped entries before the entry to be skipepd. */
1013 index += calculate_step_count(playlist, steps);
1014 if (index < 0 || index >= playlist->amount)
1015 return ;
1017 index = (index+playlist->first_index) % playlist->amount;
1018 playlist->indices[index] |= PLAYLIST_SKIPPED;
1022 * returns the index of the track that is "steps" away from current playing
1023 * track.
1025 static int get_next_index(const struct playlist_info* playlist, int steps,
1026 int repeat_mode)
1028 int current_index = playlist->index;
1029 int next_index = -1;
1031 if (playlist->amount <= 0)
1032 return -1;
1034 if (repeat_mode == -1)
1035 repeat_mode = global_settings.repeat_mode;
1037 if (repeat_mode == REPEAT_SHUFFLE &&
1038 (!global_settings.playlist_shuffle || playlist->amount <= 1))
1039 repeat_mode = REPEAT_ALL;
1041 steps = calculate_step_count(playlist, steps);
1042 switch (repeat_mode)
1044 case REPEAT_SHUFFLE:
1045 /* Treat repeat shuffle just like repeat off. At end of playlist,
1046 play will be resumed in playlist_next() */
1047 case REPEAT_OFF:
1049 current_index = rotate_index(playlist, current_index);
1050 next_index = current_index+steps;
1051 if ((next_index < 0) || (next_index >= playlist->amount))
1052 next_index = -1;
1053 else
1054 next_index = (next_index+playlist->first_index) %
1055 playlist->amount;
1057 break;
1060 case REPEAT_ONE:
1061 #if (AB_REPEAT_ENABLE == 1)
1062 case REPEAT_AB:
1063 #endif
1064 next_index = current_index;
1065 break;
1067 case REPEAT_ALL:
1068 default:
1070 next_index = (current_index+steps) % playlist->amount;
1071 while (next_index < 0)
1072 next_index += playlist->amount;
1074 if (steps >= playlist->amount)
1076 int i, index;
1078 index = next_index;
1079 next_index = -1;
1081 /* second time around so skip the queued files */
1082 for (i=0; i<playlist->amount; i++)
1084 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1085 index = (index+1) % playlist->amount;
1086 else
1088 next_index = index;
1089 break;
1093 break;
1097 /* No luck if the whole playlist was bad. */
1098 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1099 return -1;
1101 return next_index;
1105 * Search for the seek track and set appropriate indices. Used after shuffle
1106 * to make sure the current index is still pointing to correct track.
1108 static void find_and_set_playlist_index(struct playlist_info* playlist,
1109 unsigned int seek)
1111 int i;
1113 /* Set the index to the current song */
1114 for (i=0; i<playlist->amount; i++)
1116 if (playlist->indices[i] == seek)
1118 playlist->index = playlist->first_index = i;
1120 if (playlist->current)
1122 global_settings.resume_first_index = i;
1123 settings_save();
1126 break;
1132 * used to sort track indices. Sort order is as follows:
1133 * 1. Prepended tracks (in prepend order)
1134 * 2. Playlist/directory tracks (in playlist order)
1135 * 3. Inserted/Appended tracks (in insert order)
1137 static int compare(const void* p1, const void* p2)
1139 unsigned long* e1 = (unsigned long*) p1;
1140 unsigned long* e2 = (unsigned long*) p2;
1141 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1142 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1144 if (flags1 == flags2)
1145 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1146 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1147 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1148 return -1;
1149 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1150 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1151 return 1;
1152 else if (flags1 && flags2)
1153 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1154 else
1155 return *e1 - *e2;
1158 #ifdef HAVE_DIRCACHE
1160 * Thread to update filename pointers to dircache on background
1161 * without affecting playlist load up performance. This thread also flushes
1162 * any pending control commands when the disk spins up.
1164 static void playlist_thread(void)
1166 struct event ev;
1167 bool dirty_pointers = false;
1168 static char tmp[MAX_PATH+1];
1170 struct playlist_info *playlist;
1171 int index;
1172 int seek;
1173 bool control_file;
1175 int sleep_time = 5;
1177 if (global_settings.disk_spindown > 1 &&
1178 global_settings.disk_spindown <= 5)
1179 sleep_time = global_settings.disk_spindown - 1;
1181 while (1)
1183 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1185 switch (ev.id)
1187 case PLAYLIST_LOAD_POINTERS:
1188 dirty_pointers = true;
1189 break ;
1191 /* Start the background scanning after either the disk spindown
1192 timeout or 5s, whichever is less */
1193 case SYS_TIMEOUT:
1194 playlist = &current_playlist;
1196 if (playlist->control_fd >= 0 && ata_disk_is_active())
1198 if (playlist->num_cached > 0)
1199 flush_cached_control(playlist);
1201 sync_control(playlist, true);
1204 if (!dirty_pointers)
1205 break ;
1207 if (!dircache_is_enabled() || !playlist->filenames
1208 || playlist->amount <= 0)
1209 break ;
1211 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1212 cpu_boost(true);
1213 #endif
1214 for (index = 0; index < playlist->amount
1215 && queue_empty(&playlist_queue); index++)
1217 /* Process only pointers that are not already loaded. */
1218 if (playlist->filenames[index])
1219 continue ;
1221 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1222 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1224 /* Load the filename from playlist file. */
1225 if (get_filename(playlist, index, seek, control_file, tmp,
1226 sizeof(tmp)) < 0)
1227 break ;
1229 /* Set the dircache entry pointer. */
1230 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1232 /* And be on background so user doesn't notice any delays. */
1233 yield();
1236 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1237 cpu_boost(false);
1238 #endif
1239 dirty_pointers = false;
1240 break ;
1242 #ifndef SIMULATOR
1243 case SYS_USB_CONNECTED:
1244 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1245 usb_wait_for_disconnect(&playlist_queue);
1246 break ;
1247 #endif
1251 #endif
1254 * gets pathname for track at seek index
1256 static int get_filename(struct playlist_info* playlist, int index, int seek,
1257 bool control_file, char *buf, int buf_length)
1259 int fd;
1260 int max = -1;
1261 char tmp_buf[MAX_PATH+1];
1262 char dir_buf[MAX_PATH+1];
1264 if (buf_length > MAX_PATH+1)
1265 buf_length = MAX_PATH+1;
1267 #ifdef HAVE_DIRCACHE
1268 if (dircache_is_enabled() && playlist->filenames)
1270 if (playlist->filenames[index] != NULL)
1272 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1273 max = strlen(tmp_buf) + 1;
1276 #else
1277 (void)index;
1278 #endif
1280 if (playlist->in_ram && !control_file && max < 0)
1282 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1283 tmp_buf[MAX_PATH] = '\0';
1284 max = strlen(tmp_buf) + 1;
1286 else if (max < 0)
1288 mutex_lock(&playlist->control_mutex);
1290 if (control_file)
1291 fd = playlist->control_fd;
1292 else
1294 if(-1 == playlist->fd)
1295 playlist->fd = open(playlist->filename, O_RDONLY);
1297 fd = playlist->fd;
1300 if(-1 != fd)
1303 if (lseek(fd, seek, SEEK_SET) != seek)
1304 max = -1;
1305 else
1306 max = read(fd, tmp_buf, buf_length);
1309 mutex_unlock(&playlist->control_mutex);
1311 if (max < 0)
1313 if (control_file)
1314 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1315 else
1316 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1318 return max;
1322 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1323 dir_buf[playlist->dirlen-1] = 0;
1325 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1328 static int get_next_directory(char *dir){
1329 return get_next_dir(dir,true,false);
1332 static int get_previous_directory(char *dir){
1333 return get_next_dir(dir,false,false);
1337 * search through all the directories (starting with the current) to find
1338 * one that has tracks to play
1340 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1342 struct playlist_info* playlist = &current_playlist;
1343 int result = -1;
1344 int dirfilter = global_settings.dirfilter;
1345 int sort_dir = global_settings.sort_dir;
1346 char *start_dir = NULL;
1347 bool exit = false;
1348 struct tree_context* tc = tree_get_context();
1350 if (recursion){
1351 /* start with root */
1352 dir[0] = '\0';
1354 else{
1355 /* start with current directory */
1356 strncpy(dir, playlist->filename, playlist->dirlen-1);
1357 dir[playlist->dirlen-1] = '\0';
1360 /* use the tree browser dircache to load files */
1361 global_settings.dirfilter = SHOW_ALL;
1363 /* sort in another direction if previous dir is requested */
1364 if(!is_forward){
1365 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1366 global_settings.sort_dir = 4;
1367 else if (global_settings.sort_dir == 1)
1368 global_settings.sort_dir = 2;
1369 else if (global_settings.sort_dir == 2)
1370 global_settings.sort_dir = 1;
1371 else if (global_settings.sort_dir == 4)
1372 global_settings.sort_dir = 0;
1375 while (!exit)
1377 struct entry *files;
1378 int num_files = 0;
1379 int i;
1381 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1383 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1384 exit = true;
1385 result = -1;
1386 break;
1389 files = (struct entry*) tc->dircache;
1390 num_files = tc->filesindir;
1392 for (i=0; i<num_files; i++)
1394 /* user abort */
1395 if (button_get(false) == SETTINGS_CANCEL)
1397 result = -1;
1398 exit = true;
1399 break;
1402 if (files[i].attr & ATTR_DIRECTORY)
1404 if (!start_dir)
1406 result = check_subdir_for_music(dir, files[i].name);
1407 if (result != -1)
1409 exit = true;
1410 break;
1413 else if (!strcmp(start_dir, files[i].name))
1414 start_dir = NULL;
1418 if (!exit)
1420 /* move down to parent directory. current directory name is
1421 stored as the starting point for the search in parent */
1422 start_dir = strrchr(dir, '/');
1423 if (start_dir)
1425 *start_dir = '\0';
1426 start_dir++;
1428 else
1429 break;
1433 /* we've overwritten the dircache so tree browser will need to be
1434 reloaded */
1435 reload_directory();
1437 /* restore dirfilter & sort_dir */
1438 global_settings.dirfilter = dirfilter;
1439 global_settings.sort_dir = sort_dir;
1441 /* special case if nothing found: try start searching again from root */
1442 if (result == -1 && !recursion){
1443 result = get_next_dir(dir,is_forward, true);
1446 return result;
1450 * Checks if there are any music files in the dir or any of its
1451 * subdirectories. May be called recursively.
1453 static int check_subdir_for_music(char *dir, char *subdir)
1455 int result = -1;
1456 int dirlen = strlen(dir);
1457 int num_files = 0;
1458 int i;
1459 struct entry *files;
1460 bool has_music = false;
1461 bool has_subdir = false;
1462 struct tree_context* tc = tree_get_context();
1464 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1466 if (ft_load(tc, dir) < 0)
1468 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1469 return -2;
1472 files = (struct entry*) tc->dircache;
1473 num_files = tc->filesindir;
1475 for (i=0; i<num_files; i++)
1477 if (files[i].attr & ATTR_DIRECTORY)
1478 has_subdir = true;
1479 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
1481 has_music = true;
1482 break;
1486 if (has_music)
1487 return 0;
1489 if (has_subdir)
1491 for (i=0; i<num_files; i++)
1493 if (button_get(false) == SETTINGS_CANCEL)
1495 result = -2;
1496 break;
1499 if (files[i].attr & ATTR_DIRECTORY)
1501 result = check_subdir_for_music(dir, files[i].name);
1502 if (!result)
1503 break;
1508 if (result < 0)
1510 if (dirlen)
1512 dir[dirlen] = '\0';
1514 else
1516 strcpy(dir, "/");
1519 /* we now need to reload our current directory */
1520 if(ft_load(tc, dir) < 0)
1521 gui_syncsplash(HZ*2, true,
1522 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1525 return result;
1529 * Returns absolute path of track
1531 static int format_track_path(char *dest, char *src, int buf_length, int max,
1532 char *dir)
1534 int i = 0;
1535 int j;
1536 char *temp_ptr;
1538 /* Zero-terminate the file name */
1539 while((src[i] != '\n') &&
1540 (src[i] != '\r') &&
1541 (i < max))
1542 i++;
1544 /* Now work back killing white space */
1545 while((src[i-1] == ' ') ||
1546 (src[i-1] == '\t'))
1547 i--;
1549 src[i]=0;
1551 /* replace backslashes with forward slashes */
1552 for ( j=0; j<i; j++ )
1553 if ( src[j] == '\\' )
1554 src[j] = '/';
1556 if('/' == src[0])
1558 strncpy(dest, src, buf_length);
1560 else
1562 /* handle dos style drive letter */
1563 if (':' == src[1])
1564 strncpy(dest, &src[2], buf_length);
1565 else if (!strncmp(src, "../", 3))
1567 /* handle relative paths */
1568 i=3;
1569 while(!strncmp(&src[i], "../", 3))
1570 i += 3;
1571 for (j=0; j<i/3; j++) {
1572 temp_ptr = strrchr(dir, '/');
1573 if (temp_ptr)
1574 *temp_ptr = '\0';
1575 else
1576 break;
1578 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1580 else if ( '.' == src[0] && '/' == src[1] ) {
1581 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1583 else {
1584 snprintf(dest, buf_length, "%s/%s", dir, src);
1588 return 0;
1592 * Display splash message showing progress of playlist/directory insertion or
1593 * save.
1595 static void display_playlist_count(int count, const unsigned char *fmt)
1597 lcd_clear_display();
1599 #ifdef HAVE_LCD_BITMAP
1600 if(global_settings.statusbar)
1601 lcd_setmargins(0, STATUSBAR_HEIGHT);
1602 else
1603 lcd_setmargins(0, 0);
1604 #endif
1606 gui_syncsplash(0, true, fmt, count,
1607 #if CONFIG_KEYPAD == PLAYER_PAD
1608 str(LANG_STOP_ABORT)
1609 #else
1610 str(LANG_OFF_ABORT)
1611 #endif
1616 * Display buffer full message
1618 static void display_buffer_full(void)
1620 gui_syncsplash(HZ*2, true, (unsigned char *)"%s %s",
1621 str(LANG_PLAYINDICES_PLAYLIST),
1622 str(LANG_PLAYINDICES_BUFFER));
1626 * Flush any cached control commands to disk. Called when playlist is being
1627 * modified. Returns 0 on success and -1 on failure.
1629 static int flush_cached_control(struct playlist_info* playlist)
1631 int result = 0;
1632 int i;
1634 lseek(playlist->control_fd, 0, SEEK_END);
1636 for (i=0; i<playlist->num_cached; i++)
1638 struct playlist_control_cache* cache =
1639 &(playlist->control_cache[i]);
1641 switch (cache->command)
1643 case PLAYLIST_COMMAND_PLAYLIST:
1644 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1645 cache->i1, cache->s1, cache->s2);
1646 break;
1647 case PLAYLIST_COMMAND_ADD:
1648 case PLAYLIST_COMMAND_QUEUE:
1649 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1650 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1651 cache->i1, cache->i2);
1652 if (result > 0)
1654 /* save the position in file where name is written */
1655 int* seek_pos = (int *)cache->data;
1656 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1657 result = fdprintf(playlist->control_fd, "%s\n",
1658 cache->s1);
1660 break;
1661 case PLAYLIST_COMMAND_DELETE:
1662 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1663 break;
1664 case PLAYLIST_COMMAND_SHUFFLE:
1665 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1666 cache->i1, cache->i2);
1667 break;
1668 case PLAYLIST_COMMAND_UNSHUFFLE:
1669 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1670 break;
1671 case PLAYLIST_COMMAND_RESET:
1672 result = fdprintf(playlist->control_fd, "R\n");
1673 break;
1674 default:
1675 break;
1678 if (result <= 0)
1679 break;
1682 if (result > 0)
1684 if (global_settings.resume_seed >= 0)
1686 global_settings.resume_seed = -1;
1687 settings_save();
1690 playlist->num_cached = 0;
1691 playlist->pending_control_sync = true;
1693 result = 0;
1695 else
1696 result = -1;
1698 if (result < 0)
1700 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1701 return result;
1704 return result;
1708 * Update control data with new command. Depending on the command, it may be
1709 * cached or flushed to disk.
1711 static int update_control(struct playlist_info* playlist,
1712 enum playlist_command command, int i1, int i2,
1713 const char* s1, const char* s2, void* data)
1715 int result = 0;
1716 struct playlist_control_cache* cache;
1717 bool flush = false;
1719 mutex_lock(&playlist->control_mutex);
1721 cache = &(playlist->control_cache[playlist->num_cached++]);
1723 cache->command = command;
1724 cache->i1 = i1;
1725 cache->i2 = i2;
1726 cache->s1 = s1;
1727 cache->s2 = s2;
1728 cache->data = data;
1730 switch (command)
1732 case PLAYLIST_COMMAND_PLAYLIST:
1733 case PLAYLIST_COMMAND_ADD:
1734 case PLAYLIST_COMMAND_QUEUE:
1735 #ifndef HAVE_DIRCACHE
1736 case PLAYLIST_COMMAND_DELETE:
1737 case PLAYLIST_COMMAND_RESET:
1738 #endif
1739 flush = true;
1740 break;
1741 case PLAYLIST_COMMAND_SHUFFLE:
1742 case PLAYLIST_COMMAND_UNSHUFFLE:
1743 default:
1744 /* only flush when needed */
1745 break;
1748 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1749 result = flush_cached_control(playlist);
1751 mutex_unlock(&playlist->control_mutex);
1753 return result;
1757 * sync control file to disk
1759 static void sync_control(struct playlist_info* playlist, bool force)
1761 (void) force;
1762 #ifdef HAVE_DIRCACHE
1763 if (force)
1764 #endif
1766 if (playlist->pending_control_sync)
1768 mutex_lock(&playlist->control_mutex);
1769 fsync(playlist->control_fd);
1770 playlist->pending_control_sync = false;
1771 mutex_unlock(&playlist->control_mutex);
1777 * Rotate indices such that first_index is index 0
1779 static int rotate_index(const struct playlist_info* playlist, int index)
1781 index -= playlist->first_index;
1782 if (index < 0)
1783 index += playlist->amount;
1785 return index;
1789 * Initialize playlist entries at startup
1791 void playlist_init(void)
1793 struct playlist_info* playlist = &current_playlist;
1795 playlist->current = true;
1796 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1797 "%s", PLAYLIST_CONTROL_FILE);
1798 playlist->fd = -1;
1799 playlist->control_fd = -1;
1800 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1801 playlist->indices = buffer_alloc(
1802 playlist->max_playlist_size * sizeof(int));
1803 playlist->buffer_size =
1804 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1805 playlist->buffer = buffer_alloc(playlist->buffer_size);
1806 mutex_init(&playlist->control_mutex);
1807 empty_playlist(playlist, true);
1809 #ifdef HAVE_DIRCACHE
1810 playlist->filenames = buffer_alloc(
1811 playlist->max_playlist_size * sizeof(int));
1812 memset(playlist->filenames, 0,
1813 playlist->max_playlist_size * sizeof(int));
1814 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1815 playlist_thread_name);
1816 queue_init(&playlist_queue);
1817 #endif
1821 * Clean playlist at shutdown
1823 void playlist_shutdown(void)
1825 struct playlist_info* playlist = &current_playlist;
1827 if (playlist->control_fd >= 0)
1829 mutex_lock(&playlist->control_mutex);
1831 if (playlist->num_cached > 0)
1832 flush_cached_control(playlist);
1834 close(playlist->control_fd);
1836 mutex_unlock(&playlist->control_mutex);
1841 * Create new playlist
1843 int playlist_create(const char *dir, const char *file)
1845 struct playlist_info* playlist = &current_playlist;
1847 new_playlist(playlist, dir, file);
1849 if (file)
1850 /* load the playlist file */
1851 add_indices_to_playlist(playlist, NULL, 0);
1853 return 0;
1856 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1859 * Restore the playlist state based on control file commands. Called to
1860 * resume playback after shutdown.
1862 int playlist_resume(void)
1864 struct playlist_info* playlist = &current_playlist;
1865 char *buffer;
1866 int buflen;
1867 int nread;
1868 int total_read = 0;
1869 int control_file_size = 0;
1870 bool first = true;
1871 bool sorted = true;
1873 /* use mp3 buffer for maximum load speed */
1874 #if CONFIG_CODEC != SWCODEC
1875 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1876 buflen = (audiobufend - audiobuf);
1877 buffer = (char *)audiobuf;
1878 #else
1879 buflen = (audiobufend - audiobuf - talk_get_bufsize());
1880 buffer = (char *)&audiobuf[talk_get_bufsize()];
1881 #endif
1883 empty_playlist(playlist, true);
1885 gui_syncsplash(0, true, str(LANG_WAIT));
1886 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1887 if (playlist->control_fd < 0)
1889 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1890 return -1;
1892 playlist->control_created = true;
1894 control_file_size = filesize(playlist->control_fd);
1895 if (control_file_size <= 0)
1897 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1898 return -1;
1901 /* read a small amount first to get the header */
1902 nread = read(playlist->control_fd, buffer,
1903 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1904 if(nread <= 0)
1906 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1907 return -1;
1910 while (1)
1912 int result = 0;
1913 int count;
1914 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1915 int last_newline = 0;
1916 int str_count = -1;
1917 bool newline = true;
1918 bool exit_loop = false;
1919 char *p = buffer;
1920 char *str1 = NULL;
1921 char *str2 = NULL;
1922 char *str3 = NULL;
1924 for(count=0; count<nread && !exit_loop; count++,p++)
1926 /* Are we on a new line? */
1927 if((*p == '\n') || (*p == '\r'))
1929 *p = '\0';
1931 /* save last_newline in case we need to load more data */
1932 last_newline = count;
1934 switch (current_command)
1936 case PLAYLIST_COMMAND_PLAYLIST:
1938 /* str1=version str2=dir str3=file */
1939 int version;
1941 if (!str1)
1943 result = -1;
1944 exit_loop = true;
1945 break;
1948 if (!str2)
1949 str2 = "";
1951 if (!str3)
1952 str3 = "";
1954 version = atoi(str1);
1956 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1957 return -1;
1959 update_playlist_filename(playlist, str2, str3);
1961 if (str3[0] != '\0')
1963 /* NOTE: add_indices_to_playlist() overwrites the
1964 audiobuf so we need to reload control file
1965 data */
1966 add_indices_to_playlist(playlist, NULL, 0);
1968 else if (str2[0] != '\0')
1970 playlist->in_ram = true;
1971 resume_directory(str2);
1974 /* load the rest of the data */
1975 first = false;
1976 exit_loop = true;
1978 break;
1980 case PLAYLIST_COMMAND_ADD:
1981 case PLAYLIST_COMMAND_QUEUE:
1983 /* str1=position str2=last_position str3=file */
1984 int position, last_position;
1985 bool queue;
1987 if (!str1 || !str2 || !str3)
1989 result = -1;
1990 exit_loop = true;
1991 break;
1994 position = atoi(str1);
1995 last_position = atoi(str2);
1997 queue = (current_command == PLAYLIST_COMMAND_ADD)?
1998 false:true;
2000 /* seek position is based on str3's position in
2001 buffer */
2002 if (add_track_to_playlist(playlist, str3, position,
2003 queue, total_read+(str3-buffer)) < 0)
2004 return -1;
2006 playlist->last_insert_pos = last_position;
2008 break;
2010 case PLAYLIST_COMMAND_DELETE:
2012 /* str1=position */
2013 int position;
2015 if (!str1)
2017 result = -1;
2018 exit_loop = true;
2019 break;
2022 position = atoi(str1);
2024 if (remove_track_from_playlist(playlist, position,
2025 false) < 0)
2026 return -1;
2028 break;
2030 case PLAYLIST_COMMAND_SHUFFLE:
2032 /* str1=seed str2=first_index */
2033 int seed;
2035 if (!str1 || !str2)
2037 result = -1;
2038 exit_loop = true;
2039 break;
2042 if (!sorted)
2044 /* Always sort list before shuffling */
2045 sort_playlist(playlist, false, false);
2048 seed = atoi(str1);
2049 playlist->first_index = atoi(str2);
2051 if (randomise_playlist(playlist, seed, false,
2052 false) < 0)
2053 return -1;
2055 sorted = false;
2056 break;
2058 case PLAYLIST_COMMAND_UNSHUFFLE:
2060 /* str1=first_index */
2061 if (!str1)
2063 result = -1;
2064 exit_loop = true;
2065 break;
2068 playlist->first_index = atoi(str1);
2070 if (sort_playlist(playlist, false, false) < 0)
2071 return -1;
2073 sorted = true;
2074 break;
2076 case PLAYLIST_COMMAND_RESET:
2078 playlist->last_insert_pos = -1;
2079 break;
2081 case PLAYLIST_COMMAND_COMMENT:
2082 default:
2083 break;
2086 newline = true;
2088 /* to ignore any extra newlines */
2089 current_command = PLAYLIST_COMMAND_COMMENT;
2091 else if(newline)
2093 newline = false;
2095 /* first non-comment line must always specify playlist */
2096 if (first && *p != 'P' && *p != '#')
2098 result = -1;
2099 exit_loop = true;
2100 break;
2103 switch (*p)
2105 case 'P':
2106 /* playlist can only be specified once */
2107 if (!first)
2109 result = -1;
2110 exit_loop = true;
2111 break;
2114 current_command = PLAYLIST_COMMAND_PLAYLIST;
2115 break;
2116 case 'A':
2117 current_command = PLAYLIST_COMMAND_ADD;
2118 break;
2119 case 'Q':
2120 current_command = PLAYLIST_COMMAND_QUEUE;
2121 break;
2122 case 'D':
2123 current_command = PLAYLIST_COMMAND_DELETE;
2124 break;
2125 case 'S':
2126 current_command = PLAYLIST_COMMAND_SHUFFLE;
2127 break;
2128 case 'U':
2129 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2130 break;
2131 case 'R':
2132 current_command = PLAYLIST_COMMAND_RESET;
2133 break;
2134 case '#':
2135 current_command = PLAYLIST_COMMAND_COMMENT;
2136 break;
2137 default:
2138 result = -1;
2139 exit_loop = true;
2140 break;
2143 str_count = -1;
2144 str1 = NULL;
2145 str2 = NULL;
2146 str3 = NULL;
2148 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2150 /* all control file strings are separated with a colon.
2151 Replace the colon with 0 to get proper strings that can be
2152 used by commands above */
2153 if (*p == ':')
2155 *p = '\0';
2156 str_count++;
2158 if ((count+1) < nread)
2160 switch (str_count)
2162 case 0:
2163 str1 = p+1;
2164 break;
2165 case 1:
2166 str2 = p+1;
2167 break;
2168 case 2:
2169 str3 = p+1;
2170 break;
2171 default:
2172 /* allow last string to contain colons */
2173 *p = ':';
2174 break;
2181 if (result < 0)
2183 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2184 return result;
2187 if (!newline || (exit_loop && count<nread))
2189 if ((total_read + count) >= control_file_size)
2191 /* no newline at end of control file */
2192 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2193 return -1;
2196 /* We didn't end on a newline or we exited loop prematurely.
2197 Either way, re-read the remainder. */
2198 count = last_newline;
2199 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2202 total_read += count;
2204 if (first)
2205 /* still looking for header */
2206 nread = read(playlist->control_fd, buffer,
2207 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2208 else
2209 nread = read(playlist->control_fd, buffer, buflen);
2211 /* Terminate on EOF */
2212 if(nread <= 0)
2214 if (global_settings.resume_seed >= 0)
2216 /* Apply shuffle command saved in settings */
2217 if (global_settings.resume_seed == 0)
2218 sort_playlist(playlist, false, true);
2219 else
2221 if (!sorted)
2222 sort_playlist(playlist, false, false);
2224 randomise_playlist(playlist, global_settings.resume_seed,
2225 false, true);
2228 playlist->first_index = global_settings.resume_first_index;
2231 break;
2235 #ifdef HAVE_DIRCACHE
2236 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2237 #endif
2239 return 0;
2243 * Add track to in_ram playlist. Used when playing directories.
2245 int playlist_add(const char *filename)
2247 struct playlist_info* playlist = &current_playlist;
2248 int len = strlen(filename);
2250 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2251 (playlist->amount >= playlist->max_playlist_size))
2253 display_buffer_full();
2254 return -1;
2257 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2258 #ifdef HAVE_DIRCACHE
2259 playlist->filenames[playlist->amount] = NULL;
2260 #endif
2261 playlist->amount++;
2263 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2264 playlist->buffer_end_pos += len;
2265 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2267 return 0;
2270 /* shuffle newly created playlist using random seed. */
2271 int playlist_shuffle(int random_seed, int start_index)
2273 struct playlist_info* playlist = &current_playlist;
2275 unsigned int seek_pos = 0;
2276 bool start_current = false;
2278 if (start_index >= 0 && global_settings.play_selected)
2280 /* store the seek position before the shuffle */
2281 seek_pos = playlist->indices[start_index];
2282 playlist->index = global_settings.resume_first_index =
2283 playlist->first_index = start_index;
2284 start_current = true;
2287 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE));
2289 randomise_playlist(playlist, random_seed, start_current, true);
2291 return playlist->index;
2294 /* start playing current playlist at specified index/offset */
2295 int playlist_start(int start_index, int offset)
2297 struct playlist_info* playlist = &current_playlist;
2299 playlist->index = start_index;
2300 #if CONFIG_CODEC != SWCODEC
2301 talk_buffer_steal(); /* will use the mp3 buffer */
2302 #endif
2303 audio_play(offset);
2305 return 0;
2308 /* Returns false if 'steps' is out of bounds, else true */
2309 bool playlist_check(int steps)
2311 struct playlist_info* playlist = &current_playlist;
2313 /* always allow folder navigation */
2314 if (global_settings.next_folder && playlist->in_ram)
2315 return true;
2317 int index = get_next_index(playlist, steps, -1);
2319 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2320 index = get_next_index(playlist, steps, REPEAT_ALL);
2322 return (index >= 0);
2325 /* get trackname of track that is "steps" away from current playing track.
2326 NULL is used to identify end of playlist */
2327 char* playlist_peek(int steps)
2329 struct playlist_info* playlist = &current_playlist;
2330 int seek;
2331 int fd;
2332 char *temp_ptr;
2333 int index;
2334 bool control_file;
2336 index = get_next_index(playlist, steps, -1);
2337 if (index < 0)
2338 return NULL;
2340 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2341 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2343 if (get_filename(playlist, index, seek, control_file, now_playing,
2344 MAX_PATH+1) < 0)
2345 return NULL;
2347 temp_ptr = now_playing;
2349 if (!playlist->in_ram || control_file)
2351 /* remove bogus dirs from beginning of path
2352 (workaround for buggy playlist creation tools) */
2353 while (temp_ptr)
2355 #ifdef HAVE_DIRCACHE
2356 if (dircache_is_enabled())
2358 if (dircache_get_entry_ptr(temp_ptr))
2359 break;
2361 else
2362 #endif
2364 fd = open(temp_ptr, O_RDONLY);
2365 if (fd >= 0)
2367 close(fd);
2368 break;
2372 temp_ptr = strchr(temp_ptr+1, '/');
2375 if (!temp_ptr)
2377 /* Even though this is an invalid file, we still need to pass a
2378 file name to the caller because NULL is used to indicate end
2379 of playlist */
2380 return now_playing;
2384 return temp_ptr;
2388 * Update indices as track has changed
2390 int playlist_next(int steps)
2392 struct playlist_info* playlist = &current_playlist;
2393 int index;
2395 if ( (steps > 0)
2396 #if (AB_REPEAT_ENABLE == 1)
2397 && (global_settings.repeat_mode != REPEAT_AB)
2398 #endif
2399 && (global_settings.repeat_mode != REPEAT_ONE) )
2401 int i, j;
2403 /* We need to delete all the queued songs */
2404 for (i=0, j=steps; i<j; i++)
2406 index = get_next_index(playlist, i, -1);
2408 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2410 remove_track_from_playlist(playlist, index, true);
2411 steps--; /* one less track */
2416 index = get_next_index(playlist, steps, -1);
2418 if (index < 0)
2420 /* end of playlist... or is it */
2421 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2422 global_settings.playlist_shuffle &&
2423 playlist->amount > 1)
2425 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2426 playlist->first_index = global_settings.resume_first_index = 0;
2427 sort_playlist(playlist, false, false);
2428 randomise_playlist(playlist, current_tick, false, true);
2429 playlist_start(0, 0);
2430 index = 0;
2432 else if (playlist->in_ram && global_settings.next_folder)
2434 char dir[MAX_PATH+1];
2436 changing_dir = true;
2437 if (steps > 0)
2439 if (!get_next_directory(dir))
2441 /* start playing next directory */
2442 if (playlist_create(dir, NULL) != -1)
2444 ft_build_playlist(tree_get_context(), 0);
2445 if (global_settings.playlist_shuffle)
2446 playlist_shuffle(current_tick, -1);
2447 #if CONFIG_CODEC != SWCODEC
2448 playlist_start(0, 0);
2449 #endif
2450 playlist->index = 0;
2451 index = 0;
2455 else
2457 if (!get_previous_directory(dir))
2459 /* start playing previous directory */
2460 if (playlist_create(dir, NULL) != -1)
2462 ft_build_playlist(tree_get_context(), 0);
2463 if (global_settings.playlist_shuffle)
2464 playlist_shuffle(current_tick, -1);
2465 playlist_start(current_playlist.amount-1,0);
2466 index = current_playlist.amount-1;
2470 changing_dir = false;
2473 return index;
2476 playlist->index = index;
2478 if (playlist->last_insert_pos >= 0 && steps > 0)
2480 /* check to see if we've gone beyond the last inserted track */
2481 int cur = rotate_index(playlist, index);
2482 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2484 if (cur > last_pos)
2486 /* reset last inserted track */
2487 playlist->last_insert_pos = -1;
2489 if (playlist->control_fd >= 0)
2491 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2492 -1, -1, NULL, NULL, NULL);
2494 if (result < 0)
2495 return result;
2497 sync_control(playlist, false);
2502 return index;
2505 /* try playing next or previous folder */
2506 bool playlist_next_dir(int direction)
2508 char dir[MAX_PATH+1];
2509 bool result;
2510 int res;
2512 /* not to mess up real playlists */
2513 if(!current_playlist.in_ram)
2514 return false;
2516 if(changing_dir)
2517 return false;
2519 changing_dir = true;
2520 if(direction > 0)
2521 res = get_next_directory(dir);
2522 else
2523 res = get_previous_directory(dir);
2524 if (!res)
2526 if (playlist_create(dir, NULL) != -1)
2528 ft_build_playlist(tree_get_context(), 0);
2529 if (global_settings.playlist_shuffle)
2530 playlist_shuffle(current_tick, -1);
2531 playlist_start(0,0);
2532 result = true;
2534 else
2535 result = false;
2537 else
2538 result = false;
2540 changing_dir = false;
2542 return result;
2545 /* Get resume info for current playing song. If return value is -1 then
2546 settings shouldn't be saved. */
2547 int playlist_get_resume_info(int *resume_index)
2549 struct playlist_info* playlist = &current_playlist;
2551 *resume_index = playlist->index;
2553 return 0;
2556 /* Update resume info for current playing song. Returns -1 on error. */
2557 int playlist_update_resume_info(const struct mp3entry* id3)
2559 struct playlist_info* playlist = &current_playlist;
2561 if (id3)
2563 if (global_settings.resume_index != playlist->index ||
2564 global_settings.resume_offset != id3->offset)
2566 global_settings.resume_index = playlist->index;
2567 global_settings.resume_offset = id3->offset;
2568 settings_save();
2571 else
2573 global_settings.resume_index = -1;
2574 global_settings.resume_offset = -1;
2575 settings_save();
2578 return 0;
2581 /* Returns index of current playing track for display purposes. This value
2582 should not be used for resume purposes as it doesn't represent the actual
2583 index into the playlist */
2584 int playlist_get_display_index(void)
2586 struct playlist_info* playlist = &current_playlist;
2588 /* first_index should always be index 0 for display purposes */
2589 int index = rotate_index(playlist, playlist->index);
2591 return (index+1);
2594 /* returns number of tracks in current playlist */
2595 int playlist_amount(void)
2597 return playlist_amount_ex(NULL);
2601 * Create a new playlist If playlist is not NULL then we're loading a
2602 * playlist off disk for viewing/editing. The index_buffer is used to store
2603 * playlist indices (required for and only used if !current playlist). The
2604 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2606 int playlist_create_ex(struct playlist_info* playlist,
2607 const char* dir, const char* file,
2608 void* index_buffer, int index_buffer_size,
2609 void* temp_buffer, int temp_buffer_size)
2611 if (!playlist)
2612 playlist = &current_playlist;
2613 else
2615 /* Initialize playlist structure */
2616 int r = rand() % 10;
2617 playlist->current = false;
2619 /* Use random name for control file */
2620 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2621 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2622 playlist->fd = -1;
2623 playlist->control_fd = -1;
2625 if (index_buffer)
2627 int num_indices = index_buffer_size / sizeof(int);
2629 #ifdef HAVE_DIRCACHE
2630 num_indices /= 2;
2631 #endif
2632 if (num_indices > global_settings.max_files_in_playlist)
2633 num_indices = global_settings.max_files_in_playlist;
2635 playlist->max_playlist_size = num_indices;
2636 playlist->indices = index_buffer;
2637 #ifdef HAVE_DIRCACHE
2638 playlist->filenames = (const struct dircache_entry **)
2639 &playlist->indices[num_indices];
2640 #endif
2642 else
2644 playlist->max_playlist_size = current_playlist.max_playlist_size;
2645 playlist->indices = current_playlist.indices;
2646 #ifdef HAVE_DIRCACHE
2647 playlist->filenames = current_playlist.filenames;
2648 #endif
2651 playlist->buffer_size = 0;
2652 playlist->buffer = NULL;
2653 mutex_init(&playlist->control_mutex);
2656 new_playlist(playlist, dir, file);
2658 if (file)
2659 /* load the playlist file */
2660 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2662 return 0;
2666 * Set the specified playlist as the current.
2667 * NOTE: You will get undefined behaviour if something is already playing so
2668 * remember to stop before calling this. Also, this call will
2669 * effectively close your playlist, making it unusable.
2671 int playlist_set_current(struct playlist_info* playlist)
2673 if (!playlist || (check_control(playlist) < 0))
2674 return -1;
2676 empty_playlist(&current_playlist, false);
2678 strncpy(current_playlist.filename, playlist->filename,
2679 sizeof(current_playlist.filename));
2681 current_playlist.fd = playlist->fd;
2683 close(playlist->control_fd);
2684 remove(current_playlist.control_filename);
2685 if (rename(playlist->control_filename,
2686 current_playlist.control_filename) < 0)
2687 return -1;
2688 current_playlist.control_fd = open(current_playlist.control_filename,
2689 O_RDWR);
2690 if (current_playlist.control_fd < 0)
2691 return -1;
2692 current_playlist.control_created = true;
2694 current_playlist.dirlen = playlist->dirlen;
2696 if (playlist->indices && playlist->indices != current_playlist.indices)
2698 memcpy(current_playlist.indices, playlist->indices,
2699 playlist->max_playlist_size*sizeof(int));
2700 #ifdef HAVE_DIRCACHE
2701 memcpy(current_playlist.filenames, playlist->filenames,
2702 playlist->max_playlist_size*sizeof(int));
2703 #endif
2706 current_playlist.first_index = playlist->first_index;
2707 current_playlist.amount = playlist->amount;
2708 current_playlist.last_insert_pos = playlist->last_insert_pos;
2709 current_playlist.seed = playlist->seed;
2710 current_playlist.shuffle_modified = playlist->shuffle_modified;
2711 current_playlist.deleted = playlist->deleted;
2712 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2714 memcpy(current_playlist.control_cache, playlist->control_cache,
2715 sizeof(current_playlist.control_cache));
2716 current_playlist.num_cached = playlist->num_cached;
2717 current_playlist.pending_control_sync = playlist->pending_control_sync;
2719 return 0;
2723 * Close files and delete control file for non-current playlist.
2725 void playlist_close(struct playlist_info* playlist)
2727 if (!playlist)
2728 return;
2730 if (playlist->fd >= 0)
2731 close(playlist->fd);
2733 if (playlist->control_fd >= 0)
2734 close(playlist->control_fd);
2736 if (playlist->control_created)
2737 remove(playlist->control_filename);
2741 * Insert track into playlist at specified position (or one of the special
2742 * positions). Returns position where track was inserted or -1 if error.
2744 int playlist_insert_track(struct playlist_info* playlist,
2745 const char *filename, int position, bool queue)
2747 int result;
2749 if (!playlist)
2750 playlist = &current_playlist;
2752 if (check_control(playlist) < 0)
2754 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2755 return -1;
2758 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2760 if (result != -1)
2762 sync_control(playlist, false);
2763 if (audio_status() & AUDIO_STATUS_PLAY)
2764 audio_flush_and_reload_tracks();
2767 #ifdef HAVE_DIRCACHE
2768 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2769 #endif
2771 return result;
2775 * Insert all tracks from specified directory into playlist.
2777 int playlist_insert_directory(struct playlist_info* playlist,
2778 const char *dirname, int position, bool queue,
2779 bool recurse)
2781 int count = 0;
2782 int result;
2783 unsigned char *count_str;
2785 if (!playlist)
2786 playlist = &current_playlist;
2788 if (check_control(playlist) < 0)
2790 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2791 return -1;
2794 if (queue)
2795 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2796 else
2797 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2799 display_playlist_count(count, count_str);
2801 result = add_directory_to_playlist(playlist, dirname, &position, queue,
2802 &count, recurse);
2804 sync_control(playlist, false);
2806 display_playlist_count(count, count_str);
2808 if (audio_status() & AUDIO_STATUS_PLAY)
2809 audio_flush_and_reload_tracks();
2811 #ifdef HAVE_DIRCACHE
2812 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2813 #endif
2815 return result;
2819 * Insert all tracks from specified playlist into dynamic playlist.
2821 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2822 int position, bool queue)
2824 int fd;
2825 int max;
2826 char *temp_ptr;
2827 char *dir;
2828 unsigned char *count_str;
2829 char temp_buf[MAX_PATH+1];
2830 char trackname[MAX_PATH+1];
2831 int count = 0;
2832 int result = 0;
2834 if (!playlist)
2835 playlist = &current_playlist;
2837 if (check_control(playlist) < 0)
2839 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2840 return -1;
2843 fd = open(filename, O_RDONLY);
2844 if (fd < 0)
2846 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2847 return -1;
2850 /* we need the directory name for formatting purposes */
2851 dir = filename;
2853 temp_ptr = strrchr(filename+1,'/');
2854 if (temp_ptr)
2855 *temp_ptr = 0;
2856 else
2857 dir = "/";
2859 if (queue)
2860 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2861 else
2862 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2864 display_playlist_count(count, count_str);
2866 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2868 /* user abort */
2869 if (button_get(false) == SETTINGS_CANCEL)
2870 break;
2872 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2874 int insert_pos;
2876 /* we need to format so that relative paths are correctly
2877 handled */
2878 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2879 dir) < 0)
2881 result = -1;
2882 break;
2885 insert_pos = add_track_to_playlist(playlist, trackname, position,
2886 queue, -1);
2888 if (insert_pos < 0)
2890 result = -1;
2891 break;
2894 /* Make sure tracks are inserted in correct order if user
2895 requests INSERT_FIRST */
2896 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2897 position = insert_pos + 1;
2899 count++;
2901 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2903 display_playlist_count(count, count_str);
2905 if (count == PLAYLIST_DISPLAY_COUNT &&
2906 (audio_status() & AUDIO_STATUS_PLAY))
2907 audio_flush_and_reload_tracks();
2911 /* let the other threads work */
2912 yield();
2915 close(fd);
2917 if (temp_ptr)
2918 *temp_ptr = '/';
2920 sync_control(playlist, false);
2922 display_playlist_count(count, count_str);
2924 if (audio_status() & AUDIO_STATUS_PLAY)
2925 audio_flush_and_reload_tracks();
2927 #ifdef HAVE_DIRCACHE
2928 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2929 #endif
2931 return result;
2935 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2936 * we want to delete the current playing track.
2938 int playlist_delete(struct playlist_info* playlist, int index)
2940 int result = 0;
2942 if (!playlist)
2943 playlist = &current_playlist;
2945 if (check_control(playlist) < 0)
2947 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2948 return -1;
2951 if (index == PLAYLIST_DELETE_CURRENT)
2952 index = playlist->index;
2954 result = remove_track_from_playlist(playlist, index, true);
2956 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
2957 audio_flush_and_reload_tracks();
2959 return result;
2963 * Move track at index to new_index. Tracks between the two are shifted
2964 * appropriately. Returns 0 on success and -1 on failure.
2966 int playlist_move(struct playlist_info* playlist, int index, int new_index)
2968 int result;
2969 int seek;
2970 bool control_file;
2971 bool queue;
2972 bool current = false;
2973 int r;
2974 char filename[MAX_PATH];
2976 if (!playlist)
2977 playlist = &current_playlist;
2979 if (check_control(playlist) < 0)
2981 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2982 return -1;
2985 if (index == new_index)
2986 return -1;
2988 if (index == playlist->index)
2989 /* Moving the current track */
2990 current = true;
2992 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2993 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2994 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2996 if (get_filename(playlist, index, seek, control_file, filename,
2997 sizeof(filename)) < 0)
2998 return -1;
3000 /* Delete track from original position */
3001 result = remove_track_from_playlist(playlist, index, true);
3003 if (result != -1)
3005 /* We want to insert the track at the position that was specified by
3006 new_index. This may be different then new_index because of the
3007 shifting that occurred after the delete */
3008 r = rotate_index(playlist, new_index);
3010 if (r == 0)
3011 /* First index */
3012 new_index = PLAYLIST_PREPEND;
3013 else if (r == playlist->amount)
3014 /* Append */
3015 new_index = PLAYLIST_INSERT_LAST;
3016 else
3017 /* Calculate index of desired position */
3018 new_index = (r+playlist->first_index)%playlist->amount;
3020 result = add_track_to_playlist(playlist, filename, new_index, queue,
3021 -1);
3023 if (result != -1)
3025 if (current)
3027 /* Moved the current track */
3028 switch (new_index)
3030 case PLAYLIST_PREPEND:
3031 playlist->index = playlist->first_index;
3032 break;
3033 case PLAYLIST_INSERT_LAST:
3034 playlist->index = playlist->first_index - 1;
3035 if (playlist->index < 0)
3036 playlist->index += playlist->amount;
3037 break;
3038 default:
3039 playlist->index = new_index;
3040 break;
3044 if (audio_status() & AUDIO_STATUS_PLAY)
3045 audio_flush_and_reload_tracks();
3049 #ifdef HAVE_DIRCACHE
3050 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3051 #endif
3053 return result;
3056 /* shuffle currently playing playlist */
3057 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3058 bool start_current)
3060 int result;
3062 if (!playlist)
3063 playlist = &current_playlist;
3065 check_control(playlist);
3067 result = randomise_playlist(playlist, seed, start_current, true);
3069 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
3070 audio_flush_and_reload_tracks();
3072 return result;
3075 /* sort currently playing playlist */
3076 int playlist_sort(struct playlist_info* playlist, bool start_current)
3078 int result;
3080 if (!playlist)
3081 playlist = &current_playlist;
3083 check_control(playlist);
3085 result = sort_playlist(playlist, start_current, true);
3087 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
3088 audio_flush_and_reload_tracks();
3090 return result;
3093 /* returns true if playlist has been modified */
3094 bool playlist_modified(const struct playlist_info* playlist)
3096 if (!playlist)
3097 playlist = &current_playlist;
3099 if (playlist->shuffle_modified ||
3100 playlist->deleted ||
3101 playlist->num_inserted_tracks > 0)
3102 return true;
3104 return false;
3107 /* returns index of first track in playlist */
3108 int playlist_get_first_index(const struct playlist_info* playlist)
3110 if (!playlist)
3111 playlist = &current_playlist;
3113 return playlist->first_index;
3116 /* returns shuffle seed of playlist */
3117 int playlist_get_seed(const struct playlist_info* playlist)
3119 if (!playlist)
3120 playlist = &current_playlist;
3122 return playlist->seed;
3125 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3126 int playlist_amount_ex(const struct playlist_info* playlist)
3128 if (!playlist)
3129 playlist = &current_playlist;
3131 return playlist->amount;
3134 /* returns full path of playlist (minus extension) */
3135 char *playlist_name(const struct playlist_info* playlist, char *buf,
3136 int buf_size)
3138 char *sep;
3140 if (!playlist)
3141 playlist = &current_playlist;
3143 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3145 if (!buf[0])
3146 return NULL;
3148 /* Remove extension */
3149 sep = strrchr(buf, '.');
3150 if (sep)
3151 *sep = 0;
3153 return buf;
3156 /* returns the playlist filename */
3157 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3158 int buf_size)
3160 if (!playlist)
3161 playlist = &current_playlist;
3163 snprintf(buf, buf_size, "%s", playlist->filename);
3165 if (!buf[0])
3166 return NULL;
3168 return buf;
3171 /* Fills info structure with information about track at specified index.
3172 Returns 0 on success and -1 on failure */
3173 int playlist_get_track_info(struct playlist_info* playlist, int index,
3174 struct playlist_track_info* info)
3176 int seek;
3177 bool control_file;
3179 if (!playlist)
3180 playlist = &current_playlist;
3182 if (index < 0 || index >= playlist->amount)
3183 return -1;
3185 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3186 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3188 if (get_filename(playlist, index, seek, control_file, info->filename,
3189 sizeof(info->filename)) < 0)
3190 return -1;
3192 info->attr = 0;
3194 if (control_file)
3196 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3197 info->attr |= PLAYLIST_ATTR_QUEUED;
3198 else
3199 info->attr |= PLAYLIST_ATTR_INSERTED;
3203 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3204 info->attr |= PLAYLIST_ATTR_SKIPPED;
3206 info->index = index;
3207 info->display_index = rotate_index(playlist, index) + 1;
3209 return 0;
3212 /* save the current dynamic playlist to specified file */
3213 int playlist_save(struct playlist_info* playlist, char *filename)
3215 int fd;
3216 int i, index;
3217 int count = 0;
3218 char path[MAX_PATH+1];
3219 char tmp_buf[MAX_PATH+1];
3220 int result = 0;
3221 bool overwrite_current = false;
3222 int* index_buf = NULL;
3224 if (!playlist)
3225 playlist = &current_playlist;
3227 if (playlist->amount <= 0)
3228 return -1;
3230 /* use current working directory as base for pathname */
3231 if (format_track_path(path, filename, sizeof(tmp_buf),
3232 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3233 return -1;
3235 if (!strncmp(playlist->filename, path, strlen(path)))
3237 /* Attempting to overwrite current playlist file.*/
3239 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3241 /* not enough buffer space to store updated indices */
3242 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3243 return -1;
3246 /* in_ram buffer is unused for m3u files so we'll use for storing
3247 updated indices */
3248 index_buf = (int*)playlist->buffer;
3250 /* use temporary pathname */
3251 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3252 overwrite_current = true;
3255 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3256 if (fd < 0)
3258 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3259 return -1;
3262 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3264 index = playlist->first_index;
3265 for (i=0; i<playlist->amount; i++)
3267 bool control_file;
3268 bool queue;
3269 int seek;
3271 /* user abort */
3272 if (button_get(false) == SETTINGS_CANCEL)
3274 result = -1;
3275 break;
3278 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3279 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3280 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3282 /* Don't save queued files */
3283 if (!queue)
3285 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3286 MAX_PATH+1) < 0)
3288 result = -1;
3289 break;
3292 if (overwrite_current)
3293 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3295 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3297 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3298 result = -1;
3299 break;
3302 count++;
3304 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3305 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3307 yield();
3310 index = (index+1)%playlist->amount;
3313 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3315 close(fd);
3317 if (overwrite_current && result >= 0)
3319 result = -1;
3321 mutex_lock(&playlist->control_mutex);
3323 /* Replace the current playlist with the new one and update indices */
3324 close(playlist->fd);
3325 if (remove(playlist->filename) >= 0)
3327 if (rename(path, playlist->filename) >= 0)
3329 playlist->fd = open(playlist->filename, O_RDONLY);
3330 if (playlist->fd >= 0)
3332 index = playlist->first_index;
3333 for (i=0, count=0; i<playlist->amount; i++)
3335 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3337 playlist->indices[index] = index_buf[count];
3338 count++;
3340 index = (index+1)%playlist->amount;
3343 /* we need to recreate control because inserted tracks are
3344 now part of the playlist and shuffle has been
3345 invalidated */
3346 result = recreate_control(playlist);
3351 mutex_unlock(&playlist->control_mutex);
3355 return result;