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"
92 #ifdef HAVE_LCD_BITMAP
101 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
102 #define PLAYLIST_CONTROL_FILE_VERSION 2
105 Each playlist index has a flag associated with it which identifies what
106 type of track it is. These flags are stored in the 4 high order bits of
109 NOTE: This limits the playlist file size to a max of 256M.
113 01 = Track was prepended into playlist
114 10 = Track was inserted into playlist
115 11 = Track was appended into playlist
120 0 = Track entry is valid
121 1 = Track does not exist on disk and should be skipped
123 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
124 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
125 #define PLAYLIST_QUEUE_MASK 0x20000000
127 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
128 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
129 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
131 #define PLAYLIST_QUEUED 0x20000000
132 #define PLAYLIST_SKIPPED 0x10000000
134 #define PLAYLIST_DISPLAY_COUNT 10
136 static bool changing_dir
= false;
138 static struct playlist_info current_playlist
;
139 static char now_playing
[MAX_PATH
+1];
141 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
142 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
144 static void create_control(struct playlist_info
* playlist
);
145 static int check_control(struct playlist_info
* playlist
);
146 static int recreate_control(struct playlist_info
* playlist
);
147 static void update_playlist_filename(struct playlist_info
* playlist
,
148 const char *dir
, const char *file
);
149 static int add_indices_to_playlist(struct playlist_info
* playlist
,
150 char* buffer
, int buflen
);
151 static int add_track_to_playlist(struct playlist_info
* playlist
,
152 const char *filename
, int position
,
153 bool queue
, int seek_pos
);
154 static int add_directory_to_playlist(struct playlist_info
* playlist
,
155 const char *dirname
, int *position
,
156 bool queue
, int *count
, bool recurse
);
157 static int remove_track_from_playlist(struct playlist_info
* playlist
,
158 int position
, bool write
);
159 static int randomise_playlist(struct playlist_info
* playlist
,
160 unsigned int seed
, bool start_current
,
162 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
164 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
166 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
168 static int compare(const void* p1
, const void* p2
);
169 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
170 bool control_file
, char *buf
, int buf_length
);
171 static int get_next_directory(char *dir
);
172 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
173 static int get_previous_directory(char *dir
);
174 static int check_subdir_for_music(char *dir
, char *subdir
);
175 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
177 static void display_playlist_count(int count
, const unsigned char *fmt
);
178 static void display_buffer_full(void);
179 static int flush_cached_control(struct playlist_info
* playlist
);
180 static int update_control(struct playlist_info
* playlist
,
181 enum playlist_command command
, int i1
, int i2
,
182 const char* s1
, const char* s2
, void* data
);
183 static void sync_control(struct playlist_info
* playlist
, bool force
);
184 static int rotate_index(const struct playlist_info
* playlist
, int index
);
187 #define PLAYLIST_LOAD_POINTERS 1
189 static struct event_queue playlist_queue
;
190 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x400)/sizeof(long)];
191 static const char playlist_thread_name
[] = "playlist cachectrl";
195 * remove any files and indices associated with the playlist
197 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
199 playlist
->filename
[0] = '\0';
201 if(playlist
->fd
>= 0)
202 /* If there is an already open playlist, close it. */
206 if(playlist
->control_fd
>= 0)
207 close(playlist
->control_fd
);
208 playlist
->control_fd
= -1;
209 playlist
->control_created
= false;
211 playlist
->in_ram
= false;
213 if (playlist
->buffer
)
214 playlist
->buffer
[0] = 0;
216 playlist
->buffer_end_pos
= 0;
219 playlist
->first_index
= 0;
220 playlist
->amount
= 0;
221 playlist
->last_insert_pos
= -1;
223 playlist
->shuffle_modified
= false;
224 playlist
->deleted
= false;
225 playlist
->num_inserted_tracks
= 0;
227 playlist
->num_cached
= 0;
228 playlist
->pending_control_sync
= false;
230 if (!resume
&& playlist
->current
)
232 /* start with fresh playlist control file when starting new
234 create_control(playlist
);
236 /* Reset resume settings */
237 global_settings
.resume_first_index
= 0;
238 global_settings
.resume_seed
= -1;
243 * Initialize a new playlist for viewing/editing/playing. dir is the
244 * directory where the playlist is located and file is the filename.
246 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
249 empty_playlist(playlist
, false);
255 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
256 playlist
->in_ram
= true;
258 dir
= ""; /* empty playlist */
261 update_playlist_filename(playlist
, dir
, file
);
263 if (playlist
->control_fd
>= 0)
265 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
266 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
267 sync_control(playlist
, false);
272 * create control file for playlist
274 static void create_control(struct playlist_info
* playlist
)
276 playlist
->control_fd
= open(playlist
->control_filename
,
277 O_CREAT
|O_RDWR
|O_TRUNC
);
278 if (playlist
->control_fd
< 0)
280 if (check_rockboxdir())
282 gui_syncsplash(HZ
*2, true, (unsigned char *)"%s (%d)",
283 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
284 playlist
->control_fd
);
286 playlist
->control_created
= false;
290 playlist
->control_created
= true;
295 * validate the control file. This may include creating/initializing it if
298 static int check_control(struct playlist_info
* playlist
)
300 if (!playlist
->control_created
)
302 create_control(playlist
);
304 if (playlist
->control_fd
>= 0)
306 char* dir
= playlist
->filename
;
307 char* file
= playlist
->filename
+playlist
->dirlen
;
308 char c
= playlist
->filename
[playlist
->dirlen
-1];
310 playlist
->filename
[playlist
->dirlen
-1] = '\0';
312 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
313 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
314 sync_control(playlist
, false);
315 playlist
->filename
[playlist
->dirlen
-1] = c
;
319 if (playlist
->control_fd
< 0)
326 * recreate the control file based on current playlist entries
328 static int recreate_control(struct playlist_info
* playlist
)
330 char temp_file
[MAX_PATH
+1];
335 if(playlist
->control_fd
>= 0)
337 char* dir
= playlist
->filename
;
338 char* file
= playlist
->filename
+playlist
->dirlen
;
339 char c
= playlist
->filename
[playlist
->dirlen
-1];
341 close(playlist
->control_fd
);
343 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
344 playlist
->control_filename
);
346 if (rename(playlist
->control_filename
, temp_file
) < 0)
349 temp_fd
= open(temp_file
, O_RDONLY
);
353 playlist
->control_fd
= open(playlist
->control_filename
,
354 O_CREAT
|O_RDWR
|O_TRUNC
);
355 if (playlist
->control_fd
< 0)
358 playlist
->filename
[playlist
->dirlen
-1] = '\0';
360 /* cannot call update_control() because of mutex */
361 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
362 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
364 playlist
->filename
[playlist
->dirlen
-1] = c
;
374 playlist
->shuffle_modified
= false;
375 playlist
->deleted
= false;
376 playlist
->num_inserted_tracks
= 0;
378 if (playlist
->current
)
380 global_settings
.resume_seed
= -1;
384 for (i
=0; i
<playlist
->amount
; i
++)
386 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
388 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
389 char inserted_file
[MAX_PATH
+1];
391 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
393 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
395 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
396 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
399 /* save the position in file where name is written */
400 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
402 result
= fdprintf(playlist
->control_fd
, "%s\n",
405 playlist
->indices
[i
] =
406 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
412 playlist
->num_inserted_tracks
++;
418 fsync(playlist
->control_fd
);
427 * store directory and name of playlist file
429 static void update_playlist_filename(struct playlist_info
* playlist
,
430 const char *dir
, const char *file
)
433 int dirlen
= strlen(dir
);
435 /* If the dir does not end in trailing slash, we use a separator.
436 Otherwise we don't. */
437 if('/' != dir
[dirlen
-1])
443 playlist
->dirlen
= dirlen
;
445 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
446 "%s%s%s", dir
, sep
, file
);
450 * calculate track offsets within a playlist file
452 static int add_indices_to_playlist(struct playlist_info
* playlist
,
453 char* buffer
, int buflen
)
457 unsigned int count
= 0;
462 if(-1 == playlist
->fd
)
463 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
465 return -1; /* failure */
467 #ifdef HAVE_LCD_BITMAP
468 if(global_settings
.statusbar
)
469 lcd_setmargins(0, STATUSBAR_HEIGHT
);
471 lcd_setmargins(0, 0);
474 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD
));
478 /* use mp3 buffer for maximum load speed */
480 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
482 buffer
= (char *)audiobuf
;
483 buflen
= (audiobufend
- audiobuf
);
490 nread
= read(playlist
->fd
, buffer
, buflen
);
491 /* Terminate on EOF */
495 p
= (unsigned char *)buffer
;
497 for(count
=0; count
< nread
; count
++,p
++) {
499 /* Are we on a new line? */
500 if((*p
== '\n') || (*p
== '\r'))
510 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
511 display_buffer_full();
516 /* Store a new entry */
517 playlist
->indices
[ playlist
->amount
] = i
+count
;
519 if (playlist
->filenames
)
520 playlist
->filenames
[ playlist
->amount
] = NULL
;
532 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
539 * Add track to playlist at specified position. There are five special
540 * positions that can be specified:
541 * PLAYLIST_PREPEND - Add track at beginning of playlist
542 * PLAYLIST_INSERT - Add track after current song. NOTE: If
543 * there are already inserted tracks then track
544 * is added to the end of the insertion list
545 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
546 * matter what other tracks have been inserted
547 * PLAYLIST_INSERT_LAST - Add track to end of playlist
548 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
549 * current playing track and end of playlist
551 static int add_track_to_playlist(struct playlist_info
* playlist
,
552 const char *filename
, int position
,
553 bool queue
, int seek_pos
)
555 int insert_position
= position
;
556 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
559 if (playlist
->amount
>= playlist
->max_playlist_size
)
561 display_buffer_full();
567 case PLAYLIST_PREPEND
:
568 insert_position
= playlist
->first_index
;
569 flags
= PLAYLIST_INSERT_TYPE_PREPEND
;
571 case PLAYLIST_INSERT
:
572 /* if there are already inserted tracks then add track to end of
573 insertion list else add after current playing track */
574 if (playlist
->last_insert_pos
>= 0 &&
575 playlist
->last_insert_pos
< playlist
->amount
&&
576 (playlist
->indices
[playlist
->last_insert_pos
]&
577 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
578 position
= insert_position
= playlist
->last_insert_pos
+1;
579 else if (playlist
->amount
> 0)
580 position
= insert_position
= playlist
->index
+ 1;
582 position
= insert_position
= 0;
584 playlist
->last_insert_pos
= position
;
586 case PLAYLIST_INSERT_FIRST
:
587 if (playlist
->amount
> 0)
588 position
= insert_position
= playlist
->index
+ 1;
590 position
= insert_position
= 0;
592 if (playlist
->last_insert_pos
< 0)
593 playlist
->last_insert_pos
= position
;
595 case PLAYLIST_INSERT_LAST
:
596 if (playlist
->first_index
> 0)
597 insert_position
= playlist
->first_index
;
599 insert_position
= playlist
->amount
;
601 flags
= PLAYLIST_INSERT_TYPE_APPEND
;
603 case PLAYLIST_INSERT_SHUFFLED
:
606 int n
= playlist
->amount
-
607 rotate_index(playlist
, playlist
->index
);
614 position
= playlist
->index
+ offset
+ 1;
615 if (position
>= playlist
->amount
)
616 position
-= playlist
->amount
;
618 insert_position
= position
;
624 flags
|= PLAYLIST_QUEUED
;
626 /* shift indices so that track can be added */
627 for (i
=playlist
->amount
; i
>insert_position
; i
--)
629 playlist
->indices
[i
] = playlist
->indices
[i
-1];
631 if (playlist
->filenames
)
632 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
636 /* update stored indices if needed */
637 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
)
640 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
641 position
!= PLAYLIST_PREPEND
)
643 playlist
->first_index
++;
645 if (seek_pos
< 0 && playlist
->current
)
647 global_settings
.resume_first_index
= playlist
->first_index
;
652 if (insert_position
< playlist
->last_insert_pos
||
653 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
654 playlist
->last_insert_pos
++;
656 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
658 int result
= update_control(playlist
,
659 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
660 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
666 playlist
->indices
[insert_position
] = flags
| seek_pos
;
669 if (playlist
->filenames
)
670 playlist
->filenames
[insert_position
] = NULL
;
674 playlist
->num_inserted_tracks
++;
676 return insert_position
;
680 * Insert directory into playlist. May be called recursively.
682 static int add_directory_to_playlist(struct playlist_info
* playlist
,
683 const char *dirname
, int *position
,
684 bool queue
, int *count
, bool recurse
)
686 char buf
[MAX_PATH
+1];
687 unsigned char *count_str
;
691 int dirfilter
= global_settings
.dirfilter
;
693 struct tree_context
* tc
= tree_get_context();
695 /* use the tree browser dircache to load files */
696 global_settings
.dirfilter
= SHOW_ALL
;
698 if (ft_load(tc
, dirname
) < 0)
700 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
701 global_settings
.dirfilter
= dirfilter
;
705 files
= (struct entry
*) tc
->dircache
;
706 num_files
= tc
->filesindir
;
708 /* we've overwritten the dircache so tree browser will need to be
713 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
715 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
717 for (i
=0; i
<num_files
; i
++)
720 if (button_get(false) == SETTINGS_CANCEL
)
726 if (files
[i
].attr
& ATTR_DIRECTORY
)
730 /* recursively add directories */
731 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
732 result
= add_directory_to_playlist(playlist
, buf
, position
,
733 queue
, count
, recurse
);
737 /* we now need to reload our current directory */
738 if(ft_load(tc
, dirname
) < 0)
744 files
= (struct entry
*) tc
->dircache
;
745 num_files
= tc
->filesindir
;
755 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
759 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
761 insert_pos
= add_track_to_playlist(playlist
, buf
, *position
,
771 /* Make sure tracks are inserted in correct order if user requests
773 if (*position
== PLAYLIST_INSERT_FIRST
|| *position
>= 0)
774 *position
= insert_pos
+ 1;
776 if ((*count
%PLAYLIST_DISPLAY_COUNT
) == 0)
778 display_playlist_count(*count
, count_str
);
780 if (*count
== PLAYLIST_DISPLAY_COUNT
&&
781 (audio_status() & AUDIO_STATUS_PLAY
))
782 audio_flush_and_reload_tracks();
785 /* let the other threads work */
790 /* restore dirfilter */
791 global_settings
.dirfilter
= dirfilter
;
797 * remove track at specified position
799 static int remove_track_from_playlist(struct playlist_info
* playlist
,
800 int position
, bool write
)
805 if (playlist
->amount
<= 0)
808 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
810 /* shift indices now that track has been removed */
811 for (i
=position
; i
<playlist
->amount
; i
++)
813 playlist
->indices
[i
] = playlist
->indices
[i
+1];
815 if (playlist
->filenames
)
816 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
823 playlist
->num_inserted_tracks
--;
825 playlist
->deleted
= true;
827 /* update stored indices if needed */
828 if (position
< playlist
->index
)
831 if (position
< playlist
->first_index
)
833 playlist
->first_index
--;
837 global_settings
.resume_first_index
= playlist
->first_index
;
842 if (position
<= playlist
->last_insert_pos
)
843 playlist
->last_insert_pos
--;
845 if (write
&& playlist
->control_fd
>= 0)
847 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
848 position
, -1, NULL
, NULL
, NULL
);
853 sync_control(playlist
, false);
860 * randomly rearrange the array of indices for the playlist. If start_current
861 * is true then update the index to the new index of the current playing track
863 static int randomise_playlist(struct playlist_info
* playlist
,
864 unsigned int seed
, bool start_current
,
870 unsigned int current
= playlist
->indices
[playlist
->index
];
872 /* seed 0 is used to identify sorted playlist for resume purposes */
876 /* seed with the given seed */
879 /* randomise entire indices list */
880 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
882 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
883 candidate
= rand() % (count
+ 1);
885 /* now swap the values at the 'count' and 'candidate' positions */
886 store
= playlist
->indices
[candidate
];
887 playlist
->indices
[candidate
] = playlist
->indices
[count
];
888 playlist
->indices
[count
] = store
;
890 if (playlist
->filenames
)
892 store
= (long)playlist
->filenames
[candidate
];
893 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
894 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
900 find_and_set_playlist_index(playlist
, current
);
902 /* indices have been moved so last insert position is no longer valid */
903 playlist
->last_insert_pos
= -1;
905 playlist
->seed
= seed
;
906 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
907 playlist
->shuffle_modified
= true;
911 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
912 playlist
->first_index
, NULL
, NULL
, NULL
);
913 global_settings
.resume_seed
= seed
;
921 * Sort the array of indices for the playlist. If start_current is true then
922 * set the index to the new index of the current song.
924 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
927 unsigned int current
= playlist
->indices
[playlist
->index
];
929 if (playlist
->amount
> 0)
930 qsort(playlist
->indices
, playlist
->amount
,
931 sizeof(playlist
->indices
[0]), compare
);
934 /** We need to re-check the song names from disk because qsort can't
935 * sort two arrays at once :/
936 * FIXME: Please implement a better way to do this. */
937 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
941 find_and_set_playlist_index(playlist
, current
);
943 /* indices have been moved so last insert position is no longer valid */
944 playlist
->last_insert_pos
= -1;
946 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
947 playlist
->shuffle_modified
= false;
948 if (write
&& playlist
->control_fd
>= 0)
950 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
951 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
952 global_settings
.resume_seed
= 0;
959 /* Calculate how many steps we have to really step when skipping entries
962 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
964 int i
, count
, direction
;
966 int stepped_count
= 0;
979 index
= playlist
->index
;
984 index
+= playlist
->amount
;
985 if (index
>= playlist
->amount
)
986 index
-= playlist
->amount
;
988 /* Check if we found a bad entry. */
989 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
992 /* Are all entries bad? */
993 if (stepped_count
++ > playlist
->amount
)
1000 } while (i
<= count
);
1005 /* Marks the index of the track to be skipped that is "steps" away from
1006 * current playing track.
1008 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1012 if (playlist
== NULL
)
1013 playlist
= ¤t_playlist
;
1015 index
= rotate_index(playlist
, playlist
->index
);
1016 /* We should also skip already skipped entries before the entry to be skipepd. */
1017 index
+= calculate_step_count(playlist
, steps
);
1018 if (index
< 0 || index
>= playlist
->amount
)
1021 index
= (index
+playlist
->first_index
) % playlist
->amount
;
1022 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1026 * returns the index of the track that is "steps" away from current playing
1029 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1032 int current_index
= playlist
->index
;
1033 int next_index
= -1;
1035 if (playlist
->amount
<= 0)
1038 if (repeat_mode
== -1)
1039 repeat_mode
= global_settings
.repeat_mode
;
1041 if (repeat_mode
== REPEAT_SHUFFLE
&&
1042 (!global_settings
.playlist_shuffle
|| playlist
->amount
<= 1))
1043 repeat_mode
= REPEAT_ALL
;
1045 steps
= calculate_step_count(playlist
, steps
);
1046 switch (repeat_mode
)
1048 case REPEAT_SHUFFLE
:
1049 /* Treat repeat shuffle just like repeat off. At end of playlist,
1050 play will be resumed in playlist_next() */
1053 current_index
= rotate_index(playlist
, current_index
);
1054 next_index
= current_index
+steps
;
1055 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1058 next_index
= (next_index
+playlist
->first_index
) %
1065 #if (AB_REPEAT_ENABLE == 1)
1068 next_index
= current_index
;
1074 next_index
= (current_index
+steps
) % playlist
->amount
;
1075 while (next_index
< 0)
1076 next_index
+= playlist
->amount
;
1078 if (steps
>= playlist
->amount
)
1085 /* second time around so skip the queued files */
1086 for (i
=0; i
<playlist
->amount
; i
++)
1088 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1089 index
= (index
+1) % playlist
->amount
;
1101 /* No luck if the whole playlist was bad. */
1102 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1109 * Search for the seek track and set appropriate indices. Used after shuffle
1110 * to make sure the current index is still pointing to correct track.
1112 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1117 /* Set the index to the current song */
1118 for (i
=0; i
<playlist
->amount
; i
++)
1120 if (playlist
->indices
[i
] == seek
)
1122 playlist
->index
= playlist
->first_index
= i
;
1124 if (playlist
->current
)
1126 global_settings
.resume_first_index
= i
;
1136 * used to sort track indices. Sort order is as follows:
1137 * 1. Prepended tracks (in prepend order)
1138 * 2. Playlist/directory tracks (in playlist order)
1139 * 3. Inserted/Appended tracks (in insert order)
1141 static int compare(const void* p1
, const void* p2
)
1143 unsigned long* e1
= (unsigned long*) p1
;
1144 unsigned long* e2
= (unsigned long*) p2
;
1145 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1146 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1148 if (flags1
== flags2
)
1149 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1150 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1151 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1153 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1154 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1156 else if (flags1
&& flags2
)
1157 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1162 #ifdef HAVE_DIRCACHE
1164 * Thread to update filename pointers to dircache on background
1165 * without affecting playlist load up performance. This thread also flushes
1166 * any pending control commands when the disk spins up.
1168 static void playlist_thread(void)
1171 bool dirty_pointers
= false;
1172 static char tmp
[MAX_PATH
+1];
1174 struct playlist_info
*playlist
;
1181 if (global_settings
.disk_spindown
> 1 &&
1182 global_settings
.disk_spindown
<= 5)
1183 sleep_time
= global_settings
.disk_spindown
- 1;
1187 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1191 case PLAYLIST_LOAD_POINTERS
:
1192 dirty_pointers
= true;
1195 /* Start the background scanning after either the disk spindown
1196 timeout or 5s, whichever is less */
1198 playlist
= ¤t_playlist
;
1200 if (playlist
->control_fd
>= 0
1202 && ata_disk_is_active()
1206 if (playlist
->num_cached
> 0)
1207 flush_cached_control(playlist
);
1209 sync_control(playlist
, true);
1212 if (!dirty_pointers
)
1215 if (!dircache_is_enabled() || !playlist
->filenames
1216 || playlist
->amount
<= 0)
1219 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1222 for (index
= 0; index
< playlist
->amount
1223 && queue_empty(&playlist_queue
); index
++)
1225 /* Process only pointers that are not already loaded. */
1226 if (playlist
->filenames
[index
])
1229 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1230 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1232 /* Load the filename from playlist file. */
1233 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1237 /* Set the dircache entry pointer. */
1238 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1240 /* And be on background so user doesn't notice any delays. */
1244 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1247 dirty_pointers
= false;
1251 case SYS_USB_CONNECTED
:
1252 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1253 usb_wait_for_disconnect(&playlist_queue
);
1262 * gets pathname for track at seek index
1264 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1265 bool control_file
, char *buf
, int buf_length
)
1269 char tmp_buf
[MAX_PATH
+1];
1270 char dir_buf
[MAX_PATH
+1];
1272 if (buf_length
> MAX_PATH
+1)
1273 buf_length
= MAX_PATH
+1;
1275 #ifdef HAVE_DIRCACHE
1276 if (dircache_is_enabled() && playlist
->filenames
)
1278 if (playlist
->filenames
[index
] != NULL
)
1280 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1281 max
= strlen(tmp_buf
) + 1;
1288 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1290 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1291 tmp_buf
[MAX_PATH
] = '\0';
1292 max
= strlen(tmp_buf
) + 1;
1296 mutex_lock(&playlist
->control_mutex
);
1299 fd
= playlist
->control_fd
;
1302 if(-1 == playlist
->fd
)
1303 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1311 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1314 max
= read(fd
, tmp_buf
, buf_length
);
1317 mutex_unlock(&playlist
->control_mutex
);
1322 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1324 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1330 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1331 dir_buf
[playlist
->dirlen
-1] = 0;
1333 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1336 static int get_next_directory(char *dir
){
1337 return get_next_dir(dir
,true,false);
1340 static int get_previous_directory(char *dir
){
1341 return get_next_dir(dir
,false,false);
1345 * search through all the directories (starting with the current) to find
1346 * one that has tracks to play
1348 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1350 struct playlist_info
* playlist
= ¤t_playlist
;
1352 int dirfilter
= global_settings
.dirfilter
;
1353 int sort_dir
= global_settings
.sort_dir
;
1354 char *start_dir
= NULL
;
1356 struct tree_context
* tc
= tree_get_context();
1359 /* start with root */
1363 /* start with current directory */
1364 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1365 dir
[playlist
->dirlen
-1] = '\0';
1368 /* use the tree browser dircache to load files */
1369 global_settings
.dirfilter
= SHOW_ALL
;
1371 /* sort in another direction if previous dir is requested */
1373 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1374 global_settings
.sort_dir
= 4;
1375 else if (global_settings
.sort_dir
== 1)
1376 global_settings
.sort_dir
= 2;
1377 else if (global_settings
.sort_dir
== 2)
1378 global_settings
.sort_dir
= 1;
1379 else if (global_settings
.sort_dir
== 4)
1380 global_settings
.sort_dir
= 0;
1385 struct entry
*files
;
1389 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1391 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1397 files
= (struct entry
*) tc
->dircache
;
1398 num_files
= tc
->filesindir
;
1400 for (i
=0; i
<num_files
; i
++)
1403 if (button_get(false) == SETTINGS_CANCEL
)
1410 if (files
[i
].attr
& ATTR_DIRECTORY
)
1414 result
= check_subdir_for_music(dir
, files
[i
].name
);
1421 else if (!strcmp(start_dir
, files
[i
].name
))
1428 /* move down to parent directory. current directory name is
1429 stored as the starting point for the search in parent */
1430 start_dir
= strrchr(dir
, '/');
1441 /* we've overwritten the dircache so tree browser will need to be
1445 /* restore dirfilter & sort_dir */
1446 global_settings
.dirfilter
= dirfilter
;
1447 global_settings
.sort_dir
= sort_dir
;
1449 /* special case if nothing found: try start searching again from root */
1450 if (result
== -1 && !recursion
){
1451 result
= get_next_dir(dir
,is_forward
, true);
1458 * Checks if there are any music files in the dir or any of its
1459 * subdirectories. May be called recursively.
1461 static int check_subdir_for_music(char *dir
, char *subdir
)
1464 int dirlen
= strlen(dir
);
1467 struct entry
*files
;
1468 bool has_music
= false;
1469 bool has_subdir
= false;
1470 struct tree_context
* tc
= tree_get_context();
1472 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1474 if (ft_load(tc
, dir
) < 0)
1476 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1480 files
= (struct entry
*) tc
->dircache
;
1481 num_files
= tc
->filesindir
;
1483 for (i
=0; i
<num_files
; i
++)
1485 if (files
[i
].attr
& ATTR_DIRECTORY
)
1487 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
1499 for (i
=0; i
<num_files
; i
++)
1501 if (button_get(false) == SETTINGS_CANCEL
)
1507 if (files
[i
].attr
& ATTR_DIRECTORY
)
1509 result
= check_subdir_for_music(dir
, files
[i
].name
);
1527 /* we now need to reload our current directory */
1528 if(ft_load(tc
, dir
) < 0)
1529 gui_syncsplash(HZ
*2, true,
1530 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1537 * Returns absolute path of track
1539 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1546 /* Zero-terminate the file name */
1547 while((src
[i
] != '\n') &&
1552 /* Now work back killing white space */
1553 while((src
[i
-1] == ' ') ||
1559 /* replace backslashes with forward slashes */
1560 for ( j
=0; j
<i
; j
++ )
1561 if ( src
[j
] == '\\' )
1566 strncpy(dest
, src
, buf_length
);
1570 /* handle dos style drive letter */
1572 strncpy(dest
, &src
[2], buf_length
);
1573 else if (!strncmp(src
, "../", 3))
1575 /* handle relative paths */
1577 while(!strncmp(&src
[i
], "../", 3))
1579 for (j
=0; j
<i
/3; j
++) {
1580 temp_ptr
= strrchr(dir
, '/');
1586 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1588 else if ( '.' == src
[0] && '/' == src
[1] ) {
1589 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1592 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1600 * Display splash message showing progress of playlist/directory insertion or
1603 static void display_playlist_count(int count
, const unsigned char *fmt
)
1605 lcd_clear_display();
1607 #ifdef HAVE_LCD_BITMAP
1608 if(global_settings
.statusbar
)
1609 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1611 lcd_setmargins(0, 0);
1614 gui_syncsplash(0, true, fmt
, count
,
1615 #if CONFIG_KEYPAD == PLAYER_PAD
1616 str(LANG_STOP_ABORT
)
1624 * Display buffer full message
1626 static void display_buffer_full(void)
1628 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_BUFFER_FULL
));
1632 * Flush any cached control commands to disk. Called when playlist is being
1633 * modified. Returns 0 on success and -1 on failure.
1635 static int flush_cached_control(struct playlist_info
* playlist
)
1640 lseek(playlist
->control_fd
, 0, SEEK_END
);
1642 for (i
=0; i
<playlist
->num_cached
; i
++)
1644 struct playlist_control_cache
* cache
=
1645 &(playlist
->control_cache
[i
]);
1647 switch (cache
->command
)
1649 case PLAYLIST_COMMAND_PLAYLIST
:
1650 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1651 cache
->i1
, cache
->s1
, cache
->s2
);
1653 case PLAYLIST_COMMAND_ADD
:
1654 case PLAYLIST_COMMAND_QUEUE
:
1655 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1656 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1657 cache
->i1
, cache
->i2
);
1660 /* save the position in file where name is written */
1661 int* seek_pos
= (int *)cache
->data
;
1662 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1663 result
= fdprintf(playlist
->control_fd
, "%s\n",
1667 case PLAYLIST_COMMAND_DELETE
:
1668 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1670 case PLAYLIST_COMMAND_SHUFFLE
:
1671 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1672 cache
->i1
, cache
->i2
);
1674 case PLAYLIST_COMMAND_UNSHUFFLE
:
1675 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1677 case PLAYLIST_COMMAND_RESET
:
1678 result
= fdprintf(playlist
->control_fd
, "R\n");
1690 if (global_settings
.resume_seed
>= 0)
1692 global_settings
.resume_seed
= -1;
1696 playlist
->num_cached
= 0;
1697 playlist
->pending_control_sync
= true;
1706 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1714 * Update control data with new command. Depending on the command, it may be
1715 * cached or flushed to disk.
1717 static int update_control(struct playlist_info
* playlist
,
1718 enum playlist_command command
, int i1
, int i2
,
1719 const char* s1
, const char* s2
, void* data
)
1722 struct playlist_control_cache
* cache
;
1725 mutex_lock(&playlist
->control_mutex
);
1727 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1729 cache
->command
= command
;
1738 case PLAYLIST_COMMAND_PLAYLIST
:
1739 case PLAYLIST_COMMAND_ADD
:
1740 case PLAYLIST_COMMAND_QUEUE
:
1741 #ifndef HAVE_DIRCACHE
1742 case PLAYLIST_COMMAND_DELETE
:
1743 case PLAYLIST_COMMAND_RESET
:
1747 case PLAYLIST_COMMAND_SHUFFLE
:
1748 case PLAYLIST_COMMAND_UNSHUFFLE
:
1750 /* only flush when needed */
1754 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1755 result
= flush_cached_control(playlist
);
1757 mutex_unlock(&playlist
->control_mutex
);
1763 * sync control file to disk
1765 static void sync_control(struct playlist_info
* playlist
, bool force
)
1768 #ifdef HAVE_DIRCACHE
1772 if (playlist
->pending_control_sync
)
1774 mutex_lock(&playlist
->control_mutex
);
1775 fsync(playlist
->control_fd
);
1776 playlist
->pending_control_sync
= false;
1777 mutex_unlock(&playlist
->control_mutex
);
1783 * Rotate indices such that first_index is index 0
1785 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1787 index
-= playlist
->first_index
;
1789 index
+= playlist
->amount
;
1795 * Initialize playlist entries at startup
1797 void playlist_init(void)
1799 struct playlist_info
* playlist
= ¤t_playlist
;
1801 playlist
->current
= true;
1802 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1803 "%s", PLAYLIST_CONTROL_FILE
);
1805 playlist
->control_fd
= -1;
1806 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1807 playlist
->indices
= buffer_alloc(
1808 playlist
->max_playlist_size
* sizeof(int));
1809 playlist
->buffer_size
=
1810 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1811 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1812 mutex_init(&playlist
->control_mutex
);
1813 empty_playlist(playlist
, true);
1815 #ifdef HAVE_DIRCACHE
1816 playlist
->filenames
= buffer_alloc(
1817 playlist
->max_playlist_size
* sizeof(int));
1818 memset(playlist
->filenames
, 0,
1819 playlist
->max_playlist_size
* sizeof(int));
1820 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1821 playlist_thread_name
);
1822 queue_init(&playlist_queue
);
1827 * Clean playlist at shutdown
1829 void playlist_shutdown(void)
1831 struct playlist_info
* playlist
= ¤t_playlist
;
1833 if (playlist
->control_fd
>= 0)
1835 mutex_lock(&playlist
->control_mutex
);
1837 if (playlist
->num_cached
> 0)
1838 flush_cached_control(playlist
);
1840 close(playlist
->control_fd
);
1842 mutex_unlock(&playlist
->control_mutex
);
1847 * Create new playlist
1849 int playlist_create(const char *dir
, const char *file
)
1851 struct playlist_info
* playlist
= ¤t_playlist
;
1853 new_playlist(playlist
, dir
, file
);
1856 /* load the playlist file */
1857 add_indices_to_playlist(playlist
, NULL
, 0);
1862 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1865 * Restore the playlist state based on control file commands. Called to
1866 * resume playback after shutdown.
1868 int playlist_resume(void)
1870 struct playlist_info
* playlist
= ¤t_playlist
;
1875 int control_file_size
= 0;
1879 /* use mp3 buffer for maximum load speed */
1880 #if CONFIG_CODEC != SWCODEC
1881 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1882 buflen
= (audiobufend
- audiobuf
);
1883 buffer
= (char *)audiobuf
;
1885 buflen
= (audiobufend
- audiobuf
- talk_get_bufsize());
1886 buffer
= (char *)&audiobuf
[talk_get_bufsize()];
1889 empty_playlist(playlist
, true);
1891 gui_syncsplash(0, true, str(LANG_WAIT
));
1892 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1893 if (playlist
->control_fd
< 0)
1895 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1898 playlist
->control_created
= true;
1900 control_file_size
= filesize(playlist
->control_fd
);
1901 if (control_file_size
<= 0)
1903 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1907 /* read a small amount first to get the header */
1908 nread
= read(playlist
->control_fd
, buffer
,
1909 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1912 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1920 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1921 int last_newline
= 0;
1923 bool newline
= true;
1924 bool exit_loop
= false;
1930 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1932 /* Are we on a new line? */
1933 if((*p
== '\n') || (*p
== '\r'))
1937 /* save last_newline in case we need to load more data */
1938 last_newline
= count
;
1940 switch (current_command
)
1942 case PLAYLIST_COMMAND_PLAYLIST
:
1944 /* str1=version str2=dir str3=file */
1960 version
= atoi(str1
);
1962 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
1965 update_playlist_filename(playlist
, str2
, str3
);
1967 if (str3
[0] != '\0')
1969 /* NOTE: add_indices_to_playlist() overwrites the
1970 audiobuf so we need to reload control file
1972 add_indices_to_playlist(playlist
, NULL
, 0);
1974 else if (str2
[0] != '\0')
1976 playlist
->in_ram
= true;
1977 resume_directory(str2
);
1980 /* load the rest of the data */
1986 case PLAYLIST_COMMAND_ADD
:
1987 case PLAYLIST_COMMAND_QUEUE
:
1989 /* str1=position str2=last_position str3=file */
1990 int position
, last_position
;
1993 if (!str1
|| !str2
|| !str3
)
2000 position
= atoi(str1
);
2001 last_position
= atoi(str2
);
2003 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2006 /* seek position is based on str3's position in
2008 if (add_track_to_playlist(playlist
, str3
, position
,
2009 queue
, total_read
+(str3
-buffer
)) < 0)
2012 playlist
->last_insert_pos
= last_position
;
2016 case PLAYLIST_COMMAND_DELETE
:
2028 position
= atoi(str1
);
2030 if (remove_track_from_playlist(playlist
, position
,
2036 case PLAYLIST_COMMAND_SHUFFLE
:
2038 /* str1=seed str2=first_index */
2050 /* Always sort list before shuffling */
2051 sort_playlist(playlist
, false, false);
2055 playlist
->first_index
= atoi(str2
);
2057 if (randomise_playlist(playlist
, seed
, false,
2064 case PLAYLIST_COMMAND_UNSHUFFLE
:
2066 /* str1=first_index */
2074 playlist
->first_index
= atoi(str1
);
2076 if (sort_playlist(playlist
, false, false) < 0)
2082 case PLAYLIST_COMMAND_RESET
:
2084 playlist
->last_insert_pos
= -1;
2087 case PLAYLIST_COMMAND_COMMENT
:
2094 /* to ignore any extra newlines */
2095 current_command
= PLAYLIST_COMMAND_COMMENT
;
2101 /* first non-comment line must always specify playlist */
2102 if (first
&& *p
!= 'P' && *p
!= '#')
2112 /* playlist can only be specified once */
2120 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2123 current_command
= PLAYLIST_COMMAND_ADD
;
2126 current_command
= PLAYLIST_COMMAND_QUEUE
;
2129 current_command
= PLAYLIST_COMMAND_DELETE
;
2132 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2135 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2138 current_command
= PLAYLIST_COMMAND_RESET
;
2141 current_command
= PLAYLIST_COMMAND_COMMENT
;
2154 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2156 /* all control file strings are separated with a colon.
2157 Replace the colon with 0 to get proper strings that can be
2158 used by commands above */
2164 if ((count
+1) < nread
)
2178 /* allow last string to contain colons */
2189 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2193 if (!newline
|| (exit_loop
&& count
<nread
))
2195 if ((total_read
+ count
) >= control_file_size
)
2197 /* no newline at end of control file */
2198 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2202 /* We didn't end on a newline or we exited loop prematurely.
2203 Either way, re-read the remainder. */
2204 count
= last_newline
;
2205 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2208 total_read
+= count
;
2211 /* still looking for header */
2212 nread
= read(playlist
->control_fd
, buffer
,
2213 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2215 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2217 /* Terminate on EOF */
2220 if (global_settings
.resume_seed
>= 0)
2222 /* Apply shuffle command saved in settings */
2223 if (global_settings
.resume_seed
== 0)
2224 sort_playlist(playlist
, false, true);
2228 sort_playlist(playlist
, false, false);
2230 randomise_playlist(playlist
, global_settings
.resume_seed
,
2234 playlist
->first_index
= global_settings
.resume_first_index
;
2241 #ifdef HAVE_DIRCACHE
2242 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2249 * Add track to in_ram playlist. Used when playing directories.
2251 int playlist_add(const char *filename
)
2253 struct playlist_info
* playlist
= ¤t_playlist
;
2254 int len
= strlen(filename
);
2256 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2257 (playlist
->amount
>= playlist
->max_playlist_size
))
2259 display_buffer_full();
2263 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2264 #ifdef HAVE_DIRCACHE
2265 playlist
->filenames
[playlist
->amount
] = NULL
;
2269 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2270 playlist
->buffer_end_pos
+= len
;
2271 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2276 /* shuffle newly created playlist using random seed. */
2277 int playlist_shuffle(int random_seed
, int start_index
)
2279 struct playlist_info
* playlist
= ¤t_playlist
;
2281 unsigned int seek_pos
= 0;
2282 bool start_current
= false;
2284 if (start_index
>= 0 && global_settings
.play_selected
)
2286 /* store the seek position before the shuffle */
2287 seek_pos
= playlist
->indices
[start_index
];
2288 playlist
->index
= global_settings
.resume_first_index
=
2289 playlist
->first_index
= start_index
;
2290 start_current
= true;
2293 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
2295 randomise_playlist(playlist
, random_seed
, start_current
, true);
2297 return playlist
->index
;
2300 /* start playing current playlist at specified index/offset */
2301 int playlist_start(int start_index
, int offset
)
2303 struct playlist_info
* playlist
= ¤t_playlist
;
2305 playlist
->index
= start_index
;
2306 #if CONFIG_CODEC != SWCODEC
2307 talk_buffer_steal(); /* will use the mp3 buffer */
2314 /* Returns false if 'steps' is out of bounds, else true */
2315 bool playlist_check(int steps
)
2317 struct playlist_info
* playlist
= ¤t_playlist
;
2319 /* always allow folder navigation */
2320 if (global_settings
.next_folder
&& playlist
->in_ram
)
2323 int index
= get_next_index(playlist
, steps
, -1);
2325 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2326 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2328 return (index
>= 0);
2331 /* get trackname of track that is "steps" away from current playing track.
2332 NULL is used to identify end of playlist */
2333 char* playlist_peek(int steps
)
2335 struct playlist_info
* playlist
= ¤t_playlist
;
2342 index
= get_next_index(playlist
, steps
, -1);
2346 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2347 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2349 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2353 temp_ptr
= now_playing
;
2355 if (!playlist
->in_ram
|| control_file
)
2357 /* remove bogus dirs from beginning of path
2358 (workaround for buggy playlist creation tools) */
2361 #ifdef HAVE_DIRCACHE
2362 if (dircache_is_enabled())
2364 if (dircache_get_entry_ptr(temp_ptr
))
2370 fd
= open(temp_ptr
, O_RDONLY
);
2378 temp_ptr
= strchr(temp_ptr
+1, '/');
2383 /* Even though this is an invalid file, we still need to pass a
2384 file name to the caller because NULL is used to indicate end
2394 * Update indices as track has changed
2396 int playlist_next(int steps
)
2398 struct playlist_info
* playlist
= ¤t_playlist
;
2402 #if (AB_REPEAT_ENABLE == 1)
2403 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2405 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2409 /* We need to delete all the queued songs */
2410 for (i
=0, j
=steps
; i
<j
; i
++)
2412 index
= get_next_index(playlist
, i
, -1);
2414 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2416 remove_track_from_playlist(playlist
, index
, true);
2417 steps
--; /* one less track */
2422 index
= get_next_index(playlist
, steps
, -1);
2426 /* end of playlist... or is it */
2427 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2428 global_settings
.playlist_shuffle
&&
2429 playlist
->amount
> 1)
2431 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2432 playlist
->first_index
= global_settings
.resume_first_index
= 0;
2433 sort_playlist(playlist
, false, false);
2434 randomise_playlist(playlist
, current_tick
, false, true);
2435 #if CONFIG_CODEC != SWCODEC
2436 playlist_start(0, 0);
2438 playlist
->index
= 0;
2441 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2443 char dir
[MAX_PATH
+1];
2445 changing_dir
= true;
2448 if (!get_next_directory(dir
))
2450 /* start playing next directory */
2451 if (playlist_create(dir
, NULL
) != -1)
2453 ft_build_playlist(tree_get_context(), 0);
2454 if (global_settings
.playlist_shuffle
)
2455 playlist_shuffle(current_tick
, -1);
2456 #if CONFIG_CODEC != SWCODEC
2457 playlist_start(0, 0);
2459 playlist
->index
= 0;
2466 if (!get_previous_directory(dir
))
2468 /* start playing previous directory */
2469 if (playlist_create(dir
, NULL
) != -1)
2471 ft_build_playlist(tree_get_context(), 0);
2472 if (global_settings
.playlist_shuffle
)
2473 playlist_shuffle(current_tick
, -1);
2474 playlist_start(current_playlist
.amount
-1,0);
2475 index
= current_playlist
.amount
-1;
2479 changing_dir
= false;
2485 playlist
->index
= index
;
2487 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2489 /* check to see if we've gone beyond the last inserted track */
2490 int cur
= rotate_index(playlist
, index
);
2491 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2495 /* reset last inserted track */
2496 playlist
->last_insert_pos
= -1;
2498 if (playlist
->control_fd
>= 0)
2500 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2501 -1, -1, NULL
, NULL
, NULL
);
2506 sync_control(playlist
, false);
2514 /* try playing next or previous folder */
2515 bool playlist_next_dir(int direction
)
2517 char dir
[MAX_PATH
+1];
2521 /* not to mess up real playlists */
2522 if(!current_playlist
.in_ram
)
2528 changing_dir
= true;
2530 res
= get_next_directory(dir
);
2532 res
= get_previous_directory(dir
);
2535 if (playlist_create(dir
, NULL
) != -1)
2537 ft_build_playlist(tree_get_context(), 0);
2538 if (global_settings
.playlist_shuffle
)
2539 playlist_shuffle(current_tick
, -1);
2540 playlist_start(0,0);
2549 changing_dir
= false;
2554 /* Get resume info for current playing song. If return value is -1 then
2555 settings shouldn't be saved. */
2556 int playlist_get_resume_info(int *resume_index
)
2558 struct playlist_info
* playlist
= ¤t_playlist
;
2560 *resume_index
= playlist
->index
;
2565 /* Update resume info for current playing song. Returns -1 on error. */
2566 int playlist_update_resume_info(const struct mp3entry
* id3
)
2568 struct playlist_info
* playlist
= ¤t_playlist
;
2572 if (global_settings
.resume_index
!= playlist
->index
||
2573 global_settings
.resume_offset
!= id3
->offset
)
2575 global_settings
.resume_index
= playlist
->index
;
2576 global_settings
.resume_offset
= id3
->offset
;
2582 global_settings
.resume_index
= -1;
2583 global_settings
.resume_offset
= -1;
2590 /* Returns index of current playing track for display purposes. This value
2591 should not be used for resume purposes as it doesn't represent the actual
2592 index into the playlist */
2593 int playlist_get_display_index(void)
2595 struct playlist_info
* playlist
= ¤t_playlist
;
2597 /* first_index should always be index 0 for display purposes */
2598 int index
= rotate_index(playlist
, playlist
->index
);
2603 /* returns number of tracks in current playlist */
2604 int playlist_amount(void)
2606 return playlist_amount_ex(NULL
);
2610 * Create a new playlist If playlist is not NULL then we're loading a
2611 * playlist off disk for viewing/editing. The index_buffer is used to store
2612 * playlist indices (required for and only used if !current playlist). The
2613 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2615 int playlist_create_ex(struct playlist_info
* playlist
,
2616 const char* dir
, const char* file
,
2617 void* index_buffer
, int index_buffer_size
,
2618 void* temp_buffer
, int temp_buffer_size
)
2621 playlist
= ¤t_playlist
;
2624 /* Initialize playlist structure */
2625 int r
= rand() % 10;
2626 playlist
->current
= false;
2628 /* Use random name for control file */
2629 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2630 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2632 playlist
->control_fd
= -1;
2636 int num_indices
= index_buffer_size
/ sizeof(int);
2638 #ifdef HAVE_DIRCACHE
2641 if (num_indices
> global_settings
.max_files_in_playlist
)
2642 num_indices
= global_settings
.max_files_in_playlist
;
2644 playlist
->max_playlist_size
= num_indices
;
2645 playlist
->indices
= index_buffer
;
2646 #ifdef HAVE_DIRCACHE
2647 playlist
->filenames
= (const struct dircache_entry
**)
2648 &playlist
->indices
[num_indices
];
2653 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2654 playlist
->indices
= current_playlist
.indices
;
2655 #ifdef HAVE_DIRCACHE
2656 playlist
->filenames
= current_playlist
.filenames
;
2660 playlist
->buffer_size
= 0;
2661 playlist
->buffer
= NULL
;
2662 mutex_init(&playlist
->control_mutex
);
2665 new_playlist(playlist
, dir
, file
);
2668 /* load the playlist file */
2669 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2675 * Set the specified playlist as the current.
2676 * NOTE: You will get undefined behaviour if something is already playing so
2677 * remember to stop before calling this. Also, this call will
2678 * effectively close your playlist, making it unusable.
2680 int playlist_set_current(struct playlist_info
* playlist
)
2682 if (!playlist
|| (check_control(playlist
) < 0))
2685 empty_playlist(¤t_playlist
, false);
2687 strncpy(current_playlist
.filename
, playlist
->filename
,
2688 sizeof(current_playlist
.filename
));
2690 current_playlist
.fd
= playlist
->fd
;
2692 close(playlist
->control_fd
);
2693 remove(current_playlist
.control_filename
);
2694 if (rename(playlist
->control_filename
,
2695 current_playlist
.control_filename
) < 0)
2697 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2699 if (current_playlist
.control_fd
< 0)
2701 current_playlist
.control_created
= true;
2703 current_playlist
.dirlen
= playlist
->dirlen
;
2705 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2707 memcpy(current_playlist
.indices
, playlist
->indices
,
2708 playlist
->max_playlist_size
*sizeof(int));
2709 #ifdef HAVE_DIRCACHE
2710 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2711 playlist
->max_playlist_size
*sizeof(int));
2715 current_playlist
.first_index
= playlist
->first_index
;
2716 current_playlist
.amount
= playlist
->amount
;
2717 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2718 current_playlist
.seed
= playlist
->seed
;
2719 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2720 current_playlist
.deleted
= playlist
->deleted
;
2721 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2723 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2724 sizeof(current_playlist
.control_cache
));
2725 current_playlist
.num_cached
= playlist
->num_cached
;
2726 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2732 * Close files and delete control file for non-current playlist.
2734 void playlist_close(struct playlist_info
* playlist
)
2739 if (playlist
->fd
>= 0)
2740 close(playlist
->fd
);
2742 if (playlist
->control_fd
>= 0)
2743 close(playlist
->control_fd
);
2745 if (playlist
->control_created
)
2746 remove(playlist
->control_filename
);
2750 * Insert track into playlist at specified position (or one of the special
2751 * positions). Returns position where track was inserted or -1 if error.
2753 int playlist_insert_track(struct playlist_info
* playlist
,
2754 const char *filename
, int position
, bool queue
)
2759 playlist
= ¤t_playlist
;
2761 if (check_control(playlist
) < 0)
2763 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2767 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2771 sync_control(playlist
, false);
2772 if (audio_status() & AUDIO_STATUS_PLAY
)
2773 audio_flush_and_reload_tracks();
2776 #ifdef HAVE_DIRCACHE
2777 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2784 * Insert all tracks from specified directory into playlist.
2786 int playlist_insert_directory(struct playlist_info
* playlist
,
2787 const char *dirname
, int position
, bool queue
,
2792 unsigned char *count_str
;
2795 playlist
= ¤t_playlist
;
2797 if (check_control(playlist
) < 0)
2799 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2804 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2806 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2808 display_playlist_count(count
, count_str
);
2810 result
= add_directory_to_playlist(playlist
, dirname
, &position
, queue
,
2813 sync_control(playlist
, false);
2815 display_playlist_count(count
, count_str
);
2817 if (audio_status() & AUDIO_STATUS_PLAY
)
2818 audio_flush_and_reload_tracks();
2820 #ifdef HAVE_DIRCACHE
2821 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2828 * Insert all tracks from specified playlist into dynamic playlist.
2830 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2831 int position
, bool queue
)
2837 unsigned char *count_str
;
2838 char temp_buf
[MAX_PATH
+1];
2839 char trackname
[MAX_PATH
+1];
2844 playlist
= ¤t_playlist
;
2846 if (check_control(playlist
) < 0)
2848 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2852 fd
= open(filename
, O_RDONLY
);
2855 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2859 /* we need the directory name for formatting purposes */
2862 temp_ptr
= strrchr(filename
+1,'/');
2869 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2871 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2873 display_playlist_count(count
, count_str
);
2875 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2878 if (button_get(false) == SETTINGS_CANCEL
)
2881 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2885 /* we need to format so that relative paths are correctly
2887 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2894 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2903 /* Make sure tracks are inserted in correct order if user
2904 requests INSERT_FIRST */
2905 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
2906 position
= insert_pos
+ 1;
2910 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
2912 display_playlist_count(count
, count_str
);
2914 if (count
== PLAYLIST_DISPLAY_COUNT
&&
2915 (audio_status() & AUDIO_STATUS_PLAY
))
2916 audio_flush_and_reload_tracks();
2920 /* let the other threads work */
2929 sync_control(playlist
, false);
2931 display_playlist_count(count
, count_str
);
2933 if (audio_status() & AUDIO_STATUS_PLAY
)
2934 audio_flush_and_reload_tracks();
2936 #ifdef HAVE_DIRCACHE
2937 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2944 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2945 * we want to delete the current playing track.
2947 int playlist_delete(struct playlist_info
* playlist
, int index
)
2952 playlist
= ¤t_playlist
;
2954 if (check_control(playlist
) < 0)
2956 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2960 if (index
== PLAYLIST_DELETE_CURRENT
)
2961 index
= playlist
->index
;
2963 result
= remove_track_from_playlist(playlist
, index
, true);
2965 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
2966 audio_flush_and_reload_tracks();
2972 * Move track at index to new_index. Tracks between the two are shifted
2973 * appropriately. Returns 0 on success and -1 on failure.
2975 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
2981 bool current
= false;
2983 char filename
[MAX_PATH
];
2986 playlist
= ¤t_playlist
;
2988 if (check_control(playlist
) < 0)
2990 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2994 if (index
== new_index
)
2997 if (index
== playlist
->index
)
2998 /* Moving the current track */
3001 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3002 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3003 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3005 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3006 sizeof(filename
)) < 0)
3009 /* Delete track from original position */
3010 result
= remove_track_from_playlist(playlist
, index
, true);
3014 /* We want to insert the track at the position that was specified by
3015 new_index. This may be different then new_index because of the
3016 shifting that occurred after the delete */
3017 r
= rotate_index(playlist
, new_index
);
3021 new_index
= PLAYLIST_PREPEND
;
3022 else if (r
== playlist
->amount
)
3024 new_index
= PLAYLIST_INSERT_LAST
;
3026 /* Calculate index of desired position */
3027 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3029 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3036 /* Moved the current track */
3039 case PLAYLIST_PREPEND
:
3040 playlist
->index
= playlist
->first_index
;
3042 case PLAYLIST_INSERT_LAST
:
3043 playlist
->index
= playlist
->first_index
- 1;
3044 if (playlist
->index
< 0)
3045 playlist
->index
+= playlist
->amount
;
3048 playlist
->index
= new_index
;
3053 if (audio_status() & AUDIO_STATUS_PLAY
)
3054 audio_flush_and_reload_tracks();
3058 #ifdef HAVE_DIRCACHE
3059 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3065 /* shuffle currently playing playlist */
3066 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3072 playlist
= ¤t_playlist
;
3074 check_control(playlist
);
3076 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3078 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
3079 audio_flush_and_reload_tracks();
3084 /* sort currently playing playlist */
3085 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3090 playlist
= ¤t_playlist
;
3092 check_control(playlist
);
3094 result
= sort_playlist(playlist
, start_current
, true);
3096 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
3097 audio_flush_and_reload_tracks();
3102 /* returns true if playlist has been modified */
3103 bool playlist_modified(const struct playlist_info
* playlist
)
3106 playlist
= ¤t_playlist
;
3108 if (playlist
->shuffle_modified
||
3109 playlist
->deleted
||
3110 playlist
->num_inserted_tracks
> 0)
3116 /* returns index of first track in playlist */
3117 int playlist_get_first_index(const struct playlist_info
* playlist
)
3120 playlist
= ¤t_playlist
;
3122 return playlist
->first_index
;
3125 /* returns shuffle seed of playlist */
3126 int playlist_get_seed(const struct playlist_info
* playlist
)
3129 playlist
= ¤t_playlist
;
3131 return playlist
->seed
;
3134 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3135 int playlist_amount_ex(const struct playlist_info
* playlist
)
3138 playlist
= ¤t_playlist
;
3140 return playlist
->amount
;
3143 /* returns full path of playlist (minus extension) */
3144 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3150 playlist
= ¤t_playlist
;
3152 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3157 /* Remove extension */
3158 sep
= strrchr(buf
, '.');
3165 /* returns the playlist filename */
3166 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3170 playlist
= ¤t_playlist
;
3172 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3180 /* Fills info structure with information about track at specified index.
3181 Returns 0 on success and -1 on failure */
3182 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3183 struct playlist_track_info
* info
)
3189 playlist
= ¤t_playlist
;
3191 if (index
< 0 || index
>= playlist
->amount
)
3194 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3195 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3197 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3198 sizeof(info
->filename
)) < 0)
3205 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3206 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3208 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3212 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3213 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3215 info
->index
= index
;
3216 info
->display_index
= rotate_index(playlist
, index
) + 1;
3221 /* save the current dynamic playlist to specified file */
3222 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3227 char path
[MAX_PATH
+1];
3228 char tmp_buf
[MAX_PATH
+1];
3230 bool overwrite_current
= false;
3231 int* index_buf
= NULL
;
3234 playlist
= ¤t_playlist
;
3236 if (playlist
->amount
<= 0)
3239 /* use current working directory as base for pathname */
3240 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3241 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3244 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3246 /* Attempting to overwrite current playlist file.*/
3248 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3250 /* not enough buffer space to store updated indices */
3251 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3255 /* in_ram buffer is unused for m3u files so we'll use for storing
3257 index_buf
= (int*)playlist
->buffer
;
3259 /* use temporary pathname */
3260 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3261 overwrite_current
= true;
3264 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3267 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3271 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3273 index
= playlist
->first_index
;
3274 for (i
=0; i
<playlist
->amount
; i
++)
3281 if (button_get(false) == SETTINGS_CANCEL
)
3287 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3288 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3289 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3291 /* Don't save queued files */
3294 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3301 if (overwrite_current
)
3302 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3304 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3306 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3313 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3314 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3319 index
= (index
+1)%playlist
->amount
;
3322 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3326 if (overwrite_current
&& result
>= 0)
3330 mutex_lock(&playlist
->control_mutex
);
3332 /* Replace the current playlist with the new one and update indices */
3333 close(playlist
->fd
);
3334 if (remove(playlist
->filename
) >= 0)
3336 if (rename(path
, playlist
->filename
) >= 0)
3338 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3339 if (playlist
->fd
>= 0)
3341 index
= playlist
->first_index
;
3342 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3344 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3346 playlist
->indices
[index
] = index_buf
[count
];
3349 index
= (index
+1)%playlist
->amount
;
3352 /* we need to recreate control because inserted tracks are
3353 now part of the playlist and shuffle has been
3355 result
= recreate_control(playlist
);
3360 mutex_unlock(&playlist
->control_mutex
);