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.
73 #include "string-extra.h"
75 #include "ata_idle_notify.h"
85 #include "applimits.h"
87 #include "core_alloc.h"
89 #include "filefuncs.h"
95 #include "filetypes.h"
96 #ifdef HAVE_LCD_BITMAP
104 #include "rbunicode.h"
105 #include "root_menu.h"
106 #include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
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
;
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
, const char *subdir
, bool recurse
);
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 SHAREDBSS_ATTR
;
197 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x800)/sizeof(long)];
198 static const char playlist_thread_name
[] = "playlist cachectrl";
201 static struct mutex current_playlist_mutex SHAREDBSS_ATTR
;
202 static struct mutex created_playlist_mutex SHAREDBSS_ATTR
;
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);
214 /* Convert a filename in an M3U playlist to UTF-8.
216 * buf - the filename to convert; can contain more than one line from the
218 * buf_len - amount of buf that is used.
219 * buf_max - total size of buf.
220 * temp - temporary conversion buffer, at least buf_max bytes.
222 * Returns the length of the converted filename.
224 static int convert_m3u(char* buf
, int buf_len
, int buf_max
, char* temp
)
230 while ((buf
[i
] != '\n') && (buf
[i
] != '\r') && (i
< buf_len
))
235 /* Work back killing white space. */
236 while ((i
> 0) && isspace(buf
[i
- 1]))
244 /* Convert char by char, so as to not overflow temp (iso_decode should
245 * preferably handle this). No more than 4 bytes should be generated for
248 for (i
= 0; i
< buf_len
&& dest
< (temp
+ buf_max
- 4); i
++)
250 dest
= iso_decode(&buf
[i
], dest
, -1, 1);
259 * remove any files and indices associated with the playlist
261 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
263 playlist
->filename
[0] = '\0';
264 playlist
->utf8
= true;
266 if(playlist
->fd
>= 0)
267 /* If there is an already open playlist, close it. */
271 if(playlist
->control_fd
>= 0)
272 close(playlist
->control_fd
);
273 playlist
->control_fd
= -1;
274 playlist
->control_created
= false;
276 playlist
->in_ram
= false;
278 if (playlist
->buffer
)
279 playlist
->buffer
[0] = 0;
281 playlist
->buffer_end_pos
= 0;
284 playlist
->first_index
= 0;
285 playlist
->amount
= 0;
286 playlist
->last_insert_pos
= -1;
288 playlist
->shuffle_modified
= false;
289 playlist
->deleted
= false;
290 playlist
->num_inserted_tracks
= 0;
291 playlist
->started
= false;
293 playlist
->num_cached
= 0;
294 playlist
->pending_control_sync
= false;
296 if (!resume
&& playlist
->current
)
298 /* start with fresh playlist control file when starting new
300 create_control(playlist
);
305 * Initialize a new playlist for viewing/editing/playing. dir is the
306 * directory where the playlist is located and file is the filename.
308 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
311 const char *fileused
= file
;
312 const char *dirused
= dir
;
313 empty_playlist(playlist
, false);
319 if (dirused
&& playlist
->current
) /* !current cannot be in_ram */
320 playlist
->in_ram
= true;
322 dirused
= ""; /* empty playlist */
325 update_playlist_filename(playlist
, dirused
, fileused
);
327 if (playlist
->control_fd
>= 0)
329 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
330 PLAYLIST_CONTROL_FILE_VERSION
, -1, dirused
, fileused
, NULL
);
331 sync_control(playlist
, false);
336 * create control file for playlist
338 static void create_control(struct playlist_info
* playlist
)
340 playlist
->control_fd
= open(playlist
->control_filename
,
341 O_CREAT
|O_RDWR
|O_TRUNC
, 0666);
342 if (playlist
->control_fd
< 0)
344 if (check_rockboxdir())
346 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
347 splashf(HZ
*2, (unsigned char *)"%s (%d)",
348 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
349 playlist
->control_fd
);
351 playlist
->control_created
= false;
355 playlist
->control_created
= true;
360 * validate the control file. This may include creating/initializing it if
363 static int check_control(struct playlist_info
* playlist
)
365 if (!playlist
->control_created
)
367 create_control(playlist
);
369 if (playlist
->control_fd
>= 0)
371 char* dir
= playlist
->filename
;
372 char* file
= playlist
->filename
+playlist
->dirlen
;
373 char c
= playlist
->filename
[playlist
->dirlen
-1];
375 playlist
->filename
[playlist
->dirlen
-1] = '\0';
377 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
378 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
379 sync_control(playlist
, false);
380 playlist
->filename
[playlist
->dirlen
-1] = c
;
384 if (playlist
->control_fd
< 0)
391 * recreate the control file based on current playlist entries
393 static int recreate_control(struct playlist_info
* playlist
)
395 char temp_file
[MAX_PATH
+1];
400 if(playlist
->control_fd
>= 0)
402 char* dir
= playlist
->filename
;
403 char* file
= playlist
->filename
+playlist
->dirlen
;
404 char c
= playlist
->filename
[playlist
->dirlen
-1];
406 close(playlist
->control_fd
);
408 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
409 playlist
->control_filename
);
411 if (rename(playlist
->control_filename
, temp_file
) < 0)
414 temp_fd
= open(temp_file
, O_RDONLY
);
418 playlist
->control_fd
= open(playlist
->control_filename
,
419 O_CREAT
|O_RDWR
|O_TRUNC
, 0666);
420 if (playlist
->control_fd
< 0)
423 playlist
->filename
[playlist
->dirlen
-1] = '\0';
425 /* cannot call update_control() because of mutex */
426 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
427 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
429 playlist
->filename
[playlist
->dirlen
-1] = c
;
439 playlist
->shuffle_modified
= false;
440 playlist
->deleted
= false;
441 playlist
->num_inserted_tracks
= 0;
443 for (i
=0; i
<playlist
->amount
; i
++)
445 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
447 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
448 char inserted_file
[MAX_PATH
+1];
450 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
452 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
454 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
455 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
458 /* save the position in file where name is written */
459 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
461 result
= fdprintf(playlist
->control_fd
, "%s\n",
464 playlist
->indices
[i
] =
465 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
471 playlist
->num_inserted_tracks
++;
477 fsync(playlist
->control_fd
);
486 * store directory and name of playlist file
488 static void update_playlist_filename(struct playlist_info
* playlist
,
489 const char *dir
, const char *file
)
492 int dirlen
= strlen(dir
);
494 playlist
->utf8
= is_m3u8(file
);
496 /* If the dir does not end in trailing slash, we use a separator.
497 Otherwise we don't. */
498 if('/' != dir
[dirlen
-1])
504 playlist
->dirlen
= dirlen
;
506 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
507 "%s%s%s", dir
, sep
, file
);
511 * calculate track offsets within a playlist file
513 static int add_indices_to_playlist(struct playlist_info
* playlist
,
514 char* buffer
, size_t buflen
)
518 unsigned int count
= 0;
523 if(-1 == playlist
->fd
)
524 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
526 return -1; /* failure */
527 if((i
= lseek(playlist
->fd
, 0, SEEK_CUR
)) > 0)
528 playlist
->utf8
= true; /* Override any earlier indication. */
530 splash(0, ID2P(LANG_WAIT
));
534 /* use mp3 buffer for maximum load speed */
536 buffer
= audio_get_buffer(false, &buflen
);
543 nread
= read(playlist
->fd
, buffer
, buflen
);
544 /* Terminate on EOF */
548 p
= (unsigned char *)buffer
;
550 for(count
=0; count
< nread
; count
++,p
++) {
552 /* Are we on a new line? */
553 if((*p
== '\n') || (*p
== '\r'))
563 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
564 display_buffer_full();
569 /* Store a new entry */
570 playlist
->indices
[ playlist
->amount
] = i
+count
;
572 if (playlist
->filenames
)
573 playlist
->filenames
[ playlist
->amount
] = -1;
585 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
592 * Utility function to create a new playlist, fill it with the next or
593 * previous directory, shuffle it if needed, and start playback.
594 * If play_last is true and direction zero or negative, start playing
595 * the last file in the directory, otherwise start playing the first.
597 static int create_and_play_dir(int direction
, bool play_last
)
599 char dir
[MAX_PATH
+ 1];
604 res
= get_next_directory(dir
);
606 res
= get_previous_directory(dir
);
610 if (playlist_create(dir
, NULL
) != -1)
612 ft_build_playlist(tree_get_context(), 0);
614 if (global_settings
.playlist_shuffle
)
615 playlist_shuffle(current_tick
, -1);
617 if (play_last
&& direction
<= 0)
618 index
= current_playlist
.amount
- 1;
622 #if (CONFIG_CODEC == SWCODEC)
623 current_playlist
.started
= true;
625 playlist_start(index
, 0);
629 /* we've overwritten the dircache when getting the next/previous dir,
630 so the tree browser context will need to be reloaded */
638 * Removes all tracks, from the playlist, leaving the presently playing
641 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
645 if (playlist
== NULL
)
646 playlist
= ¤t_playlist
;
648 while (playlist
->index
> 0)
649 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
652 while (playlist
->amount
> 1)
653 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
656 if (playlist
->amount
== 1) {
657 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
665 * Add track to playlist at specified position. There are seven special
666 * positions that can be specified:
667 * PLAYLIST_PREPEND - Add track at beginning of playlist
668 * PLAYLIST_INSERT - Add track after current song. NOTE: If
669 * there are already inserted tracks then track
670 * is added to the end of the insertion list
671 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
672 * matter what other tracks have been inserted
673 * PLAYLIST_INSERT_LAST - Add track to end of playlist
674 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
675 * current playing track and end of playlist
676 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
678 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
679 * and inster this track at the end.
681 static int add_track_to_playlist(struct playlist_info
* playlist
,
682 const char *filename
, int position
,
683 bool queue
, int seek_pos
)
685 int insert_position
, orig_position
;
686 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
689 insert_position
= orig_position
= position
;
691 if (playlist
->amount
>= playlist
->max_playlist_size
)
693 display_buffer_full();
699 case PLAYLIST_PREPEND
:
700 position
= insert_position
= playlist
->first_index
;
702 case PLAYLIST_INSERT
:
703 /* if there are already inserted tracks then add track to end of
704 insertion list else add after current playing track */
705 if (playlist
->last_insert_pos
>= 0 &&
706 playlist
->last_insert_pos
< playlist
->amount
&&
707 (playlist
->indices
[playlist
->last_insert_pos
]&
708 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
709 position
= insert_position
= playlist
->last_insert_pos
+1;
710 else if (playlist
->amount
> 0)
711 position
= insert_position
= playlist
->index
+ 1;
713 position
= insert_position
= 0;
715 playlist
->last_insert_pos
= position
;
717 case PLAYLIST_INSERT_FIRST
:
718 if (playlist
->amount
> 0)
719 position
= insert_position
= playlist
->index
+ 1;
721 position
= insert_position
= 0;
723 playlist
->last_insert_pos
= position
;
725 case PLAYLIST_INSERT_LAST
:
726 if (playlist
->first_index
> 0)
727 position
= insert_position
= playlist
->first_index
;
729 position
= insert_position
= playlist
->amount
;
731 playlist
->last_insert_pos
= position
;
733 case PLAYLIST_INSERT_SHUFFLED
:
735 if (playlist
->started
)
738 int n
= playlist
->amount
-
739 rotate_index(playlist
, playlist
->index
);
746 position
= playlist
->index
+ offset
+ 1;
747 if (position
>= playlist
->amount
)
748 position
-= playlist
->amount
;
750 insert_position
= position
;
753 position
= insert_position
= (rand() % (playlist
->amount
+1));
756 case PLAYLIST_INSERT_LAST_SHUFFLED
:
758 position
= insert_position
= playlist
->last_shuffled_start
+
759 rand() % (playlist
->amount
- playlist
->last_shuffled_start
+ 1);
762 case PLAYLIST_REPLACE
:
763 if (playlist_remove_all_tracks(playlist
) < 0)
766 playlist
->last_insert_pos
= position
= insert_position
= playlist
->index
+ 1;
771 flags
|= PLAYLIST_QUEUED
;
773 /* shift indices so that track can be added */
774 for (i
=playlist
->amount
; i
>insert_position
; i
--)
776 playlist
->indices
[i
] = playlist
->indices
[i
-1];
778 if (playlist
->filenames
)
779 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
783 /* update stored indices if needed */
785 if (orig_position
< 0)
787 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
791 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
792 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
793 playlist
->first_index
++;
796 if (insert_position
< playlist
->last_insert_pos
||
797 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
798 playlist
->last_insert_pos
++;
800 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
802 int result
= update_control(playlist
,
803 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
804 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
810 playlist
->indices
[insert_position
] = flags
| seek_pos
;
813 if (playlist
->filenames
)
814 playlist
->filenames
[insert_position
] = -1;
818 playlist
->num_inserted_tracks
++;
820 return insert_position
;
824 * Callback for playlist_directory_tracksearch to insert track into
827 static int directory_search_callback(char* filename
, void* context
)
829 struct directory_search_context
* c
=
830 (struct directory_search_context
*) context
;
833 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
841 /* Make sure tracks are inserted in correct order if user requests
843 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
844 c
->position
= insert_pos
+ 1;
846 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
848 unsigned char* count_str
;
851 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
853 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
855 display_playlist_count(c
->count
, count_str
, false);
857 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
858 (audio_status() & AUDIO_STATUS_PLAY
) &&
859 c
->playlist
->started
)
860 audio_flush_and_reload_tracks();
867 * remove track at specified position
869 static int remove_track_from_playlist(struct playlist_info
* playlist
,
870 int position
, bool write
)
875 if (playlist
->amount
<= 0)
878 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
880 /* shift indices now that track has been removed */
881 for (i
=position
; i
<playlist
->amount
; i
++)
883 playlist
->indices
[i
] = playlist
->indices
[i
+1];
885 if (playlist
->filenames
)
886 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
893 playlist
->num_inserted_tracks
--;
895 playlist
->deleted
= true;
897 /* update stored indices if needed */
898 if (position
< playlist
->index
)
901 if (position
< playlist
->first_index
)
903 playlist
->first_index
--;
906 if (position
<= playlist
->last_insert_pos
)
907 playlist
->last_insert_pos
--;
909 if (write
&& playlist
->control_fd
>= 0)
911 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
912 position
, -1, NULL
, NULL
, NULL
);
917 sync_control(playlist
, false);
924 * randomly rearrange the array of indices for the playlist. If start_current
925 * is true then update the index to the new index of the current playing track
927 static int randomise_playlist(struct playlist_info
* playlist
,
928 unsigned int seed
, bool start_current
,
934 unsigned int current
= playlist
->indices
[playlist
->index
];
936 /* seed 0 is used to identify sorted playlist for resume purposes */
940 /* seed with the given seed */
943 /* randomise entire indices list */
944 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
946 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
947 candidate
= rand() % (count
+ 1);
949 /* now swap the values at the 'count' and 'candidate' positions */
950 store
= playlist
->indices
[candidate
];
951 playlist
->indices
[candidate
] = playlist
->indices
[count
];
952 playlist
->indices
[count
] = store
;
954 if (playlist
->filenames
)
956 store
= playlist
->filenames
[candidate
];
957 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
958 playlist
->filenames
[count
] = store
;
964 find_and_set_playlist_index(playlist
, current
);
966 /* indices have been moved so last insert position is no longer valid */
967 playlist
->last_insert_pos
= -1;
969 playlist
->seed
= seed
;
970 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
971 playlist
->shuffle_modified
= true;
975 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
976 playlist
->first_index
, NULL
, NULL
, NULL
);
983 * Sort the array of indices for the playlist. If start_current is true then
984 * set the index to the new index of the current song.
985 * Also while going to unshuffled mode set the first_index to 0.
987 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
990 unsigned int current
= playlist
->indices
[playlist
->index
];
992 if (playlist
->amount
> 0)
993 qsort(playlist
->indices
, playlist
->amount
,
994 sizeof(playlist
->indices
[0]), compare
);
997 /** We need to re-check the song names from disk because qsort can't
998 * sort two arrays at once :/
999 * FIXME: Please implement a better way to do this. */
1000 memset(playlist
->filenames
, 0xff, playlist
->max_playlist_size
* sizeof(int));
1001 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1005 find_and_set_playlist_index(playlist
, current
);
1007 /* indices have been moved so last insert position is no longer valid */
1008 playlist
->last_insert_pos
= -1;
1010 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1011 playlist
->shuffle_modified
= false;
1012 if (write
&& playlist
->control_fd
>= 0)
1014 playlist
->first_index
= 0;
1015 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1016 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1022 /* Calculate how many steps we have to really step when skipping entries
1025 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1027 int i
, count
, direction
;
1029 int stepped_count
= 0;
1042 index
= playlist
->index
;
1045 /* Boundary check */
1047 index
+= playlist
->amount
;
1048 if (index
>= playlist
->amount
)
1049 index
-= playlist
->amount
;
1051 /* Check if we found a bad entry. */
1052 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1055 /* Are all entries bad? */
1056 if (stepped_count
++ > playlist
->amount
)
1063 } while (i
<= count
);
1068 /* Marks the index of the track to be skipped that is "steps" away from
1069 * current playing track.
1071 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1075 if (playlist
== NULL
)
1076 playlist
= ¤t_playlist
;
1078 /* need to account for already skipped tracks */
1079 steps
= calculate_step_count(playlist
, steps
);
1081 index
= playlist
->index
+ steps
;
1083 index
+= playlist
->amount
;
1084 else if (index
>= playlist
->amount
)
1085 index
-= playlist
->amount
;
1087 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1091 * returns the index of the track that is "steps" away from current playing
1094 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1097 int current_index
= playlist
->index
;
1098 int next_index
= -1;
1100 if (playlist
->amount
<= 0)
1103 if (repeat_mode
== -1)
1104 repeat_mode
= global_settings
.repeat_mode
;
1106 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1107 repeat_mode
= REPEAT_ALL
;
1109 steps
= calculate_step_count(playlist
, steps
);
1110 switch (repeat_mode
)
1112 case REPEAT_SHUFFLE
:
1113 /* Treat repeat shuffle just like repeat off. At end of playlist,
1114 play will be resumed in playlist_next() */
1117 current_index
= rotate_index(playlist
, current_index
);
1118 next_index
= current_index
+steps
;
1119 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1122 next_index
= (next_index
+playlist
->first_index
) %
1129 #ifdef AB_REPEAT_ENABLE
1132 next_index
= current_index
;
1138 next_index
= (current_index
+steps
) % playlist
->amount
;
1139 while (next_index
< 0)
1140 next_index
+= playlist
->amount
;
1142 if (steps
>= playlist
->amount
)
1149 /* second time around so skip the queued files */
1150 for (i
=0; i
<playlist
->amount
; i
++)
1152 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1153 index
= (index
+1) % playlist
->amount
;
1165 /* No luck if the whole playlist was bad. */
1166 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1173 * Search for the seek track and set appropriate indices. Used after shuffle
1174 * to make sure the current index is still pointing to correct track.
1176 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1181 /* Set the index to the current song */
1182 for (i
=0; i
<playlist
->amount
; i
++)
1184 if (playlist
->indices
[i
] == seek
)
1186 playlist
->index
= playlist
->first_index
= i
;
1194 * used to sort track indices. Sort order is as follows:
1195 * 1. Prepended tracks (in prepend order)
1196 * 2. Playlist/directory tracks (in playlist order)
1197 * 3. Inserted/Appended tracks (in insert order)
1199 static int compare(const void* p1
, const void* p2
)
1201 unsigned long* e1
= (unsigned long*) p1
;
1202 unsigned long* e2
= (unsigned long*) p2
;
1203 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1204 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1206 if (flags1
== flags2
)
1207 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1208 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1209 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1211 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1212 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1214 else if (flags1
&& flags2
)
1215 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1220 #ifdef HAVE_DIRCACHE
1222 * Thread to update filename pointers to dircache on background
1223 * without affecting playlist load up performance. This thread also flushes
1224 * any pending control commands when the disk spins up.
1226 static void playlist_flush_callback(void *param
)
1229 struct playlist_info
*playlist
;
1230 playlist
= ¤t_playlist
;
1231 if (playlist
->control_fd
>= 0)
1233 if (playlist
->num_cached
> 0)
1235 mutex_lock(playlist
->control_mutex
);
1236 flush_cached_control(playlist
);
1237 mutex_unlock(playlist
->control_mutex
);
1239 sync_control(playlist
, true);
1243 static bool is_dircache_pointers_intact(void)
1245 return dircache_get_appflag(DIRCACHE_APPFLAG_PLAYLIST
) ? true : false;
1248 static void playlist_thread(void)
1250 struct queue_event ev
;
1251 bool dirty_pointers
= false;
1252 static char tmp
[MAX_PATH
+1];
1254 struct playlist_info
*playlist
;
1261 #ifdef HAVE_DISK_STORAGE
1262 if (global_settings
.disk_spindown
> 1 &&
1263 global_settings
.disk_spindown
<= 5)
1264 sleep_time
= global_settings
.disk_spindown
- 1;
1269 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1273 case PLAYLIST_LOAD_POINTERS
:
1274 dirty_pointers
= true;
1277 /* Start the background scanning after either the disk spindown
1278 timeout or 5s, whichever is less */
1281 playlist
= ¤t_playlist
;
1282 if (playlist
->control_fd
>= 0)
1284 if (playlist
->num_cached
> 0)
1285 register_storage_idle_func(playlist_flush_callback
);
1288 if (!dircache_is_enabled() || !playlist
->filenames
1289 || playlist
->amount
<= 0)
1294 /* Check if previously loaded pointers are intact. */
1295 if (is_dircache_pointers_intact() && !dirty_pointers
)
1298 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1301 for (index
= 0; index
< playlist
->amount
1302 && queue_empty(&playlist_queue
); index
++)
1304 /* Process only pointers that are not already loaded. */
1305 if (is_dircache_pointers_intact() && playlist
->filenames
[index
] >= 0)
1308 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1309 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1311 /* Load the filename from playlist file. */
1312 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1318 /* Set the dircache entry pointer. */
1319 playlist
->filenames
[index
] = dircache_get_entry_id(tmp
);
1321 /* And be on background so user doesn't notice any delays. */
1325 if (dircache_is_enabled())
1326 dircache_set_appflag(DIRCACHE_APPFLAG_PLAYLIST
);
1328 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1331 if (index
== playlist
->amount
)
1332 dirty_pointers
= false;
1337 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
1338 case SYS_USB_CONNECTED
:
1339 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1340 usb_wait_for_disconnect(&playlist_queue
);
1349 * gets pathname for track at seek index
1351 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1352 bool control_file
, char *buf
, int buf_length
)
1356 char tmp_buf
[MAX_PATH
+1];
1357 char dir_buf
[MAX_PATH
+1];
1358 bool utf8
= playlist
->utf8
;
1360 if (buf_length
> MAX_PATH
+1)
1361 buf_length
= MAX_PATH
+1;
1363 #ifdef HAVE_DIRCACHE
1364 if (is_dircache_pointers_intact() && playlist
->filenames
)
1366 if (playlist
->filenames
[index
] >= 0)
1368 max
= dircache_copy_path(playlist
->filenames
[index
],
1369 tmp_buf
, sizeof(tmp_buf
)-1);
1376 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1378 max
= strlcpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1382 mutex_lock(playlist
->control_mutex
);
1386 fd
= playlist
->control_fd
;
1391 if(-1 == playlist
->fd
)
1392 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1400 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1404 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1406 if ((max
> 0) && !utf8
)
1408 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1409 * be as large as tmp_buf.
1411 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1416 mutex_unlock(playlist
->control_mutex
);
1421 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1423 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1429 strlcpy(dir_buf
, playlist
->filename
, playlist
->dirlen
);
1431 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1434 static int get_next_directory(char *dir
){
1435 return get_next_dir(dir
,true,false);
1438 static int get_previous_directory(char *dir
){
1439 return get_next_dir(dir
,false,false);
1443 * search through all the directories (starting with the current) to find
1444 * one that has tracks to play
1446 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1448 struct playlist_info
* playlist
= ¤t_playlist
;
1450 char *start_dir
= NULL
;
1453 struct tree_context
* tc
= tree_get_context();
1454 int saved_dirfilter
= *(tc
->dirfilter
);
1456 /* process random folder advance */
1457 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1459 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat", O_RDONLY
);
1462 char buffer
[MAX_PATH
];
1463 int folder_count
= 0;
1464 srand(current_tick
);
1465 *(tc
->dirfilter
) = SHOW_MUSIC
;
1466 tc
->sort_dir
= global_settings
.sort_dir
;
1467 read(fd
,&folder_count
,sizeof(int));
1472 i
= rand()%folder_count
;
1473 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1474 read(fd
,buffer
,MAX_PATH
);
1475 if (check_subdir_for_music(buffer
, "", false) ==0)
1481 *(tc
->dirfilter
) = saved_dirfilter
;
1482 tc
->sort_dir
= global_settings
.sort_dir
;
1488 /* not random folder advance (or random folder advance unavailable) */
1491 /* start with root */
1496 /* start with current directory */
1497 strlcpy(dir
, playlist
->filename
, playlist
->dirlen
);
1500 /* use the tree browser dircache to load files */
1501 *(tc
->dirfilter
) = SHOW_ALL
;
1503 /* set up sorting/direction */
1504 tc
->sort_dir
= global_settings
.sort_dir
;
1507 static const char sortpairs
[] =
1509 [SORT_ALPHA
] = SORT_ALPHA_REVERSED
,
1510 [SORT_DATE
] = SORT_DATE_REVERSED
,
1511 [SORT_TYPE
] = SORT_TYPE_REVERSED
,
1512 [SORT_ALPHA_REVERSED
] = SORT_ALPHA
,
1513 [SORT_DATE_REVERSED
] = SORT_DATE
,
1514 [SORT_TYPE_REVERSED
] = SORT_TYPE
,
1517 if ((unsigned)tc
->sort_dir
< sizeof(sortpairs
))
1518 tc
->sort_dir
= sortpairs
[tc
->sort_dir
];
1523 struct entry
*files
;
1527 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1534 files
= tc
->cache
.entries
;
1535 num_files
= tc
->filesindir
;
1537 for (i
=0; i
<num_files
; i
++)
1540 if (action_userabort(TIMEOUT_NOBLOCK
))
1547 if (files
[i
].attr
& ATTR_DIRECTORY
)
1551 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1558 else if (!strcmp(start_dir
, files
[i
].name
))
1565 /* move down to parent directory. current directory name is
1566 stored as the starting point for the search in parent */
1567 start_dir
= strrchr(dir
, '/');
1578 /* restore dirfilter */
1579 *(tc
->dirfilter
) = saved_dirfilter
;
1580 tc
->sort_dir
= global_settings
.sort_dir
;
1582 /* special case if nothing found: try start searching again from root */
1583 if (result
== -1 && !recursion
){
1584 result
= get_next_dir(dir
, is_forward
, true);
1591 * Checks if there are any music files in the dir or any of its
1592 * subdirectories. May be called recursively.
1594 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
)
1597 int dirlen
= strlen(dir
);
1600 struct entry
*files
;
1601 bool has_music
= false;
1602 bool has_subdir
= false;
1603 struct tree_context
* tc
= tree_get_context();
1605 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1607 if (ft_load(tc
, dir
) < 0)
1612 files
= tc
->cache
.entries
;
1613 num_files
= tc
->filesindir
;
1615 for (i
=0; i
<num_files
; i
++)
1617 if (files
[i
].attr
& ATTR_DIRECTORY
)
1619 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1629 if (has_subdir
&& recurse
)
1631 for (i
=0; i
<num_files
; i
++)
1633 if (action_userabort(TIMEOUT_NOBLOCK
))
1639 if (files
[i
].attr
& ATTR_DIRECTORY
)
1641 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1659 /* we now need to reload our current directory */
1660 if(ft_load(tc
, dir
) < 0)
1661 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1667 * Returns absolute path of track
1669 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1676 /* Look for the end of the string */
1683 /* Now work back killing white space */
1685 ((src
[i
-1] == ' ') ||
1686 (src
[i
-1] == '\t')))
1689 /* Zero-terminate the file name */
1692 /* replace backslashes with forward slashes */
1693 for ( j
=0; j
<i
; j
++ )
1694 if ( src
[j
] == '\\' )
1699 strlcpy(dest
, src
, buf_length
);
1703 /* handle dos style drive letter */
1705 strlcpy(dest
, &src
[2], buf_length
);
1706 else if (!strncmp(src
, "../", 3))
1708 /* handle relative paths */
1710 while(!strncmp(&src
[i
], "../", 3))
1712 for (j
=0; j
<i
/3; j
++) {
1713 temp_ptr
= strrchr(dir
, '/');
1719 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1721 else if ( '.' == src
[0] && '/' == src
[1] ) {
1722 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1725 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1733 * Display splash message showing progress of playlist/directory insertion or
1736 static void display_playlist_count(int count
, const unsigned char *fmt
,
1739 static long talked_tick
= 0;
1740 long id
= P2ID(fmt
);
1741 if(global_settings
.talk_menu
&& id
>=0)
1743 if(final
|| (count
&& (talked_tick
== 0
1744 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1746 talked_tick
= current_tick
;
1747 talk_number(count
, false);
1753 splashf(0, fmt
, count
, str(LANG_OFF_ABORT
));
1757 * Display buffer full message
1759 static void display_buffer_full(void)
1761 splash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1765 * Flush any cached control commands to disk. Called when playlist is being
1766 * modified. Returns 0 on success and -1 on failure.
1768 static int flush_cached_control(struct playlist_info
* playlist
)
1773 if (!playlist
->num_cached
)
1776 lseek(playlist
->control_fd
, 0, SEEK_END
);
1778 for (i
=0; i
<playlist
->num_cached
; i
++)
1780 struct playlist_control_cache
* cache
=
1781 &(playlist
->control_cache
[i
]);
1783 switch (cache
->command
)
1785 case PLAYLIST_COMMAND_PLAYLIST
:
1786 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1787 cache
->i1
, cache
->s1
, cache
->s2
);
1789 case PLAYLIST_COMMAND_ADD
:
1790 case PLAYLIST_COMMAND_QUEUE
:
1791 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1792 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1793 cache
->i1
, cache
->i2
);
1796 /* save the position in file where name is written */
1797 int* seek_pos
= (int *)cache
->data
;
1798 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1799 result
= fdprintf(playlist
->control_fd
, "%s\n",
1803 case PLAYLIST_COMMAND_DELETE
:
1804 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1806 case PLAYLIST_COMMAND_SHUFFLE
:
1807 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1808 cache
->i1
, cache
->i2
);
1810 case PLAYLIST_COMMAND_UNSHUFFLE
:
1811 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1813 case PLAYLIST_COMMAND_RESET
:
1814 result
= fdprintf(playlist
->control_fd
, "R\n");
1826 playlist
->num_cached
= 0;
1827 playlist
->pending_control_sync
= true;
1834 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1841 * Update control data with new command. Depending on the command, it may be
1842 * cached or flushed to disk.
1844 static int update_control(struct playlist_info
* playlist
,
1845 enum playlist_command command
, int i1
, int i2
,
1846 const char* s1
, const char* s2
, void* data
)
1849 struct playlist_control_cache
* cache
;
1852 mutex_lock(playlist
->control_mutex
);
1854 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1856 cache
->command
= command
;
1865 case PLAYLIST_COMMAND_PLAYLIST
:
1866 case PLAYLIST_COMMAND_ADD
:
1867 case PLAYLIST_COMMAND_QUEUE
:
1868 #ifndef HAVE_DIRCACHE
1869 case PLAYLIST_COMMAND_DELETE
:
1870 case PLAYLIST_COMMAND_RESET
:
1874 case PLAYLIST_COMMAND_SHUFFLE
:
1875 case PLAYLIST_COMMAND_UNSHUFFLE
:
1877 /* only flush when needed */
1881 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1882 result
= flush_cached_control(playlist
);
1884 mutex_unlock(playlist
->control_mutex
);
1890 * sync control file to disk
1892 static void sync_control(struct playlist_info
* playlist
, bool force
)
1894 #ifdef HAVE_DIRCACHE
1895 if (playlist
->started
&& force
)
1899 if (playlist
->started
)
1902 if (playlist
->pending_control_sync
)
1904 mutex_lock(playlist
->control_mutex
);
1905 fsync(playlist
->control_fd
);
1906 playlist
->pending_control_sync
= false;
1907 mutex_unlock(playlist
->control_mutex
);
1913 * Rotate indices such that first_index is index 0
1915 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1917 index
-= playlist
->first_index
;
1919 index
+= playlist
->amount
;
1924 static int move_callback(int handle
, void* current
, void* new)
1927 struct playlist_info
* playlist
= ¤t_playlist
;
1928 if (current
== playlist
->indices
)
1929 playlist
->indices
= new;
1930 else if (current
== playlist
->buffer
)
1931 playlist
->buffer
= new;
1932 else /* current == playlist->filenames */
1933 playlist
->filenames
= new;
1935 return BUFLIB_CB_OK
;
1938 static struct buflib_callbacks ops
= {
1939 .move_callback
= move_callback
,
1940 .shrink_callback
= NULL
,
1943 * Initialize playlist entries at startup
1945 void playlist_init(void)
1948 struct playlist_info
* playlist
= ¤t_playlist
;
1950 mutex_init(¤t_playlist_mutex
);
1951 mutex_init(&created_playlist_mutex
);
1953 playlist
->current
= true;
1954 strlcpy(playlist
->control_filename
, PLAYLIST_CONTROL_FILE
,
1955 sizeof(playlist
->control_filename
));
1957 playlist
->control_fd
= -1;
1958 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1959 handle
= core_alloc_ex("playlist idx",
1960 playlist
->max_playlist_size
* sizeof(int), &ops
);
1961 playlist
->indices
= core_get_data(handle
);
1962 playlist
->buffer_size
=
1963 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1964 handle
= core_alloc_ex("playlist buf",
1965 playlist
->buffer_size
, &ops
);
1966 playlist
->buffer
= core_get_data(handle
);
1967 playlist
->control_mutex
= ¤t_playlist_mutex
;
1969 empty_playlist(playlist
, true);
1971 #ifdef HAVE_DIRCACHE
1972 handle
= core_alloc_ex("playlist dc",
1973 playlist
->max_playlist_size
* sizeof(int), &ops
);
1974 playlist
->filenames
= core_get_data(handle
);
1975 memset(playlist
->filenames
, 0xff,
1976 playlist
->max_playlist_size
* sizeof(int));
1977 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1978 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1980 queue_init(&playlist_queue
, true);
1985 * Clean playlist at shutdown
1987 void playlist_shutdown(void)
1989 struct playlist_info
* playlist
= ¤t_playlist
;
1991 if (playlist
->control_fd
>= 0)
1993 mutex_lock(playlist
->control_mutex
);
1995 if (playlist
->num_cached
> 0)
1996 flush_cached_control(playlist
);
1998 close(playlist
->control_fd
);
2000 mutex_unlock(playlist
->control_mutex
);
2005 * Create new playlist
2007 int playlist_create(const char *dir
, const char *file
)
2009 struct playlist_info
* playlist
= ¤t_playlist
;
2011 new_playlist(playlist
, dir
, file
);
2014 /* load the playlist file */
2015 add_indices_to_playlist(playlist
, NULL
, 0);
2020 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2023 * Restore the playlist state based on control file commands. Called to
2024 * resume playback after shutdown.
2026 int playlist_resume(void)
2028 struct playlist_info
* playlist
= ¤t_playlist
;
2033 int control_file_size
= 0;
2037 /* use mp3 buffer for maximum load speed */
2038 buffer
= (char *)audio_get_buffer(false, &buflen
);
2040 empty_playlist(playlist
, true);
2042 splash(0, ID2P(LANG_WAIT
));
2043 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2044 if (playlist
->control_fd
< 0)
2046 splash(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 splash(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 splash(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
;
2083 bool useraborted
= false;
2085 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2087 /* So a splash while we are loading. */
2088 if (TIME_AFTER(current_tick
, last_tick
+ HZ
/4))
2090 splashf(0, str(LANG_LOADING_PERCENT
),
2091 (total_read
+count
)*100/control_file_size
,
2092 str(LANG_OFF_ABORT
));
2093 if (action_userabort(TIMEOUT_NOBLOCK
))
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,
2232 case PLAYLIST_COMMAND_UNSHUFFLE
:
2234 /* str1=first_index */
2242 playlist
->first_index
= atoi(str1
);
2244 if (sort_playlist(playlist
, false, false) < 0)
2250 case PLAYLIST_COMMAND_RESET
:
2252 playlist
->last_insert_pos
= -1;
2255 case PLAYLIST_COMMAND_COMMENT
:
2262 /* to ignore any extra newlines */
2263 current_command
= PLAYLIST_COMMAND_COMMENT
;
2269 /* first non-comment line must always specify playlist */
2270 if (first
&& *p
!= 'P' && *p
!= '#')
2280 /* playlist can only be specified once */
2288 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2291 current_command
= PLAYLIST_COMMAND_ADD
;
2294 current_command
= PLAYLIST_COMMAND_QUEUE
;
2297 current_command
= PLAYLIST_COMMAND_DELETE
;
2300 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2303 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2306 current_command
= PLAYLIST_COMMAND_RESET
;
2309 current_command
= PLAYLIST_COMMAND_COMMENT
;
2322 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2324 /* all control file strings are separated with a colon.
2325 Replace the colon with 0 to get proper strings that can be
2326 used by commands above */
2332 if ((count
+1) < nread
)
2346 /* allow last string to contain colons */
2357 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2363 splash(HZ
*2, ID2P(LANG_CANCEL
));
2366 if (!newline
|| (exit_loop
&& count
<nread
))
2368 if ((total_read
+ count
) >= control_file_size
)
2370 /* no newline at end of control file */
2371 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2375 /* We didn't end on a newline or we exited loop prematurely.
2376 Either way, re-read the remainder. */
2377 count
= last_newline
;
2378 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2381 total_read
+= count
;
2384 /* still looking for header */
2385 nread
= read(playlist
->control_fd
, buffer
,
2386 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2388 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2390 /* Terminate on EOF */
2397 #ifdef HAVE_DIRCACHE
2398 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2405 * Add track to in_ram playlist. Used when playing directories.
2407 int playlist_add(const char *filename
)
2409 struct playlist_info
* playlist
= ¤t_playlist
;
2410 int len
= strlen(filename
);
2412 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2413 (playlist
->amount
>= playlist
->max_playlist_size
))
2415 display_buffer_full();
2419 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2420 #ifdef HAVE_DIRCACHE
2421 playlist
->filenames
[playlist
->amount
] = -1;
2425 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2426 playlist
->buffer_end_pos
+= len
;
2427 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2432 /* shuffle newly created playlist using random seed. */
2433 int playlist_shuffle(int random_seed
, int start_index
)
2435 struct playlist_info
* playlist
= ¤t_playlist
;
2437 bool start_current
= false;
2439 if (start_index
>= 0 && global_settings
.play_selected
)
2441 /* store the seek position before the shuffle */
2442 playlist
->index
= playlist
->first_index
= start_index
;
2443 start_current
= true;
2446 randomise_playlist(playlist
, random_seed
, start_current
, true);
2448 return playlist
->index
;
2451 /* start playing current playlist at specified index/offset */
2452 void playlist_start(int start_index
, int offset
)
2454 struct playlist_info
* playlist
= ¤t_playlist
;
2456 /* Cancel FM radio selection as previous music. For cases where we start
2457 playback without going to the WPS, such as playlist insert.. or
2458 playlist catalog. */
2459 previous_music_is_wps();
2461 playlist
->index
= start_index
;
2463 playlist
->started
= true;
2464 sync_control(playlist
, false);
2468 /* Returns false if 'steps' is out of bounds, else true */
2469 bool playlist_check(int steps
)
2471 struct playlist_info
* playlist
= ¤t_playlist
;
2473 /* always allow folder navigation */
2474 if (global_settings
.next_folder
&& playlist
->in_ram
)
2477 int index
= get_next_index(playlist
, steps
, -1);
2479 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2480 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2482 return (index
>= 0);
2485 /* get trackname of track that is "steps" away from current playing track.
2486 NULL is used to identify end of playlist */
2487 const char* playlist_peek(int steps
, char* buf
, size_t buf_size
)
2489 struct playlist_info
* playlist
= ¤t_playlist
;
2495 index
= get_next_index(playlist
, steps
, -1);
2499 #if CONFIG_CODEC == SWCODEC
2500 /* Just testing - don't care about the file name */
2501 if (!buf
|| !buf_size
)
2505 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2506 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2508 if (get_filename(playlist
, index
, seek
, control_file
, buf
,
2514 if (!playlist
->in_ram
|| control_file
)
2516 /* remove bogus dirs from beginning of path
2517 (workaround for buggy playlist creation tools) */
2520 if (file_exists(temp_ptr
))
2523 temp_ptr
= strchr(temp_ptr
+1, '/');
2528 /* Even though this is an invalid file, we still need to pass a
2529 file name to the caller because NULL is used to indicate end
2539 * Update indices as track has changed
2541 int playlist_next(int steps
)
2543 struct playlist_info
* playlist
= ¤t_playlist
;
2547 #ifdef AB_REPEAT_ENABLE
2548 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2550 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2554 /* We need to delete all the queued songs */
2555 for (i
=0, j
=steps
; i
<j
; i
++)
2557 index
= get_next_index(playlist
, i
, -1);
2559 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2561 remove_track_from_playlist(playlist
, index
, true);
2562 steps
--; /* one less track */
2567 index
= get_next_index(playlist
, steps
, -1);
2571 /* end of playlist... or is it */
2572 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2573 playlist
->amount
> 1)
2575 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2576 playlist
->first_index
= 0;
2577 sort_playlist(playlist
, false, false);
2578 randomise_playlist(playlist
, current_tick
, false, true);
2580 #if CONFIG_CODEC == SWCODEC
2581 playlist
->started
= true;
2583 playlist_start(0, 0);
2585 playlist
->index
= 0;
2588 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2590 index
= create_and_play_dir(steps
, true);
2594 playlist
->index
= index
;
2601 playlist
->index
= index
;
2603 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2605 /* check to see if we've gone beyond the last inserted track */
2606 int cur
= rotate_index(playlist
, index
);
2607 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2611 /* reset last inserted track */
2612 playlist
->last_insert_pos
= -1;
2614 if (playlist
->control_fd
>= 0)
2616 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2617 -1, -1, NULL
, NULL
, NULL
);
2622 sync_control(playlist
, false);
2630 /* try playing next or previous folder */
2631 bool playlist_next_dir(int direction
)
2633 /* not to mess up real playlists */
2634 if(!current_playlist
.in_ram
)
2637 return create_and_play_dir(direction
, false) >= 0;
2640 /* Get resume info for current playing song. If return value is -1 then
2641 settings shouldn't be saved. */
2642 int playlist_get_resume_info(int *resume_index
)
2644 struct playlist_info
* playlist
= ¤t_playlist
;
2646 *resume_index
= playlist
->index
;
2651 /* Update resume info for current playing song. Returns -1 on error. */
2652 int playlist_update_resume_info(const struct mp3entry
* id3
)
2654 struct playlist_info
* playlist
= ¤t_playlist
;
2658 if (global_status
.resume_index
!= playlist
->index
||
2659 global_status
.resume_offset
!= id3
->offset
)
2661 global_status
.resume_index
= playlist
->index
;
2662 global_status
.resume_offset
= id3
->offset
;
2668 global_status
.resume_index
= -1;
2669 global_status
.resume_offset
= -1;
2676 /* Returns index of current playing track for display purposes. This value
2677 should not be used for resume purposes as it doesn't represent the actual
2678 index into the playlist */
2679 int playlist_get_display_index(void)
2681 struct playlist_info
* playlist
= ¤t_playlist
;
2683 /* first_index should always be index 0 for display purposes */
2684 int index
= rotate_index(playlist
, playlist
->index
);
2689 /* returns number of tracks in current playlist */
2690 int playlist_amount(void)
2692 return playlist_amount_ex(NULL
);
2694 /* set playlist->last_shuffle_start to playlist->amount for
2695 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2696 void playlist_set_last_shuffled_start(void)
2698 struct playlist_info
* playlist
= ¤t_playlist
;
2699 playlist
->last_shuffled_start
= playlist
->amount
;
2702 * Create a new playlist If playlist is not NULL then we're loading a
2703 * playlist off disk for viewing/editing. The index_buffer is used to store
2704 * playlist indices (required for and only used if !current playlist). The
2705 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2707 int playlist_create_ex(struct playlist_info
* playlist
,
2708 const char* dir
, const char* file
,
2709 void* index_buffer
, int index_buffer_size
,
2710 void* temp_buffer
, int temp_buffer_size
)
2713 playlist
= ¤t_playlist
;
2716 /* Initialize playlist structure */
2717 int r
= rand() % 10;
2718 playlist
->current
= false;
2720 /* Use random name for control file */
2721 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2722 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2724 playlist
->control_fd
= -1;
2728 int num_indices
= index_buffer_size
/ sizeof(int);
2730 #ifdef HAVE_DIRCACHE
2733 if (num_indices
> global_settings
.max_files_in_playlist
)
2734 num_indices
= global_settings
.max_files_in_playlist
;
2736 playlist
->max_playlist_size
= num_indices
;
2737 playlist
->indices
= index_buffer
;
2738 #ifdef HAVE_DIRCACHE
2739 playlist
->filenames
= (int*)&playlist
->indices
[num_indices
];
2744 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2745 playlist
->indices
= current_playlist
.indices
;
2746 #ifdef HAVE_DIRCACHE
2747 playlist
->filenames
= current_playlist
.filenames
;
2751 playlist
->buffer_size
= 0;
2752 playlist
->buffer
= NULL
;
2753 playlist
->control_mutex
= &created_playlist_mutex
;
2756 new_playlist(playlist
, dir
, file
);
2759 /* load the playlist file */
2760 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2766 * Set the specified playlist as the current.
2767 * NOTE: You will get undefined behaviour if something is already playing so
2768 * remember to stop before calling this. Also, this call will
2769 * effectively close your playlist, making it unusable.
2771 int playlist_set_current(struct playlist_info
* playlist
)
2773 if (!playlist
|| (check_control(playlist
) < 0))
2776 empty_playlist(¤t_playlist
, false);
2778 strlcpy(current_playlist
.filename
, playlist
->filename
,
2779 sizeof(current_playlist
.filename
));
2781 current_playlist
.utf8
= playlist
->utf8
;
2782 current_playlist
.fd
= playlist
->fd
;
2784 close(playlist
->control_fd
);
2785 close(current_playlist
.control_fd
);
2786 remove(current_playlist
.control_filename
);
2787 if (rename(playlist
->control_filename
,
2788 current_playlist
.control_filename
) < 0)
2790 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2792 if (current_playlist
.control_fd
< 0)
2794 current_playlist
.control_created
= true;
2796 current_playlist
.dirlen
= playlist
->dirlen
;
2798 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2800 memcpy(current_playlist
.indices
, playlist
->indices
,
2801 playlist
->max_playlist_size
*sizeof(int));
2802 #ifdef HAVE_DIRCACHE
2803 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2804 playlist
->max_playlist_size
*sizeof(int));
2808 current_playlist
.first_index
= playlist
->first_index
;
2809 current_playlist
.amount
= playlist
->amount
;
2810 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2811 current_playlist
.seed
= playlist
->seed
;
2812 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2813 current_playlist
.deleted
= playlist
->deleted
;
2814 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2816 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2817 sizeof(current_playlist
.control_cache
));
2818 current_playlist
.num_cached
= playlist
->num_cached
;
2819 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2823 struct playlist_info
*playlist_get_current(void)
2825 return ¤t_playlist
;
2828 * Close files and delete control file for non-current playlist.
2830 void playlist_close(struct playlist_info
* playlist
)
2835 if (playlist
->fd
>= 0)
2836 close(playlist
->fd
);
2838 if (playlist
->control_fd
>= 0)
2839 close(playlist
->control_fd
);
2841 if (playlist
->control_created
)
2842 remove(playlist
->control_filename
);
2845 void playlist_sync(struct playlist_info
* playlist
)
2848 playlist
= ¤t_playlist
;
2850 sync_control(playlist
, false);
2851 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2852 audio_flush_and_reload_tracks();
2854 #ifdef HAVE_DIRCACHE
2855 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2860 * Insert track into playlist at specified position (or one of the special
2861 * positions). Returns position where track was inserted or -1 if error.
2863 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2864 int position
, bool queue
, bool sync
)
2869 playlist
= ¤t_playlist
;
2871 if (check_control(playlist
) < 0)
2873 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2877 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2879 /* Check if we want manually sync later. For example when adding
2880 * bunch of files from tagcache, syncing after every file wouldn't be
2881 * a good thing to do. */
2882 if (sync
&& result
>= 0)
2883 playlist_sync(playlist
);
2889 * Insert all tracks from specified directory into playlist.
2891 int playlist_insert_directory(struct playlist_info
* playlist
,
2892 const char *dirname
, int position
, bool queue
,
2896 unsigned char *count_str
;
2897 struct directory_search_context context
;
2900 playlist
= ¤t_playlist
;
2902 if (check_control(playlist
) < 0)
2904 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2908 if (position
== PLAYLIST_REPLACE
)
2910 if (playlist_remove_all_tracks(playlist
) == 0)
2911 position
= PLAYLIST_INSERT_LAST
;
2917 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2919 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2921 display_playlist_count(0, count_str
, false);
2923 context
.playlist
= playlist
;
2924 context
.position
= position
;
2925 context
.queue
= queue
;
2930 result
= playlist_directory_tracksearch(dirname
, recurse
,
2931 directory_search_callback
, &context
);
2933 sync_control(playlist
, false);
2937 display_playlist_count(context
.count
, count_str
, true);
2939 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2940 audio_flush_and_reload_tracks();
2942 #ifdef HAVE_DIRCACHE
2943 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2950 * Insert all tracks from specified playlist into dynamic playlist.
2952 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2953 int position
, bool queue
)
2959 unsigned char *count_str
;
2960 char temp_buf
[MAX_PATH
+1];
2961 char trackname
[MAX_PATH
+1];
2964 bool utf8
= is_m3u8(filename
);
2967 playlist
= ¤t_playlist
;
2969 if (check_control(playlist
) < 0)
2971 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2975 fd
= open_utf8(filename
, O_RDONLY
);
2978 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2982 /* we need the directory name for formatting purposes */
2985 temp_ptr
= strrchr(filename
+1,'/');
2992 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2994 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2996 display_playlist_count(count
, count_str
, false);
2998 if (position
== PLAYLIST_REPLACE
)
3000 if (playlist_remove_all_tracks(playlist
) == 0)
3001 position
= PLAYLIST_INSERT_LAST
;
3007 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3010 if (action_userabort(TIMEOUT_NOBLOCK
))
3013 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3019 /* Use trackname as a temporay buffer. Note that trackname must
3020 * be as large as temp_buf.
3022 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3025 /* we need to format so that relative paths are correctly
3027 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3034 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3043 /* Make sure tracks are inserted in correct order if user
3044 requests INSERT_FIRST */
3045 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3046 position
= insert_pos
+ 1;
3050 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3052 display_playlist_count(count
, count_str
, false);
3054 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3055 (audio_status() & AUDIO_STATUS_PLAY
) &&
3057 audio_flush_and_reload_tracks();
3061 /* let the other threads work */
3070 sync_control(playlist
, false);
3074 display_playlist_count(count
, count_str
, true);
3076 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3077 audio_flush_and_reload_tracks();
3079 #ifdef HAVE_DIRCACHE
3080 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3087 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3088 * we want to delete the current playing track.
3090 int playlist_delete(struct playlist_info
* playlist
, int index
)
3095 playlist
= ¤t_playlist
;
3097 if (check_control(playlist
) < 0)
3099 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3103 if (index
== PLAYLIST_DELETE_CURRENT
)
3104 index
= playlist
->index
;
3106 result
= remove_track_from_playlist(playlist
, index
, true);
3108 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3110 audio_flush_and_reload_tracks();
3116 * Move track at index to new_index. Tracks between the two are shifted
3117 * appropriately. Returns 0 on success and -1 on failure.
3119 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3125 bool current
= false;
3127 char filename
[MAX_PATH
];
3130 playlist
= ¤t_playlist
;
3132 if (check_control(playlist
) < 0)
3134 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3138 if (index
== new_index
)
3141 if (index
== playlist
->index
)
3142 /* Moving the current track */
3145 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3146 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3147 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3149 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3150 sizeof(filename
)) < 0)
3153 /* We want to insert the track at the position that was specified by
3154 new_index. This may be different then new_index because of the
3155 shifting that will occur after the delete.
3156 We calculate this before we do the remove as it depends on the
3157 size of the playlist before the track removal */
3158 r
= rotate_index(playlist
, new_index
);
3160 /* Delete track from original position */
3161 result
= remove_track_from_playlist(playlist
, index
, true);
3167 new_index
= PLAYLIST_PREPEND
;
3168 else if (r
== playlist
->amount
)
3170 new_index
= PLAYLIST_INSERT_LAST
;
3172 /* Calculate index of desired position */
3173 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3175 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3182 /* Moved the current track */
3185 case PLAYLIST_PREPEND
:
3186 playlist
->index
= playlist
->first_index
;
3188 case PLAYLIST_INSERT_LAST
:
3189 playlist
->index
= playlist
->first_index
- 1;
3190 if (playlist
->index
< 0)
3191 playlist
->index
+= playlist
->amount
;
3194 playlist
->index
= new_index
;
3199 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3200 audio_flush_and_reload_tracks();
3204 #ifdef HAVE_DIRCACHE
3205 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3211 /* shuffle currently playing playlist */
3212 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3218 playlist
= ¤t_playlist
;
3220 check_control(playlist
);
3222 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3224 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3226 audio_flush_and_reload_tracks();
3231 /* sort currently playing playlist */
3232 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3237 playlist
= ¤t_playlist
;
3239 check_control(playlist
);
3241 result
= sort_playlist(playlist
, start_current
, true);
3243 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3245 audio_flush_and_reload_tracks();
3250 /* returns true if playlist has been modified */
3251 bool playlist_modified(const struct playlist_info
* playlist
)
3254 playlist
= ¤t_playlist
;
3256 if (playlist
->shuffle_modified
||
3257 playlist
->deleted
||
3258 playlist
->num_inserted_tracks
> 0)
3264 /* returns index of first track in playlist */
3265 int playlist_get_first_index(const struct playlist_info
* playlist
)
3268 playlist
= ¤t_playlist
;
3270 return playlist
->first_index
;
3273 /* returns shuffle seed of playlist */
3274 int playlist_get_seed(const struct playlist_info
* playlist
)
3277 playlist
= ¤t_playlist
;
3279 return playlist
->seed
;
3282 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3283 int playlist_amount_ex(const struct playlist_info
* playlist
)
3286 playlist
= ¤t_playlist
;
3288 return playlist
->amount
;
3291 /* returns full path of playlist (minus extension) */
3292 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3298 playlist
= ¤t_playlist
;
3300 strlcpy(buf
, playlist
->filename
+playlist
->dirlen
, buf_size
);
3305 /* Remove extension */
3306 sep
= strrchr(buf
, '.');
3313 /* returns the playlist filename */
3314 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3318 playlist
= ¤t_playlist
;
3320 strlcpy(buf
, playlist
->filename
, buf_size
);
3328 /* Fills info structure with information about track at specified index.
3329 Returns 0 on success and -1 on failure */
3330 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3331 struct playlist_track_info
* info
)
3337 playlist
= ¤t_playlist
;
3339 if (index
< 0 || index
>= playlist
->amount
)
3342 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3343 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3345 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3346 sizeof(info
->filename
)) < 0)
3353 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3354 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3356 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3360 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3361 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3363 info
->index
= index
;
3364 info
->display_index
= rotate_index(playlist
, index
) + 1;
3369 /* save the current dynamic playlist to specified file */
3370 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3375 char path
[MAX_PATH
+1];
3376 char tmp_buf
[MAX_PATH
+1];
3378 bool overwrite_current
= false;
3379 char* old_buffer
= NULL
;
3380 size_t old_buffer_size
= 0;
3383 playlist
= ¤t_playlist
;
3385 if (playlist
->amount
<= 0)
3388 /* use current working directory as base for pathname */
3389 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3390 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3393 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3395 /* Attempting to overwrite current playlist file.*/
3397 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3399 /* not enough buffer space to store updated indices */
3400 /* Try to get a buffer */
3401 old_buffer
= playlist
->buffer
;
3402 old_buffer_size
= playlist
->buffer_size
;
3403 playlist
->buffer
= plugin_get_buffer((size_t*)&playlist
->buffer_size
);
3404 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3406 playlist
->buffer
= old_buffer
;
3407 playlist
->buffer_size
= old_buffer_size
;
3408 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3413 /* use temporary pathname */
3414 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3415 overwrite_current
= true;
3420 fd
= open_utf8(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3424 /* some applications require a BOM to read the file properly */
3425 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0666);
3429 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3430 if (old_buffer
!= NULL
)
3432 playlist
->buffer
= old_buffer
;
3433 playlist
->buffer_size
= old_buffer_size
;
3438 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3442 index
= playlist
->first_index
;
3443 for (i
=0; i
<playlist
->amount
; i
++)
3450 if (action_userabort(TIMEOUT_NOBLOCK
))
3456 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3457 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3458 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3460 /* Don't save queued files */
3463 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3470 if (overwrite_current
)
3471 playlist
->seek_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3473 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3475 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3482 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3483 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3489 index
= (index
+1)%playlist
->amount
;
3492 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3496 if (overwrite_current
&& result
>= 0)
3500 mutex_lock(playlist
->control_mutex
);
3502 /* Replace the current playlist with the new one and update indices */
3503 close(playlist
->fd
);
3504 if (remove(playlist
->filename
) >= 0)
3506 if (rename(path
, playlist
->filename
) >= 0)
3508 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3509 if (playlist
->fd
>= 0)
3511 index
= playlist
->first_index
;
3512 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3514 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3516 playlist
->indices
[index
] = playlist
->seek_buf
[count
];
3519 index
= (index
+1)%playlist
->amount
;
3522 /* we need to recreate control because inserted tracks are
3523 now part of the playlist and shuffle has been
3525 result
= recreate_control(playlist
);
3530 mutex_unlock(playlist
->control_mutex
);
3535 if (old_buffer
!= NULL
)
3537 playlist
->buffer
= old_buffer
;
3538 playlist
->buffer_size
= old_buffer_size
;
3545 * Search specified directory for tracks and notify via callback. May be
3546 * called recursively.
3548 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3549 int (*callback
)(char*, void*),
3552 char buf
[MAX_PATH
+1];
3556 struct entry
*files
;
3557 struct tree_context
* tc
= tree_get_context();
3558 int old_dirfilter
= *(tc
->dirfilter
);
3563 /* use the tree browser dircache to load files */
3564 *(tc
->dirfilter
) = SHOW_ALL
;
3566 if (ft_load(tc
, dirname
) < 0)
3568 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3569 *(tc
->dirfilter
) = old_dirfilter
;
3573 files
= tc
->cache
.entries
;
3574 num_files
= tc
->filesindir
;
3576 /* we've overwritten the dircache so tree browser will need to be
3580 for (i
=0; i
<num_files
; i
++)
3583 if (action_userabort(TIMEOUT_NOBLOCK
))
3589 if (files
[i
].attr
& ATTR_DIRECTORY
)
3593 /* recursively add directories */
3594 snprintf(buf
, sizeof(buf
), "%s/%s",
3595 dirname
[1]? dirname
: "", files
[i
].name
);
3596 result
= playlist_directory_tracksearch(buf
, recurse
,
3601 /* we now need to reload our current directory */
3602 if(ft_load(tc
, dirname
) < 0)
3608 files
= tc
->cache
.entries
;
3609 num_files
= tc
->filesindir
;
3619 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3621 snprintf(buf
, sizeof(buf
), "%s/%s",
3622 dirname
[1]? dirname
: "", files
[i
].name
);
3624 if (callback(buf
, context
) != 0)
3630 /* let the other threads work */
3635 /* restore dirfilter */
3636 *(tc
->dirfilter
) = old_dirfilter
;