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.
83 #include "applimits.h"
94 #ifdef HAVE_LCD_BITMAP
101 #include "rbunicode.h"
103 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
104 #define PLAYLIST_CONTROL_FILE_VERSION 2
107 Each playlist index has a flag associated with it which identifies what
108 type of track it is. These flags are stored in the 4 high order bits of
111 NOTE: This limits the playlist file size to a max of 256M.
115 01 = Track was prepended into playlist
116 10 = Track was inserted into playlist
117 11 = Track was appended into playlist
122 0 = Track entry is valid
123 1 = Track does not exist on disk and should be skipped
125 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
126 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
127 #define PLAYLIST_QUEUE_MASK 0x20000000
129 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
130 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
131 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
133 #define PLAYLIST_QUEUED 0x20000000
134 #define PLAYLIST_SKIPPED 0x10000000
136 #define PLAYLIST_DISPLAY_COUNT 10
138 struct directory_search_context
{
139 struct playlist_info
* playlist
;
145 static bool changing_dir
= false;
147 static struct playlist_info current_playlist
;
148 static char now_playing
[MAX_PATH
+1];
150 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
151 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
153 static void create_control(struct playlist_info
* playlist
);
154 static int check_control(struct playlist_info
* playlist
);
155 static int recreate_control(struct playlist_info
* playlist
);
156 static void update_playlist_filename(struct playlist_info
* playlist
,
157 const char *dir
, const char *file
);
158 static int add_indices_to_playlist(struct playlist_info
* playlist
,
159 char* buffer
, size_t buflen
);
160 static int add_track_to_playlist(struct playlist_info
* playlist
,
161 const char *filename
, int position
,
162 bool queue
, int seek_pos
);
163 static int directory_search_callback(char* filename
, void* context
);
164 static int remove_track_from_playlist(struct playlist_info
* playlist
,
165 int position
, bool write
);
166 static int randomise_playlist(struct playlist_info
* playlist
,
167 unsigned int seed
, bool start_current
,
169 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
171 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
173 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
175 static int compare(const void* p1
, const void* p2
);
176 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
177 bool control_file
, char *buf
, int buf_length
);
178 static int get_next_directory(char *dir
);
179 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
180 static int get_previous_directory(char *dir
);
181 static int check_subdir_for_music(char *dir
, char *subdir
);
182 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
184 static void display_playlist_count(int count
, const unsigned char *fmt
);
185 static void display_buffer_full(void);
186 static int flush_cached_control(struct playlist_info
* playlist
);
187 static int update_control(struct playlist_info
* playlist
,
188 enum playlist_command command
, int i1
, int i2
,
189 const char* s1
, const char* s2
, void* data
);
190 static void sync_control(struct playlist_info
* playlist
, bool force
);
191 static int rotate_index(const struct playlist_info
* playlist
, int index
);
194 #define PLAYLIST_LOAD_POINTERS 1
196 static struct event_queue playlist_queue
;
197 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x800)/sizeof(long)];
198 static const char playlist_thread_name
[] = "playlist cachectrl";
202 * remove any files and indices associated with the playlist
204 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
206 playlist
->filename
[0] = '\0';
207 playlist
->utf8
= true;
209 if(playlist
->fd
>= 0)
210 /* If there is an already open playlist, close it. */
214 if(playlist
->control_fd
>= 0)
215 close(playlist
->control_fd
);
216 playlist
->control_fd
= -1;
217 playlist
->control_created
= false;
219 playlist
->in_ram
= false;
221 if (playlist
->buffer
)
222 playlist
->buffer
[0] = 0;
224 playlist
->buffer_end_pos
= 0;
227 playlist
->first_index
= 0;
228 playlist
->amount
= 0;
229 playlist
->last_insert_pos
= -1;
231 playlist
->shuffle_modified
= false;
232 playlist
->deleted
= false;
233 playlist
->num_inserted_tracks
= 0;
234 playlist
->started
= false;
236 playlist
->num_cached
= 0;
237 playlist
->pending_control_sync
= false;
239 if (!resume
&& playlist
->current
)
241 /* start with fresh playlist control file when starting new
243 create_control(playlist
);
245 /* Reset resume settings */
246 global_status
.resume_first_index
= 0;
247 global_status
.resume_seed
= -1;
252 * Initialize a new playlist for viewing/editing/playing. dir is the
253 * directory where the playlist is located and file is the filename.
255 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
258 empty_playlist(playlist
, false);
264 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
265 playlist
->in_ram
= true;
267 dir
= ""; /* empty playlist */
270 update_playlist_filename(playlist
, dir
, file
);
272 if (playlist
->control_fd
>= 0)
274 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
275 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
276 sync_control(playlist
, false);
281 * create control file for playlist
283 static void create_control(struct playlist_info
* playlist
)
285 playlist
->control_fd
= open(playlist
->control_filename
,
286 O_CREAT
|O_RDWR
|O_TRUNC
);
287 if (playlist
->control_fd
< 0)
289 if (check_rockboxdir())
291 gui_syncsplash(HZ
*2, true, (unsigned char *)"%s (%d)",
292 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
293 playlist
->control_fd
);
295 playlist
->control_created
= false;
299 playlist
->control_created
= true;
304 * validate the control file. This may include creating/initializing it if
307 static int check_control(struct playlist_info
* playlist
)
309 if (!playlist
->control_created
)
311 create_control(playlist
);
313 if (playlist
->control_fd
>= 0)
315 char* dir
= playlist
->filename
;
316 char* file
= playlist
->filename
+playlist
->dirlen
;
317 char c
= playlist
->filename
[playlist
->dirlen
-1];
319 playlist
->filename
[playlist
->dirlen
-1] = '\0';
321 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
322 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
323 sync_control(playlist
, false);
324 playlist
->filename
[playlist
->dirlen
-1] = c
;
328 if (playlist
->control_fd
< 0)
335 * recreate the control file based on current playlist entries
337 static int recreate_control(struct playlist_info
* playlist
)
339 char temp_file
[MAX_PATH
+1];
344 if(playlist
->control_fd
>= 0)
346 char* dir
= playlist
->filename
;
347 char* file
= playlist
->filename
+playlist
->dirlen
;
348 char c
= playlist
->filename
[playlist
->dirlen
-1];
350 close(playlist
->control_fd
);
352 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
353 playlist
->control_filename
);
355 if (rename(playlist
->control_filename
, temp_file
) < 0)
358 temp_fd
= open(temp_file
, O_RDONLY
);
362 playlist
->control_fd
= open(playlist
->control_filename
,
363 O_CREAT
|O_RDWR
|O_TRUNC
);
364 if (playlist
->control_fd
< 0)
367 playlist
->filename
[playlist
->dirlen
-1] = '\0';
369 /* cannot call update_control() because of mutex */
370 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
371 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
373 playlist
->filename
[playlist
->dirlen
-1] = c
;
383 playlist
->shuffle_modified
= false;
384 playlist
->deleted
= false;
385 playlist
->num_inserted_tracks
= 0;
387 if (playlist
->current
)
389 global_status
.resume_seed
= -1;
393 for (i
=0; i
<playlist
->amount
; i
++)
395 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
397 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
398 char inserted_file
[MAX_PATH
+1];
400 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
402 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
404 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
405 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
408 /* save the position in file where name is written */
409 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
411 result
= fdprintf(playlist
->control_fd
, "%s\n",
414 playlist
->indices
[i
] =
415 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
421 playlist
->num_inserted_tracks
++;
427 fsync(playlist
->control_fd
);
436 * store directory and name of playlist file
438 static void update_playlist_filename(struct playlist_info
* playlist
,
439 const char *dir
, const char *file
)
442 int dirlen
= strlen(dir
);
443 int filelen
= strlen(file
);
445 /* Default to utf8 unless explicitly told otherwise. */
446 playlist
->utf8
= !(filelen
> 4 && strcasecmp(&file
[filelen
- 4], ".m3u") == 0);
448 /* If the dir does not end in trailing slash, we use a separator.
449 Otherwise we don't. */
450 if('/' != dir
[dirlen
-1])
456 playlist
->dirlen
= dirlen
;
458 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
459 "%s%s%s", dir
, sep
, file
);
463 * calculate track offsets within a playlist file
465 static int add_indices_to_playlist(struct playlist_info
* playlist
,
466 char* buffer
, size_t buflen
)
470 unsigned int count
= 0;
475 if(-1 == playlist
->fd
)
476 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
478 return -1; /* failure */
480 #ifdef HAVE_LCD_BITMAP
481 if(global_settings
.statusbar
)
482 lcd_setmargins(0, STATUSBAR_HEIGHT
);
484 lcd_setmargins(0, 0);
487 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD
));
491 /* use mp3 buffer for maximum load speed */
493 #if CONFIG_CODEC != SWCODEC
494 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
495 buflen
= (audiobufend
- audiobuf
);
496 buffer
= (char *)audiobuf
;
498 buffer
= (char *)audio_get_buffer(false, &buflen
);
506 nread
= read(playlist
->fd
, buffer
, buflen
);
507 /* Terminate on EOF */
511 p
= (unsigned char *)buffer
;
513 /* utf8 BOM at beginning of file? */
514 if(i
== 0 && nread
> 3
515 && *p
== 0xef && *(p
+1) == 0xbb && *(p
+2) == 0xbf) {
519 playlist
->utf8
= true; /* Override any earlier indication. */
522 for(count
=0; count
< nread
; count
++,p
++) {
524 /* Are we on a new line? */
525 if((*p
== '\n') || (*p
== '\r'))
535 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
536 display_buffer_full();
541 /* Store a new entry */
542 playlist
->indices
[ playlist
->amount
] = i
+count
;
544 if (playlist
->filenames
)
545 playlist
->filenames
[ playlist
->amount
] = NULL
;
557 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
564 * Removes all tracks, from the playlist, leaving the presently playing
567 int remove_all_tracks(struct playlist_info
*playlist
)
571 if (playlist
== NULL
)
572 playlist
= ¤t_playlist
;
574 while (playlist
->index
> 0)
575 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
578 while (playlist
->amount
> 1)
579 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
582 if (playlist
->amount
== 1) {
583 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
591 * Add track to playlist at specified position. There are five special
592 * positions that can be specified:
593 * PLAYLIST_PREPEND - Add track at beginning of playlist
594 * PLAYLIST_INSERT - Add track after current song. NOTE: If
595 * there are already inserted tracks then track
596 * is added to the end of the insertion list
597 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
598 * matter what other tracks have been inserted
599 * PLAYLIST_INSERT_LAST - Add track to end of playlist
600 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
601 * current playing track and end of playlist
602 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
603 * and inster this track at the end.
605 static int add_track_to_playlist(struct playlist_info
* playlist
,
606 const char *filename
, int position
,
607 bool queue
, int seek_pos
)
609 int insert_position
, orig_position
;
610 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
613 insert_position
= orig_position
= position
;
615 if (playlist
->amount
>= playlist
->max_playlist_size
)
617 display_buffer_full();
623 case PLAYLIST_PREPEND
:
624 position
= insert_position
= playlist
->first_index
;
626 case PLAYLIST_INSERT
:
627 /* if there are already inserted tracks then add track to end of
628 insertion list else add after current playing track */
629 if (playlist
->last_insert_pos
>= 0 &&
630 playlist
->last_insert_pos
< playlist
->amount
&&
631 (playlist
->indices
[playlist
->last_insert_pos
]&
632 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
633 position
= insert_position
= playlist
->last_insert_pos
+1;
634 else if (playlist
->amount
> 0)
635 position
= insert_position
= playlist
->index
+ 1;
637 position
= insert_position
= 0;
639 if (playlist
->started
)
640 playlist
->last_insert_pos
= position
;
642 case PLAYLIST_INSERT_FIRST
:
643 if (playlist
->amount
> 0)
644 position
= insert_position
= playlist
->index
+ 1;
646 position
= insert_position
= 0;
648 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
649 playlist
->last_insert_pos
= position
;
651 case PLAYLIST_INSERT_LAST
:
652 if (playlist
->first_index
> 0)
653 position
= insert_position
= playlist
->first_index
;
655 position
= insert_position
= playlist
->amount
;
657 case PLAYLIST_INSERT_SHUFFLED
:
659 if (playlist
->started
)
662 int n
= playlist
->amount
-
663 rotate_index(playlist
, playlist
->index
);
670 position
= playlist
->index
+ offset
+ 1;
671 if (position
>= playlist
->amount
)
672 position
-= playlist
->amount
;
674 insert_position
= position
;
677 position
= insert_position
= (rand() % (playlist
->amount
+1));
680 case PLAYLIST_REPLACE
:
681 if (remove_all_tracks(playlist
) < 0)
684 position
= insert_position
= playlist
->index
+ 1;
689 flags
|= PLAYLIST_QUEUED
;
691 /* shift indices so that track can be added */
692 for (i
=playlist
->amount
; i
>insert_position
; i
--)
694 playlist
->indices
[i
] = playlist
->indices
[i
-1];
696 if (playlist
->filenames
)
697 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
701 /* update stored indices if needed */
702 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
706 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
707 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
709 playlist
->first_index
++;
711 if (seek_pos
< 0 && playlist
->current
)
713 global_status
.resume_first_index
= playlist
->first_index
;
718 if (insert_position
< playlist
->last_insert_pos
||
719 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
720 playlist
->last_insert_pos
++;
722 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
724 int result
= update_control(playlist
,
725 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
726 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
732 playlist
->indices
[insert_position
] = flags
| seek_pos
;
735 if (playlist
->filenames
)
736 playlist
->filenames
[insert_position
] = NULL
;
740 playlist
->num_inserted_tracks
++;
742 return insert_position
;
746 * Callback for playlist_directory_tracksearch to insert track into
749 static int directory_search_callback(char* filename
, void* context
)
751 struct directory_search_context
* c
=
752 (struct directory_search_context
*) context
;
755 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
763 /* Make sure tracks are inserted in correct order if user requests
765 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
766 c
->position
= insert_pos
+ 1;
768 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
770 unsigned char* count_str
;
773 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
775 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
777 display_playlist_count(c
->count
, count_str
);
779 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
780 (audio_status() & AUDIO_STATUS_PLAY
) &&
781 c
->playlist
->started
)
782 audio_flush_and_reload_tracks();
789 * remove track at specified position
791 static int remove_track_from_playlist(struct playlist_info
* playlist
,
792 int position
, bool write
)
797 if (playlist
->amount
<= 0)
800 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
802 /* shift indices now that track has been removed */
803 for (i
=position
; i
<playlist
->amount
; i
++)
805 playlist
->indices
[i
] = playlist
->indices
[i
+1];
807 if (playlist
->filenames
)
808 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
815 playlist
->num_inserted_tracks
--;
817 playlist
->deleted
= true;
819 /* update stored indices if needed */
820 if (position
< playlist
->index
)
823 if (position
< playlist
->first_index
)
825 playlist
->first_index
--;
829 global_status
.resume_first_index
= playlist
->first_index
;
834 if (position
<= playlist
->last_insert_pos
)
835 playlist
->last_insert_pos
--;
837 if (write
&& playlist
->control_fd
>= 0)
839 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
840 position
, -1, NULL
, NULL
, NULL
);
845 sync_control(playlist
, false);
852 * randomly rearrange the array of indices for the playlist. If start_current
853 * is true then update the index to the new index of the current playing track
855 static int randomise_playlist(struct playlist_info
* playlist
,
856 unsigned int seed
, bool start_current
,
862 unsigned int current
= playlist
->indices
[playlist
->index
];
864 /* seed 0 is used to identify sorted playlist for resume purposes */
868 /* seed with the given seed */
871 /* randomise entire indices list */
872 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
874 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
875 candidate
= rand() % (count
+ 1);
877 /* now swap the values at the 'count' and 'candidate' positions */
878 store
= playlist
->indices
[candidate
];
879 playlist
->indices
[candidate
] = playlist
->indices
[count
];
880 playlist
->indices
[count
] = store
;
882 if (playlist
->filenames
)
884 store
= (long)playlist
->filenames
[candidate
];
885 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
886 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
892 find_and_set_playlist_index(playlist
, current
);
894 /* indices have been moved so last insert position is no longer valid */
895 playlist
->last_insert_pos
= -1;
897 playlist
->seed
= seed
;
898 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
899 playlist
->shuffle_modified
= true;
903 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
904 playlist
->first_index
, NULL
, NULL
, NULL
);
905 global_status
.resume_seed
= seed
;
913 * Sort the array of indices for the playlist. If start_current is true then
914 * set the index to the new index of the current song.
916 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
919 unsigned int current
= playlist
->indices
[playlist
->index
];
921 if (playlist
->amount
> 0)
922 qsort(playlist
->indices
, playlist
->amount
,
923 sizeof(playlist
->indices
[0]), compare
);
926 /** We need to re-check the song names from disk because qsort can't
927 * sort two arrays at once :/
928 * FIXME: Please implement a better way to do this. */
929 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
930 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
934 find_and_set_playlist_index(playlist
, current
);
936 /* indices have been moved so last insert position is no longer valid */
937 playlist
->last_insert_pos
= -1;
939 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
940 playlist
->shuffle_modified
= false;
941 if (write
&& playlist
->control_fd
>= 0)
943 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
944 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
945 global_status
.resume_seed
= 0;
952 /* Calculate how many steps we have to really step when skipping entries
955 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
957 int i
, count
, direction
;
959 int stepped_count
= 0;
972 index
= playlist
->index
;
977 index
+= playlist
->amount
;
978 if (index
>= playlist
->amount
)
979 index
-= playlist
->amount
;
981 /* Check if we found a bad entry. */
982 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
985 /* Are all entries bad? */
986 if (stepped_count
++ > playlist
->amount
)
993 } while (i
<= count
);
998 /* Marks the index of the track to be skipped that is "steps" away from
999 * current playing track.
1001 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1005 if (playlist
== NULL
)
1006 playlist
= ¤t_playlist
;
1008 /* need to account for already skipped tracks */
1009 steps
= calculate_step_count(playlist
, steps
);
1011 index
= playlist
->index
+ steps
;
1013 index
+= playlist
->amount
;
1014 else if (index
>= playlist
->amount
)
1015 index
-= playlist
->amount
;
1017 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1021 * returns the index of the track that is "steps" away from current playing
1024 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1027 int current_index
= playlist
->index
;
1028 int next_index
= -1;
1030 if (playlist
->amount
<= 0)
1033 if (repeat_mode
== -1)
1034 repeat_mode
= global_settings
.repeat_mode
;
1036 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1037 repeat_mode
= REPEAT_ALL
;
1039 steps
= calculate_step_count(playlist
, steps
);
1040 switch (repeat_mode
)
1042 case REPEAT_SHUFFLE
:
1043 /* Treat repeat shuffle just like repeat off. At end of playlist,
1044 play will be resumed in playlist_next() */
1047 current_index
= rotate_index(playlist
, current_index
);
1048 next_index
= current_index
+steps
;
1049 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1052 next_index
= (next_index
+playlist
->first_index
) %
1059 #ifdef AB_REPEAT_ENABLE
1062 next_index
= current_index
;
1068 next_index
= (current_index
+steps
) % playlist
->amount
;
1069 while (next_index
< 0)
1070 next_index
+= playlist
->amount
;
1072 if (steps
>= playlist
->amount
)
1079 /* second time around so skip the queued files */
1080 for (i
=0; i
<playlist
->amount
; i
++)
1082 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1083 index
= (index
+1) % playlist
->amount
;
1095 /* No luck if the whole playlist was bad. */
1096 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1103 * Search for the seek track and set appropriate indices. Used after shuffle
1104 * to make sure the current index is still pointing to correct track.
1106 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1111 /* Set the index to the current song */
1112 for (i
=0; i
<playlist
->amount
; i
++)
1114 if (playlist
->indices
[i
] == seek
)
1116 playlist
->index
= playlist
->first_index
= i
;
1118 if (playlist
->current
)
1120 global_status
.resume_first_index
= i
;
1130 * used to sort track indices. Sort order is as follows:
1131 * 1. Prepended tracks (in prepend order)
1132 * 2. Playlist/directory tracks (in playlist order)
1133 * 3. Inserted/Appended tracks (in insert order)
1135 static int compare(const void* p1
, const void* p2
)
1137 unsigned long* e1
= (unsigned long*) p1
;
1138 unsigned long* e2
= (unsigned long*) p2
;
1139 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1140 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1142 if (flags1
== flags2
)
1143 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1144 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1145 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1147 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1148 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1150 else if (flags1
&& flags2
)
1151 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1156 #ifdef HAVE_DIRCACHE
1158 * Thread to update filename pointers to dircache on background
1159 * without affecting playlist load up performance. This thread also flushes
1160 * any pending control commands when the disk spins up.
1162 static void playlist_thread(void)
1165 bool dirty_pointers
= false;
1166 static char tmp
[MAX_PATH
+1];
1168 struct playlist_info
*playlist
;
1175 if (global_settings
.disk_spindown
> 1 &&
1176 global_settings
.disk_spindown
<= 5)
1177 sleep_time
= global_settings
.disk_spindown
- 1;
1181 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1185 case PLAYLIST_LOAD_POINTERS
:
1186 dirty_pointers
= true;
1189 /* Start the background scanning after either the disk spindown
1190 timeout or 5s, whichever is less */
1192 playlist
= ¤t_playlist
;
1194 if (playlist
->control_fd
>= 0
1196 && ata_disk_is_active()
1200 if (playlist
->num_cached
> 0)
1202 mutex_lock(&playlist
->control_mutex
);
1203 flush_cached_control(playlist
);
1204 mutex_unlock(&playlist
->control_mutex
);
1207 sync_control(playlist
, true);
1210 if (!dirty_pointers
)
1213 if (!dircache_is_enabled() || !playlist
->filenames
1214 || playlist
->amount
<= 0)
1217 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1220 for (index
= 0; index
< playlist
->amount
1221 && queue_empty(&playlist_queue
); index
++)
1223 /* Process only pointers that are not already loaded. */
1224 if (playlist
->filenames
[index
])
1227 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1228 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1230 /* Load the filename from playlist file. */
1231 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1235 /* Set the dircache entry pointer. */
1236 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1238 /* And be on background so user doesn't notice any delays. */
1242 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1245 dirty_pointers
= false;
1249 case SYS_USB_CONNECTED
:
1250 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1251 usb_wait_for_disconnect(&playlist_queue
);
1260 * gets pathname for track at seek index
1262 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1263 bool control_file
, char *buf
, int buf_length
)
1267 char tmp_buf
[MAX_PATH
+1];
1268 char dir_buf
[MAX_PATH
+1];
1270 if (buf_length
> MAX_PATH
+1)
1271 buf_length
= MAX_PATH
+1;
1273 #ifdef HAVE_DIRCACHE
1274 if (dircache_is_enabled() && playlist
->filenames
)
1276 if (playlist
->filenames
[index
] != NULL
)
1278 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1279 max
= strlen(tmp_buf
) + 1;
1286 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1288 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1289 tmp_buf
[MAX_PATH
] = '\0';
1290 max
= strlen(tmp_buf
) + 1;
1294 mutex_lock(&playlist
->control_mutex
);
1297 fd
= playlist
->control_fd
;
1300 if(-1 == playlist
->fd
)
1301 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1309 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1313 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1315 if ((max
> 0) && !playlist
->utf8
)
1321 while ((tmp_buf
[i
] != '\n') && (tmp_buf
[i
] != '\r')
1327 /* Now work back killing white space. */
1328 while ((i
> 0) && isspace(tmp_buf
[i
- 1]))
1333 /* Borrow dir_buf a little... */
1334 /* TODO: iso_decode can overflow dir_buf; it really
1335 * should take a dest size argument.
1337 end
= iso_decode(tmp_buf
, dir_buf
, -1, i
);
1339 strncpy(tmp_buf
, dir_buf
, sizeof(tmp_buf
));
1340 tmp_buf
[sizeof(tmp_buf
) - 1] = 0;
1341 max
= strlen(tmp_buf
);
1346 mutex_unlock(&playlist
->control_mutex
);
1351 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1353 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1359 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1360 dir_buf
[playlist
->dirlen
-1] = 0;
1362 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1365 static int get_next_directory(char *dir
){
1366 return get_next_dir(dir
,true,false);
1369 static int get_previous_directory(char *dir
){
1370 return get_next_dir(dir
,false,false);
1374 * search through all the directories (starting with the current) to find
1375 * one that has tracks to play
1377 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1379 struct playlist_info
* playlist
= ¤t_playlist
;
1381 int sort_dir
= global_settings
.sort_dir
;
1382 char *start_dir
= NULL
;
1384 struct tree_context
* tc
= tree_get_context();
1385 int dirfilter
= *(tc
->dirfilter
);
1387 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1389 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1390 char buffer
[MAX_PATH
];
1391 int folder_count
= 0,i
;
1392 srand(current_tick
);
1395 read(fd
,&folder_count
,sizeof(int));
1398 i
= rand()%folder_count
;
1399 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1400 read(fd
,buffer
,MAX_PATH
);
1401 if (check_subdir_for_music(buffer
,"") ==0)
1409 /* not random folder advance */
1411 /* start with root */
1415 /* start with current directory */
1416 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1417 dir
[playlist
->dirlen
-1] = '\0';
1420 /* use the tree browser dircache to load files */
1421 *(tc
->dirfilter
) = SHOW_ALL
;
1423 /* sort in another direction if previous dir is requested */
1425 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1426 global_settings
.sort_dir
= 4;
1427 else if (global_settings
.sort_dir
== 1)
1428 global_settings
.sort_dir
= 2;
1429 else if (global_settings
.sort_dir
== 2)
1430 global_settings
.sort_dir
= 1;
1431 else if (global_settings
.sort_dir
== 4)
1432 global_settings
.sort_dir
= 0;
1437 struct entry
*files
;
1441 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1443 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1449 files
= (struct entry
*) tc
->dircache
;
1450 num_files
= tc
->filesindir
;
1452 for (i
=0; i
<num_files
; i
++)
1455 if (action_userabort(TIMEOUT_NOBLOCK
))
1462 if (files
[i
].attr
& ATTR_DIRECTORY
)
1466 result
= check_subdir_for_music(dir
, files
[i
].name
);
1473 else if (!strcmp(start_dir
, files
[i
].name
))
1480 /* move down to parent directory. current directory name is
1481 stored as the starting point for the search in parent */
1482 start_dir
= strrchr(dir
, '/');
1493 /* we've overwritten the dircache so tree browser will need to be
1497 /* restore dirfilter & sort_dir */
1498 *(tc
->dirfilter
) = dirfilter
;
1499 global_settings
.sort_dir
= sort_dir
;
1501 /* special case if nothing found: try start searching again from root */
1502 if (result
== -1 && !recursion
){
1503 result
= get_next_dir(dir
,is_forward
, true);
1510 * Checks if there are any music files in the dir or any of its
1511 * subdirectories. May be called recursively.
1513 static int check_subdir_for_music(char *dir
, char *subdir
)
1516 int dirlen
= strlen(dir
);
1519 struct entry
*files
;
1520 bool has_music
= false;
1521 bool has_subdir
= false;
1522 struct tree_context
* tc
= tree_get_context();
1524 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1526 if (ft_load(tc
, dir
) < 0)
1528 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1532 files
= (struct entry
*) tc
->dircache
;
1533 num_files
= tc
->filesindir
;
1535 for (i
=0; i
<num_files
; i
++)
1537 if (files
[i
].attr
& ATTR_DIRECTORY
)
1539 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
1551 for (i
=0; i
<num_files
; i
++)
1553 if (action_userabort(TIMEOUT_NOBLOCK
))
1559 if (files
[i
].attr
& ATTR_DIRECTORY
)
1561 result
= check_subdir_for_music(dir
, files
[i
].name
);
1579 /* we now need to reload our current directory */
1580 if(ft_load(tc
, dir
) < 0)
1581 gui_syncsplash(HZ
*2, true,
1582 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1589 * Returns absolute path of track
1591 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1598 /* Zero-terminate the file name */
1599 while((src
[i
] != '\n') &&
1604 /* Now work back killing white space */
1605 while((src
[i
-1] == ' ') ||
1611 /* replace backslashes with forward slashes */
1612 for ( j
=0; j
<i
; j
++ )
1613 if ( src
[j
] == '\\' )
1618 strncpy(dest
, src
, buf_length
);
1622 /* handle dos style drive letter */
1624 strncpy(dest
, &src
[2], buf_length
);
1625 else if (!strncmp(src
, "../", 3))
1627 /* handle relative paths */
1629 while(!strncmp(&src
[i
], "../", 3))
1631 for (j
=0; j
<i
/3; j
++) {
1632 temp_ptr
= strrchr(dir
, '/');
1638 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1640 else if ( '.' == src
[0] && '/' == src
[1] ) {
1641 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1644 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1652 * Display splash message showing progress of playlist/directory insertion or
1655 static void display_playlist_count(int count
, const unsigned char *fmt
)
1657 lcd_clear_display();
1659 #ifdef HAVE_LCD_BITMAP
1660 if(global_settings
.statusbar
)
1661 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1663 lcd_setmargins(0, 0);
1666 gui_syncsplash(0, true, fmt
, count
,
1667 #if CONFIG_KEYPAD == PLAYER_PAD
1668 str(LANG_STOP_ABORT
)
1676 * Display buffer full message
1678 static void display_buffer_full(void)
1680 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_BUFFER_FULL
));
1684 * Flush any cached control commands to disk. Called when playlist is being
1685 * modified. Returns 0 on success and -1 on failure.
1687 static int flush_cached_control(struct playlist_info
* playlist
)
1692 if (!playlist
->num_cached
)
1695 lseek(playlist
->control_fd
, 0, SEEK_END
);
1697 for (i
=0; i
<playlist
->num_cached
; i
++)
1699 struct playlist_control_cache
* cache
=
1700 &(playlist
->control_cache
[i
]);
1702 switch (cache
->command
)
1704 case PLAYLIST_COMMAND_PLAYLIST
:
1705 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1706 cache
->i1
, cache
->s1
, cache
->s2
);
1708 case PLAYLIST_COMMAND_ADD
:
1709 case PLAYLIST_COMMAND_QUEUE
:
1710 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1711 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1712 cache
->i1
, cache
->i2
);
1715 /* save the position in file where name is written */
1716 int* seek_pos
= (int *)cache
->data
;
1717 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1718 result
= fdprintf(playlist
->control_fd
, "%s\n",
1722 case PLAYLIST_COMMAND_DELETE
:
1723 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1725 case PLAYLIST_COMMAND_SHUFFLE
:
1726 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1727 cache
->i1
, cache
->i2
);
1729 case PLAYLIST_COMMAND_UNSHUFFLE
:
1730 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1732 case PLAYLIST_COMMAND_RESET
:
1733 result
= fdprintf(playlist
->control_fd
, "R\n");
1745 if (global_status
.resume_seed
>= 0)
1747 global_status
.resume_seed
= -1;
1751 playlist
->num_cached
= 0;
1752 playlist
->pending_control_sync
= true;
1759 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1766 * Update control data with new command. Depending on the command, it may be
1767 * cached or flushed to disk.
1769 static int update_control(struct playlist_info
* playlist
,
1770 enum playlist_command command
, int i1
, int i2
,
1771 const char* s1
, const char* s2
, void* data
)
1774 struct playlist_control_cache
* cache
;
1777 mutex_lock(&playlist
->control_mutex
);
1779 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1781 cache
->command
= command
;
1790 case PLAYLIST_COMMAND_PLAYLIST
:
1791 case PLAYLIST_COMMAND_ADD
:
1792 case PLAYLIST_COMMAND_QUEUE
:
1793 #ifndef HAVE_DIRCACHE
1794 case PLAYLIST_COMMAND_DELETE
:
1795 case PLAYLIST_COMMAND_RESET
:
1799 case PLAYLIST_COMMAND_SHUFFLE
:
1800 case PLAYLIST_COMMAND_UNSHUFFLE
:
1802 /* only flush when needed */
1806 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1807 result
= flush_cached_control(playlist
);
1809 mutex_unlock(&playlist
->control_mutex
);
1815 * sync control file to disk
1817 static void sync_control(struct playlist_info
* playlist
, bool force
)
1819 #ifdef HAVE_DIRCACHE
1820 if (playlist
->started
&& force
)
1824 if (playlist
->started
)
1827 if (playlist
->pending_control_sync
)
1829 mutex_lock(&playlist
->control_mutex
);
1830 fsync(playlist
->control_fd
);
1831 playlist
->pending_control_sync
= false;
1832 mutex_unlock(&playlist
->control_mutex
);
1838 * Rotate indices such that first_index is index 0
1840 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1842 index
-= playlist
->first_index
;
1844 index
+= playlist
->amount
;
1850 * Initialize playlist entries at startup
1852 void playlist_init(void)
1854 struct playlist_info
* playlist
= ¤t_playlist
;
1856 playlist
->current
= true;
1857 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1858 "%s", PLAYLIST_CONTROL_FILE
);
1860 playlist
->control_fd
= -1;
1861 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1862 playlist
->indices
= buffer_alloc(
1863 playlist
->max_playlist_size
* sizeof(int));
1864 playlist
->buffer_size
=
1865 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1866 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1867 mutex_init(&playlist
->control_mutex
);
1868 empty_playlist(playlist
, true);
1870 #ifdef HAVE_DIRCACHE
1871 playlist
->filenames
= buffer_alloc(
1872 playlist
->max_playlist_size
* sizeof(int));
1873 memset(playlist
->filenames
, 0,
1874 playlist
->max_playlist_size
* sizeof(int));
1875 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1876 playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1877 IF_COP(, CPU
, false));
1878 queue_init(&playlist_queue
, true);
1883 * Clean playlist at shutdown
1885 void playlist_shutdown(void)
1887 struct playlist_info
* playlist
= ¤t_playlist
;
1889 if (playlist
->control_fd
>= 0)
1891 mutex_lock(&playlist
->control_mutex
);
1893 if (playlist
->num_cached
> 0)
1894 flush_cached_control(playlist
);
1896 close(playlist
->control_fd
);
1898 mutex_unlock(&playlist
->control_mutex
);
1903 * Create new playlist
1905 int playlist_create(const char *dir
, const char *file
)
1907 struct playlist_info
* playlist
= ¤t_playlist
;
1909 new_playlist(playlist
, dir
, file
);
1912 /* load the playlist file */
1913 add_indices_to_playlist(playlist
, NULL
, 0);
1918 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1921 * Restore the playlist state based on control file commands. Called to
1922 * resume playback after shutdown.
1924 int playlist_resume(void)
1926 struct playlist_info
* playlist
= ¤t_playlist
;
1931 int control_file_size
= 0;
1935 /* use mp3 buffer for maximum load speed */
1936 #if CONFIG_CODEC != SWCODEC
1937 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1938 buflen
= (audiobufend
- audiobuf
);
1939 buffer
= (char *)audiobuf
;
1941 buffer
= (char *)audio_get_buffer(false, &buflen
);
1944 empty_playlist(playlist
, true);
1946 gui_syncsplash(0, true, str(LANG_WAIT
));
1947 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1948 if (playlist
->control_fd
< 0)
1950 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1953 playlist
->control_created
= true;
1955 control_file_size
= filesize(playlist
->control_fd
);
1956 if (control_file_size
<= 0)
1958 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1962 /* read a small amount first to get the header */
1963 nread
= read(playlist
->control_fd
, buffer
,
1964 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1967 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1971 playlist
->started
= true;
1977 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1978 int last_newline
= 0;
1980 bool newline
= true;
1981 bool exit_loop
= false;
1986 unsigned long last_tick
= current_tick
;
1988 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1990 /* So a splash while we are loading. */
1991 if (current_tick
- last_tick
> HZ
/4)
1993 gui_syncsplash(0, true, str(LANG_LOADING_PERCENT
),
1994 (total_read
+count
)*100/control_file_size
,
1995 #if CONFIG_KEYPAD == PLAYER_PAD
1996 str(LANG_STOP_ABORT
)
2001 if (action_userabort(TIMEOUT_NOBLOCK
))
2004 * Not sure how to implement this, somebody more familiar
2005 * with the code, please fix this. */
2007 last_tick
= current_tick
;
2010 /* Are we on a new line? */
2011 if((*p
== '\n') || (*p
== '\r'))
2015 /* save last_newline in case we need to load more data */
2016 last_newline
= count
;
2018 switch (current_command
)
2020 case PLAYLIST_COMMAND_PLAYLIST
:
2022 /* str1=version str2=dir str3=file */
2038 version
= atoi(str1
);
2040 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2043 update_playlist_filename(playlist
, str2
, str3
);
2045 if (str3
[0] != '\0')
2047 /* NOTE: add_indices_to_playlist() overwrites the
2048 audiobuf so we need to reload control file
2050 add_indices_to_playlist(playlist
, NULL
, 0);
2052 else if (str2
[0] != '\0')
2054 playlist
->in_ram
= true;
2055 resume_directory(str2
);
2058 /* load the rest of the data */
2064 case PLAYLIST_COMMAND_ADD
:
2065 case PLAYLIST_COMMAND_QUEUE
:
2067 /* str1=position str2=last_position str3=file */
2068 int position
, last_position
;
2071 if (!str1
|| !str2
|| !str3
)
2078 position
= atoi(str1
);
2079 last_position
= atoi(str2
);
2081 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2084 /* seek position is based on str3's position in
2086 if (add_track_to_playlist(playlist
, str3
, position
,
2087 queue
, total_read
+(str3
-buffer
)) < 0)
2090 playlist
->last_insert_pos
= last_position
;
2094 case PLAYLIST_COMMAND_DELETE
:
2106 position
= atoi(str1
);
2108 if (remove_track_from_playlist(playlist
, position
,
2114 case PLAYLIST_COMMAND_SHUFFLE
:
2116 /* str1=seed str2=first_index */
2128 /* Always sort list before shuffling */
2129 sort_playlist(playlist
, false, false);
2133 playlist
->first_index
= atoi(str2
);
2135 if (randomise_playlist(playlist
, seed
, false,
2142 case PLAYLIST_COMMAND_UNSHUFFLE
:
2144 /* str1=first_index */
2152 playlist
->first_index
= atoi(str1
);
2154 if (sort_playlist(playlist
, false, false) < 0)
2160 case PLAYLIST_COMMAND_RESET
:
2162 playlist
->last_insert_pos
= -1;
2165 case PLAYLIST_COMMAND_COMMENT
:
2172 /* to ignore any extra newlines */
2173 current_command
= PLAYLIST_COMMAND_COMMENT
;
2179 /* first non-comment line must always specify playlist */
2180 if (first
&& *p
!= 'P' && *p
!= '#')
2190 /* playlist can only be specified once */
2198 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2201 current_command
= PLAYLIST_COMMAND_ADD
;
2204 current_command
= PLAYLIST_COMMAND_QUEUE
;
2207 current_command
= PLAYLIST_COMMAND_DELETE
;
2210 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2213 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2216 current_command
= PLAYLIST_COMMAND_RESET
;
2219 current_command
= PLAYLIST_COMMAND_COMMENT
;
2232 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2234 /* all control file strings are separated with a colon.
2235 Replace the colon with 0 to get proper strings that can be
2236 used by commands above */
2242 if ((count
+1) < nread
)
2256 /* allow last string to contain colons */
2267 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2271 if (!newline
|| (exit_loop
&& count
<nread
))
2273 if ((total_read
+ count
) >= control_file_size
)
2275 /* no newline at end of control file */
2276 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2280 /* We didn't end on a newline or we exited loop prematurely.
2281 Either way, re-read the remainder. */
2282 count
= last_newline
;
2283 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2286 total_read
+= count
;
2289 /* still looking for header */
2290 nread
= read(playlist
->control_fd
, buffer
,
2291 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2293 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2295 /* Terminate on EOF */
2298 if (global_status
.resume_seed
>= 0)
2300 /* Apply shuffle command saved in settings */
2301 if (global_status
.resume_seed
== 0)
2302 sort_playlist(playlist
, false, true);
2306 sort_playlist(playlist
, false, false);
2308 randomise_playlist(playlist
, global_status
.resume_seed
,
2313 playlist
->first_index
= global_status
.resume_first_index
;
2318 #ifdef HAVE_DIRCACHE
2319 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2326 * Add track to in_ram playlist. Used when playing directories.
2328 int playlist_add(const char *filename
)
2330 struct playlist_info
* playlist
= ¤t_playlist
;
2331 int len
= strlen(filename
);
2333 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2334 (playlist
->amount
>= playlist
->max_playlist_size
))
2336 display_buffer_full();
2340 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2341 #ifdef HAVE_DIRCACHE
2342 playlist
->filenames
[playlist
->amount
] = NULL
;
2346 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2347 playlist
->buffer_end_pos
+= len
;
2348 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2353 /* shuffle newly created playlist using random seed. */
2354 int playlist_shuffle(int random_seed
, int start_index
)
2356 struct playlist_info
* playlist
= ¤t_playlist
;
2358 unsigned int seek_pos
= 0;
2359 bool start_current
= false;
2361 if (start_index
>= 0 && global_settings
.play_selected
)
2363 /* store the seek position before the shuffle */
2364 seek_pos
= playlist
->indices
[start_index
];
2365 playlist
->index
= global_status
.resume_first_index
=
2366 playlist
->first_index
= start_index
;
2367 start_current
= true;
2370 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
2372 randomise_playlist(playlist
, random_seed
, start_current
, true);
2374 return playlist
->index
;
2377 /* start playing current playlist at specified index/offset */
2378 int playlist_start(int start_index
, int offset
)
2380 struct playlist_info
* playlist
= ¤t_playlist
;
2382 playlist
->index
= start_index
;
2384 #if CONFIG_CODEC != SWCODEC
2385 talk_buffer_steal(); /* will use the mp3 buffer */
2388 playlist
->started
= true;
2389 sync_control(playlist
, false);
2395 /* Returns false if 'steps' is out of bounds, else true */
2396 bool playlist_check(int steps
)
2398 struct playlist_info
* playlist
= ¤t_playlist
;
2400 /* always allow folder navigation */
2401 if (global_settings
.next_folder
&& playlist
->in_ram
)
2404 int index
= get_next_index(playlist
, steps
, -1);
2406 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2407 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2409 return (index
>= 0);
2412 /* get trackname of track that is "steps" away from current playing track.
2413 NULL is used to identify end of playlist */
2414 char* playlist_peek(int steps
)
2416 struct playlist_info
* playlist
= ¤t_playlist
;
2423 index
= get_next_index(playlist
, steps
, -1);
2427 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2428 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2430 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2434 temp_ptr
= now_playing
;
2436 if (!playlist
->in_ram
|| control_file
)
2438 /* remove bogus dirs from beginning of path
2439 (workaround for buggy playlist creation tools) */
2442 #ifdef HAVE_DIRCACHE
2443 if (dircache_is_enabled())
2445 if (dircache_get_entry_ptr(temp_ptr
))
2451 fd
= open(temp_ptr
, O_RDONLY
);
2459 temp_ptr
= strchr(temp_ptr
+1, '/');
2464 /* Even though this is an invalid file, we still need to pass a
2465 file name to the caller because NULL is used to indicate end
2475 * Update indices as track has changed
2477 int playlist_next(int steps
)
2479 struct playlist_info
* playlist
= ¤t_playlist
;
2483 #ifdef AB_REPEAT_ENABLE
2484 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2486 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2490 /* We need to delete all the queued songs */
2491 for (i
=0, j
=steps
; i
<j
; i
++)
2493 index
= get_next_index(playlist
, i
, -1);
2495 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2497 remove_track_from_playlist(playlist
, index
, true);
2498 steps
--; /* one less track */
2503 index
= get_next_index(playlist
, steps
, -1);
2507 /* end of playlist... or is it */
2508 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2509 playlist
->amount
> 1)
2511 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2512 playlist
->first_index
= global_status
.resume_first_index
= 0;
2513 sort_playlist(playlist
, false, false);
2514 randomise_playlist(playlist
, current_tick
, false, true);
2515 #if CONFIG_CODEC != SWCODEC
2516 playlist_start(0, 0);
2518 playlist
->index
= 0;
2521 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2523 char dir
[MAX_PATH
+1];
2525 changing_dir
= true;
2528 if (!get_next_directory(dir
))
2530 /* start playing next directory */
2531 if (playlist_create(dir
, NULL
) != -1)
2533 ft_build_playlist(tree_get_context(), 0);
2534 if (global_settings
.playlist_shuffle
)
2535 playlist_shuffle(current_tick
, -1);
2536 #if CONFIG_CODEC != SWCODEC
2537 playlist_start(0, 0);
2539 playlist
->index
= index
= 0;
2545 if (!get_previous_directory(dir
))
2547 /* start playing previous directory */
2548 if (playlist_create(dir
, NULL
) != -1)
2550 ft_build_playlist(tree_get_context(), 0);
2551 if (global_settings
.playlist_shuffle
)
2552 playlist_shuffle(current_tick
, -1);
2553 #if CONFIG_CODEC != SWCODEC
2554 playlist_start(current_playlist
.amount
-1, 0);
2556 playlist
->index
= index
= current_playlist
.amount
- 1;
2560 changing_dir
= false;
2566 playlist
->index
= index
;
2568 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2570 /* check to see if we've gone beyond the last inserted track */
2571 int cur
= rotate_index(playlist
, index
);
2572 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2576 /* reset last inserted track */
2577 playlist
->last_insert_pos
= -1;
2579 if (playlist
->control_fd
>= 0)
2581 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2582 -1, -1, NULL
, NULL
, NULL
);
2587 sync_control(playlist
, false);
2595 /* try playing next or previous folder */
2596 bool playlist_next_dir(int direction
)
2598 char dir
[MAX_PATH
+1];
2602 /* not to mess up real playlists */
2603 if(!current_playlist
.in_ram
)
2609 changing_dir
= true;
2611 res
= get_next_directory(dir
);
2613 res
= get_previous_directory(dir
);
2616 if (playlist_create(dir
, NULL
) != -1)
2618 ft_build_playlist(tree_get_context(), 0);
2619 if (global_settings
.playlist_shuffle
)
2620 playlist_shuffle(current_tick
, -1);
2621 #if (CONFIG_CODEC != SWCODEC)
2622 playlist_start(0,0);
2632 changing_dir
= false;
2637 /* Get resume info for current playing song. If return value is -1 then
2638 settings shouldn't be saved. */
2639 int playlist_get_resume_info(int *resume_index
)
2641 struct playlist_info
* playlist
= ¤t_playlist
;
2643 *resume_index
= playlist
->index
;
2648 /* Update resume info for current playing song. Returns -1 on error. */
2649 int playlist_update_resume_info(const struct mp3entry
* id3
)
2651 struct playlist_info
* playlist
= ¤t_playlist
;
2655 if (global_status
.resume_index
!= playlist
->index
||
2656 global_status
.resume_offset
!= id3
->offset
)
2658 global_status
.resume_index
= playlist
->index
;
2659 global_status
.resume_offset
= id3
->offset
;
2665 global_status
.resume_index
= -1;
2666 global_status
.resume_offset
= -1;
2673 /* Returns index of current playing track for display purposes. This value
2674 should not be used for resume purposes as it doesn't represent the actual
2675 index into the playlist */
2676 int playlist_get_display_index(void)
2678 struct playlist_info
* playlist
= ¤t_playlist
;
2680 /* first_index should always be index 0 for display purposes */
2681 int index
= rotate_index(playlist
, playlist
->index
);
2686 /* returns number of tracks in current playlist */
2687 int playlist_amount(void)
2689 return playlist_amount_ex(NULL
);
2693 * Create a new playlist If playlist is not NULL then we're loading a
2694 * playlist off disk for viewing/editing. The index_buffer is used to store
2695 * playlist indices (required for and only used if !current playlist). The
2696 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2698 int playlist_create_ex(struct playlist_info
* playlist
,
2699 const char* dir
, const char* file
,
2700 void* index_buffer
, int index_buffer_size
,
2701 void* temp_buffer
, int temp_buffer_size
)
2704 playlist
= ¤t_playlist
;
2707 /* Initialize playlist structure */
2708 int r
= rand() % 10;
2709 playlist
->current
= false;
2711 /* Use random name for control file */
2712 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2713 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2715 playlist
->control_fd
= -1;
2719 int num_indices
= index_buffer_size
/ sizeof(int);
2721 #ifdef HAVE_DIRCACHE
2724 if (num_indices
> global_settings
.max_files_in_playlist
)
2725 num_indices
= global_settings
.max_files_in_playlist
;
2727 playlist
->max_playlist_size
= num_indices
;
2728 playlist
->indices
= index_buffer
;
2729 #ifdef HAVE_DIRCACHE
2730 playlist
->filenames
= (const struct dircache_entry
**)
2731 &playlist
->indices
[num_indices
];
2736 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2737 playlist
->indices
= current_playlist
.indices
;
2738 #ifdef HAVE_DIRCACHE
2739 playlist
->filenames
= current_playlist
.filenames
;
2743 playlist
->buffer_size
= 0;
2744 playlist
->buffer
= NULL
;
2745 mutex_init(&playlist
->control_mutex
);
2748 new_playlist(playlist
, dir
, file
);
2751 /* load the playlist file */
2752 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2758 * Set the specified playlist as the current.
2759 * NOTE: You will get undefined behaviour if something is already playing so
2760 * remember to stop before calling this. Also, this call will
2761 * effectively close your playlist, making it unusable.
2763 int playlist_set_current(struct playlist_info
* playlist
)
2765 if (!playlist
|| (check_control(playlist
) < 0))
2768 empty_playlist(¤t_playlist
, false);
2770 strncpy(current_playlist
.filename
, playlist
->filename
,
2771 sizeof(current_playlist
.filename
));
2773 current_playlist
.utf8
= playlist
->utf8
;
2774 current_playlist
.fd
= playlist
->fd
;
2776 close(playlist
->control_fd
);
2777 close(current_playlist
.control_fd
);
2778 remove(current_playlist
.control_filename
);
2779 if (rename(playlist
->control_filename
,
2780 current_playlist
.control_filename
) < 0)
2782 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2784 if (current_playlist
.control_fd
< 0)
2786 current_playlist
.control_created
= true;
2788 current_playlist
.dirlen
= playlist
->dirlen
;
2790 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2792 memcpy(current_playlist
.indices
, playlist
->indices
,
2793 playlist
->max_playlist_size
*sizeof(int));
2794 #ifdef HAVE_DIRCACHE
2795 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2796 playlist
->max_playlist_size
*sizeof(int));
2800 current_playlist
.first_index
= playlist
->first_index
;
2801 current_playlist
.amount
= playlist
->amount
;
2802 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2803 current_playlist
.seed
= playlist
->seed
;
2804 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2805 current_playlist
.deleted
= playlist
->deleted
;
2806 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2808 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2809 sizeof(current_playlist
.control_cache
));
2810 current_playlist
.num_cached
= playlist
->num_cached
;
2811 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2817 * Close files and delete control file for non-current playlist.
2819 void playlist_close(struct playlist_info
* playlist
)
2824 if (playlist
->fd
>= 0)
2825 close(playlist
->fd
);
2827 if (playlist
->control_fd
>= 0)
2828 close(playlist
->control_fd
);
2830 if (playlist
->control_created
)
2831 remove(playlist
->control_filename
);
2834 void playlist_sync(struct playlist_info
* playlist
)
2837 playlist
= ¤t_playlist
;
2839 sync_control(playlist
, false);
2840 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2841 audio_flush_and_reload_tracks();
2843 #ifdef HAVE_DIRCACHE
2844 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2849 * Insert track into playlist at specified position (or one of the special
2850 * positions). Returns position where track was inserted or -1 if error.
2852 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2853 int position
, bool queue
, bool sync
)
2858 playlist
= ¤t_playlist
;
2860 if (check_control(playlist
) < 0)
2862 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2866 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2868 /* Check if we want manually sync later. For example when adding
2869 * bunch of files from tagcache, syncing after every file wouldn't be
2870 * a good thing to do. */
2871 if (sync
&& result
>= 0)
2872 playlist_sync(playlist
);
2878 * Insert all tracks from specified directory into playlist.
2880 int playlist_insert_directory(struct playlist_info
* playlist
,
2881 const char *dirname
, int position
, bool queue
,
2885 unsigned char *count_str
;
2886 struct directory_search_context context
;
2889 playlist
= ¤t_playlist
;
2891 if (check_control(playlist
) < 0)
2893 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2897 if (position
== PLAYLIST_REPLACE
)
2899 if (remove_all_tracks(playlist
) == 0)
2900 position
= PLAYLIST_INSERT_LAST
;
2906 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2908 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2910 display_playlist_count(0, count_str
);
2912 context
.playlist
= playlist
;
2913 context
.position
= position
;
2914 context
.queue
= queue
;
2919 result
= playlist_directory_tracksearch(dirname
, recurse
,
2920 directory_search_callback
, &context
);
2922 sync_control(playlist
, false);
2926 display_playlist_count(context
.count
, count_str
);
2928 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2929 audio_flush_and_reload_tracks();
2931 #ifdef HAVE_DIRCACHE
2932 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2939 * Insert all tracks from specified playlist into dynamic playlist.
2941 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2942 int position
, bool queue
)
2948 unsigned char *count_str
;
2949 char temp_buf
[MAX_PATH
+1];
2950 char trackname
[MAX_PATH
+1];
2955 playlist
= ¤t_playlist
;
2957 if (check_control(playlist
) < 0)
2959 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2963 fd
= open(filename
, O_RDONLY
);
2966 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2970 /* we need the directory name for formatting purposes */
2973 temp_ptr
= strrchr(filename
+1,'/');
2980 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2982 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2984 display_playlist_count(count
, count_str
);
2986 if (position
== PLAYLIST_REPLACE
)
2988 if (remove_all_tracks(playlist
) == 0)
2989 position
= PLAYLIST_INSERT_LAST
;
2995 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2998 if (action_userabort(TIMEOUT_NOBLOCK
))
3001 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3005 /* we need to format so that relative paths are correctly
3007 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3014 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3023 /* Make sure tracks are inserted in correct order if user
3024 requests INSERT_FIRST */
3025 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3026 position
= insert_pos
+ 1;
3030 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3032 display_playlist_count(count
, count_str
);
3034 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3035 (audio_status() & AUDIO_STATUS_PLAY
) &&
3037 audio_flush_and_reload_tracks();
3041 /* let the other threads work */
3050 sync_control(playlist
, false);
3054 display_playlist_count(count
, count_str
);
3056 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3057 audio_flush_and_reload_tracks();
3059 #ifdef HAVE_DIRCACHE
3060 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3067 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3068 * we want to delete the current playing track.
3070 int playlist_delete(struct playlist_info
* playlist
, int index
)
3075 playlist
= ¤t_playlist
;
3077 if (check_control(playlist
) < 0)
3079 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3083 if (index
== PLAYLIST_DELETE_CURRENT
)
3084 index
= playlist
->index
;
3086 result
= remove_track_from_playlist(playlist
, index
, true);
3088 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3090 audio_flush_and_reload_tracks();
3096 * Move track at index to new_index. Tracks between the two are shifted
3097 * appropriately. Returns 0 on success and -1 on failure.
3099 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3105 bool current
= false;
3107 char filename
[MAX_PATH
];
3110 playlist
= ¤t_playlist
;
3112 if (check_control(playlist
) < 0)
3114 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3118 if (index
== new_index
)
3121 if (index
== playlist
->index
)
3122 /* Moving the current track */
3125 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3126 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3127 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3129 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3130 sizeof(filename
)) < 0)
3133 /* Delete track from original position */
3134 result
= remove_track_from_playlist(playlist
, index
, true);
3138 /* We want to insert the track at the position that was specified by
3139 new_index. This may be different then new_index because of the
3140 shifting that occurred after the delete */
3141 r
= rotate_index(playlist
, new_index
);
3145 new_index
= PLAYLIST_PREPEND
;
3146 else if (r
== playlist
->amount
)
3148 new_index
= PLAYLIST_INSERT_LAST
;
3150 /* Calculate index of desired position */
3151 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3153 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3160 /* Moved the current track */
3163 case PLAYLIST_PREPEND
:
3164 playlist
->index
= playlist
->first_index
;
3166 case PLAYLIST_INSERT_LAST
:
3167 playlist
->index
= playlist
->first_index
- 1;
3168 if (playlist
->index
< 0)
3169 playlist
->index
+= playlist
->amount
;
3172 playlist
->index
= new_index
;
3177 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3178 audio_flush_and_reload_tracks();
3182 #ifdef HAVE_DIRCACHE
3183 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3189 /* shuffle currently playing playlist */
3190 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3196 playlist
= ¤t_playlist
;
3198 check_control(playlist
);
3200 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3202 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3204 audio_flush_and_reload_tracks();
3209 /* sort currently playing playlist */
3210 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3215 playlist
= ¤t_playlist
;
3217 check_control(playlist
);
3219 result
= sort_playlist(playlist
, start_current
, true);
3221 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3223 audio_flush_and_reload_tracks();
3228 /* returns true if playlist has been modified */
3229 bool playlist_modified(const struct playlist_info
* playlist
)
3232 playlist
= ¤t_playlist
;
3234 if (playlist
->shuffle_modified
||
3235 playlist
->deleted
||
3236 playlist
->num_inserted_tracks
> 0)
3242 /* returns index of first track in playlist */
3243 int playlist_get_first_index(const struct playlist_info
* playlist
)
3246 playlist
= ¤t_playlist
;
3248 return playlist
->first_index
;
3251 /* returns shuffle seed of playlist */
3252 int playlist_get_seed(const struct playlist_info
* playlist
)
3255 playlist
= ¤t_playlist
;
3257 return playlist
->seed
;
3260 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3261 int playlist_amount_ex(const struct playlist_info
* playlist
)
3264 playlist
= ¤t_playlist
;
3266 return playlist
->amount
;
3269 /* returns full path of playlist (minus extension) */
3270 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3276 playlist
= ¤t_playlist
;
3278 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3283 /* Remove extension */
3284 sep
= strrchr(buf
, '.');
3291 /* returns the playlist filename */
3292 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3296 playlist
= ¤t_playlist
;
3298 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3306 /* Fills info structure with information about track at specified index.
3307 Returns 0 on success and -1 on failure */
3308 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3309 struct playlist_track_info
* info
)
3315 playlist
= ¤t_playlist
;
3317 if (index
< 0 || index
>= playlist
->amount
)
3320 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3321 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3323 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3324 sizeof(info
->filename
)) < 0)
3331 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3332 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3334 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3338 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3339 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3341 info
->index
= index
;
3342 info
->display_index
= rotate_index(playlist
, index
) + 1;
3347 /* save the current dynamic playlist to specified file */
3348 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3353 char path
[MAX_PATH
+1];
3354 char tmp_buf
[MAX_PATH
+1];
3356 bool overwrite_current
= false;
3357 int* index_buf
= NULL
;
3360 playlist
= ¤t_playlist
;
3362 if (playlist
->amount
<= 0)
3365 /* use current working directory as base for pathname */
3366 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3367 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3370 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3372 /* Attempting to overwrite current playlist file.*/
3374 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3376 /* not enough buffer space to store updated indices */
3377 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3381 /* in_ram buffer is unused for m3u files so we'll use for storing
3383 index_buf
= (int*)playlist
->buffer
;
3385 /* use temporary pathname */
3386 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3387 overwrite_current
= true;
3390 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3393 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3397 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3401 index
= playlist
->first_index
;
3402 for (i
=0; i
<playlist
->amount
; i
++)
3409 if (action_userabort(TIMEOUT_NOBLOCK
))
3415 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3416 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3417 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3419 /* Don't save queued files */
3422 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3429 if (overwrite_current
)
3430 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3432 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3434 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3441 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3442 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3447 index
= (index
+1)%playlist
->amount
;
3450 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3454 if (overwrite_current
&& result
>= 0)
3458 mutex_lock(&playlist
->control_mutex
);
3460 /* Replace the current playlist with the new one and update indices */
3461 close(playlist
->fd
);
3462 if (remove(playlist
->filename
) >= 0)
3464 if (rename(path
, playlist
->filename
) >= 0)
3466 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3467 if (playlist
->fd
>= 0)
3469 index
= playlist
->first_index
;
3470 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3472 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3474 playlist
->indices
[index
] = index_buf
[count
];
3477 index
= (index
+1)%playlist
->amount
;
3480 /* we need to recreate control because inserted tracks are
3481 now part of the playlist and shuffle has been
3483 result
= recreate_control(playlist
);
3488 mutex_unlock(&playlist
->control_mutex
);
3498 * Search specified directory for tracks and notify via callback. May be
3499 * called recursively.
3501 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3502 int (*callback
)(char*, void*),
3505 char buf
[MAX_PATH
+1];
3509 struct entry
*files
;
3510 struct tree_context
* tc
= tree_get_context();
3511 int old_dirfilter
= *(tc
->dirfilter
);
3516 /* use the tree browser dircache to load files */
3517 *(tc
->dirfilter
) = SHOW_ALL
;
3519 if (ft_load(tc
, dirname
) < 0)
3521 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3522 *(tc
->dirfilter
) = old_dirfilter
;
3526 files
= (struct entry
*) tc
->dircache
;
3527 num_files
= tc
->filesindir
;
3529 /* we've overwritten the dircache so tree browser will need to be
3533 for (i
=0; i
<num_files
; i
++)
3536 if (action_userabort(TIMEOUT_NOBLOCK
))
3542 if (files
[i
].attr
& ATTR_DIRECTORY
)
3546 /* recursively add directories */
3547 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3548 result
= playlist_directory_tracksearch(buf
, recurse
,
3553 /* we now need to reload our current directory */
3554 if(ft_load(tc
, dirname
) < 0)
3560 files
= (struct entry
*) tc
->dircache
;
3561 num_files
= tc
->filesindir
;
3571 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
3573 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3575 if (callback(buf
, context
) != 0)
3581 /* let the other threads work */
3586 /* restore dirfilter */
3587 *(tc
->dirfilter
) = old_dirfilter
;