1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by wavey@wavey.org
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 Dynamic playlist design (based on design originally proposed by ricII)
25 There are two files associated with a dynamic playlist:
26 1. Playlist file : This file contains the initial songs in the playlist.
27 The file is created by the user and stored on the hard
28 drive. NOTE: If we are playing the contents of a
29 directory, there will be no playlist file.
30 2. Control file : This file is automatically created when a playlist is
31 started and contains all the commands done to it.
33 The first non-comment line in a control file must begin with
34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
35 DIR is the directory where the playlist is located and FILE is the
36 playlist filename. For dirplay, FILE will be empty. An empty playlist
37 will have both entries as null.
39 Control file commands:
40 a. Add track (A:<position>:<last position>:<path to track>)
41 - Insert a track at the specified position in the current
42 playlist. Last position is used to specify where last insertion
44 b. Queue track (Q:<position>:<last position>:<path to track>)
45 - Queue a track at the specified position in the current
46 playlist. Queued tracks differ from added tracks in that they
47 are deleted from the playlist as soon as they are played and
48 they are not saved to disk as part of the playlist.
49 c. Delete track (D:<position>)
50 - Delete track from specified position in the current playlist.
51 d. Shuffle playlist (S:<seed>:<index>)
52 - Shuffle entire playlist with specified seed. The index
53 identifies the first index in the newly shuffled playlist
54 (needed for repeat mode).
55 e. Unshuffle playlist (U:<index>)
56 - Unshuffle entire playlist. The index identifies the first index
57 in the newly unshuffled playlist.
58 f. Reset last insert position (R)
59 - Needed so that insertions work properly after resume
62 The only resume info that needs to be saved is the current index in the
63 playlist and the position in the track. When resuming, all the commands
64 in the control file will be reapplied so that the playlist indices are
65 exactly the same as before shutdown. To avoid unnecessary disk
66 accesses, the shuffle mode settings are also saved in settings and only
67 flushed to disk when required.
75 #include "ata_idle_notify.h"
86 #include "applimits.h"
95 #include "filetypes.h"
96 #ifdef HAVE_LCD_BITMAP
104 #include "rbunicode.h"
105 #include "root_menu.h"
107 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
108 #define PLAYLIST_CONTROL_FILE_VERSION 2
111 Each playlist index has a flag associated with it which identifies what
112 type of track it is. These flags are stored in the 4 high order bits of
115 NOTE: This limits the playlist file size to a max of 256M.
119 01 = Track was prepended into playlist
120 10 = Track was inserted into playlist
121 11 = Track was appended into playlist
126 0 = Track entry is valid
127 1 = Track does not exist on disk and should be skipped
129 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
130 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
131 #define PLAYLIST_QUEUE_MASK 0x20000000
133 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
134 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
135 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
137 #define PLAYLIST_QUEUED 0x20000000
138 #define PLAYLIST_SKIPPED 0x10000000
140 struct directory_search_context
{
141 struct playlist_info
* playlist
;
147 static struct playlist_info current_playlist
;
148 static char now_playing
[MAX_PATH
+1];
150 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
151 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
153 static void create_control(struct playlist_info
* playlist
);
154 static int check_control(struct playlist_info
* playlist
);
155 static int recreate_control(struct playlist_info
* playlist
);
156 static void update_playlist_filename(struct playlist_info
* playlist
,
157 const char *dir
, const char *file
);
158 static int add_indices_to_playlist(struct playlist_info
* playlist
,
159 char* buffer
, size_t buflen
);
160 static int add_track_to_playlist(struct playlist_info
* playlist
,
161 const char *filename
, int position
,
162 bool queue
, int seek_pos
);
163 static int directory_search_callback(char* filename
, void* context
);
164 static int remove_track_from_playlist(struct playlist_info
* playlist
,
165 int position
, bool write
);
166 static int randomise_playlist(struct playlist_info
* playlist
,
167 unsigned int seed
, bool start_current
,
169 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
171 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
173 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
175 static int compare(const void* p1
, const void* p2
);
176 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
177 bool control_file
, char *buf
, int buf_length
);
178 static int get_next_directory(char *dir
);
179 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
180 static int get_previous_directory(char *dir
);
181 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
);
182 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
184 static void display_playlist_count(int count
, const unsigned char *fmt
,
186 static void display_buffer_full(void);
187 static int flush_cached_control(struct playlist_info
* playlist
);
188 static int update_control(struct playlist_info
* playlist
,
189 enum playlist_command command
, int i1
, int i2
,
190 const char* s1
, const char* s2
, void* data
);
191 static void sync_control(struct playlist_info
* playlist
, bool force
);
192 static int rotate_index(const struct playlist_info
* playlist
, int index
);
195 #define PLAYLIST_LOAD_POINTERS 1
197 static struct event_queue playlist_queue
;
198 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x800)/sizeof(long)];
199 static const char playlist_thread_name
[] = "playlist cachectrl";
202 /* Check if the filename suggests M3U or M3U8 format. */
203 static bool is_m3u8(const char* filename
)
205 int len
= strlen(filename
);
207 /* Default to M3U8 unless explicitly told otherwise. */
208 return !(len
> 4 && strcasecmp(&filename
[len
- 4], ".m3u") == 0);
212 /* Convert a filename in an M3U playlist to UTF-8.
214 * buf - the filename to convert; can contain more than one line from the
216 * buf_len - amount of buf that is used.
217 * buf_max - total size of buf.
218 * temp - temporary conversion buffer, at least buf_max bytes.
220 * Returns the length of the converted filename.
222 static int convert_m3u(char* buf
, int buf_len
, int buf_max
, char* temp
)
228 while ((buf
[i
] != '\n') && (buf
[i
] != '\r') && (i
< buf_len
))
233 /* Work back killing white space. */
234 while ((i
> 0) && isspace(buf
[i
- 1]))
242 /* Convert char by char, so as to not overflow temp (iso_decode should
243 * preferably handle this). No more than 4 bytes should be generated for
246 for (i
= 0; i
< buf_len
&& dest
< (temp
+ buf_max
- 4); i
++)
248 dest
= iso_decode(&buf
[i
], dest
, -1, 1);
257 * remove any files and indices associated with the playlist
259 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
261 playlist
->filename
[0] = '\0';
262 playlist
->utf8
= true;
264 if(playlist
->fd
>= 0)
265 /* If there is an already open playlist, close it. */
269 if(playlist
->control_fd
>= 0)
270 close(playlist
->control_fd
);
271 playlist
->control_fd
= -1;
272 playlist
->control_created
= false;
274 playlist
->in_ram
= false;
276 if (playlist
->buffer
)
277 playlist
->buffer
[0] = 0;
279 playlist
->buffer_end_pos
= 0;
282 playlist
->first_index
= 0;
283 playlist
->amount
= 0;
284 playlist
->last_insert_pos
= -1;
286 playlist
->shuffle_modified
= false;
287 playlist
->deleted
= false;
288 playlist
->num_inserted_tracks
= 0;
289 playlist
->started
= false;
291 playlist
->num_cached
= 0;
292 playlist
->pending_control_sync
= false;
294 if (!resume
&& playlist
->current
)
296 /* start with fresh playlist control file when starting new
298 create_control(playlist
);
303 * Initialize a new playlist for viewing/editing/playing. dir is the
304 * directory where the playlist is located and file is the filename.
306 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
309 const char *fileused
= file
;
310 const char *dirused
= dir
;
311 empty_playlist(playlist
, false);
317 if (dirused
&& playlist
->current
) /* !current cannot be in_ram */
318 playlist
->in_ram
= true;
320 dirused
= ""; /* empty playlist */
323 update_playlist_filename(playlist
, dirused
, fileused
);
325 if (playlist
->control_fd
>= 0)
327 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
328 PLAYLIST_CONTROL_FILE_VERSION
, -1, dirused
, fileused
, NULL
);
329 sync_control(playlist
, false);
334 * create control file for playlist
336 static void create_control(struct playlist_info
* playlist
)
338 playlist
->control_fd
= open(playlist
->control_filename
,
339 O_CREAT
|O_RDWR
|O_TRUNC
);
340 if (playlist
->control_fd
< 0)
342 if (check_rockboxdir())
344 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
345 splashf(HZ
*2, (unsigned char *)"%s (%d)",
346 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
347 playlist
->control_fd
);
349 playlist
->control_created
= false;
353 playlist
->control_created
= true;
358 * validate the control file. This may include creating/initializing it if
361 static int check_control(struct playlist_info
* playlist
)
363 if (!playlist
->control_created
)
365 create_control(playlist
);
367 if (playlist
->control_fd
>= 0)
369 char* dir
= playlist
->filename
;
370 char* file
= playlist
->filename
+playlist
->dirlen
;
371 char c
= playlist
->filename
[playlist
->dirlen
-1];
373 playlist
->filename
[playlist
->dirlen
-1] = '\0';
375 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
376 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
377 sync_control(playlist
, false);
378 playlist
->filename
[playlist
->dirlen
-1] = c
;
382 if (playlist
->control_fd
< 0)
389 * recreate the control file based on current playlist entries
391 static int recreate_control(struct playlist_info
* playlist
)
393 char temp_file
[MAX_PATH
+1];
398 if(playlist
->control_fd
>= 0)
400 char* dir
= playlist
->filename
;
401 char* file
= playlist
->filename
+playlist
->dirlen
;
402 char c
= playlist
->filename
[playlist
->dirlen
-1];
404 close(playlist
->control_fd
);
406 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
407 playlist
->control_filename
);
409 if (rename(playlist
->control_filename
, temp_file
) < 0)
412 temp_fd
= open(temp_file
, O_RDONLY
);
416 playlist
->control_fd
= open(playlist
->control_filename
,
417 O_CREAT
|O_RDWR
|O_TRUNC
);
418 if (playlist
->control_fd
< 0)
421 playlist
->filename
[playlist
->dirlen
-1] = '\0';
423 /* cannot call update_control() because of mutex */
424 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
425 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
427 playlist
->filename
[playlist
->dirlen
-1] = c
;
437 playlist
->shuffle_modified
= false;
438 playlist
->deleted
= false;
439 playlist
->num_inserted_tracks
= 0;
441 for (i
=0; i
<playlist
->amount
; i
++)
443 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
445 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
446 char inserted_file
[MAX_PATH
+1];
448 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
450 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
452 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
453 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
456 /* save the position in file where name is written */
457 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
459 result
= fdprintf(playlist
->control_fd
, "%s\n",
462 playlist
->indices
[i
] =
463 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
469 playlist
->num_inserted_tracks
++;
475 fsync(playlist
->control_fd
);
484 * store directory and name of playlist file
486 static void update_playlist_filename(struct playlist_info
* playlist
,
487 const char *dir
, const char *file
)
490 int dirlen
= strlen(dir
);
492 playlist
->utf8
= is_m3u8(file
);
494 /* If the dir does not end in trailing slash, we use a separator.
495 Otherwise we don't. */
496 if('/' != dir
[dirlen
-1])
502 playlist
->dirlen
= dirlen
;
504 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
505 "%s%s%s", dir
, sep
, file
);
509 * calculate track offsets within a playlist file
511 static int add_indices_to_playlist(struct playlist_info
* playlist
,
512 char* buffer
, size_t buflen
)
516 unsigned int count
= 0;
521 if(-1 == playlist
->fd
)
522 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
524 return -1; /* failure */
525 if((i
= lseek(playlist
->fd
, 0, SEEK_CUR
)) > 0)
526 playlist
->utf8
= true; /* Override any earlier indication. */
528 splash(0, ID2P(LANG_WAIT
));
532 /* use mp3 buffer for maximum load speed */
534 #if CONFIG_CODEC != SWCODEC
535 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
536 buflen
= (audiobufend
- audiobuf
);
537 buffer
= (char *)audiobuf
;
539 buffer
= (char *)audio_get_buffer(false, &buflen
);
547 nread
= read(playlist
->fd
, buffer
, buflen
);
548 /* Terminate on EOF */
552 p
= (unsigned char *)buffer
;
554 for(count
=0; count
< nread
; count
++,p
++) {
556 /* Are we on a new line? */
557 if((*p
== '\n') || (*p
== '\r'))
567 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
568 display_buffer_full();
573 /* Store a new entry */
574 playlist
->indices
[ playlist
->amount
] = i
+count
;
576 if (playlist
->filenames
)
577 playlist
->filenames
[ playlist
->amount
] = NULL
;
589 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
596 * Utility function to create a new playlist, fill it with the next or
597 * previous directory, shuffle it if needed, and start playback.
598 * If play_last is true and direction zero or negative, start playing
599 * the last file in the directory, otherwise start playing the first.
601 static int create_and_play_dir(int direction
, bool play_last
)
603 char dir
[MAX_PATH
+ 1];
608 res
= get_next_directory(dir
);
610 res
= get_previous_directory(dir
);
614 if (playlist_create(dir
, NULL
) != -1)
616 ft_build_playlist(tree_get_context(), 0);
618 if (global_settings
.playlist_shuffle
)
619 playlist_shuffle(current_tick
, -1);
621 if (play_last
&& direction
<= 0)
622 index
= current_playlist
.amount
- 1;
626 #if (CONFIG_CODEC != SWCODEC)
627 playlist_start(index
, 0);
631 /* we've overwritten the dircache when getting the next/previous dir,
632 so the tree browser context will need to be reloaded */
640 * Removes all tracks, from the playlist, leaving the presently playing
643 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
647 if (playlist
== NULL
)
648 playlist
= ¤t_playlist
;
650 while (playlist
->index
> 0)
651 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
654 while (playlist
->amount
> 1)
655 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
658 if (playlist
->amount
== 1) {
659 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
667 * Add track to playlist at specified position. There are seven special
668 * positions that can be specified:
669 * PLAYLIST_PREPEND - Add track at beginning of playlist
670 * PLAYLIST_INSERT - Add track after current song. NOTE: If
671 * there are already inserted tracks then track
672 * is added to the end of the insertion list
673 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
674 * matter what other tracks have been inserted
675 * PLAYLIST_INSERT_LAST - Add track to end of playlist
676 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
677 * current playing track and end of playlist
678 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
680 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
681 * and inster this track at the end.
683 static int add_track_to_playlist(struct playlist_info
* playlist
,
684 const char *filename
, int position
,
685 bool queue
, int seek_pos
)
687 int insert_position
, orig_position
;
688 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
691 insert_position
= orig_position
= position
;
693 if (playlist
->amount
>= playlist
->max_playlist_size
)
695 display_buffer_full();
701 case PLAYLIST_PREPEND
:
702 position
= insert_position
= playlist
->first_index
;
704 case PLAYLIST_INSERT
:
705 /* if there are already inserted tracks then add track to end of
706 insertion list else add after current playing track */
707 if (playlist
->last_insert_pos
>= 0 &&
708 playlist
->last_insert_pos
< playlist
->amount
&&
709 (playlist
->indices
[playlist
->last_insert_pos
]&
710 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
711 position
= insert_position
= playlist
->last_insert_pos
+1;
712 else if (playlist
->amount
> 0)
713 position
= insert_position
= playlist
->index
+ 1;
715 position
= insert_position
= 0;
717 playlist
->last_insert_pos
= position
;
719 case PLAYLIST_INSERT_FIRST
:
720 if (playlist
->amount
> 0)
721 position
= insert_position
= playlist
->index
+ 1;
723 position
= insert_position
= 0;
725 playlist
->last_insert_pos
= position
;
727 case PLAYLIST_INSERT_LAST
:
728 if (playlist
->first_index
> 0)
729 position
= insert_position
= playlist
->first_index
;
731 position
= insert_position
= playlist
->amount
;
733 playlist
->last_insert_pos
= position
;
735 case PLAYLIST_INSERT_SHUFFLED
:
737 if (playlist
->started
)
740 int n
= playlist
->amount
-
741 rotate_index(playlist
, playlist
->index
);
748 position
= playlist
->index
+ offset
+ 1;
749 if (position
>= playlist
->amount
)
750 position
-= playlist
->amount
;
752 insert_position
= position
;
755 position
= insert_position
= (rand() % (playlist
->amount
+1));
758 case PLAYLIST_INSERT_LAST_SHUFFLED
:
760 position
= insert_position
= playlist
->last_shuffled_start
+
761 rand() % (playlist
->amount
- playlist
->last_shuffled_start
+ 1);
764 case PLAYLIST_REPLACE
:
765 if (playlist_remove_all_tracks(playlist
) < 0)
768 playlist
->last_insert_pos
= position
= insert_position
= playlist
->index
+ 1;
773 flags
|= PLAYLIST_QUEUED
;
775 /* shift indices so that track can be added */
776 for (i
=playlist
->amount
; i
>insert_position
; i
--)
778 playlist
->indices
[i
] = playlist
->indices
[i
-1];
780 if (playlist
->filenames
)
781 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
785 /* update stored indices if needed */
787 if (orig_position
< 0)
789 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
793 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
794 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
795 playlist
->first_index
++;
798 if (insert_position
< playlist
->last_insert_pos
||
799 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
800 playlist
->last_insert_pos
++;
802 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
804 int result
= update_control(playlist
,
805 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
806 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
812 playlist
->indices
[insert_position
] = flags
| seek_pos
;
815 if (playlist
->filenames
)
816 playlist
->filenames
[insert_position
] = NULL
;
820 playlist
->num_inserted_tracks
++;
822 return insert_position
;
826 * Callback for playlist_directory_tracksearch to insert track into
829 static int directory_search_callback(char* filename
, void* context
)
831 struct directory_search_context
* c
=
832 (struct directory_search_context
*) context
;
835 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
843 /* Make sure tracks are inserted in correct order if user requests
845 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
846 c
->position
= insert_pos
+ 1;
848 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
850 unsigned char* count_str
;
853 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
855 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
857 display_playlist_count(c
->count
, count_str
, false);
859 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
860 (audio_status() & AUDIO_STATUS_PLAY
) &&
861 c
->playlist
->started
)
862 audio_flush_and_reload_tracks();
869 * remove track at specified position
871 static int remove_track_from_playlist(struct playlist_info
* playlist
,
872 int position
, bool write
)
877 if (playlist
->amount
<= 0)
880 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
882 /* shift indices now that track has been removed */
883 for (i
=position
; i
<playlist
->amount
; i
++)
885 playlist
->indices
[i
] = playlist
->indices
[i
+1];
887 if (playlist
->filenames
)
888 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
895 playlist
->num_inserted_tracks
--;
897 playlist
->deleted
= true;
899 /* update stored indices if needed */
900 if (position
< playlist
->index
)
903 if (position
< playlist
->first_index
)
905 playlist
->first_index
--;
908 if (position
<= playlist
->last_insert_pos
)
909 playlist
->last_insert_pos
--;
911 if (write
&& playlist
->control_fd
>= 0)
913 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
914 position
, -1, NULL
, NULL
, NULL
);
919 sync_control(playlist
, false);
926 * randomly rearrange the array of indices for the playlist. If start_current
927 * is true then update the index to the new index of the current playing track
929 static int randomise_playlist(struct playlist_info
* playlist
,
930 unsigned int seed
, bool start_current
,
936 unsigned int current
= playlist
->indices
[playlist
->index
];
938 /* seed 0 is used to identify sorted playlist for resume purposes */
942 /* seed with the given seed */
945 /* randomise entire indices list */
946 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
948 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
949 candidate
= rand() % (count
+ 1);
951 /* now swap the values at the 'count' and 'candidate' positions */
952 store
= playlist
->indices
[candidate
];
953 playlist
->indices
[candidate
] = playlist
->indices
[count
];
954 playlist
->indices
[count
] = store
;
956 if (playlist
->filenames
)
958 store
= (long)playlist
->filenames
[candidate
];
959 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
960 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
966 find_and_set_playlist_index(playlist
, current
);
968 /* indices have been moved so last insert position is no longer valid */
969 playlist
->last_insert_pos
= -1;
971 playlist
->seed
= seed
;
972 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
973 playlist
->shuffle_modified
= true;
977 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
978 playlist
->first_index
, NULL
, NULL
, NULL
);
985 * Sort the array of indices for the playlist. If start_current is true then
986 * set the index to the new index of the current song.
987 * Also while going to unshuffled mode set the first_index to 0.
989 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
992 unsigned int current
= playlist
->indices
[playlist
->index
];
994 if (playlist
->amount
> 0)
995 qsort(playlist
->indices
, playlist
->amount
,
996 sizeof(playlist
->indices
[0]), compare
);
999 /** We need to re-check the song names from disk because qsort can't
1000 * sort two arrays at once :/
1001 * FIXME: Please implement a better way to do this. */
1002 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
1003 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1007 find_and_set_playlist_index(playlist
, current
);
1009 /* indices have been moved so last insert position is no longer valid */
1010 playlist
->last_insert_pos
= -1;
1012 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1013 playlist
->shuffle_modified
= false;
1014 if (write
&& playlist
->control_fd
>= 0)
1016 playlist
->first_index
= 0;
1017 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1018 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1024 /* Calculate how many steps we have to really step when skipping entries
1027 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1029 int i
, count
, direction
;
1031 int stepped_count
= 0;
1044 index
= playlist
->index
;
1047 /* Boundary check */
1049 index
+= playlist
->amount
;
1050 if (index
>= playlist
->amount
)
1051 index
-= playlist
->amount
;
1053 /* Check if we found a bad entry. */
1054 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1057 /* Are all entries bad? */
1058 if (stepped_count
++ > playlist
->amount
)
1065 } while (i
<= count
);
1070 /* Marks the index of the track to be skipped that is "steps" away from
1071 * current playing track.
1073 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1077 if (playlist
== NULL
)
1078 playlist
= ¤t_playlist
;
1080 /* need to account for already skipped tracks */
1081 steps
= calculate_step_count(playlist
, steps
);
1083 index
= playlist
->index
+ steps
;
1085 index
+= playlist
->amount
;
1086 else if (index
>= playlist
->amount
)
1087 index
-= playlist
->amount
;
1089 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1093 * returns the index of the track that is "steps" away from current playing
1096 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1099 int current_index
= playlist
->index
;
1100 int next_index
= -1;
1102 if (playlist
->amount
<= 0)
1105 if (repeat_mode
== -1)
1106 repeat_mode
= global_settings
.repeat_mode
;
1108 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1109 repeat_mode
= REPEAT_ALL
;
1111 steps
= calculate_step_count(playlist
, steps
);
1112 switch (repeat_mode
)
1114 case REPEAT_SHUFFLE
:
1115 /* Treat repeat shuffle just like repeat off. At end of playlist,
1116 play will be resumed in playlist_next() */
1119 current_index
= rotate_index(playlist
, current_index
);
1120 next_index
= current_index
+steps
;
1121 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1124 next_index
= (next_index
+playlist
->first_index
) %
1131 #ifdef AB_REPEAT_ENABLE
1134 next_index
= current_index
;
1140 next_index
= (current_index
+steps
) % playlist
->amount
;
1141 while (next_index
< 0)
1142 next_index
+= playlist
->amount
;
1144 if (steps
>= playlist
->amount
)
1151 /* second time around so skip the queued files */
1152 for (i
=0; i
<playlist
->amount
; i
++)
1154 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1155 index
= (index
+1) % playlist
->amount
;
1167 /* No luck if the whole playlist was bad. */
1168 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1175 * Search for the seek track and set appropriate indices. Used after shuffle
1176 * to make sure the current index is still pointing to correct track.
1178 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1183 /* Set the index to the current song */
1184 for (i
=0; i
<playlist
->amount
; i
++)
1186 if (playlist
->indices
[i
] == seek
)
1188 playlist
->index
= playlist
->first_index
= i
;
1196 * used to sort track indices. Sort order is as follows:
1197 * 1. Prepended tracks (in prepend order)
1198 * 2. Playlist/directory tracks (in playlist order)
1199 * 3. Inserted/Appended tracks (in insert order)
1201 static int compare(const void* p1
, const void* p2
)
1203 unsigned long* e1
= (unsigned long*) p1
;
1204 unsigned long* e2
= (unsigned long*) p2
;
1205 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1206 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1208 if (flags1
== flags2
)
1209 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1210 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1211 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1213 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1214 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1216 else if (flags1
&& flags2
)
1217 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1222 #ifdef HAVE_DIRCACHE
1224 * Thread to update filename pointers to dircache on background
1225 * without affecting playlist load up performance. This thread also flushes
1226 * any pending control commands when the disk spins up.
1228 static void playlist_flush_callback(void *param
)
1231 struct playlist_info
*playlist
;
1232 playlist
= ¤t_playlist
;
1233 if (playlist
->control_fd
>= 0)
1235 if (playlist
->num_cached
> 0)
1237 mutex_lock(&playlist
->control_mutex
);
1238 flush_cached_control(playlist
);
1239 mutex_unlock(&playlist
->control_mutex
);
1241 sync_control(playlist
, true);
1245 static void playlist_thread(void)
1247 struct queue_event ev
;
1248 bool dirty_pointers
= false;
1249 static char tmp
[MAX_PATH
+1];
1251 struct playlist_info
*playlist
;
1258 #ifdef HAVE_DISK_STORAGE
1259 if (global_settings
.disk_spindown
> 1 &&
1260 global_settings
.disk_spindown
<= 5)
1261 sleep_time
= global_settings
.disk_spindown
- 1;
1266 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1270 case PLAYLIST_LOAD_POINTERS
:
1271 dirty_pointers
= true;
1274 /* Start the background scanning after either the disk spindown
1275 timeout or 5s, whichever is less */
1277 playlist
= ¤t_playlist
;
1278 if (playlist
->control_fd
>= 0)
1280 if (playlist
->num_cached
> 0)
1281 register_storage_idle_func(playlist_flush_callback
);
1284 if (!dirty_pointers
)
1287 if (!dircache_is_enabled() || !playlist
->filenames
1288 || playlist
->amount
<= 0)
1291 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1294 for (index
= 0; index
< playlist
->amount
1295 && queue_empty(&playlist_queue
); index
++)
1297 /* Process only pointers that are not already loaded. */
1298 if (playlist
->filenames
[index
])
1301 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1302 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1304 /* Load the filename from playlist file. */
1305 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1309 /* Set the dircache entry pointer. */
1310 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1312 /* And be on background so user doesn't notice any delays. */
1316 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1319 dirty_pointers
= false;
1323 case SYS_USB_CONNECTED
:
1324 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1325 usb_wait_for_disconnect(&playlist_queue
);
1334 * gets pathname for track at seek index
1336 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1337 bool control_file
, char *buf
, int buf_length
)
1341 char tmp_buf
[MAX_PATH
+1];
1342 char dir_buf
[MAX_PATH
+1];
1343 bool utf8
= playlist
->utf8
;
1345 if (buf_length
> MAX_PATH
+1)
1346 buf_length
= MAX_PATH
+1;
1348 #ifdef HAVE_DIRCACHE
1349 if (dircache_is_enabled() && playlist
->filenames
)
1351 if (playlist
->filenames
[index
] != NULL
)
1353 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1354 max
= strlen(tmp_buf
);
1361 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1363 max
= strlcpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1367 mutex_lock(&playlist
->control_mutex
);
1371 fd
= playlist
->control_fd
;
1376 if(-1 == playlist
->fd
)
1377 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1385 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1389 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1391 if ((max
> 0) && !utf8
)
1393 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1394 * be as large as tmp_buf.
1396 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1401 mutex_unlock(&playlist
->control_mutex
);
1406 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1408 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1414 strlcpy(dir_buf
, playlist
->filename
, playlist
->dirlen
);
1416 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1419 static int get_next_directory(char *dir
){
1420 return get_next_dir(dir
,true,false);
1423 static int get_previous_directory(char *dir
){
1424 return get_next_dir(dir
,false,false);
1428 * search through all the directories (starting with the current) to find
1429 * one that has tracks to play
1431 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1433 struct playlist_info
* playlist
= ¤t_playlist
;
1435 char *start_dir
= NULL
;
1438 struct tree_context
* tc
= tree_get_context();
1439 int saved_dirfilter
= *(tc
->dirfilter
);
1441 /* process random folder advance */
1442 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1444 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat", O_RDONLY
);
1447 char buffer
[MAX_PATH
];
1448 int folder_count
= 0;
1449 srand(current_tick
);
1450 *(tc
->dirfilter
) = SHOW_MUSIC
;
1451 tc
->sort_dir
= global_settings
.sort_dir
;
1452 read(fd
,&folder_count
,sizeof(int));
1457 i
= rand()%folder_count
;
1458 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1459 read(fd
,buffer
,MAX_PATH
);
1460 if (check_subdir_for_music(buffer
, "", false) ==0)
1466 *(tc
->dirfilter
) = saved_dirfilter
;
1467 tc
->sort_dir
= global_settings
.sort_dir
;
1473 /* not random folder advance (or random folder advance unavailable) */
1476 /* start with root */
1481 /* start with current directory */
1482 strlcpy(dir
, playlist
->filename
, playlist
->dirlen
);
1485 /* use the tree browser dircache to load files */
1486 *(tc
->dirfilter
) = SHOW_ALL
;
1488 /* set up sorting/direction */
1489 tc
->sort_dir
= global_settings
.sort_dir
;
1492 static const char sortpairs
[] =
1494 [SORT_ALPHA
] = SORT_ALPHA_REVERSED
,
1495 [SORT_DATE
] = SORT_DATE_REVERSED
,
1496 [SORT_TYPE
] = SORT_TYPE_REVERSED
,
1497 [SORT_ALPHA_REVERSED
] = SORT_ALPHA
,
1498 [SORT_DATE_REVERSED
] = SORT_DATE
,
1499 [SORT_TYPE_REVERSED
] = SORT_TYPE
,
1502 if ((unsigned)tc
->sort_dir
< sizeof(sortpairs
))
1503 tc
->sort_dir
= sortpairs
[tc
->sort_dir
];
1508 struct entry
*files
;
1512 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1519 files
= (struct entry
*) tc
->dircache
;
1520 num_files
= tc
->filesindir
;
1522 for (i
=0; i
<num_files
; i
++)
1525 if (action_userabort(TIMEOUT_NOBLOCK
))
1532 if (files
[i
].attr
& ATTR_DIRECTORY
)
1536 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1543 else if (!strcmp(start_dir
, files
[i
].name
))
1550 /* move down to parent directory. current directory name is
1551 stored as the starting point for the search in parent */
1552 start_dir
= strrchr(dir
, '/');
1563 /* restore dirfilter */
1564 *(tc
->dirfilter
) = saved_dirfilter
;
1565 tc
->sort_dir
= global_settings
.sort_dir
;
1567 /* special case if nothing found: try start searching again from root */
1568 if (result
== -1 && !recursion
){
1569 result
= get_next_dir(dir
, is_forward
, true);
1576 * Checks if there are any music files in the dir or any of its
1577 * subdirectories. May be called recursively.
1579 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
)
1582 int dirlen
= strlen(dir
);
1585 struct entry
*files
;
1586 bool has_music
= false;
1587 bool has_subdir
= false;
1588 struct tree_context
* tc
= tree_get_context();
1590 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1592 if (ft_load(tc
, dir
) < 0)
1597 files
= (struct entry
*) tc
->dircache
;
1598 num_files
= tc
->filesindir
;
1600 for (i
=0; i
<num_files
; i
++)
1602 if (files
[i
].attr
& ATTR_DIRECTORY
)
1604 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1614 if (has_subdir
&& recurse
)
1616 for (i
=0; i
<num_files
; i
++)
1618 if (action_userabort(TIMEOUT_NOBLOCK
))
1624 if (files
[i
].attr
& ATTR_DIRECTORY
)
1626 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1644 /* we now need to reload our current directory */
1645 if(ft_load(tc
, dir
) < 0)
1646 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1652 * Returns absolute path of track
1654 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1661 /* Zero-terminate the file name */
1662 while((src
[i
] != '\n') &&
1667 /* Now work back killing white space */
1668 while((src
[i
-1] == ' ') ||
1674 /* replace backslashes with forward slashes */
1675 for ( j
=0; j
<i
; j
++ )
1676 if ( src
[j
] == '\\' )
1681 strlcpy(dest
, src
, buf_length
);
1685 /* handle dos style drive letter */
1687 strlcpy(dest
, &src
[2], buf_length
);
1688 else if (!strncmp(src
, "../", 3))
1690 /* handle relative paths */
1692 while(!strncmp(&src
[i
], "../", 3))
1694 for (j
=0; j
<i
/3; j
++) {
1695 temp_ptr
= strrchr(dir
, '/');
1701 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1703 else if ( '.' == src
[0] && '/' == src
[1] ) {
1704 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1707 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1715 * Display splash message showing progress of playlist/directory insertion or
1718 static void display_playlist_count(int count
, const unsigned char *fmt
,
1721 static long talked_tick
= 0;
1722 long id
= P2ID(fmt
);
1723 if(global_settings
.talk_menu
&& id
>=0)
1725 if(final
|| (count
&& (talked_tick
== 0
1726 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1728 talked_tick
= current_tick
;
1729 talk_number(count
, false);
1735 splashf(0, fmt
, count
, str(LANG_OFF_ABORT
));
1739 * Display buffer full message
1741 static void display_buffer_full(void)
1743 splash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1747 * Flush any cached control commands to disk. Called when playlist is being
1748 * modified. Returns 0 on success and -1 on failure.
1750 static int flush_cached_control(struct playlist_info
* playlist
)
1755 if (!playlist
->num_cached
)
1758 lseek(playlist
->control_fd
, 0, SEEK_END
);
1760 for (i
=0; i
<playlist
->num_cached
; i
++)
1762 struct playlist_control_cache
* cache
=
1763 &(playlist
->control_cache
[i
]);
1765 switch (cache
->command
)
1767 case PLAYLIST_COMMAND_PLAYLIST
:
1768 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1769 cache
->i1
, cache
->s1
, cache
->s2
);
1771 case PLAYLIST_COMMAND_ADD
:
1772 case PLAYLIST_COMMAND_QUEUE
:
1773 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1774 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1775 cache
->i1
, cache
->i2
);
1778 /* save the position in file where name is written */
1779 int* seek_pos
= (int *)cache
->data
;
1780 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1781 result
= fdprintf(playlist
->control_fd
, "%s\n",
1785 case PLAYLIST_COMMAND_DELETE
:
1786 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1788 case PLAYLIST_COMMAND_SHUFFLE
:
1789 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1790 cache
->i1
, cache
->i2
);
1792 case PLAYLIST_COMMAND_UNSHUFFLE
:
1793 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1795 case PLAYLIST_COMMAND_RESET
:
1796 result
= fdprintf(playlist
->control_fd
, "R\n");
1808 playlist
->num_cached
= 0;
1809 playlist
->pending_control_sync
= true;
1816 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1823 * Update control data with new command. Depending on the command, it may be
1824 * cached or flushed to disk.
1826 static int update_control(struct playlist_info
* playlist
,
1827 enum playlist_command command
, int i1
, int i2
,
1828 const char* s1
, const char* s2
, void* data
)
1831 struct playlist_control_cache
* cache
;
1834 mutex_lock(&playlist
->control_mutex
);
1836 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1838 cache
->command
= command
;
1847 case PLAYLIST_COMMAND_PLAYLIST
:
1848 case PLAYLIST_COMMAND_ADD
:
1849 case PLAYLIST_COMMAND_QUEUE
:
1850 #ifndef HAVE_DIRCACHE
1851 case PLAYLIST_COMMAND_DELETE
:
1852 case PLAYLIST_COMMAND_RESET
:
1856 case PLAYLIST_COMMAND_SHUFFLE
:
1857 case PLAYLIST_COMMAND_UNSHUFFLE
:
1859 /* only flush when needed */
1863 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1864 result
= flush_cached_control(playlist
);
1866 mutex_unlock(&playlist
->control_mutex
);
1872 * sync control file to disk
1874 static void sync_control(struct playlist_info
* playlist
, bool force
)
1876 #ifdef HAVE_DIRCACHE
1877 if (playlist
->started
&& force
)
1881 if (playlist
->started
)
1884 if (playlist
->pending_control_sync
)
1886 mutex_lock(&playlist
->control_mutex
);
1887 fsync(playlist
->control_fd
);
1888 playlist
->pending_control_sync
= false;
1889 mutex_unlock(&playlist
->control_mutex
);
1895 * Rotate indices such that first_index is index 0
1897 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1899 index
-= playlist
->first_index
;
1901 index
+= playlist
->amount
;
1907 * Initialize playlist entries at startup
1909 void playlist_init(void)
1911 struct playlist_info
* playlist
= ¤t_playlist
;
1913 playlist
->current
= true;
1914 strlcpy(playlist
->control_filename
, PLAYLIST_CONTROL_FILE
,
1915 sizeof(playlist
->control_filename
));
1917 playlist
->control_fd
= -1;
1918 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1919 playlist
->indices
= buffer_alloc(
1920 playlist
->max_playlist_size
* sizeof(int));
1921 playlist
->buffer_size
=
1922 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1923 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1924 mutex_init(&playlist
->control_mutex
);
1925 empty_playlist(playlist
, true);
1927 #ifdef HAVE_DIRCACHE
1928 playlist
->filenames
= buffer_alloc(
1929 playlist
->max_playlist_size
* sizeof(int));
1930 memset(playlist
->filenames
, 0,
1931 playlist
->max_playlist_size
* sizeof(int));
1932 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1933 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1935 queue_init(&playlist_queue
, true);
1940 * Clean playlist at shutdown
1942 void playlist_shutdown(void)
1944 struct playlist_info
* playlist
= ¤t_playlist
;
1946 if (playlist
->control_fd
>= 0)
1948 mutex_lock(&playlist
->control_mutex
);
1950 if (playlist
->num_cached
> 0)
1951 flush_cached_control(playlist
);
1953 close(playlist
->control_fd
);
1955 mutex_unlock(&playlist
->control_mutex
);
1960 * Create new playlist
1962 int playlist_create(const char *dir
, const char *file
)
1964 struct playlist_info
* playlist
= ¤t_playlist
;
1966 new_playlist(playlist
, dir
, file
);
1969 /* load the playlist file */
1970 add_indices_to_playlist(playlist
, NULL
, 0);
1975 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1978 * Restore the playlist state based on control file commands. Called to
1979 * resume playback after shutdown.
1981 int playlist_resume(void)
1983 struct playlist_info
* playlist
= ¤t_playlist
;
1988 int control_file_size
= 0;
1992 /* use mp3 buffer for maximum load speed */
1993 #if CONFIG_CODEC != SWCODEC
1994 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1995 buflen
= (audiobufend
- audiobuf
);
1996 buffer
= (char *)audiobuf
;
1998 buffer
= (char *)audio_get_buffer(false, &buflen
);
2001 empty_playlist(playlist
, true);
2003 splash(0, ID2P(LANG_WAIT
));
2004 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2005 if (playlist
->control_fd
< 0)
2007 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2010 playlist
->control_created
= true;
2012 control_file_size
= filesize(playlist
->control_fd
);
2013 if (control_file_size
<= 0)
2015 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2019 /* read a small amount first to get the header */
2020 nread
= read(playlist
->control_fd
, buffer
,
2021 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2024 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2028 playlist
->started
= true;
2034 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2035 int last_newline
= 0;
2037 bool newline
= true;
2038 bool exit_loop
= false;
2043 unsigned long last_tick
= current_tick
;
2044 bool useraborted
= false;
2046 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2048 /* So a splash while we are loading. */
2049 if (TIME_AFTER(current_tick
, last_tick
+ HZ
/4))
2051 splashf(0, str(LANG_LOADING_PERCENT
),
2052 (total_read
+count
)*100/control_file_size
,
2053 str(LANG_OFF_ABORT
));
2054 if (action_userabort(TIMEOUT_NOBLOCK
))
2059 last_tick
= current_tick
;
2062 /* Are we on a new line? */
2063 if((*p
== '\n') || (*p
== '\r'))
2067 /* save last_newline in case we need to load more data */
2068 last_newline
= count
;
2070 switch (current_command
)
2072 case PLAYLIST_COMMAND_PLAYLIST
:
2074 /* str1=version str2=dir str3=file */
2090 version
= atoi(str1
);
2092 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2095 update_playlist_filename(playlist
, str2
, str3
);
2097 if (str3
[0] != '\0')
2099 /* NOTE: add_indices_to_playlist() overwrites the
2100 audiobuf so we need to reload control file
2102 add_indices_to_playlist(playlist
, NULL
, 0);
2104 else if (str2
[0] != '\0')
2106 playlist
->in_ram
= true;
2107 resume_directory(str2
);
2110 /* load the rest of the data */
2116 case PLAYLIST_COMMAND_ADD
:
2117 case PLAYLIST_COMMAND_QUEUE
:
2119 /* str1=position str2=last_position str3=file */
2120 int position
, last_position
;
2123 if (!str1
|| !str2
|| !str3
)
2130 position
= atoi(str1
);
2131 last_position
= atoi(str2
);
2133 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2136 /* seek position is based on str3's position in
2138 if (add_track_to_playlist(playlist
, str3
, position
,
2139 queue
, total_read
+(str3
-buffer
)) < 0)
2142 playlist
->last_insert_pos
= last_position
;
2146 case PLAYLIST_COMMAND_DELETE
:
2158 position
= atoi(str1
);
2160 if (remove_track_from_playlist(playlist
, position
,
2166 case PLAYLIST_COMMAND_SHUFFLE
:
2168 /* str1=seed str2=first_index */
2180 /* Always sort list before shuffling */
2181 sort_playlist(playlist
, false, false);
2185 playlist
->first_index
= atoi(str2
);
2187 if (randomise_playlist(playlist
, seed
, false,
2193 case PLAYLIST_COMMAND_UNSHUFFLE
:
2195 /* str1=first_index */
2203 playlist
->first_index
= atoi(str1
);
2205 if (sort_playlist(playlist
, false, false) < 0)
2211 case PLAYLIST_COMMAND_RESET
:
2213 playlist
->last_insert_pos
= -1;
2216 case PLAYLIST_COMMAND_COMMENT
:
2223 /* to ignore any extra newlines */
2224 current_command
= PLAYLIST_COMMAND_COMMENT
;
2230 /* first non-comment line must always specify playlist */
2231 if (first
&& *p
!= 'P' && *p
!= '#')
2241 /* playlist can only be specified once */
2249 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2252 current_command
= PLAYLIST_COMMAND_ADD
;
2255 current_command
= PLAYLIST_COMMAND_QUEUE
;
2258 current_command
= PLAYLIST_COMMAND_DELETE
;
2261 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2264 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2267 current_command
= PLAYLIST_COMMAND_RESET
;
2270 current_command
= PLAYLIST_COMMAND_COMMENT
;
2283 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2285 /* all control file strings are separated with a colon.
2286 Replace the colon with 0 to get proper strings that can be
2287 used by commands above */
2293 if ((count
+1) < nread
)
2307 /* allow last string to contain colons */
2318 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2324 splash(HZ
*2, ID2P(LANG_CANCEL
));
2327 if (!newline
|| (exit_loop
&& count
<nread
))
2329 if ((total_read
+ count
) >= control_file_size
)
2331 /* no newline at end of control file */
2332 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2336 /* We didn't end on a newline or we exited loop prematurely.
2337 Either way, re-read the remainder. */
2338 count
= last_newline
;
2339 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2342 total_read
+= count
;
2345 /* still looking for header */
2346 nread
= read(playlist
->control_fd
, buffer
,
2347 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2349 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2351 /* Terminate on EOF */
2358 #ifdef HAVE_DIRCACHE
2359 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2366 * Add track to in_ram playlist. Used when playing directories.
2368 int playlist_add(const char *filename
)
2370 struct playlist_info
* playlist
= ¤t_playlist
;
2371 int len
= strlen(filename
);
2373 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2374 (playlist
->amount
>= playlist
->max_playlist_size
))
2376 display_buffer_full();
2380 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2381 #ifdef HAVE_DIRCACHE
2382 playlist
->filenames
[playlist
->amount
] = NULL
;
2386 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2387 playlist
->buffer_end_pos
+= len
;
2388 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2393 /* shuffle newly created playlist using random seed. */
2394 int playlist_shuffle(int random_seed
, int start_index
)
2396 struct playlist_info
* playlist
= ¤t_playlist
;
2398 unsigned int seek_pos
= 0;
2399 bool start_current
= false;
2401 if (start_index
>= 0 && global_settings
.play_selected
)
2403 /* store the seek position before the shuffle */
2404 seek_pos
= playlist
->indices
[start_index
];
2405 playlist
->index
= playlist
->first_index
= start_index
;
2406 start_current
= true;
2409 randomise_playlist(playlist
, random_seed
, start_current
, true);
2411 return playlist
->index
;
2414 /* start playing current playlist at specified index/offset */
2415 void playlist_start(int start_index
, int offset
)
2417 struct playlist_info
* playlist
= ¤t_playlist
;
2419 /* Cancel FM radio selection as previous music. For cases where we start
2420 playback without going to the WPS, such as playlist insert.. or
2421 playlist catalog. */
2422 previous_music_is_wps();
2424 playlist
->index
= start_index
;
2426 #if CONFIG_CODEC != SWCODEC
2427 talk_buffer_steal(); /* will use the mp3 buffer */
2430 playlist
->started
= true;
2431 sync_control(playlist
, false);
2435 /* Returns false if 'steps' is out of bounds, else true */
2436 bool playlist_check(int steps
)
2438 struct playlist_info
* playlist
= ¤t_playlist
;
2440 /* always allow folder navigation */
2441 if (global_settings
.next_folder
&& playlist
->in_ram
)
2444 int index
= get_next_index(playlist
, steps
, -1);
2446 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2447 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2449 return (index
>= 0);
2452 /* get trackname of track that is "steps" away from current playing track.
2453 NULL is used to identify end of playlist */
2454 char* playlist_peek(int steps
)
2456 struct playlist_info
* playlist
= ¤t_playlist
;
2462 index
= get_next_index(playlist
, steps
, -1);
2466 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2467 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2469 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2473 temp_ptr
= now_playing
;
2475 if (!playlist
->in_ram
|| control_file
)
2477 /* remove bogus dirs from beginning of path
2478 (workaround for buggy playlist creation tools) */
2481 if (file_exists(temp_ptr
))
2484 temp_ptr
= strchr(temp_ptr
+1, '/');
2489 /* Even though this is an invalid file, we still need to pass a
2490 file name to the caller because NULL is used to indicate end
2500 * Update indices as track has changed
2502 int playlist_next(int steps
)
2504 struct playlist_info
* playlist
= ¤t_playlist
;
2508 #ifdef AB_REPEAT_ENABLE
2509 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2511 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2515 /* We need to delete all the queued songs */
2516 for (i
=0, j
=steps
; i
<j
; i
++)
2518 index
= get_next_index(playlist
, i
, -1);
2520 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2522 remove_track_from_playlist(playlist
, index
, true);
2523 steps
--; /* one less track */
2528 index
= get_next_index(playlist
, steps
, -1);
2532 /* end of playlist... or is it */
2533 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2534 playlist
->amount
> 1)
2536 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2537 playlist
->first_index
= 0;
2538 sort_playlist(playlist
, false, false);
2539 randomise_playlist(playlist
, current_tick
, false, true);
2540 #if CONFIG_CODEC != SWCODEC
2541 playlist_start(0, 0);
2543 playlist
->index
= 0;
2546 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2548 index
= create_and_play_dir(steps
, true);
2552 playlist
->index
= index
;
2559 playlist
->index
= index
;
2561 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2563 /* check to see if we've gone beyond the last inserted track */
2564 int cur
= rotate_index(playlist
, index
);
2565 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2569 /* reset last inserted track */
2570 playlist
->last_insert_pos
= -1;
2572 if (playlist
->control_fd
>= 0)
2574 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2575 -1, -1, NULL
, NULL
, NULL
);
2580 sync_control(playlist
, false);
2588 /* try playing next or previous folder */
2589 bool playlist_next_dir(int direction
)
2591 /* not to mess up real playlists */
2592 if(!current_playlist
.in_ram
)
2595 return create_and_play_dir(direction
, false) >= 0;
2598 /* Get resume info for current playing song. If return value is -1 then
2599 settings shouldn't be saved. */
2600 int playlist_get_resume_info(int *resume_index
)
2602 struct playlist_info
* playlist
= ¤t_playlist
;
2604 *resume_index
= playlist
->index
;
2609 /* Update resume info for current playing song. Returns -1 on error. */
2610 int playlist_update_resume_info(const struct mp3entry
* id3
)
2612 struct playlist_info
* playlist
= ¤t_playlist
;
2616 if (global_status
.resume_index
!= playlist
->index
||
2617 global_status
.resume_offset
!= id3
->offset
)
2619 global_status
.resume_index
= playlist
->index
;
2620 global_status
.resume_offset
= id3
->offset
;
2626 global_status
.resume_index
= -1;
2627 global_status
.resume_offset
= -1;
2634 /* Returns index of current playing track for display purposes. This value
2635 should not be used for resume purposes as it doesn't represent the actual
2636 index into the playlist */
2637 int playlist_get_display_index(void)
2639 struct playlist_info
* playlist
= ¤t_playlist
;
2641 /* first_index should always be index 0 for display purposes */
2642 int index
= rotate_index(playlist
, playlist
->index
);
2647 /* returns number of tracks in current playlist */
2648 int playlist_amount(void)
2650 return playlist_amount_ex(NULL
);
2652 /* set playlist->last_shuffle_start to playlist->amount for
2653 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2654 void playlist_set_last_shuffled_start(void)
2656 struct playlist_info
* playlist
= ¤t_playlist
;
2657 playlist
->last_shuffled_start
= playlist
->amount
;
2660 * Create a new playlist If playlist is not NULL then we're loading a
2661 * playlist off disk for viewing/editing. The index_buffer is used to store
2662 * playlist indices (required for and only used if !current playlist). The
2663 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2665 int playlist_create_ex(struct playlist_info
* playlist
,
2666 const char* dir
, const char* file
,
2667 void* index_buffer
, int index_buffer_size
,
2668 void* temp_buffer
, int temp_buffer_size
)
2671 playlist
= ¤t_playlist
;
2674 /* Initialize playlist structure */
2675 int r
= rand() % 10;
2676 playlist
->current
= false;
2678 /* Use random name for control file */
2679 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2680 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2682 playlist
->control_fd
= -1;
2686 int num_indices
= index_buffer_size
/ sizeof(int);
2688 #ifdef HAVE_DIRCACHE
2691 if (num_indices
> global_settings
.max_files_in_playlist
)
2692 num_indices
= global_settings
.max_files_in_playlist
;
2694 playlist
->max_playlist_size
= num_indices
;
2695 playlist
->indices
= index_buffer
;
2696 #ifdef HAVE_DIRCACHE
2697 playlist
->filenames
= (const struct dircache_entry
**)
2698 &playlist
->indices
[num_indices
];
2703 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2704 playlist
->indices
= current_playlist
.indices
;
2705 #ifdef HAVE_DIRCACHE
2706 playlist
->filenames
= current_playlist
.filenames
;
2710 playlist
->buffer_size
= 0;
2711 playlist
->buffer
= NULL
;
2712 mutex_init(&playlist
->control_mutex
);
2715 new_playlist(playlist
, dir
, file
);
2718 /* load the playlist file */
2719 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2725 * Set the specified playlist as the current.
2726 * NOTE: You will get undefined behaviour if something is already playing so
2727 * remember to stop before calling this. Also, this call will
2728 * effectively close your playlist, making it unusable.
2730 int playlist_set_current(struct playlist_info
* playlist
)
2732 if (!playlist
|| (check_control(playlist
) < 0))
2735 empty_playlist(¤t_playlist
, false);
2737 strlcpy(current_playlist
.filename
, playlist
->filename
,
2738 sizeof(current_playlist
.filename
));
2740 current_playlist
.utf8
= playlist
->utf8
;
2741 current_playlist
.fd
= playlist
->fd
;
2743 close(playlist
->control_fd
);
2744 close(current_playlist
.control_fd
);
2745 remove(current_playlist
.control_filename
);
2746 if (rename(playlist
->control_filename
,
2747 current_playlist
.control_filename
) < 0)
2749 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2751 if (current_playlist
.control_fd
< 0)
2753 current_playlist
.control_created
= true;
2755 current_playlist
.dirlen
= playlist
->dirlen
;
2757 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2759 memcpy(current_playlist
.indices
, playlist
->indices
,
2760 playlist
->max_playlist_size
*sizeof(int));
2761 #ifdef HAVE_DIRCACHE
2762 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2763 playlist
->max_playlist_size
*sizeof(int));
2767 current_playlist
.first_index
= playlist
->first_index
;
2768 current_playlist
.amount
= playlist
->amount
;
2769 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2770 current_playlist
.seed
= playlist
->seed
;
2771 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2772 current_playlist
.deleted
= playlist
->deleted
;
2773 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2775 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2776 sizeof(current_playlist
.control_cache
));
2777 current_playlist
.num_cached
= playlist
->num_cached
;
2778 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2782 struct playlist_info
*playlist_get_current(void)
2784 return ¤t_playlist
;
2787 * Close files and delete control file for non-current playlist.
2789 void playlist_close(struct playlist_info
* playlist
)
2794 if (playlist
->fd
>= 0)
2795 close(playlist
->fd
);
2797 if (playlist
->control_fd
>= 0)
2798 close(playlist
->control_fd
);
2800 if (playlist
->control_created
)
2801 remove(playlist
->control_filename
);
2804 void playlist_sync(struct playlist_info
* playlist
)
2807 playlist
= ¤t_playlist
;
2809 sync_control(playlist
, false);
2810 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2811 audio_flush_and_reload_tracks();
2813 #ifdef HAVE_DIRCACHE
2814 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2819 * Insert track into playlist at specified position (or one of the special
2820 * positions). Returns position where track was inserted or -1 if error.
2822 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2823 int position
, bool queue
, bool sync
)
2828 playlist
= ¤t_playlist
;
2830 if (check_control(playlist
) < 0)
2832 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2836 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2838 /* Check if we want manually sync later. For example when adding
2839 * bunch of files from tagcache, syncing after every file wouldn't be
2840 * a good thing to do. */
2841 if (sync
&& result
>= 0)
2842 playlist_sync(playlist
);
2848 * Insert all tracks from specified directory into playlist.
2850 int playlist_insert_directory(struct playlist_info
* playlist
,
2851 const char *dirname
, int position
, bool queue
,
2855 unsigned char *count_str
;
2856 struct directory_search_context context
;
2859 playlist
= ¤t_playlist
;
2861 if (check_control(playlist
) < 0)
2863 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2867 if (position
== PLAYLIST_REPLACE
)
2869 if (playlist_remove_all_tracks(playlist
) == 0)
2870 position
= PLAYLIST_INSERT_LAST
;
2876 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2878 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2880 display_playlist_count(0, count_str
, false);
2882 context
.playlist
= playlist
;
2883 context
.position
= position
;
2884 context
.queue
= queue
;
2889 result
= playlist_directory_tracksearch(dirname
, recurse
,
2890 directory_search_callback
, &context
);
2892 sync_control(playlist
, false);
2896 display_playlist_count(context
.count
, count_str
, true);
2898 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2899 audio_flush_and_reload_tracks();
2901 #ifdef HAVE_DIRCACHE
2902 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2909 * Insert all tracks from specified playlist into dynamic playlist.
2911 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2912 int position
, bool queue
)
2918 unsigned char *count_str
;
2919 char temp_buf
[MAX_PATH
+1];
2920 char trackname
[MAX_PATH
+1];
2923 bool utf8
= is_m3u8(filename
);
2926 playlist
= ¤t_playlist
;
2928 if (check_control(playlist
) < 0)
2930 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2934 fd
= open_utf8(filename
, O_RDONLY
);
2937 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2941 /* we need the directory name for formatting purposes */
2944 temp_ptr
= strrchr(filename
+1,'/');
2951 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2953 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2955 display_playlist_count(count
, count_str
, false);
2957 if (position
== PLAYLIST_REPLACE
)
2959 if (playlist_remove_all_tracks(playlist
) == 0)
2960 position
= PLAYLIST_INSERT_LAST
;
2966 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2969 if (action_userabort(TIMEOUT_NOBLOCK
))
2972 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2978 /* Use trackname as a temporay buffer. Note that trackname must
2979 * be as large as temp_buf.
2981 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
2984 /* we need to format so that relative paths are correctly
2986 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2993 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3002 /* Make sure tracks are inserted in correct order if user
3003 requests INSERT_FIRST */
3004 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3005 position
= insert_pos
+ 1;
3009 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3011 display_playlist_count(count
, count_str
, false);
3013 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3014 (audio_status() & AUDIO_STATUS_PLAY
) &&
3016 audio_flush_and_reload_tracks();
3020 /* let the other threads work */
3029 sync_control(playlist
, false);
3033 display_playlist_count(count
, count_str
, true);
3035 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3036 audio_flush_and_reload_tracks();
3038 #ifdef HAVE_DIRCACHE
3039 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3046 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3047 * we want to delete the current playing track.
3049 int playlist_delete(struct playlist_info
* playlist
, int index
)
3054 playlist
= ¤t_playlist
;
3056 if (check_control(playlist
) < 0)
3058 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3062 if (index
== PLAYLIST_DELETE_CURRENT
)
3063 index
= playlist
->index
;
3065 result
= remove_track_from_playlist(playlist
, index
, true);
3067 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3069 audio_flush_and_reload_tracks();
3075 * Move track at index to new_index. Tracks between the two are shifted
3076 * appropriately. Returns 0 on success and -1 on failure.
3078 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3084 bool current
= false;
3086 char filename
[MAX_PATH
];
3089 playlist
= ¤t_playlist
;
3091 if (check_control(playlist
) < 0)
3093 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3097 if (index
== new_index
)
3100 if (index
== playlist
->index
)
3101 /* Moving the current track */
3104 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3105 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3106 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3108 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3109 sizeof(filename
)) < 0)
3112 /* We want to insert the track at the position that was specified by
3113 new_index. This may be different then new_index because of the
3114 shifting that will occur after the delete.
3115 We calculate this before we do the remove as it depends on the
3116 size of the playlist before the track removal */
3117 r
= rotate_index(playlist
, new_index
);
3119 /* Delete track from original position */
3120 result
= remove_track_from_playlist(playlist
, index
, true);
3126 new_index
= PLAYLIST_PREPEND
;
3127 else if (r
== playlist
->amount
)
3129 new_index
= PLAYLIST_INSERT_LAST
;
3131 /* Calculate index of desired position */
3132 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3134 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3141 /* Moved the current track */
3144 case PLAYLIST_PREPEND
:
3145 playlist
->index
= playlist
->first_index
;
3147 case PLAYLIST_INSERT_LAST
:
3148 playlist
->index
= playlist
->first_index
- 1;
3149 if (playlist
->index
< 0)
3150 playlist
->index
+= playlist
->amount
;
3153 playlist
->index
= new_index
;
3158 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3159 audio_flush_and_reload_tracks();
3163 #ifdef HAVE_DIRCACHE
3164 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3170 /* shuffle currently playing playlist */
3171 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3177 playlist
= ¤t_playlist
;
3179 check_control(playlist
);
3181 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3183 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3185 audio_flush_and_reload_tracks();
3190 /* sort currently playing playlist */
3191 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3196 playlist
= ¤t_playlist
;
3198 check_control(playlist
);
3200 result
= sort_playlist(playlist
, start_current
, true);
3202 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3204 audio_flush_and_reload_tracks();
3209 /* returns true if playlist has been modified */
3210 bool playlist_modified(const struct playlist_info
* playlist
)
3213 playlist
= ¤t_playlist
;
3215 if (playlist
->shuffle_modified
||
3216 playlist
->deleted
||
3217 playlist
->num_inserted_tracks
> 0)
3223 /* returns index of first track in playlist */
3224 int playlist_get_first_index(const struct playlist_info
* playlist
)
3227 playlist
= ¤t_playlist
;
3229 return playlist
->first_index
;
3232 /* returns shuffle seed of playlist */
3233 int playlist_get_seed(const struct playlist_info
* playlist
)
3236 playlist
= ¤t_playlist
;
3238 return playlist
->seed
;
3241 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3242 int playlist_amount_ex(const struct playlist_info
* playlist
)
3245 playlist
= ¤t_playlist
;
3247 return playlist
->amount
;
3250 /* returns full path of playlist (minus extension) */
3251 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3257 playlist
= ¤t_playlist
;
3259 strlcpy(buf
, playlist
->filename
+playlist
->dirlen
, buf_size
);
3264 /* Remove extension */
3265 sep
= strrchr(buf
, '.');
3272 /* returns the playlist filename */
3273 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3277 playlist
= ¤t_playlist
;
3279 strlcpy(buf
, playlist
->filename
, buf_size
);
3287 /* Fills info structure with information about track at specified index.
3288 Returns 0 on success and -1 on failure */
3289 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3290 struct playlist_track_info
* info
)
3296 playlist
= ¤t_playlist
;
3298 if (index
< 0 || index
>= playlist
->amount
)
3301 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3302 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3304 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3305 sizeof(info
->filename
)) < 0)
3312 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3313 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3315 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3319 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3320 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3322 info
->index
= index
;
3323 info
->display_index
= rotate_index(playlist
, index
) + 1;
3328 /* save the current dynamic playlist to specified file */
3329 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3334 char path
[MAX_PATH
+1];
3335 char tmp_buf
[MAX_PATH
+1];
3337 bool overwrite_current
= false;
3338 int* index_buf
= NULL
;
3341 playlist
= ¤t_playlist
;
3343 if (playlist
->amount
<= 0)
3346 /* use current working directory as base for pathname */
3347 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3348 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3351 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3353 /* Attempting to overwrite current playlist file.*/
3355 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3357 /* not enough buffer space to store updated indices */
3358 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3362 /* in_ram buffer is unused for m3u files so we'll use for storing
3364 index_buf
= (int*)playlist
->buffer
;
3366 /* use temporary pathname */
3367 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3368 overwrite_current
= true;
3373 fd
= open_utf8(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3377 /* some applications require a BOM to read the file properly */
3378 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3382 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3386 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3390 index
= playlist
->first_index
;
3391 for (i
=0; i
<playlist
->amount
; i
++)
3398 if (action_userabort(TIMEOUT_NOBLOCK
))
3404 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3405 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3406 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3408 /* Don't save queued files */
3411 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3418 if (overwrite_current
)
3419 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3421 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3423 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3430 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3431 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3437 index
= (index
+1)%playlist
->amount
;
3440 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3444 if (overwrite_current
&& result
>= 0)
3448 mutex_lock(&playlist
->control_mutex
);
3450 /* Replace the current playlist with the new one and update indices */
3451 close(playlist
->fd
);
3452 if (remove(playlist
->filename
) >= 0)
3454 if (rename(path
, playlist
->filename
) >= 0)
3456 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3457 if (playlist
->fd
>= 0)
3459 index
= playlist
->first_index
;
3460 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3462 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3464 playlist
->indices
[index
] = index_buf
[count
];
3467 index
= (index
+1)%playlist
->amount
;
3470 /* we need to recreate control because inserted tracks are
3471 now part of the playlist and shuffle has been
3473 result
= recreate_control(playlist
);
3478 mutex_unlock(&playlist
->control_mutex
);
3488 * Search specified directory for tracks and notify via callback. May be
3489 * called recursively.
3491 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3492 int (*callback
)(char*, void*),
3495 char buf
[MAX_PATH
+1];
3499 struct entry
*files
;
3500 struct tree_context
* tc
= tree_get_context();
3501 int old_dirfilter
= *(tc
->dirfilter
);
3506 /* use the tree browser dircache to load files */
3507 *(tc
->dirfilter
) = SHOW_ALL
;
3509 if (ft_load(tc
, dirname
) < 0)
3511 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3512 *(tc
->dirfilter
) = old_dirfilter
;
3516 files
= (struct entry
*) tc
->dircache
;
3517 num_files
= tc
->filesindir
;
3519 /* we've overwritten the dircache so tree browser will need to be
3523 for (i
=0; i
<num_files
; i
++)
3526 if (action_userabort(TIMEOUT_NOBLOCK
))
3532 if (files
[i
].attr
& ATTR_DIRECTORY
)
3536 /* recursively add directories */
3537 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3538 result
= playlist_directory_tracksearch(buf
, recurse
,
3543 /* we now need to reload our current directory */
3544 if(ft_load(tc
, dirname
) < 0)
3550 files
= (struct entry
*) tc
->dircache
;
3551 num_files
= tc
->filesindir
;
3561 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3563 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3565 if (callback(buf
, context
) != 0)
3571 /* let the other threads work */
3576 /* restore dirfilter */
3577 *(tc
->dirfilter
) = old_dirfilter
;