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"
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
;
1925 * Initialize playlist entries at startup
1927 void playlist_init(void)
1929 struct playlist_info
* playlist
= ¤t_playlist
;
1931 mutex_init(¤t_playlist_mutex
);
1932 mutex_init(&created_playlist_mutex
);
1934 playlist
->current
= true;
1935 strlcpy(playlist
->control_filename
, PLAYLIST_CONTROL_FILE
,
1936 sizeof(playlist
->control_filename
));
1938 playlist
->control_fd
= -1;
1939 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1940 playlist
->indices
= buffer_alloc(
1941 playlist
->max_playlist_size
* sizeof(int));
1942 playlist
->buffer_size
=
1943 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1944 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1945 playlist
->control_mutex
= ¤t_playlist_mutex
;
1947 empty_playlist(playlist
, true);
1949 #ifdef HAVE_DIRCACHE
1950 playlist
->filenames
= buffer_alloc(
1951 playlist
->max_playlist_size
* sizeof(int));
1952 memset(playlist
->filenames
, 0xff,
1953 playlist
->max_playlist_size
* sizeof(int));
1954 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1955 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1957 queue_init(&playlist_queue
, true);
1962 * Clean playlist at shutdown
1964 void playlist_shutdown(void)
1966 struct playlist_info
* playlist
= ¤t_playlist
;
1968 if (playlist
->control_fd
>= 0)
1970 mutex_lock(playlist
->control_mutex
);
1972 if (playlist
->num_cached
> 0)
1973 flush_cached_control(playlist
);
1975 close(playlist
->control_fd
);
1977 mutex_unlock(playlist
->control_mutex
);
1982 * Create new playlist
1984 int playlist_create(const char *dir
, const char *file
)
1986 struct playlist_info
* playlist
= ¤t_playlist
;
1988 new_playlist(playlist
, dir
, file
);
1991 /* load the playlist file */
1992 add_indices_to_playlist(playlist
, NULL
, 0);
1997 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2000 * Restore the playlist state based on control file commands. Called to
2001 * resume playback after shutdown.
2003 int playlist_resume(void)
2005 struct playlist_info
* playlist
= ¤t_playlist
;
2010 int control_file_size
= 0;
2014 /* use mp3 buffer for maximum load speed */
2015 buffer
= (char *)audio_get_buffer(false, &buflen
);
2017 empty_playlist(playlist
, true);
2019 splash(0, ID2P(LANG_WAIT
));
2020 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2021 if (playlist
->control_fd
< 0)
2023 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2026 playlist
->control_created
= true;
2028 control_file_size
= filesize(playlist
->control_fd
);
2029 if (control_file_size
<= 0)
2031 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2035 /* read a small amount first to get the header */
2036 nread
= read(playlist
->control_fd
, buffer
,
2037 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2040 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2044 playlist
->started
= true;
2050 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2051 int last_newline
= 0;
2053 bool newline
= true;
2054 bool exit_loop
= false;
2059 unsigned long last_tick
= current_tick
;
2060 bool useraborted
= false;
2062 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2064 /* So a splash while we are loading. */
2065 if (TIME_AFTER(current_tick
, last_tick
+ HZ
/4))
2067 splashf(0, str(LANG_LOADING_PERCENT
),
2068 (total_read
+count
)*100/control_file_size
,
2069 str(LANG_OFF_ABORT
));
2070 if (action_userabort(TIMEOUT_NOBLOCK
))
2075 last_tick
= current_tick
;
2078 /* Are we on a new line? */
2079 if((*p
== '\n') || (*p
== '\r'))
2083 /* save last_newline in case we need to load more data */
2084 last_newline
= count
;
2086 switch (current_command
)
2088 case PLAYLIST_COMMAND_PLAYLIST
:
2090 /* str1=version str2=dir str3=file */
2106 version
= atoi(str1
);
2108 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2111 update_playlist_filename(playlist
, str2
, str3
);
2113 if (str3
[0] != '\0')
2115 /* NOTE: add_indices_to_playlist() overwrites the
2116 audiobuf so we need to reload control file
2118 add_indices_to_playlist(playlist
, NULL
, 0);
2120 else if (str2
[0] != '\0')
2122 playlist
->in_ram
= true;
2123 resume_directory(str2
);
2126 /* load the rest of the data */
2132 case PLAYLIST_COMMAND_ADD
:
2133 case PLAYLIST_COMMAND_QUEUE
:
2135 /* str1=position str2=last_position str3=file */
2136 int position
, last_position
;
2139 if (!str1
|| !str2
|| !str3
)
2146 position
= atoi(str1
);
2147 last_position
= atoi(str2
);
2149 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2152 /* seek position is based on str3's position in
2154 if (add_track_to_playlist(playlist
, str3
, position
,
2155 queue
, total_read
+(str3
-buffer
)) < 0)
2158 playlist
->last_insert_pos
= last_position
;
2162 case PLAYLIST_COMMAND_DELETE
:
2174 position
= atoi(str1
);
2176 if (remove_track_from_playlist(playlist
, position
,
2182 case PLAYLIST_COMMAND_SHUFFLE
:
2184 /* str1=seed str2=first_index */
2196 /* Always sort list before shuffling */
2197 sort_playlist(playlist
, false, false);
2201 playlist
->first_index
= atoi(str2
);
2203 if (randomise_playlist(playlist
, seed
, false,
2209 case PLAYLIST_COMMAND_UNSHUFFLE
:
2211 /* str1=first_index */
2219 playlist
->first_index
= atoi(str1
);
2221 if (sort_playlist(playlist
, false, false) < 0)
2227 case PLAYLIST_COMMAND_RESET
:
2229 playlist
->last_insert_pos
= -1;
2232 case PLAYLIST_COMMAND_COMMENT
:
2239 /* to ignore any extra newlines */
2240 current_command
= PLAYLIST_COMMAND_COMMENT
;
2246 /* first non-comment line must always specify playlist */
2247 if (first
&& *p
!= 'P' && *p
!= '#')
2257 /* playlist can only be specified once */
2265 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2268 current_command
= PLAYLIST_COMMAND_ADD
;
2271 current_command
= PLAYLIST_COMMAND_QUEUE
;
2274 current_command
= PLAYLIST_COMMAND_DELETE
;
2277 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2280 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2283 current_command
= PLAYLIST_COMMAND_RESET
;
2286 current_command
= PLAYLIST_COMMAND_COMMENT
;
2299 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2301 /* all control file strings are separated with a colon.
2302 Replace the colon with 0 to get proper strings that can be
2303 used by commands above */
2309 if ((count
+1) < nread
)
2323 /* allow last string to contain colons */
2334 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2340 splash(HZ
*2, ID2P(LANG_CANCEL
));
2343 if (!newline
|| (exit_loop
&& count
<nread
))
2345 if ((total_read
+ count
) >= control_file_size
)
2347 /* no newline at end of control file */
2348 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2352 /* We didn't end on a newline or we exited loop prematurely.
2353 Either way, re-read the remainder. */
2354 count
= last_newline
;
2355 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2358 total_read
+= count
;
2361 /* still looking for header */
2362 nread
= read(playlist
->control_fd
, buffer
,
2363 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2365 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2367 /* Terminate on EOF */
2374 #ifdef HAVE_DIRCACHE
2375 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2382 * Add track to in_ram playlist. Used when playing directories.
2384 int playlist_add(const char *filename
)
2386 struct playlist_info
* playlist
= ¤t_playlist
;
2387 int len
= strlen(filename
);
2389 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2390 (playlist
->amount
>= playlist
->max_playlist_size
))
2392 display_buffer_full();
2396 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2397 #ifdef HAVE_DIRCACHE
2398 playlist
->filenames
[playlist
->amount
] = -1;
2402 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2403 playlist
->buffer_end_pos
+= len
;
2404 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2409 /* shuffle newly created playlist using random seed. */
2410 int playlist_shuffle(int random_seed
, int start_index
)
2412 struct playlist_info
* playlist
= ¤t_playlist
;
2414 bool start_current
= false;
2416 if (start_index
>= 0 && global_settings
.play_selected
)
2418 /* store the seek position before the shuffle */
2419 playlist
->index
= playlist
->first_index
= start_index
;
2420 start_current
= true;
2423 randomise_playlist(playlist
, random_seed
, start_current
, true);
2425 return playlist
->index
;
2428 /* start playing current playlist at specified index/offset */
2429 void playlist_start(int start_index
, int offset
)
2431 struct playlist_info
* playlist
= ¤t_playlist
;
2433 /* Cancel FM radio selection as previous music. For cases where we start
2434 playback without going to the WPS, such as playlist insert.. or
2435 playlist catalog. */
2436 previous_music_is_wps();
2438 playlist
->index
= start_index
;
2440 playlist
->started
= true;
2441 sync_control(playlist
, false);
2445 /* Returns false if 'steps' is out of bounds, else true */
2446 bool playlist_check(int steps
)
2448 struct playlist_info
* playlist
= ¤t_playlist
;
2450 /* always allow folder navigation */
2451 if (global_settings
.next_folder
&& playlist
->in_ram
)
2454 int index
= get_next_index(playlist
, steps
, -1);
2456 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2457 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2459 return (index
>= 0);
2462 /* get trackname of track that is "steps" away from current playing track.
2463 NULL is used to identify end of playlist */
2464 const char* playlist_peek(int steps
, char* buf
, size_t buf_size
)
2466 struct playlist_info
* playlist
= ¤t_playlist
;
2472 index
= get_next_index(playlist
, steps
, -1);
2476 #if CONFIG_CODEC == SWCODEC
2477 /* Just testing - don't care about the file name */
2478 if (!buf
|| !buf_size
)
2482 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2483 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2485 if (get_filename(playlist
, index
, seek
, control_file
, buf
,
2491 if (!playlist
->in_ram
|| control_file
)
2493 /* remove bogus dirs from beginning of path
2494 (workaround for buggy playlist creation tools) */
2497 if (file_exists(temp_ptr
))
2500 temp_ptr
= strchr(temp_ptr
+1, '/');
2505 /* Even though this is an invalid file, we still need to pass a
2506 file name to the caller because NULL is used to indicate end
2516 * Update indices as track has changed
2518 int playlist_next(int steps
)
2520 struct playlist_info
* playlist
= ¤t_playlist
;
2524 #ifdef AB_REPEAT_ENABLE
2525 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2527 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2531 /* We need to delete all the queued songs */
2532 for (i
=0, j
=steps
; i
<j
; i
++)
2534 index
= get_next_index(playlist
, i
, -1);
2536 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2538 remove_track_from_playlist(playlist
, index
, true);
2539 steps
--; /* one less track */
2544 index
= get_next_index(playlist
, steps
, -1);
2548 /* end of playlist... or is it */
2549 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2550 playlist
->amount
> 1)
2552 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2553 playlist
->first_index
= 0;
2554 sort_playlist(playlist
, false, false);
2555 randomise_playlist(playlist
, current_tick
, false, true);
2557 #if CONFIG_CODEC == SWCODEC
2558 playlist
->started
= true;
2560 playlist_start(0, 0);
2562 playlist
->index
= 0;
2565 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2567 index
= create_and_play_dir(steps
, true);
2571 playlist
->index
= index
;
2578 playlist
->index
= index
;
2580 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2582 /* check to see if we've gone beyond the last inserted track */
2583 int cur
= rotate_index(playlist
, index
);
2584 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2588 /* reset last inserted track */
2589 playlist
->last_insert_pos
= -1;
2591 if (playlist
->control_fd
>= 0)
2593 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2594 -1, -1, NULL
, NULL
, NULL
);
2599 sync_control(playlist
, false);
2607 /* try playing next or previous folder */
2608 bool playlist_next_dir(int direction
)
2610 /* not to mess up real playlists */
2611 if(!current_playlist
.in_ram
)
2614 return create_and_play_dir(direction
, false) >= 0;
2617 /* Get resume info for current playing song. If return value is -1 then
2618 settings shouldn't be saved. */
2619 int playlist_get_resume_info(int *resume_index
)
2621 struct playlist_info
* playlist
= ¤t_playlist
;
2623 *resume_index
= playlist
->index
;
2628 /* Update resume info for current playing song. Returns -1 on error. */
2629 int playlist_update_resume_info(const struct mp3entry
* id3
)
2631 struct playlist_info
* playlist
= ¤t_playlist
;
2635 if (global_status
.resume_index
!= playlist
->index
||
2636 global_status
.resume_offset
!= id3
->offset
)
2638 global_status
.resume_index
= playlist
->index
;
2639 global_status
.resume_offset
= id3
->offset
;
2645 global_status
.resume_index
= -1;
2646 global_status
.resume_offset
= -1;
2653 /* Returns index of current playing track for display purposes. This value
2654 should not be used for resume purposes as it doesn't represent the actual
2655 index into the playlist */
2656 int playlist_get_display_index(void)
2658 struct playlist_info
* playlist
= ¤t_playlist
;
2660 /* first_index should always be index 0 for display purposes */
2661 int index
= rotate_index(playlist
, playlist
->index
);
2666 /* returns number of tracks in current playlist */
2667 int playlist_amount(void)
2669 return playlist_amount_ex(NULL
);
2671 /* set playlist->last_shuffle_start to playlist->amount for
2672 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2673 void playlist_set_last_shuffled_start(void)
2675 struct playlist_info
* playlist
= ¤t_playlist
;
2676 playlist
->last_shuffled_start
= playlist
->amount
;
2679 * Create a new playlist If playlist is not NULL then we're loading a
2680 * playlist off disk for viewing/editing. The index_buffer is used to store
2681 * playlist indices (required for and only used if !current playlist). The
2682 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2684 int playlist_create_ex(struct playlist_info
* playlist
,
2685 const char* dir
, const char* file
,
2686 void* index_buffer
, int index_buffer_size
,
2687 void* temp_buffer
, int temp_buffer_size
)
2690 playlist
= ¤t_playlist
;
2693 /* Initialize playlist structure */
2694 int r
= rand() % 10;
2695 playlist
->current
= false;
2697 /* Use random name for control file */
2698 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2699 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2701 playlist
->control_fd
= -1;
2705 int num_indices
= index_buffer_size
/ sizeof(int);
2707 #ifdef HAVE_DIRCACHE
2710 if (num_indices
> global_settings
.max_files_in_playlist
)
2711 num_indices
= global_settings
.max_files_in_playlist
;
2713 playlist
->max_playlist_size
= num_indices
;
2714 playlist
->indices
= index_buffer
;
2715 #ifdef HAVE_DIRCACHE
2716 playlist
->filenames
= (int*)&playlist
->indices
[num_indices
];
2721 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2722 playlist
->indices
= current_playlist
.indices
;
2723 #ifdef HAVE_DIRCACHE
2724 playlist
->filenames
= current_playlist
.filenames
;
2728 playlist
->buffer_size
= 0;
2729 playlist
->buffer
= NULL
;
2730 playlist
->control_mutex
= &created_playlist_mutex
;
2733 new_playlist(playlist
, dir
, file
);
2736 /* load the playlist file */
2737 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2743 * Set the specified playlist as the current.
2744 * NOTE: You will get undefined behaviour if something is already playing so
2745 * remember to stop before calling this. Also, this call will
2746 * effectively close your playlist, making it unusable.
2748 int playlist_set_current(struct playlist_info
* playlist
)
2750 if (!playlist
|| (check_control(playlist
) < 0))
2753 empty_playlist(¤t_playlist
, false);
2755 strlcpy(current_playlist
.filename
, playlist
->filename
,
2756 sizeof(current_playlist
.filename
));
2758 current_playlist
.utf8
= playlist
->utf8
;
2759 current_playlist
.fd
= playlist
->fd
;
2761 close(playlist
->control_fd
);
2762 close(current_playlist
.control_fd
);
2763 remove(current_playlist
.control_filename
);
2764 if (rename(playlist
->control_filename
,
2765 current_playlist
.control_filename
) < 0)
2767 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2769 if (current_playlist
.control_fd
< 0)
2771 current_playlist
.control_created
= true;
2773 current_playlist
.dirlen
= playlist
->dirlen
;
2775 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2777 memcpy(current_playlist
.indices
, playlist
->indices
,
2778 playlist
->max_playlist_size
*sizeof(int));
2779 #ifdef HAVE_DIRCACHE
2780 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2781 playlist
->max_playlist_size
*sizeof(int));
2785 current_playlist
.first_index
= playlist
->first_index
;
2786 current_playlist
.amount
= playlist
->amount
;
2787 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2788 current_playlist
.seed
= playlist
->seed
;
2789 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2790 current_playlist
.deleted
= playlist
->deleted
;
2791 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2793 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2794 sizeof(current_playlist
.control_cache
));
2795 current_playlist
.num_cached
= playlist
->num_cached
;
2796 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2800 struct playlist_info
*playlist_get_current(void)
2802 return ¤t_playlist
;
2805 * Close files and delete control file for non-current playlist.
2807 void playlist_close(struct playlist_info
* playlist
)
2812 if (playlist
->fd
>= 0)
2813 close(playlist
->fd
);
2815 if (playlist
->control_fd
>= 0)
2816 close(playlist
->control_fd
);
2818 if (playlist
->control_created
)
2819 remove(playlist
->control_filename
);
2822 void playlist_sync(struct playlist_info
* playlist
)
2825 playlist
= ¤t_playlist
;
2827 sync_control(playlist
, false);
2828 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2829 audio_flush_and_reload_tracks();
2831 #ifdef HAVE_DIRCACHE
2832 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2837 * Insert track into playlist at specified position (or one of the special
2838 * positions). Returns position where track was inserted or -1 if error.
2840 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2841 int position
, bool queue
, bool sync
)
2846 playlist
= ¤t_playlist
;
2848 if (check_control(playlist
) < 0)
2850 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2854 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2856 /* Check if we want manually sync later. For example when adding
2857 * bunch of files from tagcache, syncing after every file wouldn't be
2858 * a good thing to do. */
2859 if (sync
&& result
>= 0)
2860 playlist_sync(playlist
);
2866 * Insert all tracks from specified directory into playlist.
2868 int playlist_insert_directory(struct playlist_info
* playlist
,
2869 const char *dirname
, int position
, bool queue
,
2873 unsigned char *count_str
;
2874 struct directory_search_context context
;
2877 playlist
= ¤t_playlist
;
2879 if (check_control(playlist
) < 0)
2881 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2885 if (position
== PLAYLIST_REPLACE
)
2887 if (playlist_remove_all_tracks(playlist
) == 0)
2888 position
= PLAYLIST_INSERT_LAST
;
2894 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2896 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2898 display_playlist_count(0, count_str
, false);
2900 context
.playlist
= playlist
;
2901 context
.position
= position
;
2902 context
.queue
= queue
;
2907 result
= playlist_directory_tracksearch(dirname
, recurse
,
2908 directory_search_callback
, &context
);
2910 sync_control(playlist
, false);
2914 display_playlist_count(context
.count
, count_str
, true);
2916 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2917 audio_flush_and_reload_tracks();
2919 #ifdef HAVE_DIRCACHE
2920 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2927 * Insert all tracks from specified playlist into dynamic playlist.
2929 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2930 int position
, bool queue
)
2936 unsigned char *count_str
;
2937 char temp_buf
[MAX_PATH
+1];
2938 char trackname
[MAX_PATH
+1];
2941 bool utf8
= is_m3u8(filename
);
2944 playlist
= ¤t_playlist
;
2946 if (check_control(playlist
) < 0)
2948 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2952 fd
= open_utf8(filename
, O_RDONLY
);
2955 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2959 /* we need the directory name for formatting purposes */
2962 temp_ptr
= strrchr(filename
+1,'/');
2969 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2971 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2973 display_playlist_count(count
, count_str
, false);
2975 if (position
== PLAYLIST_REPLACE
)
2977 if (playlist_remove_all_tracks(playlist
) == 0)
2978 position
= PLAYLIST_INSERT_LAST
;
2984 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2987 if (action_userabort(TIMEOUT_NOBLOCK
))
2990 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2996 /* Use trackname as a temporay buffer. Note that trackname must
2997 * be as large as temp_buf.
2999 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3002 /* we need to format so that relative paths are correctly
3004 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3011 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3020 /* Make sure tracks are inserted in correct order if user
3021 requests INSERT_FIRST */
3022 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3023 position
= insert_pos
+ 1;
3027 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3029 display_playlist_count(count
, count_str
, false);
3031 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3032 (audio_status() & AUDIO_STATUS_PLAY
) &&
3034 audio_flush_and_reload_tracks();
3038 /* let the other threads work */
3047 sync_control(playlist
, false);
3051 display_playlist_count(count
, count_str
, true);
3053 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3054 audio_flush_and_reload_tracks();
3056 #ifdef HAVE_DIRCACHE
3057 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3064 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3065 * we want to delete the current playing track.
3067 int playlist_delete(struct playlist_info
* playlist
, int index
)
3072 playlist
= ¤t_playlist
;
3074 if (check_control(playlist
) < 0)
3076 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3080 if (index
== PLAYLIST_DELETE_CURRENT
)
3081 index
= playlist
->index
;
3083 result
= remove_track_from_playlist(playlist
, index
, true);
3085 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3087 audio_flush_and_reload_tracks();
3093 * Move track at index to new_index. Tracks between the two are shifted
3094 * appropriately. Returns 0 on success and -1 on failure.
3096 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3102 bool current
= false;
3104 char filename
[MAX_PATH
];
3107 playlist
= ¤t_playlist
;
3109 if (check_control(playlist
) < 0)
3111 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3115 if (index
== new_index
)
3118 if (index
== playlist
->index
)
3119 /* Moving the current track */
3122 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3123 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3124 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3126 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3127 sizeof(filename
)) < 0)
3130 /* We want to insert the track at the position that was specified by
3131 new_index. This may be different then new_index because of the
3132 shifting that will occur after the delete.
3133 We calculate this before we do the remove as it depends on the
3134 size of the playlist before the track removal */
3135 r
= rotate_index(playlist
, new_index
);
3137 /* Delete track from original position */
3138 result
= remove_track_from_playlist(playlist
, index
, true);
3144 new_index
= PLAYLIST_PREPEND
;
3145 else if (r
== playlist
->amount
)
3147 new_index
= PLAYLIST_INSERT_LAST
;
3149 /* Calculate index of desired position */
3150 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3152 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3159 /* Moved the current track */
3162 case PLAYLIST_PREPEND
:
3163 playlist
->index
= playlist
->first_index
;
3165 case PLAYLIST_INSERT_LAST
:
3166 playlist
->index
= playlist
->first_index
- 1;
3167 if (playlist
->index
< 0)
3168 playlist
->index
+= playlist
->amount
;
3171 playlist
->index
= new_index
;
3176 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3177 audio_flush_and_reload_tracks();
3181 #ifdef HAVE_DIRCACHE
3182 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3188 /* shuffle currently playing playlist */
3189 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3195 playlist
= ¤t_playlist
;
3197 check_control(playlist
);
3199 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3201 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3203 audio_flush_and_reload_tracks();
3208 /* sort currently playing playlist */
3209 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3214 playlist
= ¤t_playlist
;
3216 check_control(playlist
);
3218 result
= sort_playlist(playlist
, start_current
, true);
3220 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3222 audio_flush_and_reload_tracks();
3227 /* returns true if playlist has been modified */
3228 bool playlist_modified(const struct playlist_info
* playlist
)
3231 playlist
= ¤t_playlist
;
3233 if (playlist
->shuffle_modified
||
3234 playlist
->deleted
||
3235 playlist
->num_inserted_tracks
> 0)
3241 /* returns index of first track in playlist */
3242 int playlist_get_first_index(const struct playlist_info
* playlist
)
3245 playlist
= ¤t_playlist
;
3247 return playlist
->first_index
;
3250 /* returns shuffle seed of playlist */
3251 int playlist_get_seed(const struct playlist_info
* playlist
)
3254 playlist
= ¤t_playlist
;
3256 return playlist
->seed
;
3259 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3260 int playlist_amount_ex(const struct playlist_info
* playlist
)
3263 playlist
= ¤t_playlist
;
3265 return playlist
->amount
;
3268 /* returns full path of playlist (minus extension) */
3269 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3275 playlist
= ¤t_playlist
;
3277 strlcpy(buf
, playlist
->filename
+playlist
->dirlen
, buf_size
);
3282 /* Remove extension */
3283 sep
= strrchr(buf
, '.');
3290 /* returns the playlist filename */
3291 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3295 playlist
= ¤t_playlist
;
3297 strlcpy(buf
, playlist
->filename
, buf_size
);
3305 /* Fills info structure with information about track at specified index.
3306 Returns 0 on success and -1 on failure */
3307 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3308 struct playlist_track_info
* info
)
3314 playlist
= ¤t_playlist
;
3316 if (index
< 0 || index
>= playlist
->amount
)
3319 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3320 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3322 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3323 sizeof(info
->filename
)) < 0)
3330 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3331 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3333 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3337 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3338 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3340 info
->index
= index
;
3341 info
->display_index
= rotate_index(playlist
, index
) + 1;
3346 /* save the current dynamic playlist to specified file */
3347 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3352 char path
[MAX_PATH
+1];
3353 char tmp_buf
[MAX_PATH
+1];
3355 bool overwrite_current
= false;
3356 int* index_buf
= NULL
;
3357 char* old_buffer
= NULL
;
3358 size_t old_buffer_size
= 0;
3361 playlist
= ¤t_playlist
;
3363 if (playlist
->amount
<= 0)
3366 /* use current working directory as base for pathname */
3367 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3368 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3371 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3373 /* Attempting to overwrite current playlist file.*/
3375 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3377 /* not enough buffer space to store updated indices */
3378 /* Try to get a buffer */
3379 old_buffer
= playlist
->buffer
;
3380 old_buffer_size
= playlist
->buffer_size
;
3381 playlist
->buffer
= plugin_get_buffer((size_t*)&playlist
->buffer_size
);
3382 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3384 playlist
->buffer
= old_buffer
;
3385 playlist
->buffer_size
= old_buffer_size
;
3386 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3391 /* in_ram buffer is unused for m3u files so we'll use for storing
3393 index_buf
= (int*)playlist
->buffer
;
3395 /* use temporary pathname */
3396 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3397 overwrite_current
= true;
3402 fd
= open_utf8(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3406 /* some applications require a BOM to read the file properly */
3407 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0666);
3411 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3412 if (old_buffer
!= NULL
)
3414 playlist
->buffer
= old_buffer
;
3415 playlist
->buffer_size
= old_buffer_size
;
3420 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3424 index
= playlist
->first_index
;
3425 for (i
=0; i
<playlist
->amount
; i
++)
3432 if (action_userabort(TIMEOUT_NOBLOCK
))
3438 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3439 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3440 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3442 /* Don't save queued files */
3445 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3452 if (overwrite_current
)
3453 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3455 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3457 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3464 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3465 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3471 index
= (index
+1)%playlist
->amount
;
3474 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3478 if (overwrite_current
&& result
>= 0)
3482 mutex_lock(playlist
->control_mutex
);
3484 /* Replace the current playlist with the new one and update indices */
3485 close(playlist
->fd
);
3486 if (remove(playlist
->filename
) >= 0)
3488 if (rename(path
, playlist
->filename
) >= 0)
3490 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3491 if (playlist
->fd
>= 0)
3493 index
= playlist
->first_index
;
3494 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3496 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3498 playlist
->indices
[index
] = index_buf
[count
];
3501 index
= (index
+1)%playlist
->amount
;
3504 /* we need to recreate control because inserted tracks are
3505 now part of the playlist and shuffle has been
3507 result
= recreate_control(playlist
);
3512 mutex_unlock(playlist
->control_mutex
);
3517 if (old_buffer
!= NULL
)
3519 playlist
->buffer
= old_buffer
;
3520 playlist
->buffer_size
= old_buffer_size
;
3527 * Search specified directory for tracks and notify via callback. May be
3528 * called recursively.
3530 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3531 int (*callback
)(char*, void*),
3534 char buf
[MAX_PATH
+1];
3538 struct entry
*files
;
3539 struct tree_context
* tc
= tree_get_context();
3540 int old_dirfilter
= *(tc
->dirfilter
);
3545 /* use the tree browser dircache to load files */
3546 *(tc
->dirfilter
) = SHOW_ALL
;
3548 if (ft_load(tc
, dirname
) < 0)
3550 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3551 *(tc
->dirfilter
) = old_dirfilter
;
3555 files
= tc
->cache
.entries
;
3556 num_files
= tc
->filesindir
;
3558 /* we've overwritten the dircache so tree browser will need to be
3562 for (i
=0; i
<num_files
; i
++)
3565 if (action_userabort(TIMEOUT_NOBLOCK
))
3571 if (files
[i
].attr
& ATTR_DIRECTORY
)
3575 /* recursively add directories */
3576 snprintf(buf
, sizeof(buf
), "%s/%s",
3577 dirname
[1]? dirname
: "", files
[i
].name
);
3578 result
= playlist_directory_tracksearch(buf
, recurse
,
3583 /* we now need to reload our current directory */
3584 if(ft_load(tc
, dirname
) < 0)
3590 files
= tc
->cache
.entries
;
3591 num_files
= tc
->filesindir
;
3601 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3603 snprintf(buf
, sizeof(buf
), "%s/%s",
3604 dirname
[1]? dirname
: "", files
[i
].name
);
3606 if (callback(buf
, context
) != 0)
3612 /* let the other threads work */
3617 /* restore dirfilter */
3618 *(tc
->dirfilter
) = old_dirfilter
;