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"
93 #include "filetypes.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, (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, 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 #ifndef HAVE_FLASH_STORAGE
1176 if (global_settings
.disk_spindown
> 1 &&
1177 global_settings
.disk_spindown
<= 5)
1178 sleep_time
= global_settings
.disk_spindown
- 1;
1183 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1187 case PLAYLIST_LOAD_POINTERS
:
1188 dirty_pointers
= true;
1191 /* Start the background scanning after either the disk spindown
1192 timeout or 5s, whichever is less */
1194 playlist
= ¤t_playlist
;
1196 if (playlist
->control_fd
>= 0
1198 && ata_disk_is_active()
1202 if (playlist
->num_cached
> 0)
1204 mutex_lock(&playlist
->control_mutex
);
1205 flush_cached_control(playlist
);
1206 mutex_unlock(&playlist
->control_mutex
);
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
)
1315 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1317 if ((max
> 0) && !playlist
->utf8
)
1323 while ((tmp_buf
[i
] != '\n') && (tmp_buf
[i
] != '\r')
1329 /* Now work back killing white space. */
1330 while ((i
> 0) && isspace(tmp_buf
[i
- 1]))
1335 /* Borrow dir_buf a little... */
1336 /* TODO: iso_decode can overflow dir_buf; it really
1337 * should take a dest size argument.
1339 end
= iso_decode(tmp_buf
, dir_buf
, -1, i
);
1341 strncpy(tmp_buf
, dir_buf
, sizeof(tmp_buf
));
1342 tmp_buf
[sizeof(tmp_buf
) - 1] = 0;
1343 max
= strlen(tmp_buf
);
1348 mutex_unlock(&playlist
->control_mutex
);
1353 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1355 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_ACCESS_ERROR
));
1361 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1362 dir_buf
[playlist
->dirlen
-1] = 0;
1364 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1367 static int get_next_directory(char *dir
){
1368 return get_next_dir(dir
,true,false);
1371 static int get_previous_directory(char *dir
){
1372 return get_next_dir(dir
,false,false);
1376 * search through all the directories (starting with the current) to find
1377 * one that has tracks to play
1379 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1381 struct playlist_info
* playlist
= ¤t_playlist
;
1383 int sort_dir
= global_settings
.sort_dir
;
1384 char *start_dir
= NULL
;
1386 struct tree_context
* tc
= tree_get_context();
1387 int dirfilter
= *(tc
->dirfilter
);
1389 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1391 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1392 char buffer
[MAX_PATH
];
1393 int folder_count
= 0,i
;
1394 srand(current_tick
);
1395 *(tc
->dirfilter
) = SHOW_MUSIC
;
1398 read(fd
,&folder_count
,sizeof(int));
1401 i
= rand()%folder_count
;
1402 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1403 read(fd
,buffer
,MAX_PATH
);
1404 if (check_subdir_for_music(buffer
,"") ==0)
1409 *(tc
->dirfilter
) = dirfilter
;
1414 /* not random folder advance */
1416 /* start with root */
1420 /* start with current directory */
1421 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1422 dir
[playlist
->dirlen
-1] = '\0';
1425 /* use the tree browser dircache to load files */
1426 *(tc
->dirfilter
) = SHOW_ALL
;
1428 /* sort in another direction if previous dir is requested */
1430 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1431 global_settings
.sort_dir
= 4;
1432 else if (global_settings
.sort_dir
== 1)
1433 global_settings
.sort_dir
= 2;
1434 else if (global_settings
.sort_dir
== 2)
1435 global_settings
.sort_dir
= 1;
1436 else if (global_settings
.sort_dir
== 4)
1437 global_settings
.sort_dir
= 0;
1442 struct entry
*files
;
1446 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1448 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1454 files
= (struct entry
*) tc
->dircache
;
1455 num_files
= tc
->filesindir
;
1457 for (i
=0; i
<num_files
; i
++)
1460 if (action_userabort(TIMEOUT_NOBLOCK
))
1467 if (files
[i
].attr
& ATTR_DIRECTORY
)
1471 result
= check_subdir_for_music(dir
, files
[i
].name
);
1478 else if (!strcmp(start_dir
, files
[i
].name
))
1485 /* move down to parent directory. current directory name is
1486 stored as the starting point for the search in parent */
1487 start_dir
= strrchr(dir
, '/');
1498 /* we've overwritten the dircache so tree browser will need to be
1502 /* restore dirfilter & sort_dir */
1503 *(tc
->dirfilter
) = dirfilter
;
1504 global_settings
.sort_dir
= sort_dir
;
1506 /* special case if nothing found: try start searching again from root */
1507 if (result
== -1 && !recursion
){
1508 result
= get_next_dir(dir
,is_forward
, true);
1515 * Checks if there are any music files in the dir or any of its
1516 * subdirectories. May be called recursively.
1518 static int check_subdir_for_music(char *dir
, char *subdir
)
1521 int dirlen
= strlen(dir
);
1524 struct entry
*files
;
1525 bool has_music
= false;
1526 bool has_subdir
= false;
1527 struct tree_context
* tc
= tree_get_context();
1529 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1531 if (ft_load(tc
, dir
) < 0)
1533 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1537 files
= (struct entry
*) tc
->dircache
;
1538 num_files
= tc
->filesindir
;
1540 for (i
=0; i
<num_files
; i
++)
1542 if (files
[i
].attr
& ATTR_DIRECTORY
)
1544 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1556 for (i
=0; i
<num_files
; i
++)
1558 if (action_userabort(TIMEOUT_NOBLOCK
))
1564 if (files
[i
].attr
& ATTR_DIRECTORY
)
1566 result
= check_subdir_for_music(dir
, files
[i
].name
);
1584 /* we now need to reload our current directory */
1585 if(ft_load(tc
, dir
) < 0)
1586 gui_syncsplash(HZ
*2,
1587 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1594 * Returns absolute path of track
1596 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1603 /* Zero-terminate the file name */
1604 while((src
[i
] != '\n') &&
1609 /* Now work back killing white space */
1610 while((src
[i
-1] == ' ') ||
1616 /* replace backslashes with forward slashes */
1617 for ( j
=0; j
<i
; j
++ )
1618 if ( src
[j
] == '\\' )
1623 strncpy(dest
, src
, buf_length
);
1627 /* handle dos style drive letter */
1629 strncpy(dest
, &src
[2], buf_length
);
1630 else if (!strncmp(src
, "../", 3))
1632 /* handle relative paths */
1634 while(!strncmp(&src
[i
], "../", 3))
1636 for (j
=0; j
<i
/3; j
++) {
1637 temp_ptr
= strrchr(dir
, '/');
1643 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1645 else if ( '.' == src
[0] && '/' == src
[1] ) {
1646 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1649 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1657 * Display splash message showing progress of playlist/directory insertion or
1660 static void display_playlist_count(int count
, const unsigned char *fmt
)
1662 lcd_clear_display();
1664 #ifdef HAVE_LCD_BITMAP
1665 if(global_settings
.statusbar
)
1666 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1668 lcd_setmargins(0, 0);
1671 gui_syncsplash(0, fmt
, count
,
1672 #if CONFIG_KEYPAD == PLAYER_PAD
1673 str(LANG_STOP_ABORT
)
1681 * Display buffer full message
1683 static void display_buffer_full(void)
1685 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_BUFFER_FULL
));
1689 * Flush any cached control commands to disk. Called when playlist is being
1690 * modified. Returns 0 on success and -1 on failure.
1692 static int flush_cached_control(struct playlist_info
* playlist
)
1697 if (!playlist
->num_cached
)
1700 lseek(playlist
->control_fd
, 0, SEEK_END
);
1702 for (i
=0; i
<playlist
->num_cached
; i
++)
1704 struct playlist_control_cache
* cache
=
1705 &(playlist
->control_cache
[i
]);
1707 switch (cache
->command
)
1709 case PLAYLIST_COMMAND_PLAYLIST
:
1710 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1711 cache
->i1
, cache
->s1
, cache
->s2
);
1713 case PLAYLIST_COMMAND_ADD
:
1714 case PLAYLIST_COMMAND_QUEUE
:
1715 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1716 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1717 cache
->i1
, cache
->i2
);
1720 /* save the position in file where name is written */
1721 int* seek_pos
= (int *)cache
->data
;
1722 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1723 result
= fdprintf(playlist
->control_fd
, "%s\n",
1727 case PLAYLIST_COMMAND_DELETE
:
1728 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1730 case PLAYLIST_COMMAND_SHUFFLE
:
1731 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1732 cache
->i1
, cache
->i2
);
1734 case PLAYLIST_COMMAND_UNSHUFFLE
:
1735 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1737 case PLAYLIST_COMMAND_RESET
:
1738 result
= fdprintf(playlist
->control_fd
, "R\n");
1750 if (global_status
.resume_seed
>= 0)
1752 global_status
.resume_seed
= -1;
1756 playlist
->num_cached
= 0;
1757 playlist
->pending_control_sync
= true;
1764 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1771 * Update control data with new command. Depending on the command, it may be
1772 * cached or flushed to disk.
1774 static int update_control(struct playlist_info
* playlist
,
1775 enum playlist_command command
, int i1
, int i2
,
1776 const char* s1
, const char* s2
, void* data
)
1779 struct playlist_control_cache
* cache
;
1782 mutex_lock(&playlist
->control_mutex
);
1784 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1786 cache
->command
= command
;
1795 case PLAYLIST_COMMAND_PLAYLIST
:
1796 case PLAYLIST_COMMAND_ADD
:
1797 case PLAYLIST_COMMAND_QUEUE
:
1798 #ifndef HAVE_DIRCACHE
1799 case PLAYLIST_COMMAND_DELETE
:
1800 case PLAYLIST_COMMAND_RESET
:
1804 case PLAYLIST_COMMAND_SHUFFLE
:
1805 case PLAYLIST_COMMAND_UNSHUFFLE
:
1807 /* only flush when needed */
1811 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1812 result
= flush_cached_control(playlist
);
1814 mutex_unlock(&playlist
->control_mutex
);
1820 * sync control file to disk
1822 static void sync_control(struct playlist_info
* playlist
, bool force
)
1824 #ifdef HAVE_DIRCACHE
1825 if (playlist
->started
&& force
)
1829 if (playlist
->started
)
1832 if (playlist
->pending_control_sync
)
1834 mutex_lock(&playlist
->control_mutex
);
1835 fsync(playlist
->control_fd
);
1836 playlist
->pending_control_sync
= false;
1837 mutex_unlock(&playlist
->control_mutex
);
1843 * Rotate indices such that first_index is index 0
1845 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1847 index
-= playlist
->first_index
;
1849 index
+= playlist
->amount
;
1855 * Initialize playlist entries at startup
1857 void playlist_init(void)
1859 struct playlist_info
* playlist
= ¤t_playlist
;
1861 playlist
->current
= true;
1862 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1863 "%s", PLAYLIST_CONTROL_FILE
);
1865 playlist
->control_fd
= -1;
1866 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1867 playlist
->indices
= buffer_alloc(
1868 playlist
->max_playlist_size
* sizeof(int));
1869 playlist
->buffer_size
=
1870 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1871 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1872 mutex_init(&playlist
->control_mutex
);
1873 empty_playlist(playlist
, true);
1875 #ifdef HAVE_DIRCACHE
1876 playlist
->filenames
= buffer_alloc(
1877 playlist
->max_playlist_size
* sizeof(int));
1878 memset(playlist
->filenames
, 0,
1879 playlist
->max_playlist_size
* sizeof(int));
1880 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1881 playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1882 IF_COP(, CPU
, false));
1883 queue_init(&playlist_queue
, true);
1888 * Clean playlist at shutdown
1890 void playlist_shutdown(void)
1892 struct playlist_info
* playlist
= ¤t_playlist
;
1894 if (playlist
->control_fd
>= 0)
1896 mutex_lock(&playlist
->control_mutex
);
1898 if (playlist
->num_cached
> 0)
1899 flush_cached_control(playlist
);
1901 close(playlist
->control_fd
);
1903 mutex_unlock(&playlist
->control_mutex
);
1908 * Create new playlist
1910 int playlist_create(const char *dir
, const char *file
)
1912 struct playlist_info
* playlist
= ¤t_playlist
;
1914 new_playlist(playlist
, dir
, file
);
1917 /* load the playlist file */
1918 add_indices_to_playlist(playlist
, NULL
, 0);
1923 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1926 * Restore the playlist state based on control file commands. Called to
1927 * resume playback after shutdown.
1929 int playlist_resume(void)
1931 struct playlist_info
* playlist
= ¤t_playlist
;
1936 int control_file_size
= 0;
1940 /* use mp3 buffer for maximum load speed */
1941 #if CONFIG_CODEC != SWCODEC
1942 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1943 buflen
= (audiobufend
- audiobuf
);
1944 buffer
= (char *)audiobuf
;
1946 buffer
= (char *)audio_get_buffer(false, &buflen
);
1949 empty_playlist(playlist
, true);
1951 gui_syncsplash(0, str(LANG_WAIT
));
1952 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1953 if (playlist
->control_fd
< 0)
1955 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1958 playlist
->control_created
= true;
1960 control_file_size
= filesize(playlist
->control_fd
);
1961 if (control_file_size
<= 0)
1963 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1967 /* read a small amount first to get the header */
1968 nread
= read(playlist
->control_fd
, buffer
,
1969 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1972 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1976 playlist
->started
= true;
1982 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1983 int last_newline
= 0;
1985 bool newline
= true;
1986 bool exit_loop
= false;
1991 unsigned long last_tick
= current_tick
;
1993 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1995 /* So a splash while we are loading. */
1996 if (current_tick
- last_tick
> HZ
/4)
1998 gui_syncsplash(0, str(LANG_LOADING_PERCENT
),
1999 (total_read
+count
)*100/control_file_size
,
2000 #if CONFIG_KEYPAD == PLAYER_PAD
2001 str(LANG_STOP_ABORT
)
2006 if (action_userabort(TIMEOUT_NOBLOCK
))
2009 * Not sure how to implement this, somebody more familiar
2010 * with the code, please fix this. */
2012 last_tick
= current_tick
;
2015 /* Are we on a new line? */
2016 if((*p
== '\n') || (*p
== '\r'))
2020 /* save last_newline in case we need to load more data */
2021 last_newline
= count
;
2023 switch (current_command
)
2025 case PLAYLIST_COMMAND_PLAYLIST
:
2027 /* str1=version str2=dir str3=file */
2043 version
= atoi(str1
);
2045 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2048 update_playlist_filename(playlist
, str2
, str3
);
2050 if (str3
[0] != '\0')
2052 /* NOTE: add_indices_to_playlist() overwrites the
2053 audiobuf so we need to reload control file
2055 add_indices_to_playlist(playlist
, NULL
, 0);
2057 else if (str2
[0] != '\0')
2059 playlist
->in_ram
= true;
2060 resume_directory(str2
);
2063 /* load the rest of the data */
2069 case PLAYLIST_COMMAND_ADD
:
2070 case PLAYLIST_COMMAND_QUEUE
:
2072 /* str1=position str2=last_position str3=file */
2073 int position
, last_position
;
2076 if (!str1
|| !str2
|| !str3
)
2083 position
= atoi(str1
);
2084 last_position
= atoi(str2
);
2086 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2089 /* seek position is based on str3's position in
2091 if (add_track_to_playlist(playlist
, str3
, position
,
2092 queue
, total_read
+(str3
-buffer
)) < 0)
2095 playlist
->last_insert_pos
= last_position
;
2099 case PLAYLIST_COMMAND_DELETE
:
2111 position
= atoi(str1
);
2113 if (remove_track_from_playlist(playlist
, position
,
2119 case PLAYLIST_COMMAND_SHUFFLE
:
2121 /* str1=seed str2=first_index */
2133 /* Always sort list before shuffling */
2134 sort_playlist(playlist
, false, false);
2138 playlist
->first_index
= atoi(str2
);
2140 if (randomise_playlist(playlist
, seed
, false,
2147 case PLAYLIST_COMMAND_UNSHUFFLE
:
2149 /* str1=first_index */
2157 playlist
->first_index
= atoi(str1
);
2159 if (sort_playlist(playlist
, false, false) < 0)
2165 case PLAYLIST_COMMAND_RESET
:
2167 playlist
->last_insert_pos
= -1;
2170 case PLAYLIST_COMMAND_COMMENT
:
2177 /* to ignore any extra newlines */
2178 current_command
= PLAYLIST_COMMAND_COMMENT
;
2184 /* first non-comment line must always specify playlist */
2185 if (first
&& *p
!= 'P' && *p
!= '#')
2195 /* playlist can only be specified once */
2203 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2206 current_command
= PLAYLIST_COMMAND_ADD
;
2209 current_command
= PLAYLIST_COMMAND_QUEUE
;
2212 current_command
= PLAYLIST_COMMAND_DELETE
;
2215 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2218 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2221 current_command
= PLAYLIST_COMMAND_RESET
;
2224 current_command
= PLAYLIST_COMMAND_COMMENT
;
2237 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2239 /* all control file strings are separated with a colon.
2240 Replace the colon with 0 to get proper strings that can be
2241 used by commands above */
2247 if ((count
+1) < nread
)
2261 /* allow last string to contain colons */
2272 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_INVALID
));
2276 if (!newline
|| (exit_loop
&& count
<nread
))
2278 if ((total_read
+ count
) >= control_file_size
)
2280 /* no newline at end of control file */
2281 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_INVALID
));
2285 /* We didn't end on a newline or we exited loop prematurely.
2286 Either way, re-read the remainder. */
2287 count
= last_newline
;
2288 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2291 total_read
+= count
;
2294 /* still looking for header */
2295 nread
= read(playlist
->control_fd
, buffer
,
2296 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2298 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2300 /* Terminate on EOF */
2303 if (global_status
.resume_seed
>= 0)
2305 /* Apply shuffle command saved in settings */
2306 if (global_status
.resume_seed
== 0)
2307 sort_playlist(playlist
, false, true);
2311 sort_playlist(playlist
, false, false);
2313 randomise_playlist(playlist
, global_status
.resume_seed
,
2318 playlist
->first_index
= global_status
.resume_first_index
;
2323 #ifdef HAVE_DIRCACHE
2324 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2331 * Add track to in_ram playlist. Used when playing directories.
2333 int playlist_add(const char *filename
)
2335 struct playlist_info
* playlist
= ¤t_playlist
;
2336 int len
= strlen(filename
);
2338 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2339 (playlist
->amount
>= playlist
->max_playlist_size
))
2341 display_buffer_full();
2345 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2346 #ifdef HAVE_DIRCACHE
2347 playlist
->filenames
[playlist
->amount
] = NULL
;
2351 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2352 playlist
->buffer_end_pos
+= len
;
2353 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2358 /* shuffle newly created playlist using random seed. */
2359 int playlist_shuffle(int random_seed
, int start_index
)
2361 struct playlist_info
* playlist
= ¤t_playlist
;
2363 unsigned int seek_pos
= 0;
2364 bool start_current
= false;
2366 if (start_index
>= 0 && global_settings
.play_selected
)
2368 /* store the seek position before the shuffle */
2369 seek_pos
= playlist
->indices
[start_index
];
2370 playlist
->index
= global_status
.resume_first_index
=
2371 playlist
->first_index
= start_index
;
2372 start_current
= true;
2375 gui_syncsplash(0, str(LANG_PLAYLIST_SHUFFLE
));
2377 randomise_playlist(playlist
, random_seed
, start_current
, true);
2379 return playlist
->index
;
2382 /* start playing current playlist at specified index/offset */
2383 int playlist_start(int start_index
, int offset
)
2385 struct playlist_info
* playlist
= ¤t_playlist
;
2387 playlist
->index
= start_index
;
2389 #if CONFIG_CODEC != SWCODEC
2390 talk_buffer_steal(); /* will use the mp3 buffer */
2393 playlist
->started
= true;
2394 sync_control(playlist
, false);
2400 /* Returns false if 'steps' is out of bounds, else true */
2401 bool playlist_check(int steps
)
2403 struct playlist_info
* playlist
= ¤t_playlist
;
2405 /* always allow folder navigation */
2406 if (global_settings
.next_folder
&& playlist
->in_ram
)
2409 int index
= get_next_index(playlist
, steps
, -1);
2411 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2412 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2414 return (index
>= 0);
2417 /* get trackname of track that is "steps" away from current playing track.
2418 NULL is used to identify end of playlist */
2419 char* playlist_peek(int steps
)
2421 struct playlist_info
* playlist
= ¤t_playlist
;
2428 index
= get_next_index(playlist
, steps
, -1);
2432 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2433 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2435 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2439 temp_ptr
= now_playing
;
2441 if (!playlist
->in_ram
|| control_file
)
2443 /* remove bogus dirs from beginning of path
2444 (workaround for buggy playlist creation tools) */
2447 #ifdef HAVE_DIRCACHE
2448 if (dircache_is_enabled())
2450 if (dircache_get_entry_ptr(temp_ptr
))
2456 fd
= open(temp_ptr
, O_RDONLY
);
2464 temp_ptr
= strchr(temp_ptr
+1, '/');
2469 /* Even though this is an invalid file, we still need to pass a
2470 file name to the caller because NULL is used to indicate end
2480 * Update indices as track has changed
2482 int playlist_next(int steps
)
2484 struct playlist_info
* playlist
= ¤t_playlist
;
2488 #ifdef AB_REPEAT_ENABLE
2489 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2491 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2495 /* We need to delete all the queued songs */
2496 for (i
=0, j
=steps
; i
<j
; i
++)
2498 index
= get_next_index(playlist
, i
, -1);
2500 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2502 remove_track_from_playlist(playlist
, index
, true);
2503 steps
--; /* one less track */
2508 index
= get_next_index(playlist
, steps
, -1);
2512 /* end of playlist... or is it */
2513 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2514 playlist
->amount
> 1)
2516 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2517 playlist
->first_index
= global_status
.resume_first_index
= 0;
2518 sort_playlist(playlist
, false, false);
2519 randomise_playlist(playlist
, current_tick
, false, true);
2520 #if CONFIG_CODEC != SWCODEC
2521 playlist_start(0, 0);
2523 playlist
->index
= 0;
2526 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2528 char dir
[MAX_PATH
+1];
2530 changing_dir
= true;
2533 if (!get_next_directory(dir
))
2535 /* start playing next directory */
2536 if (playlist_create(dir
, NULL
) != -1)
2538 ft_build_playlist(tree_get_context(), 0);
2539 if (global_settings
.playlist_shuffle
)
2540 playlist_shuffle(current_tick
, -1);
2541 #if CONFIG_CODEC != SWCODEC
2542 playlist_start(0, 0);
2544 playlist
->index
= index
= 0;
2550 if (!get_previous_directory(dir
))
2552 /* start playing previous directory */
2553 if (playlist_create(dir
, NULL
) != -1)
2555 ft_build_playlist(tree_get_context(), 0);
2556 if (global_settings
.playlist_shuffle
)
2557 playlist_shuffle(current_tick
, -1);
2558 #if CONFIG_CODEC != SWCODEC
2559 playlist_start(current_playlist
.amount
-1, 0);
2561 playlist
->index
= index
= current_playlist
.amount
- 1;
2565 changing_dir
= false;
2571 playlist
->index
= index
;
2573 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2575 /* check to see if we've gone beyond the last inserted track */
2576 int cur
= rotate_index(playlist
, index
);
2577 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2581 /* reset last inserted track */
2582 playlist
->last_insert_pos
= -1;
2584 if (playlist
->control_fd
>= 0)
2586 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2587 -1, -1, NULL
, NULL
, NULL
);
2592 sync_control(playlist
, false);
2600 /* try playing next or previous folder */
2601 bool playlist_next_dir(int direction
)
2603 char dir
[MAX_PATH
+1];
2607 /* not to mess up real playlists */
2608 if(!current_playlist
.in_ram
)
2614 changing_dir
= true;
2616 res
= get_next_directory(dir
);
2618 res
= get_previous_directory(dir
);
2621 if (playlist_create(dir
, NULL
) != -1)
2623 ft_build_playlist(tree_get_context(), 0);
2624 if (global_settings
.playlist_shuffle
)
2625 playlist_shuffle(current_tick
, -1);
2626 #if (CONFIG_CODEC != SWCODEC)
2627 playlist_start(0,0);
2637 changing_dir
= false;
2642 /* Get resume info for current playing song. If return value is -1 then
2643 settings shouldn't be saved. */
2644 int playlist_get_resume_info(int *resume_index
)
2646 struct playlist_info
* playlist
= ¤t_playlist
;
2648 *resume_index
= playlist
->index
;
2653 /* Update resume info for current playing song. Returns -1 on error. */
2654 int playlist_update_resume_info(const struct mp3entry
* id3
)
2656 struct playlist_info
* playlist
= ¤t_playlist
;
2660 if (global_status
.resume_index
!= playlist
->index
||
2661 global_status
.resume_offset
!= id3
->offset
)
2663 global_status
.resume_index
= playlist
->index
;
2664 global_status
.resume_offset
= id3
->offset
;
2670 global_status
.resume_index
= -1;
2671 global_status
.resume_offset
= -1;
2678 /* Returns index of current playing track for display purposes. This value
2679 should not be used for resume purposes as it doesn't represent the actual
2680 index into the playlist */
2681 int playlist_get_display_index(void)
2683 struct playlist_info
* playlist
= ¤t_playlist
;
2685 /* first_index should always be index 0 for display purposes */
2686 int index
= rotate_index(playlist
, playlist
->index
);
2691 /* returns number of tracks in current playlist */
2692 int playlist_amount(void)
2694 return playlist_amount_ex(NULL
);
2698 * Create a new playlist If playlist is not NULL then we're loading a
2699 * playlist off disk for viewing/editing. The index_buffer is used to store
2700 * playlist indices (required for and only used if !current playlist). The
2701 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2703 int playlist_create_ex(struct playlist_info
* playlist
,
2704 const char* dir
, const char* file
,
2705 void* index_buffer
, int index_buffer_size
,
2706 void* temp_buffer
, int temp_buffer_size
)
2709 playlist
= ¤t_playlist
;
2712 /* Initialize playlist structure */
2713 int r
= rand() % 10;
2714 playlist
->current
= false;
2716 /* Use random name for control file */
2717 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2718 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2720 playlist
->control_fd
= -1;
2724 int num_indices
= index_buffer_size
/ sizeof(int);
2726 #ifdef HAVE_DIRCACHE
2729 if (num_indices
> global_settings
.max_files_in_playlist
)
2730 num_indices
= global_settings
.max_files_in_playlist
;
2732 playlist
->max_playlist_size
= num_indices
;
2733 playlist
->indices
= index_buffer
;
2734 #ifdef HAVE_DIRCACHE
2735 playlist
->filenames
= (const struct dircache_entry
**)
2736 &playlist
->indices
[num_indices
];
2741 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2742 playlist
->indices
= current_playlist
.indices
;
2743 #ifdef HAVE_DIRCACHE
2744 playlist
->filenames
= current_playlist
.filenames
;
2748 playlist
->buffer_size
= 0;
2749 playlist
->buffer
= NULL
;
2750 mutex_init(&playlist
->control_mutex
);
2753 new_playlist(playlist
, dir
, file
);
2756 /* load the playlist file */
2757 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2763 * Set the specified playlist as the current.
2764 * NOTE: You will get undefined behaviour if something is already playing so
2765 * remember to stop before calling this. Also, this call will
2766 * effectively close your playlist, making it unusable.
2768 int playlist_set_current(struct playlist_info
* playlist
)
2770 if (!playlist
|| (check_control(playlist
) < 0))
2773 empty_playlist(¤t_playlist
, false);
2775 strncpy(current_playlist
.filename
, playlist
->filename
,
2776 sizeof(current_playlist
.filename
));
2778 current_playlist
.utf8
= playlist
->utf8
;
2779 current_playlist
.fd
= playlist
->fd
;
2781 close(playlist
->control_fd
);
2782 close(current_playlist
.control_fd
);
2783 remove(current_playlist
.control_filename
);
2784 if (rename(playlist
->control_filename
,
2785 current_playlist
.control_filename
) < 0)
2787 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2789 if (current_playlist
.control_fd
< 0)
2791 current_playlist
.control_created
= true;
2793 current_playlist
.dirlen
= playlist
->dirlen
;
2795 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2797 memcpy(current_playlist
.indices
, playlist
->indices
,
2798 playlist
->max_playlist_size
*sizeof(int));
2799 #ifdef HAVE_DIRCACHE
2800 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2801 playlist
->max_playlist_size
*sizeof(int));
2805 current_playlist
.first_index
= playlist
->first_index
;
2806 current_playlist
.amount
= playlist
->amount
;
2807 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2808 current_playlist
.seed
= playlist
->seed
;
2809 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2810 current_playlist
.deleted
= playlist
->deleted
;
2811 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2813 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2814 sizeof(current_playlist
.control_cache
));
2815 current_playlist
.num_cached
= playlist
->num_cached
;
2816 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2822 * Close files and delete control file for non-current playlist.
2824 void playlist_close(struct playlist_info
* playlist
)
2829 if (playlist
->fd
>= 0)
2830 close(playlist
->fd
);
2832 if (playlist
->control_fd
>= 0)
2833 close(playlist
->control_fd
);
2835 if (playlist
->control_created
)
2836 remove(playlist
->control_filename
);
2839 void playlist_sync(struct playlist_info
* playlist
)
2842 playlist
= ¤t_playlist
;
2844 sync_control(playlist
, false);
2845 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2846 audio_flush_and_reload_tracks();
2848 #ifdef HAVE_DIRCACHE
2849 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2854 * Insert track into playlist at specified position (or one of the special
2855 * positions). Returns position where track was inserted or -1 if error.
2857 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2858 int position
, bool queue
, bool sync
)
2863 playlist
= ¤t_playlist
;
2865 if (check_control(playlist
) < 0)
2867 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2871 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2873 /* Check if we want manually sync later. For example when adding
2874 * bunch of files from tagcache, syncing after every file wouldn't be
2875 * a good thing to do. */
2876 if (sync
&& result
>= 0)
2877 playlist_sync(playlist
);
2883 * Insert all tracks from specified directory into playlist.
2885 int playlist_insert_directory(struct playlist_info
* playlist
,
2886 const char *dirname
, int position
, bool queue
,
2890 unsigned char *count_str
;
2891 struct directory_search_context context
;
2894 playlist
= ¤t_playlist
;
2896 if (check_control(playlist
) < 0)
2898 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2902 if (position
== PLAYLIST_REPLACE
)
2904 if (remove_all_tracks(playlist
) == 0)
2905 position
= PLAYLIST_INSERT_LAST
;
2911 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2913 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2915 display_playlist_count(0, count_str
);
2917 context
.playlist
= playlist
;
2918 context
.position
= position
;
2919 context
.queue
= queue
;
2924 result
= playlist_directory_tracksearch(dirname
, recurse
,
2925 directory_search_callback
, &context
);
2927 sync_control(playlist
, false);
2931 display_playlist_count(context
.count
, count_str
);
2933 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2934 audio_flush_and_reload_tracks();
2936 #ifdef HAVE_DIRCACHE
2937 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2944 * Insert all tracks from specified playlist into dynamic playlist.
2946 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2947 int position
, bool queue
)
2953 unsigned char *count_str
;
2954 char temp_buf
[MAX_PATH
+1];
2955 char trackname
[MAX_PATH
+1];
2960 playlist
= ¤t_playlist
;
2962 if (check_control(playlist
) < 0)
2964 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2968 fd
= open(filename
, O_RDONLY
);
2971 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_ACCESS_ERROR
));
2975 /* we need the directory name for formatting purposes */
2978 temp_ptr
= strrchr(filename
+1,'/');
2985 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2987 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2989 display_playlist_count(count
, count_str
);
2991 if (position
== PLAYLIST_REPLACE
)
2993 if (remove_all_tracks(playlist
) == 0)
2994 position
= PLAYLIST_INSERT_LAST
;
3000 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3003 if (action_userabort(TIMEOUT_NOBLOCK
))
3006 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3010 /* we need to format so that relative paths are correctly
3012 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3019 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3028 /* Make sure tracks are inserted in correct order if user
3029 requests INSERT_FIRST */
3030 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3031 position
= insert_pos
+ 1;
3035 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3037 display_playlist_count(count
, count_str
);
3039 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3040 (audio_status() & AUDIO_STATUS_PLAY
) &&
3042 audio_flush_and_reload_tracks();
3046 /* let the other threads work */
3055 sync_control(playlist
, false);
3059 display_playlist_count(count
, count_str
);
3061 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3062 audio_flush_and_reload_tracks();
3064 #ifdef HAVE_DIRCACHE
3065 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3072 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3073 * we want to delete the current playing track.
3075 int playlist_delete(struct playlist_info
* playlist
, int index
)
3080 playlist
= ¤t_playlist
;
3082 if (check_control(playlist
) < 0)
3084 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3088 if (index
== PLAYLIST_DELETE_CURRENT
)
3089 index
= playlist
->index
;
3091 result
= remove_track_from_playlist(playlist
, index
, true);
3093 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3095 audio_flush_and_reload_tracks();
3101 * Move track at index to new_index. Tracks between the two are shifted
3102 * appropriately. Returns 0 on success and -1 on failure.
3104 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3110 bool current
= false;
3112 char filename
[MAX_PATH
];
3115 playlist
= ¤t_playlist
;
3117 if (check_control(playlist
) < 0)
3119 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3123 if (index
== new_index
)
3126 if (index
== playlist
->index
)
3127 /* Moving the current track */
3130 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3131 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3132 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3134 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3135 sizeof(filename
)) < 0)
3138 /* Delete track from original position */
3139 result
= remove_track_from_playlist(playlist
, index
, true);
3143 /* We want to insert the track at the position that was specified by
3144 new_index. This may be different then new_index because of the
3145 shifting that occurred after the delete */
3146 r
= rotate_index(playlist
, new_index
);
3150 new_index
= PLAYLIST_PREPEND
;
3151 else if (r
== playlist
->amount
)
3153 new_index
= PLAYLIST_INSERT_LAST
;
3155 /* Calculate index of desired position */
3156 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3158 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3165 /* Moved the current track */
3168 case PLAYLIST_PREPEND
:
3169 playlist
->index
= playlist
->first_index
;
3171 case PLAYLIST_INSERT_LAST
:
3172 playlist
->index
= playlist
->first_index
- 1;
3173 if (playlist
->index
< 0)
3174 playlist
->index
+= playlist
->amount
;
3177 playlist
->index
= new_index
;
3182 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3183 audio_flush_and_reload_tracks();
3187 #ifdef HAVE_DIRCACHE
3188 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3194 /* shuffle currently playing playlist */
3195 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3201 playlist
= ¤t_playlist
;
3203 check_control(playlist
);
3205 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3207 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3209 audio_flush_and_reload_tracks();
3214 /* sort currently playing playlist */
3215 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3220 playlist
= ¤t_playlist
;
3222 check_control(playlist
);
3224 result
= sort_playlist(playlist
, start_current
, true);
3226 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3228 audio_flush_and_reload_tracks();
3233 /* returns true if playlist has been modified */
3234 bool playlist_modified(const struct playlist_info
* playlist
)
3237 playlist
= ¤t_playlist
;
3239 if (playlist
->shuffle_modified
||
3240 playlist
->deleted
||
3241 playlist
->num_inserted_tracks
> 0)
3247 /* returns index of first track in playlist */
3248 int playlist_get_first_index(const struct playlist_info
* playlist
)
3251 playlist
= ¤t_playlist
;
3253 return playlist
->first_index
;
3256 /* returns shuffle seed of playlist */
3257 int playlist_get_seed(const struct playlist_info
* playlist
)
3260 playlist
= ¤t_playlist
;
3262 return playlist
->seed
;
3265 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3266 int playlist_amount_ex(const struct playlist_info
* playlist
)
3269 playlist
= ¤t_playlist
;
3271 return playlist
->amount
;
3274 /* returns full path of playlist (minus extension) */
3275 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3281 playlist
= ¤t_playlist
;
3283 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3288 /* Remove extension */
3289 sep
= strrchr(buf
, '.');
3296 /* returns the playlist filename */
3297 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3301 playlist
= ¤t_playlist
;
3303 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3311 /* Fills info structure with information about track at specified index.
3312 Returns 0 on success and -1 on failure */
3313 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3314 struct playlist_track_info
* info
)
3320 playlist
= ¤t_playlist
;
3322 if (index
< 0 || index
>= playlist
->amount
)
3325 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3326 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3328 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3329 sizeof(info
->filename
)) < 0)
3336 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3337 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3339 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3343 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3344 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3346 info
->index
= index
;
3347 info
->display_index
= rotate_index(playlist
, index
) + 1;
3352 /* save the current dynamic playlist to specified file */
3353 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3358 char path
[MAX_PATH
+1];
3359 char tmp_buf
[MAX_PATH
+1];
3361 bool overwrite_current
= false;
3362 int* index_buf
= NULL
;
3365 playlist
= ¤t_playlist
;
3367 if (playlist
->amount
<= 0)
3370 /* use current working directory as base for pathname */
3371 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3372 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3375 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3377 /* Attempting to overwrite current playlist file.*/
3379 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3381 /* not enough buffer space to store updated indices */
3382 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_ACCESS_ERROR
));
3386 /* in_ram buffer is unused for m3u files so we'll use for storing
3388 index_buf
= (int*)playlist
->buffer
;
3390 /* use temporary pathname */
3391 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3392 overwrite_current
= true;
3395 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3398 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_ACCESS_ERROR
));
3402 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3406 index
= playlist
->first_index
;
3407 for (i
=0; i
<playlist
->amount
; i
++)
3414 if (action_userabort(TIMEOUT_NOBLOCK
))
3420 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3421 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3422 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3424 /* Don't save queued files */
3427 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3434 if (overwrite_current
)
3435 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3437 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3439 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_ACCESS_ERROR
));
3446 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3447 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3452 index
= (index
+1)%playlist
->amount
;
3455 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3459 if (overwrite_current
&& result
>= 0)
3463 mutex_lock(&playlist
->control_mutex
);
3465 /* Replace the current playlist with the new one and update indices */
3466 close(playlist
->fd
);
3467 if (remove(playlist
->filename
) >= 0)
3469 if (rename(path
, playlist
->filename
) >= 0)
3471 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3472 if (playlist
->fd
>= 0)
3474 index
= playlist
->first_index
;
3475 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3477 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3479 playlist
->indices
[index
] = index_buf
[count
];
3482 index
= (index
+1)%playlist
->amount
;
3485 /* we need to recreate control because inserted tracks are
3486 now part of the playlist and shuffle has been
3488 result
= recreate_control(playlist
);
3493 mutex_unlock(&playlist
->control_mutex
);
3503 * Search specified directory for tracks and notify via callback. May be
3504 * called recursively.
3506 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3507 int (*callback
)(char*, void*),
3510 char buf
[MAX_PATH
+1];
3514 struct entry
*files
;
3515 struct tree_context
* tc
= tree_get_context();
3516 int old_dirfilter
= *(tc
->dirfilter
);
3521 /* use the tree browser dircache to load files */
3522 *(tc
->dirfilter
) = SHOW_ALL
;
3524 if (ft_load(tc
, dirname
) < 0)
3526 gui_syncsplash(HZ
*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3527 *(tc
->dirfilter
) = old_dirfilter
;
3531 files
= (struct entry
*) tc
->dircache
;
3532 num_files
= tc
->filesindir
;
3534 /* we've overwritten the dircache so tree browser will need to be
3538 for (i
=0; i
<num_files
; i
++)
3541 if (action_userabort(TIMEOUT_NOBLOCK
))
3547 if (files
[i
].attr
& ATTR_DIRECTORY
)
3551 /* recursively add directories */
3552 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3553 result
= playlist_directory_tracksearch(buf
, recurse
,
3558 /* we now need to reload our current directory */
3559 if(ft_load(tc
, dirname
) < 0)
3565 files
= (struct entry
*) tc
->dircache
;
3566 num_files
= tc
->filesindir
;
3576 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3578 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3580 if (callback(buf
, context
) != 0)
3586 /* let the other threads work */
3591 /* restore dirfilter */
3592 *(tc
->dirfilter
) = old_dirfilter
;