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.
73 #include "ata_idle_notify.h"
84 #include "applimits.h"
93 #include "filetypes.h"
94 #ifdef HAVE_LCD_BITMAP
101 #include "rbunicode.h"
102 #include "root_menu.h"
104 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
105 #define PLAYLIST_CONTROL_FILE_VERSION 2
108 Each playlist index has a flag associated with it which identifies what
109 type of track it is. These flags are stored in the 4 high order bits of
112 NOTE: This limits the playlist file size to a max of 256M.
116 01 = Track was prepended into playlist
117 10 = Track was inserted into playlist
118 11 = Track was appended into playlist
123 0 = Track entry is valid
124 1 = Track does not exist on disk and should be skipped
126 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
127 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
128 #define PLAYLIST_QUEUE_MASK 0x20000000
130 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
131 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
132 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
134 #define PLAYLIST_QUEUED 0x20000000
135 #define PLAYLIST_SKIPPED 0x10000000
137 #define PLAYLIST_DISPLAY_COUNT 10
139 struct directory_search_context
{
140 struct playlist_info
* playlist
;
146 static struct playlist_info current_playlist
;
147 static char now_playing
[MAX_PATH
+1];
149 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
150 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
152 static void create_control(struct playlist_info
* playlist
);
153 static int check_control(struct playlist_info
* playlist
);
154 static int recreate_control(struct playlist_info
* playlist
);
155 static void update_playlist_filename(struct playlist_info
* playlist
,
156 const char *dir
, const char *file
);
157 static int add_indices_to_playlist(struct playlist_info
* playlist
,
158 char* buffer
, size_t buflen
);
159 static int add_track_to_playlist(struct playlist_info
* playlist
,
160 const char *filename
, int position
,
161 bool queue
, int seek_pos
);
162 static int directory_search_callback(char* filename
, void* context
);
163 static int remove_track_from_playlist(struct playlist_info
* playlist
,
164 int position
, bool write
);
165 static int randomise_playlist(struct playlist_info
* playlist
,
166 unsigned int seed
, bool start_current
,
168 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
170 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
172 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
174 static int compare(const void* p1
, const void* p2
);
175 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
176 bool control_file
, char *buf
, int buf_length
);
177 static int get_next_directory(char *dir
);
178 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
179 static int get_previous_directory(char *dir
);
180 static int check_subdir_for_music(char *dir
, char *subdir
);
181 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
183 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";
201 #define BOM "\xef\xbb\xbf"
204 /* Check if the filename suggests M3U or M3U8 format. */
205 static bool is_m3u8(const char* filename
)
207 int len
= strlen(filename
);
209 /* Default to M3U8 unless explicitly told otherwise. */
210 return !(len
> 4 && strcasecmp(&filename
[len
- 4], ".m3u") == 0);
213 /* Check if a strings starts with an UTF-8 byte-order mark. */
214 static bool is_utf8_bom(const char* str
, int len
)
216 return len
>= BOM_SIZE
&& memcmp(str
, BOM
, BOM_SIZE
) == 0;
219 /* Convert a filename in an M3U playlist to UTF-8.
221 * buf - the filename to convert; can contain more than one line from the
223 * buf_len - amount of buf that is used.
224 * buf_max - total size of buf.
225 * temp - temporary conversion buffer, at least buf_max bytes.
227 * Returns the length of the converted filename.
229 static int convert_m3u(char* buf
, int buf_len
, int buf_max
, char* temp
)
235 while ((buf
[i
] != '\n') && (buf
[i
] != '\r') && (i
< buf_len
))
240 /* Work back killing white space. */
241 while ((i
> 0) && isspace(buf
[i
- 1]))
249 /* Convert char by char, so as to not overflow temp (iso_decode should
250 * preferably handle this). No more than 4 bytes should be generated for
253 for (i
= 0; i
< buf_len
&& dest
< (temp
+ buf_max
- 4); i
++)
255 dest
= iso_decode(&buf
[i
], dest
, -1, 1);
264 * remove any files and indices associated with the playlist
266 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
268 playlist
->filename
[0] = '\0';
269 playlist
->utf8
= true;
271 if(playlist
->fd
>= 0)
272 /* If there is an already open playlist, close it. */
276 if(playlist
->control_fd
>= 0)
277 close(playlist
->control_fd
);
278 playlist
->control_fd
= -1;
279 playlist
->control_created
= false;
281 playlist
->in_ram
= false;
283 if (playlist
->buffer
)
284 playlist
->buffer
[0] = 0;
286 playlist
->buffer_end_pos
= 0;
289 playlist
->first_index
= 0;
290 playlist
->amount
= 0;
291 playlist
->last_insert_pos
= -1;
293 playlist
->shuffle_modified
= false;
294 playlist
->deleted
= false;
295 playlist
->num_inserted_tracks
= 0;
296 playlist
->started
= false;
298 playlist
->num_cached
= 0;
299 playlist
->pending_control_sync
= false;
301 if (!resume
&& playlist
->current
)
303 /* start with fresh playlist control file when starting new
305 create_control(playlist
);
307 /* Reset resume settings */
308 global_status
.resume_first_index
= 0;
309 global_status
.resume_seed
= -1;
314 * Initialize a new playlist for viewing/editing/playing. dir is the
315 * directory where the playlist is located and file is the filename.
317 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
320 const char *fileused
= file
;
321 const char *dirused
= dir
;
322 empty_playlist(playlist
, false);
328 if (dirused
&& playlist
->current
) /* !current cannot be in_ram */
329 playlist
->in_ram
= true;
331 dirused
= ""; /* empty playlist */
334 update_playlist_filename(playlist
, dirused
, fileused
);
336 if (playlist
->control_fd
>= 0)
338 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
339 PLAYLIST_CONTROL_FILE_VERSION
, -1, dirused
, fileused
, NULL
);
340 sync_control(playlist
, false);
345 * create control file for playlist
347 static void create_control(struct playlist_info
* playlist
)
349 playlist
->control_fd
= open(playlist
->control_filename
,
350 O_CREAT
|O_RDWR
|O_TRUNC
);
351 if (playlist
->control_fd
< 0)
353 if (check_rockboxdir())
355 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
356 gui_syncsplash(HZ
*2, (unsigned char *)"%s (%d)",
357 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
358 playlist
->control_fd
);
360 playlist
->control_created
= false;
364 playlist
->control_created
= true;
369 * validate the control file. This may include creating/initializing it if
372 static int check_control(struct playlist_info
* playlist
)
374 if (!playlist
->control_created
)
376 create_control(playlist
);
378 if (playlist
->control_fd
>= 0)
380 char* dir
= playlist
->filename
;
381 char* file
= playlist
->filename
+playlist
->dirlen
;
382 char c
= playlist
->filename
[playlist
->dirlen
-1];
384 playlist
->filename
[playlist
->dirlen
-1] = '\0';
386 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
387 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
388 sync_control(playlist
, false);
389 playlist
->filename
[playlist
->dirlen
-1] = c
;
393 if (playlist
->control_fd
< 0)
400 * recreate the control file based on current playlist entries
402 static int recreate_control(struct playlist_info
* playlist
)
404 char temp_file
[MAX_PATH
+1];
409 if(playlist
->control_fd
>= 0)
411 char* dir
= playlist
->filename
;
412 char* file
= playlist
->filename
+playlist
->dirlen
;
413 char c
= playlist
->filename
[playlist
->dirlen
-1];
415 close(playlist
->control_fd
);
417 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
418 playlist
->control_filename
);
420 if (rename(playlist
->control_filename
, temp_file
) < 0)
423 temp_fd
= open(temp_file
, O_RDONLY
);
427 playlist
->control_fd
= open(playlist
->control_filename
,
428 O_CREAT
|O_RDWR
|O_TRUNC
);
429 if (playlist
->control_fd
< 0)
432 playlist
->filename
[playlist
->dirlen
-1] = '\0';
434 /* cannot call update_control() because of mutex */
435 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
436 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
438 playlist
->filename
[playlist
->dirlen
-1] = c
;
448 playlist
->shuffle_modified
= false;
449 playlist
->deleted
= false;
450 playlist
->num_inserted_tracks
= 0;
452 if (playlist
->current
)
454 global_status
.resume_seed
= -1;
458 for (i
=0; i
<playlist
->amount
; i
++)
460 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
462 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
463 char inserted_file
[MAX_PATH
+1];
465 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
467 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
469 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
470 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
473 /* save the position in file where name is written */
474 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
476 result
= fdprintf(playlist
->control_fd
, "%s\n",
479 playlist
->indices
[i
] =
480 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
486 playlist
->num_inserted_tracks
++;
492 fsync(playlist
->control_fd
);
501 * store directory and name of playlist file
503 static void update_playlist_filename(struct playlist_info
* playlist
,
504 const char *dir
, const char *file
)
507 int dirlen
= strlen(dir
);
509 playlist
->utf8
= is_m3u8(file
);
511 /* If the dir does not end in trailing slash, we use a separator.
512 Otherwise we don't. */
513 if('/' != dir
[dirlen
-1])
519 playlist
->dirlen
= dirlen
;
521 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
522 "%s%s%s", dir
, sep
, file
);
526 * calculate track offsets within a playlist file
528 static int add_indices_to_playlist(struct playlist_info
* playlist
,
529 char* buffer
, size_t buflen
)
533 unsigned int count
= 0;
538 if(-1 == playlist
->fd
)
539 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
541 return -1; /* failure */
543 gui_syncsplash(0, ID2P(LANG_WAIT
));
547 /* use mp3 buffer for maximum load speed */
549 #if CONFIG_CODEC != SWCODEC
550 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
551 buflen
= (audiobufend
- audiobuf
);
552 buffer
= (char *)audiobuf
;
554 buffer
= (char *)audio_get_buffer(false, &buflen
);
562 nread
= read(playlist
->fd
, buffer
, buflen
);
563 /* Terminate on EOF */
567 p
= (unsigned char *)buffer
;
569 /* utf8 BOM at beginning of file? */
570 if(i
== 0 && is_utf8_bom(p
, nread
)) {
574 playlist
->utf8
= true; /* Override any earlier indication. */
577 for(count
=0; count
< nread
; count
++,p
++) {
579 /* Are we on a new line? */
580 if((*p
== '\n') || (*p
== '\r'))
590 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
591 display_buffer_full();
596 /* Store a new entry */
597 playlist
->indices
[ playlist
->amount
] = i
+count
;
599 if (playlist
->filenames
)
600 playlist
->filenames
[ playlist
->amount
] = NULL
;
612 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
619 * Utility function to create a new playlist, fill it with the next or
620 * previous directory, shuffle it if needed, and start playback.
621 * If play_last is true and direction zero or negative, start playing
622 * the last file in the directory, otherwise start playing the first.
624 static int create_and_play_dir(int direction
, bool play_last
)
626 char dir
[MAX_PATH
+ 1];
631 res
= get_next_directory(dir
);
633 res
= get_previous_directory(dir
);
637 if (playlist_create(dir
, NULL
) != -1)
639 ft_build_playlist(tree_get_context(), 0);
641 if (global_settings
.playlist_shuffle
)
642 playlist_shuffle(current_tick
, -1);
644 if (play_last
&& direction
<= 0)
645 index
= current_playlist
.amount
- 1;
649 #if (CONFIG_CODEC != SWCODEC)
650 playlist_start(index
, 0);
654 /* we've overwritten the dircache when getting the next/previous dir,
655 so the tree browser context will need to be reloaded */
663 * Removes all tracks, from the playlist, leaving the presently playing
666 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
670 if (playlist
== NULL
)
671 playlist
= ¤t_playlist
;
673 while (playlist
->index
> 0)
674 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
677 while (playlist
->amount
> 1)
678 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
681 if (playlist
->amount
== 1) {
682 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
690 * Add track to playlist at specified position. There are five special
691 * positions that can be specified:
692 * PLAYLIST_PREPEND - Add track at beginning of playlist
693 * PLAYLIST_INSERT - Add track after current song. NOTE: If
694 * there are already inserted tracks then track
695 * is added to the end of the insertion list
696 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
697 * matter what other tracks have been inserted
698 * PLAYLIST_INSERT_LAST - Add track to end of playlist
699 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
700 * current playing track and end of playlist
701 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
702 * and inster this track at the end.
704 static int add_track_to_playlist(struct playlist_info
* playlist
,
705 const char *filename
, int position
,
706 bool queue
, int seek_pos
)
708 int insert_position
, orig_position
;
709 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
712 insert_position
= orig_position
= position
;
714 if (playlist
->amount
>= playlist
->max_playlist_size
)
716 display_buffer_full();
722 case PLAYLIST_PREPEND
:
723 position
= insert_position
= playlist
->first_index
;
725 case PLAYLIST_INSERT
:
726 /* if there are already inserted tracks then add track to end of
727 insertion list else add after current playing track */
728 if (playlist
->last_insert_pos
>= 0 &&
729 playlist
->last_insert_pos
< playlist
->amount
&&
730 (playlist
->indices
[playlist
->last_insert_pos
]&
731 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
732 position
= insert_position
= playlist
->last_insert_pos
+1;
733 else if (playlist
->amount
> 0)
734 position
= insert_position
= playlist
->index
+ 1;
736 position
= insert_position
= 0;
738 if (playlist
->started
)
739 playlist
->last_insert_pos
= position
;
741 case PLAYLIST_INSERT_FIRST
:
742 if (playlist
->amount
> 0)
743 position
= insert_position
= playlist
->index
+ 1;
745 position
= insert_position
= 0;
747 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
748 playlist
->last_insert_pos
= position
;
750 case PLAYLIST_INSERT_LAST
:
751 if (playlist
->first_index
> 0)
752 position
= insert_position
= playlist
->first_index
;
754 position
= insert_position
= playlist
->amount
;
756 case PLAYLIST_INSERT_SHUFFLED
:
758 if (playlist
->started
)
761 int n
= playlist
->amount
-
762 rotate_index(playlist
, playlist
->index
);
769 position
= playlist
->index
+ offset
+ 1;
770 if (position
>= playlist
->amount
)
771 position
-= playlist
->amount
;
773 insert_position
= position
;
776 position
= insert_position
= (rand() % (playlist
->amount
+1));
779 case PLAYLIST_REPLACE
:
780 if (playlist_remove_all_tracks(playlist
) < 0)
783 position
= insert_position
= playlist
->index
+ 1;
788 flags
|= PLAYLIST_QUEUED
;
790 /* shift indices so that track can be added */
791 for (i
=playlist
->amount
; i
>insert_position
; i
--)
793 playlist
->indices
[i
] = playlist
->indices
[i
-1];
795 if (playlist
->filenames
)
796 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
800 /* update stored indices if needed */
801 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
805 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
806 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
808 playlist
->first_index
++;
810 if (seek_pos
< 0 && playlist
->current
)
812 global_status
.resume_first_index
= playlist
->first_index
;
817 if (insert_position
< playlist
->last_insert_pos
||
818 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
819 playlist
->last_insert_pos
++;
821 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
823 int result
= update_control(playlist
,
824 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
825 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
831 playlist
->indices
[insert_position
] = flags
| seek_pos
;
834 if (playlist
->filenames
)
835 playlist
->filenames
[insert_position
] = NULL
;
839 playlist
->num_inserted_tracks
++;
841 return insert_position
;
845 * Callback for playlist_directory_tracksearch to insert track into
848 static int directory_search_callback(char* filename
, void* context
)
850 struct directory_search_context
* c
=
851 (struct directory_search_context
*) context
;
854 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
862 /* Make sure tracks are inserted in correct order if user requests
864 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
865 c
->position
= insert_pos
+ 1;
867 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
869 unsigned char* count_str
;
872 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
874 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
876 display_playlist_count(c
->count
, count_str
, false);
878 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
879 (audio_status() & AUDIO_STATUS_PLAY
) &&
880 c
->playlist
->started
)
881 audio_flush_and_reload_tracks();
888 * remove track at specified position
890 static int remove_track_from_playlist(struct playlist_info
* playlist
,
891 int position
, bool write
)
896 if (playlist
->amount
<= 0)
899 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
901 /* shift indices now that track has been removed */
902 for (i
=position
; i
<playlist
->amount
; i
++)
904 playlist
->indices
[i
] = playlist
->indices
[i
+1];
906 if (playlist
->filenames
)
907 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
914 playlist
->num_inserted_tracks
--;
916 playlist
->deleted
= true;
918 /* update stored indices if needed */
919 if (position
< playlist
->index
)
922 if (position
< playlist
->first_index
)
924 playlist
->first_index
--;
928 global_status
.resume_first_index
= playlist
->first_index
;
933 if (position
<= playlist
->last_insert_pos
)
934 playlist
->last_insert_pos
--;
936 if (write
&& playlist
->control_fd
>= 0)
938 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
939 position
, -1, NULL
, NULL
, NULL
);
944 sync_control(playlist
, false);
951 * randomly rearrange the array of indices for the playlist. If start_current
952 * is true then update the index to the new index of the current playing track
954 static int randomise_playlist(struct playlist_info
* playlist
,
955 unsigned int seed
, bool start_current
,
961 unsigned int current
= playlist
->indices
[playlist
->index
];
963 /* seed 0 is used to identify sorted playlist for resume purposes */
967 /* seed with the given seed */
970 /* randomise entire indices list */
971 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
973 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
974 candidate
= rand() % (count
+ 1);
976 /* now swap the values at the 'count' and 'candidate' positions */
977 store
= playlist
->indices
[candidate
];
978 playlist
->indices
[candidate
] = playlist
->indices
[count
];
979 playlist
->indices
[count
] = store
;
981 if (playlist
->filenames
)
983 store
= (long)playlist
->filenames
[candidate
];
984 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
985 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
991 find_and_set_playlist_index(playlist
, current
);
993 /* indices have been moved so last insert position is no longer valid */
994 playlist
->last_insert_pos
= -1;
996 playlist
->seed
= seed
;
997 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
998 playlist
->shuffle_modified
= true;
1002 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
1003 playlist
->first_index
, NULL
, NULL
, NULL
);
1004 global_status
.resume_seed
= seed
;
1012 * Sort the array of indices for the playlist. If start_current is true then
1013 * set the index to the new index of the current song.
1015 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
1018 unsigned int current
= playlist
->indices
[playlist
->index
];
1020 if (playlist
->amount
> 0)
1021 qsort(playlist
->indices
, playlist
->amount
,
1022 sizeof(playlist
->indices
[0]), compare
);
1024 #ifdef HAVE_DIRCACHE
1025 /** We need to re-check the song names from disk because qsort can't
1026 * sort two arrays at once :/
1027 * FIXME: Please implement a better way to do this. */
1028 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
1029 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1033 find_and_set_playlist_index(playlist
, current
);
1035 /* indices have been moved so last insert position is no longer valid */
1036 playlist
->last_insert_pos
= -1;
1038 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1039 playlist
->shuffle_modified
= false;
1040 if (write
&& playlist
->control_fd
>= 0)
1042 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1043 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1044 global_status
.resume_seed
= 0;
1051 /* Calculate how many steps we have to really step when skipping entries
1054 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1056 int i
, count
, direction
;
1058 int stepped_count
= 0;
1071 index
= playlist
->index
;
1074 /* Boundary check */
1076 index
+= playlist
->amount
;
1077 if (index
>= playlist
->amount
)
1078 index
-= playlist
->amount
;
1080 /* Check if we found a bad entry. */
1081 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1084 /* Are all entries bad? */
1085 if (stepped_count
++ > playlist
->amount
)
1092 } while (i
<= count
);
1097 /* Marks the index of the track to be skipped that is "steps" away from
1098 * current playing track.
1100 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1104 if (playlist
== NULL
)
1105 playlist
= ¤t_playlist
;
1107 /* need to account for already skipped tracks */
1108 steps
= calculate_step_count(playlist
, steps
);
1110 index
= playlist
->index
+ steps
;
1112 index
+= playlist
->amount
;
1113 else if (index
>= playlist
->amount
)
1114 index
-= playlist
->amount
;
1116 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1120 * returns the index of the track that is "steps" away from current playing
1123 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1126 int current_index
= playlist
->index
;
1127 int next_index
= -1;
1129 if (playlist
->amount
<= 0)
1132 if (repeat_mode
== -1)
1133 repeat_mode
= global_settings
.repeat_mode
;
1135 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1136 repeat_mode
= REPEAT_ALL
;
1138 steps
= calculate_step_count(playlist
, steps
);
1139 switch (repeat_mode
)
1141 case REPEAT_SHUFFLE
:
1142 /* Treat repeat shuffle just like repeat off. At end of playlist,
1143 play will be resumed in playlist_next() */
1146 current_index
= rotate_index(playlist
, current_index
);
1147 next_index
= current_index
+steps
;
1148 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1151 next_index
= (next_index
+playlist
->first_index
) %
1158 #ifdef AB_REPEAT_ENABLE
1161 next_index
= current_index
;
1167 next_index
= (current_index
+steps
) % playlist
->amount
;
1168 while (next_index
< 0)
1169 next_index
+= playlist
->amount
;
1171 if (steps
>= playlist
->amount
)
1178 /* second time around so skip the queued files */
1179 for (i
=0; i
<playlist
->amount
; i
++)
1181 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1182 index
= (index
+1) % playlist
->amount
;
1194 /* No luck if the whole playlist was bad. */
1195 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1202 * Search for the seek track and set appropriate indices. Used after shuffle
1203 * to make sure the current index is still pointing to correct track.
1205 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1210 /* Set the index to the current song */
1211 for (i
=0; i
<playlist
->amount
; i
++)
1213 if (playlist
->indices
[i
] == seek
)
1215 playlist
->index
= playlist
->first_index
= i
;
1217 if (playlist
->current
)
1219 global_status
.resume_first_index
= i
;
1229 * used to sort track indices. Sort order is as follows:
1230 * 1. Prepended tracks (in prepend order)
1231 * 2. Playlist/directory tracks (in playlist order)
1232 * 3. Inserted/Appended tracks (in insert order)
1234 static int compare(const void* p1
, const void* p2
)
1236 unsigned long* e1
= (unsigned long*) p1
;
1237 unsigned long* e2
= (unsigned long*) p2
;
1238 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1239 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1241 if (flags1
== flags2
)
1242 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1243 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1244 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1246 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1247 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1249 else if (flags1
&& flags2
)
1250 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1255 #ifdef HAVE_DIRCACHE
1257 * Thread to update filename pointers to dircache on background
1258 * without affecting playlist load up performance. This thread also flushes
1259 * any pending control commands when the disk spins up.
1261 static bool playlist_flush_callback(void)
1263 struct playlist_info
*playlist
;
1264 playlist
= ¤t_playlist
;
1265 if (playlist
->control_fd
>= 0)
1267 if (playlist
->num_cached
> 0)
1269 mutex_lock(&playlist
->control_mutex
);
1270 flush_cached_control(playlist
);
1271 mutex_unlock(&playlist
->control_mutex
);
1273 sync_control(playlist
, true);
1278 static void playlist_thread(void)
1280 struct queue_event ev
;
1281 bool dirty_pointers
= false;
1282 static char tmp
[MAX_PATH
+1];
1284 struct playlist_info
*playlist
;
1291 #ifndef HAVE_FLASH_STORAGE
1292 if (global_settings
.disk_spindown
> 1 &&
1293 global_settings
.disk_spindown
<= 5)
1294 sleep_time
= global_settings
.disk_spindown
- 1;
1299 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1303 case PLAYLIST_LOAD_POINTERS
:
1304 dirty_pointers
= true;
1307 /* Start the background scanning after either the disk spindown
1308 timeout or 5s, whichever is less */
1310 playlist
= ¤t_playlist
;
1311 if (playlist
->control_fd
>= 0)
1313 if (playlist
->num_cached
> 0)
1314 register_ata_idle_func(playlist_flush_callback
);
1317 if (!dirty_pointers
)
1320 if (!dircache_is_enabled() || !playlist
->filenames
1321 || playlist
->amount
<= 0)
1324 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1327 for (index
= 0; index
< playlist
->amount
1328 && queue_empty(&playlist_queue
); index
++)
1330 /* Process only pointers that are not already loaded. */
1331 if (playlist
->filenames
[index
])
1334 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1335 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1337 /* Load the filename from playlist file. */
1338 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1342 /* Set the dircache entry pointer. */
1343 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1345 /* And be on background so user doesn't notice any delays. */
1349 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1352 dirty_pointers
= false;
1356 case SYS_USB_CONNECTED
:
1357 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1358 usb_wait_for_disconnect(&playlist_queue
);
1367 * gets pathname for track at seek index
1369 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1370 bool control_file
, char *buf
, int buf_length
)
1374 char tmp_buf
[MAX_PATH
+1];
1375 char dir_buf
[MAX_PATH
+1];
1376 bool utf8
= playlist
->utf8
;
1378 if (buf_length
> MAX_PATH
+1)
1379 buf_length
= MAX_PATH
+1;
1381 #ifdef HAVE_DIRCACHE
1382 if (dircache_is_enabled() && playlist
->filenames
)
1384 if (playlist
->filenames
[index
] != NULL
)
1386 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1387 max
= strlen(tmp_buf
) + 1;
1394 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1396 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1397 tmp_buf
[MAX_PATH
] = '\0';
1398 max
= strlen(tmp_buf
) + 1;
1402 mutex_lock(&playlist
->control_mutex
);
1406 fd
= playlist
->control_fd
;
1411 if(-1 == playlist
->fd
)
1412 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1420 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1424 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1426 if ((max
> 0) && !utf8
)
1428 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1429 * be as large as tmp_buf.
1431 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1436 mutex_unlock(&playlist
->control_mutex
);
1441 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1443 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1449 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1450 dir_buf
[playlist
->dirlen
-1] = 0;
1452 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1455 static int get_next_directory(char *dir
){
1456 return get_next_dir(dir
,true,false);
1459 static int get_previous_directory(char *dir
){
1460 return get_next_dir(dir
,false,false);
1464 * search through all the directories (starting with the current) to find
1465 * one that has tracks to play
1467 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1469 struct playlist_info
* playlist
= ¤t_playlist
;
1471 int sort_dir
= global_settings
.sort_dir
;
1472 char *start_dir
= NULL
;
1474 struct tree_context
* tc
= tree_get_context();
1475 int dirfilter
= *(tc
->dirfilter
);
1477 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1479 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1480 char buffer
[MAX_PATH
];
1481 int folder_count
= 0,i
;
1482 srand(current_tick
);
1483 *(tc
->dirfilter
) = SHOW_MUSIC
;
1486 read(fd
,&folder_count
,sizeof(int));
1489 i
= rand()%folder_count
;
1490 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1491 read(fd
,buffer
,MAX_PATH
);
1492 if (check_subdir_for_music(buffer
,"") ==0)
1497 *(tc
->dirfilter
) = dirfilter
;
1502 /* not random folder advance */
1504 /* start with root */
1508 /* start with current directory */
1509 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1510 dir
[playlist
->dirlen
-1] = '\0';
1513 /* use the tree browser dircache to load files */
1514 *(tc
->dirfilter
) = SHOW_ALL
;
1516 /* sort in another direction if previous dir is requested */
1518 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1519 global_settings
.sort_dir
= 4;
1520 else if (global_settings
.sort_dir
== 1)
1521 global_settings
.sort_dir
= 2;
1522 else if (global_settings
.sort_dir
== 2)
1523 global_settings
.sort_dir
= 1;
1524 else if (global_settings
.sort_dir
== 4)
1525 global_settings
.sort_dir
= 0;
1530 struct entry
*files
;
1534 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1536 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1542 files
= (struct entry
*) tc
->dircache
;
1543 num_files
= tc
->filesindir
;
1545 for (i
=0; i
<num_files
; i
++)
1548 if (action_userabort(TIMEOUT_NOBLOCK
))
1555 if (files
[i
].attr
& ATTR_DIRECTORY
)
1559 result
= check_subdir_for_music(dir
, files
[i
].name
);
1566 else if (!strcmp(start_dir
, files
[i
].name
))
1573 /* move down to parent directory. current directory name is
1574 stored as the starting point for the search in parent */
1575 start_dir
= strrchr(dir
, '/');
1586 /* restore dirfilter & sort_dir */
1587 *(tc
->dirfilter
) = dirfilter
;
1588 global_settings
.sort_dir
= sort_dir
;
1590 /* special case if nothing found: try start searching again from root */
1591 if (result
== -1 && !recursion
){
1592 result
= get_next_dir(dir
,is_forward
, true);
1599 * Checks if there are any music files in the dir or any of its
1600 * subdirectories. May be called recursively.
1602 static int check_subdir_for_music(char *dir
, char *subdir
)
1605 int dirlen
= strlen(dir
);
1608 struct entry
*files
;
1609 bool has_music
= false;
1610 bool has_subdir
= false;
1611 struct tree_context
* tc
= tree_get_context();
1613 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1615 if (ft_load(tc
, dir
) < 0)
1617 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1621 files
= (struct entry
*) tc
->dircache
;
1622 num_files
= tc
->filesindir
;
1624 for (i
=0; i
<num_files
; i
++)
1626 if (files
[i
].attr
& ATTR_DIRECTORY
)
1628 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1640 for (i
=0; i
<num_files
; i
++)
1642 if (action_userabort(TIMEOUT_NOBLOCK
))
1648 if (files
[i
].attr
& ATTR_DIRECTORY
)
1650 result
= check_subdir_for_music(dir
, files
[i
].name
);
1668 /* we now need to reload our current directory */
1669 if(ft_load(tc
, dir
) < 0)
1670 gui_syncsplash(HZ
*2,
1671 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1678 * Returns absolute path of track
1680 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1687 /* Zero-terminate the file name */
1688 while((src
[i
] != '\n') &&
1693 /* Now work back killing white space */
1694 while((src
[i
-1] == ' ') ||
1700 /* replace backslashes with forward slashes */
1701 for ( j
=0; j
<i
; j
++ )
1702 if ( src
[j
] == '\\' )
1707 strncpy(dest
, src
, buf_length
);
1711 /* handle dos style drive letter */
1713 strncpy(dest
, &src
[2], buf_length
);
1714 else if (!strncmp(src
, "../", 3))
1716 /* handle relative paths */
1718 while(!strncmp(&src
[i
], "../", 3))
1720 for (j
=0; j
<i
/3; j
++) {
1721 temp_ptr
= strrchr(dir
, '/');
1727 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1729 else if ( '.' == src
[0] && '/' == src
[1] ) {
1730 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1733 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1741 * Display splash message showing progress of playlist/directory insertion or
1744 static void display_playlist_count(int count
, const unsigned char *fmt
,
1747 static long talked_tick
= 0;
1748 long id
= P2ID(fmt
);
1749 if(global_settings
.talk_menu
&& id
>=0)
1751 if(final
|| (count
&& (talked_tick
== 0
1752 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1754 talked_tick
= current_tick
;
1755 talk_number(count
, false);
1761 gui_syncsplash(0, fmt
, count
, str(LANG_OFF_ABORT
));
1765 * Display buffer full message
1767 static void display_buffer_full(void)
1769 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1773 * Flush any cached control commands to disk. Called when playlist is being
1774 * modified. Returns 0 on success and -1 on failure.
1776 static int flush_cached_control(struct playlist_info
* playlist
)
1781 if (!playlist
->num_cached
)
1784 lseek(playlist
->control_fd
, 0, SEEK_END
);
1786 for (i
=0; i
<playlist
->num_cached
; i
++)
1788 struct playlist_control_cache
* cache
=
1789 &(playlist
->control_cache
[i
]);
1791 switch (cache
->command
)
1793 case PLAYLIST_COMMAND_PLAYLIST
:
1794 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1795 cache
->i1
, cache
->s1
, cache
->s2
);
1797 case PLAYLIST_COMMAND_ADD
:
1798 case PLAYLIST_COMMAND_QUEUE
:
1799 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1800 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1801 cache
->i1
, cache
->i2
);
1804 /* save the position in file where name is written */
1805 int* seek_pos
= (int *)cache
->data
;
1806 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1807 result
= fdprintf(playlist
->control_fd
, "%s\n",
1811 case PLAYLIST_COMMAND_DELETE
:
1812 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1814 case PLAYLIST_COMMAND_SHUFFLE
:
1815 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1816 cache
->i1
, cache
->i2
);
1818 case PLAYLIST_COMMAND_UNSHUFFLE
:
1819 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1821 case PLAYLIST_COMMAND_RESET
:
1822 result
= fdprintf(playlist
->control_fd
, "R\n");
1834 if (global_status
.resume_seed
>= 0)
1836 global_status
.resume_seed
= -1;
1840 playlist
->num_cached
= 0;
1841 playlist
->pending_control_sync
= true;
1848 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1855 * Update control data with new command. Depending on the command, it may be
1856 * cached or flushed to disk.
1858 static int update_control(struct playlist_info
* playlist
,
1859 enum playlist_command command
, int i1
, int i2
,
1860 const char* s1
, const char* s2
, void* data
)
1863 struct playlist_control_cache
* cache
;
1866 mutex_lock(&playlist
->control_mutex
);
1868 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1870 cache
->command
= command
;
1879 case PLAYLIST_COMMAND_PLAYLIST
:
1880 case PLAYLIST_COMMAND_ADD
:
1881 case PLAYLIST_COMMAND_QUEUE
:
1882 #ifndef HAVE_DIRCACHE
1883 case PLAYLIST_COMMAND_DELETE
:
1884 case PLAYLIST_COMMAND_RESET
:
1888 case PLAYLIST_COMMAND_SHUFFLE
:
1889 case PLAYLIST_COMMAND_UNSHUFFLE
:
1891 /* only flush when needed */
1895 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1896 result
= flush_cached_control(playlist
);
1898 mutex_unlock(&playlist
->control_mutex
);
1904 * sync control file to disk
1906 static void sync_control(struct playlist_info
* playlist
, bool force
)
1908 #ifdef HAVE_DIRCACHE
1909 if (playlist
->started
&& force
)
1913 if (playlist
->started
)
1916 if (playlist
->pending_control_sync
)
1918 mutex_lock(&playlist
->control_mutex
);
1919 fsync(playlist
->control_fd
);
1920 playlist
->pending_control_sync
= false;
1921 mutex_unlock(&playlist
->control_mutex
);
1927 * Rotate indices such that first_index is index 0
1929 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1931 index
-= playlist
->first_index
;
1933 index
+= playlist
->amount
;
1939 * Initialize playlist entries at startup
1941 void playlist_init(void)
1943 struct playlist_info
* playlist
= ¤t_playlist
;
1945 playlist
->current
= true;
1946 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1947 "%s", PLAYLIST_CONTROL_FILE
);
1949 playlist
->control_fd
= -1;
1950 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1951 playlist
->indices
= buffer_alloc(
1952 playlist
->max_playlist_size
* sizeof(int));
1953 playlist
->buffer_size
=
1954 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1955 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1956 mutex_init(&playlist
->control_mutex
);
1957 empty_playlist(playlist
, true);
1959 #ifdef HAVE_DIRCACHE
1960 playlist
->filenames
= buffer_alloc(
1961 playlist
->max_playlist_size
* sizeof(int));
1962 memset(playlist
->filenames
, 0,
1963 playlist
->max_playlist_size
* sizeof(int));
1964 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1965 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1967 queue_init(&playlist_queue
, true);
1972 * Clean playlist at shutdown
1974 void playlist_shutdown(void)
1976 struct playlist_info
* playlist
= ¤t_playlist
;
1978 if (playlist
->control_fd
>= 0)
1980 mutex_lock(&playlist
->control_mutex
);
1982 if (playlist
->num_cached
> 0)
1983 flush_cached_control(playlist
);
1985 close(playlist
->control_fd
);
1987 mutex_unlock(&playlist
->control_mutex
);
1992 * Create new playlist
1994 int playlist_create(const char *dir
, const char *file
)
1996 struct playlist_info
* playlist
= ¤t_playlist
;
1998 new_playlist(playlist
, dir
, file
);
2001 /* load the playlist file */
2002 add_indices_to_playlist(playlist
, NULL
, 0);
2007 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2010 * Restore the playlist state based on control file commands. Called to
2011 * resume playback after shutdown.
2013 int playlist_resume(void)
2015 struct playlist_info
* playlist
= ¤t_playlist
;
2020 int control_file_size
= 0;
2024 /* use mp3 buffer for maximum load speed */
2025 #if CONFIG_CODEC != SWCODEC
2026 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2027 buflen
= (audiobufend
- audiobuf
);
2028 buffer
= (char *)audiobuf
;
2030 buffer
= (char *)audio_get_buffer(false, &buflen
);
2033 empty_playlist(playlist
, true);
2035 gui_syncsplash(0, ID2P(LANG_WAIT
));
2036 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2037 if (playlist
->control_fd
< 0)
2039 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2042 playlist
->control_created
= true;
2044 control_file_size
= filesize(playlist
->control_fd
);
2045 if (control_file_size
<= 0)
2047 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2051 /* read a small amount first to get the header */
2052 nread
= read(playlist
->control_fd
, buffer
,
2053 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2056 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2060 playlist
->started
= true;
2066 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2067 int last_newline
= 0;
2069 bool newline
= true;
2070 bool exit_loop
= false;
2075 unsigned long last_tick
= current_tick
;
2077 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
2079 /* So a splash while we are loading. */
2080 if (current_tick
- last_tick
> HZ
/4)
2082 gui_syncsplash(0, str(LANG_LOADING_PERCENT
),
2083 (total_read
+count
)*100/control_file_size
,
2084 str(LANG_OFF_ABORT
));
2085 if (action_userabort(TIMEOUT_NOBLOCK
))
2088 * Not sure how to implement this, somebody more familiar
2089 * with the code, please fix this. */
2091 last_tick
= current_tick
;
2094 /* Are we on a new line? */
2095 if((*p
== '\n') || (*p
== '\r'))
2099 /* save last_newline in case we need to load more data */
2100 last_newline
= count
;
2102 switch (current_command
)
2104 case PLAYLIST_COMMAND_PLAYLIST
:
2106 /* str1=version str2=dir str3=file */
2122 version
= atoi(str1
);
2124 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2127 update_playlist_filename(playlist
, str2
, str3
);
2129 if (str3
[0] != '\0')
2131 /* NOTE: add_indices_to_playlist() overwrites the
2132 audiobuf so we need to reload control file
2134 add_indices_to_playlist(playlist
, NULL
, 0);
2136 else if (str2
[0] != '\0')
2138 playlist
->in_ram
= true;
2139 resume_directory(str2
);
2142 /* load the rest of the data */
2148 case PLAYLIST_COMMAND_ADD
:
2149 case PLAYLIST_COMMAND_QUEUE
:
2151 /* str1=position str2=last_position str3=file */
2152 int position
, last_position
;
2155 if (!str1
|| !str2
|| !str3
)
2162 position
= atoi(str1
);
2163 last_position
= atoi(str2
);
2165 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2168 /* seek position is based on str3's position in
2170 if (add_track_to_playlist(playlist
, str3
, position
,
2171 queue
, total_read
+(str3
-buffer
)) < 0)
2174 playlist
->last_insert_pos
= last_position
;
2178 case PLAYLIST_COMMAND_DELETE
:
2190 position
= atoi(str1
);
2192 if (remove_track_from_playlist(playlist
, position
,
2198 case PLAYLIST_COMMAND_SHUFFLE
:
2200 /* str1=seed str2=first_index */
2212 /* Always sort list before shuffling */
2213 sort_playlist(playlist
, false, false);
2217 playlist
->first_index
= atoi(str2
);
2219 if (randomise_playlist(playlist
, seed
, false,
2226 case PLAYLIST_COMMAND_UNSHUFFLE
:
2228 /* str1=first_index */
2236 playlist
->first_index
= atoi(str1
);
2238 if (sort_playlist(playlist
, false, false) < 0)
2244 case PLAYLIST_COMMAND_RESET
:
2246 playlist
->last_insert_pos
= -1;
2249 case PLAYLIST_COMMAND_COMMENT
:
2256 /* to ignore any extra newlines */
2257 current_command
= PLAYLIST_COMMAND_COMMENT
;
2263 /* first non-comment line must always specify playlist */
2264 if (first
&& *p
!= 'P' && *p
!= '#')
2274 /* playlist can only be specified once */
2282 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2285 current_command
= PLAYLIST_COMMAND_ADD
;
2288 current_command
= PLAYLIST_COMMAND_QUEUE
;
2291 current_command
= PLAYLIST_COMMAND_DELETE
;
2294 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2297 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2300 current_command
= PLAYLIST_COMMAND_RESET
;
2303 current_command
= PLAYLIST_COMMAND_COMMENT
;
2316 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2318 /* all control file strings are separated with a colon.
2319 Replace the colon with 0 to get proper strings that can be
2320 used by commands above */
2326 if ((count
+1) < nread
)
2340 /* allow last string to contain colons */
2351 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2355 if (!newline
|| (exit_loop
&& count
<nread
))
2357 if ((total_read
+ count
) >= control_file_size
)
2359 /* no newline at end of control file */
2360 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2364 /* We didn't end on a newline or we exited loop prematurely.
2365 Either way, re-read the remainder. */
2366 count
= last_newline
;
2367 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2370 total_read
+= count
;
2373 /* still looking for header */
2374 nread
= read(playlist
->control_fd
, buffer
,
2375 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2377 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2379 /* Terminate on EOF */
2382 if (global_status
.resume_seed
>= 0)
2384 /* Apply shuffle command saved in settings */
2385 if (global_status
.resume_seed
== 0)
2386 sort_playlist(playlist
, false, true);
2390 sort_playlist(playlist
, false, false);
2392 randomise_playlist(playlist
, global_status
.resume_seed
,
2397 playlist
->first_index
= global_status
.resume_first_index
;
2402 #ifdef HAVE_DIRCACHE
2403 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2410 * Add track to in_ram playlist. Used when playing directories.
2412 int playlist_add(const char *filename
)
2414 struct playlist_info
* playlist
= ¤t_playlist
;
2415 int len
= strlen(filename
);
2417 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2418 (playlist
->amount
>= playlist
->max_playlist_size
))
2420 display_buffer_full();
2424 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2425 #ifdef HAVE_DIRCACHE
2426 playlist
->filenames
[playlist
->amount
] = NULL
;
2430 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2431 playlist
->buffer_end_pos
+= len
;
2432 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2437 /* shuffle newly created playlist using random seed. */
2438 int playlist_shuffle(int random_seed
, int start_index
)
2440 struct playlist_info
* playlist
= ¤t_playlist
;
2442 unsigned int seek_pos
= 0;
2443 bool start_current
= false;
2445 if (start_index
>= 0 && global_settings
.play_selected
)
2447 /* store the seek position before the shuffle */
2448 seek_pos
= playlist
->indices
[start_index
];
2449 playlist
->index
= global_status
.resume_first_index
=
2450 playlist
->first_index
= start_index
;
2451 start_current
= true;
2454 randomise_playlist(playlist
, random_seed
, start_current
, true);
2456 return playlist
->index
;
2459 /* start playing current playlist at specified index/offset */
2460 int playlist_start(int start_index
, int offset
)
2462 struct playlist_info
* playlist
= ¤t_playlist
;
2464 /* Cancel FM radio selection as previous music. For cases where we start
2465 playback without going to the WPS, such as playlist insert.. or
2466 playlist catalog. */
2467 previous_music_is_wps();
2469 playlist
->index
= start_index
;
2471 #if CONFIG_CODEC != SWCODEC
2472 talk_buffer_steal(); /* will use the mp3 buffer */
2475 playlist
->started
= true;
2476 sync_control(playlist
, false);
2482 /* Returns false if 'steps' is out of bounds, else true */
2483 bool playlist_check(int steps
)
2485 struct playlist_info
* playlist
= ¤t_playlist
;
2487 /* always allow folder navigation */
2488 if (global_settings
.next_folder
&& playlist
->in_ram
)
2491 int index
= get_next_index(playlist
, steps
, -1);
2493 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2494 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2496 return (index
>= 0);
2499 /* get trackname of track that is "steps" away from current playing track.
2500 NULL is used to identify end of playlist */
2501 char* playlist_peek(int steps
)
2503 struct playlist_info
* playlist
= ¤t_playlist
;
2509 index
= get_next_index(playlist
, steps
, -1);
2513 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2514 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2516 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2520 temp_ptr
= now_playing
;
2522 if (!playlist
->in_ram
|| control_file
)
2524 /* remove bogus dirs from beginning of path
2525 (workaround for buggy playlist creation tools) */
2528 if (file_exists(temp_ptr
))
2531 temp_ptr
= strchr(temp_ptr
+1, '/');
2536 /* Even though this is an invalid file, we still need to pass a
2537 file name to the caller because NULL is used to indicate end
2547 * Update indices as track has changed
2549 int playlist_next(int steps
)
2551 struct playlist_info
* playlist
= ¤t_playlist
;
2555 #ifdef AB_REPEAT_ENABLE
2556 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2558 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2562 /* We need to delete all the queued songs */
2563 for (i
=0, j
=steps
; i
<j
; i
++)
2565 index
= get_next_index(playlist
, i
, -1);
2567 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2569 remove_track_from_playlist(playlist
, index
, true);
2570 steps
--; /* one less track */
2575 index
= get_next_index(playlist
, steps
, -1);
2579 /* end of playlist... or is it */
2580 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2581 playlist
->amount
> 1)
2583 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2584 playlist
->first_index
= global_status
.resume_first_index
= 0;
2585 sort_playlist(playlist
, false, false);
2586 randomise_playlist(playlist
, current_tick
, false, true);
2587 #if CONFIG_CODEC != SWCODEC
2588 playlist_start(0, 0);
2590 playlist
->index
= 0;
2593 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2595 index
= create_and_play_dir(steps
, true);
2599 playlist
->index
= index
;
2606 playlist
->index
= index
;
2608 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2610 /* check to see if we've gone beyond the last inserted track */
2611 int cur
= rotate_index(playlist
, index
);
2612 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2616 /* reset last inserted track */
2617 playlist
->last_insert_pos
= -1;
2619 if (playlist
->control_fd
>= 0)
2621 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2622 -1, -1, NULL
, NULL
, NULL
);
2627 sync_control(playlist
, false);
2635 /* try playing next or previous folder */
2636 bool playlist_next_dir(int direction
)
2638 /* not to mess up real playlists */
2639 if(!current_playlist
.in_ram
)
2642 return create_and_play_dir(direction
, false) >= 0;
2645 /* Get resume info for current playing song. If return value is -1 then
2646 settings shouldn't be saved. */
2647 int playlist_get_resume_info(int *resume_index
)
2649 struct playlist_info
* playlist
= ¤t_playlist
;
2651 *resume_index
= playlist
->index
;
2656 /* Update resume info for current playing song. Returns -1 on error. */
2657 int playlist_update_resume_info(const struct mp3entry
* id3
)
2659 struct playlist_info
* playlist
= ¤t_playlist
;
2663 if (global_status
.resume_index
!= playlist
->index
||
2664 global_status
.resume_offset
!= id3
->offset
)
2666 global_status
.resume_index
= playlist
->index
;
2667 global_status
.resume_offset
= id3
->offset
;
2673 global_status
.resume_index
= -1;
2674 global_status
.resume_offset
= -1;
2681 /* Returns index of current playing track for display purposes. This value
2682 should not be used for resume purposes as it doesn't represent the actual
2683 index into the playlist */
2684 int playlist_get_display_index(void)
2686 struct playlist_info
* playlist
= ¤t_playlist
;
2688 /* first_index should always be index 0 for display purposes */
2689 int index
= rotate_index(playlist
, playlist
->index
);
2694 /* returns number of tracks in current playlist */
2695 int playlist_amount(void)
2697 return playlist_amount_ex(NULL
);
2701 * Create a new playlist If playlist is not NULL then we're loading a
2702 * playlist off disk for viewing/editing. The index_buffer is used to store
2703 * playlist indices (required for and only used if !current playlist). The
2704 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2706 int playlist_create_ex(struct playlist_info
* playlist
,
2707 const char* dir
, const char* file
,
2708 void* index_buffer
, int index_buffer_size
,
2709 void* temp_buffer
, int temp_buffer_size
)
2712 playlist
= ¤t_playlist
;
2715 /* Initialize playlist structure */
2716 int r
= rand() % 10;
2717 playlist
->current
= false;
2719 /* Use random name for control file */
2720 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2721 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2723 playlist
->control_fd
= -1;
2727 int num_indices
= index_buffer_size
/ sizeof(int);
2729 #ifdef HAVE_DIRCACHE
2732 if (num_indices
> global_settings
.max_files_in_playlist
)
2733 num_indices
= global_settings
.max_files_in_playlist
;
2735 playlist
->max_playlist_size
= num_indices
;
2736 playlist
->indices
= index_buffer
;
2737 #ifdef HAVE_DIRCACHE
2738 playlist
->filenames
= (const struct dircache_entry
**)
2739 &playlist
->indices
[num_indices
];
2744 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2745 playlist
->indices
= current_playlist
.indices
;
2746 #ifdef HAVE_DIRCACHE
2747 playlist
->filenames
= current_playlist
.filenames
;
2751 playlist
->buffer_size
= 0;
2752 playlist
->buffer
= NULL
;
2753 mutex_init(&playlist
->control_mutex
);
2756 new_playlist(playlist
, dir
, file
);
2759 /* load the playlist file */
2760 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2766 * Set the specified playlist as the current.
2767 * NOTE: You will get undefined behaviour if something is already playing so
2768 * remember to stop before calling this. Also, this call will
2769 * effectively close your playlist, making it unusable.
2771 int playlist_set_current(struct playlist_info
* playlist
)
2773 if (!playlist
|| (check_control(playlist
) < 0))
2776 empty_playlist(¤t_playlist
, false);
2778 strncpy(current_playlist
.filename
, playlist
->filename
,
2779 sizeof(current_playlist
.filename
));
2781 current_playlist
.utf8
= playlist
->utf8
;
2782 current_playlist
.fd
= playlist
->fd
;
2784 close(playlist
->control_fd
);
2785 close(current_playlist
.control_fd
);
2786 remove(current_playlist
.control_filename
);
2787 if (rename(playlist
->control_filename
,
2788 current_playlist
.control_filename
) < 0)
2790 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2792 if (current_playlist
.control_fd
< 0)
2794 current_playlist
.control_created
= true;
2796 current_playlist
.dirlen
= playlist
->dirlen
;
2798 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2800 memcpy(current_playlist
.indices
, playlist
->indices
,
2801 playlist
->max_playlist_size
*sizeof(int));
2802 #ifdef HAVE_DIRCACHE
2803 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2804 playlist
->max_playlist_size
*sizeof(int));
2808 current_playlist
.first_index
= playlist
->first_index
;
2809 current_playlist
.amount
= playlist
->amount
;
2810 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2811 current_playlist
.seed
= playlist
->seed
;
2812 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2813 current_playlist
.deleted
= playlist
->deleted
;
2814 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2816 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2817 sizeof(current_playlist
.control_cache
));
2818 current_playlist
.num_cached
= playlist
->num_cached
;
2819 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2825 * Close files and delete control file for non-current playlist.
2827 void playlist_close(struct playlist_info
* playlist
)
2832 if (playlist
->fd
>= 0)
2833 close(playlist
->fd
);
2835 if (playlist
->control_fd
>= 0)
2836 close(playlist
->control_fd
);
2838 if (playlist
->control_created
)
2839 remove(playlist
->control_filename
);
2842 void playlist_sync(struct playlist_info
* playlist
)
2845 playlist
= ¤t_playlist
;
2847 sync_control(playlist
, false);
2848 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2849 audio_flush_and_reload_tracks();
2851 #ifdef HAVE_DIRCACHE
2852 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2857 * Insert track into playlist at specified position (or one of the special
2858 * positions). Returns position where track was inserted or -1 if error.
2860 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2861 int position
, bool queue
, bool sync
)
2866 playlist
= ¤t_playlist
;
2868 if (check_control(playlist
) < 0)
2870 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2874 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2876 /* Check if we want manually sync later. For example when adding
2877 * bunch of files from tagcache, syncing after every file wouldn't be
2878 * a good thing to do. */
2879 if (sync
&& result
>= 0)
2880 playlist_sync(playlist
);
2886 * Insert all tracks from specified directory into playlist.
2888 int playlist_insert_directory(struct playlist_info
* playlist
,
2889 const char *dirname
, int position
, bool queue
,
2893 unsigned char *count_str
;
2894 struct directory_search_context context
;
2897 playlist
= ¤t_playlist
;
2899 if (check_control(playlist
) < 0)
2901 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2905 if (position
== PLAYLIST_REPLACE
)
2907 if (playlist_remove_all_tracks(playlist
) == 0)
2908 position
= PLAYLIST_INSERT_LAST
;
2914 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2916 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2918 display_playlist_count(0, count_str
, false);
2920 context
.playlist
= playlist
;
2921 context
.position
= position
;
2922 context
.queue
= queue
;
2927 result
= playlist_directory_tracksearch(dirname
, recurse
,
2928 directory_search_callback
, &context
);
2930 sync_control(playlist
, false);
2934 display_playlist_count(context
.count
, count_str
, true);
2936 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2937 audio_flush_and_reload_tracks();
2939 #ifdef HAVE_DIRCACHE
2940 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2947 * Insert all tracks from specified playlist into dynamic playlist.
2949 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2950 int position
, bool queue
)
2956 unsigned char *count_str
;
2957 char temp_buf
[MAX_PATH
+1];
2958 char trackname
[MAX_PATH
+1];
2961 bool utf8
= is_m3u8(filename
);
2964 playlist
= ¤t_playlist
;
2966 if (check_control(playlist
) < 0)
2968 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2972 fd
= open(filename
, O_RDONLY
);
2975 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2979 /* we need the directory name for formatting purposes */
2982 temp_ptr
= strrchr(filename
+1,'/');
2989 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2991 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2993 display_playlist_count(count
, count_str
, false);
2995 if (position
== PLAYLIST_REPLACE
)
2997 if (playlist_remove_all_tracks(playlist
) == 0)
2998 position
= PLAYLIST_INSERT_LAST
;
3004 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3007 if (action_userabort(TIMEOUT_NOBLOCK
))
3010 if (count
== 0 && is_utf8_bom(temp_buf
, max
))
3013 memmove(temp_buf
, temp_buf
+ BOM_SIZE
, max
);
3016 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3022 /* Use trackname as a temporay buffer. Note that trackname must
3023 * be as large as temp_buf.
3025 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3028 /* we need to format so that relative paths are correctly
3030 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3037 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3046 /* Make sure tracks are inserted in correct order if user
3047 requests INSERT_FIRST */
3048 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3049 position
= insert_pos
+ 1;
3053 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3055 display_playlist_count(count
, count_str
, false);
3057 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3058 (audio_status() & AUDIO_STATUS_PLAY
) &&
3060 audio_flush_and_reload_tracks();
3064 /* let the other threads work */
3073 sync_control(playlist
, false);
3077 display_playlist_count(count
, count_str
, true);
3079 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3080 audio_flush_and_reload_tracks();
3082 #ifdef HAVE_DIRCACHE
3083 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3090 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3091 * we want to delete the current playing track.
3093 int playlist_delete(struct playlist_info
* playlist
, int index
)
3098 playlist
= ¤t_playlist
;
3100 if (check_control(playlist
) < 0)
3102 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3106 if (index
== PLAYLIST_DELETE_CURRENT
)
3107 index
= playlist
->index
;
3109 result
= remove_track_from_playlist(playlist
, index
, true);
3111 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3113 audio_flush_and_reload_tracks();
3119 * Move track at index to new_index. Tracks between the two are shifted
3120 * appropriately. Returns 0 on success and -1 on failure.
3122 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3128 bool current
= false;
3130 char filename
[MAX_PATH
];
3133 playlist
= ¤t_playlist
;
3135 if (check_control(playlist
) < 0)
3137 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3141 if (index
== new_index
)
3144 if (index
== playlist
->index
)
3145 /* Moving the current track */
3148 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3149 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3150 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3152 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3153 sizeof(filename
)) < 0)
3156 /* Delete track from original position */
3157 result
= remove_track_from_playlist(playlist
, index
, true);
3161 /* We want to insert the track at the position that was specified by
3162 new_index. This may be different then new_index because of the
3163 shifting that occurred after the delete */
3164 r
= rotate_index(playlist
, new_index
);
3168 new_index
= PLAYLIST_PREPEND
;
3169 else if (r
== playlist
->amount
)
3171 new_index
= PLAYLIST_INSERT_LAST
;
3173 /* Calculate index of desired position */
3174 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3176 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3183 /* Moved the current track */
3186 case PLAYLIST_PREPEND
:
3187 playlist
->index
= playlist
->first_index
;
3189 case PLAYLIST_INSERT_LAST
:
3190 playlist
->index
= playlist
->first_index
- 1;
3191 if (playlist
->index
< 0)
3192 playlist
->index
+= playlist
->amount
;
3195 playlist
->index
= new_index
;
3200 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3201 audio_flush_and_reload_tracks();
3205 #ifdef HAVE_DIRCACHE
3206 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3212 /* shuffle currently playing playlist */
3213 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3219 playlist
= ¤t_playlist
;
3221 check_control(playlist
);
3223 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3225 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3227 audio_flush_and_reload_tracks();
3232 /* sort currently playing playlist */
3233 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3238 playlist
= ¤t_playlist
;
3240 check_control(playlist
);
3242 result
= sort_playlist(playlist
, start_current
, true);
3244 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3246 audio_flush_and_reload_tracks();
3251 /* returns true if playlist has been modified */
3252 bool playlist_modified(const struct playlist_info
* playlist
)
3255 playlist
= ¤t_playlist
;
3257 if (playlist
->shuffle_modified
||
3258 playlist
->deleted
||
3259 playlist
->num_inserted_tracks
> 0)
3265 /* returns index of first track in playlist */
3266 int playlist_get_first_index(const struct playlist_info
* playlist
)
3269 playlist
= ¤t_playlist
;
3271 return playlist
->first_index
;
3274 /* returns shuffle seed of playlist */
3275 int playlist_get_seed(const struct playlist_info
* playlist
)
3278 playlist
= ¤t_playlist
;
3280 return playlist
->seed
;
3283 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3284 int playlist_amount_ex(const struct playlist_info
* playlist
)
3287 playlist
= ¤t_playlist
;
3289 return playlist
->amount
;
3292 /* returns full path of playlist (minus extension) */
3293 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3299 playlist
= ¤t_playlist
;
3301 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3306 /* Remove extension */
3307 sep
= strrchr(buf
, '.');
3314 /* returns the playlist filename */
3315 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3319 playlist
= ¤t_playlist
;
3321 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3329 /* Fills info structure with information about track at specified index.
3330 Returns 0 on success and -1 on failure */
3331 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3332 struct playlist_track_info
* info
)
3338 playlist
= ¤t_playlist
;
3340 if (index
< 0 || index
>= playlist
->amount
)
3343 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3344 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3346 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3347 sizeof(info
->filename
)) < 0)
3354 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3355 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3357 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3361 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3362 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3364 info
->index
= index
;
3365 info
->display_index
= rotate_index(playlist
, index
) + 1;
3370 /* save the current dynamic playlist to specified file */
3371 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3376 char path
[MAX_PATH
+1];
3377 char tmp_buf
[MAX_PATH
+1];
3379 bool overwrite_current
= false;
3380 int* index_buf
= NULL
;
3383 playlist
= ¤t_playlist
;
3385 if (playlist
->amount
<= 0)
3388 /* use current working directory as base for pathname */
3389 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3390 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3393 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3395 /* Attempting to overwrite current playlist file.*/
3397 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3399 /* not enough buffer space to store updated indices */
3400 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3404 /* in_ram buffer is unused for m3u files so we'll use for storing
3406 index_buf
= (int*)playlist
->buffer
;
3408 /* use temporary pathname */
3409 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3410 overwrite_current
= true;
3413 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3416 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3420 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3426 /* some applications require a BOM to read the file properly */
3427 write(fd
, BOM
, BOM_SIZE
);
3430 index
= playlist
->first_index
;
3431 for (i
=0; i
<playlist
->amount
; i
++)
3438 if (action_userabort(TIMEOUT_NOBLOCK
))
3444 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3445 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3446 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3448 /* Don't save queued files */
3451 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3458 if (overwrite_current
)
3459 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3461 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3463 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3470 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3471 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3477 index
= (index
+1)%playlist
->amount
;
3480 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3484 if (overwrite_current
&& result
>= 0)
3488 mutex_lock(&playlist
->control_mutex
);
3490 /* Replace the current playlist with the new one and update indices */
3491 close(playlist
->fd
);
3492 if (remove(playlist
->filename
) >= 0)
3494 if (rename(path
, playlist
->filename
) >= 0)
3496 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3497 if (playlist
->fd
>= 0)
3499 index
= playlist
->first_index
;
3500 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3502 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3504 playlist
->indices
[index
] = index_buf
[count
];
3507 index
= (index
+1)%playlist
->amount
;
3510 /* we need to recreate control because inserted tracks are
3511 now part of the playlist and shuffle has been
3513 result
= recreate_control(playlist
);
3518 mutex_unlock(&playlist
->control_mutex
);
3528 * Search specified directory for tracks and notify via callback. May be
3529 * called recursively.
3531 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3532 int (*callback
)(char*, void*),
3535 char buf
[MAX_PATH
+1];
3539 struct entry
*files
;
3540 struct tree_context
* tc
= tree_get_context();
3541 int old_dirfilter
= *(tc
->dirfilter
);
3546 /* use the tree browser dircache to load files */
3547 *(tc
->dirfilter
) = SHOW_ALL
;
3549 if (ft_load(tc
, dirname
) < 0)
3551 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3552 *(tc
->dirfilter
) = old_dirfilter
;
3556 files
= (struct entry
*) tc
->dircache
;
3557 num_files
= tc
->filesindir
;
3559 /* we've overwritten the dircache so tree browser will need to be
3563 for (i
=0; i
<num_files
; i
++)
3566 if (action_userabort(TIMEOUT_NOBLOCK
))
3572 if (files
[i
].attr
& ATTR_DIRECTORY
)
3576 /* recursively add directories */
3577 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3578 result
= playlist_directory_tracksearch(buf
, recurse
,
3583 /* we now need to reload our current directory */
3584 if(ft_load(tc
, dirname
) < 0)
3590 files
= (struct entry
*) tc
->dircache
;
3591 num_files
= tc
->filesindir
;
3601 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3603 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3605 if (callback(buf
, context
) != 0)
3611 /* let the other threads work */
3616 /* restore dirfilter */
3617 *(tc
->dirfilter
) = old_dirfilter
;