1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by wavey@wavey.org
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 Dynamic playlist design (based on design originally proposed by ricII)
25 There are two files associated with a dynamic playlist:
26 1. Playlist file : This file contains the initial songs in the playlist.
27 The file is created by the user and stored on the hard
28 drive. NOTE: If we are playing the contents of a
29 directory, there will be no playlist file.
30 2. Control file : This file is automatically created when a playlist is
31 started and contains all the commands done to it.
33 The first non-comment line in a control file must begin with
34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
35 DIR is the directory where the playlist is located and FILE is the
36 playlist filename. For dirplay, FILE will be empty. An empty playlist
37 will have both entries as null.
39 Control file commands:
40 a. Add track (A:<position>:<last position>:<path to track>)
41 - Insert a track at the specified position in the current
42 playlist. Last position is used to specify where last insertion
44 b. Queue track (Q:<position>:<last position>:<path to track>)
45 - Queue a track at the specified position in the current
46 playlist. Queued tracks differ from added tracks in that they
47 are deleted from the playlist as soon as they are played and
48 they are not saved to disk as part of the playlist.
49 c. Delete track (D:<position>)
50 - Delete track from specified position in the current playlist.
51 d. Shuffle playlist (S:<seed>:<index>)
52 - Shuffle entire playlist with specified seed. The index
53 identifies the first index in the newly shuffled playlist
54 (needed for repeat mode).
55 e. Unshuffle playlist (U:<index>)
56 - Unshuffle entire playlist. The index identifies the first index
57 in the newly unshuffled playlist.
58 f. Reset last insert position (R)
59 - Needed so that insertions work properly after resume
62 The only resume info that needs to be saved is the current index in the
63 playlist and the position in the track. When resuming, all the commands
64 in the control file will be reapplied so that the playlist indices are
65 exactly the same as before shutdown. To avoid unnecessary disk
66 accesses, the shuffle mode settings are also saved in settings and only
67 flushed to disk when required.
75 #include "ata_idle_notify.h"
86 #include "applimits.h"
95 #include "filetypes.h"
96 #ifdef HAVE_LCD_BITMAP
104 #include "rbunicode.h"
105 #include "root_menu.h"
107 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
108 #define PLAYLIST_CONTROL_FILE_VERSION 2
111 Each playlist index has a flag associated with it which identifies what
112 type of track it is. These flags are stored in the 4 high order bits of
115 NOTE: This limits the playlist file size to a max of 256M.
119 01 = Track was prepended into playlist
120 10 = Track was inserted into playlist
121 11 = Track was appended into playlist
126 0 = Track entry is valid
127 1 = Track does not exist on disk and should be skipped
129 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
130 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
131 #define PLAYLIST_QUEUE_MASK 0x20000000
133 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
134 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
135 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
137 #define PLAYLIST_QUEUED 0x20000000
138 #define PLAYLIST_SKIPPED 0x10000000
140 struct directory_search_context
{
141 struct playlist_info
* playlist
;
147 static struct playlist_info current_playlist
;
148 static char now_playing
[MAX_PATH
+1];
150 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
151 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
153 static void create_control(struct playlist_info
* playlist
);
154 static int check_control(struct playlist_info
* playlist
);
155 static int recreate_control(struct playlist_info
* playlist
);
156 static void update_playlist_filename(struct playlist_info
* playlist
,
157 const char *dir
, const char *file
);
158 static int add_indices_to_playlist(struct playlist_info
* playlist
,
159 char* buffer
, size_t buflen
);
160 static int add_track_to_playlist(struct playlist_info
* playlist
,
161 const char *filename
, int position
,
162 bool queue
, int seek_pos
);
163 static int directory_search_callback(char* filename
, void* context
);
164 static int remove_track_from_playlist(struct playlist_info
* playlist
,
165 int position
, bool write
);
166 static int randomise_playlist(struct playlist_info
* playlist
,
167 unsigned int seed
, bool start_current
,
169 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
171 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
173 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
175 static int compare(const void* p1
, const void* p2
);
176 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
177 bool control_file
, char *buf
, int buf_length
);
178 static int get_next_directory(char *dir
);
179 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
180 static int get_previous_directory(char *dir
);
181 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
);
182 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
184 static void display_playlist_count(int count
, const unsigned char *fmt
,
186 static void display_buffer_full(void);
187 static int flush_cached_control(struct playlist_info
* playlist
);
188 static int update_control(struct playlist_info
* playlist
,
189 enum playlist_command command
, int i1
, int i2
,
190 const char* s1
, const char* s2
, void* data
);
191 static void sync_control(struct playlist_info
* playlist
, bool force
);
192 static int rotate_index(const struct playlist_info
* playlist
, int index
);
195 #define PLAYLIST_LOAD_POINTERS 1
197 static struct event_queue playlist_queue
;
198 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x800)/sizeof(long)];
199 static const char playlist_thread_name
[] = "playlist cachectrl";
202 /* Check if the filename suggests M3U or M3U8 format. */
203 static bool is_m3u8(const char* filename
)
205 int len
= strlen(filename
);
207 /* Default to M3U8 unless explicitly told otherwise. */
208 return !(len
> 4 && strcasecmp(&filename
[len
- 4], ".m3u") == 0);
212 /* Convert a filename in an M3U playlist to UTF-8.
214 * buf - the filename to convert; can contain more than one line from the
216 * buf_len - amount of buf that is used.
217 * buf_max - total size of buf.
218 * temp - temporary conversion buffer, at least buf_max bytes.
220 * Returns the length of the converted filename.
222 static int convert_m3u(char* buf
, int buf_len
, int buf_max
, char* temp
)
228 while ((buf
[i
] != '\n') && (buf
[i
] != '\r') && (i
< buf_len
))
233 /* Work back killing white space. */
234 while ((i
> 0) && isspace(buf
[i
- 1]))
242 /* Convert char by char, so as to not overflow temp (iso_decode should
243 * preferably handle this). No more than 4 bytes should be generated for
246 for (i
= 0; i
< buf_len
&& dest
< (temp
+ buf_max
- 4); i
++)
248 dest
= iso_decode(&buf
[i
], dest
, -1, 1);
257 * remove any files and indices associated with the playlist
259 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
261 playlist
->filename
[0] = '\0';
262 playlist
->utf8
= true;
264 if(playlist
->fd
>= 0)
265 /* If there is an already open playlist, close it. */
269 if(playlist
->control_fd
>= 0)
270 close(playlist
->control_fd
);
271 playlist
->control_fd
= -1;
272 playlist
->control_created
= false;
274 playlist
->in_ram
= false;
276 if (playlist
->buffer
)
277 playlist
->buffer
[0] = 0;
279 playlist
->buffer_end_pos
= 0;
282 playlist
->first_index
= 0;
283 playlist
->amount
= 0;
284 playlist
->last_insert_pos
= -1;
286 playlist
->shuffle_modified
= false;
287 playlist
->deleted
= false;
288 playlist
->num_inserted_tracks
= 0;
289 playlist
->started
= false;
291 playlist
->num_cached
= 0;
292 playlist
->pending_control_sync
= false;
294 if (!resume
&& playlist
->current
)
296 /* start with fresh playlist control file when starting new
298 create_control(playlist
);
303 * Initialize a new playlist for viewing/editing/playing. dir is the
304 * directory where the playlist is located and file is the filename.
306 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
309 const char *fileused
= file
;
310 const char *dirused
= dir
;
311 empty_playlist(playlist
, false);
317 if (dirused
&& playlist
->current
) /* !current cannot be in_ram */
318 playlist
->in_ram
= true;
320 dirused
= ""; /* empty playlist */
323 update_playlist_filename(playlist
, dirused
, fileused
);
325 if (playlist
->control_fd
>= 0)
327 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
328 PLAYLIST_CONTROL_FILE_VERSION
, -1, dirused
, fileused
, NULL
);
329 sync_control(playlist
, false);
334 * create control file for playlist
336 static void create_control(struct playlist_info
* playlist
)
338 playlist
->control_fd
= open(playlist
->control_filename
,
339 O_CREAT
|O_RDWR
|O_TRUNC
);
340 if (playlist
->control_fd
< 0)
342 if (check_rockboxdir())
344 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
345 splashf(HZ
*2, (unsigned char *)"%s (%d)",
346 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
347 playlist
->control_fd
);
349 playlist
->control_created
= false;
353 playlist
->control_created
= true;
358 * validate the control file. This may include creating/initializing it if
361 static int check_control(struct playlist_info
* playlist
)
363 if (!playlist
->control_created
)
365 create_control(playlist
);
367 if (playlist
->control_fd
>= 0)
369 char* dir
= playlist
->filename
;
370 char* file
= playlist
->filename
+playlist
->dirlen
;
371 char c
= playlist
->filename
[playlist
->dirlen
-1];
373 playlist
->filename
[playlist
->dirlen
-1] = '\0';
375 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
376 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
377 sync_control(playlist
, false);
378 playlist
->filename
[playlist
->dirlen
-1] = c
;
382 if (playlist
->control_fd
< 0)
389 * recreate the control file based on current playlist entries
391 static int recreate_control(struct playlist_info
* playlist
)
393 char temp_file
[MAX_PATH
+1];
398 if(playlist
->control_fd
>= 0)
400 char* dir
= playlist
->filename
;
401 char* file
= playlist
->filename
+playlist
->dirlen
;
402 char c
= playlist
->filename
[playlist
->dirlen
-1];
404 close(playlist
->control_fd
);
406 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
407 playlist
->control_filename
);
409 if (rename(playlist
->control_filename
, temp_file
) < 0)
412 temp_fd
= open(temp_file
, O_RDONLY
);
416 playlist
->control_fd
= open(playlist
->control_filename
,
417 O_CREAT
|O_RDWR
|O_TRUNC
);
418 if (playlist
->control_fd
< 0)
421 playlist
->filename
[playlist
->dirlen
-1] = '\0';
423 /* cannot call update_control() because of mutex */
424 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
425 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
427 playlist
->filename
[playlist
->dirlen
-1] = c
;
437 playlist
->shuffle_modified
= false;
438 playlist
->deleted
= false;
439 playlist
->num_inserted_tracks
= 0;
441 for (i
=0; i
<playlist
->amount
; i
++)
443 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
445 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
446 char inserted_file
[MAX_PATH
+1];
448 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
450 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
452 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
453 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
456 /* save the position in file where name is written */
457 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
459 result
= fdprintf(playlist
->control_fd
, "%s\n",
462 playlist
->indices
[i
] =
463 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
469 playlist
->num_inserted_tracks
++;
475 fsync(playlist
->control_fd
);
484 * store directory and name of playlist file
486 static void update_playlist_filename(struct playlist_info
* playlist
,
487 const char *dir
, const char *file
)
490 int dirlen
= strlen(dir
);
492 playlist
->utf8
= is_m3u8(file
);
494 /* If the dir does not end in trailing slash, we use a separator.
495 Otherwise we don't. */
496 if('/' != dir
[dirlen
-1])
502 playlist
->dirlen
= dirlen
;
504 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
505 "%s%s%s", dir
, sep
, file
);
509 * calculate track offsets within a playlist file
511 static int add_indices_to_playlist(struct playlist_info
* playlist
,
512 char* buffer
, size_t buflen
)
516 unsigned int count
= 0;
521 if(-1 == playlist
->fd
)
522 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
524 return -1; /* failure */
525 if((i
= lseek(playlist
->fd
, 0, SEEK_CUR
)) > 0)
526 playlist
->utf8
= true; /* Override any earlier indication. */
528 splash(0, ID2P(LANG_WAIT
));
532 /* use mp3 buffer for maximum load speed */
534 #if CONFIG_CODEC != SWCODEC
535 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
536 buflen
= (audiobufend
- audiobuf
);
537 buffer
= (char *)audiobuf
;
539 buffer
= (char *)audio_get_buffer(false, &buflen
);
547 nread
= read(playlist
->fd
, buffer
, buflen
);
548 /* Terminate on EOF */
552 p
= (unsigned char *)buffer
;
554 for(count
=0; count
< nread
; count
++,p
++) {
556 /* Are we on a new line? */
557 if((*p
== '\n') || (*p
== '\r'))
567 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
568 display_buffer_full();
573 /* Store a new entry */
574 playlist
->indices
[ playlist
->amount
] = i
+count
;
576 if (playlist
->filenames
)
577 playlist
->filenames
[ playlist
->amount
] = NULL
;
589 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
596 * Utility function to create a new playlist, fill it with the next or
597 * previous directory, shuffle it if needed, and start playback.
598 * If play_last is true and direction zero or negative, start playing
599 * the last file in the directory, otherwise start playing the first.
601 static int create_and_play_dir(int direction
, bool play_last
)
603 char dir
[MAX_PATH
+ 1];
608 res
= get_next_directory(dir
);
610 res
= get_previous_directory(dir
);
614 if (playlist_create(dir
, NULL
) != -1)
616 ft_build_playlist(tree_get_context(), 0);
618 if (global_settings
.playlist_shuffle
)
619 playlist_shuffle(current_tick
, -1);
621 if (play_last
&& direction
<= 0)
622 index
= current_playlist
.amount
- 1;
626 #if (CONFIG_CODEC != SWCODEC)
627 playlist_start(index
, 0);
631 /* we've overwritten the dircache when getting the next/previous dir,
632 so the tree browser context will need to be reloaded */
640 * Removes all tracks, from the playlist, leaving the presently playing
643 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
647 if (playlist
== NULL
)
648 playlist
= ¤t_playlist
;
650 while (playlist
->index
> 0)
651 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
654 while (playlist
->amount
> 1)
655 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
658 if (playlist
->amount
== 1) {
659 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
667 * Add track to playlist at specified position. There are seven special
668 * positions that can be specified:
669 * PLAYLIST_PREPEND - Add track at beginning of playlist
670 * PLAYLIST_INSERT - Add track after current song. NOTE: If
671 * there are already inserted tracks then track
672 * is added to the end of the insertion list
673 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
674 * matter what other tracks have been inserted
675 * PLAYLIST_INSERT_LAST - Add track to end of playlist
676 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
677 * current playing track and end of playlist
678 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
680 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
681 * and inster this track at the end.
683 static int add_track_to_playlist(struct playlist_info
* playlist
,
684 const char *filename
, int position
,
685 bool queue
, int seek_pos
)
687 int insert_position
, orig_position
;
688 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
691 insert_position
= orig_position
= position
;
693 if (playlist
->amount
>= playlist
->max_playlist_size
)
695 display_buffer_full();
701 case PLAYLIST_PREPEND
:
702 position
= insert_position
= playlist
->first_index
;
704 case PLAYLIST_INSERT
:
705 /* if there are already inserted tracks then add track to end of
706 insertion list else add after current playing track */
707 if (playlist
->last_insert_pos
>= 0 &&
708 playlist
->last_insert_pos
< playlist
->amount
&&
709 (playlist
->indices
[playlist
->last_insert_pos
]&
710 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
711 position
= insert_position
= playlist
->last_insert_pos
+1;
712 else if (playlist
->amount
> 0)
713 position
= insert_position
= playlist
->index
+ 1;
715 position
= insert_position
= 0;
717 playlist
->last_insert_pos
= position
;
719 case PLAYLIST_INSERT_FIRST
:
720 if (playlist
->amount
> 0)
721 position
= insert_position
= playlist
->index
+ 1;
723 position
= insert_position
= 0;
725 playlist
->last_insert_pos
= position
;
727 case PLAYLIST_INSERT_LAST
:
728 if (playlist
->first_index
> 0)
729 position
= insert_position
= playlist
->first_index
;
731 position
= insert_position
= playlist
->amount
;
733 playlist
->last_insert_pos
= position
;
735 case PLAYLIST_INSERT_SHUFFLED
:
737 if (playlist
->started
)
740 int n
= playlist
->amount
-
741 rotate_index(playlist
, playlist
->index
);
748 position
= playlist
->index
+ offset
+ 1;
749 if (position
>= playlist
->amount
)
750 position
-= playlist
->amount
;
752 insert_position
= position
;
755 position
= insert_position
= (rand() % (playlist
->amount
+1));
758 case PLAYLIST_INSERT_LAST_SHUFFLED
:
760 position
= insert_position
= playlist
->last_shuffled_start
+
761 rand() % (playlist
->amount
- playlist
->last_shuffled_start
+ 1);
764 case PLAYLIST_REPLACE
:
765 if (playlist_remove_all_tracks(playlist
) < 0)
768 playlist
->last_insert_pos
= position
= insert_position
= playlist
->index
+ 1;
773 flags
|= PLAYLIST_QUEUED
;
775 /* shift indices so that track can be added */
776 for (i
=playlist
->amount
; i
>insert_position
; i
--)
778 playlist
->indices
[i
] = playlist
->indices
[i
-1];
780 if (playlist
->filenames
)
781 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
785 /* update stored indices if needed */
786 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
790 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
791 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
793 playlist
->first_index
++;
797 if (insert_position
< playlist
->last_insert_pos
||
798 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
799 playlist
->last_insert_pos
++;
801 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
803 int result
= update_control(playlist
,
804 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
805 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
811 playlist
->indices
[insert_position
] = flags
| seek_pos
;
814 if (playlist
->filenames
)
815 playlist
->filenames
[insert_position
] = NULL
;
819 playlist
->num_inserted_tracks
++;
821 return insert_position
;
825 * Callback for playlist_directory_tracksearch to insert track into
828 static int directory_search_callback(char* filename
, void* context
)
830 struct directory_search_context
* c
=
831 (struct directory_search_context
*) context
;
834 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
842 /* Make sure tracks are inserted in correct order if user requests
844 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
845 c
->position
= insert_pos
+ 1;
847 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
849 unsigned char* count_str
;
852 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
854 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
856 display_playlist_count(c
->count
, count_str
, false);
858 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
859 (audio_status() & AUDIO_STATUS_PLAY
) &&
860 c
->playlist
->started
)
861 audio_flush_and_reload_tracks();
868 * remove track at specified position
870 static int remove_track_from_playlist(struct playlist_info
* playlist
,
871 int position
, bool write
)
876 if (playlist
->amount
<= 0)
879 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
881 /* shift indices now that track has been removed */
882 for (i
=position
; i
<playlist
->amount
; i
++)
884 playlist
->indices
[i
] = playlist
->indices
[i
+1];
886 if (playlist
->filenames
)
887 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
894 playlist
->num_inserted_tracks
--;
896 playlist
->deleted
= true;
898 /* update stored indices if needed */
899 if (position
< playlist
->index
)
902 if (position
< playlist
->first_index
)
904 playlist
->first_index
--;
907 if (position
<= playlist
->last_insert_pos
)
908 playlist
->last_insert_pos
--;
910 if (write
&& playlist
->control_fd
>= 0)
912 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
913 position
, -1, NULL
, NULL
, NULL
);
918 sync_control(playlist
, false);
925 * randomly rearrange the array of indices for the playlist. If start_current
926 * is true then update the index to the new index of the current playing track
928 static int randomise_playlist(struct playlist_info
* playlist
,
929 unsigned int seed
, bool start_current
,
935 unsigned int current
= playlist
->indices
[playlist
->index
];
937 /* seed 0 is used to identify sorted playlist for resume purposes */
941 /* seed with the given seed */
944 /* randomise entire indices list */
945 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
947 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
948 candidate
= rand() % (count
+ 1);
950 /* now swap the values at the 'count' and 'candidate' positions */
951 store
= playlist
->indices
[candidate
];
952 playlist
->indices
[candidate
] = playlist
->indices
[count
];
953 playlist
->indices
[count
] = store
;
955 if (playlist
->filenames
)
957 store
= (long)playlist
->filenames
[candidate
];
958 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
959 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
965 find_and_set_playlist_index(playlist
, current
);
967 /* indices have been moved so last insert position is no longer valid */
968 playlist
->last_insert_pos
= -1;
970 playlist
->seed
= seed
;
971 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
972 playlist
->shuffle_modified
= true;
976 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
977 playlist
->first_index
, NULL
, NULL
, NULL
);
984 * Sort the array of indices for the playlist. If start_current is true then
985 * set the index to the new index of the current song.
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
, 0, 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 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1015 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1021 /* Calculate how many steps we have to really step when skipping entries
1024 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1026 int i
, count
, direction
;
1028 int stepped_count
= 0;
1041 index
= playlist
->index
;
1044 /* Boundary check */
1046 index
+= playlist
->amount
;
1047 if (index
>= playlist
->amount
)
1048 index
-= playlist
->amount
;
1050 /* Check if we found a bad entry. */
1051 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1054 /* Are all entries bad? */
1055 if (stepped_count
++ > playlist
->amount
)
1062 } while (i
<= count
);
1067 /* Marks the index of the track to be skipped that is "steps" away from
1068 * current playing track.
1070 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1074 if (playlist
== NULL
)
1075 playlist
= ¤t_playlist
;
1077 /* need to account for already skipped tracks */
1078 steps
= calculate_step_count(playlist
, steps
);
1080 index
= playlist
->index
+ steps
;
1082 index
+= playlist
->amount
;
1083 else if (index
>= playlist
->amount
)
1084 index
-= playlist
->amount
;
1086 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1090 * returns the index of the track that is "steps" away from current playing
1093 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1096 int current_index
= playlist
->index
;
1097 int next_index
= -1;
1099 if (playlist
->amount
<= 0)
1102 if (repeat_mode
== -1)
1103 repeat_mode
= global_settings
.repeat_mode
;
1105 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1106 repeat_mode
= REPEAT_ALL
;
1108 steps
= calculate_step_count(playlist
, steps
);
1109 switch (repeat_mode
)
1111 case REPEAT_SHUFFLE
:
1112 /* Treat repeat shuffle just like repeat off. At end of playlist,
1113 play will be resumed in playlist_next() */
1116 current_index
= rotate_index(playlist
, current_index
);
1117 next_index
= current_index
+steps
;
1118 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1121 next_index
= (next_index
+playlist
->first_index
) %
1128 #ifdef AB_REPEAT_ENABLE
1131 next_index
= current_index
;
1137 next_index
= (current_index
+steps
) % playlist
->amount
;
1138 while (next_index
< 0)
1139 next_index
+= playlist
->amount
;
1141 if (steps
>= playlist
->amount
)
1148 /* second time around so skip the queued files */
1149 for (i
=0; i
<playlist
->amount
; i
++)
1151 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1152 index
= (index
+1) % playlist
->amount
;
1164 /* No luck if the whole playlist was bad. */
1165 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1172 * Search for the seek track and set appropriate indices. Used after shuffle
1173 * to make sure the current index is still pointing to correct track.
1175 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1180 /* Set the index to the current song */
1181 for (i
=0; i
<playlist
->amount
; i
++)
1183 if (playlist
->indices
[i
] == seek
)
1185 playlist
->index
= playlist
->first_index
= i
;
1193 * used to sort track indices. Sort order is as follows:
1194 * 1. Prepended tracks (in prepend order)
1195 * 2. Playlist/directory tracks (in playlist order)
1196 * 3. Inserted/Appended tracks (in insert order)
1198 static int compare(const void* p1
, const void* p2
)
1200 unsigned long* e1
= (unsigned long*) p1
;
1201 unsigned long* e2
= (unsigned long*) p2
;
1202 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1203 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1205 if (flags1
== flags2
)
1206 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1207 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1208 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1210 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1211 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1213 else if (flags1
&& flags2
)
1214 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1219 #ifdef HAVE_DIRCACHE
1221 * Thread to update filename pointers to dircache on background
1222 * without affecting playlist load up performance. This thread also flushes
1223 * any pending control commands when the disk spins up.
1225 static void playlist_flush_callback(void *param
)
1228 struct playlist_info
*playlist
;
1229 playlist
= ¤t_playlist
;
1230 if (playlist
->control_fd
>= 0)
1232 if (playlist
->num_cached
> 0)
1234 mutex_lock(&playlist
->control_mutex
);
1235 flush_cached_control(playlist
);
1236 mutex_unlock(&playlist
->control_mutex
);
1238 sync_control(playlist
, true);
1242 static void playlist_thread(void)
1244 struct queue_event ev
;
1245 bool dirty_pointers
= false;
1246 static char tmp
[MAX_PATH
+1];
1248 struct playlist_info
*playlist
;
1255 #ifdef HAVE_DISK_STORAGE
1256 if (global_settings
.disk_spindown
> 1 &&
1257 global_settings
.disk_spindown
<= 5)
1258 sleep_time
= global_settings
.disk_spindown
- 1;
1263 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1267 case PLAYLIST_LOAD_POINTERS
:
1268 dirty_pointers
= true;
1271 /* Start the background scanning after either the disk spindown
1272 timeout or 5s, whichever is less */
1274 playlist
= ¤t_playlist
;
1275 if (playlist
->control_fd
>= 0)
1277 if (playlist
->num_cached
> 0)
1278 register_storage_idle_func(playlist_flush_callback
);
1281 if (!dirty_pointers
)
1284 if (!dircache_is_enabled() || !playlist
->filenames
1285 || playlist
->amount
<= 0)
1288 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1291 for (index
= 0; index
< playlist
->amount
1292 && queue_empty(&playlist_queue
); index
++)
1294 /* Process only pointers that are not already loaded. */
1295 if (playlist
->filenames
[index
])
1298 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1299 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1301 /* Load the filename from playlist file. */
1302 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1306 /* Set the dircache entry pointer. */
1307 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1309 /* And be on background so user doesn't notice any delays. */
1313 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1316 dirty_pointers
= false;
1320 case SYS_USB_CONNECTED
:
1321 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1322 usb_wait_for_disconnect(&playlist_queue
);
1331 * gets pathname for track at seek index
1333 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1334 bool control_file
, char *buf
, int buf_length
)
1338 char tmp_buf
[MAX_PATH
+1];
1339 char dir_buf
[MAX_PATH
+1];
1340 bool utf8
= playlist
->utf8
;
1342 if (buf_length
> MAX_PATH
+1)
1343 buf_length
= MAX_PATH
+1;
1345 #ifdef HAVE_DIRCACHE
1346 if (dircache_is_enabled() && playlist
->filenames
)
1348 if (playlist
->filenames
[index
] != NULL
)
1350 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1351 max
= strlen(tmp_buf
);
1358 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1360 max
= strlcpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1364 mutex_lock(&playlist
->control_mutex
);
1368 fd
= playlist
->control_fd
;
1373 if(-1 == playlist
->fd
)
1374 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1382 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1386 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1388 if ((max
> 0) && !utf8
)
1390 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1391 * be as large as tmp_buf.
1393 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1398 mutex_unlock(&playlist
->control_mutex
);
1403 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1405 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1411 strlcpy(dir_buf
, playlist
->filename
, playlist
->dirlen
);
1413 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1416 static int get_next_directory(char *dir
){
1417 return get_next_dir(dir
,true,false);
1420 static int get_previous_directory(char *dir
){
1421 return get_next_dir(dir
,false,false);
1425 * search through all the directories (starting with the current) to find
1426 * one that has tracks to play
1428 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1430 struct playlist_info
* playlist
= ¤t_playlist
;
1432 char *start_dir
= NULL
;
1435 struct tree_context
* tc
= tree_get_context();
1436 int saved_dirfilter
= *(tc
->dirfilter
);
1438 /* process random folder advance */
1439 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1441 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat", O_RDONLY
);
1444 char buffer
[MAX_PATH
];
1445 int folder_count
= 0;
1446 srand(current_tick
);
1447 *(tc
->dirfilter
) = SHOW_MUSIC
;
1448 tc
->sort_dir
= global_settings
.sort_dir
;
1449 read(fd
,&folder_count
,sizeof(int));
1454 i
= rand()%folder_count
;
1455 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1456 read(fd
,buffer
,MAX_PATH
);
1457 if (check_subdir_for_music(buffer
, "", false) ==0)
1463 *(tc
->dirfilter
) = saved_dirfilter
;
1464 tc
->sort_dir
= global_settings
.sort_dir
;
1470 /* not random folder advance (or random folder advance unavailable) */
1473 /* start with root */
1478 /* start with current directory */
1479 strlcpy(dir
, playlist
->filename
, playlist
->dirlen
);
1482 /* use the tree browser dircache to load files */
1483 *(tc
->dirfilter
) = SHOW_ALL
;
1485 /* set up sorting/direction */
1486 tc
->sort_dir
= global_settings
.sort_dir
;
1489 static const char sortpairs
[] =
1491 [SORT_ALPHA
] = SORT_ALPHA_REVERSED
,
1492 [SORT_DATE
] = SORT_DATE_REVERSED
,
1493 [SORT_TYPE
] = SORT_TYPE_REVERSED
,
1494 [SORT_ALPHA_REVERSED
] = SORT_ALPHA
,
1495 [SORT_DATE_REVERSED
] = SORT_DATE
,
1496 [SORT_TYPE_REVERSED
] = SORT_TYPE
,
1499 if ((unsigned)tc
->sort_dir
< sizeof(sortpairs
))
1500 tc
->sort_dir
= sortpairs
[tc
->sort_dir
];
1505 struct entry
*files
;
1509 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1516 files
= (struct entry
*) tc
->dircache
;
1517 num_files
= tc
->filesindir
;
1519 for (i
=0; i
<num_files
; i
++)
1522 if (action_userabort(TIMEOUT_NOBLOCK
))
1529 if (files
[i
].attr
& ATTR_DIRECTORY
)
1533 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1540 else if (!strcmp(start_dir
, files
[i
].name
))
1547 /* move down to parent directory. current directory name is
1548 stored as the starting point for the search in parent */
1549 start_dir
= strrchr(dir
, '/');
1560 /* restore dirfilter */
1561 *(tc
->dirfilter
) = saved_dirfilter
;
1562 tc
->sort_dir
= global_settings
.sort_dir
;
1564 /* special case if nothing found: try start searching again from root */
1565 if (result
== -1 && !recursion
){
1566 result
= get_next_dir(dir
, is_forward
, true);
1573 * Checks if there are any music files in the dir or any of its
1574 * subdirectories. May be called recursively.
1576 static int check_subdir_for_music(char *dir
, const char *subdir
, bool recurse
)
1579 int dirlen
= strlen(dir
);
1582 struct entry
*files
;
1583 bool has_music
= false;
1584 bool has_subdir
= false;
1585 struct tree_context
* tc
= tree_get_context();
1587 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1589 if (ft_load(tc
, dir
) < 0)
1594 files
= (struct entry
*) tc
->dircache
;
1595 num_files
= tc
->filesindir
;
1597 for (i
=0; i
<num_files
; i
++)
1599 if (files
[i
].attr
& ATTR_DIRECTORY
)
1601 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1611 if (has_subdir
&& recurse
)
1613 for (i
=0; i
<num_files
; i
++)
1615 if (action_userabort(TIMEOUT_NOBLOCK
))
1621 if (files
[i
].attr
& ATTR_DIRECTORY
)
1623 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1641 /* we now need to reload our current directory */
1642 if(ft_load(tc
, dir
) < 0)
1643 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1649 * Returns absolute path of track
1651 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1658 /* Zero-terminate the file name */
1659 while((src
[i
] != '\n') &&
1664 /* Now work back killing white space */
1665 while((src
[i
-1] == ' ') ||
1671 /* replace backslashes with forward slashes */
1672 for ( j
=0; j
<i
; j
++ )
1673 if ( src
[j
] == '\\' )
1678 strlcpy(dest
, src
, buf_length
);
1682 /* handle dos style drive letter */
1684 strlcpy(dest
, &src
[2], buf_length
);
1685 else if (!strncmp(src
, "../", 3))
1687 /* handle relative paths */
1689 while(!strncmp(&src
[i
], "../", 3))
1691 for (j
=0; j
<i
/3; j
++) {
1692 temp_ptr
= strrchr(dir
, '/');
1698 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1700 else if ( '.' == src
[0] && '/' == src
[1] ) {
1701 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1704 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1712 * Display splash message showing progress of playlist/directory insertion or
1715 static void display_playlist_count(int count
, const unsigned char *fmt
,
1718 static long talked_tick
= 0;
1719 long id
= P2ID(fmt
);
1720 if(global_settings
.talk_menu
&& id
>=0)
1722 if(final
|| (count
&& (talked_tick
== 0
1723 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1725 talked_tick
= current_tick
;
1726 talk_number(count
, false);
1732 splashf(0, fmt
, count
, str(LANG_OFF_ABORT
));
1736 * Display buffer full message
1738 static void display_buffer_full(void)
1740 splash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1744 * Flush any cached control commands to disk. Called when playlist is being
1745 * modified. Returns 0 on success and -1 on failure.
1747 static int flush_cached_control(struct playlist_info
* playlist
)
1752 if (!playlist
->num_cached
)
1755 lseek(playlist
->control_fd
, 0, SEEK_END
);
1757 for (i
=0; i
<playlist
->num_cached
; i
++)
1759 struct playlist_control_cache
* cache
=
1760 &(playlist
->control_cache
[i
]);
1762 switch (cache
->command
)
1764 case PLAYLIST_COMMAND_PLAYLIST
:
1765 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1766 cache
->i1
, cache
->s1
, cache
->s2
);
1768 case PLAYLIST_COMMAND_ADD
:
1769 case PLAYLIST_COMMAND_QUEUE
:
1770 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1771 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1772 cache
->i1
, cache
->i2
);
1775 /* save the position in file where name is written */
1776 int* seek_pos
= (int *)cache
->data
;
1777 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1778 result
= fdprintf(playlist
->control_fd
, "%s\n",
1782 case PLAYLIST_COMMAND_DELETE
:
1783 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1785 case PLAYLIST_COMMAND_SHUFFLE
:
1786 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1787 cache
->i1
, cache
->i2
);
1789 case PLAYLIST_COMMAND_UNSHUFFLE
:
1790 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1792 case PLAYLIST_COMMAND_RESET
:
1793 result
= fdprintf(playlist
->control_fd
, "R\n");
1805 playlist
->num_cached
= 0;
1806 playlist
->pending_control_sync
= true;
1813 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1820 * Update control data with new command. Depending on the command, it may be
1821 * cached or flushed to disk.
1823 static int update_control(struct playlist_info
* playlist
,
1824 enum playlist_command command
, int i1
, int i2
,
1825 const char* s1
, const char* s2
, void* data
)
1828 struct playlist_control_cache
* cache
;
1831 mutex_lock(&playlist
->control_mutex
);
1833 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1835 cache
->command
= command
;
1844 case PLAYLIST_COMMAND_PLAYLIST
:
1845 case PLAYLIST_COMMAND_ADD
:
1846 case PLAYLIST_COMMAND_QUEUE
:
1847 #ifndef HAVE_DIRCACHE
1848 case PLAYLIST_COMMAND_DELETE
:
1849 case PLAYLIST_COMMAND_RESET
:
1853 case PLAYLIST_COMMAND_SHUFFLE
:
1854 case PLAYLIST_COMMAND_UNSHUFFLE
:
1856 /* only flush when needed */
1860 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1861 result
= flush_cached_control(playlist
);
1863 mutex_unlock(&playlist
->control_mutex
);
1869 * sync control file to disk
1871 static void sync_control(struct playlist_info
* playlist
, bool force
)
1873 #ifdef HAVE_DIRCACHE
1874 if (playlist
->started
&& force
)
1878 if (playlist
->started
)
1881 if (playlist
->pending_control_sync
)
1883 mutex_lock(&playlist
->control_mutex
);
1884 fsync(playlist
->control_fd
);
1885 playlist
->pending_control_sync
= false;
1886 mutex_unlock(&playlist
->control_mutex
);
1892 * Rotate indices such that first_index is index 0
1894 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1896 index
-= playlist
->first_index
;
1898 index
+= playlist
->amount
;
1904 * Initialize playlist entries at startup
1906 void playlist_init(void)
1908 struct playlist_info
* playlist
= ¤t_playlist
;
1910 playlist
->current
= true;
1911 strlcpy(playlist
->control_filename
, PLAYLIST_CONTROL_FILE
,
1912 sizeof(playlist
->control_filename
));
1914 playlist
->control_fd
= -1;
1915 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1916 playlist
->indices
= buffer_alloc(
1917 playlist
->max_playlist_size
* sizeof(int));
1918 playlist
->buffer_size
=
1919 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1920 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1921 mutex_init(&playlist
->control_mutex
);
1922 empty_playlist(playlist
, true);
1924 #ifdef HAVE_DIRCACHE
1925 playlist
->filenames
= buffer_alloc(
1926 playlist
->max_playlist_size
* sizeof(int));
1927 memset(playlist
->filenames
, 0,
1928 playlist
->max_playlist_size
* sizeof(int));
1929 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1930 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1932 queue_init(&playlist_queue
, true);
1937 * Clean playlist at shutdown
1939 void playlist_shutdown(void)
1941 struct playlist_info
* playlist
= ¤t_playlist
;
1943 if (playlist
->control_fd
>= 0)
1945 mutex_lock(&playlist
->control_mutex
);
1947 if (playlist
->num_cached
> 0)
1948 flush_cached_control(playlist
);
1950 close(playlist
->control_fd
);
1952 mutex_unlock(&playlist
->control_mutex
);
1957 * Create new playlist
1959 int playlist_create(const char *dir
, const char *file
)
1961 struct playlist_info
* playlist
= ¤t_playlist
;
1963 new_playlist(playlist
, dir
, file
);
1966 /* load the playlist file */
1967 add_indices_to_playlist(playlist
, NULL
, 0);
1972 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1975 * Restore the playlist state based on control file commands. Called to
1976 * resume playback after shutdown.
1978 int playlist_resume(void)
1980 struct playlist_info
* playlist
= ¤t_playlist
;
1985 int control_file_size
= 0;
1989 /* use mp3 buffer for maximum load speed */
1990 #if CONFIG_CODEC != SWCODEC
1991 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1992 buflen
= (audiobufend
- audiobuf
);
1993 buffer
= (char *)audiobuf
;
1995 buffer
= (char *)audio_get_buffer(false, &buflen
);
1998 empty_playlist(playlist
, true);
2000 splash(0, ID2P(LANG_WAIT
));
2001 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2002 if (playlist
->control_fd
< 0)
2004 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2007 playlist
->control_created
= true;
2009 control_file_size
= filesize(playlist
->control_fd
);
2010 if (control_file_size
<= 0)
2012 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2016 /* read a small amount first to get the header */
2017 nread
= read(playlist
->control_fd
, buffer
,
2018 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2021 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2025 playlist
->started
= true;
2031 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2032 int last_newline
= 0;
2034 bool newline
= true;
2035 bool exit_loop
= false;
2040 unsigned long last_tick
= current_tick
;
2041 bool useraborted
= false;
2043 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2045 /* So a splash while we are loading. */
2046 if (TIME_AFTER(current_tick
, last_tick
+ HZ
/4))
2048 splashf(0, str(LANG_LOADING_PERCENT
),
2049 (total_read
+count
)*100/control_file_size
,
2050 str(LANG_OFF_ABORT
));
2051 if (action_userabort(TIMEOUT_NOBLOCK
))
2056 last_tick
= current_tick
;
2059 /* Are we on a new line? */
2060 if((*p
== '\n') || (*p
== '\r'))
2064 /* save last_newline in case we need to load more data */
2065 last_newline
= count
;
2067 switch (current_command
)
2069 case PLAYLIST_COMMAND_PLAYLIST
:
2071 /* str1=version str2=dir str3=file */
2087 version
= atoi(str1
);
2089 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2092 update_playlist_filename(playlist
, str2
, str3
);
2094 if (str3
[0] != '\0')
2096 /* NOTE: add_indices_to_playlist() overwrites the
2097 audiobuf so we need to reload control file
2099 add_indices_to_playlist(playlist
, NULL
, 0);
2101 else if (str2
[0] != '\0')
2103 playlist
->in_ram
= true;
2104 resume_directory(str2
);
2107 /* load the rest of the data */
2113 case PLAYLIST_COMMAND_ADD
:
2114 case PLAYLIST_COMMAND_QUEUE
:
2116 /* str1=position str2=last_position str3=file */
2117 int position
, last_position
;
2120 if (!str1
|| !str2
|| !str3
)
2127 position
= atoi(str1
);
2128 last_position
= atoi(str2
);
2130 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2133 /* seek position is based on str3's position in
2135 if (add_track_to_playlist(playlist
, str3
, position
,
2136 queue
, total_read
+(str3
-buffer
)) < 0)
2139 playlist
->last_insert_pos
= last_position
;
2143 case PLAYLIST_COMMAND_DELETE
:
2155 position
= atoi(str1
);
2157 if (remove_track_from_playlist(playlist
, position
,
2163 case PLAYLIST_COMMAND_SHUFFLE
:
2165 /* str1=seed str2=first_index */
2177 /* Always sort list before shuffling */
2178 sort_playlist(playlist
, false, false);
2182 playlist
->first_index
= atoi(str2
);
2184 if (randomise_playlist(playlist
, seed
, false,
2190 case PLAYLIST_COMMAND_UNSHUFFLE
:
2192 /* str1=first_index */
2200 playlist
->first_index
= atoi(str1
);
2202 if (sort_playlist(playlist
, false, false) < 0)
2208 case PLAYLIST_COMMAND_RESET
:
2210 playlist
->last_insert_pos
= -1;
2213 case PLAYLIST_COMMAND_COMMENT
:
2220 /* to ignore any extra newlines */
2221 current_command
= PLAYLIST_COMMAND_COMMENT
;
2227 /* first non-comment line must always specify playlist */
2228 if (first
&& *p
!= 'P' && *p
!= '#')
2238 /* playlist can only be specified once */
2246 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2249 current_command
= PLAYLIST_COMMAND_ADD
;
2252 current_command
= PLAYLIST_COMMAND_QUEUE
;
2255 current_command
= PLAYLIST_COMMAND_DELETE
;
2258 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2261 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2264 current_command
= PLAYLIST_COMMAND_RESET
;
2267 current_command
= PLAYLIST_COMMAND_COMMENT
;
2280 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2282 /* all control file strings are separated with a colon.
2283 Replace the colon with 0 to get proper strings that can be
2284 used by commands above */
2290 if ((count
+1) < nread
)
2304 /* allow last string to contain colons */
2315 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2321 splash(HZ
*2, ID2P(LANG_CANCEL
));
2324 if (!newline
|| (exit_loop
&& count
<nread
))
2326 if ((total_read
+ count
) >= control_file_size
)
2328 /* no newline at end of control file */
2329 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2333 /* We didn't end on a newline or we exited loop prematurely.
2334 Either way, re-read the remainder. */
2335 count
= last_newline
;
2336 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2339 total_read
+= count
;
2342 /* still looking for header */
2343 nread
= read(playlist
->control_fd
, buffer
,
2344 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2346 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2348 /* Terminate on EOF */
2355 #ifdef HAVE_DIRCACHE
2356 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2363 * Add track to in_ram playlist. Used when playing directories.
2365 int playlist_add(const char *filename
)
2367 struct playlist_info
* playlist
= ¤t_playlist
;
2368 int len
= strlen(filename
);
2370 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2371 (playlist
->amount
>= playlist
->max_playlist_size
))
2373 display_buffer_full();
2377 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2378 #ifdef HAVE_DIRCACHE
2379 playlist
->filenames
[playlist
->amount
] = NULL
;
2383 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2384 playlist
->buffer_end_pos
+= len
;
2385 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2390 /* shuffle newly created playlist using random seed. */
2391 int playlist_shuffle(int random_seed
, int start_index
)
2393 struct playlist_info
* playlist
= ¤t_playlist
;
2395 unsigned int seek_pos
= 0;
2396 bool start_current
= false;
2398 if (start_index
>= 0 && global_settings
.play_selected
)
2400 /* store the seek position before the shuffle */
2401 seek_pos
= playlist
->indices
[start_index
];
2402 playlist
->index
= playlist
->first_index
= start_index
;
2403 start_current
= true;
2406 randomise_playlist(playlist
, random_seed
, start_current
, true);
2408 return playlist
->index
;
2411 /* start playing current playlist at specified index/offset */
2412 void playlist_start(int start_index
, int offset
)
2414 struct playlist_info
* playlist
= ¤t_playlist
;
2416 /* Cancel FM radio selection as previous music. For cases where we start
2417 playback without going to the WPS, such as playlist insert.. or
2418 playlist catalog. */
2419 previous_music_is_wps();
2421 playlist
->index
= start_index
;
2423 #if CONFIG_CODEC != SWCODEC
2424 talk_buffer_steal(); /* will use the mp3 buffer */
2427 playlist
->started
= true;
2428 sync_control(playlist
, false);
2432 /* Returns false if 'steps' is out of bounds, else true */
2433 bool playlist_check(int steps
)
2435 struct playlist_info
* playlist
= ¤t_playlist
;
2437 /* always allow folder navigation */
2438 if (global_settings
.next_folder
&& playlist
->in_ram
)
2441 int index
= get_next_index(playlist
, steps
, -1);
2443 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2444 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2446 return (index
>= 0);
2449 /* get trackname of track that is "steps" away from current playing track.
2450 NULL is used to identify end of playlist */
2451 char* playlist_peek(int steps
)
2453 struct playlist_info
* playlist
= ¤t_playlist
;
2459 index
= get_next_index(playlist
, steps
, -1);
2463 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2464 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2466 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2470 temp_ptr
= now_playing
;
2472 if (!playlist
->in_ram
|| control_file
)
2474 /* remove bogus dirs from beginning of path
2475 (workaround for buggy playlist creation tools) */
2478 if (file_exists(temp_ptr
))
2481 temp_ptr
= strchr(temp_ptr
+1, '/');
2486 /* Even though this is an invalid file, we still need to pass a
2487 file name to the caller because NULL is used to indicate end
2497 * Update indices as track has changed
2499 int playlist_next(int steps
)
2501 struct playlist_info
* playlist
= ¤t_playlist
;
2505 #ifdef AB_REPEAT_ENABLE
2506 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2508 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2512 /* We need to delete all the queued songs */
2513 for (i
=0, j
=steps
; i
<j
; i
++)
2515 index
= get_next_index(playlist
, i
, -1);
2517 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2519 remove_track_from_playlist(playlist
, index
, true);
2520 steps
--; /* one less track */
2525 index
= get_next_index(playlist
, steps
, -1);
2529 /* end of playlist... or is it */
2530 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2531 playlist
->amount
> 1)
2533 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2534 playlist
->first_index
= 0;
2535 sort_playlist(playlist
, false, false);
2536 randomise_playlist(playlist
, current_tick
, false, true);
2537 #if CONFIG_CODEC != SWCODEC
2538 playlist_start(0, 0);
2540 playlist
->index
= 0;
2543 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2545 index
= create_and_play_dir(steps
, true);
2549 playlist
->index
= index
;
2556 playlist
->index
= index
;
2558 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2560 /* check to see if we've gone beyond the last inserted track */
2561 int cur
= rotate_index(playlist
, index
);
2562 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2566 /* reset last inserted track */
2567 playlist
->last_insert_pos
= -1;
2569 if (playlist
->control_fd
>= 0)
2571 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2572 -1, -1, NULL
, NULL
, NULL
);
2577 sync_control(playlist
, false);
2585 /* try playing next or previous folder */
2586 bool playlist_next_dir(int direction
)
2588 /* not to mess up real playlists */
2589 if(!current_playlist
.in_ram
)
2592 return create_and_play_dir(direction
, false) >= 0;
2595 /* Get resume info for current playing song. If return value is -1 then
2596 settings shouldn't be saved. */
2597 int playlist_get_resume_info(int *resume_index
)
2599 struct playlist_info
* playlist
= ¤t_playlist
;
2601 *resume_index
= playlist
->index
;
2606 /* Update resume info for current playing song. Returns -1 on error. */
2607 int playlist_update_resume_info(const struct mp3entry
* id3
)
2609 struct playlist_info
* playlist
= ¤t_playlist
;
2613 if (global_status
.resume_index
!= playlist
->index
||
2614 global_status
.resume_offset
!= id3
->offset
)
2616 global_status
.resume_index
= playlist
->index
;
2617 global_status
.resume_offset
= id3
->offset
;
2623 global_status
.resume_index
= -1;
2624 global_status
.resume_offset
= -1;
2631 /* Returns index of current playing track for display purposes. This value
2632 should not be used for resume purposes as it doesn't represent the actual
2633 index into the playlist */
2634 int playlist_get_display_index(void)
2636 struct playlist_info
* playlist
= ¤t_playlist
;
2638 /* first_index should always be index 0 for display purposes */
2639 int index
= rotate_index(playlist
, playlist
->index
);
2644 /* returns number of tracks in current playlist */
2645 int playlist_amount(void)
2647 return playlist_amount_ex(NULL
);
2649 /* set playlist->last_shuffle_start to playlist->amount for
2650 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2651 void playlist_set_last_shuffled_start(void)
2653 struct playlist_info
* playlist
= ¤t_playlist
;
2654 playlist
->last_shuffled_start
= playlist
->amount
;
2657 * Create a new playlist If playlist is not NULL then we're loading a
2658 * playlist off disk for viewing/editing. The index_buffer is used to store
2659 * playlist indices (required for and only used if !current playlist). The
2660 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2662 int playlist_create_ex(struct playlist_info
* playlist
,
2663 const char* dir
, const char* file
,
2664 void* index_buffer
, int index_buffer_size
,
2665 void* temp_buffer
, int temp_buffer_size
)
2668 playlist
= ¤t_playlist
;
2671 /* Initialize playlist structure */
2672 int r
= rand() % 10;
2673 playlist
->current
= false;
2675 /* Use random name for control file */
2676 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2677 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2679 playlist
->control_fd
= -1;
2683 int num_indices
= index_buffer_size
/ sizeof(int);
2685 #ifdef HAVE_DIRCACHE
2688 if (num_indices
> global_settings
.max_files_in_playlist
)
2689 num_indices
= global_settings
.max_files_in_playlist
;
2691 playlist
->max_playlist_size
= num_indices
;
2692 playlist
->indices
= index_buffer
;
2693 #ifdef HAVE_DIRCACHE
2694 playlist
->filenames
= (const struct dircache_entry
**)
2695 &playlist
->indices
[num_indices
];
2700 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2701 playlist
->indices
= current_playlist
.indices
;
2702 #ifdef HAVE_DIRCACHE
2703 playlist
->filenames
= current_playlist
.filenames
;
2707 playlist
->buffer_size
= 0;
2708 playlist
->buffer
= NULL
;
2709 mutex_init(&playlist
->control_mutex
);
2712 new_playlist(playlist
, dir
, file
);
2715 /* load the playlist file */
2716 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2722 * Set the specified playlist as the current.
2723 * NOTE: You will get undefined behaviour if something is already playing so
2724 * remember to stop before calling this. Also, this call will
2725 * effectively close your playlist, making it unusable.
2727 int playlist_set_current(struct playlist_info
* playlist
)
2729 if (!playlist
|| (check_control(playlist
) < 0))
2732 empty_playlist(¤t_playlist
, false);
2734 strlcpy(current_playlist
.filename
, playlist
->filename
,
2735 sizeof(current_playlist
.filename
));
2737 current_playlist
.utf8
= playlist
->utf8
;
2738 current_playlist
.fd
= playlist
->fd
;
2740 close(playlist
->control_fd
);
2741 close(current_playlist
.control_fd
);
2742 remove(current_playlist
.control_filename
);
2743 if (rename(playlist
->control_filename
,
2744 current_playlist
.control_filename
) < 0)
2746 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2748 if (current_playlist
.control_fd
< 0)
2750 current_playlist
.control_created
= true;
2752 current_playlist
.dirlen
= playlist
->dirlen
;
2754 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2756 memcpy(current_playlist
.indices
, playlist
->indices
,
2757 playlist
->max_playlist_size
*sizeof(int));
2758 #ifdef HAVE_DIRCACHE
2759 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2760 playlist
->max_playlist_size
*sizeof(int));
2764 current_playlist
.first_index
= playlist
->first_index
;
2765 current_playlist
.amount
= playlist
->amount
;
2766 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2767 current_playlist
.seed
= playlist
->seed
;
2768 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2769 current_playlist
.deleted
= playlist
->deleted
;
2770 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2772 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2773 sizeof(current_playlist
.control_cache
));
2774 current_playlist
.num_cached
= playlist
->num_cached
;
2775 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2779 struct playlist_info
*playlist_get_current(void)
2781 return ¤t_playlist
;
2784 * Close files and delete control file for non-current playlist.
2786 void playlist_close(struct playlist_info
* playlist
)
2791 if (playlist
->fd
>= 0)
2792 close(playlist
->fd
);
2794 if (playlist
->control_fd
>= 0)
2795 close(playlist
->control_fd
);
2797 if (playlist
->control_created
)
2798 remove(playlist
->control_filename
);
2801 void playlist_sync(struct playlist_info
* playlist
)
2804 playlist
= ¤t_playlist
;
2806 sync_control(playlist
, false);
2807 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2808 audio_flush_and_reload_tracks();
2810 #ifdef HAVE_DIRCACHE
2811 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2816 * Insert track into playlist at specified position (or one of the special
2817 * positions). Returns position where track was inserted or -1 if error.
2819 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2820 int position
, bool queue
, bool sync
)
2825 playlist
= ¤t_playlist
;
2827 if (check_control(playlist
) < 0)
2829 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2833 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2835 /* Check if we want manually sync later. For example when adding
2836 * bunch of files from tagcache, syncing after every file wouldn't be
2837 * a good thing to do. */
2838 if (sync
&& result
>= 0)
2839 playlist_sync(playlist
);
2845 * Insert all tracks from specified directory into playlist.
2847 int playlist_insert_directory(struct playlist_info
* playlist
,
2848 const char *dirname
, int position
, bool queue
,
2852 unsigned char *count_str
;
2853 struct directory_search_context context
;
2856 playlist
= ¤t_playlist
;
2858 if (check_control(playlist
) < 0)
2860 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2864 if (position
== PLAYLIST_REPLACE
)
2866 if (playlist_remove_all_tracks(playlist
) == 0)
2867 position
= PLAYLIST_INSERT_LAST
;
2873 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2875 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2877 display_playlist_count(0, count_str
, false);
2879 context
.playlist
= playlist
;
2880 context
.position
= position
;
2881 context
.queue
= queue
;
2886 result
= playlist_directory_tracksearch(dirname
, recurse
,
2887 directory_search_callback
, &context
);
2889 sync_control(playlist
, false);
2893 display_playlist_count(context
.count
, count_str
, true);
2895 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2896 audio_flush_and_reload_tracks();
2898 #ifdef HAVE_DIRCACHE
2899 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2906 * Insert all tracks from specified playlist into dynamic playlist.
2908 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2909 int position
, bool queue
)
2915 unsigned char *count_str
;
2916 char temp_buf
[MAX_PATH
+1];
2917 char trackname
[MAX_PATH
+1];
2920 bool utf8
= is_m3u8(filename
);
2923 playlist
= ¤t_playlist
;
2925 if (check_control(playlist
) < 0)
2927 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2931 fd
= open_utf8(filename
, O_RDONLY
);
2934 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2938 /* we need the directory name for formatting purposes */
2941 temp_ptr
= strrchr(filename
+1,'/');
2948 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2950 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2952 display_playlist_count(count
, count_str
, false);
2954 if (position
== PLAYLIST_REPLACE
)
2956 if (playlist_remove_all_tracks(playlist
) == 0)
2957 position
= PLAYLIST_INSERT_LAST
;
2963 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2966 if (action_userabort(TIMEOUT_NOBLOCK
))
2969 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2975 /* Use trackname as a temporay buffer. Note that trackname must
2976 * be as large as temp_buf.
2978 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
2981 /* we need to format so that relative paths are correctly
2983 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2990 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2999 /* Make sure tracks are inserted in correct order if user
3000 requests INSERT_FIRST */
3001 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3002 position
= insert_pos
+ 1;
3006 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3008 display_playlist_count(count
, count_str
, false);
3010 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3011 (audio_status() & AUDIO_STATUS_PLAY
) &&
3013 audio_flush_and_reload_tracks();
3017 /* let the other threads work */
3026 sync_control(playlist
, false);
3030 display_playlist_count(count
, count_str
, true);
3032 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3033 audio_flush_and_reload_tracks();
3035 #ifdef HAVE_DIRCACHE
3036 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3043 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3044 * we want to delete the current playing track.
3046 int playlist_delete(struct playlist_info
* playlist
, int index
)
3051 playlist
= ¤t_playlist
;
3053 if (check_control(playlist
) < 0)
3055 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3059 if (index
== PLAYLIST_DELETE_CURRENT
)
3060 index
= playlist
->index
;
3062 result
= remove_track_from_playlist(playlist
, index
, true);
3064 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3066 audio_flush_and_reload_tracks();
3072 * Move track at index to new_index. Tracks between the two are shifted
3073 * appropriately. Returns 0 on success and -1 on failure.
3075 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3081 bool current
= false;
3083 char filename
[MAX_PATH
];
3086 playlist
= ¤t_playlist
;
3088 if (check_control(playlist
) < 0)
3090 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3094 if (index
== new_index
)
3097 if (index
== playlist
->index
)
3098 /* Moving the current track */
3101 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3102 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3103 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3105 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3106 sizeof(filename
)) < 0)
3109 /* We want to insert the track at the position that was specified by
3110 new_index. This may be different then new_index because of the
3111 shifting that will occur after the delete.
3112 We calculate this before we do the remove as it depends on the
3113 size of the playlist before the track removal */
3114 r
= rotate_index(playlist
, new_index
);
3116 /* Delete track from original position */
3117 result
= remove_track_from_playlist(playlist
, index
, true);
3123 new_index
= PLAYLIST_PREPEND
;
3124 else if (r
== playlist
->amount
)
3126 new_index
= PLAYLIST_INSERT_LAST
;
3128 /* Calculate index of desired position */
3129 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3131 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3138 /* Moved the current track */
3141 case PLAYLIST_PREPEND
:
3142 playlist
->index
= playlist
->first_index
;
3144 case PLAYLIST_INSERT_LAST
:
3145 playlist
->index
= playlist
->first_index
- 1;
3146 if (playlist
->index
< 0)
3147 playlist
->index
+= playlist
->amount
;
3150 playlist
->index
= new_index
;
3155 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3156 audio_flush_and_reload_tracks();
3160 #ifdef HAVE_DIRCACHE
3161 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3167 /* shuffle currently playing playlist */
3168 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3174 playlist
= ¤t_playlist
;
3176 check_control(playlist
);
3178 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3180 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3182 audio_flush_and_reload_tracks();
3187 /* sort currently playing playlist */
3188 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3193 playlist
= ¤t_playlist
;
3195 check_control(playlist
);
3197 result
= sort_playlist(playlist
, start_current
, true);
3199 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3201 audio_flush_and_reload_tracks();
3206 /* returns true if playlist has been modified */
3207 bool playlist_modified(const struct playlist_info
* playlist
)
3210 playlist
= ¤t_playlist
;
3212 if (playlist
->shuffle_modified
||
3213 playlist
->deleted
||
3214 playlist
->num_inserted_tracks
> 0)
3220 /* returns index of first track in playlist */
3221 int playlist_get_first_index(const struct playlist_info
* playlist
)
3224 playlist
= ¤t_playlist
;
3226 return playlist
->first_index
;
3229 /* returns shuffle seed of playlist */
3230 int playlist_get_seed(const struct playlist_info
* playlist
)
3233 playlist
= ¤t_playlist
;
3235 return playlist
->seed
;
3238 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3239 int playlist_amount_ex(const struct playlist_info
* playlist
)
3242 playlist
= ¤t_playlist
;
3244 return playlist
->amount
;
3247 /* returns full path of playlist (minus extension) */
3248 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3254 playlist
= ¤t_playlist
;
3256 strlcpy(buf
, playlist
->filename
+playlist
->dirlen
, buf_size
);
3261 /* Remove extension */
3262 sep
= strrchr(buf
, '.');
3269 /* returns the playlist filename */
3270 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3274 playlist
= ¤t_playlist
;
3276 strlcpy(buf
, playlist
->filename
, buf_size
);
3284 /* Fills info structure with information about track at specified index.
3285 Returns 0 on success and -1 on failure */
3286 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3287 struct playlist_track_info
* info
)
3293 playlist
= ¤t_playlist
;
3295 if (index
< 0 || index
>= playlist
->amount
)
3298 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3299 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3301 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3302 sizeof(info
->filename
)) < 0)
3309 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3310 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3312 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3316 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3317 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3319 info
->index
= index
;
3320 info
->display_index
= rotate_index(playlist
, index
) + 1;
3325 /* save the current dynamic playlist to specified file */
3326 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3331 char path
[MAX_PATH
+1];
3332 char tmp_buf
[MAX_PATH
+1];
3334 bool overwrite_current
= false;
3335 int* index_buf
= NULL
;
3338 playlist
= ¤t_playlist
;
3340 if (playlist
->amount
<= 0)
3343 /* use current working directory as base for pathname */
3344 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3345 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3348 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3350 /* Attempting to overwrite current playlist file.*/
3352 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3354 /* not enough buffer space to store updated indices */
3355 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3359 /* in_ram buffer is unused for m3u files so we'll use for storing
3361 index_buf
= (int*)playlist
->buffer
;
3363 /* use temporary pathname */
3364 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3365 overwrite_current
= true;
3370 fd
= open_utf8(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3374 /* some applications require a BOM to read the file properly */
3375 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3379 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3383 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3387 index
= playlist
->first_index
;
3388 for (i
=0; i
<playlist
->amount
; i
++)
3395 if (action_userabort(TIMEOUT_NOBLOCK
))
3401 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3402 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3403 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3405 /* Don't save queued files */
3408 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3415 if (overwrite_current
)
3416 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3418 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3420 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3427 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3428 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3434 index
= (index
+1)%playlist
->amount
;
3437 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3441 if (overwrite_current
&& result
>= 0)
3445 mutex_lock(&playlist
->control_mutex
);
3447 /* Replace the current playlist with the new one and update indices */
3448 close(playlist
->fd
);
3449 if (remove(playlist
->filename
) >= 0)
3451 if (rename(path
, playlist
->filename
) >= 0)
3453 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3454 if (playlist
->fd
>= 0)
3456 index
= playlist
->first_index
;
3457 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3459 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3461 playlist
->indices
[index
] = index_buf
[count
];
3464 index
= (index
+1)%playlist
->amount
;
3467 /* we need to recreate control because inserted tracks are
3468 now part of the playlist and shuffle has been
3470 result
= recreate_control(playlist
);
3475 mutex_unlock(&playlist
->control_mutex
);
3485 * Search specified directory for tracks and notify via callback. May be
3486 * called recursively.
3488 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3489 int (*callback
)(char*, void*),
3492 char buf
[MAX_PATH
+1];
3496 struct entry
*files
;
3497 struct tree_context
* tc
= tree_get_context();
3498 int old_dirfilter
= *(tc
->dirfilter
);
3503 /* use the tree browser dircache to load files */
3504 *(tc
->dirfilter
) = SHOW_ALL
;
3506 if (ft_load(tc
, dirname
) < 0)
3508 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3509 *(tc
->dirfilter
) = old_dirfilter
;
3513 files
= (struct entry
*) tc
->dircache
;
3514 num_files
= tc
->filesindir
;
3516 /* we've overwritten the dircache so tree browser will need to be
3520 for (i
=0; i
<num_files
; i
++)
3523 if (action_userabort(TIMEOUT_NOBLOCK
))
3529 if (files
[i
].attr
& ATTR_DIRECTORY
)
3533 /* recursively add directories */
3534 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3535 result
= playlist_directory_tracksearch(buf
, recurse
,
3540 /* we now need to reload our current directory */
3541 if(ft_load(tc
, dirname
) < 0)
3547 files
= (struct entry
*) tc
->dircache
;
3548 num_files
= tc
->filesindir
;
3558 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3560 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3562 if (callback(buf
, context
) != 0)
3568 /* let the other threads work */
3573 /* restore dirfilter */
3574 *(tc
->dirfilter
) = old_dirfilter
;