Update several codec Makefiles so that the codec libs build again on Coldfire targets...
[Rockbox.git] / apps / playlist.c
blob5a5313b7361685bfa8fff165ec773312cd53cc77
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 "action.h"
74 #include "dir.h"
75 #include "sprintf.h"
76 #include "debug.h"
77 #include "audio.h"
78 #include "lcd.h"
79 #include "kernel.h"
80 #include "settings.h"
81 #include "status.h"
82 #include "applimits.h"
83 #include "screens.h"
84 #include "buffer.h"
85 #include "atoi.h"
86 #include "misc.h"
87 #include "button.h"
88 #include "filetree.h"
89 #include "abrepeat.h"
90 #include "dircache.h"
91 #include "thread.h"
92 #include "usb.h"
93 #ifdef HAVE_LCD_BITMAP
94 #include "icons.h"
95 #include "widgets.h"
96 #endif
98 #include "lang.h"
99 #include "talk.h"
100 #include "splash.h"
102 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
103 #define PLAYLIST_CONTROL_FILE_VERSION 2
106 Each playlist index has a flag associated with it which identifies what
107 type of track it is. These flags are stored in the 4 high order bits of
108 the index.
110 NOTE: This limits the playlist file size to a max of 256M.
112 Bits 31-30:
113 00 = Playlist track
114 01 = Track was prepended into playlist
115 10 = Track was inserted into playlist
116 11 = Track was appended into playlist
117 Bit 29:
118 0 = Added track
119 1 = Queued track
120 Bit 28:
121 0 = Track entry is valid
122 1 = Track does not exist on disk and should be skipped
124 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
125 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
126 #define PLAYLIST_QUEUE_MASK 0x20000000
128 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
129 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
130 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
132 #define PLAYLIST_QUEUED 0x20000000
133 #define PLAYLIST_SKIPPED 0x10000000
135 #define PLAYLIST_DISPLAY_COUNT 10
137 struct directory_search_context {
138 struct playlist_info* playlist;
139 int position;
140 bool queue;
141 int count;
144 static bool changing_dir = false;
146 static struct playlist_info current_playlist;
147 static char now_playing[MAX_PATH+1];
149 static void empty_playlist(struct playlist_info* playlist, bool resume);
150 static void new_playlist(struct playlist_info* playlist, const char *dir,
151 const char *file);
152 static void create_control(struct playlist_info* playlist);
153 static int check_control(struct playlist_info* playlist);
154 static int recreate_control(struct playlist_info* playlist);
155 static void update_playlist_filename(struct playlist_info* playlist,
156 const char *dir, const char *file);
157 static int add_indices_to_playlist(struct playlist_info* playlist,
158 char* buffer, int buflen);
159 static int add_track_to_playlist(struct playlist_info* playlist,
160 const char *filename, int position,
161 bool queue, int seek_pos);
162 static int directory_search_callback(char* filename, void* context);
163 static int remove_track_from_playlist(struct playlist_info* playlist,
164 int position, bool write);
165 static int randomise_playlist(struct playlist_info* playlist,
166 unsigned int seed, bool start_current,
167 bool write);
168 static int sort_playlist(struct playlist_info* playlist, bool start_current,
169 bool write);
170 static int get_next_index(const struct playlist_info* playlist, int steps,
171 int repeat_mode);
172 static void find_and_set_playlist_index(struct playlist_info* playlist,
173 unsigned int seek);
174 static int compare(const void* p1, const void* p2);
175 static int get_filename(struct playlist_info* playlist, int index, int seek,
176 bool control_file, char *buf, int buf_length);
177 static int get_next_directory(char *dir);
178 static int get_next_dir(char *dir, bool is_forward, bool recursion);
179 static int get_previous_directory(char *dir);
180 static int check_subdir_for_music(char *dir, char *subdir);
181 static int format_track_path(char *dest, char *src, int buf_length, int max,
182 char *dir);
183 static void display_playlist_count(int count, const unsigned char *fmt);
184 static void display_buffer_full(void);
185 static int flush_cached_control(struct playlist_info* playlist);
186 static int update_control(struct playlist_info* playlist,
187 enum playlist_command command, int i1, int i2,
188 const char* s1, const char* s2, void* data);
189 static void sync_control(struct playlist_info* playlist, bool force);
190 static int rotate_index(const struct playlist_info* playlist, int index);
192 #ifdef HAVE_DIRCACHE
193 #define PLAYLIST_LOAD_POINTERS 1
195 static struct event_queue playlist_queue;
196 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x400)/sizeof(long)];
197 static const char playlist_thread_name[] = "playlist cachectrl";
198 #endif
201 * remove any files and indices associated with the playlist
203 static void empty_playlist(struct playlist_info* playlist, bool resume)
205 playlist->filename[0] = '\0';
207 if(playlist->fd >= 0)
208 /* If there is an already open playlist, close it. */
209 close(playlist->fd);
210 playlist->fd = -1;
212 if(playlist->control_fd >= 0)
213 close(playlist->control_fd);
214 playlist->control_fd = -1;
215 playlist->control_created = false;
217 playlist->in_ram = false;
219 if (playlist->buffer)
220 playlist->buffer[0] = 0;
222 playlist->buffer_end_pos = 0;
224 playlist->index = 0;
225 playlist->first_index = 0;
226 playlist->amount = 0;
227 playlist->last_insert_pos = -1;
228 playlist->seed = 0;
229 playlist->shuffle_modified = false;
230 playlist->deleted = false;
231 playlist->num_inserted_tracks = 0;
232 playlist->started = false;
234 playlist->num_cached = 0;
235 playlist->pending_control_sync = false;
237 if (!resume && playlist->current)
239 /* start with fresh playlist control file when starting new
240 playlist */
241 create_control(playlist);
243 /* Reset resume settings */
244 global_settings.resume_first_index = 0;
245 global_settings.resume_seed = -1;
250 * Initialize a new playlist for viewing/editing/playing. dir is the
251 * directory where the playlist is located and file is the filename.
253 static void new_playlist(struct playlist_info* playlist, const char *dir,
254 const char *file)
256 empty_playlist(playlist, false);
258 if (!file)
260 file = "";
262 if (dir && playlist->current) /* !current cannot be in_ram */
263 playlist->in_ram = true;
264 else
265 dir = ""; /* empty playlist */
268 update_playlist_filename(playlist, dir, file);
270 if (playlist->control_fd >= 0)
272 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
273 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
274 sync_control(playlist, false);
279 * create control file for playlist
281 static void create_control(struct playlist_info* playlist)
283 playlist->control_fd = open(playlist->control_filename,
284 O_CREAT|O_RDWR|O_TRUNC);
285 if (playlist->control_fd < 0)
287 if (check_rockboxdir())
289 gui_syncsplash(HZ*2, true, (unsigned char *)"%s (%d)",
290 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
291 playlist->control_fd);
293 playlist->control_created = false;
295 else
297 playlist->control_created = true;
302 * validate the control file. This may include creating/initializing it if
303 * necessary;
305 static int check_control(struct playlist_info* playlist)
307 if (!playlist->control_created)
309 create_control(playlist);
311 if (playlist->control_fd >= 0)
313 char* dir = playlist->filename;
314 char* file = playlist->filename+playlist->dirlen;
315 char c = playlist->filename[playlist->dirlen-1];
317 playlist->filename[playlist->dirlen-1] = '\0';
319 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
320 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
321 sync_control(playlist, false);
322 playlist->filename[playlist->dirlen-1] = c;
326 if (playlist->control_fd < 0)
327 return -1;
329 return 0;
333 * recreate the control file based on current playlist entries
335 static int recreate_control(struct playlist_info* playlist)
337 char temp_file[MAX_PATH+1];
338 int temp_fd = -1;
339 int i;
340 int result = 0;
342 if(playlist->control_fd >= 0)
344 char* dir = playlist->filename;
345 char* file = playlist->filename+playlist->dirlen;
346 char c = playlist->filename[playlist->dirlen-1];
348 close(playlist->control_fd);
350 snprintf(temp_file, sizeof(temp_file), "%s_temp",
351 playlist->control_filename);
353 if (rename(playlist->control_filename, temp_file) < 0)
354 return -1;
356 temp_fd = open(temp_file, O_RDONLY);
357 if (temp_fd < 0)
358 return -1;
360 playlist->control_fd = open(playlist->control_filename,
361 O_CREAT|O_RDWR|O_TRUNC);
362 if (playlist->control_fd < 0)
363 return -1;
365 playlist->filename[playlist->dirlen-1] = '\0';
367 /* cannot call update_control() because of mutex */
368 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
369 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
371 playlist->filename[playlist->dirlen-1] = c;
373 if (result < 0)
375 close(temp_fd);
376 return result;
380 playlist->seed = 0;
381 playlist->shuffle_modified = false;
382 playlist->deleted = false;
383 playlist->num_inserted_tracks = 0;
385 if (playlist->current)
387 global_settings.resume_seed = -1;
388 settings_save();
391 for (i=0; i<playlist->amount; i++)
393 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
395 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
396 char inserted_file[MAX_PATH+1];
398 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
399 SEEK_SET);
400 read_line(temp_fd, inserted_file, sizeof(inserted_file));
402 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
403 queue?'Q':'A', i, playlist->last_insert_pos);
404 if (result > 0)
406 /* save the position in file where name is written */
407 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
409 result = fdprintf(playlist->control_fd, "%s\n",
410 inserted_file);
412 playlist->indices[i] =
413 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
416 if (result < 0)
417 break;
419 playlist->num_inserted_tracks++;
423 close(temp_fd);
424 remove(temp_file);
425 fsync(playlist->control_fd);
427 if (result < 0)
428 return result;
430 return 0;
434 * store directory and name of playlist file
436 static void update_playlist_filename(struct playlist_info* playlist,
437 const char *dir, const char *file)
439 char *sep="";
440 int dirlen = strlen(dir);
442 /* If the dir does not end in trailing slash, we use a separator.
443 Otherwise we don't. */
444 if('/' != dir[dirlen-1])
446 sep="/";
447 dirlen++;
450 playlist->dirlen = dirlen;
452 snprintf(playlist->filename, sizeof(playlist->filename),
453 "%s%s%s", dir, sep, file);
457 * calculate track offsets within a playlist file
459 static int add_indices_to_playlist(struct playlist_info* playlist,
460 char* buffer, int buflen)
462 unsigned int nread;
463 unsigned int i = 0;
464 unsigned int count = 0;
465 bool store_index;
466 unsigned char *p;
467 int result = 0;
469 if(-1 == playlist->fd)
470 playlist->fd = open(playlist->filename, O_RDONLY);
471 if(playlist->fd < 0)
472 return -1; /* failure */
474 #ifdef HAVE_LCD_BITMAP
475 if(global_settings.statusbar)
476 lcd_setmargins(0, STATUSBAR_HEIGHT);
477 else
478 lcd_setmargins(0, 0);
479 #endif
481 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD));
483 if (!buffer)
485 /* use mp3 buffer for maximum load speed */
486 audio_stop();
487 #if CONFIG_CODEC != SWCODEC
488 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
489 buflen = (audiobufend - audiobuf);
490 buffer = (char *)audiobuf;
491 #else
492 buflen = (audiobufend - audiobuf - talk_get_bufsize());
493 buffer = (char *)&audiobuf[talk_get_bufsize()];
494 #endif
497 store_index = true;
499 while(1)
501 nread = read(playlist->fd, buffer, buflen);
502 /* Terminate on EOF */
503 if(nread <= 0)
504 break;
506 p = (unsigned char *)buffer;
508 /* utf8 BOM at beginning of file? */
509 if(i == 0 && nread > 3
510 && *p == 0xef && *(p+1) == 0xbb && *(p+2) == 0xbf) {
511 nread -= 3;
512 p += 3;
513 i += 3;
516 for(count=0; count < nread; count++,p++) {
518 /* Are we on a new line? */
519 if((*p == '\n') || (*p == '\r'))
521 store_index = true;
523 else if(store_index)
525 store_index = false;
527 if(*p != '#')
529 if ( playlist->amount >= playlist->max_playlist_size ) {
530 display_buffer_full();
531 result = -1;
532 goto exit;
535 /* Store a new entry */
536 playlist->indices[ playlist->amount ] = i+count;
537 #ifdef HAVE_DIRCACHE
538 if (playlist->filenames)
539 playlist->filenames[ playlist->amount ] = NULL;
540 #endif
541 playlist->amount++;
546 i+= count;
549 exit:
550 #ifdef HAVE_DIRCACHE
551 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
552 #endif
554 return result;
558 * Add track to playlist at specified position. There are five special
559 * positions that can be specified:
560 * PLAYLIST_PREPEND - Add track at beginning of playlist
561 * PLAYLIST_INSERT - Add track after current song. NOTE: If
562 * there are already inserted tracks then track
563 * is added to the end of the insertion list
564 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
565 * matter what other tracks have been inserted
566 * PLAYLIST_INSERT_LAST - Add track to end of playlist
567 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
568 * current playing track and end of playlist
570 static int add_track_to_playlist(struct playlist_info* playlist,
571 const char *filename, int position,
572 bool queue, int seek_pos)
574 int insert_position, orig_position;
575 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
576 int i;
578 insert_position = orig_position = position;
580 if (playlist->amount >= playlist->max_playlist_size)
582 display_buffer_full();
583 return -1;
586 switch (position)
588 case PLAYLIST_PREPEND:
589 position = insert_position = playlist->first_index;
590 break;
591 case PLAYLIST_INSERT:
592 /* if there are already inserted tracks then add track to end of
593 insertion list else add after current playing track */
594 if (playlist->last_insert_pos >= 0 &&
595 playlist->last_insert_pos < playlist->amount &&
596 (playlist->indices[playlist->last_insert_pos]&
597 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
598 position = insert_position = playlist->last_insert_pos+1;
599 else if (playlist->amount > 0)
600 position = insert_position = playlist->index + 1;
601 else
602 position = insert_position = 0;
604 if (playlist->started)
605 playlist->last_insert_pos = position;
606 break;
607 case PLAYLIST_INSERT_FIRST:
608 if (playlist->amount > 0)
609 position = insert_position = playlist->index + 1;
610 else
611 position = insert_position = 0;
613 if (playlist->last_insert_pos < 0 && playlist->started)
614 playlist->last_insert_pos = position;
615 break;
616 case PLAYLIST_INSERT_LAST:
617 if (playlist->first_index > 0)
618 position = insert_position = playlist->first_index;
619 else
620 position = insert_position = playlist->amount;
621 break;
622 case PLAYLIST_INSERT_SHUFFLED:
624 if (playlist->started)
626 int offset;
627 int n = playlist->amount -
628 rotate_index(playlist, playlist->index);
630 if (n > 0)
631 offset = rand() % n;
632 else
633 offset = 0;
635 position = playlist->index + offset + 1;
636 if (position >= playlist->amount)
637 position -= playlist->amount;
639 insert_position = position;
641 else
642 position = insert_position = (rand() % (playlist->amount+1));
643 break;
647 if (queue)
648 flags |= PLAYLIST_QUEUED;
650 /* shift indices so that track can be added */
651 for (i=playlist->amount; i>insert_position; i--)
653 playlist->indices[i] = playlist->indices[i-1];
654 #ifdef HAVE_DIRCACHE
655 if (playlist->filenames)
656 playlist->filenames[i] = playlist->filenames[i-1];
657 #endif
660 /* update stored indices if needed */
661 if (playlist->amount > 0 && insert_position <= playlist->index &&
662 playlist->started)
663 playlist->index++;
665 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
666 orig_position != PLAYLIST_PREPEND && playlist->started)
668 playlist->first_index++;
670 if (seek_pos < 0 && playlist->current)
672 global_settings.resume_first_index = playlist->first_index;
673 settings_save();
677 if (insert_position < playlist->last_insert_pos ||
678 (insert_position == playlist->last_insert_pos && position < 0))
679 playlist->last_insert_pos++;
681 if (seek_pos < 0 && playlist->control_fd >= 0)
683 int result = update_control(playlist,
684 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
685 playlist->last_insert_pos, filename, NULL, &seek_pos);
687 if (result < 0)
688 return result;
691 playlist->indices[insert_position] = flags | seek_pos;
693 #ifdef HAVE_DIRCACHE
694 if (playlist->filenames)
695 playlist->filenames[insert_position] = NULL;
696 #endif
698 playlist->amount++;
699 playlist->num_inserted_tracks++;
701 return insert_position;
705 * Callback for playlist_directory_tracksearch to insert track into
706 * playlist.
708 static int directory_search_callback(char* filename, void* context)
710 struct directory_search_context* c =
711 (struct directory_search_context*) context;
712 int insert_pos;
714 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
715 c->queue, -1);
717 if (insert_pos < 0)
718 return -1;
720 (c->count)++;
722 /* Make sure tracks are inserted in correct order if user requests
723 INSERT_FIRST */
724 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
725 c->position = insert_pos + 1;
727 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
729 unsigned char* count_str;
731 if (c->queue)
732 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
733 else
734 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
736 display_playlist_count(c->count, count_str);
738 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
739 (audio_status() & AUDIO_STATUS_PLAY) &&
740 c->playlist->started)
741 audio_flush_and_reload_tracks();
744 return 0;
748 * remove track at specified position
750 static int remove_track_from_playlist(struct playlist_info* playlist,
751 int position, bool write)
753 int i;
754 bool inserted;
756 if (playlist->amount <= 0)
757 return -1;
759 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
761 /* shift indices now that track has been removed */
762 for (i=position; i<playlist->amount; i++)
764 playlist->indices[i] = playlist->indices[i+1];
765 #ifdef HAVE_DIRCACHE
766 if (playlist->filenames)
767 playlist->filenames[i] = playlist->filenames[i+1];
768 #endif
771 playlist->amount--;
773 if (inserted)
774 playlist->num_inserted_tracks--;
775 else
776 playlist->deleted = true;
778 /* update stored indices if needed */
779 if (position < playlist->index)
780 playlist->index--;
782 if (position < playlist->first_index)
784 playlist->first_index--;
786 if (write)
788 global_settings.resume_first_index = playlist->first_index;
789 settings_save();
793 if (position <= playlist->last_insert_pos)
794 playlist->last_insert_pos--;
796 if (write && playlist->control_fd >= 0)
798 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
799 position, -1, NULL, NULL, NULL);
801 if (result < 0)
802 return result;
804 sync_control(playlist, false);
807 return 0;
811 * randomly rearrange the array of indices for the playlist. If start_current
812 * is true then update the index to the new index of the current playing track
814 static int randomise_playlist(struct playlist_info* playlist,
815 unsigned int seed, bool start_current,
816 bool write)
818 int count;
819 int candidate;
820 long store;
821 unsigned int current = playlist->indices[playlist->index];
823 /* seed 0 is used to identify sorted playlist for resume purposes */
824 if (seed == 0)
825 seed = 1;
827 /* seed with the given seed */
828 srand(seed);
830 /* randomise entire indices list */
831 for(count = playlist->amount - 1; count >= 0; count--)
833 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
834 candidate = rand() % (count + 1);
836 /* now swap the values at the 'count' and 'candidate' positions */
837 store = playlist->indices[candidate];
838 playlist->indices[candidate] = playlist->indices[count];
839 playlist->indices[count] = store;
840 #ifdef HAVE_DIRCACHE
841 if (playlist->filenames)
843 store = (long)playlist->filenames[candidate];
844 playlist->filenames[candidate] = playlist->filenames[count];
845 playlist->filenames[count] = (struct dircache_entry *)store;
847 #endif
850 if (start_current)
851 find_and_set_playlist_index(playlist, current);
853 /* indices have been moved so last insert position is no longer valid */
854 playlist->last_insert_pos = -1;
856 playlist->seed = seed;
857 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
858 playlist->shuffle_modified = true;
860 if (write)
862 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
863 playlist->first_index, NULL, NULL, NULL);
864 global_settings.resume_seed = seed;
865 settings_save();
868 return 0;
872 * Sort the array of indices for the playlist. If start_current is true then
873 * set the index to the new index of the current song.
875 static int sort_playlist(struct playlist_info* playlist, bool start_current,
876 bool write)
878 unsigned int current = playlist->indices[playlist->index];
880 if (playlist->amount > 0)
881 qsort(playlist->indices, playlist->amount,
882 sizeof(playlist->indices[0]), compare);
884 #ifdef HAVE_DIRCACHE
885 /** We need to re-check the song names from disk because qsort can't
886 * sort two arrays at once :/
887 * FIXME: Please implement a better way to do this. */
888 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
889 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
890 #endif
892 if (start_current)
893 find_and_set_playlist_index(playlist, current);
895 /* indices have been moved so last insert position is no longer valid */
896 playlist->last_insert_pos = -1;
898 if (!playlist->num_inserted_tracks && !playlist->deleted)
899 playlist->shuffle_modified = false;
900 if (write && playlist->control_fd >= 0)
902 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
903 playlist->first_index, -1, NULL, NULL, NULL);
904 global_settings.resume_seed = 0;
905 settings_save();
908 return 0;
911 /* Calculate how many steps we have to really step when skipping entries
912 * marked as bad.
914 static int calculate_step_count(const struct playlist_info *playlist, int steps)
916 int i, count, direction;
917 int index;
918 int stepped_count = 0;
920 if (steps < 0)
922 direction = -1;
923 count = -steps;
925 else
927 direction = 1;
928 count = steps;
931 index = playlist->index;
932 i = 0;
933 do {
934 /* Boundary check */
935 if (index < 0)
936 index += playlist->amount;
937 if (index >= playlist->amount)
938 index -= playlist->amount;
940 /* Check if we found a bad entry. */
941 if (playlist->indices[index] & PLAYLIST_SKIPPED)
943 steps += direction;
944 /* Are all entries bad? */
945 if (stepped_count++ > playlist->amount)
946 break ;
948 else
949 i++;
951 index += direction;
952 } while (i <= count);
954 return steps;
957 /* Marks the index of the track to be skipped that is "steps" away from
958 * current playing track.
960 void playlist_skip_entry(struct playlist_info *playlist, int steps)
962 int index;
964 if (playlist == NULL)
965 playlist = &current_playlist;
967 /* need to account for already skipped tracks */
968 steps = calculate_step_count(playlist, steps);
970 index = playlist->index + steps;
971 if (index < 0)
972 index += playlist->amount;
973 else if (index >= playlist->amount)
974 index -= playlist->amount;
976 playlist->indices[index] |= PLAYLIST_SKIPPED;
980 * returns the index of the track that is "steps" away from current playing
981 * track.
983 static int get_next_index(const struct playlist_info* playlist, int steps,
984 int repeat_mode)
986 int current_index = playlist->index;
987 int next_index = -1;
989 if (playlist->amount <= 0)
990 return -1;
992 if (repeat_mode == -1)
993 repeat_mode = global_settings.repeat_mode;
995 if (repeat_mode == REPEAT_SHUFFLE &&
996 (!global_settings.playlist_shuffle || playlist->amount <= 1))
997 repeat_mode = REPEAT_ALL;
999 steps = calculate_step_count(playlist, steps);
1000 switch (repeat_mode)
1002 case REPEAT_SHUFFLE:
1003 /* Treat repeat shuffle just like repeat off. At end of playlist,
1004 play will be resumed in playlist_next() */
1005 case REPEAT_OFF:
1007 current_index = rotate_index(playlist, current_index);
1008 next_index = current_index+steps;
1009 if ((next_index < 0) || (next_index >= playlist->amount))
1010 next_index = -1;
1011 else
1012 next_index = (next_index+playlist->first_index) %
1013 playlist->amount;
1015 break;
1018 case REPEAT_ONE:
1019 #if (AB_REPEAT_ENABLE == 1)
1020 case REPEAT_AB:
1021 #endif
1022 next_index = current_index;
1023 break;
1025 case REPEAT_ALL:
1026 default:
1028 next_index = (current_index+steps) % playlist->amount;
1029 while (next_index < 0)
1030 next_index += playlist->amount;
1032 if (steps >= playlist->amount)
1034 int i, index;
1036 index = next_index;
1037 next_index = -1;
1039 /* second time around so skip the queued files */
1040 for (i=0; i<playlist->amount; i++)
1042 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1043 index = (index+1) % playlist->amount;
1044 else
1046 next_index = index;
1047 break;
1051 break;
1055 /* No luck if the whole playlist was bad. */
1056 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1057 return -1;
1059 return next_index;
1063 * Search for the seek track and set appropriate indices. Used after shuffle
1064 * to make sure the current index is still pointing to correct track.
1066 static void find_and_set_playlist_index(struct playlist_info* playlist,
1067 unsigned int seek)
1069 int i;
1071 /* Set the index to the current song */
1072 for (i=0; i<playlist->amount; i++)
1074 if (playlist->indices[i] == seek)
1076 playlist->index = playlist->first_index = i;
1078 if (playlist->current)
1080 global_settings.resume_first_index = i;
1081 settings_save();
1084 break;
1090 * used to sort track indices. Sort order is as follows:
1091 * 1. Prepended tracks (in prepend order)
1092 * 2. Playlist/directory tracks (in playlist order)
1093 * 3. Inserted/Appended tracks (in insert order)
1095 static int compare(const void* p1, const void* p2)
1097 unsigned long* e1 = (unsigned long*) p1;
1098 unsigned long* e2 = (unsigned long*) p2;
1099 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1100 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1102 if (flags1 == flags2)
1103 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1104 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1105 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1106 return -1;
1107 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1108 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1109 return 1;
1110 else if (flags1 && flags2)
1111 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1112 else
1113 return *e1 - *e2;
1116 #ifdef HAVE_DIRCACHE
1118 * Thread to update filename pointers to dircache on background
1119 * without affecting playlist load up performance. This thread also flushes
1120 * any pending control commands when the disk spins up.
1122 static void playlist_thread(void)
1124 struct event ev;
1125 bool dirty_pointers = false;
1126 static char tmp[MAX_PATH+1];
1128 struct playlist_info *playlist;
1129 int index;
1130 int seek;
1131 bool control_file;
1133 int sleep_time = 5;
1135 if (global_settings.disk_spindown > 1 &&
1136 global_settings.disk_spindown <= 5)
1137 sleep_time = global_settings.disk_spindown - 1;
1139 while (1)
1141 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1143 switch (ev.id)
1145 case PLAYLIST_LOAD_POINTERS:
1146 dirty_pointers = true;
1147 break ;
1149 /* Start the background scanning after either the disk spindown
1150 timeout or 5s, whichever is less */
1151 case SYS_TIMEOUT:
1152 playlist = &current_playlist;
1154 if (playlist->control_fd >= 0
1155 # ifndef SIMULATOR
1156 && ata_disk_is_active()
1157 # endif
1160 if (playlist->num_cached > 0)
1162 mutex_lock(&playlist->control_mutex);
1163 flush_cached_control(playlist);
1164 mutex_unlock(&playlist->control_mutex);
1167 sync_control(playlist, true);
1170 if (!dirty_pointers)
1171 break ;
1173 if (!dircache_is_enabled() || !playlist->filenames
1174 || playlist->amount <= 0)
1175 break ;
1177 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1178 cpu_boost(true);
1179 #endif
1180 for (index = 0; index < playlist->amount
1181 && queue_empty(&playlist_queue); index++)
1183 /* Process only pointers that are not already loaded. */
1184 if (playlist->filenames[index])
1185 continue ;
1187 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1188 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1190 /* Load the filename from playlist file. */
1191 if (get_filename(playlist, index, seek, control_file, tmp,
1192 sizeof(tmp)) < 0)
1193 break ;
1195 /* Set the dircache entry pointer. */
1196 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1198 /* And be on background so user doesn't notice any delays. */
1199 yield();
1202 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1203 cpu_boost(false);
1204 #endif
1205 dirty_pointers = false;
1206 break ;
1208 #ifndef SIMULATOR
1209 case SYS_USB_CONNECTED:
1210 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1211 usb_wait_for_disconnect(&playlist_queue);
1212 break ;
1213 #endif
1217 #endif
1220 * gets pathname for track at seek index
1222 static int get_filename(struct playlist_info* playlist, int index, int seek,
1223 bool control_file, char *buf, int buf_length)
1225 int fd;
1226 int max = -1;
1227 char tmp_buf[MAX_PATH+1];
1228 char dir_buf[MAX_PATH+1];
1230 if (buf_length > MAX_PATH+1)
1231 buf_length = MAX_PATH+1;
1233 #ifdef HAVE_DIRCACHE
1234 if (dircache_is_enabled() && playlist->filenames)
1236 if (playlist->filenames[index] != NULL)
1238 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1239 max = strlen(tmp_buf) + 1;
1242 #else
1243 (void)index;
1244 #endif
1246 if (playlist->in_ram && !control_file && max < 0)
1248 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1249 tmp_buf[MAX_PATH] = '\0';
1250 max = strlen(tmp_buf) + 1;
1252 else if (max < 0)
1254 mutex_lock(&playlist->control_mutex);
1256 if (control_file)
1257 fd = playlist->control_fd;
1258 else
1260 if(-1 == playlist->fd)
1261 playlist->fd = open(playlist->filename, O_RDONLY);
1263 fd = playlist->fd;
1266 if(-1 != fd)
1269 if (lseek(fd, seek, SEEK_SET) != seek)
1270 max = -1;
1271 else
1272 max = read(fd, tmp_buf, buf_length);
1275 mutex_unlock(&playlist->control_mutex);
1277 if (max < 0)
1279 if (control_file)
1280 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1281 else
1282 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1284 return max;
1288 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1289 dir_buf[playlist->dirlen-1] = 0;
1291 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1294 static int get_next_directory(char *dir){
1295 return get_next_dir(dir,true,false);
1298 static int get_previous_directory(char *dir){
1299 return get_next_dir(dir,false,false);
1303 * search through all the directories (starting with the current) to find
1304 * one that has tracks to play
1306 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1308 struct playlist_info* playlist = &current_playlist;
1309 int result = -1;
1310 int sort_dir = global_settings.sort_dir;
1311 char *start_dir = NULL;
1312 bool exit = false;
1313 struct tree_context* tc = tree_get_context();
1314 int dirfilter = *(tc->dirfilter);
1316 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1318 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1319 char buffer[MAX_PATH];
1320 int folder_count = 0,i;
1321 srand(current_tick);
1322 if (fd >= 0)
1324 read(fd,&folder_count,sizeof(int));
1325 while (!exit)
1327 i = rand()%folder_count;
1328 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1329 read(fd,buffer,MAX_PATH);
1330 if (check_subdir_for_music(buffer,"") ==0)
1331 exit = true;
1333 strcpy(dir,buffer);
1334 close(fd);
1335 return 0;
1338 /* not random folder advance */
1339 if (recursion){
1340 /* start with root */
1341 dir[0] = '\0';
1343 else{
1344 /* start with current directory */
1345 strncpy(dir, playlist->filename, playlist->dirlen-1);
1346 dir[playlist->dirlen-1] = '\0';
1349 /* use the tree browser dircache to load files */
1350 *(tc->dirfilter) = SHOW_ALL;
1352 /* sort in another direction if previous dir is requested */
1353 if(!is_forward){
1354 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1355 global_settings.sort_dir = 4;
1356 else if (global_settings.sort_dir == 1)
1357 global_settings.sort_dir = 2;
1358 else if (global_settings.sort_dir == 2)
1359 global_settings.sort_dir = 1;
1360 else if (global_settings.sort_dir == 4)
1361 global_settings.sort_dir = 0;
1364 while (!exit)
1366 struct entry *files;
1367 int num_files = 0;
1368 int i;
1370 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1372 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1373 exit = true;
1374 result = -1;
1375 break;
1378 files = (struct entry*) tc->dircache;
1379 num_files = tc->filesindir;
1381 for (i=0; i<num_files; i++)
1383 /* user abort */
1384 if (action_userabort(TIMEOUT_NOBLOCK))
1386 result = -1;
1387 exit = true;
1388 break;
1391 if (files[i].attr & ATTR_DIRECTORY)
1393 if (!start_dir)
1395 result = check_subdir_for_music(dir, files[i].name);
1396 if (result != -1)
1398 exit = true;
1399 break;
1402 else if (!strcmp(start_dir, files[i].name))
1403 start_dir = NULL;
1407 if (!exit)
1409 /* move down to parent directory. current directory name is
1410 stored as the starting point for the search in parent */
1411 start_dir = strrchr(dir, '/');
1412 if (start_dir)
1414 *start_dir = '\0';
1415 start_dir++;
1417 else
1418 break;
1422 /* we've overwritten the dircache so tree browser will need to be
1423 reloaded */
1424 reload_directory();
1426 /* restore dirfilter & sort_dir */
1427 *(tc->dirfilter) = dirfilter;
1428 global_settings.sort_dir = sort_dir;
1430 /* special case if nothing found: try start searching again from root */
1431 if (result == -1 && !recursion){
1432 result = get_next_dir(dir,is_forward, true);
1435 return result;
1439 * Checks if there are any music files in the dir or any of its
1440 * subdirectories. May be called recursively.
1442 static int check_subdir_for_music(char *dir, char *subdir)
1444 int result = -1;
1445 int dirlen = strlen(dir);
1446 int num_files = 0;
1447 int i;
1448 struct entry *files;
1449 bool has_music = false;
1450 bool has_subdir = false;
1451 struct tree_context* tc = tree_get_context();
1453 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1455 if (ft_load(tc, dir) < 0)
1457 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1458 return -2;
1461 files = (struct entry*) tc->dircache;
1462 num_files = tc->filesindir;
1464 for (i=0; i<num_files; i++)
1466 if (files[i].attr & ATTR_DIRECTORY)
1467 has_subdir = true;
1468 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
1470 has_music = true;
1471 break;
1475 if (has_music)
1476 return 0;
1478 if (has_subdir)
1480 for (i=0; i<num_files; i++)
1482 if (action_userabort(TIMEOUT_NOBLOCK))
1484 result = -2;
1485 break;
1488 if (files[i].attr & ATTR_DIRECTORY)
1490 result = check_subdir_for_music(dir, files[i].name);
1491 if (!result)
1492 break;
1497 if (result < 0)
1499 if (dirlen)
1501 dir[dirlen] = '\0';
1503 else
1505 strcpy(dir, "/");
1508 /* we now need to reload our current directory */
1509 if(ft_load(tc, dir) < 0)
1510 gui_syncsplash(HZ*2, true,
1511 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1514 return result;
1518 * Returns absolute path of track
1520 static int format_track_path(char *dest, char *src, int buf_length, int max,
1521 char *dir)
1523 int i = 0;
1524 int j;
1525 char *temp_ptr;
1527 /* Zero-terminate the file name */
1528 while((src[i] != '\n') &&
1529 (src[i] != '\r') &&
1530 (i < max))
1531 i++;
1533 /* Now work back killing white space */
1534 while((src[i-1] == ' ') ||
1535 (src[i-1] == '\t'))
1536 i--;
1538 src[i]=0;
1540 /* replace backslashes with forward slashes */
1541 for ( j=0; j<i; j++ )
1542 if ( src[j] == '\\' )
1543 src[j] = '/';
1545 if('/' == src[0])
1547 strncpy(dest, src, buf_length);
1549 else
1551 /* handle dos style drive letter */
1552 if (':' == src[1])
1553 strncpy(dest, &src[2], buf_length);
1554 else if (!strncmp(src, "../", 3))
1556 /* handle relative paths */
1557 i=3;
1558 while(!strncmp(&src[i], "../", 3))
1559 i += 3;
1560 for (j=0; j<i/3; j++) {
1561 temp_ptr = strrchr(dir, '/');
1562 if (temp_ptr)
1563 *temp_ptr = '\0';
1564 else
1565 break;
1567 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1569 else if ( '.' == src[0] && '/' == src[1] ) {
1570 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1572 else {
1573 snprintf(dest, buf_length, "%s/%s", dir, src);
1577 return 0;
1581 * Display splash message showing progress of playlist/directory insertion or
1582 * save.
1584 static void display_playlist_count(int count, const unsigned char *fmt)
1586 lcd_clear_display();
1588 #ifdef HAVE_LCD_BITMAP
1589 if(global_settings.statusbar)
1590 lcd_setmargins(0, STATUSBAR_HEIGHT);
1591 else
1592 lcd_setmargins(0, 0);
1593 #endif
1595 gui_syncsplash(0, true, fmt, count,
1596 #if CONFIG_KEYPAD == PLAYER_PAD
1597 str(LANG_STOP_ABORT)
1598 #else
1599 str(LANG_OFF_ABORT)
1600 #endif
1605 * Display buffer full message
1607 static void display_buffer_full(void)
1609 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_BUFFER_FULL));
1613 * Flush any cached control commands to disk. Called when playlist is being
1614 * modified. Returns 0 on success and -1 on failure.
1616 static int flush_cached_control(struct playlist_info* playlist)
1618 int result = 0;
1619 int i;
1621 if (!playlist->num_cached)
1622 return 0;
1624 lseek(playlist->control_fd, 0, SEEK_END);
1626 for (i=0; i<playlist->num_cached; i++)
1628 struct playlist_control_cache* cache =
1629 &(playlist->control_cache[i]);
1631 switch (cache->command)
1633 case PLAYLIST_COMMAND_PLAYLIST:
1634 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1635 cache->i1, cache->s1, cache->s2);
1636 break;
1637 case PLAYLIST_COMMAND_ADD:
1638 case PLAYLIST_COMMAND_QUEUE:
1639 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1640 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1641 cache->i1, cache->i2);
1642 if (result > 0)
1644 /* save the position in file where name is written */
1645 int* seek_pos = (int *)cache->data;
1646 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1647 result = fdprintf(playlist->control_fd, "%s\n",
1648 cache->s1);
1650 break;
1651 case PLAYLIST_COMMAND_DELETE:
1652 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1653 break;
1654 case PLAYLIST_COMMAND_SHUFFLE:
1655 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1656 cache->i1, cache->i2);
1657 break;
1658 case PLAYLIST_COMMAND_UNSHUFFLE:
1659 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1660 break;
1661 case PLAYLIST_COMMAND_RESET:
1662 result = fdprintf(playlist->control_fd, "R\n");
1663 break;
1664 default:
1665 break;
1668 if (result <= 0)
1669 break;
1672 if (result > 0)
1674 if (global_settings.resume_seed >= 0)
1676 global_settings.resume_seed = -1;
1677 settings_save();
1680 playlist->num_cached = 0;
1681 playlist->pending_control_sync = true;
1683 result = 0;
1685 else
1687 result = -1;
1688 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1691 return result;
1695 * Update control data with new command. Depending on the command, it may be
1696 * cached or flushed to disk.
1698 static int update_control(struct playlist_info* playlist,
1699 enum playlist_command command, int i1, int i2,
1700 const char* s1, const char* s2, void* data)
1702 int result = 0;
1703 struct playlist_control_cache* cache;
1704 bool flush = false;
1706 mutex_lock(&playlist->control_mutex);
1708 cache = &(playlist->control_cache[playlist->num_cached++]);
1710 cache->command = command;
1711 cache->i1 = i1;
1712 cache->i2 = i2;
1713 cache->s1 = s1;
1714 cache->s2 = s2;
1715 cache->data = data;
1717 switch (command)
1719 case PLAYLIST_COMMAND_PLAYLIST:
1720 case PLAYLIST_COMMAND_ADD:
1721 case PLAYLIST_COMMAND_QUEUE:
1722 #ifndef HAVE_DIRCACHE
1723 case PLAYLIST_COMMAND_DELETE:
1724 case PLAYLIST_COMMAND_RESET:
1725 #endif
1726 flush = true;
1727 break;
1728 case PLAYLIST_COMMAND_SHUFFLE:
1729 case PLAYLIST_COMMAND_UNSHUFFLE:
1730 default:
1731 /* only flush when needed */
1732 break;
1735 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1736 result = flush_cached_control(playlist);
1738 mutex_unlock(&playlist->control_mutex);
1740 return result;
1744 * sync control file to disk
1746 static void sync_control(struct playlist_info* playlist, bool force)
1748 #ifdef HAVE_DIRCACHE
1749 if (playlist->started && force)
1750 #else
1751 (void) force;
1753 if (playlist->started)
1754 #endif
1756 if (playlist->pending_control_sync)
1758 mutex_lock(&playlist->control_mutex);
1759 fsync(playlist->control_fd);
1760 playlist->pending_control_sync = false;
1761 mutex_unlock(&playlist->control_mutex);
1767 * Rotate indices such that first_index is index 0
1769 static int rotate_index(const struct playlist_info* playlist, int index)
1771 index -= playlist->first_index;
1772 if (index < 0)
1773 index += playlist->amount;
1775 return index;
1779 * Initialize playlist entries at startup
1781 void playlist_init(void)
1783 struct playlist_info* playlist = &current_playlist;
1785 playlist->current = true;
1786 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1787 "%s", PLAYLIST_CONTROL_FILE);
1788 playlist->fd = -1;
1789 playlist->control_fd = -1;
1790 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1791 playlist->indices = buffer_alloc(
1792 playlist->max_playlist_size * sizeof(int));
1793 playlist->buffer_size =
1794 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1795 playlist->buffer = buffer_alloc(playlist->buffer_size);
1796 mutex_init(&playlist->control_mutex);
1797 empty_playlist(playlist, true);
1799 #ifdef HAVE_DIRCACHE
1800 playlist->filenames = buffer_alloc(
1801 playlist->max_playlist_size * sizeof(int));
1802 memset(playlist->filenames, 0,
1803 playlist->max_playlist_size * sizeof(int));
1804 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1805 playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND));
1806 queue_init(&playlist_queue, true);
1807 #endif
1811 * Clean playlist at shutdown
1813 void playlist_shutdown(void)
1815 struct playlist_info* playlist = &current_playlist;
1817 if (playlist->control_fd >= 0)
1819 mutex_lock(&playlist->control_mutex);
1821 if (playlist->num_cached > 0)
1822 flush_cached_control(playlist);
1824 close(playlist->control_fd);
1826 mutex_unlock(&playlist->control_mutex);
1831 * Create new playlist
1833 int playlist_create(const char *dir, const char *file)
1835 struct playlist_info* playlist = &current_playlist;
1837 new_playlist(playlist, dir, file);
1839 if (file)
1840 /* load the playlist file */
1841 add_indices_to_playlist(playlist, NULL, 0);
1843 return 0;
1846 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1849 * Restore the playlist state based on control file commands. Called to
1850 * resume playback after shutdown.
1852 int playlist_resume(void)
1854 struct playlist_info* playlist = &current_playlist;
1855 char *buffer;
1856 int buflen;
1857 int nread;
1858 int total_read = 0;
1859 int control_file_size = 0;
1860 bool first = true;
1861 bool sorted = true;
1863 /* use mp3 buffer for maximum load speed */
1864 #if CONFIG_CODEC != SWCODEC
1865 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1866 buflen = (audiobufend - audiobuf);
1867 buffer = (char *)audiobuf;
1868 #else
1869 buflen = (audiobufend - audiobuf - talk_get_bufsize());
1870 buffer = (char *)&audiobuf[talk_get_bufsize()];
1871 #endif
1873 empty_playlist(playlist, true);
1875 gui_syncsplash(0, true, str(LANG_WAIT));
1876 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1877 if (playlist->control_fd < 0)
1879 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1880 return -1;
1882 playlist->control_created = true;
1884 control_file_size = filesize(playlist->control_fd);
1885 if (control_file_size <= 0)
1887 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1888 return -1;
1891 /* read a small amount first to get the header */
1892 nread = read(playlist->control_fd, buffer,
1893 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1894 if(nread <= 0)
1896 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1897 return -1;
1900 playlist->started = true;
1902 while (1)
1904 int result = 0;
1905 int count;
1906 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1907 int last_newline = 0;
1908 int str_count = -1;
1909 bool newline = true;
1910 bool exit_loop = false;
1911 char *p = buffer;
1912 char *str1 = NULL;
1913 char *str2 = NULL;
1914 char *str3 = NULL;
1915 unsigned long last_tick = current_tick;
1917 for(count=0; count<nread && !exit_loop; count++,p++)
1919 /* So a splash while we are loading. */
1920 if (current_tick - last_tick > HZ/4)
1922 gui_syncsplash(0, true, str(LANG_LOADING_PERCENT),
1923 (total_read+count)*100/control_file_size,
1924 #if CONFIG_KEYPAD == PLAYER_PAD
1925 str(LANG_STOP_ABORT)
1926 #else
1927 str(LANG_OFF_ABORT)
1928 #endif
1930 if (action_userabort(TIMEOUT_NOBLOCK))
1932 /* FIXME:
1933 * Not sure how to implement this, somebody more familiar
1934 * with the code, please fix this. */
1936 last_tick = current_tick;
1939 /* Are we on a new line? */
1940 if((*p == '\n') || (*p == '\r'))
1942 *p = '\0';
1944 /* save last_newline in case we need to load more data */
1945 last_newline = count;
1947 switch (current_command)
1949 case PLAYLIST_COMMAND_PLAYLIST:
1951 /* str1=version str2=dir str3=file */
1952 int version;
1954 if (!str1)
1956 result = -1;
1957 exit_loop = true;
1958 break;
1961 if (!str2)
1962 str2 = "";
1964 if (!str3)
1965 str3 = "";
1967 version = atoi(str1);
1969 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1970 return -1;
1972 update_playlist_filename(playlist, str2, str3);
1974 if (str3[0] != '\0')
1976 /* NOTE: add_indices_to_playlist() overwrites the
1977 audiobuf so we need to reload control file
1978 data */
1979 add_indices_to_playlist(playlist, NULL, 0);
1981 else if (str2[0] != '\0')
1983 playlist->in_ram = true;
1984 resume_directory(str2);
1987 /* load the rest of the data */
1988 first = false;
1989 exit_loop = true;
1991 break;
1993 case PLAYLIST_COMMAND_ADD:
1994 case PLAYLIST_COMMAND_QUEUE:
1996 /* str1=position str2=last_position str3=file */
1997 int position, last_position;
1998 bool queue;
2000 if (!str1 || !str2 || !str3)
2002 result = -1;
2003 exit_loop = true;
2004 break;
2007 position = atoi(str1);
2008 last_position = atoi(str2);
2010 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2011 false:true;
2013 /* seek position is based on str3's position in
2014 buffer */
2015 if (add_track_to_playlist(playlist, str3, position,
2016 queue, total_read+(str3-buffer)) < 0)
2017 return -1;
2019 playlist->last_insert_pos = last_position;
2021 break;
2023 case PLAYLIST_COMMAND_DELETE:
2025 /* str1=position */
2026 int position;
2028 if (!str1)
2030 result = -1;
2031 exit_loop = true;
2032 break;
2035 position = atoi(str1);
2037 if (remove_track_from_playlist(playlist, position,
2038 false) < 0)
2039 return -1;
2041 break;
2043 case PLAYLIST_COMMAND_SHUFFLE:
2045 /* str1=seed str2=first_index */
2046 int seed;
2048 if (!str1 || !str2)
2050 result = -1;
2051 exit_loop = true;
2052 break;
2055 if (!sorted)
2057 /* Always sort list before shuffling */
2058 sort_playlist(playlist, false, false);
2061 seed = atoi(str1);
2062 playlist->first_index = atoi(str2);
2064 if (randomise_playlist(playlist, seed, false,
2065 false) < 0)
2066 return -1;
2068 sorted = false;
2069 break;
2071 case PLAYLIST_COMMAND_UNSHUFFLE:
2073 /* str1=first_index */
2074 if (!str1)
2076 result = -1;
2077 exit_loop = true;
2078 break;
2081 playlist->first_index = atoi(str1);
2083 if (sort_playlist(playlist, false, false) < 0)
2084 return -1;
2086 sorted = true;
2087 break;
2089 case PLAYLIST_COMMAND_RESET:
2091 playlist->last_insert_pos = -1;
2092 break;
2094 case PLAYLIST_COMMAND_COMMENT:
2095 default:
2096 break;
2099 newline = true;
2101 /* to ignore any extra newlines */
2102 current_command = PLAYLIST_COMMAND_COMMENT;
2104 else if(newline)
2106 newline = false;
2108 /* first non-comment line must always specify playlist */
2109 if (first && *p != 'P' && *p != '#')
2111 result = -1;
2112 exit_loop = true;
2113 break;
2116 switch (*p)
2118 case 'P':
2119 /* playlist can only be specified once */
2120 if (!first)
2122 result = -1;
2123 exit_loop = true;
2124 break;
2127 current_command = PLAYLIST_COMMAND_PLAYLIST;
2128 break;
2129 case 'A':
2130 current_command = PLAYLIST_COMMAND_ADD;
2131 break;
2132 case 'Q':
2133 current_command = PLAYLIST_COMMAND_QUEUE;
2134 break;
2135 case 'D':
2136 current_command = PLAYLIST_COMMAND_DELETE;
2137 break;
2138 case 'S':
2139 current_command = PLAYLIST_COMMAND_SHUFFLE;
2140 break;
2141 case 'U':
2142 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2143 break;
2144 case 'R':
2145 current_command = PLAYLIST_COMMAND_RESET;
2146 break;
2147 case '#':
2148 current_command = PLAYLIST_COMMAND_COMMENT;
2149 break;
2150 default:
2151 result = -1;
2152 exit_loop = true;
2153 break;
2156 str_count = -1;
2157 str1 = NULL;
2158 str2 = NULL;
2159 str3 = NULL;
2161 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2163 /* all control file strings are separated with a colon.
2164 Replace the colon with 0 to get proper strings that can be
2165 used by commands above */
2166 if (*p == ':')
2168 *p = '\0';
2169 str_count++;
2171 if ((count+1) < nread)
2173 switch (str_count)
2175 case 0:
2176 str1 = p+1;
2177 break;
2178 case 1:
2179 str2 = p+1;
2180 break;
2181 case 2:
2182 str3 = p+1;
2183 break;
2184 default:
2185 /* allow last string to contain colons */
2186 *p = ':';
2187 break;
2194 if (result < 0)
2196 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2197 return result;
2200 if (!newline || (exit_loop && count<nread))
2202 if ((total_read + count) >= control_file_size)
2204 /* no newline at end of control file */
2205 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
2206 return -1;
2209 /* We didn't end on a newline or we exited loop prematurely.
2210 Either way, re-read the remainder. */
2211 count = last_newline;
2212 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2215 total_read += count;
2217 if (first)
2218 /* still looking for header */
2219 nread = read(playlist->control_fd, buffer,
2220 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2221 else
2222 nread = read(playlist->control_fd, buffer, buflen);
2224 /* Terminate on EOF */
2225 if(nread <= 0)
2227 if (global_settings.resume_seed >= 0)
2229 /* Apply shuffle command saved in settings */
2230 if (global_settings.resume_seed == 0)
2231 sort_playlist(playlist, false, true);
2232 else
2234 if (!sorted)
2235 sort_playlist(playlist, false, false);
2237 randomise_playlist(playlist, global_settings.resume_seed,
2238 false, true);
2242 playlist->first_index = global_settings.resume_first_index;
2243 break;
2247 #ifdef HAVE_DIRCACHE
2248 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2249 #endif
2251 return 0;
2255 * Add track to in_ram playlist. Used when playing directories.
2257 int playlist_add(const char *filename)
2259 struct playlist_info* playlist = &current_playlist;
2260 int len = strlen(filename);
2262 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2263 (playlist->amount >= playlist->max_playlist_size))
2265 display_buffer_full();
2266 return -1;
2269 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2270 #ifdef HAVE_DIRCACHE
2271 playlist->filenames[playlist->amount] = NULL;
2272 #endif
2273 playlist->amount++;
2275 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2276 playlist->buffer_end_pos += len;
2277 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2279 return 0;
2282 /* shuffle newly created playlist using random seed. */
2283 int playlist_shuffle(int random_seed, int start_index)
2285 struct playlist_info* playlist = &current_playlist;
2287 unsigned int seek_pos = 0;
2288 bool start_current = false;
2290 if (start_index >= 0 && global_settings.play_selected)
2292 /* store the seek position before the shuffle */
2293 seek_pos = playlist->indices[start_index];
2294 playlist->index = global_settings.resume_first_index =
2295 playlist->first_index = start_index;
2296 start_current = true;
2299 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE));
2301 randomise_playlist(playlist, random_seed, start_current, true);
2303 return playlist->index;
2306 /* start playing current playlist at specified index/offset */
2307 int playlist_start(int start_index, int offset)
2309 struct playlist_info* playlist = &current_playlist;
2311 playlist->index = start_index;
2313 #if CONFIG_CODEC != SWCODEC
2314 talk_buffer_steal(); /* will use the mp3 buffer */
2315 #endif
2317 playlist->started = true;
2318 sync_control(playlist, false);
2319 audio_play(offset);
2321 return 0;
2324 /* Returns false if 'steps' is out of bounds, else true */
2325 bool playlist_check(int steps)
2327 struct playlist_info* playlist = &current_playlist;
2329 /* always allow folder navigation */
2330 if (global_settings.next_folder && playlist->in_ram)
2331 return true;
2333 int index = get_next_index(playlist, steps, -1);
2335 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2336 index = get_next_index(playlist, steps, REPEAT_ALL);
2338 return (index >= 0);
2341 /* get trackname of track that is "steps" away from current playing track.
2342 NULL is used to identify end of playlist */
2343 char* playlist_peek(int steps)
2345 struct playlist_info* playlist = &current_playlist;
2346 int seek;
2347 int fd;
2348 char *temp_ptr;
2349 int index;
2350 bool control_file;
2352 index = get_next_index(playlist, steps, -1);
2353 if (index < 0)
2354 return NULL;
2356 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2357 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2359 if (get_filename(playlist, index, seek, control_file, now_playing,
2360 MAX_PATH+1) < 0)
2361 return NULL;
2363 temp_ptr = now_playing;
2365 if (!playlist->in_ram || control_file)
2367 /* remove bogus dirs from beginning of path
2368 (workaround for buggy playlist creation tools) */
2369 while (temp_ptr)
2371 #ifdef HAVE_DIRCACHE
2372 if (dircache_is_enabled())
2374 if (dircache_get_entry_ptr(temp_ptr))
2375 break;
2377 else
2378 #endif
2380 fd = open(temp_ptr, O_RDONLY);
2381 if (fd >= 0)
2383 close(fd);
2384 break;
2388 temp_ptr = strchr(temp_ptr+1, '/');
2391 if (!temp_ptr)
2393 /* Even though this is an invalid file, we still need to pass a
2394 file name to the caller because NULL is used to indicate end
2395 of playlist */
2396 return now_playing;
2400 return temp_ptr;
2404 * Update indices as track has changed
2406 int playlist_next(int steps)
2408 struct playlist_info* playlist = &current_playlist;
2409 int index;
2411 if ( (steps > 0)
2412 #if (AB_REPEAT_ENABLE == 1)
2413 && (global_settings.repeat_mode != REPEAT_AB)
2414 #endif
2415 && (global_settings.repeat_mode != REPEAT_ONE) )
2417 int i, j;
2419 /* We need to delete all the queued songs */
2420 for (i=0, j=steps; i<j; i++)
2422 index = get_next_index(playlist, i, -1);
2424 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2426 remove_track_from_playlist(playlist, index, true);
2427 steps--; /* one less track */
2432 index = get_next_index(playlist, steps, -1);
2434 if (index < 0)
2436 /* end of playlist... or is it */
2437 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2438 global_settings.playlist_shuffle &&
2439 playlist->amount > 1)
2441 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2442 playlist->first_index = global_settings.resume_first_index = 0;
2443 sort_playlist(playlist, false, false);
2444 randomise_playlist(playlist, current_tick, false, true);
2445 #if CONFIG_CODEC != SWCODEC
2446 playlist_start(0, 0);
2447 #endif
2448 playlist->index = 0;
2449 index = 0;
2451 else if (playlist->in_ram && global_settings.next_folder)
2453 char dir[MAX_PATH+1];
2455 changing_dir = true;
2456 if (steps > 0)
2458 if (!get_next_directory(dir))
2460 /* start playing next directory */
2461 if (playlist_create(dir, NULL) != -1)
2463 ft_build_playlist(tree_get_context(), 0);
2464 if (global_settings.playlist_shuffle)
2465 playlist_shuffle(current_tick, -1);
2466 #if CONFIG_CODEC != SWCODEC
2467 playlist_start(0, 0);
2468 #endif
2469 playlist->index = index = 0;
2473 else
2475 if (!get_previous_directory(dir))
2477 /* start playing previous directory */
2478 if (playlist_create(dir, NULL) != -1)
2480 ft_build_playlist(tree_get_context(), 0);
2481 if (global_settings.playlist_shuffle)
2482 playlist_shuffle(current_tick, -1);
2483 #if CONFIG_CODEC != SWCODEC
2484 playlist_start(current_playlist.amount-1, 0);
2485 #endif
2486 playlist->index = index = current_playlist.amount - 1;
2490 changing_dir = false;
2493 return index;
2496 playlist->index = index;
2498 if (playlist->last_insert_pos >= 0 && steps > 0)
2500 /* check to see if we've gone beyond the last inserted track */
2501 int cur = rotate_index(playlist, index);
2502 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2504 if (cur > last_pos)
2506 /* reset last inserted track */
2507 playlist->last_insert_pos = -1;
2509 if (playlist->control_fd >= 0)
2511 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2512 -1, -1, NULL, NULL, NULL);
2514 if (result < 0)
2515 return result;
2517 sync_control(playlist, false);
2522 return index;
2525 /* try playing next or previous folder */
2526 bool playlist_next_dir(int direction)
2528 char dir[MAX_PATH+1];
2529 bool result;
2530 int res;
2532 /* not to mess up real playlists */
2533 if(!current_playlist.in_ram)
2534 return false;
2536 if(changing_dir)
2537 return false;
2539 changing_dir = true;
2540 if(direction > 0)
2541 res = get_next_directory(dir);
2542 else
2543 res = get_previous_directory(dir);
2544 if (!res)
2546 if (playlist_create(dir, NULL) != -1)
2548 ft_build_playlist(tree_get_context(), 0);
2549 if (global_settings.playlist_shuffle)
2550 playlist_shuffle(current_tick, -1);
2551 #if (CONFIG_CODEC != SWCODEC)
2552 playlist_start(0,0);
2553 #endif
2554 result = true;
2556 else
2557 result = false;
2559 else
2560 result = false;
2562 changing_dir = false;
2564 return result;
2567 /* Get resume info for current playing song. If return value is -1 then
2568 settings shouldn't be saved. */
2569 int playlist_get_resume_info(int *resume_index)
2571 struct playlist_info* playlist = &current_playlist;
2573 *resume_index = playlist->index;
2575 return 0;
2578 /* Update resume info for current playing song. Returns -1 on error. */
2579 int playlist_update_resume_info(const struct mp3entry* id3)
2581 struct playlist_info* playlist = &current_playlist;
2583 if (id3)
2585 if (global_settings.resume_index != playlist->index ||
2586 global_settings.resume_offset != id3->offset)
2588 global_settings.resume_index = playlist->index;
2589 global_settings.resume_offset = id3->offset;
2590 settings_save();
2593 else
2595 global_settings.resume_index = -1;
2596 global_settings.resume_offset = -1;
2597 settings_save();
2600 return 0;
2603 /* Returns index of current playing track for display purposes. This value
2604 should not be used for resume purposes as it doesn't represent the actual
2605 index into the playlist */
2606 int playlist_get_display_index(void)
2608 struct playlist_info* playlist = &current_playlist;
2610 /* first_index should always be index 0 for display purposes */
2611 int index = rotate_index(playlist, playlist->index);
2613 return (index+1);
2616 /* returns number of tracks in current playlist */
2617 int playlist_amount(void)
2619 return playlist_amount_ex(NULL);
2623 * Create a new playlist If playlist is not NULL then we're loading a
2624 * playlist off disk for viewing/editing. The index_buffer is used to store
2625 * playlist indices (required for and only used if !current playlist). The
2626 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2628 int playlist_create_ex(struct playlist_info* playlist,
2629 const char* dir, const char* file,
2630 void* index_buffer, int index_buffer_size,
2631 void* temp_buffer, int temp_buffer_size)
2633 if (!playlist)
2634 playlist = &current_playlist;
2635 else
2637 /* Initialize playlist structure */
2638 int r = rand() % 10;
2639 playlist->current = false;
2641 /* Use random name for control file */
2642 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2643 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2644 playlist->fd = -1;
2645 playlist->control_fd = -1;
2647 if (index_buffer)
2649 int num_indices = index_buffer_size / sizeof(int);
2651 #ifdef HAVE_DIRCACHE
2652 num_indices /= 2;
2653 #endif
2654 if (num_indices > global_settings.max_files_in_playlist)
2655 num_indices = global_settings.max_files_in_playlist;
2657 playlist->max_playlist_size = num_indices;
2658 playlist->indices = index_buffer;
2659 #ifdef HAVE_DIRCACHE
2660 playlist->filenames = (const struct dircache_entry **)
2661 &playlist->indices[num_indices];
2662 #endif
2664 else
2666 playlist->max_playlist_size = current_playlist.max_playlist_size;
2667 playlist->indices = current_playlist.indices;
2668 #ifdef HAVE_DIRCACHE
2669 playlist->filenames = current_playlist.filenames;
2670 #endif
2673 playlist->buffer_size = 0;
2674 playlist->buffer = NULL;
2675 mutex_init(&playlist->control_mutex);
2678 new_playlist(playlist, dir, file);
2680 if (file)
2681 /* load the playlist file */
2682 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2684 return 0;
2688 * Set the specified playlist as the current.
2689 * NOTE: You will get undefined behaviour if something is already playing so
2690 * remember to stop before calling this. Also, this call will
2691 * effectively close your playlist, making it unusable.
2693 int playlist_set_current(struct playlist_info* playlist)
2695 if (!playlist || (check_control(playlist) < 0))
2696 return -1;
2698 empty_playlist(&current_playlist, false);
2700 strncpy(current_playlist.filename, playlist->filename,
2701 sizeof(current_playlist.filename));
2703 current_playlist.fd = playlist->fd;
2705 close(playlist->control_fd);
2706 close(current_playlist.control_fd);
2707 remove(current_playlist.control_filename);
2708 if (rename(playlist->control_filename,
2709 current_playlist.control_filename) < 0)
2710 return -1;
2711 current_playlist.control_fd = open(current_playlist.control_filename,
2712 O_RDWR);
2713 if (current_playlist.control_fd < 0)
2714 return -1;
2715 current_playlist.control_created = true;
2717 current_playlist.dirlen = playlist->dirlen;
2719 if (playlist->indices && playlist->indices != current_playlist.indices)
2721 memcpy(current_playlist.indices, playlist->indices,
2722 playlist->max_playlist_size*sizeof(int));
2723 #ifdef HAVE_DIRCACHE
2724 memcpy(current_playlist.filenames, playlist->filenames,
2725 playlist->max_playlist_size*sizeof(int));
2726 #endif
2729 current_playlist.first_index = playlist->first_index;
2730 current_playlist.amount = playlist->amount;
2731 current_playlist.last_insert_pos = playlist->last_insert_pos;
2732 current_playlist.seed = playlist->seed;
2733 current_playlist.shuffle_modified = playlist->shuffle_modified;
2734 current_playlist.deleted = playlist->deleted;
2735 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2737 memcpy(current_playlist.control_cache, playlist->control_cache,
2738 sizeof(current_playlist.control_cache));
2739 current_playlist.num_cached = playlist->num_cached;
2740 current_playlist.pending_control_sync = playlist->pending_control_sync;
2742 return 0;
2746 * Close files and delete control file for non-current playlist.
2748 void playlist_close(struct playlist_info* playlist)
2750 if (!playlist)
2751 return;
2753 if (playlist->fd >= 0)
2754 close(playlist->fd);
2756 if (playlist->control_fd >= 0)
2757 close(playlist->control_fd);
2759 if (playlist->control_created)
2760 remove(playlist->control_filename);
2763 void playlist_sync(struct playlist_info* playlist)
2765 if (!playlist)
2766 playlist = &current_playlist;
2768 sync_control(playlist, false);
2769 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2770 audio_flush_and_reload_tracks();
2772 #ifdef HAVE_DIRCACHE
2773 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2774 #endif
2778 * Insert track into playlist at specified position (or one of the special
2779 * positions). Returns position where track was inserted or -1 if error.
2781 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2782 int position, bool queue, bool sync)
2784 int result;
2786 if (!playlist)
2787 playlist = &current_playlist;
2789 if (check_control(playlist) < 0)
2791 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2792 return -1;
2795 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2797 /* Check if we want manually sync later. For example when adding
2798 * bunch of files from tagcache, syncing after every file wouldn't be
2799 * a good thing to do. */
2800 if (sync && result >= 0)
2801 playlist_sync(playlist);
2803 return result;
2807 * Insert all tracks from specified directory into playlist.
2809 int playlist_insert_directory(struct playlist_info* playlist,
2810 const char *dirname, int position, bool queue,
2811 bool recurse)
2813 int result;
2814 unsigned char *count_str;
2815 struct directory_search_context context;
2817 if (!playlist)
2818 playlist = &current_playlist;
2820 if (check_control(playlist) < 0)
2822 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2823 return -1;
2826 if (queue)
2827 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2828 else
2829 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2831 display_playlist_count(0, count_str);
2833 context.playlist = playlist;
2834 context.position = position;
2835 context.queue = queue;
2836 context.count = 0;
2838 cpu_boost_id(true, CPUBOOSTID_PLAYLIST);
2840 result = playlist_directory_tracksearch(dirname, recurse,
2841 directory_search_callback, &context);
2843 sync_control(playlist, false);
2845 cpu_boost_id(false, CPUBOOSTID_PLAYLIST);
2847 display_playlist_count(context.count, count_str);
2849 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2850 audio_flush_and_reload_tracks();
2852 #ifdef HAVE_DIRCACHE
2853 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2854 #endif
2856 return result;
2860 * Insert all tracks from specified playlist into dynamic playlist.
2862 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2863 int position, bool queue)
2865 int fd;
2866 int max;
2867 char *temp_ptr;
2868 char *dir;
2869 unsigned char *count_str;
2870 char temp_buf[MAX_PATH+1];
2871 char trackname[MAX_PATH+1];
2872 int count = 0;
2873 int result = 0;
2875 if (!playlist)
2876 playlist = &current_playlist;
2878 if (check_control(playlist) < 0)
2880 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2881 return -1;
2884 fd = open(filename, O_RDONLY);
2885 if (fd < 0)
2887 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2888 return -1;
2891 /* we need the directory name for formatting purposes */
2892 dir = filename;
2894 temp_ptr = strrchr(filename+1,'/');
2895 if (temp_ptr)
2896 *temp_ptr = 0;
2897 else
2898 dir = "/";
2900 if (queue)
2901 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2902 else
2903 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2905 display_playlist_count(count, count_str);
2907 cpu_boost_id(true, CPUBOOSTID_PLAYLIST);
2909 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2911 /* user abort */
2912 if (action_userabort(TIMEOUT_NOBLOCK))
2913 break;
2915 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2917 int insert_pos;
2919 /* we need to format so that relative paths are correctly
2920 handled */
2921 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2922 dir) < 0)
2924 result = -1;
2925 break;
2928 insert_pos = add_track_to_playlist(playlist, trackname, position,
2929 queue, -1);
2931 if (insert_pos < 0)
2933 result = -1;
2934 break;
2937 /* Make sure tracks are inserted in correct order if user
2938 requests INSERT_FIRST */
2939 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2940 position = insert_pos + 1;
2942 count++;
2944 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2946 display_playlist_count(count, count_str);
2948 if (count == PLAYLIST_DISPLAY_COUNT &&
2949 (audio_status() & AUDIO_STATUS_PLAY) &&
2950 playlist->started)
2951 audio_flush_and_reload_tracks();
2955 /* let the other threads work */
2956 yield();
2959 close(fd);
2961 if (temp_ptr)
2962 *temp_ptr = '/';
2964 sync_control(playlist, false);
2966 cpu_boost_id(false, CPUBOOSTID_PLAYLIST);
2968 display_playlist_count(count, count_str);
2970 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2971 audio_flush_and_reload_tracks();
2973 #ifdef HAVE_DIRCACHE
2974 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2975 #endif
2977 return result;
2981 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2982 * we want to delete the current playing track.
2984 int playlist_delete(struct playlist_info* playlist, int index)
2986 int result = 0;
2988 if (!playlist)
2989 playlist = &current_playlist;
2991 if (check_control(playlist) < 0)
2993 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2994 return -1;
2997 if (index == PLAYLIST_DELETE_CURRENT)
2998 index = playlist->index;
3000 result = remove_track_from_playlist(playlist, index, true);
3002 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3003 playlist->started)
3004 audio_flush_and_reload_tracks();
3006 return result;
3010 * Move track at index to new_index. Tracks between the two are shifted
3011 * appropriately. Returns 0 on success and -1 on failure.
3013 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3015 int result;
3016 int seek;
3017 bool control_file;
3018 bool queue;
3019 bool current = false;
3020 int r;
3021 char filename[MAX_PATH];
3023 if (!playlist)
3024 playlist = &current_playlist;
3026 if (check_control(playlist) < 0)
3028 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3029 return -1;
3032 if (index == new_index)
3033 return -1;
3035 if (index == playlist->index)
3036 /* Moving the current track */
3037 current = true;
3039 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3040 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3041 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3043 if (get_filename(playlist, index, seek, control_file, filename,
3044 sizeof(filename)) < 0)
3045 return -1;
3047 /* Delete track from original position */
3048 result = remove_track_from_playlist(playlist, index, true);
3050 if (result != -1)
3052 /* We want to insert the track at the position that was specified by
3053 new_index. This may be different then new_index because of the
3054 shifting that occurred after the delete */
3055 r = rotate_index(playlist, new_index);
3057 if (r == 0)
3058 /* First index */
3059 new_index = PLAYLIST_PREPEND;
3060 else if (r == playlist->amount)
3061 /* Append */
3062 new_index = PLAYLIST_INSERT_LAST;
3063 else
3064 /* Calculate index of desired position */
3065 new_index = (r+playlist->first_index)%playlist->amount;
3067 result = add_track_to_playlist(playlist, filename, new_index, queue,
3068 -1);
3070 if (result != -1)
3072 if (current)
3074 /* Moved the current track */
3075 switch (new_index)
3077 case PLAYLIST_PREPEND:
3078 playlist->index = playlist->first_index;
3079 break;
3080 case PLAYLIST_INSERT_LAST:
3081 playlist->index = playlist->first_index - 1;
3082 if (playlist->index < 0)
3083 playlist->index += playlist->amount;
3084 break;
3085 default:
3086 playlist->index = new_index;
3087 break;
3091 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3092 audio_flush_and_reload_tracks();
3096 #ifdef HAVE_DIRCACHE
3097 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3098 #endif
3100 return result;
3103 /* shuffle currently playing playlist */
3104 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3105 bool start_current)
3107 int result;
3109 if (!playlist)
3110 playlist = &current_playlist;
3112 check_control(playlist);
3114 result = randomise_playlist(playlist, seed, start_current, true);
3116 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3117 playlist->started)
3118 audio_flush_and_reload_tracks();
3120 return result;
3123 /* sort currently playing playlist */
3124 int playlist_sort(struct playlist_info* playlist, bool start_current)
3126 int result;
3128 if (!playlist)
3129 playlist = &current_playlist;
3131 check_control(playlist);
3133 result = sort_playlist(playlist, start_current, true);
3135 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3136 playlist->started)
3137 audio_flush_and_reload_tracks();
3139 return result;
3142 /* returns true if playlist has been modified */
3143 bool playlist_modified(const struct playlist_info* playlist)
3145 if (!playlist)
3146 playlist = &current_playlist;
3148 if (playlist->shuffle_modified ||
3149 playlist->deleted ||
3150 playlist->num_inserted_tracks > 0)
3151 return true;
3153 return false;
3156 /* returns index of first track in playlist */
3157 int playlist_get_first_index(const struct playlist_info* playlist)
3159 if (!playlist)
3160 playlist = &current_playlist;
3162 return playlist->first_index;
3165 /* returns shuffle seed of playlist */
3166 int playlist_get_seed(const struct playlist_info* playlist)
3168 if (!playlist)
3169 playlist = &current_playlist;
3171 return playlist->seed;
3174 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3175 int playlist_amount_ex(const struct playlist_info* playlist)
3177 if (!playlist)
3178 playlist = &current_playlist;
3180 return playlist->amount;
3183 /* returns full path of playlist (minus extension) */
3184 char *playlist_name(const struct playlist_info* playlist, char *buf,
3185 int buf_size)
3187 char *sep;
3189 if (!playlist)
3190 playlist = &current_playlist;
3192 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3194 if (!buf[0])
3195 return NULL;
3197 /* Remove extension */
3198 sep = strrchr(buf, '.');
3199 if (sep)
3200 *sep = 0;
3202 return buf;
3205 /* returns the playlist filename */
3206 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3207 int buf_size)
3209 if (!playlist)
3210 playlist = &current_playlist;
3212 snprintf(buf, buf_size, "%s", playlist->filename);
3214 if (!buf[0])
3215 return NULL;
3217 return buf;
3220 /* Fills info structure with information about track at specified index.
3221 Returns 0 on success and -1 on failure */
3222 int playlist_get_track_info(struct playlist_info* playlist, int index,
3223 struct playlist_track_info* info)
3225 int seek;
3226 bool control_file;
3228 if (!playlist)
3229 playlist = &current_playlist;
3231 if (index < 0 || index >= playlist->amount)
3232 return -1;
3234 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3235 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3237 if (get_filename(playlist, index, seek, control_file, info->filename,
3238 sizeof(info->filename)) < 0)
3239 return -1;
3241 info->attr = 0;
3243 if (control_file)
3245 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3246 info->attr |= PLAYLIST_ATTR_QUEUED;
3247 else
3248 info->attr |= PLAYLIST_ATTR_INSERTED;
3252 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3253 info->attr |= PLAYLIST_ATTR_SKIPPED;
3255 info->index = index;
3256 info->display_index = rotate_index(playlist, index) + 1;
3258 return 0;
3261 /* save the current dynamic playlist to specified file */
3262 int playlist_save(struct playlist_info* playlist, char *filename)
3264 int fd;
3265 int i, index;
3266 int count = 0;
3267 char path[MAX_PATH+1];
3268 char tmp_buf[MAX_PATH+1];
3269 int result = 0;
3270 bool overwrite_current = false;
3271 int* index_buf = NULL;
3273 if (!playlist)
3274 playlist = &current_playlist;
3276 if (playlist->amount <= 0)
3277 return -1;
3279 /* use current working directory as base for pathname */
3280 if (format_track_path(path, filename, sizeof(tmp_buf),
3281 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3282 return -1;
3284 if (!strncmp(playlist->filename, path, strlen(path)))
3286 /* Attempting to overwrite current playlist file.*/
3288 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3290 /* not enough buffer space to store updated indices */
3291 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3292 return -1;
3295 /* in_ram buffer is unused for m3u files so we'll use for storing
3296 updated indices */
3297 index_buf = (int*)playlist->buffer;
3299 /* use temporary pathname */
3300 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3301 overwrite_current = true;
3304 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3305 if (fd < 0)
3307 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3308 return -1;
3311 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3313 cpu_boost_id(true, CPUBOOSTID_PLAYLIST);
3315 index = playlist->first_index;
3316 for (i=0; i<playlist->amount; i++)
3318 bool control_file;
3319 bool queue;
3320 int seek;
3322 /* user abort */
3323 if (action_userabort(TIMEOUT_NOBLOCK))
3325 result = -1;
3326 break;
3329 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3330 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3331 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3333 /* Don't save queued files */
3334 if (!queue)
3336 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3337 MAX_PATH+1) < 0)
3339 result = -1;
3340 break;
3343 if (overwrite_current)
3344 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3346 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3348 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
3349 result = -1;
3350 break;
3353 count++;
3355 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3356 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3358 yield();
3361 index = (index+1)%playlist->amount;
3364 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
3366 close(fd);
3368 if (overwrite_current && result >= 0)
3370 result = -1;
3372 mutex_lock(&playlist->control_mutex);
3374 /* Replace the current playlist with the new one and update indices */
3375 close(playlist->fd);
3376 if (remove(playlist->filename) >= 0)
3378 if (rename(path, playlist->filename) >= 0)
3380 playlist->fd = open(playlist->filename, O_RDONLY);
3381 if (playlist->fd >= 0)
3383 index = playlist->first_index;
3384 for (i=0, count=0; i<playlist->amount; i++)
3386 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3388 playlist->indices[index] = index_buf[count];
3389 count++;
3391 index = (index+1)%playlist->amount;
3394 /* we need to recreate control because inserted tracks are
3395 now part of the playlist and shuffle has been
3396 invalidated */
3397 result = recreate_control(playlist);
3402 mutex_unlock(&playlist->control_mutex);
3406 cpu_boost_id(false, CPUBOOSTID_PLAYLIST);
3408 return result;
3412 * Search specified directory for tracks and notify via callback. May be
3413 * called recursively.
3415 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3416 int (*callback)(char*, void*),
3417 void* context)
3419 char buf[MAX_PATH+1];
3420 int result = 0;
3421 int num_files = 0;
3422 int i;
3423 struct entry *files;
3424 struct tree_context* tc = tree_get_context();
3425 int old_dirfilter = *(tc->dirfilter);
3427 if (!callback)
3428 return -1;
3430 /* use the tree browser dircache to load files */
3431 *(tc->dirfilter) = SHOW_ALL;
3433 if (ft_load(tc, dirname) < 0)
3435 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3436 *(tc->dirfilter) = old_dirfilter;
3437 return -1;
3440 files = (struct entry*) tc->dircache;
3441 num_files = tc->filesindir;
3443 /* we've overwritten the dircache so tree browser will need to be
3444 reloaded */
3445 reload_directory();
3447 for (i=0; i<num_files; i++)
3449 /* user abort */
3450 if (action_userabort(TIMEOUT_NOBLOCK))
3452 result = -1;
3453 break;
3456 if (files[i].attr & ATTR_DIRECTORY)
3458 if (recurse)
3460 /* recursively add directories */
3461 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3462 result = playlist_directory_tracksearch(buf, recurse,
3463 callback, context);
3464 if (result < 0)
3465 break;
3467 /* we now need to reload our current directory */
3468 if(ft_load(tc, dirname) < 0)
3470 result = -1;
3471 break;
3474 files = (struct entry*) tc->dircache;
3475 num_files = tc->filesindir;
3476 if (!num_files)
3478 result = -1;
3479 break;
3482 else
3483 continue;
3485 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
3487 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3489 if (callback(buf, context) != 0)
3491 result = -1;
3492 break;
3495 /* let the other threads work */
3496 yield();
3500 /* restore dirfilter */
3501 *(tc->dirfilter) = old_dirfilter;
3503 return result;