1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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
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
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.
81 #include "applimits.h"
89 #ifdef HAVE_LCD_BITMAP
98 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
99 #define PLAYLIST_CONTROL_FILE_VERSION 2
102 Each playlist index has a flag associated with it which identifies what
103 type of track it is. These flags are stored in the 4 high order bits of
106 NOTE: This limits the playlist file size to a max of 256M.
110 01 = Track was prepended into playlist
111 10 = Track was inserted into playlist
112 11 = Track was appended into playlist
117 0 = Track entry is valid
118 1 = Track does not exist on disk and should be skipped
120 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
121 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
122 #define PLAYLIST_QUEUE_MASK 0x20000000
124 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
125 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
126 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
128 #define PLAYLIST_QUEUED 0x20000000
129 #define PLAYLIST_SKIPPED 0x10000000
131 #define PLAYLIST_DISPLAY_COUNT 10
133 static bool changing_dir
= false;
135 static struct playlist_info current_playlist
;
136 static char now_playing
[MAX_PATH
+1];
138 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
139 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
141 static void create_control(struct playlist_info
* playlist
);
142 static int check_control(struct playlist_info
* playlist
);
143 static void update_playlist_filename(struct playlist_info
* playlist
,
144 const char *dir
, const char *file
);
145 static int add_indices_to_playlist(struct playlist_info
* playlist
,
146 char* buffer
, int buflen
);
147 static int add_track_to_playlist(struct playlist_info
* playlist
,
148 const char *filename
, int position
,
149 bool queue
, int seek_pos
);
150 static int add_directory_to_playlist(struct playlist_info
* playlist
,
151 const char *dirname
, int *position
,
152 bool queue
, int *count
, bool recurse
);
153 static int remove_track_from_playlist(struct playlist_info
* playlist
,
154 int position
, bool write
);
155 static int randomise_playlist(struct playlist_info
* playlist
,
156 unsigned int seed
, bool start_current
,
158 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
160 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
162 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
164 static int compare(const void* p1
, const void* p2
);
165 static int get_filename(struct playlist_info
* playlist
, int seek
,
166 bool control_file
, char *buf
, int buf_length
);
167 static int get_next_directory(char *dir
);
168 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
169 static int get_previous_directory(char *dir
);
170 static int check_subdir_for_music(char *dir
, char *subdir
);
171 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
173 static void display_playlist_count(int count
, const char *fmt
);
174 static void display_buffer_full(void);
175 static int flush_pending_control(struct playlist_info
* playlist
);
176 static int rotate_index(const struct playlist_info
* playlist
, int index
);
179 * remove any files and indices associated with the playlist
181 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
183 playlist
->filename
[0] = '\0';
185 if(playlist
->fd
>= 0)
186 /* If there is an already open playlist, close it. */
190 if(playlist
->control_fd
>= 0)
191 close(playlist
->control_fd
);
192 playlist
->control_fd
= -1;
193 playlist
->control_created
= false;
195 playlist
->in_ram
= false;
197 if (playlist
->buffer
)
198 playlist
->buffer
[0] = 0;
200 playlist
->buffer_end_pos
= 0;
203 playlist
->first_index
= 0;
204 playlist
->amount
= 0;
205 playlist
->last_insert_pos
= -1;
207 playlist
->shuffle_modified
= false;
208 playlist
->deleted
= false;
209 playlist
->num_inserted_tracks
= 0;
210 playlist
->shuffle_flush
= false;
212 if (!resume
&& playlist
->current
)
214 /* start with fresh playlist control file when starting new
216 create_control(playlist
);
218 /* Reset resume settings */
219 global_settings
.resume_first_index
= 0;
220 global_settings
.resume_seed
= -1;
225 * Initialize a new playlist for viewing/editing/playing. dir is the
226 * directory where the playlist is located and file is the filename.
228 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
231 empty_playlist(playlist
, false);
237 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
238 playlist
->in_ram
= true;
240 dir
= ""; /* empty playlist */
243 update_playlist_filename(playlist
, dir
, file
);
245 if (playlist
->control_fd
>= 0)
247 if (fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
248 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
) > 0)
249 fsync(playlist
->control_fd
);
251 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
256 * create control file for playlist
258 static void create_control(struct playlist_info
* playlist
)
260 playlist
->control_fd
= open(playlist
->control_filename
,
261 O_CREAT
|O_RDWR
|O_TRUNC
);
262 if (playlist
->control_fd
< 0)
264 if (check_rockboxdir())
266 gui_syncsplash(HZ
*2, true, "%s (%d)",
267 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
268 playlist
->control_fd
);
270 playlist
->control_created
= false;
274 playlist
->control_created
= true;
279 * validate the control file. This may include creating/initializing it if
282 static int check_control(struct playlist_info
* playlist
)
284 if (!playlist
->control_created
)
286 create_control(playlist
);
288 if (playlist
->control_fd
>= 0)
290 char* dir
= playlist
->filename
;
291 char* file
= playlist
->filename
+playlist
->dirlen
;
292 char c
= playlist
->filename
[playlist
->dirlen
-1];
294 playlist
->filename
[playlist
->dirlen
-1] = '\0';
296 if (fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
297 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
) > 0)
298 fsync(playlist
->control_fd
);
300 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
302 playlist
->filename
[playlist
->dirlen
-1] = c
;
306 if (playlist
->control_fd
< 0)
313 * store directory and name of playlist file
315 static void update_playlist_filename(struct playlist_info
* playlist
,
316 const char *dir
, const char *file
)
319 int dirlen
= strlen(dir
);
321 /* If the dir does not end in trailing slash, we use a separator.
322 Otherwise we don't. */
323 if('/' != dir
[dirlen
-1])
329 playlist
->dirlen
= dirlen
;
331 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
332 "%s%s%s", dir
, sep
, file
);
336 * calculate track offsets within a playlist file
338 static int add_indices_to_playlist(struct playlist_info
* playlist
,
339 char* buffer
, int buflen
)
343 unsigned int count
= 0;
347 if(-1 == playlist
->fd
)
348 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
350 return -1; /* failure */
352 #ifdef HAVE_LCD_BITMAP
353 if(global_settings
.statusbar
)
354 lcd_setmargins(0, STATUSBAR_HEIGHT
);
356 lcd_setmargins(0, 0);
359 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD
));
363 /* use mp3 buffer for maximum load speed */
365 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
368 buflen
= (audiobufend
- audiobuf
);
375 nread
= read(playlist
->fd
, buffer
, buflen
);
376 /* Terminate on EOF */
382 for(count
=0; count
< nread
; count
++,p
++) {
384 /* Are we on a new line? */
385 if((*p
== '\n') || (*p
== '\r'))
395 /* Store a new entry */
396 playlist
->indices
[ playlist
->amount
] = i
+count
;
398 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
399 display_buffer_full();
413 * Add track to playlist at specified position. There are five special
414 * positions that can be specified:
415 * PLAYLIST_PREPEND - Add track at beginning of playlist
416 * PLAYLIST_INSERT - Add track after current song. NOTE: If
417 * there are already inserted tracks then track
418 * is added to the end of the insertion list
419 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
420 * matter what other tracks have been inserted
421 * PLAYLIST_INSERT_LAST - Add track to end of playlist
422 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
423 * current playing track and end of playlist
425 static int add_track_to_playlist(struct playlist_info
* playlist
,
426 const char *filename
, int position
,
427 bool queue
, int seek_pos
)
429 int insert_position
= position
;
430 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
433 if (playlist
->amount
>= playlist
->max_playlist_size
)
435 display_buffer_full();
441 case PLAYLIST_PREPEND
:
442 insert_position
= playlist
->first_index
;
443 flags
= PLAYLIST_INSERT_TYPE_PREPEND
;
445 case PLAYLIST_INSERT
:
446 /* if there are already inserted tracks then add track to end of
447 insertion list else add after current playing track */
448 if (playlist
->last_insert_pos
>= 0 &&
449 playlist
->last_insert_pos
< playlist
->amount
&&
450 (playlist
->indices
[playlist
->last_insert_pos
]&
451 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
452 position
= insert_position
= playlist
->last_insert_pos
+1;
453 else if (playlist
->amount
> 0)
454 position
= insert_position
= playlist
->index
+ 1;
456 position
= insert_position
= 0;
458 playlist
->last_insert_pos
= position
;
460 case PLAYLIST_INSERT_FIRST
:
461 if (playlist
->amount
> 0)
462 position
= insert_position
= playlist
->index
+ 1;
464 position
= insert_position
= 0;
466 if (playlist
->last_insert_pos
< 0)
467 playlist
->last_insert_pos
= position
;
469 case PLAYLIST_INSERT_LAST
:
470 if (playlist
->first_index
> 0)
471 insert_position
= playlist
->first_index
;
473 insert_position
= playlist
->amount
;
475 flags
= PLAYLIST_INSERT_TYPE_APPEND
;
477 case PLAYLIST_INSERT_SHUFFLED
:
480 int n
= playlist
->amount
-
481 rotate_index(playlist
, playlist
->index
);
488 position
= playlist
->index
+ offset
+ 1;
489 if (position
>= playlist
->amount
)
490 position
-= playlist
->amount
;
492 insert_position
= position
;
498 flags
|= PLAYLIST_QUEUED
;
500 /* shift indices so that track can be added */
501 for (i
=playlist
->amount
; i
>insert_position
; i
--)
502 playlist
->indices
[i
] = playlist
->indices
[i
-1];
504 /* update stored indices if needed */
505 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
)
508 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
509 position
!= PLAYLIST_PREPEND
)
511 playlist
->first_index
++;
513 if (seek_pos
< 0 && playlist
->current
)
515 global_settings
.resume_first_index
= playlist
->first_index
;
520 if (insert_position
< playlist
->last_insert_pos
||
521 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
522 playlist
->last_insert_pos
++;
524 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
528 if (flush_pending_control(playlist
) < 0)
531 mutex_lock(&playlist
->control_mutex
);
533 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
535 if (fdprintf(playlist
->control_fd
, "%c:%d:%d:", (queue
?'Q':'A'),
536 position
, playlist
->last_insert_pos
) > 0)
538 /* save the position in file where track name is written */
539 seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
541 if (fdprintf(playlist
->control_fd
, "%s\n", filename
) > 0)
546 mutex_unlock(&playlist
->control_mutex
);
550 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
555 playlist
->indices
[insert_position
] = flags
| seek_pos
;
558 playlist
->num_inserted_tracks
++;
560 return insert_position
;
564 * Insert directory into playlist. May be called recursively.
566 static int add_directory_to_playlist(struct playlist_info
* playlist
,
567 const char *dirname
, int *position
,
568 bool queue
, int *count
, bool recurse
)
570 char buf
[MAX_PATH
+1];
575 int dirfilter
= global_settings
.dirfilter
;
577 struct tree_context
* tc
= tree_get_context();
579 /* use the tree browser dircache to load files */
580 global_settings
.dirfilter
= SHOW_ALL
;
582 if (ft_load(tc
, dirname
) < 0)
584 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
585 global_settings
.dirfilter
= dirfilter
;
589 files
= (struct entry
*) tc
->dircache
;
590 num_files
= tc
->filesindir
;
592 /* we've overwritten the dircache so tree browser will need to be
597 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
599 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
601 for (i
=0; i
<num_files
; i
++)
604 if (button_get(false) == SETTINGS_CANCEL
)
610 if (files
[i
].attr
& ATTR_DIRECTORY
)
614 /* recursively add directories */
615 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
616 result
= add_directory_to_playlist(playlist
, buf
, position
,
617 queue
, count
, recurse
);
621 /* we now need to reload our current directory */
622 if(ft_load(tc
, dirname
) < 0)
628 files
= (struct entry
*) tc
->dircache
;
629 num_files
= tc
->filesindir
;
639 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
643 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
645 insert_pos
= add_track_to_playlist(playlist
, buf
, *position
,
655 /* Make sure tracks are inserted in correct order if user requests
657 if (*position
== PLAYLIST_INSERT_FIRST
|| *position
>= 0)
658 *position
= insert_pos
+ 1;
660 if ((*count
%PLAYLIST_DISPLAY_COUNT
) == 0)
662 display_playlist_count(*count
, count_str
);
664 if (*count
== PLAYLIST_DISPLAY_COUNT
&&
665 (audio_status() & AUDIO_STATUS_PLAY
))
666 audio_flush_and_reload_tracks();
669 /* let the other threads work */
674 /* restore dirfilter */
675 global_settings
.dirfilter
= dirfilter
;
681 * remove track at specified position
683 static int remove_track_from_playlist(struct playlist_info
* playlist
,
684 int position
, bool write
)
689 if (playlist
->amount
<= 0)
692 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
694 /* shift indices now that track has been removed */
695 for (i
=position
; i
<playlist
->amount
; i
++)
696 playlist
->indices
[i
] = playlist
->indices
[i
+1];
701 playlist
->num_inserted_tracks
--;
703 playlist
->deleted
= true;
705 /* update stored indices if needed */
706 if (position
< playlist
->index
)
709 if (position
< playlist
->first_index
)
711 playlist
->first_index
--;
715 global_settings
.resume_first_index
= playlist
->first_index
;
720 if (position
<= playlist
->last_insert_pos
)
721 playlist
->last_insert_pos
--;
723 if (write
&& playlist
->control_fd
>= 0)
727 if (flush_pending_control(playlist
) < 0)
730 mutex_lock(&playlist
->control_mutex
);
732 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
734 if (fdprintf(playlist
->control_fd
, "D:%d\n", position
) > 0)
736 fsync(playlist
->control_fd
);
741 mutex_unlock(&playlist
->control_mutex
);
745 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
754 * randomly rearrange the array of indices for the playlist. If start_current
755 * is true then update the index to the new index of the current playing track
757 static int randomise_playlist(struct playlist_info
* playlist
,
758 unsigned int seed
, bool start_current
,
764 unsigned int current
= playlist
->indices
[playlist
->index
];
766 /* seed 0 is used to identify sorted playlist for resume purposes */
770 /* seed with the given seed */
773 /* randomise entire indices list */
774 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
776 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
777 candidate
= rand() % (count
+ 1);
779 /* now swap the values at the 'count' and 'candidate' positions */
780 store
= playlist
->indices
[candidate
];
781 playlist
->indices
[candidate
] = playlist
->indices
[count
];
782 playlist
->indices
[count
] = store
;
786 find_and_set_playlist_index(playlist
, current
);
788 /* indices have been moved so last insert position is no longer valid */
789 playlist
->last_insert_pos
= -1;
791 playlist
->seed
= seed
;
792 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
793 playlist
->shuffle_modified
= true;
797 /* Don't write to disk immediately. Instead, save in settings and
798 only flush if playlist is modified (insertion/deletion) */
799 playlist
->shuffle_flush
= true;
800 global_settings
.resume_seed
= seed
;
808 * Sort the array of indices for the playlist. If start_current is true then
809 * set the index to the new index of the current song.
811 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
814 unsigned int current
= playlist
->indices
[playlist
->index
];
816 if (playlist
->amount
> 0)
817 qsort(playlist
->indices
, playlist
->amount
,
818 sizeof(playlist
->indices
[0]), compare
);
821 find_and_set_playlist_index(playlist
, current
);
823 /* indices have been moved so last insert position is no longer valid */
824 playlist
->last_insert_pos
= -1;
826 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
827 playlist
->shuffle_modified
= false;
828 if (write
&& playlist
->control_fd
>= 0)
830 /* Don't write to disk immediately. Instead, save in settings and
831 only flush if playlist is modified (insertion/deletion) */
832 playlist
->shuffle_flush
= true;
833 global_settings
.resume_seed
= 0;
840 /* Marks the index of the track to be skipped that is "steps" away from
841 * current playing track.
843 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
847 if (playlist
== NULL
)
848 playlist
= ¤t_playlist
;
850 index
= rotate_index(playlist
, playlist
->index
);
852 if (index
< 0 || index
>= playlist
->amount
)
855 index
= (index
+playlist
->first_index
) % playlist
->amount
;
856 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
859 /* Calculate how many steps we have to really step when skipping entries
862 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
864 int i
, count
, direction
;
866 int stepped_count
= 0;
879 index
= playlist
->index
;
886 index
+= playlist
->amount
;
887 if (index
>= playlist
->amount
)
888 index
-= playlist
->amount
;
890 /* Check if we found a bad entry. */
891 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
894 /* Are all entries bad? */
895 if (stepped_count
++ > playlist
->amount
)
907 * returns the index of the track that is "steps" away from current playing
910 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
913 int current_index
= playlist
->index
;
916 if (playlist
->amount
<= 0)
919 if (repeat_mode
== -1)
920 repeat_mode
= global_settings
.repeat_mode
;
922 if (repeat_mode
== REPEAT_SHUFFLE
&&
923 (!global_settings
.playlist_shuffle
|| playlist
->amount
<= 1))
924 repeat_mode
= REPEAT_ALL
;
926 steps
= calculate_step_count(playlist
, steps
);
930 /* Treat repeat shuffle just like repeat off. At end of playlist,
931 play will be resumed in playlist_next() */
934 current_index
= rotate_index(playlist
, current_index
);
935 next_index
= current_index
+steps
;
936 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
939 next_index
= (next_index
+playlist
->first_index
) %
946 #ifdef AB_REPEAT_ENABLE
949 next_index
= current_index
;
955 next_index
= (current_index
+steps
) % playlist
->amount
;
956 while (next_index
< 0)
957 next_index
+= playlist
->amount
;
959 if (steps
>= playlist
->amount
)
966 /* second time around so skip the queued files */
967 for (i
=0; i
<playlist
->amount
; i
++)
969 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
970 index
= (index
+1) % playlist
->amount
;
982 /* No luck if the whole playlist was bad. */
983 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
990 * Search for the seek track and set appropriate indices. Used after shuffle
991 * to make sure the current index is still pointing to correct track.
993 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
998 /* Set the index to the current song */
999 for (i
=0; i
<playlist
->amount
; i
++)
1001 if (playlist
->indices
[i
] == seek
)
1003 playlist
->index
= playlist
->first_index
= i
;
1005 if (playlist
->current
)
1007 global_settings
.resume_first_index
= i
;
1017 * used to sort track indices. Sort order is as follows:
1018 * 1. Prepended tracks (in prepend order)
1019 * 2. Playlist/directory tracks (in playlist order)
1020 * 3. Inserted/Appended tracks (in insert order)
1022 static int compare(const void* p1
, const void* p2
)
1024 unsigned long* e1
= (unsigned long*) p1
;
1025 unsigned long* e2
= (unsigned long*) p2
;
1026 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1027 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1029 if (flags1
== flags2
)
1030 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1031 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1032 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1034 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1035 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1037 else if (flags1
&& flags2
)
1038 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1044 * gets pathname for track at seek index
1046 static int get_filename(struct playlist_info
* playlist
, int seek
,
1047 bool control_file
, char *buf
, int buf_length
)
1051 char tmp_buf
[MAX_PATH
+1];
1052 char dir_buf
[MAX_PATH
+1];
1054 if (buf_length
> MAX_PATH
+1)
1055 buf_length
= MAX_PATH
+1;
1057 if (playlist
->in_ram
&& !control_file
)
1059 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1060 tmp_buf
[MAX_PATH
] = '\0';
1061 max
= strlen(tmp_buf
) + 1;
1066 fd
= playlist
->control_fd
;
1069 if(-1 == playlist
->fd
)
1070 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1078 mutex_lock(&playlist
->control_mutex
);
1080 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1083 max
= read(fd
, tmp_buf
, buf_length
);
1086 mutex_unlock(&playlist
->control_mutex
);
1092 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1094 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1100 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1101 dir_buf
[playlist
->dirlen
-1] = 0;
1103 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1106 static int get_next_directory(char *dir
){
1107 return get_next_dir(dir
,true,false);
1110 static int get_previous_directory(char *dir
){
1111 return get_next_dir(dir
,false,false);
1115 * search through all the directories (starting with the current) to find
1116 * one that has tracks to play
1118 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1120 struct playlist_info
* playlist
= ¤t_playlist
;
1122 int dirfilter
= global_settings
.dirfilter
;
1123 int sort_dir
= global_settings
.sort_dir
;
1124 char *start_dir
= NULL
;
1126 struct tree_context
* tc
= tree_get_context();
1129 /* start with root */
1133 /* start with current directory */
1134 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1135 dir
[playlist
->dirlen
-1] = '\0';
1138 /* use the tree browser dircache to load files */
1139 global_settings
.dirfilter
= SHOW_ALL
;
1141 /* sort in another direction if previous dir is requested */
1143 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1144 global_settings
.sort_dir
= 4;
1145 else if (global_settings
.sort_dir
== 1)
1146 global_settings
.sort_dir
= 2;
1147 else if (global_settings
.sort_dir
== 2)
1148 global_settings
.sort_dir
= 1;
1149 else if (global_settings
.sort_dir
== 4)
1150 global_settings
.sort_dir
= 0;
1155 struct entry
*files
;
1159 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1161 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1167 files
= (struct entry
*) tc
->dircache
;
1168 num_files
= tc
->filesindir
;
1170 for (i
=0; i
<num_files
; i
++)
1173 if (button_get(false) == SETTINGS_CANCEL
)
1180 if (files
[i
].attr
& ATTR_DIRECTORY
)
1184 result
= check_subdir_for_music(dir
, files
[i
].name
);
1191 else if (!strcmp(start_dir
, files
[i
].name
))
1198 /* move down to parent directory. current directory name is
1199 stored as the starting point for the search in parent */
1200 start_dir
= strrchr(dir
, '/');
1211 /* we've overwritten the dircache so tree browser will need to be
1215 /* restore dirfilter & sort_dir */
1216 global_settings
.dirfilter
= dirfilter
;
1217 global_settings
.sort_dir
= sort_dir
;
1219 /* special case if nothing found: try start searching again from root */
1220 if (result
== -1 && !recursion
){
1221 result
= get_next_dir(dir
,is_forward
, true);
1228 * Checks if there are any music files in the dir or any of its
1229 * subdirectories. May be called recursively.
1231 static int check_subdir_for_music(char *dir
, char *subdir
)
1234 int dirlen
= strlen(dir
);
1237 struct entry
*files
;
1238 bool has_music
= false;
1239 bool has_subdir
= false;
1240 struct tree_context
* tc
= tree_get_context();
1242 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1244 if (ft_load(tc
, dir
) < 0)
1246 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1250 files
= (struct entry
*) tc
->dircache
;
1251 num_files
= tc
->filesindir
;
1253 for (i
=0; i
<num_files
; i
++)
1255 if (files
[i
].attr
& ATTR_DIRECTORY
)
1257 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
1269 for (i
=0; i
<num_files
; i
++)
1271 if (button_get(false) == SETTINGS_CANCEL
)
1277 if (files
[i
].attr
& ATTR_DIRECTORY
)
1279 result
= check_subdir_for_music(dir
, files
[i
].name
);
1297 /* we now need to reload our current directory */
1298 if(ft_load(tc
, dir
) < 0)
1299 gui_syncsplash(HZ
*2, true,
1300 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1307 * Returns absolute path of track
1309 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1316 /* Zero-terminate the file name */
1317 while((src
[i
] != '\n') &&
1322 /* Now work back killing white space */
1323 while((src
[i
-1] == ' ') ||
1329 /* replace backslashes with forward slashes */
1330 for ( j
=0; j
<i
; j
++ )
1331 if ( src
[j
] == '\\' )
1336 strncpy(dest
, src
, buf_length
);
1340 /* handle dos style drive letter */
1342 strncpy(dest
, &src
[2], buf_length
);
1343 else if (!strncmp(src
, "../", 3))
1345 /* handle relative paths */
1347 while(!strncmp(&src
[i
], "../", 3))
1349 for (j
=0; j
<i
/3; j
++) {
1350 temp_ptr
= strrchr(dir
, '/');
1356 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1358 else if ( '.' == src
[0] && '/' == src
[1] ) {
1359 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1362 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1370 * Display splash message showing progress of playlist/directory insertion or
1373 static void display_playlist_count(int count
, const char *fmt
)
1375 lcd_clear_display();
1377 #ifdef HAVE_LCD_BITMAP
1378 if(global_settings
.statusbar
)
1379 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1381 lcd_setmargins(0, 0);
1384 gui_syncsplash(0, true, fmt
, count
,
1385 #if CONFIG_KEYPAD == PLAYER_PAD
1386 str(LANG_STOP_ABORT
)
1394 * Display buffer full message
1396 static void display_buffer_full(void)
1398 gui_syncsplash(HZ
*2, true, "%s %s",
1399 str(LANG_PLAYINDICES_PLAYLIST
),
1400 str(LANG_PLAYINDICES_BUFFER
));
1404 * Flush any pending control commands to disk. Called when playlist is being
1405 * modified. Returns 0 on success and -1 on failure.
1407 static int flush_pending_control(struct playlist_info
* playlist
)
1411 if (playlist
->shuffle_flush
&& global_settings
.resume_seed
>= 0)
1413 /* pending shuffle */
1414 mutex_lock(&playlist
->control_mutex
);
1416 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
1418 if (global_settings
.resume_seed
== 0)
1419 result
= fdprintf(playlist
->control_fd
, "U:%d\n",
1420 playlist
->first_index
);
1422 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1423 global_settings
.resume_seed
, playlist
->first_index
);
1427 fsync(playlist
->control_fd
);
1429 playlist
->shuffle_flush
= false;
1430 global_settings
.resume_seed
= -1;
1441 mutex_unlock(&playlist
->control_mutex
);
1445 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1454 * Rotate indices such that first_index is index 0
1456 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1458 index
-= playlist
->first_index
;
1460 index
+= playlist
->amount
;
1466 * Initialize playlist entries at startup
1468 void playlist_init(void)
1470 struct playlist_info
* playlist
= ¤t_playlist
;
1472 playlist
->current
= true;
1473 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1474 "%s", PLAYLIST_CONTROL_FILE
);
1476 playlist
->control_fd
= -1;
1477 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1478 playlist
->indices
= buffer_alloc(
1479 playlist
->max_playlist_size
* sizeof(int));
1480 playlist
->buffer_size
=
1481 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1482 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1483 mutex_init(&playlist
->control_mutex
);
1484 empty_playlist(playlist
, true);
1488 * Create new playlist
1490 int playlist_create(const char *dir
, const char *file
)
1492 struct playlist_info
* playlist
= ¤t_playlist
;
1494 new_playlist(playlist
, dir
, file
);
1497 /* load the playlist file */
1498 add_indices_to_playlist(playlist
, NULL
, 0);
1503 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1506 * Restore the playlist state based on control file commands. Called to
1507 * resume playback after shutdown.
1509 int playlist_resume(void)
1511 struct playlist_info
* playlist
= ¤t_playlist
;
1516 int control_file_size
= 0;
1531 /* use mp3 buffer for maximum load speed */
1532 #if CONFIG_CODEC != SWCODEC
1533 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1534 buflen
= (audiobufend
- audiobuf
);
1537 buflen
= (audiobufend
- audiobuf
- talk_get_bufsize());
1538 buffer
= &audiobuf
[talk_get_bufsize()];
1541 empty_playlist(playlist
, true);
1543 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1544 if (playlist
->control_fd
< 0)
1546 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1549 playlist
->control_created
= true;
1551 control_file_size
= filesize(playlist
->control_fd
);
1552 if (control_file_size
<= 0)
1554 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1558 /* read a small amount first to get the header */
1559 nread
= read(playlist
->control_fd
, buffer
,
1560 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1563 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1571 int current_command
= resume_comment
;
1572 int last_newline
= 0;
1574 bool newline
= true;
1575 bool exit_loop
= false;
1581 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1583 /* Are we on a new line? */
1584 if((*p
== '\n') || (*p
== '\r'))
1588 /* save last_newline in case we need to load more data */
1589 last_newline
= count
;
1591 switch (current_command
)
1593 case resume_playlist
:
1595 /* str1=version str2=dir str3=file */
1611 version
= atoi(str1
);
1613 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
1616 update_playlist_filename(playlist
, str2
, str3
);
1618 if (str3
[0] != '\0')
1620 /* NOTE: add_indices_to_playlist() overwrites the
1621 audiobuf so we need to reload control file
1623 add_indices_to_playlist(playlist
, NULL
, 0);
1625 else if (str2
[0] != '\0')
1627 playlist
->in_ram
= true;
1628 resume_directory(str2
);
1631 /* load the rest of the data */
1640 /* str1=position str2=last_position str3=file */
1641 int position
, last_position
;
1644 if (!str1
|| !str2
|| !str3
)
1651 position
= atoi(str1
);
1652 last_position
= atoi(str2
);
1654 queue
= (current_command
== resume_add
)?false:true;
1656 /* seek position is based on str3's position in
1658 if (add_track_to_playlist(playlist
, str3
, position
,
1659 queue
, total_read
+(str3
-buffer
)) < 0)
1662 playlist
->last_insert_pos
= last_position
;
1678 position
= atoi(str1
);
1680 if (remove_track_from_playlist(playlist
, position
,
1686 case resume_shuffle
:
1688 /* str1=seed str2=first_index */
1700 /* Always sort list before shuffling */
1701 sort_playlist(playlist
, false, false);
1705 playlist
->first_index
= atoi(str2
);
1707 if (randomise_playlist(playlist
, seed
, false,
1714 case resume_unshuffle
:
1716 /* str1=first_index */
1724 playlist
->first_index
= atoi(str1
);
1726 if (sort_playlist(playlist
, false, false) < 0)
1734 playlist
->last_insert_pos
= -1;
1737 case resume_comment
:
1744 /* to ignore any extra newlines */
1745 current_command
= resume_comment
;
1751 /* first non-comment line must always specify playlist */
1752 if (first
&& *p
!= 'P' && *p
!= '#')
1762 /* playlist can only be specified once */
1770 current_command
= resume_playlist
;
1773 current_command
= resume_add
;
1776 current_command
= resume_queue
;
1779 current_command
= resume_delete
;
1782 current_command
= resume_shuffle
;
1785 current_command
= resume_unshuffle
;
1788 current_command
= resume_reset
;
1791 current_command
= resume_comment
;
1804 else if(current_command
!= resume_comment
)
1806 /* all control file strings are separated with a colon.
1807 Replace the colon with 0 to get proper strings that can be
1808 used by commands above */
1814 if ((count
+1) < nread
)
1828 /* allow last string to contain colons */
1839 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
1843 if (!newline
|| (exit_loop
&& count
<nread
))
1845 if ((total_read
+ count
) >= control_file_size
)
1847 /* no newline at end of control file */
1848 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
1852 /* We didn't end on a newline or we exited loop prematurely.
1853 Either way, re-read the remainder. */
1854 count
= last_newline
;
1855 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
1858 total_read
+= count
;
1861 /* still looking for header */
1862 nread
= read(playlist
->control_fd
, buffer
,
1863 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1865 nread
= read(playlist
->control_fd
, buffer
, buflen
);
1867 /* Terminate on EOF */
1870 if (global_settings
.resume_seed
>= 0)
1872 /* Apply shuffle command saved in settings */
1873 if (global_settings
.resume_seed
== 0)
1874 sort_playlist(playlist
, false, true);
1878 sort_playlist(playlist
, false, false);
1880 randomise_playlist(playlist
, global_settings
.resume_seed
,
1884 playlist
->first_index
= global_settings
.resume_first_index
;
1895 * Add track to in_ram playlist. Used when playing directories.
1897 int playlist_add(const char *filename
)
1899 struct playlist_info
* playlist
= ¤t_playlist
;
1900 int len
= strlen(filename
);
1902 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
1903 (playlist
->amount
>= playlist
->max_playlist_size
))
1905 display_buffer_full();
1909 playlist
->indices
[playlist
->amount
++] = playlist
->buffer_end_pos
;
1911 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
1912 playlist
->buffer_end_pos
+= len
;
1913 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
1918 /* shuffle newly created playlist using random seed. */
1919 int playlist_shuffle(int random_seed
, int start_index
)
1921 struct playlist_info
* playlist
= ¤t_playlist
;
1923 unsigned int seek_pos
= 0;
1924 bool start_current
= false;
1926 if (start_index
>= 0 && global_settings
.play_selected
)
1928 /* store the seek position before the shuffle */
1929 seek_pos
= playlist
->indices
[start_index
];
1930 playlist
->index
= global_settings
.resume_first_index
=
1931 playlist
->first_index
= start_index
;
1932 start_current
= true;
1935 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
1937 randomise_playlist(playlist
, random_seed
, start_current
, true);
1939 /* Flush shuffle command to disk */
1940 flush_pending_control(playlist
);
1942 return playlist
->index
;
1945 /* start playing current playlist at specified index/offset */
1946 int playlist_start(int start_index
, int offset
)
1948 struct playlist_info
* playlist
= ¤t_playlist
;
1950 playlist
->index
= start_index
;
1951 #if CONFIG_CODEC != SWCODEC
1952 talk_buffer_steal(); /* will use the mp3 buffer */
1959 /* Returns false if 'steps' is out of bounds, else true */
1960 bool playlist_check(int steps
)
1962 struct playlist_info
* playlist
= ¤t_playlist
;
1964 /* always allow folder navigation */
1965 if (global_settings
.next_folder
&& playlist
->in_ram
)
1968 int index
= get_next_index(playlist
, steps
, -1);
1970 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
1971 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
1973 return (index
>= 0);
1976 /* get trackname of track that is "steps" away from current playing track.
1977 NULL is used to identify end of playlist */
1978 char* playlist_peek(int steps
)
1980 struct playlist_info
* playlist
= ¤t_playlist
;
1987 index
= get_next_index(playlist
, steps
, -1);
1991 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1992 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1994 if (get_filename(playlist
, seek
, control_file
, now_playing
,
1998 temp_ptr
= now_playing
;
2000 if (!playlist
->in_ram
|| control_file
)
2002 /* remove bogus dirs from beginning of path
2003 (workaround for buggy playlist creation tools) */
2006 fd
= open(temp_ptr
, O_RDONLY
);
2013 temp_ptr
= strchr(temp_ptr
+1, '/');
2018 /* Even though this is an invalid file, we still need to pass a
2019 file name to the caller because NULL is used to indicate end
2029 * Update indices as track has changed
2031 int playlist_next(int steps
)
2033 struct playlist_info
* playlist
= ¤t_playlist
;
2037 #ifdef AB_REPEAT_ENABLE
2038 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2040 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2044 /* We need to delete all the queued songs */
2045 for (i
=0, j
=steps
; i
<j
; i
++)
2047 index
= get_next_index(playlist
, i
, -1);
2049 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2051 remove_track_from_playlist(playlist
, index
, true);
2052 steps
--; /* one less track */
2057 index
= get_next_index(playlist
, steps
, -1);
2061 /* end of playlist... or is it */
2062 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2063 global_settings
.playlist_shuffle
&&
2064 playlist
->amount
> 1)
2066 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2067 playlist
->first_index
= global_settings
.resume_first_index
= 0;
2068 sort_playlist(playlist
, false, false);
2069 randomise_playlist(playlist
, current_tick
, false, true);
2070 playlist_start(0, 0);
2073 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2075 char dir
[MAX_PATH
+1];
2077 changing_dir
= true;
2080 if (!get_next_directory(dir
))
2082 /* start playing next directory */
2083 if (playlist_create(dir
, NULL
) != -1)
2085 ft_build_playlist(tree_get_context(), 0);
2086 if (global_settings
.playlist_shuffle
)
2087 playlist_shuffle(current_tick
, -1);
2088 playlist_start(0, 0);
2095 if (!get_previous_directory(dir
))
2097 /* start playing previous directory */
2098 if (playlist_create(dir
, NULL
) != -1)
2100 ft_build_playlist(tree_get_context(), 0);
2101 if (global_settings
.playlist_shuffle
)
2102 playlist_shuffle(current_tick
, -1);
2103 playlist_start(current_playlist
.amount
-1,0);
2104 index
= current_playlist
.amount
-1;
2108 changing_dir
= true;
2114 playlist
->index
= index
;
2116 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2118 /* check to see if we've gone beyond the last inserted track */
2119 int cur
= rotate_index(playlist
, index
);
2120 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2124 /* reset last inserted track */
2125 playlist
->last_insert_pos
= -1;
2127 if (playlist
->control_fd
>= 0)
2131 mutex_lock(&playlist
->control_mutex
);
2133 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
2135 if (fdprintf(playlist
->control_fd
, "R\n") > 0)
2137 fsync(playlist
->control_fd
);
2142 mutex_unlock(&playlist
->control_mutex
);
2146 gui_syncsplash(HZ
*2, true,
2147 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
2157 /* try playing next or previous folder */
2158 bool playlist_next_dir(int direction
)
2160 char dir
[MAX_PATH
+1];
2164 /* not to mess up real playlists */
2165 if(!current_playlist
.in_ram
)
2171 changing_dir
= true;
2173 res
= get_next_directory(dir
);
2175 res
= get_previous_directory(dir
);
2178 if (playlist_create(dir
, NULL
) != -1)
2180 ft_build_playlist(tree_get_context(), 0);
2181 if (global_settings
.playlist_shuffle
)
2182 playlist_shuffle(current_tick
, -1);
2183 playlist_start(0,0);
2192 changing_dir
= false;
2197 /* Get resume info for current playing song. If return value is -1 then
2198 settings shouldn't be saved. */
2199 int playlist_get_resume_info(int *resume_index
)
2201 struct playlist_info
* playlist
= ¤t_playlist
;
2203 *resume_index
= playlist
->index
;
2208 /* Update resume info for current playing song. Returns -1 on error. */
2209 int playlist_update_resume_info(const struct mp3entry
* id3
)
2211 struct playlist_info
* playlist
= ¤t_playlist
;
2215 if (global_settings
.resume_index
!= playlist
->index
||
2216 global_settings
.resume_offset
!= id3
->offset
)
2218 global_settings
.resume_index
= playlist
->index
;
2219 global_settings
.resume_offset
= id3
->offset
;
2225 global_settings
.resume_index
= -1;
2226 global_settings
.resume_offset
= -1;
2233 /* Returns index of current playing track for display purposes. This value
2234 should not be used for resume purposes as it doesn't represent the actual
2235 index into the playlist */
2236 int playlist_get_display_index(void)
2238 struct playlist_info
* playlist
= ¤t_playlist
;
2240 /* first_index should always be index 0 for display purposes */
2241 int index
= rotate_index(playlist
, playlist
->index
);
2246 /* returns number of tracks in current playlist */
2247 int playlist_amount(void)
2249 return playlist_amount_ex(NULL
);
2253 * Create a new playlist If playlist is not NULL then we're loading a
2254 * playlist off disk for viewing/editing. The index_buffer is used to store
2255 * playlist indices (required for and only used if !current playlist). The
2256 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2258 int playlist_create_ex(struct playlist_info
* playlist
,
2259 const char* dir
, const char* file
,
2260 void* index_buffer
, int index_buffer_size
,
2261 void* temp_buffer
, int temp_buffer_size
)
2264 playlist
= ¤t_playlist
;
2267 /* Initialize playlist structure */
2268 int r
= rand() % 10;
2269 playlist
->current
= false;
2271 /* Use random name for control file */
2272 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2273 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2275 playlist
->control_fd
= -1;
2279 int num_indices
= index_buffer_size
/ sizeof(int);
2281 if (num_indices
> global_settings
.max_files_in_playlist
)
2282 num_indices
= global_settings
.max_files_in_playlist
;
2284 playlist
->max_playlist_size
= num_indices
;
2285 playlist
->indices
= index_buffer
;
2289 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2290 playlist
->indices
= current_playlist
.indices
;
2293 playlist
->buffer_size
= 0;
2294 playlist
->buffer
= NULL
;
2295 mutex_init(&playlist
->control_mutex
);
2298 new_playlist(playlist
, dir
, file
);
2301 /* load the playlist file */
2302 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2308 * Set the specified playlist as the current.
2309 * NOTE: You will get undefined behaviour if something is already playing so
2310 * remember to stop before calling this. Also, this call will
2311 * effectively close your playlist, making it unusable.
2313 int playlist_set_current(struct playlist_info
* playlist
)
2315 if (!playlist
|| (check_control(playlist
) < 0))
2318 empty_playlist(¤t_playlist
, false);
2320 strncpy(current_playlist
.filename
, playlist
->filename
,
2321 sizeof(current_playlist
.filename
));
2323 current_playlist
.fd
= playlist
->fd
;
2325 close(playlist
->control_fd
);
2326 remove(current_playlist
.control_filename
);
2327 if (rename(playlist
->control_filename
,
2328 current_playlist
.control_filename
) < 0)
2330 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2332 if (current_playlist
.control_fd
< 0)
2334 current_playlist
.control_created
= true;
2336 current_playlist
.dirlen
= playlist
->dirlen
;
2338 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2339 memcpy(current_playlist
.indices
, playlist
->indices
,
2340 playlist
->max_playlist_size
*sizeof(int));
2342 current_playlist
.first_index
= playlist
->first_index
;
2343 current_playlist
.amount
= playlist
->amount
;
2344 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2345 current_playlist
.seed
= playlist
->seed
;
2346 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2347 current_playlist
.deleted
= playlist
->deleted
;
2348 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2349 current_playlist
.shuffle_flush
= playlist
->shuffle_flush
;
2355 * Close files and delete control file for non-current playlist.
2357 void playlist_close(struct playlist_info
* playlist
)
2362 if (playlist
->fd
>= 0)
2363 close(playlist
->fd
);
2365 if (playlist
->control_fd
>= 0)
2366 close(playlist
->control_fd
);
2368 if (playlist
->control_created
)
2369 remove(playlist
->control_filename
);
2373 * Insert track into playlist at specified position (or one of the special
2374 * positions). Returns position where track was inserted or -1 if error.
2376 int playlist_insert_track(struct playlist_info
* playlist
,
2377 const char *filename
, int position
, bool queue
)
2382 playlist
= ¤t_playlist
;
2384 if (check_control(playlist
) < 0)
2386 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2390 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2394 mutex_lock(&playlist
->control_mutex
);
2395 fsync(playlist
->control_fd
);
2396 mutex_unlock(&playlist
->control_mutex
);
2398 if (audio_status() & AUDIO_STATUS_PLAY
)
2399 audio_flush_and_reload_tracks();
2406 * Insert all tracks from specified directory into playlist.
2408 int playlist_insert_directory(struct playlist_info
* playlist
,
2409 const char *dirname
, int position
, bool queue
,
2417 playlist
= ¤t_playlist
;
2419 if (check_control(playlist
) < 0)
2421 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2426 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2428 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2430 display_playlist_count(count
, count_str
);
2432 result
= add_directory_to_playlist(playlist
, dirname
, &position
, queue
,
2435 mutex_lock(&playlist
->control_mutex
);
2436 fsync(playlist
->control_fd
);
2437 mutex_unlock(&playlist
->control_mutex
);
2439 display_playlist_count(count
, count_str
);
2441 if (audio_status() & AUDIO_STATUS_PLAY
)
2442 audio_flush_and_reload_tracks();
2448 * Insert all tracks from specified playlist into dynamic playlist.
2450 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2451 int position
, bool queue
)
2458 char temp_buf
[MAX_PATH
+1];
2459 char trackname
[MAX_PATH
+1];
2464 playlist
= ¤t_playlist
;
2466 if (check_control(playlist
) < 0)
2468 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2472 fd
= open(filename
, O_RDONLY
);
2475 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2479 /* we need the directory name for formatting purposes */
2482 temp_ptr
= strrchr(filename
+1,'/');
2489 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2491 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2493 display_playlist_count(count
, count_str
);
2495 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2498 if (button_get(false) == SETTINGS_CANCEL
)
2501 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2505 /* we need to format so that relative paths are correctly
2507 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2514 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2523 /* Make sure tracks are inserted in correct order if user
2524 requests INSERT_FIRST */
2525 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
2526 position
= insert_pos
+ 1;
2530 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
2532 display_playlist_count(count
, count_str
);
2534 if (count
== PLAYLIST_DISPLAY_COUNT
&&
2535 (audio_status() & AUDIO_STATUS_PLAY
))
2536 audio_flush_and_reload_tracks();
2540 /* let the other threads work */
2546 mutex_lock(&playlist
->control_mutex
);
2547 fsync(playlist
->control_fd
);
2548 mutex_unlock(&playlist
->control_mutex
);
2553 display_playlist_count(count
, count_str
);
2555 if (audio_status() & AUDIO_STATUS_PLAY
)
2556 audio_flush_and_reload_tracks();
2562 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2563 * we want to delete the current playing track.
2565 int playlist_delete(struct playlist_info
* playlist
, int index
)
2570 playlist
= ¤t_playlist
;
2572 if (check_control(playlist
) < 0)
2574 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2578 if (index
== PLAYLIST_DELETE_CURRENT
)
2579 index
= playlist
->index
;
2581 result
= remove_track_from_playlist(playlist
, index
, true);
2583 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
2584 audio_flush_and_reload_tracks();
2590 * Move track at index to new_index. Tracks between the two are shifted
2591 * appropriately. Returns 0 on success and -1 on failure.
2593 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
2599 bool current
= false;
2601 char filename
[MAX_PATH
];
2604 playlist
= ¤t_playlist
;
2606 if (check_control(playlist
) < 0)
2608 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2612 if (index
== new_index
)
2615 if (index
== playlist
->index
)
2616 /* Moving the current track */
2619 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2620 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
2621 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2623 if (get_filename(playlist
, seek
, control_file
, filename
,
2624 sizeof(filename
)) < 0)
2627 /* Delete track from original position */
2628 result
= remove_track_from_playlist(playlist
, index
, true);
2632 /* We want to insert the track at the position that was specified by
2633 new_index. This may be different then new_index because of the
2634 shifting that occurred after the delete */
2635 r
= rotate_index(playlist
, new_index
);
2639 new_index
= PLAYLIST_PREPEND
;
2640 else if (r
== playlist
->amount
)
2642 new_index
= PLAYLIST_INSERT_LAST
;
2644 /* Calculate index of desired position */
2645 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
2647 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
2654 /* Moved the current track */
2657 case PLAYLIST_PREPEND
:
2658 playlist
->index
= playlist
->first_index
;
2660 case PLAYLIST_INSERT_LAST
:
2661 playlist
->index
= playlist
->first_index
- 1;
2662 if (playlist
->index
< 0)
2663 playlist
->index
+= playlist
->amount
;
2666 playlist
->index
= new_index
;
2671 mutex_lock(&playlist
->control_mutex
);
2672 fsync(playlist
->control_fd
);
2673 mutex_unlock(&playlist
->control_mutex
);
2675 if (audio_status() & AUDIO_STATUS_PLAY
)
2676 audio_flush_and_reload_tracks();
2683 /* shuffle currently playing playlist */
2684 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
2690 playlist
= ¤t_playlist
;
2692 check_control(playlist
);
2694 result
= randomise_playlist(playlist
, seed
, start_current
, true);
2696 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
2697 audio_flush_and_reload_tracks();
2702 /* sort currently playing playlist */
2703 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
2708 playlist
= ¤t_playlist
;
2710 check_control(playlist
);
2712 result
= sort_playlist(playlist
, start_current
, true);
2714 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
2715 audio_flush_and_reload_tracks();
2720 /* returns true if playlist has been modified */
2721 bool playlist_modified(const struct playlist_info
* playlist
)
2724 playlist
= ¤t_playlist
;
2726 if (playlist
->shuffle_modified
||
2727 playlist
->deleted
||
2728 playlist
->num_inserted_tracks
> 0)
2734 /* returns index of first track in playlist */
2735 int playlist_get_first_index(const struct playlist_info
* playlist
)
2738 playlist
= ¤t_playlist
;
2740 return playlist
->first_index
;
2743 /* returns shuffle seed of playlist */
2744 int playlist_get_seed(const struct playlist_info
* playlist
)
2747 playlist
= ¤t_playlist
;
2749 return playlist
->seed
;
2752 /* returns number of tracks in playlist (includes queued/inserted tracks) */
2753 int playlist_amount_ex(const struct playlist_info
* playlist
)
2756 playlist
= ¤t_playlist
;
2758 return playlist
->amount
;
2761 /* returns full path of playlist (minus extension) */
2762 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
2768 playlist
= ¤t_playlist
;
2770 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
2775 /* Remove extension */
2776 sep
= strrchr(buf
, '.');
2783 /* returns the playlist filename */
2784 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
2788 playlist
= ¤t_playlist
;
2790 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
2798 /* Fills info structure with information about track at specified index.
2799 Returns 0 on success and -1 on failure */
2800 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
2801 struct playlist_track_info
* info
)
2807 playlist
= ¤t_playlist
;
2809 if (index
< 0 || index
>= playlist
->amount
)
2812 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2813 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2815 if (get_filename(playlist
, seek
, control_file
, info
->filename
,
2816 sizeof(info
->filename
)) < 0)
2823 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2824 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
2826 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
2830 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
2831 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
2833 info
->index
= index
;
2834 info
->display_index
= rotate_index(playlist
, index
) + 1;
2839 /* save the current dynamic playlist to specified file */
2840 int playlist_save(struct playlist_info
* playlist
, char *filename
)
2845 char tmp_buf
[MAX_PATH
+1];
2849 playlist
= ¤t_playlist
;
2851 if (playlist
->amount
<= 0)
2854 /* use current working directory as base for pathname */
2855 if (format_track_path(tmp_buf
, filename
, sizeof(tmp_buf
),
2856 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
2859 fd
= open(tmp_buf
, O_CREAT
|O_WRONLY
|O_TRUNC
);
2862 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2866 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
2868 index
= playlist
->first_index
;
2869 for (i
=0; i
<playlist
->amount
; i
++)
2876 if (button_get(false) == SETTINGS_CANCEL
)
2879 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2880 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
2881 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2883 /* Don't save queued files */
2886 if (get_filename(playlist
, seek
, control_file
, tmp_buf
,
2893 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
2895 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
2902 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
2903 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
2908 index
= (index
+1)%playlist
->amount
;
2911 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));