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 empty_playlist(playlist
, false);
326 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
327 playlist
->in_ram
= true;
329 dir
= ""; /* empty playlist */
332 update_playlist_filename(playlist
, dir
, file
);
334 if (playlist
->control_fd
>= 0)
336 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
337 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
338 sync_control(playlist
, false);
343 * create control file for playlist
345 static void create_control(struct playlist_info
* playlist
)
347 playlist
->control_fd
= open(playlist
->control_filename
,
348 O_CREAT
|O_RDWR
|O_TRUNC
);
349 if (playlist
->control_fd
< 0)
351 if (check_rockboxdir())
353 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
354 gui_syncsplash(HZ
*2, (unsigned char *)"%s (%d)",
355 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
356 playlist
->control_fd
);
358 playlist
->control_created
= false;
362 playlist
->control_created
= true;
367 * validate the control file. This may include creating/initializing it if
370 static int check_control(struct playlist_info
* playlist
)
372 if (!playlist
->control_created
)
374 create_control(playlist
);
376 if (playlist
->control_fd
>= 0)
378 char* dir
= playlist
->filename
;
379 char* file
= playlist
->filename
+playlist
->dirlen
;
380 char c
= playlist
->filename
[playlist
->dirlen
-1];
382 playlist
->filename
[playlist
->dirlen
-1] = '\0';
384 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
385 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
386 sync_control(playlist
, false);
387 playlist
->filename
[playlist
->dirlen
-1] = c
;
391 if (playlist
->control_fd
< 0)
398 * recreate the control file based on current playlist entries
400 static int recreate_control(struct playlist_info
* playlist
)
402 char temp_file
[MAX_PATH
+1];
407 if(playlist
->control_fd
>= 0)
409 char* dir
= playlist
->filename
;
410 char* file
= playlist
->filename
+playlist
->dirlen
;
411 char c
= playlist
->filename
[playlist
->dirlen
-1];
413 close(playlist
->control_fd
);
415 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
416 playlist
->control_filename
);
418 if (rename(playlist
->control_filename
, temp_file
) < 0)
421 temp_fd
= open(temp_file
, O_RDONLY
);
425 playlist
->control_fd
= open(playlist
->control_filename
,
426 O_CREAT
|O_RDWR
|O_TRUNC
);
427 if (playlist
->control_fd
< 0)
430 playlist
->filename
[playlist
->dirlen
-1] = '\0';
432 /* cannot call update_control() because of mutex */
433 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
434 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
436 playlist
->filename
[playlist
->dirlen
-1] = c
;
446 playlist
->shuffle_modified
= false;
447 playlist
->deleted
= false;
448 playlist
->num_inserted_tracks
= 0;
450 if (playlist
->current
)
452 global_status
.resume_seed
= -1;
456 for (i
=0; i
<playlist
->amount
; i
++)
458 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
460 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
461 char inserted_file
[MAX_PATH
+1];
463 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
465 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
467 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
468 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
471 /* save the position in file where name is written */
472 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
474 result
= fdprintf(playlist
->control_fd
, "%s\n",
477 playlist
->indices
[i
] =
478 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
484 playlist
->num_inserted_tracks
++;
490 fsync(playlist
->control_fd
);
499 * store directory and name of playlist file
501 static void update_playlist_filename(struct playlist_info
* playlist
,
502 const char *dir
, const char *file
)
505 int dirlen
= strlen(dir
);
507 playlist
->utf8
= is_m3u8(file
);
509 /* If the dir does not end in trailing slash, we use a separator.
510 Otherwise we don't. */
511 if('/' != dir
[dirlen
-1])
517 playlist
->dirlen
= dirlen
;
519 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
520 "%s%s%s", dir
, sep
, file
);
524 * calculate track offsets within a playlist file
526 static int add_indices_to_playlist(struct playlist_info
* playlist
,
527 char* buffer
, size_t buflen
)
531 unsigned int count
= 0;
536 if(-1 == playlist
->fd
)
537 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
539 return -1; /* failure */
541 gui_syncsplash(0, ID2P(LANG_WAIT
));
545 /* use mp3 buffer for maximum load speed */
547 #if CONFIG_CODEC != SWCODEC
548 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
549 buflen
= (audiobufend
- audiobuf
);
550 buffer
= (char *)audiobuf
;
552 buffer
= (char *)audio_get_buffer(false, &buflen
);
560 nread
= read(playlist
->fd
, buffer
, buflen
);
561 /* Terminate on EOF */
565 p
= (unsigned char *)buffer
;
567 /* utf8 BOM at beginning of file? */
568 if(i
== 0 && is_utf8_bom(p
, nread
)) {
572 playlist
->utf8
= true; /* Override any earlier indication. */
575 for(count
=0; count
< nread
; count
++,p
++) {
577 /* Are we on a new line? */
578 if((*p
== '\n') || (*p
== '\r'))
588 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
589 display_buffer_full();
594 /* Store a new entry */
595 playlist
->indices
[ playlist
->amount
] = i
+count
;
597 if (playlist
->filenames
)
598 playlist
->filenames
[ playlist
->amount
] = NULL
;
610 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
617 * Utility function to create a new playlist, fill it with the next or
618 * previous directory, shuffle it if needed, and start playback.
619 * If play_last is true and direction zero or negative, start playing
620 * the last file in the directory, otherwise start playing the first.
622 static int create_and_play_dir(int direction
, bool play_last
)
624 char dir
[MAX_PATH
+ 1];
629 res
= get_next_directory(dir
);
631 res
= get_previous_directory(dir
);
635 if (playlist_create(dir
, NULL
) != -1)
637 ft_build_playlist(tree_get_context(), 0);
639 if (global_settings
.playlist_shuffle
)
640 playlist_shuffle(current_tick
, -1);
642 if (play_last
&& direction
<= 0)
643 index
= current_playlist
.amount
- 1;
647 #if (CONFIG_CODEC != SWCODEC)
648 playlist_start(index
, 0);
652 /* we've overwritten the dircache when getting the next/previous dir,
653 so the tree browser context will need to be reloaded */
661 * Removes all tracks, from the playlist, leaving the presently playing
664 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
668 if (playlist
== NULL
)
669 playlist
= ¤t_playlist
;
671 while (playlist
->index
> 0)
672 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
675 while (playlist
->amount
> 1)
676 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
679 if (playlist
->amount
== 1) {
680 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
688 * Add track to playlist at specified position. There are five special
689 * positions that can be specified:
690 * PLAYLIST_PREPEND - Add track at beginning of playlist
691 * PLAYLIST_INSERT - Add track after current song. NOTE: If
692 * there are already inserted tracks then track
693 * is added to the end of the insertion list
694 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
695 * matter what other tracks have been inserted
696 * PLAYLIST_INSERT_LAST - Add track to end of playlist
697 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
698 * current playing track and end of playlist
699 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
700 * and inster this track at the end.
702 static int add_track_to_playlist(struct playlist_info
* playlist
,
703 const char *filename
, int position
,
704 bool queue
, int seek_pos
)
706 int insert_position
, orig_position
;
707 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
710 insert_position
= orig_position
= position
;
712 if (playlist
->amount
>= playlist
->max_playlist_size
)
714 display_buffer_full();
720 case PLAYLIST_PREPEND
:
721 position
= insert_position
= playlist
->first_index
;
723 case PLAYLIST_INSERT
:
724 /* if there are already inserted tracks then add track to end of
725 insertion list else add after current playing track */
726 if (playlist
->last_insert_pos
>= 0 &&
727 playlist
->last_insert_pos
< playlist
->amount
&&
728 (playlist
->indices
[playlist
->last_insert_pos
]&
729 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
730 position
= insert_position
= playlist
->last_insert_pos
+1;
731 else if (playlist
->amount
> 0)
732 position
= insert_position
= playlist
->index
+ 1;
734 position
= insert_position
= 0;
736 if (playlist
->started
)
737 playlist
->last_insert_pos
= position
;
739 case PLAYLIST_INSERT_FIRST
:
740 if (playlist
->amount
> 0)
741 position
= insert_position
= playlist
->index
+ 1;
743 position
= insert_position
= 0;
745 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
746 playlist
->last_insert_pos
= position
;
748 case PLAYLIST_INSERT_LAST
:
749 if (playlist
->first_index
> 0)
750 position
= insert_position
= playlist
->first_index
;
752 position
= insert_position
= playlist
->amount
;
754 case PLAYLIST_INSERT_SHUFFLED
:
756 if (playlist
->started
)
759 int n
= playlist
->amount
-
760 rotate_index(playlist
, playlist
->index
);
767 position
= playlist
->index
+ offset
+ 1;
768 if (position
>= playlist
->amount
)
769 position
-= playlist
->amount
;
771 insert_position
= position
;
774 position
= insert_position
= (rand() % (playlist
->amount
+1));
777 case PLAYLIST_REPLACE
:
778 if (playlist_remove_all_tracks(playlist
) < 0)
781 position
= insert_position
= playlist
->index
+ 1;
786 flags
|= PLAYLIST_QUEUED
;
788 /* shift indices so that track can be added */
789 for (i
=playlist
->amount
; i
>insert_position
; i
--)
791 playlist
->indices
[i
] = playlist
->indices
[i
-1];
793 if (playlist
->filenames
)
794 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
798 /* update stored indices if needed */
799 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
803 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
804 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
806 playlist
->first_index
++;
808 if (seek_pos
< 0 && playlist
->current
)
810 global_status
.resume_first_index
= playlist
->first_index
;
815 if (insert_position
< playlist
->last_insert_pos
||
816 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
817 playlist
->last_insert_pos
++;
819 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
821 int result
= update_control(playlist
,
822 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
823 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
829 playlist
->indices
[insert_position
] = flags
| seek_pos
;
832 if (playlist
->filenames
)
833 playlist
->filenames
[insert_position
] = NULL
;
837 playlist
->num_inserted_tracks
++;
839 return insert_position
;
843 * Callback for playlist_directory_tracksearch to insert track into
846 static int directory_search_callback(char* filename
, void* context
)
848 struct directory_search_context
* c
=
849 (struct directory_search_context
*) context
;
852 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
860 /* Make sure tracks are inserted in correct order if user requests
862 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
863 c
->position
= insert_pos
+ 1;
865 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
867 unsigned char* count_str
;
870 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
872 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
874 display_playlist_count(c
->count
, count_str
, false);
876 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
877 (audio_status() & AUDIO_STATUS_PLAY
) &&
878 c
->playlist
->started
)
879 audio_flush_and_reload_tracks();
886 * remove track at specified position
888 static int remove_track_from_playlist(struct playlist_info
* playlist
,
889 int position
, bool write
)
894 if (playlist
->amount
<= 0)
897 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
899 /* shift indices now that track has been removed */
900 for (i
=position
; i
<playlist
->amount
; i
++)
902 playlist
->indices
[i
] = playlist
->indices
[i
+1];
904 if (playlist
->filenames
)
905 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
912 playlist
->num_inserted_tracks
--;
914 playlist
->deleted
= true;
916 /* update stored indices if needed */
917 if (position
< playlist
->index
)
920 if (position
< playlist
->first_index
)
922 playlist
->first_index
--;
926 global_status
.resume_first_index
= playlist
->first_index
;
931 if (position
<= playlist
->last_insert_pos
)
932 playlist
->last_insert_pos
--;
934 if (write
&& playlist
->control_fd
>= 0)
936 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
937 position
, -1, NULL
, NULL
, NULL
);
942 sync_control(playlist
, false);
949 * randomly rearrange the array of indices for the playlist. If start_current
950 * is true then update the index to the new index of the current playing track
952 static int randomise_playlist(struct playlist_info
* playlist
,
953 unsigned int seed
, bool start_current
,
959 unsigned int current
= playlist
->indices
[playlist
->index
];
961 /* seed 0 is used to identify sorted playlist for resume purposes */
965 /* seed with the given seed */
968 /* randomise entire indices list */
969 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
971 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
972 candidate
= rand() % (count
+ 1);
974 /* now swap the values at the 'count' and 'candidate' positions */
975 store
= playlist
->indices
[candidate
];
976 playlist
->indices
[candidate
] = playlist
->indices
[count
];
977 playlist
->indices
[count
] = store
;
979 if (playlist
->filenames
)
981 store
= (long)playlist
->filenames
[candidate
];
982 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
983 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
989 find_and_set_playlist_index(playlist
, current
);
991 /* indices have been moved so last insert position is no longer valid */
992 playlist
->last_insert_pos
= -1;
994 playlist
->seed
= seed
;
995 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
996 playlist
->shuffle_modified
= true;
1000 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
1001 playlist
->first_index
, NULL
, NULL
, NULL
);
1002 global_status
.resume_seed
= seed
;
1010 * Sort the array of indices for the playlist. If start_current is true then
1011 * set the index to the new index of the current song.
1013 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
1016 unsigned int current
= playlist
->indices
[playlist
->index
];
1018 if (playlist
->amount
> 0)
1019 qsort(playlist
->indices
, playlist
->amount
,
1020 sizeof(playlist
->indices
[0]), compare
);
1022 #ifdef HAVE_DIRCACHE
1023 /** We need to re-check the song names from disk because qsort can't
1024 * sort two arrays at once :/
1025 * FIXME: Please implement a better way to do this. */
1026 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
1027 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1031 find_and_set_playlist_index(playlist
, current
);
1033 /* indices have been moved so last insert position is no longer valid */
1034 playlist
->last_insert_pos
= -1;
1036 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1037 playlist
->shuffle_modified
= false;
1038 if (write
&& playlist
->control_fd
>= 0)
1040 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1041 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1042 global_status
.resume_seed
= 0;
1049 /* Calculate how many steps we have to really step when skipping entries
1052 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1054 int i
, count
, direction
;
1056 int stepped_count
= 0;
1069 index
= playlist
->index
;
1072 /* Boundary check */
1074 index
+= playlist
->amount
;
1075 if (index
>= playlist
->amount
)
1076 index
-= playlist
->amount
;
1078 /* Check if we found a bad entry. */
1079 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1082 /* Are all entries bad? */
1083 if (stepped_count
++ > playlist
->amount
)
1090 } while (i
<= count
);
1095 /* Marks the index of the track to be skipped that is "steps" away from
1096 * current playing track.
1098 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1102 if (playlist
== NULL
)
1103 playlist
= ¤t_playlist
;
1105 /* need to account for already skipped tracks */
1106 steps
= calculate_step_count(playlist
, steps
);
1108 index
= playlist
->index
+ steps
;
1110 index
+= playlist
->amount
;
1111 else if (index
>= playlist
->amount
)
1112 index
-= playlist
->amount
;
1114 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1118 * returns the index of the track that is "steps" away from current playing
1121 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1124 int current_index
= playlist
->index
;
1125 int next_index
= -1;
1127 if (playlist
->amount
<= 0)
1130 if (repeat_mode
== -1)
1131 repeat_mode
= global_settings
.repeat_mode
;
1133 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1134 repeat_mode
= REPEAT_ALL
;
1136 steps
= calculate_step_count(playlist
, steps
);
1137 switch (repeat_mode
)
1139 case REPEAT_SHUFFLE
:
1140 /* Treat repeat shuffle just like repeat off. At end of playlist,
1141 play will be resumed in playlist_next() */
1144 current_index
= rotate_index(playlist
, current_index
);
1145 next_index
= current_index
+steps
;
1146 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1149 next_index
= (next_index
+playlist
->first_index
) %
1156 #ifdef AB_REPEAT_ENABLE
1159 next_index
= current_index
;
1165 next_index
= (current_index
+steps
) % playlist
->amount
;
1166 while (next_index
< 0)
1167 next_index
+= playlist
->amount
;
1169 if (steps
>= playlist
->amount
)
1176 /* second time around so skip the queued files */
1177 for (i
=0; i
<playlist
->amount
; i
++)
1179 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1180 index
= (index
+1) % playlist
->amount
;
1192 /* No luck if the whole playlist was bad. */
1193 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1200 * Search for the seek track and set appropriate indices. Used after shuffle
1201 * to make sure the current index is still pointing to correct track.
1203 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1208 /* Set the index to the current song */
1209 for (i
=0; i
<playlist
->amount
; i
++)
1211 if (playlist
->indices
[i
] == seek
)
1213 playlist
->index
= playlist
->first_index
= i
;
1215 if (playlist
->current
)
1217 global_status
.resume_first_index
= i
;
1227 * used to sort track indices. Sort order is as follows:
1228 * 1. Prepended tracks (in prepend order)
1229 * 2. Playlist/directory tracks (in playlist order)
1230 * 3. Inserted/Appended tracks (in insert order)
1232 static int compare(const void* p1
, const void* p2
)
1234 unsigned long* e1
= (unsigned long*) p1
;
1235 unsigned long* e2
= (unsigned long*) p2
;
1236 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1237 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1239 if (flags1
== flags2
)
1240 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1241 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1242 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1244 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1245 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1247 else if (flags1
&& flags2
)
1248 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1253 #ifdef HAVE_DIRCACHE
1255 * Thread to update filename pointers to dircache on background
1256 * without affecting playlist load up performance. This thread also flushes
1257 * any pending control commands when the disk spins up.
1259 static bool playlist_flush_callback(void)
1261 struct playlist_info
*playlist
;
1262 playlist
= ¤t_playlist
;
1263 if (playlist
->control_fd
>= 0)
1265 if (playlist
->num_cached
> 0)
1267 mutex_lock(&playlist
->control_mutex
);
1268 flush_cached_control(playlist
);
1269 mutex_unlock(&playlist
->control_mutex
);
1271 sync_control(playlist
, true);
1276 static void playlist_thread(void)
1278 struct queue_event ev
;
1279 bool dirty_pointers
= false;
1280 static char tmp
[MAX_PATH
+1];
1282 struct playlist_info
*playlist
;
1289 #ifndef HAVE_FLASH_STORAGE
1290 if (global_settings
.disk_spindown
> 1 &&
1291 global_settings
.disk_spindown
<= 5)
1292 sleep_time
= global_settings
.disk_spindown
- 1;
1297 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1301 case PLAYLIST_LOAD_POINTERS
:
1302 dirty_pointers
= true;
1305 /* Start the background scanning after either the disk spindown
1306 timeout or 5s, whichever is less */
1308 playlist
= ¤t_playlist
;
1309 if (playlist
->control_fd
>= 0)
1311 if (playlist
->num_cached
> 0)
1312 register_ata_idle_func(playlist_flush_callback
);
1315 if (!dirty_pointers
)
1318 if (!dircache_is_enabled() || !playlist
->filenames
1319 || playlist
->amount
<= 0)
1322 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1325 for (index
= 0; index
< playlist
->amount
1326 && queue_empty(&playlist_queue
); index
++)
1328 /* Process only pointers that are not already loaded. */
1329 if (playlist
->filenames
[index
])
1332 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1333 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1335 /* Load the filename from playlist file. */
1336 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1340 /* Set the dircache entry pointer. */
1341 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1343 /* And be on background so user doesn't notice any delays. */
1347 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1350 dirty_pointers
= false;
1354 case SYS_USB_CONNECTED
:
1355 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1356 usb_wait_for_disconnect(&playlist_queue
);
1365 * gets pathname for track at seek index
1367 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1368 bool control_file
, char *buf
, int buf_length
)
1372 char tmp_buf
[MAX_PATH
+1];
1373 char dir_buf
[MAX_PATH
+1];
1374 bool utf8
= playlist
->utf8
;
1376 if (buf_length
> MAX_PATH
+1)
1377 buf_length
= MAX_PATH
+1;
1379 #ifdef HAVE_DIRCACHE
1380 if (dircache_is_enabled() && playlist
->filenames
)
1382 if (playlist
->filenames
[index
] != NULL
)
1384 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1385 max
= strlen(tmp_buf
) + 1;
1392 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1394 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1395 tmp_buf
[MAX_PATH
] = '\0';
1396 max
= strlen(tmp_buf
) + 1;
1400 mutex_lock(&playlist
->control_mutex
);
1404 fd
= playlist
->control_fd
;
1409 if(-1 == playlist
->fd
)
1410 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1418 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1422 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1424 if ((max
> 0) && !utf8
)
1426 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1427 * be as large as tmp_buf.
1429 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1434 mutex_unlock(&playlist
->control_mutex
);
1439 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1441 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1447 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1448 dir_buf
[playlist
->dirlen
-1] = 0;
1450 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1453 static int get_next_directory(char *dir
){
1454 return get_next_dir(dir
,true,false);
1457 static int get_previous_directory(char *dir
){
1458 return get_next_dir(dir
,false,false);
1462 * search through all the directories (starting with the current) to find
1463 * one that has tracks to play
1465 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1467 struct playlist_info
* playlist
= ¤t_playlist
;
1469 int sort_dir
= global_settings
.sort_dir
;
1470 char *start_dir
= NULL
;
1472 struct tree_context
* tc
= tree_get_context();
1473 int dirfilter
= *(tc
->dirfilter
);
1475 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1477 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1478 char buffer
[MAX_PATH
];
1479 int folder_count
= 0,i
;
1480 srand(current_tick
);
1481 *(tc
->dirfilter
) = SHOW_MUSIC
;
1484 read(fd
,&folder_count
,sizeof(int));
1487 i
= rand()%folder_count
;
1488 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1489 read(fd
,buffer
,MAX_PATH
);
1490 if (check_subdir_for_music(buffer
,"") ==0)
1495 *(tc
->dirfilter
) = dirfilter
;
1500 /* not random folder advance */
1502 /* start with root */
1506 /* start with current directory */
1507 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1508 dir
[playlist
->dirlen
-1] = '\0';
1511 /* use the tree browser dircache to load files */
1512 *(tc
->dirfilter
) = SHOW_ALL
;
1514 /* sort in another direction if previous dir is requested */
1516 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1517 global_settings
.sort_dir
= 4;
1518 else if (global_settings
.sort_dir
== 1)
1519 global_settings
.sort_dir
= 2;
1520 else if (global_settings
.sort_dir
== 2)
1521 global_settings
.sort_dir
= 1;
1522 else if (global_settings
.sort_dir
== 4)
1523 global_settings
.sort_dir
= 0;
1528 struct entry
*files
;
1532 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1534 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1540 files
= (struct entry
*) tc
->dircache
;
1541 num_files
= tc
->filesindir
;
1543 for (i
=0; i
<num_files
; i
++)
1546 if (action_userabort(TIMEOUT_NOBLOCK
))
1553 if (files
[i
].attr
& ATTR_DIRECTORY
)
1557 result
= check_subdir_for_music(dir
, files
[i
].name
);
1564 else if (!strcmp(start_dir
, files
[i
].name
))
1571 /* move down to parent directory. current directory name is
1572 stored as the starting point for the search in parent */
1573 start_dir
= strrchr(dir
, '/');
1584 /* restore dirfilter & sort_dir */
1585 *(tc
->dirfilter
) = dirfilter
;
1586 global_settings
.sort_dir
= sort_dir
;
1588 /* special case if nothing found: try start searching again from root */
1589 if (result
== -1 && !recursion
){
1590 result
= get_next_dir(dir
,is_forward
, true);
1597 * Checks if there are any music files in the dir or any of its
1598 * subdirectories. May be called recursively.
1600 static int check_subdir_for_music(char *dir
, char *subdir
)
1603 int dirlen
= strlen(dir
);
1606 struct entry
*files
;
1607 bool has_music
= false;
1608 bool has_subdir
= false;
1609 struct tree_context
* tc
= tree_get_context();
1611 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1613 if (ft_load(tc
, dir
) < 0)
1615 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1619 files
= (struct entry
*) tc
->dircache
;
1620 num_files
= tc
->filesindir
;
1622 for (i
=0; i
<num_files
; i
++)
1624 if (files
[i
].attr
& ATTR_DIRECTORY
)
1626 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1638 for (i
=0; i
<num_files
; i
++)
1640 if (action_userabort(TIMEOUT_NOBLOCK
))
1646 if (files
[i
].attr
& ATTR_DIRECTORY
)
1648 result
= check_subdir_for_music(dir
, files
[i
].name
);
1666 /* we now need to reload our current directory */
1667 if(ft_load(tc
, dir
) < 0)
1668 gui_syncsplash(HZ
*2,
1669 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1676 * Returns absolute path of track
1678 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1685 /* Zero-terminate the file name */
1686 while((src
[i
] != '\n') &&
1691 /* Now work back killing white space */
1692 while((src
[i
-1] == ' ') ||
1698 /* replace backslashes with forward slashes */
1699 for ( j
=0; j
<i
; j
++ )
1700 if ( src
[j
] == '\\' )
1705 strncpy(dest
, src
, buf_length
);
1709 /* handle dos style drive letter */
1711 strncpy(dest
, &src
[2], buf_length
);
1712 else if (!strncmp(src
, "../", 3))
1714 /* handle relative paths */
1716 while(!strncmp(&src
[i
], "../", 3))
1718 for (j
=0; j
<i
/3; j
++) {
1719 temp_ptr
= strrchr(dir
, '/');
1725 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1727 else if ( '.' == src
[0] && '/' == src
[1] ) {
1728 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1731 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1739 * Display splash message showing progress of playlist/directory insertion or
1742 static void display_playlist_count(int count
, const unsigned char *fmt
,
1745 static long talked_tick
= 0;
1746 long id
= P2ID(fmt
);
1747 if(global_settings
.talk_menu
&& id
>=0)
1749 if(final
|| (count
&& (talked_tick
== 0
1750 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1752 talked_tick
= current_tick
;
1753 talk_number(count
, false);
1759 lcd_clear_display();
1761 #ifdef HAVE_LCD_BITMAP
1762 if(global_settings
.statusbar
)
1763 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1765 lcd_setmargins(0, 0);
1768 gui_syncsplash(0, fmt
, count
, str(LANG_OFF_ABORT
));
1772 * Display buffer full message
1774 static void display_buffer_full(void)
1776 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1780 * Flush any cached control commands to disk. Called when playlist is being
1781 * modified. Returns 0 on success and -1 on failure.
1783 static int flush_cached_control(struct playlist_info
* playlist
)
1788 if (!playlist
->num_cached
)
1791 lseek(playlist
->control_fd
, 0, SEEK_END
);
1793 for (i
=0; i
<playlist
->num_cached
; i
++)
1795 struct playlist_control_cache
* cache
=
1796 &(playlist
->control_cache
[i
]);
1798 switch (cache
->command
)
1800 case PLAYLIST_COMMAND_PLAYLIST
:
1801 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1802 cache
->i1
, cache
->s1
, cache
->s2
);
1804 case PLAYLIST_COMMAND_ADD
:
1805 case PLAYLIST_COMMAND_QUEUE
:
1806 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1807 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1808 cache
->i1
, cache
->i2
);
1811 /* save the position in file where name is written */
1812 int* seek_pos
= (int *)cache
->data
;
1813 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1814 result
= fdprintf(playlist
->control_fd
, "%s\n",
1818 case PLAYLIST_COMMAND_DELETE
:
1819 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1821 case PLAYLIST_COMMAND_SHUFFLE
:
1822 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1823 cache
->i1
, cache
->i2
);
1825 case PLAYLIST_COMMAND_UNSHUFFLE
:
1826 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1828 case PLAYLIST_COMMAND_RESET
:
1829 result
= fdprintf(playlist
->control_fd
, "R\n");
1841 if (global_status
.resume_seed
>= 0)
1843 global_status
.resume_seed
= -1;
1847 playlist
->num_cached
= 0;
1848 playlist
->pending_control_sync
= true;
1855 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1862 * Update control data with new command. Depending on the command, it may be
1863 * cached or flushed to disk.
1865 static int update_control(struct playlist_info
* playlist
,
1866 enum playlist_command command
, int i1
, int i2
,
1867 const char* s1
, const char* s2
, void* data
)
1870 struct playlist_control_cache
* cache
;
1873 mutex_lock(&playlist
->control_mutex
);
1875 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1877 cache
->command
= command
;
1886 case PLAYLIST_COMMAND_PLAYLIST
:
1887 case PLAYLIST_COMMAND_ADD
:
1888 case PLAYLIST_COMMAND_QUEUE
:
1889 #ifndef HAVE_DIRCACHE
1890 case PLAYLIST_COMMAND_DELETE
:
1891 case PLAYLIST_COMMAND_RESET
:
1895 case PLAYLIST_COMMAND_SHUFFLE
:
1896 case PLAYLIST_COMMAND_UNSHUFFLE
:
1898 /* only flush when needed */
1902 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1903 result
= flush_cached_control(playlist
);
1905 mutex_unlock(&playlist
->control_mutex
);
1911 * sync control file to disk
1913 static void sync_control(struct playlist_info
* playlist
, bool force
)
1915 #ifdef HAVE_DIRCACHE
1916 if (playlist
->started
&& force
)
1920 if (playlist
->started
)
1923 if (playlist
->pending_control_sync
)
1925 mutex_lock(&playlist
->control_mutex
);
1926 fsync(playlist
->control_fd
);
1927 playlist
->pending_control_sync
= false;
1928 mutex_unlock(&playlist
->control_mutex
);
1934 * Rotate indices such that first_index is index 0
1936 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1938 index
-= playlist
->first_index
;
1940 index
+= playlist
->amount
;
1946 * Initialize playlist entries at startup
1948 void playlist_init(void)
1950 struct playlist_info
* playlist
= ¤t_playlist
;
1952 playlist
->current
= true;
1953 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1954 "%s", PLAYLIST_CONTROL_FILE
);
1956 playlist
->control_fd
= -1;
1957 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1958 playlist
->indices
= buffer_alloc(
1959 playlist
->max_playlist_size
* sizeof(int));
1960 playlist
->buffer_size
=
1961 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1962 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1963 mutex_init(&playlist
->control_mutex
);
1964 empty_playlist(playlist
, true);
1966 #ifdef HAVE_DIRCACHE
1967 playlist
->filenames
= buffer_alloc(
1968 playlist
->max_playlist_size
* sizeof(int));
1969 memset(playlist
->filenames
, 0,
1970 playlist
->max_playlist_size
* sizeof(int));
1971 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1972 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1974 queue_init(&playlist_queue
, true);
1979 * Clean playlist at shutdown
1981 void playlist_shutdown(void)
1983 struct playlist_info
* playlist
= ¤t_playlist
;
1985 if (playlist
->control_fd
>= 0)
1987 mutex_lock(&playlist
->control_mutex
);
1989 if (playlist
->num_cached
> 0)
1990 flush_cached_control(playlist
);
1992 close(playlist
->control_fd
);
1994 mutex_unlock(&playlist
->control_mutex
);
1999 * Create new playlist
2001 int playlist_create(const char *dir
, const char *file
)
2003 struct playlist_info
* playlist
= ¤t_playlist
;
2005 new_playlist(playlist
, dir
, file
);
2008 /* load the playlist file */
2009 add_indices_to_playlist(playlist
, NULL
, 0);
2014 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2017 * Restore the playlist state based on control file commands. Called to
2018 * resume playback after shutdown.
2020 int playlist_resume(void)
2022 struct playlist_info
* playlist
= ¤t_playlist
;
2027 int control_file_size
= 0;
2031 /* use mp3 buffer for maximum load speed */
2032 #if CONFIG_CODEC != SWCODEC
2033 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2034 buflen
= (audiobufend
- audiobuf
);
2035 buffer
= (char *)audiobuf
;
2037 buffer
= (char *)audio_get_buffer(false, &buflen
);
2040 empty_playlist(playlist
, true);
2042 gui_syncsplash(0, ID2P(LANG_WAIT
));
2043 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2044 if (playlist
->control_fd
< 0)
2046 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2049 playlist
->control_created
= true;
2051 control_file_size
= filesize(playlist
->control_fd
);
2052 if (control_file_size
<= 0)
2054 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2058 /* read a small amount first to get the header */
2059 nread
= read(playlist
->control_fd
, buffer
,
2060 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2063 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2067 playlist
->started
= true;
2073 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2074 int last_newline
= 0;
2076 bool newline
= true;
2077 bool exit_loop
= false;
2082 unsigned long last_tick
= current_tick
;
2084 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
2086 /* So a splash while we are loading. */
2087 if (current_tick
- last_tick
> HZ
/4)
2089 gui_syncsplash(0, str(LANG_LOADING_PERCENT
),
2090 (total_read
+count
)*100/control_file_size
,
2091 str(LANG_OFF_ABORT
));
2092 if (action_userabort(TIMEOUT_NOBLOCK
))
2095 * Not sure how to implement this, somebody more familiar
2096 * with the code, please fix this. */
2098 last_tick
= current_tick
;
2101 /* Are we on a new line? */
2102 if((*p
== '\n') || (*p
== '\r'))
2106 /* save last_newline in case we need to load more data */
2107 last_newline
= count
;
2109 switch (current_command
)
2111 case PLAYLIST_COMMAND_PLAYLIST
:
2113 /* str1=version str2=dir str3=file */
2129 version
= atoi(str1
);
2131 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2134 update_playlist_filename(playlist
, str2
, str3
);
2136 if (str3
[0] != '\0')
2138 /* NOTE: add_indices_to_playlist() overwrites the
2139 audiobuf so we need to reload control file
2141 add_indices_to_playlist(playlist
, NULL
, 0);
2143 else if (str2
[0] != '\0')
2145 playlist
->in_ram
= true;
2146 resume_directory(str2
);
2149 /* load the rest of the data */
2155 case PLAYLIST_COMMAND_ADD
:
2156 case PLAYLIST_COMMAND_QUEUE
:
2158 /* str1=position str2=last_position str3=file */
2159 int position
, last_position
;
2162 if (!str1
|| !str2
|| !str3
)
2169 position
= atoi(str1
);
2170 last_position
= atoi(str2
);
2172 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2175 /* seek position is based on str3's position in
2177 if (add_track_to_playlist(playlist
, str3
, position
,
2178 queue
, total_read
+(str3
-buffer
)) < 0)
2181 playlist
->last_insert_pos
= last_position
;
2185 case PLAYLIST_COMMAND_DELETE
:
2197 position
= atoi(str1
);
2199 if (remove_track_from_playlist(playlist
, position
,
2205 case PLAYLIST_COMMAND_SHUFFLE
:
2207 /* str1=seed str2=first_index */
2219 /* Always sort list before shuffling */
2220 sort_playlist(playlist
, false, false);
2224 playlist
->first_index
= atoi(str2
);
2226 if (randomise_playlist(playlist
, seed
, false,
2233 case PLAYLIST_COMMAND_UNSHUFFLE
:
2235 /* str1=first_index */
2243 playlist
->first_index
= atoi(str1
);
2245 if (sort_playlist(playlist
, false, false) < 0)
2251 case PLAYLIST_COMMAND_RESET
:
2253 playlist
->last_insert_pos
= -1;
2256 case PLAYLIST_COMMAND_COMMENT
:
2263 /* to ignore any extra newlines */
2264 current_command
= PLAYLIST_COMMAND_COMMENT
;
2270 /* first non-comment line must always specify playlist */
2271 if (first
&& *p
!= 'P' && *p
!= '#')
2281 /* playlist can only be specified once */
2289 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2292 current_command
= PLAYLIST_COMMAND_ADD
;
2295 current_command
= PLAYLIST_COMMAND_QUEUE
;
2298 current_command
= PLAYLIST_COMMAND_DELETE
;
2301 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2304 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2307 current_command
= PLAYLIST_COMMAND_RESET
;
2310 current_command
= PLAYLIST_COMMAND_COMMENT
;
2323 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2325 /* all control file strings are separated with a colon.
2326 Replace the colon with 0 to get proper strings that can be
2327 used by commands above */
2333 if ((count
+1) < nread
)
2347 /* allow last string to contain colons */
2358 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2362 if (!newline
|| (exit_loop
&& count
<nread
))
2364 if ((total_read
+ count
) >= control_file_size
)
2366 /* no newline at end of control file */
2367 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2371 /* We didn't end on a newline or we exited loop prematurely.
2372 Either way, re-read the remainder. */
2373 count
= last_newline
;
2374 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2377 total_read
+= count
;
2380 /* still looking for header */
2381 nread
= read(playlist
->control_fd
, buffer
,
2382 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2384 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2386 /* Terminate on EOF */
2389 if (global_status
.resume_seed
>= 0)
2391 /* Apply shuffle command saved in settings */
2392 if (global_status
.resume_seed
== 0)
2393 sort_playlist(playlist
, false, true);
2397 sort_playlist(playlist
, false, false);
2399 randomise_playlist(playlist
, global_status
.resume_seed
,
2404 playlist
->first_index
= global_status
.resume_first_index
;
2409 #ifdef HAVE_DIRCACHE
2410 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2417 * Add track to in_ram playlist. Used when playing directories.
2419 int playlist_add(const char *filename
)
2421 struct playlist_info
* playlist
= ¤t_playlist
;
2422 int len
= strlen(filename
);
2424 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2425 (playlist
->amount
>= playlist
->max_playlist_size
))
2427 display_buffer_full();
2431 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2432 #ifdef HAVE_DIRCACHE
2433 playlist
->filenames
[playlist
->amount
] = NULL
;
2437 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2438 playlist
->buffer_end_pos
+= len
;
2439 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2444 /* shuffle newly created playlist using random seed. */
2445 int playlist_shuffle(int random_seed
, int start_index
)
2447 struct playlist_info
* playlist
= ¤t_playlist
;
2449 unsigned int seek_pos
= 0;
2450 bool start_current
= false;
2452 if (start_index
>= 0 && global_settings
.play_selected
)
2454 /* store the seek position before the shuffle */
2455 seek_pos
= playlist
->indices
[start_index
];
2456 playlist
->index
= global_status
.resume_first_index
=
2457 playlist
->first_index
= start_index
;
2458 start_current
= true;
2461 randomise_playlist(playlist
, random_seed
, start_current
, true);
2463 return playlist
->index
;
2466 /* start playing current playlist at specified index/offset */
2467 int playlist_start(int start_index
, int offset
)
2469 struct playlist_info
* playlist
= ¤t_playlist
;
2471 /* Cancel FM radio selection as previous music. For cases where we start
2472 playback without going to the WPS, such as playlist insert.. or
2473 playlist catalog. */
2474 previous_music_is_wps();
2476 playlist
->index
= start_index
;
2478 #if CONFIG_CODEC != SWCODEC
2479 talk_buffer_steal(); /* will use the mp3 buffer */
2482 playlist
->started
= true;
2483 sync_control(playlist
, false);
2489 /* Returns false if 'steps' is out of bounds, else true */
2490 bool playlist_check(int steps
)
2492 struct playlist_info
* playlist
= ¤t_playlist
;
2494 /* always allow folder navigation */
2495 if (global_settings
.next_folder
&& playlist
->in_ram
)
2498 int index
= get_next_index(playlist
, steps
, -1);
2500 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2501 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2503 return (index
>= 0);
2506 /* get trackname of track that is "steps" away from current playing track.
2507 NULL is used to identify end of playlist */
2508 char* playlist_peek(int steps
)
2510 struct playlist_info
* playlist
= ¤t_playlist
;
2516 index
= get_next_index(playlist
, steps
, -1);
2520 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2521 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2523 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2527 temp_ptr
= now_playing
;
2529 if (!playlist
->in_ram
|| control_file
)
2531 /* remove bogus dirs from beginning of path
2532 (workaround for buggy playlist creation tools) */
2535 if (file_exists(temp_ptr
))
2538 temp_ptr
= strchr(temp_ptr
+1, '/');
2543 /* Even though this is an invalid file, we still need to pass a
2544 file name to the caller because NULL is used to indicate end
2554 * Update indices as track has changed
2556 int playlist_next(int steps
)
2558 struct playlist_info
* playlist
= ¤t_playlist
;
2562 #ifdef AB_REPEAT_ENABLE
2563 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2565 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2569 /* We need to delete all the queued songs */
2570 for (i
=0, j
=steps
; i
<j
; i
++)
2572 index
= get_next_index(playlist
, i
, -1);
2574 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2576 remove_track_from_playlist(playlist
, index
, true);
2577 steps
--; /* one less track */
2582 index
= get_next_index(playlist
, steps
, -1);
2586 /* end of playlist... or is it */
2587 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2588 playlist
->amount
> 1)
2590 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2591 playlist
->first_index
= global_status
.resume_first_index
= 0;
2592 sort_playlist(playlist
, false, false);
2593 randomise_playlist(playlist
, current_tick
, false, true);
2594 #if CONFIG_CODEC != SWCODEC
2595 playlist_start(0, 0);
2597 playlist
->index
= 0;
2600 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2602 index
= create_and_play_dir(steps
, true);
2606 playlist
->index
= index
;
2613 playlist
->index
= index
;
2615 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2617 /* check to see if we've gone beyond the last inserted track */
2618 int cur
= rotate_index(playlist
, index
);
2619 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2623 /* reset last inserted track */
2624 playlist
->last_insert_pos
= -1;
2626 if (playlist
->control_fd
>= 0)
2628 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2629 -1, -1, NULL
, NULL
, NULL
);
2634 sync_control(playlist
, false);
2642 /* try playing next or previous folder */
2643 bool playlist_next_dir(int direction
)
2645 /* not to mess up real playlists */
2646 if(!current_playlist
.in_ram
)
2649 return create_and_play_dir(direction
, false) >= 0;
2652 /* Get resume info for current playing song. If return value is -1 then
2653 settings shouldn't be saved. */
2654 int playlist_get_resume_info(int *resume_index
)
2656 struct playlist_info
* playlist
= ¤t_playlist
;
2658 *resume_index
= playlist
->index
;
2663 /* Update resume info for current playing song. Returns -1 on error. */
2664 int playlist_update_resume_info(const struct mp3entry
* id3
)
2666 struct playlist_info
* playlist
= ¤t_playlist
;
2670 if (global_status
.resume_index
!= playlist
->index
||
2671 global_status
.resume_offset
!= id3
->offset
)
2673 global_status
.resume_index
= playlist
->index
;
2674 global_status
.resume_offset
= id3
->offset
;
2680 global_status
.resume_index
= -1;
2681 global_status
.resume_offset
= -1;
2688 /* Returns index of current playing track for display purposes. This value
2689 should not be used for resume purposes as it doesn't represent the actual
2690 index into the playlist */
2691 int playlist_get_display_index(void)
2693 struct playlist_info
* playlist
= ¤t_playlist
;
2695 /* first_index should always be index 0 for display purposes */
2696 int index
= rotate_index(playlist
, playlist
->index
);
2701 /* returns number of tracks in current playlist */
2702 int playlist_amount(void)
2704 return playlist_amount_ex(NULL
);
2708 * Create a new playlist If playlist is not NULL then we're loading a
2709 * playlist off disk for viewing/editing. The index_buffer is used to store
2710 * playlist indices (required for and only used if !current playlist). The
2711 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2713 int playlist_create_ex(struct playlist_info
* playlist
,
2714 const char* dir
, const char* file
,
2715 void* index_buffer
, int index_buffer_size
,
2716 void* temp_buffer
, int temp_buffer_size
)
2719 playlist
= ¤t_playlist
;
2722 /* Initialize playlist structure */
2723 int r
= rand() % 10;
2724 playlist
->current
= false;
2726 /* Use random name for control file */
2727 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2728 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2730 playlist
->control_fd
= -1;
2734 int num_indices
= index_buffer_size
/ sizeof(int);
2736 #ifdef HAVE_DIRCACHE
2739 if (num_indices
> global_settings
.max_files_in_playlist
)
2740 num_indices
= global_settings
.max_files_in_playlist
;
2742 playlist
->max_playlist_size
= num_indices
;
2743 playlist
->indices
= index_buffer
;
2744 #ifdef HAVE_DIRCACHE
2745 playlist
->filenames
= (const struct dircache_entry
**)
2746 &playlist
->indices
[num_indices
];
2751 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2752 playlist
->indices
= current_playlist
.indices
;
2753 #ifdef HAVE_DIRCACHE
2754 playlist
->filenames
= current_playlist
.filenames
;
2758 playlist
->buffer_size
= 0;
2759 playlist
->buffer
= NULL
;
2760 mutex_init(&playlist
->control_mutex
);
2763 new_playlist(playlist
, dir
, file
);
2766 /* load the playlist file */
2767 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2773 * Set the specified playlist as the current.
2774 * NOTE: You will get undefined behaviour if something is already playing so
2775 * remember to stop before calling this. Also, this call will
2776 * effectively close your playlist, making it unusable.
2778 int playlist_set_current(struct playlist_info
* playlist
)
2780 if (!playlist
|| (check_control(playlist
) < 0))
2783 empty_playlist(¤t_playlist
, false);
2785 strncpy(current_playlist
.filename
, playlist
->filename
,
2786 sizeof(current_playlist
.filename
));
2788 current_playlist
.utf8
= playlist
->utf8
;
2789 current_playlist
.fd
= playlist
->fd
;
2791 close(playlist
->control_fd
);
2792 close(current_playlist
.control_fd
);
2793 remove(current_playlist
.control_filename
);
2794 if (rename(playlist
->control_filename
,
2795 current_playlist
.control_filename
) < 0)
2797 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2799 if (current_playlist
.control_fd
< 0)
2801 current_playlist
.control_created
= true;
2803 current_playlist
.dirlen
= playlist
->dirlen
;
2805 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2807 memcpy(current_playlist
.indices
, playlist
->indices
,
2808 playlist
->max_playlist_size
*sizeof(int));
2809 #ifdef HAVE_DIRCACHE
2810 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2811 playlist
->max_playlist_size
*sizeof(int));
2815 current_playlist
.first_index
= playlist
->first_index
;
2816 current_playlist
.amount
= playlist
->amount
;
2817 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2818 current_playlist
.seed
= playlist
->seed
;
2819 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2820 current_playlist
.deleted
= playlist
->deleted
;
2821 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2823 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2824 sizeof(current_playlist
.control_cache
));
2825 current_playlist
.num_cached
= playlist
->num_cached
;
2826 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2832 * Close files and delete control file for non-current playlist.
2834 void playlist_close(struct playlist_info
* playlist
)
2839 if (playlist
->fd
>= 0)
2840 close(playlist
->fd
);
2842 if (playlist
->control_fd
>= 0)
2843 close(playlist
->control_fd
);
2845 if (playlist
->control_created
)
2846 remove(playlist
->control_filename
);
2849 void playlist_sync(struct playlist_info
* playlist
)
2852 playlist
= ¤t_playlist
;
2854 sync_control(playlist
, false);
2855 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2856 audio_flush_and_reload_tracks();
2858 #ifdef HAVE_DIRCACHE
2859 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2864 * Insert track into playlist at specified position (or one of the special
2865 * positions). Returns position where track was inserted or -1 if error.
2867 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2868 int position
, bool queue
, bool sync
)
2873 playlist
= ¤t_playlist
;
2875 if (check_control(playlist
) < 0)
2877 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2881 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2883 /* Check if we want manually sync later. For example when adding
2884 * bunch of files from tagcache, syncing after every file wouldn't be
2885 * a good thing to do. */
2886 if (sync
&& result
>= 0)
2887 playlist_sync(playlist
);
2893 * Insert all tracks from specified directory into playlist.
2895 int playlist_insert_directory(struct playlist_info
* playlist
,
2896 const char *dirname
, int position
, bool queue
,
2900 unsigned char *count_str
;
2901 struct directory_search_context context
;
2904 playlist
= ¤t_playlist
;
2906 if (check_control(playlist
) < 0)
2908 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2912 if (position
== PLAYLIST_REPLACE
)
2914 if (playlist_remove_all_tracks(playlist
) == 0)
2915 position
= PLAYLIST_INSERT_LAST
;
2921 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2923 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2925 display_playlist_count(0, count_str
, false);
2927 context
.playlist
= playlist
;
2928 context
.position
= position
;
2929 context
.queue
= queue
;
2934 result
= playlist_directory_tracksearch(dirname
, recurse
,
2935 directory_search_callback
, &context
);
2937 sync_control(playlist
, false);
2941 display_playlist_count(context
.count
, count_str
, true);
2943 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2944 audio_flush_and_reload_tracks();
2946 #ifdef HAVE_DIRCACHE
2947 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2954 * Insert all tracks from specified playlist into dynamic playlist.
2956 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2957 int position
, bool queue
)
2963 unsigned char *count_str
;
2964 char temp_buf
[MAX_PATH
+1];
2965 char trackname
[MAX_PATH
+1];
2968 bool utf8
= is_m3u8(filename
);
2971 playlist
= ¤t_playlist
;
2973 if (check_control(playlist
) < 0)
2975 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2979 fd
= open(filename
, O_RDONLY
);
2982 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2986 /* we need the directory name for formatting purposes */
2989 temp_ptr
= strrchr(filename
+1,'/');
2996 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2998 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
3000 display_playlist_count(count
, count_str
, false);
3002 if (position
== PLAYLIST_REPLACE
)
3004 if (playlist_remove_all_tracks(playlist
) == 0)
3005 position
= PLAYLIST_INSERT_LAST
;
3011 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3014 if (action_userabort(TIMEOUT_NOBLOCK
))
3017 if (count
== 0 && is_utf8_bom(temp_buf
, max
))
3020 memmove(temp_buf
, temp_buf
+ BOM_SIZE
, max
);
3023 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3029 /* Use trackname as a temporay buffer. Note that trackname must
3030 * be as large as temp_buf.
3032 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3035 /* we need to format so that relative paths are correctly
3037 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3044 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3053 /* Make sure tracks are inserted in correct order if user
3054 requests INSERT_FIRST */
3055 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3056 position
= insert_pos
+ 1;
3060 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3062 display_playlist_count(count
, count_str
, false);
3064 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3065 (audio_status() & AUDIO_STATUS_PLAY
) &&
3067 audio_flush_and_reload_tracks();
3071 /* let the other threads work */
3080 sync_control(playlist
, false);
3084 display_playlist_count(count
, count_str
, true);
3086 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3087 audio_flush_and_reload_tracks();
3089 #ifdef HAVE_DIRCACHE
3090 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3097 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3098 * we want to delete the current playing track.
3100 int playlist_delete(struct playlist_info
* playlist
, int index
)
3105 playlist
= ¤t_playlist
;
3107 if (check_control(playlist
) < 0)
3109 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3113 if (index
== PLAYLIST_DELETE_CURRENT
)
3114 index
= playlist
->index
;
3116 result
= remove_track_from_playlist(playlist
, index
, true);
3118 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3120 audio_flush_and_reload_tracks();
3126 * Move track at index to new_index. Tracks between the two are shifted
3127 * appropriately. Returns 0 on success and -1 on failure.
3129 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3135 bool current
= false;
3137 char filename
[MAX_PATH
];
3140 playlist
= ¤t_playlist
;
3142 if (check_control(playlist
) < 0)
3144 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3148 if (index
== new_index
)
3151 if (index
== playlist
->index
)
3152 /* Moving the current track */
3155 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3156 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3157 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3159 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3160 sizeof(filename
)) < 0)
3163 /* Delete track from original position */
3164 result
= remove_track_from_playlist(playlist
, index
, true);
3168 /* We want to insert the track at the position that was specified by
3169 new_index. This may be different then new_index because of the
3170 shifting that occurred after the delete */
3171 r
= rotate_index(playlist
, new_index
);
3175 new_index
= PLAYLIST_PREPEND
;
3176 else if (r
== playlist
->amount
)
3178 new_index
= PLAYLIST_INSERT_LAST
;
3180 /* Calculate index of desired position */
3181 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3183 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3190 /* Moved the current track */
3193 case PLAYLIST_PREPEND
:
3194 playlist
->index
= playlist
->first_index
;
3196 case PLAYLIST_INSERT_LAST
:
3197 playlist
->index
= playlist
->first_index
- 1;
3198 if (playlist
->index
< 0)
3199 playlist
->index
+= playlist
->amount
;
3202 playlist
->index
= new_index
;
3207 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3208 audio_flush_and_reload_tracks();
3212 #ifdef HAVE_DIRCACHE
3213 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3219 /* shuffle currently playing playlist */
3220 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3226 playlist
= ¤t_playlist
;
3228 check_control(playlist
);
3230 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3232 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3234 audio_flush_and_reload_tracks();
3239 /* sort currently playing playlist */
3240 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3245 playlist
= ¤t_playlist
;
3247 check_control(playlist
);
3249 result
= sort_playlist(playlist
, start_current
, true);
3251 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3253 audio_flush_and_reload_tracks();
3258 /* returns true if playlist has been modified */
3259 bool playlist_modified(const struct playlist_info
* playlist
)
3262 playlist
= ¤t_playlist
;
3264 if (playlist
->shuffle_modified
||
3265 playlist
->deleted
||
3266 playlist
->num_inserted_tracks
> 0)
3272 /* returns index of first track in playlist */
3273 int playlist_get_first_index(const struct playlist_info
* playlist
)
3276 playlist
= ¤t_playlist
;
3278 return playlist
->first_index
;
3281 /* returns shuffle seed of playlist */
3282 int playlist_get_seed(const struct playlist_info
* playlist
)
3285 playlist
= ¤t_playlist
;
3287 return playlist
->seed
;
3290 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3291 int playlist_amount_ex(const struct playlist_info
* playlist
)
3294 playlist
= ¤t_playlist
;
3296 return playlist
->amount
;
3299 /* returns full path of playlist (minus extension) */
3300 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3306 playlist
= ¤t_playlist
;
3308 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3313 /* Remove extension */
3314 sep
= strrchr(buf
, '.');
3321 /* returns the playlist filename */
3322 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3326 playlist
= ¤t_playlist
;
3328 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3336 /* Fills info structure with information about track at specified index.
3337 Returns 0 on success and -1 on failure */
3338 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3339 struct playlist_track_info
* info
)
3345 playlist
= ¤t_playlist
;
3347 if (index
< 0 || index
>= playlist
->amount
)
3350 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3351 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3353 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3354 sizeof(info
->filename
)) < 0)
3361 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3362 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3364 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3368 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3369 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3371 info
->index
= index
;
3372 info
->display_index
= rotate_index(playlist
, index
) + 1;
3377 /* save the current dynamic playlist to specified file */
3378 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3383 char path
[MAX_PATH
+1];
3384 char tmp_buf
[MAX_PATH
+1];
3386 bool overwrite_current
= false;
3387 int* index_buf
= NULL
;
3390 playlist
= ¤t_playlist
;
3392 if (playlist
->amount
<= 0)
3395 /* use current working directory as base for pathname */
3396 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3397 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3400 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3402 /* Attempting to overwrite current playlist file.*/
3404 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3406 /* not enough buffer space to store updated indices */
3407 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3411 /* in_ram buffer is unused for m3u files so we'll use for storing
3413 index_buf
= (int*)playlist
->buffer
;
3415 /* use temporary pathname */
3416 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3417 overwrite_current
= true;
3420 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3423 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3427 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3431 index
= playlist
->first_index
;
3432 for (i
=0; i
<playlist
->amount
; i
++)
3439 if (action_userabort(TIMEOUT_NOBLOCK
))
3445 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3446 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3447 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3449 /* Don't save queued files */
3452 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3459 if (overwrite_current
)
3460 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3462 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3464 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3471 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3472 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3478 index
= (index
+1)%playlist
->amount
;
3481 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3485 if (overwrite_current
&& result
>= 0)
3489 mutex_lock(&playlist
->control_mutex
);
3491 /* Replace the current playlist with the new one and update indices */
3492 close(playlist
->fd
);
3493 if (remove(playlist
->filename
) >= 0)
3495 if (rename(path
, playlist
->filename
) >= 0)
3497 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3498 if (playlist
->fd
>= 0)
3500 index
= playlist
->first_index
;
3501 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3503 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3505 playlist
->indices
[index
] = index_buf
[count
];
3508 index
= (index
+1)%playlist
->amount
;
3511 /* we need to recreate control because inserted tracks are
3512 now part of the playlist and shuffle has been
3514 result
= recreate_control(playlist
);
3519 mutex_unlock(&playlist
->control_mutex
);
3529 * Search specified directory for tracks and notify via callback. May be
3530 * called recursively.
3532 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3533 int (*callback
)(char*, void*),
3536 char buf
[MAX_PATH
+1];
3540 struct entry
*files
;
3541 struct tree_context
* tc
= tree_get_context();
3542 int old_dirfilter
= *(tc
->dirfilter
);
3547 /* use the tree browser dircache to load files */
3548 *(tc
->dirfilter
) = SHOW_ALL
;
3550 if (ft_load(tc
, dirname
) < 0)
3552 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3553 *(tc
->dirfilter
) = old_dirfilter
;
3557 files
= (struct entry
*) tc
->dircache
;
3558 num_files
= tc
->filesindir
;
3560 /* we've overwritten the dircache so tree browser will need to be
3564 for (i
=0; i
<num_files
; i
++)
3567 if (action_userabort(TIMEOUT_NOBLOCK
))
3573 if (files
[i
].attr
& ATTR_DIRECTORY
)
3577 /* recursively add directories */
3578 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3579 result
= playlist_directory_tracksearch(buf
, recurse
,
3584 /* we now need to reload our current directory */
3585 if(ft_load(tc
, dirname
) < 0)
3591 files
= (struct entry
*) tc
->dircache
;
3592 num_files
= tc
->filesindir
;
3602 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3604 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3606 if (callback(buf
, context
) != 0)
3612 /* let the other threads work */
3617 /* restore dirfilter */
3618 *(tc
->dirfilter
) = old_dirfilter
;