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
);
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)
426 playlist
->filename
[playlist
->dirlen
-1] = '\0';
428 /* cannot call update_control() because of mutex */
429 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
430 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
432 playlist
->filename
[playlist
->dirlen
-1] = c
;
442 playlist
->shuffle_modified
= false;
443 playlist
->deleted
= false;
444 playlist
->num_inserted_tracks
= 0;
446 for (i
=0; i
<playlist
->amount
; i
++)
448 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
450 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
451 char inserted_file
[MAX_PATH
+1];
453 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
455 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
457 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
458 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
461 /* save the position in file where name is written */
462 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
464 result
= fdprintf(playlist
->control_fd
, "%s\n",
467 playlist
->indices
[i
] =
468 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
474 playlist
->num_inserted_tracks
++;
480 fsync(playlist
->control_fd
);
489 * store directory and name of playlist file
491 static void update_playlist_filename(struct playlist_info
* playlist
,
492 const char *dir
, const char *file
)
495 int dirlen
= strlen(dir
);
497 playlist
->utf8
= is_m3u8(file
);
499 /* If the dir does not end in trailing slash, we use a separator.
500 Otherwise we don't. */
501 if('/' != dir
[dirlen
-1])
507 playlist
->dirlen
= dirlen
;
509 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
510 "%s%s%s", dir
, sep
, file
);
514 * calculate track offsets within a playlist file
516 static int add_indices_to_playlist(struct playlist_info
* playlist
,
517 char* buffer
, size_t buflen
)
521 unsigned int count
= 0;
526 if(-1 == playlist
->fd
)
527 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
529 return -1; /* failure */
530 if((i
= lseek(playlist
->fd
, 0, SEEK_CUR
)) > 0)
531 playlist
->utf8
= true; /* Override any earlier indication. */
533 splash(0, ID2P(LANG_WAIT
));
537 /* use mp3 buffer for maximum load speed */
539 buffer
= audio_get_buffer(false, &buflen
);
546 nread
= read(playlist
->fd
, buffer
, buflen
);
547 /* Terminate on EOF */
551 p
= (unsigned char *)buffer
;
553 for(count
=0; count
< nread
; count
++,p
++) {
555 /* Are we on a new line? */
556 if((*p
== '\n') || (*p
== '\r'))
566 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
567 display_buffer_full();
572 /* Store a new entry */
573 playlist
->indices
[ playlist
->amount
] = i
+count
;
575 if (playlist
->filenames
)
576 playlist
->filenames
[ playlist
->amount
] = -1;
588 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
595 * Utility function to create a new playlist, fill it with the next or
596 * previous directory, shuffle it if needed, and start playback.
597 * If play_last is true and direction zero or negative, start playing
598 * the last file in the directory, otherwise start playing the first.
600 static int create_and_play_dir(int direction
, bool play_last
)
602 char dir
[MAX_PATH
+ 1];
607 res
= get_next_directory(dir
);
609 res
= get_previous_directory(dir
);
612 /* return the error encountered */
615 if (playlist_create(dir
, NULL
) != -1)
617 ft_build_playlist(tree_get_context(), 0);
619 if (global_settings
.playlist_shuffle
)
620 playlist_shuffle(current_tick
, -1);
622 if (play_last
&& direction
<= 0)
623 index
= current_playlist
.amount
- 1;
627 #if (CONFIG_CODEC == SWCODEC)
628 current_playlist
.started
= true;
630 playlist_start(index
, 0);
634 /* we've overwritten the dircache when getting the next/previous dir,
635 so the tree browser context will need to be reloaded */
642 * Removes all tracks, from the playlist, leaving the presently playing
645 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
649 if (playlist
== NULL
)
650 playlist
= ¤t_playlist
;
652 while (playlist
->index
> 0)
653 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
656 while (playlist
->amount
> 1)
657 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
660 if (playlist
->amount
== 1) {
661 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
669 * Add track to playlist at specified position. There are seven special
670 * positions that can be specified:
671 * PLAYLIST_PREPEND - Add track at beginning of playlist
672 * PLAYLIST_INSERT - Add track after current song. NOTE: If
673 * there are already inserted tracks then track
674 * is added to the end of the insertion list
675 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
676 * matter what other tracks have been inserted
677 * PLAYLIST_INSERT_LAST - Add track to end of playlist
678 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
679 * current playing track and end of playlist
680 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
682 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
683 * and inster this track at the end.
685 static int add_track_to_playlist(struct playlist_info
* playlist
,
686 const char *filename
, int position
,
687 bool queue
, int seek_pos
)
689 int insert_position
, orig_position
;
690 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
693 insert_position
= orig_position
= position
;
695 if (playlist
->amount
>= playlist
->max_playlist_size
)
697 display_buffer_full();
703 case PLAYLIST_PREPEND
:
704 position
= insert_position
= playlist
->first_index
;
706 case PLAYLIST_INSERT
:
707 /* if there are already inserted tracks then add track to end of
708 insertion list else add after current playing track */
709 if (playlist
->last_insert_pos
>= 0 &&
710 playlist
->last_insert_pos
< playlist
->amount
&&
711 (playlist
->indices
[playlist
->last_insert_pos
]&
712 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
713 position
= insert_position
= playlist
->last_insert_pos
+1;
714 else if (playlist
->amount
> 0)
715 position
= insert_position
= playlist
->index
+ 1;
717 position
= insert_position
= 0;
719 playlist
->last_insert_pos
= position
;
721 case PLAYLIST_INSERT_FIRST
:
722 if (playlist
->amount
> 0)
723 position
= insert_position
= playlist
->index
+ 1;
725 position
= insert_position
= 0;
727 playlist
->last_insert_pos
= position
;
729 case PLAYLIST_INSERT_LAST
:
730 if (playlist
->first_index
> 0)
731 position
= insert_position
= playlist
->first_index
;
733 position
= insert_position
= playlist
->amount
;
735 playlist
->last_insert_pos
= position
;
737 case PLAYLIST_INSERT_SHUFFLED
:
739 if (playlist
->started
)
742 int n
= playlist
->amount
-
743 rotate_index(playlist
, playlist
->index
);
750 position
= playlist
->index
+ offset
+ 1;
751 if (position
>= playlist
->amount
)
752 position
-= playlist
->amount
;
754 insert_position
= position
;
757 position
= insert_position
= (rand() % (playlist
->amount
+1));
760 case PLAYLIST_INSERT_LAST_SHUFFLED
:
762 position
= insert_position
= playlist
->last_shuffled_start
+
763 rand() % (playlist
->amount
- playlist
->last_shuffled_start
+ 1);
766 case PLAYLIST_REPLACE
:
767 if (playlist_remove_all_tracks(playlist
) < 0)
770 playlist
->last_insert_pos
= position
= insert_position
= playlist
->index
+ 1;
775 flags
|= PLAYLIST_QUEUED
;
777 /* shift indices so that track can be added */
778 for (i
=playlist
->amount
; i
>insert_position
; i
--)
780 playlist
->indices
[i
] = playlist
->indices
[i
-1];
782 if (playlist
->filenames
)
783 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
787 /* update stored indices if needed */
789 if (orig_position
< 0)
791 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
795 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
796 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
797 playlist
->first_index
++;
800 if (insert_position
< playlist
->last_insert_pos
||
801 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
802 playlist
->last_insert_pos
++;
804 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
806 int result
= update_control(playlist
,
807 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
808 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
814 playlist
->indices
[insert_position
] = flags
| seek_pos
;
817 if (playlist
->filenames
)
818 playlist
->filenames
[insert_position
] = -1;
822 playlist
->num_inserted_tracks
++;
824 return insert_position
;
828 * Callback for playlist_directory_tracksearch to insert track into
831 static int directory_search_callback(char* filename
, void* context
)
833 struct directory_search_context
* c
=
834 (struct directory_search_context
*) context
;
837 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
845 /* Make sure tracks are inserted in correct order if user requests
847 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
848 c
->position
= insert_pos
+ 1;
850 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
852 unsigned char* count_str
;
855 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
857 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
859 display_playlist_count(c
->count
, count_str
, false);
861 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
862 (audio_status() & AUDIO_STATUS_PLAY
) &&
863 c
->playlist
->started
)
864 audio_flush_and_reload_tracks();
871 * remove track at specified position
873 static int remove_track_from_playlist(struct playlist_info
* playlist
,
874 int position
, bool write
)
879 if (playlist
->amount
<= 0)
882 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
884 /* shift indices now that track has been removed */
885 for (i
=position
; i
<playlist
->amount
; i
++)
887 playlist
->indices
[i
] = playlist
->indices
[i
+1];
889 if (playlist
->filenames
)
890 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
897 playlist
->num_inserted_tracks
--;
899 playlist
->deleted
= true;
901 /* update stored indices if needed */
902 if (position
< playlist
->index
)
905 if (position
< playlist
->first_index
)
907 playlist
->first_index
--;
910 if (position
<= playlist
->last_insert_pos
)
911 playlist
->last_insert_pos
--;
913 if (write
&& playlist
->control_fd
>= 0)
915 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
916 position
, -1, NULL
, NULL
, NULL
);
921 sync_control(playlist
, false);
928 * randomly rearrange the array of indices for the playlist. If start_current
929 * is true then update the index to the new index of the current playing track
931 static int randomise_playlist(struct playlist_info
* playlist
,
932 unsigned int seed
, bool start_current
,
938 unsigned int current
= playlist
->indices
[playlist
->index
];
940 /* seed 0 is used to identify sorted playlist for resume purposes */
944 /* seed with the given seed */
947 /* randomise entire indices list */
948 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
950 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
951 candidate
= rand() % (count
+ 1);
953 /* now swap the values at the 'count' and 'candidate' positions */
954 store
= playlist
->indices
[candidate
];
955 playlist
->indices
[candidate
] = playlist
->indices
[count
];
956 playlist
->indices
[count
] = store
;
958 if (playlist
->filenames
)
960 store
= playlist
->filenames
[candidate
];
961 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
962 playlist
->filenames
[count
] = store
;
968 find_and_set_playlist_index(playlist
, current
);
970 /* indices have been moved so last insert position is no longer valid */
971 playlist
->last_insert_pos
= -1;
973 playlist
->seed
= seed
;
974 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
975 playlist
->shuffle_modified
= true;
979 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
980 playlist
->first_index
, NULL
, NULL
, NULL
);
987 * Sort the array of indices for the playlist. If start_current is true then
988 * set the index to the new index of the current song.
989 * Also while going to unshuffled mode set the first_index to 0.
991 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
994 unsigned int current
= playlist
->indices
[playlist
->index
];
996 if (playlist
->amount
> 0)
997 qsort((void*)playlist
->indices
, playlist
->amount
,
998 sizeof(playlist
->indices
[0]), compare
);
1000 #ifdef HAVE_DIRCACHE
1001 /** We need to re-check the song names from disk because qsort can't
1002 * sort two arrays at once :/
1003 * FIXME: Please implement a better way to do this. */
1004 memset((void*)playlist
->filenames
, 0xff, playlist
->max_playlist_size
* sizeof(int));
1005 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1009 find_and_set_playlist_index(playlist
, current
);
1011 /* indices have been moved so last insert position is no longer valid */
1012 playlist
->last_insert_pos
= -1;
1014 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1015 playlist
->shuffle_modified
= false;
1016 if (write
&& playlist
->control_fd
>= 0)
1018 playlist
->first_index
= 0;
1019 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1020 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1026 /* Calculate how many steps we have to really step when skipping entries
1029 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1031 int i
, count
, direction
;
1033 int stepped_count
= 0;
1046 index
= playlist
->index
;
1049 /* Boundary check */
1051 index
+= playlist
->amount
;
1052 if (index
>= playlist
->amount
)
1053 index
-= playlist
->amount
;
1055 /* Check if we found a bad entry. */
1056 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1059 /* Are all entries bad? */
1060 if (stepped_count
++ > playlist
->amount
)
1067 } while (i
<= count
);
1072 #if CONFIG_CODEC == SWCODEC
1073 /* Marks the index of the track to be skipped that is "steps" away from
1074 * current playing track.
1076 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1080 if (playlist
== NULL
)
1081 playlist
= ¤t_playlist
;
1083 /* need to account for already skipped tracks */
1084 steps
= calculate_step_count(playlist
, steps
);
1086 index
= playlist
->index
+ steps
;
1088 index
+= playlist
->amount
;
1089 else if (index
>= playlist
->amount
)
1090 index
-= playlist
->amount
;
1092 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1094 #endif /* CONFIG_CODEC == SWCODEC */
1097 * returns the index of the track that is "steps" away from current playing
1100 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1103 int current_index
= playlist
->index
;
1104 int next_index
= -1;
1106 if (playlist
->amount
<= 0)
1109 if (repeat_mode
== -1)
1110 repeat_mode
= global_settings
.repeat_mode
;
1112 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1113 repeat_mode
= REPEAT_ALL
;
1115 steps
= calculate_step_count(playlist
, steps
);
1116 switch (repeat_mode
)
1118 case REPEAT_SHUFFLE
:
1119 /* Treat repeat shuffle just like repeat off. At end of playlist,
1120 play will be resumed in playlist_next() */
1123 current_index
= rotate_index(playlist
, current_index
);
1124 next_index
= current_index
+steps
;
1125 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1128 next_index
= (next_index
+playlist
->first_index
) %
1135 #ifdef AB_REPEAT_ENABLE
1138 next_index
= current_index
;
1144 next_index
= (current_index
+steps
) % playlist
->amount
;
1145 while (next_index
< 0)
1146 next_index
+= playlist
->amount
;
1148 if (steps
>= playlist
->amount
)
1155 /* second time around so skip the queued files */
1156 for (i
=0; i
<playlist
->amount
; i
++)
1158 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1159 index
= (index
+1) % playlist
->amount
;
1171 /* No luck if the whole playlist was bad. */
1172 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1179 * Search for the seek track and set appropriate indices. Used after shuffle
1180 * to make sure the current index is still pointing to correct track.
1182 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1187 /* Set the index to the current song */
1188 for (i
=0; i
<playlist
->amount
; i
++)
1190 if (playlist
->indices
[i
] == seek
)
1192 playlist
->index
= playlist
->first_index
= i
;
1200 * used to sort track indices. Sort order is as follows:
1201 * 1. Prepended tracks (in prepend order)
1202 * 2. Playlist/directory tracks (in playlist order)
1203 * 3. Inserted/Appended tracks (in insert order)
1205 static int compare(const void* p1
, const void* p2
)
1207 unsigned long* e1
= (unsigned long*) p1
;
1208 unsigned long* e2
= (unsigned long*) p2
;
1209 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1210 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1212 if (flags1
== flags2
)
1213 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1214 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1215 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1217 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1218 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1220 else if (flags1
&& flags2
)
1221 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1226 #ifdef HAVE_DIRCACHE
1228 * Thread to update filename pointers to dircache on background
1229 * without affecting playlist load up performance. This thread also flushes
1230 * any pending control commands when the disk spins up.
1232 static void playlist_flush_callback(void *param
)
1235 struct playlist_info
*playlist
;
1236 playlist
= ¤t_playlist
;
1237 if (playlist
->control_fd
>= 0)
1239 if (playlist
->num_cached
> 0)
1241 mutex_lock(playlist
->control_mutex
);
1242 flush_cached_control(playlist
);
1243 mutex_unlock(playlist
->control_mutex
);
1245 sync_control(playlist
, true);
1249 static bool is_dircache_pointers_intact(void)
1251 return dircache_get_appflag(DIRCACHE_APPFLAG_PLAYLIST
) ? true : false;
1254 static void playlist_thread(void)
1256 struct queue_event ev
;
1257 bool dirty_pointers
= false;
1258 static char tmp
[MAX_PATH
+1];
1260 struct playlist_info
*playlist
;
1267 #ifdef HAVE_DISK_STORAGE
1268 if (global_settings
.disk_spindown
> 1 &&
1269 global_settings
.disk_spindown
<= 5)
1270 sleep_time
= global_settings
.disk_spindown
- 1;
1275 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1279 case PLAYLIST_LOAD_POINTERS
:
1280 dirty_pointers
= true;
1283 /* Start the background scanning after either the disk spindown
1284 timeout or 5s, whichever is less */
1287 playlist
= ¤t_playlist
;
1288 if (playlist
->control_fd
>= 0)
1290 if (playlist
->num_cached
> 0)
1291 register_storage_idle_func(playlist_flush_callback
);
1294 if (!dircache_is_enabled() || !playlist
->filenames
1295 || playlist
->amount
<= 0)
1300 /* Check if previously loaded pointers are intact. */
1301 if (is_dircache_pointers_intact() && !dirty_pointers
)
1304 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1307 for (index
= 0; index
< playlist
->amount
1308 && queue_empty(&playlist_queue
); index
++)
1310 /* Process only pointers that are not already loaded. */
1311 if (is_dircache_pointers_intact() && playlist
->filenames
[index
] >= 0)
1314 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1315 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1317 /* Load the filename from playlist file. */
1318 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1324 /* Set the dircache entry pointer. */
1325 playlist
->filenames
[index
] = dircache_get_entry_id(tmp
);
1327 /* And be on background so user doesn't notice any delays. */
1331 if (dircache_is_enabled())
1332 dircache_set_appflag(DIRCACHE_APPFLAG_PLAYLIST
);
1334 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1337 if (index
== playlist
->amount
)
1338 dirty_pointers
= false;
1343 case SYS_USB_CONNECTED
:
1344 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1345 usb_wait_for_disconnect(&playlist_queue
);
1353 * gets pathname for track at seek index
1355 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1356 bool control_file
, char *buf
, int buf_length
)
1360 char tmp_buf
[MAX_PATH
+1];
1361 char dir_buf
[MAX_PATH
+1];
1362 bool utf8
= playlist
->utf8
;
1364 if (buf_length
> MAX_PATH
+1)
1365 buf_length
= MAX_PATH
+1;
1367 #ifdef HAVE_DIRCACHE
1368 if (is_dircache_pointers_intact() && playlist
->filenames
)
1370 if (playlist
->filenames
[index
] >= 0)
1372 max
= dircache_copy_path(playlist
->filenames
[index
],
1373 tmp_buf
, sizeof(tmp_buf
)-1);
1380 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1382 max
= strlcpy(tmp_buf
, (char*)&playlist
->buffer
[seek
], sizeof(tmp_buf
));
1386 mutex_lock(playlist
->control_mutex
);
1390 fd
= playlist
->control_fd
;
1395 if(-1 == playlist
->fd
)
1396 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1404 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1408 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1410 if ((max
> 0) && !utf8
)
1412 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1413 * be as large as tmp_buf.
1415 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1420 mutex_unlock(playlist
->control_mutex
);
1425 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1427 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1433 strlcpy(dir_buf
, playlist
->filename
, playlist
->dirlen
);
1435 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1438 static int get_next_directory(char *dir
){
1439 return get_next_dir(dir
, true);
1442 static int get_previous_directory(char *dir
){
1443 return get_next_dir(dir
, false);
1447 * search through all the directories (starting with the current) to find
1448 * one that has tracks to play
1450 static int get_next_dir(char *dir
, bool is_forward
)
1452 struct playlist_info
* playlist
= ¤t_playlist
;
1454 char *start_dir
= NULL
;
1456 struct tree_context
* tc
= tree_get_context();
1457 int saved_dirfilter
= *(tc
->dirfilter
);
1458 unsigned int base_len
;
1460 if (global_settings
.constrain_next_folder
)
1462 /* constrain results to directories below user's start directory */
1463 strcpy(dir
, global_settings
.start_directory
);
1464 base_len
= strlen(dir
);
1466 /* strip any trailing slash from base directory */
1467 if (base_len
> 0 && dir
[base_len
- 1] == '/')
1470 dir
[base_len
] = '\0';
1475 /* start from root directory */
1480 /* process random folder advance */
1481 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1483 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat", O_RDONLY
);
1486 int folder_count
= 0;
1487 read(fd
,&folder_count
,sizeof(int));
1490 char buffer
[MAX_PATH
];
1491 /* give up looking for a directory after we've had four
1492 times as many tries as there are directories. */
1493 unsigned long allowed_tries
= folder_count
* 4;
1495 srand(current_tick
);
1496 *(tc
->dirfilter
) = SHOW_MUSIC
;
1497 tc
->sort_dir
= global_settings
.sort_dir
;
1498 while (!exit
&& allowed_tries
--)
1500 i
= rand() % folder_count
;
1501 lseek(fd
, sizeof(int) + (MAX_PATH
* i
), SEEK_SET
);
1502 read(fd
, buffer
, MAX_PATH
);
1503 /* is the current dir within our base dir and has music? */
1504 if ((base_len
== 0 || !strncmp(buffer
, dir
, base_len
))
1505 && check_subdir_for_music(buffer
, "", false) == 0)
1509 *(tc
->dirfilter
) = saved_dirfilter
;
1510 tc
->sort_dir
= global_settings
.sort_dir
;
1523 /* if the current file is within our base dir, use its dir instead */
1524 if (base_len
== 0 || !strncmp(playlist
->filename
, dir
, base_len
))
1525 strlcpy(dir
, playlist
->filename
, playlist
->dirlen
);
1527 /* use the tree browser dircache to load files */
1528 *(tc
->dirfilter
) = SHOW_ALL
;
1530 /* set up sorting/direction */
1531 tc
->sort_dir
= global_settings
.sort_dir
;
1534 static const char sortpairs
[] =
1536 [SORT_ALPHA
] = SORT_ALPHA_REVERSED
,
1537 [SORT_DATE
] = SORT_DATE_REVERSED
,
1538 [SORT_TYPE
] = SORT_TYPE_REVERSED
,
1539 [SORT_ALPHA_REVERSED
] = SORT_ALPHA
,
1540 [SORT_DATE_REVERSED
] = SORT_DATE
,
1541 [SORT_TYPE_REVERSED
] = SORT_TYPE
,
1544 if ((unsigned)tc
->sort_dir
< sizeof(sortpairs
))
1545 tc
->sort_dir
= sortpairs
[tc
->sort_dir
];
1550 struct entry
*files
;
1554 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1561 files
= tree_get_entries(tc
);
1562 num_files
= tc
->filesindir
;
1564 tree_lock_cache(tc
);
1565 for (i
=0; i
<num_files
; i
++)
1568 if (action_userabort(TIMEOUT_NOBLOCK
))
1575 if (files
[i
].attr
& ATTR_DIRECTORY
)
1579 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1586 else if (!strcmp(start_dir
, files
[i
].name
))
1590 tree_unlock_cache(tc
);
1594 /* we've already descended to the base dir with nothing found,
1595 check whether that contains music */
1596 if (strlen(dir
) <= base_len
)
1598 result
= check_subdir_for_music(dir
, "", true);
1600 /* there's no music files in the base directory,
1601 treat as a fatal error */
1607 /* move down to parent directory. current directory name is
1608 stored as the starting point for the search in parent */
1609 start_dir
= strrchr(dir
, '/');
1621 /* restore dirfilter */
1622 *(tc
->dirfilter
) = saved_dirfilter
;
1623 tc
->sort_dir
= global_settings
.sort_dir
;
1629 * Checks if there are any music files in the dir or any of its
1630 * subdirectories. May be called recursively.
1632 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
)
1635 int dirlen
= strlen(dir
);
1638 struct entry
*files
;
1639 bool has_music
= false;
1640 bool has_subdir
= false;
1641 struct tree_context
* tc
= tree_get_context();
1644 dir
+ dirlen
, MAX_PATH
- dirlen
,
1645 /* only add a trailing slash if we need one */
1646 dirlen
&& dir
[dirlen
- 1] == '/' ? "%s" : "/%s",
1650 if (ft_load(tc
, dir
) < 0)
1655 files
= tree_get_entries(tc
);
1656 num_files
= tc
->filesindir
;
1658 for (i
=0; i
<num_files
; i
++)
1660 if (files
[i
].attr
& ATTR_DIRECTORY
)
1662 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1672 tree_lock_cache(tc
);
1673 if (has_subdir
&& recurse
)
1675 for (i
=0; i
<num_files
; i
++)
1677 if (action_userabort(TIMEOUT_NOBLOCK
))
1683 if (files
[i
].attr
& ATTR_DIRECTORY
)
1685 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1691 tree_unlock_cache(tc
);
1704 /* we now need to reload our current directory */
1705 if(ft_load(tc
, dir
) < 0)
1706 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1712 * Returns absolute path of track
1714 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1721 /* Look for the end of the string */
1728 /* Now work back killing white space */
1730 ((src
[i
-1] == ' ') ||
1731 (src
[i
-1] == '\t')))
1734 /* Zero-terminate the file name */
1737 /* replace backslashes with forward slashes */
1738 for ( j
=0; j
<i
; j
++ )
1739 if ( src
[j
] == '\\' )
1744 strlcpy(dest
, src
, buf_length
);
1748 /* handle dos style drive letter */
1750 strlcpy(dest
, &src
[2], buf_length
);
1751 else if (!strncmp(src
, "../", 3))
1753 /* handle relative paths */
1755 while(!strncmp(&src
[i
], "../", 3))
1757 for (j
=0; j
<i
/3; j
++) {
1758 temp_ptr
= strrchr(dir
, '/');
1764 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1766 else if ( '.' == src
[0] && '/' == src
[1] ) {
1767 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1770 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1773 #ifdef HAVE_MULTIVOLUME
1775 char vol_string
[VOL_ENUM_POS
+ 8];
1776 snprintf(vol_string
, sizeof(vol_string
), "/"VOL_NAMES
, 1);
1778 /*check if the playlist is on a external card, and correct path if needed */
1779 if(strstr(dir
, vol_string
) && (strstr(dest
, vol_string
) == NULL
)){
1780 char temp
[buf_length
];
1781 strlcpy(temp
, dest
, buf_length
);
1782 snprintf(dest
, buf_length
, "%s%s", vol_string
, temp
);
1790 * Display splash message showing progress of playlist/directory insertion or
1793 static void display_playlist_count(int count
, const unsigned char *fmt
,
1796 static long talked_tick
= 0;
1797 long id
= P2ID(fmt
);
1798 if(global_settings
.talk_menu
&& id
>=0)
1800 if(final
|| (count
&& (talked_tick
== 0
1801 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1803 talked_tick
= current_tick
;
1804 talk_number(count
, false);
1810 splashf(0, fmt
, count
, str(LANG_OFF_ABORT
));
1814 * Display buffer full message
1816 static void display_buffer_full(void)
1818 splash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1822 * Flush any cached control commands to disk. Called when playlist is being
1823 * modified. Returns 0 on success and -1 on failure.
1825 static int flush_cached_control(struct playlist_info
* playlist
)
1830 if (!playlist
->num_cached
)
1833 lseek(playlist
->control_fd
, 0, SEEK_END
);
1835 for (i
=0; i
<playlist
->num_cached
; i
++)
1837 struct playlist_control_cache
* cache
=
1838 &(playlist
->control_cache
[i
]);
1840 switch (cache
->command
)
1842 case PLAYLIST_COMMAND_PLAYLIST
:
1843 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1844 cache
->i1
, cache
->s1
, cache
->s2
);
1846 case PLAYLIST_COMMAND_ADD
:
1847 case PLAYLIST_COMMAND_QUEUE
:
1848 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1849 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1850 cache
->i1
, cache
->i2
);
1853 /* save the position in file where name is written */
1854 int* seek_pos
= (int *)cache
->data
;
1855 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1856 result
= fdprintf(playlist
->control_fd
, "%s\n",
1860 case PLAYLIST_COMMAND_DELETE
:
1861 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1863 case PLAYLIST_COMMAND_SHUFFLE
:
1864 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1865 cache
->i1
, cache
->i2
);
1867 case PLAYLIST_COMMAND_UNSHUFFLE
:
1868 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1870 case PLAYLIST_COMMAND_RESET
:
1871 result
= fdprintf(playlist
->control_fd
, "R\n");
1883 playlist
->num_cached
= 0;
1884 playlist
->pending_control_sync
= true;
1891 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1898 * Update control data with new command. Depending on the command, it may be
1899 * cached or flushed to disk.
1901 static int update_control(struct playlist_info
* playlist
,
1902 enum playlist_command command
, int i1
, int i2
,
1903 const char* s1
, const char* s2
, void* data
)
1906 struct playlist_control_cache
* cache
;
1909 mutex_lock(playlist
->control_mutex
);
1911 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1913 cache
->command
= command
;
1922 case PLAYLIST_COMMAND_PLAYLIST
:
1923 case PLAYLIST_COMMAND_ADD
:
1924 case PLAYLIST_COMMAND_QUEUE
:
1925 #ifndef HAVE_DIRCACHE
1926 case PLAYLIST_COMMAND_DELETE
:
1927 case PLAYLIST_COMMAND_RESET
:
1931 case PLAYLIST_COMMAND_SHUFFLE
:
1932 case PLAYLIST_COMMAND_UNSHUFFLE
:
1934 /* only flush when needed */
1938 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1939 result
= flush_cached_control(playlist
);
1941 mutex_unlock(playlist
->control_mutex
);
1947 * sync control file to disk
1949 static void sync_control(struct playlist_info
* playlist
, bool force
)
1951 #ifdef HAVE_DIRCACHE
1952 if (playlist
->started
&& force
)
1956 if (playlist
->started
)
1959 if (playlist
->pending_control_sync
)
1961 mutex_lock(playlist
->control_mutex
);
1962 fsync(playlist
->control_fd
);
1963 playlist
->pending_control_sync
= false;
1964 mutex_unlock(playlist
->control_mutex
);
1970 * Rotate indices such that first_index is index 0
1972 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1974 index
-= playlist
->first_index
;
1976 index
+= playlist
->amount
;
1982 * Need no movement protection since all 3 allocations are not passed to
1983 * other functions which can yield().
1985 static int move_callback(int handle
, void* current
, void* new)
1988 struct playlist_info
* playlist
= ¤t_playlist
;
1989 if (current
== playlist
->indices
)
1990 playlist
->indices
= new;
1991 else if (current
== playlist
->filenames
)
1992 playlist
->filenames
= new;
1993 /* buffer can possibly point to a new buffer temporarily (playlist_save()).
1994 * just don't overwrite the pointer to that temp buffer */
1995 else if (current
== playlist
->buffer
)
1996 playlist
->buffer
= new;
1998 return BUFLIB_CB_OK
;
2002 static struct buflib_callbacks ops
= {
2003 .move_callback
= move_callback
,
2004 .shrink_callback
= NULL
,
2007 * Initialize playlist entries at startup
2009 void playlist_init(void)
2012 struct playlist_info
* playlist
= ¤t_playlist
;
2014 mutex_init(¤t_playlist_mutex
);
2015 mutex_init(&created_playlist_mutex
);
2017 playlist
->current
= true;
2018 strlcpy(playlist
->control_filename
, PLAYLIST_CONTROL_FILE
,
2019 sizeof(playlist
->control_filename
));
2021 playlist
->control_fd
= -1;
2022 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
2023 handle
= core_alloc_ex("playlist idx",
2024 playlist
->max_playlist_size
* sizeof(int), &ops
);
2025 playlist
->indices
= core_get_data(handle
);
2026 playlist
->buffer_size
=
2027 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
2028 handle
= core_alloc_ex("playlist buf",
2029 playlist
->buffer_size
, &ops
);
2030 playlist
->buffer
= core_get_data(handle
);
2031 playlist
->buffer_handle
= handle
;
2032 playlist
->control_mutex
= ¤t_playlist_mutex
;
2034 empty_playlist(playlist
, true);
2036 #ifdef HAVE_DIRCACHE
2037 handle
= core_alloc_ex("playlist dc",
2038 playlist
->max_playlist_size
* sizeof(int), &ops
);
2039 playlist
->filenames
= core_get_data(handle
);
2040 memset((void*)playlist
->filenames
, 0xff,
2041 playlist
->max_playlist_size
* sizeof(int));
2042 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
2043 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
2045 queue_init(&playlist_queue
, true);
2050 * Clean playlist at shutdown
2052 void playlist_shutdown(void)
2054 struct playlist_info
* playlist
= ¤t_playlist
;
2056 if (playlist
->control_fd
>= 0)
2058 mutex_lock(playlist
->control_mutex
);
2060 if (playlist
->num_cached
> 0)
2061 flush_cached_control(playlist
);
2063 close(playlist
->control_fd
);
2064 playlist
->control_fd
= -1;
2066 mutex_unlock(playlist
->control_mutex
);
2071 * Create new playlist
2073 int playlist_create(const char *dir
, const char *file
)
2075 struct playlist_info
* playlist
= ¤t_playlist
;
2077 new_playlist(playlist
, dir
, file
);
2080 /* load the playlist file */
2081 add_indices_to_playlist(playlist
, NULL
, 0);
2086 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2089 * Restore the playlist state based on control file commands. Called to
2090 * resume playback after shutdown.
2092 int playlist_resume(void)
2094 struct playlist_info
* playlist
= ¤t_playlist
;
2099 int control_file_size
= 0;
2103 /* use mp3 buffer for maximum load speed */
2104 buffer
= (char *)audio_get_buffer(false, &buflen
);
2106 empty_playlist(playlist
, true);
2108 splash(0, ID2P(LANG_WAIT
));
2109 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2110 if (playlist
->control_fd
< 0)
2112 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2115 playlist
->control_created
= true;
2117 control_file_size
= filesize(playlist
->control_fd
);
2118 if (control_file_size
<= 0)
2120 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2124 /* read a small amount first to get the header */
2125 nread
= read(playlist
->control_fd
, buffer
,
2126 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2129 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2133 playlist
->started
= true;
2139 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2140 int last_newline
= 0;
2142 bool newline
= true;
2143 bool exit_loop
= false;
2148 unsigned long last_tick
= current_tick
;
2149 bool useraborted
= false;
2151 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2153 /* So a splash while we are loading. */
2154 if (TIME_AFTER(current_tick
, last_tick
+ HZ
/4))
2156 splashf(0, str(LANG_LOADING_PERCENT
),
2157 (total_read
+count
)*100/control_file_size
,
2158 str(LANG_OFF_ABORT
));
2159 if (action_userabort(TIMEOUT_NOBLOCK
))
2164 last_tick
= current_tick
;
2167 /* Are we on a new line? */
2168 if((*p
== '\n') || (*p
== '\r'))
2172 /* save last_newline in case we need to load more data */
2173 last_newline
= count
;
2175 switch (current_command
)
2177 case PLAYLIST_COMMAND_PLAYLIST
:
2179 /* str1=version str2=dir str3=file */
2195 version
= atoi(str1
);
2197 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2200 update_playlist_filename(playlist
, str2
, str3
);
2202 if (str3
[0] != '\0')
2204 /* NOTE: add_indices_to_playlist() overwrites the
2205 audiobuf so we need to reload control file
2207 add_indices_to_playlist(playlist
, NULL
, 0);
2209 else if (str2
[0] != '\0')
2211 playlist
->in_ram
= true;
2212 resume_directory(str2
);
2215 /* load the rest of the data */
2221 case PLAYLIST_COMMAND_ADD
:
2222 case PLAYLIST_COMMAND_QUEUE
:
2224 /* str1=position str2=last_position str3=file */
2225 int position
, last_position
;
2228 if (!str1
|| !str2
|| !str3
)
2235 position
= atoi(str1
);
2236 last_position
= atoi(str2
);
2238 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2241 /* seek position is based on str3's position in
2243 if (add_track_to_playlist(playlist
, str3
, position
,
2244 queue
, total_read
+(str3
-buffer
)) < 0)
2247 playlist
->last_insert_pos
= last_position
;
2251 case PLAYLIST_COMMAND_DELETE
:
2263 position
= atoi(str1
);
2265 if (remove_track_from_playlist(playlist
, position
,
2271 case PLAYLIST_COMMAND_SHUFFLE
:
2273 /* str1=seed str2=first_index */
2285 /* Always sort list before shuffling */
2286 sort_playlist(playlist
, false, false);
2290 playlist
->first_index
= atoi(str2
);
2292 if (randomise_playlist(playlist
, seed
, false,
2298 case PLAYLIST_COMMAND_UNSHUFFLE
:
2300 /* str1=first_index */
2308 playlist
->first_index
= atoi(str1
);
2310 if (sort_playlist(playlist
, false, false) < 0)
2316 case PLAYLIST_COMMAND_RESET
:
2318 playlist
->last_insert_pos
= -1;
2321 case PLAYLIST_COMMAND_COMMENT
:
2328 /* to ignore any extra newlines */
2329 current_command
= PLAYLIST_COMMAND_COMMENT
;
2335 /* first non-comment line must always specify playlist */
2336 if (first
&& *p
!= 'P' && *p
!= '#')
2346 /* playlist can only be specified once */
2354 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2357 current_command
= PLAYLIST_COMMAND_ADD
;
2360 current_command
= PLAYLIST_COMMAND_QUEUE
;
2363 current_command
= PLAYLIST_COMMAND_DELETE
;
2366 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2369 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2372 current_command
= PLAYLIST_COMMAND_RESET
;
2375 current_command
= PLAYLIST_COMMAND_COMMENT
;
2388 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2390 /* all control file strings are separated with a colon.
2391 Replace the colon with 0 to get proper strings that can be
2392 used by commands above */
2398 if ((count
+1) < nread
)
2412 /* allow last string to contain colons */
2423 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2429 splash(HZ
*2, ID2P(LANG_CANCEL
));
2432 if (!newline
|| (exit_loop
&& count
<nread
))
2434 if ((total_read
+ count
) >= control_file_size
)
2436 /* no newline at end of control file */
2437 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2441 /* We didn't end on a newline or we exited loop prematurely.
2442 Either way, re-read the remainder. */
2443 count
= last_newline
;
2444 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2447 total_read
+= count
;
2450 /* still looking for header */
2451 nread
= read(playlist
->control_fd
, buffer
,
2452 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2454 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2456 /* Terminate on EOF */
2463 #ifdef HAVE_DIRCACHE
2464 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2471 * Add track to in_ram playlist. Used when playing directories.
2473 int playlist_add(const char *filename
)
2475 struct playlist_info
* playlist
= ¤t_playlist
;
2476 int len
= strlen(filename
);
2478 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2479 (playlist
->amount
>= playlist
->max_playlist_size
))
2481 display_buffer_full();
2485 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2486 #ifdef HAVE_DIRCACHE
2487 playlist
->filenames
[playlist
->amount
] = -1;
2491 strcpy((char*)&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2492 playlist
->buffer_end_pos
+= len
;
2493 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2498 /* shuffle newly created playlist using random seed. */
2499 int playlist_shuffle(int random_seed
, int start_index
)
2501 struct playlist_info
* playlist
= ¤t_playlist
;
2503 bool start_current
= false;
2505 if (start_index
>= 0 && global_settings
.play_selected
)
2507 /* store the seek position before the shuffle */
2508 playlist
->index
= playlist
->first_index
= start_index
;
2509 start_current
= true;
2512 randomise_playlist(playlist
, random_seed
, start_current
, true);
2514 return playlist
->index
;
2517 /* returns the crc32 of the filename of the track at the specified index */
2518 unsigned int playlist_get_filename_crc32(struct playlist_info
*playlist
,
2521 struct playlist_track_info track_info
;
2522 if (playlist_get_track_info(playlist
, index
, &track_info
) == -1)
2525 return crc_32(track_info
.filename
, strlen(track_info
.filename
), -1);
2528 /* resume a playlist track with the given crc_32 of the track name. */
2529 void playlist_resume_track(int start_index
, unsigned int crc
, int offset
)
2532 unsigned int tmp_crc
;
2533 struct playlist_info
* playlist
= ¤t_playlist
;
2534 tmp_crc
= playlist_get_filename_crc32(playlist
, start_index
);
2537 playlist_start(start_index
, offset
);
2541 for (i
= 0 ; i
< playlist
->amount
; i
++)
2543 tmp_crc
= playlist_get_filename_crc32(playlist
, i
);
2546 playlist_start(i
, offset
);
2551 /* If we got here the file wasnt found, so start from the beginning */
2552 playlist_start(0,0);
2555 /* start playing current playlist at specified index/offset */
2556 void playlist_start(int start_index
, int offset
)
2558 struct playlist_info
* playlist
= ¤t_playlist
;
2560 /* Cancel FM radio selection as previous music. For cases where we start
2561 playback without going to the WPS, such as playlist insert.. or
2562 playlist catalog. */
2563 previous_music_is_wps();
2565 playlist
->index
= start_index
;
2567 playlist
->started
= true;
2568 sync_control(playlist
, false);
2572 /* Returns false if 'steps' is out of bounds, else true */
2573 bool playlist_check(int steps
)
2575 struct playlist_info
* playlist
= ¤t_playlist
;
2577 /* always allow folder navigation */
2578 if (global_settings
.next_folder
&& playlist
->in_ram
)
2581 int index
= get_next_index(playlist
, steps
, -1);
2583 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2584 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2586 return (index
>= 0);
2589 /* get trackname of track that is "steps" away from current playing track.
2590 NULL is used to identify end of playlist */
2591 const char* playlist_peek(int steps
, char* buf
, size_t buf_size
)
2593 struct playlist_info
* playlist
= ¤t_playlist
;
2599 index
= get_next_index(playlist
, steps
, -1);
2603 #if CONFIG_CODEC == SWCODEC
2604 /* Just testing - don't care about the file name */
2605 if (!buf
|| !buf_size
)
2609 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2610 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2612 if (get_filename(playlist
, index
, seek
, control_file
, buf
,
2618 if (!playlist
->in_ram
|| control_file
)
2620 /* remove bogus dirs from beginning of path
2621 (workaround for buggy playlist creation tools) */
2624 if (file_exists(temp_ptr
))
2627 temp_ptr
= strchr(temp_ptr
+1, '/');
2632 /* Even though this is an invalid file, we still need to pass a
2633 file name to the caller because NULL is used to indicate end
2643 * Update indices as track has changed
2645 int playlist_next(int steps
)
2647 struct playlist_info
* playlist
= ¤t_playlist
;
2651 #ifdef AB_REPEAT_ENABLE
2652 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2654 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2658 /* We need to delete all the queued songs */
2659 for (i
=0, j
=steps
; i
<j
; i
++)
2661 index
= get_next_index(playlist
, i
, -1);
2663 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2665 remove_track_from_playlist(playlist
, index
, true);
2666 steps
--; /* one less track */
2671 index
= get_next_index(playlist
, steps
, -1);
2675 /* end of playlist... or is it */
2676 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2677 playlist
->amount
> 1)
2679 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2680 playlist
->first_index
= 0;
2681 sort_playlist(playlist
, false, false);
2682 randomise_playlist(playlist
, current_tick
, false, true);
2684 #if CONFIG_CODEC == SWCODEC
2685 playlist
->started
= true;
2687 playlist_start(0, 0);
2689 playlist
->index
= 0;
2692 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2694 index
= create_and_play_dir(steps
, true);
2698 playlist
->index
= index
;
2705 playlist
->index
= index
;
2707 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2709 /* check to see if we've gone beyond the last inserted track */
2710 int cur
= rotate_index(playlist
, index
);
2711 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2715 /* reset last inserted track */
2716 playlist
->last_insert_pos
= -1;
2718 if (playlist
->control_fd
>= 0)
2720 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2721 -1, -1, NULL
, NULL
, NULL
);
2726 sync_control(playlist
, false);
2734 #if CONFIG_CODEC == SWCODEC
2735 /* try playing next or previous folder */
2736 bool playlist_next_dir(int direction
)
2738 /* not to mess up real playlists */
2739 if(!current_playlist
.in_ram
)
2742 return create_and_play_dir(direction
, false) >= 0;
2744 #endif /* CONFIG_CODEC == SWCODEC */
2746 /* Get resume info for current playing song. If return value is -1 then
2747 settings shouldn't be saved. */
2748 int playlist_get_resume_info(int *resume_index
)
2750 struct playlist_info
* playlist
= ¤t_playlist
;
2752 *resume_index
= playlist
->index
;
2757 /* Update resume info for current playing song. Returns -1 on error. */
2758 int playlist_update_resume_info(const struct mp3entry
* id3
)
2760 struct playlist_info
* playlist
= ¤t_playlist
;
2764 if (global_status
.resume_index
!= playlist
->index
||
2765 global_status
.resume_offset
!= id3
->offset
)
2767 unsigned int crc
= crc_32(id3
->path
, strlen(id3
->path
), -1);
2768 global_status
.resume_index
= playlist
->index
;
2769 global_status
.resume_crc32
= crc
;
2770 global_status
.resume_offset
= id3
->offset
;
2776 global_status
.resume_index
= -1;
2777 global_status
.resume_crc32
= -1;
2778 global_status
.resume_offset
= -1;
2785 /* Returns index of current playing track for display purposes. This value
2786 should not be used for resume purposes as it doesn't represent the actual
2787 index into the playlist */
2788 int playlist_get_display_index(void)
2790 struct playlist_info
* playlist
= ¤t_playlist
;
2792 /* first_index should always be index 0 for display purposes */
2793 int index
= rotate_index(playlist
, playlist
->index
);
2798 /* returns number of tracks in current playlist */
2799 int playlist_amount(void)
2801 return playlist_amount_ex(NULL
);
2803 /* set playlist->last_shuffle_start to playlist->amount for
2804 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2805 void playlist_set_last_shuffled_start(void)
2807 struct playlist_info
* playlist
= ¤t_playlist
;
2808 playlist
->last_shuffled_start
= playlist
->amount
;
2811 * Create a new playlist If playlist is not NULL then we're loading a
2812 * playlist off disk for viewing/editing. The index_buffer is used to store
2813 * playlist indices (required for and only used if !current playlist). The
2814 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2816 int playlist_create_ex(struct playlist_info
* playlist
,
2817 const char* dir
, const char* file
,
2818 void* index_buffer
, int index_buffer_size
,
2819 void* temp_buffer
, int temp_buffer_size
)
2822 playlist
= ¤t_playlist
;
2825 /* Initialize playlist structure */
2826 int r
= rand() % 10;
2827 playlist
->current
= false;
2829 /* Use random name for control file */
2830 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2831 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2833 playlist
->control_fd
= -1;
2837 int num_indices
= index_buffer_size
/ sizeof(int);
2839 #ifdef HAVE_DIRCACHE
2842 if (num_indices
> global_settings
.max_files_in_playlist
)
2843 num_indices
= global_settings
.max_files_in_playlist
;
2845 playlist
->max_playlist_size
= num_indices
;
2846 playlist
->indices
= index_buffer
;
2847 #ifdef HAVE_DIRCACHE
2848 playlist
->filenames
= (int*)&playlist
->indices
[num_indices
];
2853 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2854 playlist
->indices
= current_playlist
.indices
;
2855 #ifdef HAVE_DIRCACHE
2856 playlist
->filenames
= current_playlist
.filenames
;
2860 playlist
->buffer_size
= 0;
2861 playlist
->buffer_handle
= -1;
2862 playlist
->buffer
= NULL
;
2863 playlist
->control_mutex
= &created_playlist_mutex
;
2866 new_playlist(playlist
, dir
, file
);
2869 /* load the playlist file */
2870 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2876 * Set the specified playlist as the current.
2877 * NOTE: You will get undefined behaviour if something is already playing so
2878 * remember to stop before calling this. Also, this call will
2879 * effectively close your playlist, making it unusable.
2881 int playlist_set_current(struct playlist_info
* playlist
)
2883 if (!playlist
|| (check_control(playlist
) < 0))
2886 empty_playlist(¤t_playlist
, false);
2888 strlcpy(current_playlist
.filename
, playlist
->filename
,
2889 sizeof(current_playlist
.filename
));
2891 current_playlist
.utf8
= playlist
->utf8
;
2892 current_playlist
.fd
= playlist
->fd
;
2894 close(playlist
->control_fd
);
2895 playlist
->control_fd
= -1;
2896 close(current_playlist
.control_fd
);
2897 current_playlist
.control_fd
= -1;
2898 remove(current_playlist
.control_filename
);
2899 current_playlist
.control_created
= false;
2900 if (rename(playlist
->control_filename
,
2901 current_playlist
.control_filename
) < 0)
2903 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2905 if (current_playlist
.control_fd
< 0)
2907 current_playlist
.control_created
= true;
2909 current_playlist
.dirlen
= playlist
->dirlen
;
2911 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2913 memcpy((void*)current_playlist
.indices
, (void*)playlist
->indices
,
2914 playlist
->max_playlist_size
*sizeof(int));
2915 #ifdef HAVE_DIRCACHE
2916 memcpy((void*)current_playlist
.filenames
, (void*)playlist
->filenames
,
2917 playlist
->max_playlist_size
*sizeof(int));
2921 current_playlist
.first_index
= playlist
->first_index
;
2922 current_playlist
.amount
= playlist
->amount
;
2923 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2924 current_playlist
.seed
= playlist
->seed
;
2925 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2926 current_playlist
.deleted
= playlist
->deleted
;
2927 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2929 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2930 sizeof(current_playlist
.control_cache
));
2931 current_playlist
.num_cached
= playlist
->num_cached
;
2932 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2936 struct playlist_info
*playlist_get_current(void)
2938 return ¤t_playlist
;
2941 * Close files and delete control file for non-current playlist.
2943 void playlist_close(struct playlist_info
* playlist
)
2948 if (playlist
->fd
>= 0) {
2949 close(playlist
->fd
);
2953 if (playlist
->control_fd
>= 0) {
2954 close(playlist
->control_fd
);
2955 playlist
->control_fd
= -1;
2958 if (playlist
->control_created
) {
2959 remove(playlist
->control_filename
);
2960 playlist
->control_created
= false;
2964 void playlist_sync(struct playlist_info
* playlist
)
2967 playlist
= ¤t_playlist
;
2969 sync_control(playlist
, false);
2970 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2971 audio_flush_and_reload_tracks();
2973 #ifdef HAVE_DIRCACHE
2974 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2979 * Insert track into playlist at specified position (or one of the special
2980 * positions). Returns position where track was inserted or -1 if error.
2982 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2983 int position
, bool queue
, bool sync
)
2988 playlist
= ¤t_playlist
;
2990 if (check_control(playlist
) < 0)
2992 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2996 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2998 /* Check if we want manually sync later. For example when adding
2999 * bunch of files from tagcache, syncing after every file wouldn't be
3000 * a good thing to do. */
3001 if (sync
&& result
>= 0)
3002 playlist_sync(playlist
);
3008 * Insert all tracks from specified directory into playlist.
3010 int playlist_insert_directory(struct playlist_info
* playlist
,
3011 const char *dirname
, int position
, bool queue
,
3015 unsigned char *count_str
;
3016 struct directory_search_context context
;
3019 playlist
= ¤t_playlist
;
3021 if (check_control(playlist
) < 0)
3023 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3027 if (position
== PLAYLIST_REPLACE
)
3029 if (playlist_remove_all_tracks(playlist
) == 0)
3030 position
= PLAYLIST_INSERT_LAST
;
3036 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
3038 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
3040 display_playlist_count(0, count_str
, false);
3042 context
.playlist
= playlist
;
3043 context
.position
= position
;
3044 context
.queue
= queue
;
3049 result
= playlist_directory_tracksearch(dirname
, recurse
,
3050 directory_search_callback
, &context
);
3052 sync_control(playlist
, false);
3056 display_playlist_count(context
.count
, count_str
, true);
3058 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3059 audio_flush_and_reload_tracks();
3061 #ifdef HAVE_DIRCACHE
3062 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3069 * Insert all tracks from specified playlist into dynamic playlist.
3071 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
3072 int position
, bool queue
)
3078 unsigned char *count_str
;
3079 char temp_buf
[MAX_PATH
+1];
3080 char trackname
[MAX_PATH
+1];
3083 bool utf8
= is_m3u8(filename
);
3086 playlist
= ¤t_playlist
;
3088 if (check_control(playlist
) < 0)
3090 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3094 fd
= open_utf8(filename
, O_RDONLY
);
3097 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3101 /* we need the directory name for formatting purposes */
3104 temp_ptr
= strrchr(filename
+1,'/');
3111 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
3113 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
3115 display_playlist_count(count
, count_str
, false);
3117 if (position
== PLAYLIST_REPLACE
)
3119 if (playlist_remove_all_tracks(playlist
) == 0)
3120 position
= PLAYLIST_INSERT_LAST
;
3126 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3129 if (action_userabort(TIMEOUT_NOBLOCK
))
3132 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3138 /* Use trackname as a temporay buffer. Note that trackname must
3139 * be as large as temp_buf.
3141 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3144 /* we need to format so that relative paths are correctly
3146 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3153 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3162 /* Make sure tracks are inserted in correct order if user
3163 requests INSERT_FIRST */
3164 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3165 position
= insert_pos
+ 1;
3169 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3171 display_playlist_count(count
, count_str
, false);
3173 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3174 (audio_status() & AUDIO_STATUS_PLAY
) &&
3176 audio_flush_and_reload_tracks();
3180 /* let the other threads work */
3189 sync_control(playlist
, false);
3193 display_playlist_count(count
, count_str
, true);
3195 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3196 audio_flush_and_reload_tracks();
3198 #ifdef HAVE_DIRCACHE
3199 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3206 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3207 * we want to delete the current playing track.
3209 int playlist_delete(struct playlist_info
* playlist
, int index
)
3214 playlist
= ¤t_playlist
;
3216 if (check_control(playlist
) < 0)
3218 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3222 if (index
== PLAYLIST_DELETE_CURRENT
)
3223 index
= playlist
->index
;
3225 result
= remove_track_from_playlist(playlist
, index
, true);
3227 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3229 audio_flush_and_reload_tracks();
3235 * Move track at index to new_index. Tracks between the two are shifted
3236 * appropriately. Returns 0 on success and -1 on failure.
3238 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3244 bool current
= false;
3246 char filename
[MAX_PATH
];
3249 playlist
= ¤t_playlist
;
3251 if (check_control(playlist
) < 0)
3253 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3257 if (index
== new_index
)
3260 if (index
== playlist
->index
)
3261 /* Moving the current track */
3264 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3265 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3266 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3268 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3269 sizeof(filename
)) < 0)
3272 /* We want to insert the track at the position that was specified by
3273 new_index. This may be different then new_index because of the
3274 shifting that will occur after the delete.
3275 We calculate this before we do the remove as it depends on the
3276 size of the playlist before the track removal */
3277 r
= rotate_index(playlist
, new_index
);
3279 /* Delete track from original position */
3280 result
= remove_track_from_playlist(playlist
, index
, true);
3286 new_index
= PLAYLIST_PREPEND
;
3287 else if (r
== playlist
->amount
)
3289 new_index
= PLAYLIST_INSERT_LAST
;
3291 /* Calculate index of desired position */
3292 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3294 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3301 /* Moved the current track */
3304 case PLAYLIST_PREPEND
:
3305 playlist
->index
= playlist
->first_index
;
3307 case PLAYLIST_INSERT_LAST
:
3308 playlist
->index
= playlist
->first_index
- 1;
3309 if (playlist
->index
< 0)
3310 playlist
->index
+= playlist
->amount
;
3313 playlist
->index
= new_index
;
3318 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3319 audio_flush_and_reload_tracks();
3323 #ifdef HAVE_DIRCACHE
3324 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3330 /* shuffle currently playing playlist */
3331 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3337 playlist
= ¤t_playlist
;
3339 check_control(playlist
);
3341 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3343 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3345 audio_flush_and_reload_tracks();
3350 /* sort currently playing playlist */
3351 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3356 playlist
= ¤t_playlist
;
3358 check_control(playlist
);
3360 result
= sort_playlist(playlist
, start_current
, true);
3362 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3364 audio_flush_and_reload_tracks();
3369 /* returns true if playlist has been modified */
3370 bool playlist_modified(const struct playlist_info
* playlist
)
3373 playlist
= ¤t_playlist
;
3375 if (playlist
->shuffle_modified
||
3376 playlist
->deleted
||
3377 playlist
->num_inserted_tracks
> 0)
3383 /* returns index of first track in playlist */
3384 int playlist_get_first_index(const struct playlist_info
* playlist
)
3387 playlist
= ¤t_playlist
;
3389 return playlist
->first_index
;
3392 /* returns shuffle seed of playlist */
3393 int playlist_get_seed(const struct playlist_info
* playlist
)
3396 playlist
= ¤t_playlist
;
3398 return playlist
->seed
;
3401 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3402 int playlist_amount_ex(const struct playlist_info
* playlist
)
3405 playlist
= ¤t_playlist
;
3407 return playlist
->amount
;
3410 /* returns full path of playlist (minus extension) */
3411 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3417 playlist
= ¤t_playlist
;
3419 strlcpy(buf
, playlist
->filename
+playlist
->dirlen
, buf_size
);
3424 /* Remove extension */
3425 sep
= strrchr(buf
, '.');
3432 /* returns the playlist filename */
3433 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3437 playlist
= ¤t_playlist
;
3439 strlcpy(buf
, playlist
->filename
, buf_size
);
3447 /* Fills info structure with information about track at specified index.
3448 Returns 0 on success and -1 on failure */
3449 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3450 struct playlist_track_info
* info
)
3456 playlist
= ¤t_playlist
;
3458 if (index
< 0 || index
>= playlist
->amount
)
3461 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3462 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3464 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3465 sizeof(info
->filename
)) < 0)
3472 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3473 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3475 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3479 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3480 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3482 info
->index
= index
;
3483 info
->display_index
= rotate_index(playlist
, index
) + 1;
3488 /* save the current dynamic playlist to specified file */
3489 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3494 char path
[MAX_PATH
+1];
3495 char tmp_buf
[MAX_PATH
+1];
3497 bool overwrite_current
= false;
3500 playlist
= ¤t_playlist
;
3502 if (playlist
->amount
<= 0)
3505 /* use current working directory as base for pathname */
3506 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3507 strlen(filename
)+1, "/") < 0)
3510 /* can ignore volatile here, because core_get_data() is called later */
3511 char* old_buffer
= (char*)playlist
->buffer
;
3512 size_t old_buffer_size
= playlist
->buffer_size
;
3514 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3516 /* Attempting to overwrite current playlist file.*/
3518 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3520 /* not enough buffer space to store updated indices */
3521 /* Try to get a buffer */
3522 playlist
->buffer
= plugin_get_buffer((size_t*)&playlist
->buffer_size
);
3523 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3525 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3527 goto reset_old_buffer
;
3531 /* use temporary pathname */
3532 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3533 overwrite_current
= true;
3538 fd
= open_utf8(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3542 /* some applications require a BOM to read the file properly */
3543 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0666);
3547 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3549 goto reset_old_buffer
;
3552 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3556 index
= playlist
->first_index
;
3557 for (i
=0; i
<playlist
->amount
; i
++)
3564 if (action_userabort(TIMEOUT_NOBLOCK
))
3570 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3571 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3572 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3574 /* Don't save queued files */
3577 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3584 if (overwrite_current
)
3585 playlist
->seek_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3587 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3589 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3596 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3597 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3603 index
= (index
+1)%playlist
->amount
;
3606 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3610 if (overwrite_current
&& result
>= 0)
3614 mutex_lock(playlist
->control_mutex
);
3616 /* Replace the current playlist with the new one and update indices */
3617 close(playlist
->fd
);
3619 if (remove(playlist
->filename
) >= 0)
3621 if (rename(path
, playlist
->filename
) >= 0)
3623 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3624 if (playlist
->fd
>= 0)
3626 index
= playlist
->first_index
;
3627 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3629 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3631 playlist
->indices
[index
] = playlist
->seek_buf
[count
];
3634 index
= (index
+1)%playlist
->amount
;
3637 /* we need to recreate control because inserted tracks are
3638 now part of the playlist and shuffle has been
3640 result
= recreate_control(playlist
);
3645 mutex_unlock(playlist
->control_mutex
);
3652 if (playlist
->buffer_handle
> 0)
3653 old_buffer
= core_get_data(playlist
->buffer_handle
);
3654 playlist
->buffer
= old_buffer
;
3655 playlist
->buffer_size
= old_buffer_size
;
3661 * Search specified directory for tracks and notify via callback. May be
3662 * called recursively.
3664 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3665 int (*callback
)(char*, void*),
3668 char buf
[MAX_PATH
+1];
3672 struct tree_context
* tc
= tree_get_context();
3673 struct tree_cache
* cache
= &tc
->cache
;
3674 int old_dirfilter
= *(tc
->dirfilter
);
3679 /* use the tree browser dircache to load files */
3680 *(tc
->dirfilter
) = SHOW_ALL
;
3682 if (ft_load(tc
, dirname
) < 0)
3684 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3685 *(tc
->dirfilter
) = old_dirfilter
;
3689 num_files
= tc
->filesindir
;
3691 /* we've overwritten the dircache so tree browser will need to be
3695 for (i
=0; i
<num_files
; i
++)
3698 if (action_userabort(TIMEOUT_NOBLOCK
))
3704 struct entry
*files
= core_get_data(cache
->entries_handle
);
3705 if (files
[i
].attr
& ATTR_DIRECTORY
)
3709 /* recursively add directories */
3710 snprintf(buf
, sizeof(buf
), "%s/%s",
3711 dirname
[1]? dirname
: "", files
[i
].name
);
3712 result
= playlist_directory_tracksearch(buf
, recurse
,
3717 /* we now need to reload our current directory */
3718 if(ft_load(tc
, dirname
) < 0)
3724 num_files
= tc
->filesindir
;
3734 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3736 snprintf(buf
, sizeof(buf
), "%s/%s",
3737 dirname
[1]? dirname
: "", files
[i
].name
);
3739 if (callback(buf
, context
) != 0)
3745 /* let the other threads work */
3750 /* restore dirfilter */
3751 *(tc
->dirfilter
) = old_dirfilter
;