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
103 #include "rbunicode.h"
104 #include "root_menu.h"
106 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
107 #define PLAYLIST_CONTROL_FILE_VERSION 2
110 Each playlist index has a flag associated with it which identifies what
111 type of track it is. These flags are stored in the 4 high order bits of
114 NOTE: This limits the playlist file size to a max of 256M.
118 01 = Track was prepended into playlist
119 10 = Track was inserted into playlist
120 11 = Track was appended into playlist
125 0 = Track entry is valid
126 1 = Track does not exist on disk and should be skipped
128 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
129 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
130 #define PLAYLIST_QUEUE_MASK 0x20000000
132 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
133 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
134 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
136 #define PLAYLIST_QUEUED 0x20000000
137 #define PLAYLIST_SKIPPED 0x10000000
139 #define PLAYLIST_DISPLAY_COUNT 10
141 struct directory_search_context
{
142 struct playlist_info
* playlist
;
148 static struct playlist_info current_playlist
;
149 static char now_playing
[MAX_PATH
+1];
151 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
152 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
154 static void create_control(struct playlist_info
* playlist
);
155 static int check_control(struct playlist_info
* playlist
);
156 static int recreate_control(struct playlist_info
* playlist
);
157 static void update_playlist_filename(struct playlist_info
* playlist
,
158 const char *dir
, const char *file
);
159 static int add_indices_to_playlist(struct playlist_info
* playlist
,
160 char* buffer
, size_t buflen
);
161 static int add_track_to_playlist(struct playlist_info
* playlist
,
162 const char *filename
, int position
,
163 bool queue
, int seek_pos
);
164 static int directory_search_callback(char* filename
, void* context
);
165 static int remove_track_from_playlist(struct playlist_info
* playlist
,
166 int position
, bool write
);
167 static int randomise_playlist(struct playlist_info
* playlist
,
168 unsigned int seed
, bool start_current
,
170 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
172 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
174 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
176 static int compare(const void* p1
, const void* p2
);
177 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
178 bool control_file
, char *buf
, int buf_length
);
179 static int get_next_directory(char *dir
);
180 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
181 static int get_previous_directory(char *dir
);
182 static int check_subdir_for_music(char *dir
, char *subdir
, bool recurse
);
183 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
185 static void display_playlist_count(int count
, const unsigned char *fmt
,
187 static void display_buffer_full(void);
188 static int flush_cached_control(struct playlist_info
* playlist
);
189 static int update_control(struct playlist_info
* playlist
,
190 enum playlist_command command
, int i1
, int i2
,
191 const char* s1
, const char* s2
, void* data
);
192 static void sync_control(struct playlist_info
* playlist
, bool force
);
193 static int rotate_index(const struct playlist_info
* playlist
, int index
);
196 #define PLAYLIST_LOAD_POINTERS 1
198 static struct event_queue playlist_queue
;
199 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x800)/sizeof(long)];
200 static const char playlist_thread_name
[] = "playlist cachectrl";
203 /* Check if the filename suggests M3U or M3U8 format. */
204 static bool is_m3u8(const char* filename
)
206 int len
= strlen(filename
);
208 /* Default to M3U8 unless explicitly told otherwise. */
209 return !(len
> 4 && strcasecmp(&filename
[len
- 4], ".m3u") == 0);
213 /* Convert a filename in an M3U playlist to UTF-8.
215 * buf - the filename to convert; can contain more than one line from the
217 * buf_len - amount of buf that is used.
218 * buf_max - total size of buf.
219 * temp - temporary conversion buffer, at least buf_max bytes.
221 * Returns the length of the converted filename.
223 static int convert_m3u(char* buf
, int buf_len
, int buf_max
, char* temp
)
229 while ((buf
[i
] != '\n') && (buf
[i
] != '\r') && (i
< buf_len
))
234 /* Work back killing white space. */
235 while ((i
> 0) && isspace(buf
[i
- 1]))
243 /* Convert char by char, so as to not overflow temp (iso_decode should
244 * preferably handle this). No more than 4 bytes should be generated for
247 for (i
= 0; i
< buf_len
&& dest
< (temp
+ buf_max
- 4); i
++)
249 dest
= iso_decode(&buf
[i
], dest
, -1, 1);
258 * remove any files and indices associated with the playlist
260 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
262 playlist
->filename
[0] = '\0';
263 playlist
->utf8
= true;
265 if(playlist
->fd
>= 0)
266 /* If there is an already open playlist, close it. */
270 if(playlist
->control_fd
>= 0)
271 close(playlist
->control_fd
);
272 playlist
->control_fd
= -1;
273 playlist
->control_created
= false;
275 playlist
->in_ram
= false;
277 if (playlist
->buffer
)
278 playlist
->buffer
[0] = 0;
280 playlist
->buffer_end_pos
= 0;
283 playlist
->first_index
= 0;
284 playlist
->amount
= 0;
285 playlist
->last_insert_pos
= -1;
287 playlist
->shuffle_modified
= false;
288 playlist
->deleted
= false;
289 playlist
->num_inserted_tracks
= 0;
290 playlist
->started
= false;
292 playlist
->num_cached
= 0;
293 playlist
->pending_control_sync
= false;
295 if (!resume
&& playlist
->current
)
297 /* start with fresh playlist control file when starting new
299 create_control(playlist
);
301 /* Reset resume settings */
302 global_status
.resume_first_index
= 0;
303 global_status
.resume_seed
= -1;
308 * Initialize a new playlist for viewing/editing/playing. dir is the
309 * directory where the playlist is located and file is the filename.
311 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
314 const char *fileused
= file
;
315 const char *dirused
= dir
;
316 empty_playlist(playlist
, false);
322 if (dirused
&& playlist
->current
) /* !current cannot be in_ram */
323 playlist
->in_ram
= true;
325 dirused
= ""; /* empty playlist */
328 update_playlist_filename(playlist
, dirused
, fileused
);
330 if (playlist
->control_fd
>= 0)
332 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
333 PLAYLIST_CONTROL_FILE_VERSION
, -1, dirused
, fileused
, NULL
);
334 sync_control(playlist
, false);
339 * create control file for playlist
341 static void create_control(struct playlist_info
* playlist
)
343 playlist
->control_fd
= open(playlist
->control_filename
,
344 O_CREAT
|O_RDWR
|O_TRUNC
);
345 if (playlist
->control_fd
< 0)
347 if (check_rockboxdir())
349 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
350 splashf(HZ
*2, (unsigned char *)"%s (%d)",
351 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
352 playlist
->control_fd
);
354 playlist
->control_created
= false;
358 playlist
->control_created
= true;
363 * validate the control file. This may include creating/initializing it if
366 static int check_control(struct playlist_info
* playlist
)
368 if (!playlist
->control_created
)
370 create_control(playlist
);
372 if (playlist
->control_fd
>= 0)
374 char* dir
= playlist
->filename
;
375 char* file
= playlist
->filename
+playlist
->dirlen
;
376 char c
= playlist
->filename
[playlist
->dirlen
-1];
378 playlist
->filename
[playlist
->dirlen
-1] = '\0';
380 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
381 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
382 sync_control(playlist
, false);
383 playlist
->filename
[playlist
->dirlen
-1] = c
;
387 if (playlist
->control_fd
< 0)
394 * recreate the control file based on current playlist entries
396 static int recreate_control(struct playlist_info
* playlist
)
398 char temp_file
[MAX_PATH
+1];
403 if(playlist
->control_fd
>= 0)
405 char* dir
= playlist
->filename
;
406 char* file
= playlist
->filename
+playlist
->dirlen
;
407 char c
= playlist
->filename
[playlist
->dirlen
-1];
409 close(playlist
->control_fd
);
411 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
412 playlist
->control_filename
);
414 if (rename(playlist
->control_filename
, temp_file
) < 0)
417 temp_fd
= open(temp_file
, O_RDONLY
);
421 playlist
->control_fd
= open(playlist
->control_filename
,
422 O_CREAT
|O_RDWR
|O_TRUNC
);
423 if (playlist
->control_fd
< 0)
426 playlist
->filename
[playlist
->dirlen
-1] = '\0';
428 /* cannot call update_control() because of mutex */
429 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
430 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
432 playlist
->filename
[playlist
->dirlen
-1] = c
;
442 playlist
->shuffle_modified
= false;
443 playlist
->deleted
= false;
444 playlist
->num_inserted_tracks
= 0;
446 if (playlist
->current
)
448 global_status
.resume_seed
= -1;
452 for (i
=0; i
<playlist
->amount
; i
++)
454 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
456 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
457 char inserted_file
[MAX_PATH
+1];
459 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
461 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
463 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
464 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
467 /* save the position in file where name is written */
468 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
470 result
= fdprintf(playlist
->control_fd
, "%s\n",
473 playlist
->indices
[i
] =
474 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
480 playlist
->num_inserted_tracks
++;
486 fsync(playlist
->control_fd
);
495 * store directory and name of playlist file
497 static void update_playlist_filename(struct playlist_info
* playlist
,
498 const char *dir
, const char *file
)
501 int dirlen
= strlen(dir
);
503 playlist
->utf8
= is_m3u8(file
);
505 /* If the dir does not end in trailing slash, we use a separator.
506 Otherwise we don't. */
507 if('/' != dir
[dirlen
-1])
513 playlist
->dirlen
= dirlen
;
515 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
516 "%s%s%s", dir
, sep
, file
);
520 * calculate track offsets within a playlist file
522 static int add_indices_to_playlist(struct playlist_info
* playlist
,
523 char* buffer
, size_t buflen
)
527 unsigned int count
= 0;
532 if(-1 == playlist
->fd
)
533 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
535 return -1; /* failure */
536 if((i
= lseek(playlist
->fd
, 0, SEEK_CUR
)) > 0)
537 playlist
->utf8
= true; /* Override any earlier indication. */
539 splash(0, ID2P(LANG_WAIT
));
543 /* use mp3 buffer for maximum load speed */
545 #if CONFIG_CODEC != SWCODEC
546 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
547 buflen
= (audiobufend
- audiobuf
);
548 buffer
= (char *)audiobuf
;
550 buffer
= (char *)audio_get_buffer(false, &buflen
);
558 nread
= read(playlist
->fd
, buffer
, buflen
);
559 /* Terminate on EOF */
563 p
= (unsigned char *)buffer
;
565 for(count
=0; count
< nread
; count
++,p
++) {
567 /* Are we on a new line? */
568 if((*p
== '\n') || (*p
== '\r'))
578 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
579 display_buffer_full();
584 /* Store a new entry */
585 playlist
->indices
[ playlist
->amount
] = i
+count
;
587 if (playlist
->filenames
)
588 playlist
->filenames
[ playlist
->amount
] = NULL
;
600 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
607 * Utility function to create a new playlist, fill it with the next or
608 * previous directory, shuffle it if needed, and start playback.
609 * If play_last is true and direction zero or negative, start playing
610 * the last file in the directory, otherwise start playing the first.
612 static int create_and_play_dir(int direction
, bool play_last
)
614 char dir
[MAX_PATH
+ 1];
619 res
= get_next_directory(dir
);
621 res
= get_previous_directory(dir
);
625 if (playlist_create(dir
, NULL
) != -1)
627 ft_build_playlist(tree_get_context(), 0);
629 if (global_settings
.playlist_shuffle
)
630 playlist_shuffle(current_tick
, -1);
632 if (play_last
&& direction
<= 0)
633 index
= current_playlist
.amount
- 1;
637 #if (CONFIG_CODEC != SWCODEC)
638 playlist_start(index
, 0);
642 /* we've overwritten the dircache when getting the next/previous dir,
643 so the tree browser context will need to be reloaded */
651 * Removes all tracks, from the playlist, leaving the presently playing
654 int playlist_remove_all_tracks(struct playlist_info
*playlist
)
658 if (playlist
== NULL
)
659 playlist
= ¤t_playlist
;
661 while (playlist
->index
> 0)
662 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
665 while (playlist
->amount
> 1)
666 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
669 if (playlist
->amount
== 1) {
670 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
678 * Add track to playlist at specified position. There are five special
679 * positions that can be specified:
680 * PLAYLIST_PREPEND - Add track at beginning of playlist
681 * PLAYLIST_INSERT - Add track after current song. NOTE: If
682 * there are already inserted tracks then track
683 * is added to the end of the insertion list
684 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
685 * matter what other tracks have been inserted
686 * PLAYLIST_INSERT_LAST - Add track to end of playlist
687 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
688 * current playing track and end of playlist
689 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
690 * and inster this track at the end.
692 static int add_track_to_playlist(struct playlist_info
* playlist
,
693 const char *filename
, int position
,
694 bool queue
, int seek_pos
)
696 int insert_position
, orig_position
;
697 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
700 insert_position
= orig_position
= position
;
702 if (playlist
->amount
>= playlist
->max_playlist_size
)
704 display_buffer_full();
710 case PLAYLIST_PREPEND
:
711 position
= insert_position
= playlist
->first_index
;
713 case PLAYLIST_INSERT
:
714 /* if there are already inserted tracks then add track to end of
715 insertion list else add after current playing track */
716 if (playlist
->last_insert_pos
>= 0 &&
717 playlist
->last_insert_pos
< playlist
->amount
&&
718 (playlist
->indices
[playlist
->last_insert_pos
]&
719 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
720 position
= insert_position
= playlist
->last_insert_pos
+1;
721 else if (playlist
->amount
> 0)
722 position
= insert_position
= playlist
->index
+ 1;
724 position
= insert_position
= 0;
726 if (playlist
->started
)
727 playlist
->last_insert_pos
= position
;
729 case PLAYLIST_INSERT_FIRST
:
730 if (playlist
->amount
> 0)
731 position
= insert_position
= playlist
->index
+ 1;
733 position
= insert_position
= 0;
735 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
736 playlist
->last_insert_pos
= position
;
738 case PLAYLIST_INSERT_LAST
:
739 if (playlist
->first_index
> 0)
740 position
= insert_position
= playlist
->first_index
;
742 position
= insert_position
= playlist
->amount
;
744 case PLAYLIST_INSERT_SHUFFLED
:
746 if (playlist
->started
)
749 int n
= playlist
->amount
-
750 rotate_index(playlist
, playlist
->index
);
757 position
= playlist
->index
+ offset
+ 1;
758 if (position
>= playlist
->amount
)
759 position
-= playlist
->amount
;
761 insert_position
= position
;
764 position
= insert_position
= (rand() % (playlist
->amount
+1));
767 case PLAYLIST_REPLACE
:
768 if (playlist_remove_all_tracks(playlist
) < 0)
771 position
= insert_position
= playlist
->index
+ 1;
776 flags
|= PLAYLIST_QUEUED
;
778 /* shift indices so that track can be added */
779 for (i
=playlist
->amount
; i
>insert_position
; i
--)
781 playlist
->indices
[i
] = playlist
->indices
[i
-1];
783 if (playlist
->filenames
)
784 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
788 /* update stored indices if needed */
789 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
793 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
794 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
796 playlist
->first_index
++;
798 if (seek_pos
< 0 && playlist
->current
)
800 global_status
.resume_first_index
= playlist
->first_index
;
805 if (insert_position
< playlist
->last_insert_pos
||
806 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
807 playlist
->last_insert_pos
++;
809 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
811 int result
= update_control(playlist
,
812 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
813 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
819 playlist
->indices
[insert_position
] = flags
| seek_pos
;
822 if (playlist
->filenames
)
823 playlist
->filenames
[insert_position
] = NULL
;
827 playlist
->num_inserted_tracks
++;
829 return insert_position
;
833 * Callback for playlist_directory_tracksearch to insert track into
836 static int directory_search_callback(char* filename
, void* context
)
838 struct directory_search_context
* c
=
839 (struct directory_search_context
*) context
;
842 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
850 /* Make sure tracks are inserted in correct order if user requests
852 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
853 c
->position
= insert_pos
+ 1;
855 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
857 unsigned char* count_str
;
860 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
862 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
864 display_playlist_count(c
->count
, count_str
, false);
866 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
867 (audio_status() & AUDIO_STATUS_PLAY
) &&
868 c
->playlist
->started
)
869 audio_flush_and_reload_tracks();
876 * remove track at specified position
878 static int remove_track_from_playlist(struct playlist_info
* playlist
,
879 int position
, bool write
)
884 if (playlist
->amount
<= 0)
887 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
889 /* shift indices now that track has been removed */
890 for (i
=position
; i
<playlist
->amount
; i
++)
892 playlist
->indices
[i
] = playlist
->indices
[i
+1];
894 if (playlist
->filenames
)
895 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
902 playlist
->num_inserted_tracks
--;
904 playlist
->deleted
= true;
906 /* update stored indices if needed */
907 if (position
< playlist
->index
)
910 if (position
< playlist
->first_index
)
912 playlist
->first_index
--;
916 global_status
.resume_first_index
= playlist
->first_index
;
921 if (position
<= playlist
->last_insert_pos
)
922 playlist
->last_insert_pos
--;
924 if (write
&& playlist
->control_fd
>= 0)
926 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
927 position
, -1, NULL
, NULL
, NULL
);
932 sync_control(playlist
, false);
939 * randomly rearrange the array of indices for the playlist. If start_current
940 * is true then update the index to the new index of the current playing track
942 static int randomise_playlist(struct playlist_info
* playlist
,
943 unsigned int seed
, bool start_current
,
949 unsigned int current
= playlist
->indices
[playlist
->index
];
951 /* seed 0 is used to identify sorted playlist for resume purposes */
955 /* seed with the given seed */
958 /* randomise entire indices list */
959 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
961 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
962 candidate
= rand() % (count
+ 1);
964 /* now swap the values at the 'count' and 'candidate' positions */
965 store
= playlist
->indices
[candidate
];
966 playlist
->indices
[candidate
] = playlist
->indices
[count
];
967 playlist
->indices
[count
] = store
;
969 if (playlist
->filenames
)
971 store
= (long)playlist
->filenames
[candidate
];
972 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
973 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
979 find_and_set_playlist_index(playlist
, current
);
981 /* indices have been moved so last insert position is no longer valid */
982 playlist
->last_insert_pos
= -1;
984 playlist
->seed
= seed
;
985 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
986 playlist
->shuffle_modified
= true;
990 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
991 playlist
->first_index
, NULL
, NULL
, NULL
);
992 global_status
.resume_seed
= seed
;
1000 * Sort the array of indices for the playlist. If start_current is true then
1001 * set the index to the new index of the current song.
1003 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
1006 unsigned int current
= playlist
->indices
[playlist
->index
];
1008 if (playlist
->amount
> 0)
1009 qsort(playlist
->indices
, playlist
->amount
,
1010 sizeof(playlist
->indices
[0]), compare
);
1012 #ifdef HAVE_DIRCACHE
1013 /** We need to re-check the song names from disk because qsort can't
1014 * sort two arrays at once :/
1015 * FIXME: Please implement a better way to do this. */
1016 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
1017 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
1021 find_and_set_playlist_index(playlist
, current
);
1023 /* indices have been moved so last insert position is no longer valid */
1024 playlist
->last_insert_pos
= -1;
1026 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
1027 playlist
->shuffle_modified
= false;
1028 if (write
&& playlist
->control_fd
>= 0)
1030 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
1031 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
1032 global_status
.resume_seed
= 0;
1039 /* Calculate how many steps we have to really step when skipping entries
1042 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
1044 int i
, count
, direction
;
1046 int stepped_count
= 0;
1059 index
= playlist
->index
;
1062 /* Boundary check */
1064 index
+= playlist
->amount
;
1065 if (index
>= playlist
->amount
)
1066 index
-= playlist
->amount
;
1068 /* Check if we found a bad entry. */
1069 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
1072 /* Are all entries bad? */
1073 if (stepped_count
++ > playlist
->amount
)
1080 } while (i
<= count
);
1085 /* Marks the index of the track to be skipped that is "steps" away from
1086 * current playing track.
1088 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1092 if (playlist
== NULL
)
1093 playlist
= ¤t_playlist
;
1095 /* need to account for already skipped tracks */
1096 steps
= calculate_step_count(playlist
, steps
);
1098 index
= playlist
->index
+ steps
;
1100 index
+= playlist
->amount
;
1101 else if (index
>= playlist
->amount
)
1102 index
-= playlist
->amount
;
1104 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1108 * returns the index of the track that is "steps" away from current playing
1111 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1114 int current_index
= playlist
->index
;
1115 int next_index
= -1;
1117 if (playlist
->amount
<= 0)
1120 if (repeat_mode
== -1)
1121 repeat_mode
= global_settings
.repeat_mode
;
1123 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1124 repeat_mode
= REPEAT_ALL
;
1126 steps
= calculate_step_count(playlist
, steps
);
1127 switch (repeat_mode
)
1129 case REPEAT_SHUFFLE
:
1130 /* Treat repeat shuffle just like repeat off. At end of playlist,
1131 play will be resumed in playlist_next() */
1134 current_index
= rotate_index(playlist
, current_index
);
1135 next_index
= current_index
+steps
;
1136 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1139 next_index
= (next_index
+playlist
->first_index
) %
1146 #ifdef AB_REPEAT_ENABLE
1149 next_index
= current_index
;
1155 next_index
= (current_index
+steps
) % playlist
->amount
;
1156 while (next_index
< 0)
1157 next_index
+= playlist
->amount
;
1159 if (steps
>= playlist
->amount
)
1166 /* second time around so skip the queued files */
1167 for (i
=0; i
<playlist
->amount
; i
++)
1169 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1170 index
= (index
+1) % playlist
->amount
;
1182 /* No luck if the whole playlist was bad. */
1183 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1190 * Search for the seek track and set appropriate indices. Used after shuffle
1191 * to make sure the current index is still pointing to correct track.
1193 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1198 /* Set the index to the current song */
1199 for (i
=0; i
<playlist
->amount
; i
++)
1201 if (playlist
->indices
[i
] == seek
)
1203 playlist
->index
= playlist
->first_index
= i
;
1205 if (playlist
->current
)
1207 global_status
.resume_first_index
= i
;
1217 * used to sort track indices. Sort order is as follows:
1218 * 1. Prepended tracks (in prepend order)
1219 * 2. Playlist/directory tracks (in playlist order)
1220 * 3. Inserted/Appended tracks (in insert order)
1222 static int compare(const void* p1
, const void* p2
)
1224 unsigned long* e1
= (unsigned long*) p1
;
1225 unsigned long* e2
= (unsigned long*) p2
;
1226 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1227 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1229 if (flags1
== flags2
)
1230 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1231 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1232 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1234 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1235 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1237 else if (flags1
&& flags2
)
1238 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1243 #ifdef HAVE_DIRCACHE
1245 * Thread to update filename pointers to dircache on background
1246 * without affecting playlist load up performance. This thread also flushes
1247 * any pending control commands when the disk spins up.
1249 static bool playlist_flush_callback(void)
1251 struct playlist_info
*playlist
;
1252 playlist
= ¤t_playlist
;
1253 if (playlist
->control_fd
>= 0)
1255 if (playlist
->num_cached
> 0)
1257 mutex_lock(&playlist
->control_mutex
);
1258 flush_cached_control(playlist
);
1259 mutex_unlock(&playlist
->control_mutex
);
1261 sync_control(playlist
, true);
1266 static void playlist_thread(void)
1268 struct queue_event ev
;
1269 bool dirty_pointers
= false;
1270 static char tmp
[MAX_PATH
+1];
1272 struct playlist_info
*playlist
;
1279 #ifndef HAVE_FLASH_STORAGE
1280 if (global_settings
.disk_spindown
> 1 &&
1281 global_settings
.disk_spindown
<= 5)
1282 sleep_time
= global_settings
.disk_spindown
- 1;
1287 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1291 case PLAYLIST_LOAD_POINTERS
:
1292 dirty_pointers
= true;
1295 /* Start the background scanning after either the disk spindown
1296 timeout or 5s, whichever is less */
1298 playlist
= ¤t_playlist
;
1299 if (playlist
->control_fd
>= 0)
1301 if (playlist
->num_cached
> 0)
1302 register_ata_idle_func(playlist_flush_callback
);
1305 if (!dirty_pointers
)
1308 if (!dircache_is_enabled() || !playlist
->filenames
1309 || playlist
->amount
<= 0)
1312 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1315 for (index
= 0; index
< playlist
->amount
1316 && queue_empty(&playlist_queue
); index
++)
1318 /* Process only pointers that are not already loaded. */
1319 if (playlist
->filenames
[index
])
1322 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1323 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1325 /* Load the filename from playlist file. */
1326 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1330 /* Set the dircache entry pointer. */
1331 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1333 /* And be on background so user doesn't notice any delays. */
1337 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1340 dirty_pointers
= false;
1344 case SYS_USB_CONNECTED
:
1345 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1346 usb_wait_for_disconnect(&playlist_queue
);
1355 * gets pathname for track at seek index
1357 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1358 bool control_file
, char *buf
, int buf_length
)
1362 char tmp_buf
[MAX_PATH
+1];
1363 char dir_buf
[MAX_PATH
+1];
1364 bool utf8
= playlist
->utf8
;
1366 if (buf_length
> MAX_PATH
+1)
1367 buf_length
= MAX_PATH
+1;
1369 #ifdef HAVE_DIRCACHE
1370 if (dircache_is_enabled() && playlist
->filenames
)
1372 if (playlist
->filenames
[index
] != NULL
)
1374 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1375 max
= strlen(tmp_buf
) + 1;
1382 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1384 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1385 tmp_buf
[MAX_PATH
] = '\0';
1386 max
= strlen(tmp_buf
) + 1;
1390 mutex_lock(&playlist
->control_mutex
);
1394 fd
= playlist
->control_fd
;
1399 if(-1 == playlist
->fd
)
1400 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1408 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1412 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1414 if ((max
> 0) && !utf8
)
1416 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1417 * be as large as tmp_buf.
1419 max
= convert_m3u(tmp_buf
, max
, sizeof(tmp_buf
), dir_buf
);
1424 mutex_unlock(&playlist
->control_mutex
);
1429 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1431 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1437 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1438 dir_buf
[playlist
->dirlen
-1] = 0;
1440 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1443 static int get_next_directory(char *dir
){
1444 return get_next_dir(dir
,true,false);
1447 static int get_previous_directory(char *dir
){
1448 return get_next_dir(dir
,false,false);
1452 * search through all the directories (starting with the current) to find
1453 * one that has tracks to play
1455 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1457 struct playlist_info
* playlist
= ¤t_playlist
;
1459 int sort_dir
= global_settings
.sort_dir
;
1460 char *start_dir
= NULL
;
1462 struct tree_context
* tc
= tree_get_context();
1463 int dirfilter
= *(tc
->dirfilter
);
1464 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1466 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1467 char buffer
[MAX_PATH
];
1468 int folder_count
= 0,i
;
1469 srand(current_tick
);
1470 *(tc
->dirfilter
) = SHOW_MUSIC
;
1473 read(fd
,&folder_count
,sizeof(int));
1478 i
= rand()%folder_count
;
1479 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1480 read(fd
,buffer
,MAX_PATH
);
1481 if (check_subdir_for_music(buffer
, "", false) ==0)
1487 *(tc
->dirfilter
) = dirfilter
;
1492 /* not random folder advance */
1494 /* start with root */
1498 /* start with current directory */
1499 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1500 dir
[playlist
->dirlen
-1] = '\0';
1503 /* use the tree browser dircache to load files */
1504 *(tc
->dirfilter
) = SHOW_ALL
;
1506 /* sort in another direction if previous dir is requested */
1508 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1509 global_settings
.sort_dir
= 4;
1510 else if (global_settings
.sort_dir
== 1)
1511 global_settings
.sort_dir
= 2;
1512 else if (global_settings
.sort_dir
== 2)
1513 global_settings
.sort_dir
= 1;
1514 else if (global_settings
.sort_dir
== 4)
1515 global_settings
.sort_dir
= 0;
1520 struct entry
*files
;
1524 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1526 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1532 files
= (struct entry
*) tc
->dircache
;
1533 num_files
= tc
->filesindir
;
1535 for (i
=0; i
<num_files
; i
++)
1538 if (action_userabort(TIMEOUT_NOBLOCK
))
1545 if (files
[i
].attr
& ATTR_DIRECTORY
)
1549 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1556 else if (!strcmp(start_dir
, files
[i
].name
))
1563 /* move down to parent directory. current directory name is
1564 stored as the starting point for the search in parent */
1565 start_dir
= strrchr(dir
, '/');
1576 /* restore dirfilter & sort_dir */
1577 *(tc
->dirfilter
) = dirfilter
;
1578 global_settings
.sort_dir
= sort_dir
;
1580 /* special case if nothing found: try start searching again from root */
1581 if (result
== -1 && !recursion
){
1582 result
= get_next_dir(dir
,is_forward
, true);
1589 * Checks if there are any music files in the dir or any of its
1590 * subdirectories. May be called recursively.
1592 static int check_subdir_for_music(char *dir
, char *subdir
, bool recurse
)
1595 int dirlen
= strlen(dir
);
1598 struct entry
*files
;
1599 bool has_music
= false;
1600 bool has_subdir
= false;
1601 struct tree_context
* tc
= tree_get_context();
1603 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1605 if (ft_load(tc
, dir
) < 0)
1607 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1611 files
= (struct entry
*) tc
->dircache
;
1612 num_files
= tc
->filesindir
;
1614 for (i
=0; i
<num_files
; i
++)
1616 if (files
[i
].attr
& ATTR_DIRECTORY
)
1618 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1628 if (has_subdir
&& recurse
)
1630 for (i
=0; i
<num_files
; i
++)
1632 if (action_userabort(TIMEOUT_NOBLOCK
))
1638 if (files
[i
].attr
& ATTR_DIRECTORY
)
1640 result
= check_subdir_for_music(dir
, files
[i
].name
, true);
1658 /* we now need to reload our current directory */
1659 if(ft_load(tc
, dir
) < 0)
1660 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1666 * Returns absolute path of track
1668 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1675 /* Zero-terminate the file name */
1676 while((src
[i
] != '\n') &&
1681 /* Now work back killing white space */
1682 while((src
[i
-1] == ' ') ||
1688 /* replace backslashes with forward slashes */
1689 for ( j
=0; j
<i
; j
++ )
1690 if ( src
[j
] == '\\' )
1695 strncpy(dest
, src
, buf_length
);
1699 /* handle dos style drive letter */
1701 strncpy(dest
, &src
[2], buf_length
);
1702 else if (!strncmp(src
, "../", 3))
1704 /* handle relative paths */
1706 while(!strncmp(&src
[i
], "../", 3))
1708 for (j
=0; j
<i
/3; j
++) {
1709 temp_ptr
= strrchr(dir
, '/');
1715 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1717 else if ( '.' == src
[0] && '/' == src
[1] ) {
1718 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1721 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1729 * Display splash message showing progress of playlist/directory insertion or
1732 static void display_playlist_count(int count
, const unsigned char *fmt
,
1735 static long talked_tick
= 0;
1736 long id
= P2ID(fmt
);
1737 if(global_settings
.talk_menu
&& id
>=0)
1739 if(final
|| (count
&& (talked_tick
== 0
1740 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1742 talked_tick
= current_tick
;
1743 talk_number(count
, false);
1749 splashf(0, fmt
, count
, str(LANG_OFF_ABORT
));
1753 * Display buffer full message
1755 static void display_buffer_full(void)
1757 splash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1761 * Flush any cached control commands to disk. Called when playlist is being
1762 * modified. Returns 0 on success and -1 on failure.
1764 static int flush_cached_control(struct playlist_info
* playlist
)
1769 if (!playlist
->num_cached
)
1772 lseek(playlist
->control_fd
, 0, SEEK_END
);
1774 for (i
=0; i
<playlist
->num_cached
; i
++)
1776 struct playlist_control_cache
* cache
=
1777 &(playlist
->control_cache
[i
]);
1779 switch (cache
->command
)
1781 case PLAYLIST_COMMAND_PLAYLIST
:
1782 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1783 cache
->i1
, cache
->s1
, cache
->s2
);
1785 case PLAYLIST_COMMAND_ADD
:
1786 case PLAYLIST_COMMAND_QUEUE
:
1787 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1788 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1789 cache
->i1
, cache
->i2
);
1792 /* save the position in file where name is written */
1793 int* seek_pos
= (int *)cache
->data
;
1794 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1795 result
= fdprintf(playlist
->control_fd
, "%s\n",
1799 case PLAYLIST_COMMAND_DELETE
:
1800 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1802 case PLAYLIST_COMMAND_SHUFFLE
:
1803 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1804 cache
->i1
, cache
->i2
);
1806 case PLAYLIST_COMMAND_UNSHUFFLE
:
1807 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1809 case PLAYLIST_COMMAND_RESET
:
1810 result
= fdprintf(playlist
->control_fd
, "R\n");
1822 if (global_status
.resume_seed
>= 0)
1824 global_status
.resume_seed
= -1;
1828 playlist
->num_cached
= 0;
1829 playlist
->pending_control_sync
= true;
1836 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1843 * Update control data with new command. Depending on the command, it may be
1844 * cached or flushed to disk.
1846 static int update_control(struct playlist_info
* playlist
,
1847 enum playlist_command command
, int i1
, int i2
,
1848 const char* s1
, const char* s2
, void* data
)
1851 struct playlist_control_cache
* cache
;
1854 mutex_lock(&playlist
->control_mutex
);
1856 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1858 cache
->command
= command
;
1867 case PLAYLIST_COMMAND_PLAYLIST
:
1868 case PLAYLIST_COMMAND_ADD
:
1869 case PLAYLIST_COMMAND_QUEUE
:
1870 #ifndef HAVE_DIRCACHE
1871 case PLAYLIST_COMMAND_DELETE
:
1872 case PLAYLIST_COMMAND_RESET
:
1876 case PLAYLIST_COMMAND_SHUFFLE
:
1877 case PLAYLIST_COMMAND_UNSHUFFLE
:
1879 /* only flush when needed */
1883 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1884 result
= flush_cached_control(playlist
);
1886 mutex_unlock(&playlist
->control_mutex
);
1892 * sync control file to disk
1894 static void sync_control(struct playlist_info
* playlist
, bool force
)
1896 #ifdef HAVE_DIRCACHE
1897 if (playlist
->started
&& force
)
1901 if (playlist
->started
)
1904 if (playlist
->pending_control_sync
)
1906 mutex_lock(&playlist
->control_mutex
);
1907 fsync(playlist
->control_fd
);
1908 playlist
->pending_control_sync
= false;
1909 mutex_unlock(&playlist
->control_mutex
);
1915 * Rotate indices such that first_index is index 0
1917 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1919 index
-= playlist
->first_index
;
1921 index
+= playlist
->amount
;
1927 * Initialize playlist entries at startup
1929 void playlist_init(void)
1931 struct playlist_info
* playlist
= ¤t_playlist
;
1933 playlist
->current
= true;
1934 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1935 "%s", PLAYLIST_CONTROL_FILE
);
1937 playlist
->control_fd
= -1;
1938 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1939 playlist
->indices
= buffer_alloc(
1940 playlist
->max_playlist_size
* sizeof(int));
1941 playlist
->buffer_size
=
1942 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1943 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1944 mutex_init(&playlist
->control_mutex
);
1945 empty_playlist(playlist
, true);
1947 #ifdef HAVE_DIRCACHE
1948 playlist
->filenames
= buffer_alloc(
1949 playlist
->max_playlist_size
* sizeof(int));
1950 memset(playlist
->filenames
, 0,
1951 playlist
->max_playlist_size
* sizeof(int));
1952 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1953 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1955 queue_init(&playlist_queue
, true);
1960 * Clean playlist at shutdown
1962 void playlist_shutdown(void)
1964 struct playlist_info
* playlist
= ¤t_playlist
;
1966 if (playlist
->control_fd
>= 0)
1968 mutex_lock(&playlist
->control_mutex
);
1970 if (playlist
->num_cached
> 0)
1971 flush_cached_control(playlist
);
1973 close(playlist
->control_fd
);
1975 mutex_unlock(&playlist
->control_mutex
);
1980 * Create new playlist
1982 int playlist_create(const char *dir
, const char *file
)
1984 struct playlist_info
* playlist
= ¤t_playlist
;
1986 new_playlist(playlist
, dir
, file
);
1989 /* load the playlist file */
1990 add_indices_to_playlist(playlist
, NULL
, 0);
1995 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1998 * Restore the playlist state based on control file commands. Called to
1999 * resume playback after shutdown.
2001 int playlist_resume(void)
2003 struct playlist_info
* playlist
= ¤t_playlist
;
2008 int control_file_size
= 0;
2012 /* use mp3 buffer for maximum load speed */
2013 #if CONFIG_CODEC != SWCODEC
2014 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2015 buflen
= (audiobufend
- audiobuf
);
2016 buffer
= (char *)audiobuf
;
2018 buffer
= (char *)audio_get_buffer(false, &buflen
);
2021 empty_playlist(playlist
, true);
2023 splash(0, ID2P(LANG_WAIT
));
2024 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
2025 if (playlist
->control_fd
< 0)
2027 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2030 playlist
->control_created
= true;
2032 control_file_size
= filesize(playlist
->control_fd
);
2033 if (control_file_size
<= 0)
2035 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2039 /* read a small amount first to get the header */
2040 nread
= read(playlist
->control_fd
, buffer
,
2041 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2044 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2048 playlist
->started
= true;
2054 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
2055 int last_newline
= 0;
2057 bool newline
= true;
2058 bool exit_loop
= false;
2063 unsigned long last_tick
= current_tick
;
2064 bool useraborted
= false;
2066 for(count
=0; count
<nread
&& !exit_loop
&& !useraborted
; count
++,p
++)
2068 /* So a splash while we are loading. */
2069 if (current_tick
- last_tick
> HZ
/4)
2071 splashf(0, str(LANG_LOADING_PERCENT
),
2072 (total_read
+count
)*100/control_file_size
,
2073 str(LANG_OFF_ABORT
));
2074 if (action_userabort(TIMEOUT_NOBLOCK
))
2079 last_tick
= current_tick
;
2082 /* Are we on a new line? */
2083 if((*p
== '\n') || (*p
== '\r'))
2087 /* save last_newline in case we need to load more data */
2088 last_newline
= count
;
2090 switch (current_command
)
2092 case PLAYLIST_COMMAND_PLAYLIST
:
2094 /* str1=version str2=dir str3=file */
2110 version
= atoi(str1
);
2112 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2115 update_playlist_filename(playlist
, str2
, str3
);
2117 if (str3
[0] != '\0')
2119 /* NOTE: add_indices_to_playlist() overwrites the
2120 audiobuf so we need to reload control file
2122 add_indices_to_playlist(playlist
, NULL
, 0);
2124 else if (str2
[0] != '\0')
2126 playlist
->in_ram
= true;
2127 resume_directory(str2
);
2130 /* load the rest of the data */
2136 case PLAYLIST_COMMAND_ADD
:
2137 case PLAYLIST_COMMAND_QUEUE
:
2139 /* str1=position str2=last_position str3=file */
2140 int position
, last_position
;
2143 if (!str1
|| !str2
|| !str3
)
2150 position
= atoi(str1
);
2151 last_position
= atoi(str2
);
2153 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2156 /* seek position is based on str3's position in
2158 if (add_track_to_playlist(playlist
, str3
, position
,
2159 queue
, total_read
+(str3
-buffer
)) < 0)
2162 playlist
->last_insert_pos
= last_position
;
2166 case PLAYLIST_COMMAND_DELETE
:
2178 position
= atoi(str1
);
2180 if (remove_track_from_playlist(playlist
, position
,
2186 case PLAYLIST_COMMAND_SHUFFLE
:
2188 /* str1=seed str2=first_index */
2200 /* Always sort list before shuffling */
2201 sort_playlist(playlist
, false, false);
2205 playlist
->first_index
= atoi(str2
);
2207 if (randomise_playlist(playlist
, seed
, false,
2214 case PLAYLIST_COMMAND_UNSHUFFLE
:
2216 /* str1=first_index */
2224 playlist
->first_index
= atoi(str1
);
2226 if (sort_playlist(playlist
, false, false) < 0)
2232 case PLAYLIST_COMMAND_RESET
:
2234 playlist
->last_insert_pos
= -1;
2237 case PLAYLIST_COMMAND_COMMENT
:
2244 /* to ignore any extra newlines */
2245 current_command
= PLAYLIST_COMMAND_COMMENT
;
2251 /* first non-comment line must always specify playlist */
2252 if (first
&& *p
!= 'P' && *p
!= '#')
2262 /* playlist can only be specified once */
2270 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2273 current_command
= PLAYLIST_COMMAND_ADD
;
2276 current_command
= PLAYLIST_COMMAND_QUEUE
;
2279 current_command
= PLAYLIST_COMMAND_DELETE
;
2282 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2285 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2288 current_command
= PLAYLIST_COMMAND_RESET
;
2291 current_command
= PLAYLIST_COMMAND_COMMENT
;
2304 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2306 /* all control file strings are separated with a colon.
2307 Replace the colon with 0 to get proper strings that can be
2308 used by commands above */
2314 if ((count
+1) < nread
)
2328 /* allow last string to contain colons */
2339 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2345 splash(HZ
*2, ID2P(LANG_CANCEL
));
2348 if (!newline
|| (exit_loop
&& count
<nread
))
2350 if ((total_read
+ count
) >= control_file_size
)
2352 /* no newline at end of control file */
2353 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2357 /* We didn't end on a newline or we exited loop prematurely.
2358 Either way, re-read the remainder. */
2359 count
= last_newline
;
2360 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2363 total_read
+= count
;
2366 /* still looking for header */
2367 nread
= read(playlist
->control_fd
, buffer
,
2368 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2370 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2372 /* Terminate on EOF */
2375 if (global_status
.resume_seed
>= 0)
2377 /* Apply shuffle command saved in settings */
2378 if (global_status
.resume_seed
== 0)
2379 sort_playlist(playlist
, false, true);
2383 sort_playlist(playlist
, false, false);
2385 randomise_playlist(playlist
, global_status
.resume_seed
,
2390 playlist
->first_index
= global_status
.resume_first_index
;
2395 #ifdef HAVE_DIRCACHE
2396 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2403 * Add track to in_ram playlist. Used when playing directories.
2405 int playlist_add(const char *filename
)
2407 struct playlist_info
* playlist
= ¤t_playlist
;
2408 int len
= strlen(filename
);
2410 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2411 (playlist
->amount
>= playlist
->max_playlist_size
))
2413 display_buffer_full();
2417 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2418 #ifdef HAVE_DIRCACHE
2419 playlist
->filenames
[playlist
->amount
] = NULL
;
2423 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2424 playlist
->buffer_end_pos
+= len
;
2425 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2430 /* shuffle newly created playlist using random seed. */
2431 int playlist_shuffle(int random_seed
, int start_index
)
2433 struct playlist_info
* playlist
= ¤t_playlist
;
2435 unsigned int seek_pos
= 0;
2436 bool start_current
= false;
2438 if (start_index
>= 0 && global_settings
.play_selected
)
2440 /* store the seek position before the shuffle */
2441 seek_pos
= playlist
->indices
[start_index
];
2442 playlist
->index
= global_status
.resume_first_index
=
2443 playlist
->first_index
= start_index
;
2444 start_current
= true;
2447 randomise_playlist(playlist
, random_seed
, start_current
, true);
2449 return playlist
->index
;
2452 /* start playing current playlist at specified index/offset */
2453 int playlist_start(int start_index
, int offset
)
2455 struct playlist_info
* playlist
= ¤t_playlist
;
2457 /* Cancel FM radio selection as previous music. For cases where we start
2458 playback without going to the WPS, such as playlist insert.. or
2459 playlist catalog. */
2460 previous_music_is_wps();
2462 playlist
->index
= start_index
;
2464 #if CONFIG_CODEC != SWCODEC
2465 talk_buffer_steal(); /* will use the mp3 buffer */
2468 playlist
->started
= true;
2469 sync_control(playlist
, false);
2475 /* Returns false if 'steps' is out of bounds, else true */
2476 bool playlist_check(int steps
)
2478 struct playlist_info
* playlist
= ¤t_playlist
;
2480 /* always allow folder navigation */
2481 if (global_settings
.next_folder
&& playlist
->in_ram
)
2484 int index
= get_next_index(playlist
, steps
, -1);
2486 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2487 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2489 return (index
>= 0);
2492 /* get trackname of track that is "steps" away from current playing track.
2493 NULL is used to identify end of playlist */
2494 char* playlist_peek(int steps
)
2496 struct playlist_info
* playlist
= ¤t_playlist
;
2502 index
= get_next_index(playlist
, steps
, -1);
2506 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2507 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2509 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2513 temp_ptr
= now_playing
;
2515 if (!playlist
->in_ram
|| control_file
)
2517 /* remove bogus dirs from beginning of path
2518 (workaround for buggy playlist creation tools) */
2521 if (file_exists(temp_ptr
))
2524 temp_ptr
= strchr(temp_ptr
+1, '/');
2529 /* Even though this is an invalid file, we still need to pass a
2530 file name to the caller because NULL is used to indicate end
2540 * Update indices as track has changed
2542 int playlist_next(int steps
)
2544 struct playlist_info
* playlist
= ¤t_playlist
;
2548 #ifdef AB_REPEAT_ENABLE
2549 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2551 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2555 /* We need to delete all the queued songs */
2556 for (i
=0, j
=steps
; i
<j
; i
++)
2558 index
= get_next_index(playlist
, i
, -1);
2560 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2562 remove_track_from_playlist(playlist
, index
, true);
2563 steps
--; /* one less track */
2568 index
= get_next_index(playlist
, steps
, -1);
2572 /* end of playlist... or is it */
2573 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2574 playlist
->amount
> 1)
2576 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2577 playlist
->first_index
= global_status
.resume_first_index
= 0;
2578 sort_playlist(playlist
, false, false);
2579 randomise_playlist(playlist
, current_tick
, false, true);
2580 #if CONFIG_CODEC != SWCODEC
2581 playlist_start(0, 0);
2583 playlist
->index
= 0;
2586 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2588 index
= create_and_play_dir(steps
, true);
2592 playlist
->index
= index
;
2599 playlist
->index
= index
;
2601 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2603 /* check to see if we've gone beyond the last inserted track */
2604 int cur
= rotate_index(playlist
, index
);
2605 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2609 /* reset last inserted track */
2610 playlist
->last_insert_pos
= -1;
2612 if (playlist
->control_fd
>= 0)
2614 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2615 -1, -1, NULL
, NULL
, NULL
);
2620 sync_control(playlist
, false);
2628 /* try playing next or previous folder */
2629 bool playlist_next_dir(int direction
)
2631 /* not to mess up real playlists */
2632 if(!current_playlist
.in_ram
)
2635 return create_and_play_dir(direction
, false) >= 0;
2638 /* Get resume info for current playing song. If return value is -1 then
2639 settings shouldn't be saved. */
2640 int playlist_get_resume_info(int *resume_index
)
2642 struct playlist_info
* playlist
= ¤t_playlist
;
2644 *resume_index
= playlist
->index
;
2649 /* Update resume info for current playing song. Returns -1 on error. */
2650 int playlist_update_resume_info(const struct mp3entry
* id3
)
2652 struct playlist_info
* playlist
= ¤t_playlist
;
2656 if (global_status
.resume_index
!= playlist
->index
||
2657 global_status
.resume_offset
!= id3
->offset
)
2659 global_status
.resume_index
= playlist
->index
;
2660 global_status
.resume_offset
= id3
->offset
;
2666 global_status
.resume_index
= -1;
2667 global_status
.resume_offset
= -1;
2674 /* Returns index of current playing track for display purposes. This value
2675 should not be used for resume purposes as it doesn't represent the actual
2676 index into the playlist */
2677 int playlist_get_display_index(void)
2679 struct playlist_info
* playlist
= ¤t_playlist
;
2681 /* first_index should always be index 0 for display purposes */
2682 int index
= rotate_index(playlist
, playlist
->index
);
2687 /* returns number of tracks in current playlist */
2688 int playlist_amount(void)
2690 return playlist_amount_ex(NULL
);
2694 * Create a new playlist If playlist is not NULL then we're loading a
2695 * playlist off disk for viewing/editing. The index_buffer is used to store
2696 * playlist indices (required for and only used if !current playlist). The
2697 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2699 int playlist_create_ex(struct playlist_info
* playlist
,
2700 const char* dir
, const char* file
,
2701 void* index_buffer
, int index_buffer_size
,
2702 void* temp_buffer
, int temp_buffer_size
)
2705 playlist
= ¤t_playlist
;
2708 /* Initialize playlist structure */
2709 int r
= rand() % 10;
2710 playlist
->current
= false;
2712 /* Use random name for control file */
2713 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2714 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2716 playlist
->control_fd
= -1;
2720 int num_indices
= index_buffer_size
/ sizeof(int);
2722 #ifdef HAVE_DIRCACHE
2725 if (num_indices
> global_settings
.max_files_in_playlist
)
2726 num_indices
= global_settings
.max_files_in_playlist
;
2728 playlist
->max_playlist_size
= num_indices
;
2729 playlist
->indices
= index_buffer
;
2730 #ifdef HAVE_DIRCACHE
2731 playlist
->filenames
= (const struct dircache_entry
**)
2732 &playlist
->indices
[num_indices
];
2737 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2738 playlist
->indices
= current_playlist
.indices
;
2739 #ifdef HAVE_DIRCACHE
2740 playlist
->filenames
= current_playlist
.filenames
;
2744 playlist
->buffer_size
= 0;
2745 playlist
->buffer
= NULL
;
2746 mutex_init(&playlist
->control_mutex
);
2749 new_playlist(playlist
, dir
, file
);
2752 /* load the playlist file */
2753 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2759 * Set the specified playlist as the current.
2760 * NOTE: You will get undefined behaviour if something is already playing so
2761 * remember to stop before calling this. Also, this call will
2762 * effectively close your playlist, making it unusable.
2764 int playlist_set_current(struct playlist_info
* playlist
)
2766 if (!playlist
|| (check_control(playlist
) < 0))
2769 empty_playlist(¤t_playlist
, false);
2771 strncpy(current_playlist
.filename
, playlist
->filename
,
2772 sizeof(current_playlist
.filename
));
2774 current_playlist
.utf8
= playlist
->utf8
;
2775 current_playlist
.fd
= playlist
->fd
;
2777 close(playlist
->control_fd
);
2778 close(current_playlist
.control_fd
);
2779 remove(current_playlist
.control_filename
);
2780 if (rename(playlist
->control_filename
,
2781 current_playlist
.control_filename
) < 0)
2783 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2785 if (current_playlist
.control_fd
< 0)
2787 current_playlist
.control_created
= true;
2789 current_playlist
.dirlen
= playlist
->dirlen
;
2791 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2793 memcpy(current_playlist
.indices
, playlist
->indices
,
2794 playlist
->max_playlist_size
*sizeof(int));
2795 #ifdef HAVE_DIRCACHE
2796 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2797 playlist
->max_playlist_size
*sizeof(int));
2801 current_playlist
.first_index
= playlist
->first_index
;
2802 current_playlist
.amount
= playlist
->amount
;
2803 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2804 current_playlist
.seed
= playlist
->seed
;
2805 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2806 current_playlist
.deleted
= playlist
->deleted
;
2807 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2809 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2810 sizeof(current_playlist
.control_cache
));
2811 current_playlist
.num_cached
= playlist
->num_cached
;
2812 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2818 * Close files and delete control file for non-current playlist.
2820 void playlist_close(struct playlist_info
* playlist
)
2825 if (playlist
->fd
>= 0)
2826 close(playlist
->fd
);
2828 if (playlist
->control_fd
>= 0)
2829 close(playlist
->control_fd
);
2831 if (playlist
->control_created
)
2832 remove(playlist
->control_filename
);
2835 void playlist_sync(struct playlist_info
* playlist
)
2838 playlist
= ¤t_playlist
;
2840 sync_control(playlist
, false);
2841 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2842 audio_flush_and_reload_tracks();
2844 #ifdef HAVE_DIRCACHE
2845 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2850 * Insert track into playlist at specified position (or one of the special
2851 * positions). Returns position where track was inserted or -1 if error.
2853 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2854 int position
, bool queue
, bool sync
)
2859 playlist
= ¤t_playlist
;
2861 if (check_control(playlist
) < 0)
2863 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2867 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2869 /* Check if we want manually sync later. For example when adding
2870 * bunch of files from tagcache, syncing after every file wouldn't be
2871 * a good thing to do. */
2872 if (sync
&& result
>= 0)
2873 playlist_sync(playlist
);
2879 * Insert all tracks from specified directory into playlist.
2881 int playlist_insert_directory(struct playlist_info
* playlist
,
2882 const char *dirname
, int position
, bool queue
,
2886 unsigned char *count_str
;
2887 struct directory_search_context context
;
2890 playlist
= ¤t_playlist
;
2892 if (check_control(playlist
) < 0)
2894 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2898 if (position
== PLAYLIST_REPLACE
)
2900 if (playlist_remove_all_tracks(playlist
) == 0)
2901 position
= PLAYLIST_INSERT_LAST
;
2907 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2909 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2911 display_playlist_count(0, count_str
, false);
2913 context
.playlist
= playlist
;
2914 context
.position
= position
;
2915 context
.queue
= queue
;
2920 result
= playlist_directory_tracksearch(dirname
, recurse
,
2921 directory_search_callback
, &context
);
2923 sync_control(playlist
, false);
2927 display_playlist_count(context
.count
, count_str
, true);
2929 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2930 audio_flush_and_reload_tracks();
2932 #ifdef HAVE_DIRCACHE
2933 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2940 * Insert all tracks from specified playlist into dynamic playlist.
2942 int playlist_insert_playlist(struct playlist_info
* playlist
, const char *filename
,
2943 int position
, bool queue
)
2949 unsigned char *count_str
;
2950 char temp_buf
[MAX_PATH
+1];
2951 char trackname
[MAX_PATH
+1];
2954 bool utf8
= is_m3u8(filename
);
2957 playlist
= ¤t_playlist
;
2959 if (check_control(playlist
) < 0)
2961 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2965 fd
= open_utf8(filename
, O_RDONLY
);
2968 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2972 /* we need the directory name for formatting purposes */
2975 temp_ptr
= strrchr(filename
+1,'/');
2982 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2984 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2986 display_playlist_count(count
, count_str
, false);
2988 if (position
== PLAYLIST_REPLACE
)
2990 if (playlist_remove_all_tracks(playlist
) == 0)
2991 position
= PLAYLIST_INSERT_LAST
;
2997 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3000 if (action_userabort(TIMEOUT_NOBLOCK
))
3003 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3009 /* Use trackname as a temporay buffer. Note that trackname must
3010 * be as large as temp_buf.
3012 max
= convert_m3u(temp_buf
, max
, sizeof(temp_buf
), trackname
);
3015 /* we need to format so that relative paths are correctly
3017 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3024 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3033 /* Make sure tracks are inserted in correct order if user
3034 requests INSERT_FIRST */
3035 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3036 position
= insert_pos
+ 1;
3040 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3042 display_playlist_count(count
, count_str
, false);
3044 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3045 (audio_status() & AUDIO_STATUS_PLAY
) &&
3047 audio_flush_and_reload_tracks();
3051 /* let the other threads work */
3060 sync_control(playlist
, false);
3064 display_playlist_count(count
, count_str
, true);
3066 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3067 audio_flush_and_reload_tracks();
3069 #ifdef HAVE_DIRCACHE
3070 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3077 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3078 * we want to delete the current playing track.
3080 int playlist_delete(struct playlist_info
* playlist
, int index
)
3085 playlist
= ¤t_playlist
;
3087 if (check_control(playlist
) < 0)
3089 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3093 if (index
== PLAYLIST_DELETE_CURRENT
)
3094 index
= playlist
->index
;
3096 result
= remove_track_from_playlist(playlist
, index
, true);
3098 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3100 audio_flush_and_reload_tracks();
3106 * Move track at index to new_index. Tracks between the two are shifted
3107 * appropriately. Returns 0 on success and -1 on failure.
3109 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3115 bool current
= false;
3117 char filename
[MAX_PATH
];
3120 playlist
= ¤t_playlist
;
3122 if (check_control(playlist
) < 0)
3124 splash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3128 if (index
== new_index
)
3131 if (index
== playlist
->index
)
3132 /* Moving the current track */
3135 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3136 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3137 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3139 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3140 sizeof(filename
)) < 0)
3143 /* Delete track from original position */
3144 result
= remove_track_from_playlist(playlist
, index
, true);
3148 /* We want to insert the track at the position that was specified by
3149 new_index. This may be different then new_index because of the
3150 shifting that occurred after the delete */
3151 r
= rotate_index(playlist
, new_index
);
3155 new_index
= PLAYLIST_PREPEND
;
3156 else if (r
== playlist
->amount
)
3158 new_index
= PLAYLIST_INSERT_LAST
;
3160 /* Calculate index of desired position */
3161 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3163 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3170 /* Moved the current track */
3173 case PLAYLIST_PREPEND
:
3174 playlist
->index
= playlist
->first_index
;
3176 case PLAYLIST_INSERT_LAST
:
3177 playlist
->index
= playlist
->first_index
- 1;
3178 if (playlist
->index
< 0)
3179 playlist
->index
+= playlist
->amount
;
3182 playlist
->index
= new_index
;
3187 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3188 audio_flush_and_reload_tracks();
3192 #ifdef HAVE_DIRCACHE
3193 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3199 /* shuffle currently playing playlist */
3200 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3206 playlist
= ¤t_playlist
;
3208 check_control(playlist
);
3210 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3212 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3214 audio_flush_and_reload_tracks();
3219 /* sort currently playing playlist */
3220 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3225 playlist
= ¤t_playlist
;
3227 check_control(playlist
);
3229 result
= sort_playlist(playlist
, start_current
, true);
3231 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3233 audio_flush_and_reload_tracks();
3238 /* returns true if playlist has been modified */
3239 bool playlist_modified(const struct playlist_info
* playlist
)
3242 playlist
= ¤t_playlist
;
3244 if (playlist
->shuffle_modified
||
3245 playlist
->deleted
||
3246 playlist
->num_inserted_tracks
> 0)
3252 /* returns index of first track in playlist */
3253 int playlist_get_first_index(const struct playlist_info
* playlist
)
3256 playlist
= ¤t_playlist
;
3258 return playlist
->first_index
;
3261 /* returns shuffle seed of playlist */
3262 int playlist_get_seed(const struct playlist_info
* playlist
)
3265 playlist
= ¤t_playlist
;
3267 return playlist
->seed
;
3270 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3271 int playlist_amount_ex(const struct playlist_info
* playlist
)
3274 playlist
= ¤t_playlist
;
3276 return playlist
->amount
;
3279 /* returns full path of playlist (minus extension) */
3280 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3286 playlist
= ¤t_playlist
;
3288 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3293 /* Remove extension */
3294 sep
= strrchr(buf
, '.');
3301 /* returns the playlist filename */
3302 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3306 playlist
= ¤t_playlist
;
3308 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3316 /* Fills info structure with information about track at specified index.
3317 Returns 0 on success and -1 on failure */
3318 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3319 struct playlist_track_info
* info
)
3325 playlist
= ¤t_playlist
;
3327 if (index
< 0 || index
>= playlist
->amount
)
3330 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3331 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3333 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3334 sizeof(info
->filename
)) < 0)
3341 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3342 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3344 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3348 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3349 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3351 info
->index
= index
;
3352 info
->display_index
= rotate_index(playlist
, index
) + 1;
3357 /* save the current dynamic playlist to specified file */
3358 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3363 char path
[MAX_PATH
+1];
3364 char tmp_buf
[MAX_PATH
+1];
3366 bool overwrite_current
= false;
3367 int* index_buf
= NULL
;
3370 playlist
= ¤t_playlist
;
3372 if (playlist
->amount
<= 0)
3375 /* use current working directory as base for pathname */
3376 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3377 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3380 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3382 /* Attempting to overwrite current playlist file.*/
3384 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3386 /* not enough buffer space to store updated indices */
3387 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
);
3411 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3415 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3419 index
= playlist
->first_index
;
3420 for (i
=0; i
<playlist
->amount
; i
++)
3427 if (action_userabort(TIMEOUT_NOBLOCK
))
3433 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3434 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3435 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3437 /* Don't save queued files */
3440 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3447 if (overwrite_current
)
3448 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3450 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3452 splash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3459 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3460 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3466 index
= (index
+1)%playlist
->amount
;
3469 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3473 if (overwrite_current
&& result
>= 0)
3477 mutex_lock(&playlist
->control_mutex
);
3479 /* Replace the current playlist with the new one and update indices */
3480 close(playlist
->fd
);
3481 if (remove(playlist
->filename
) >= 0)
3483 if (rename(path
, playlist
->filename
) >= 0)
3485 playlist
->fd
= open_utf8(playlist
->filename
, O_RDONLY
);
3486 if (playlist
->fd
>= 0)
3488 index
= playlist
->first_index
;
3489 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3491 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3493 playlist
->indices
[index
] = index_buf
[count
];
3496 index
= (index
+1)%playlist
->amount
;
3499 /* we need to recreate control because inserted tracks are
3500 now part of the playlist and shuffle has been
3502 result
= recreate_control(playlist
);
3507 mutex_unlock(&playlist
->control_mutex
);
3517 * Search specified directory for tracks and notify via callback. May be
3518 * called recursively.
3520 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3521 int (*callback
)(char*, void*),
3524 char buf
[MAX_PATH
+1];
3528 struct entry
*files
;
3529 struct tree_context
* tc
= tree_get_context();
3530 int old_dirfilter
= *(tc
->dirfilter
);
3535 /* use the tree browser dircache to load files */
3536 *(tc
->dirfilter
) = SHOW_ALL
;
3538 if (ft_load(tc
, dirname
) < 0)
3540 splash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3541 *(tc
->dirfilter
) = old_dirfilter
;
3545 files
= (struct entry
*) tc
->dircache
;
3546 num_files
= tc
->filesindir
;
3548 /* we've overwritten the dircache so tree browser will need to be
3552 for (i
=0; i
<num_files
; i
++)
3555 if (action_userabort(TIMEOUT_NOBLOCK
))
3561 if (files
[i
].attr
& ATTR_DIRECTORY
)
3565 /* recursively add directories */
3566 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3567 result
= playlist_directory_tracksearch(buf
, recurse
,
3572 /* we now need to reload our current directory */
3573 if(ft_load(tc
, dirname
) < 0)
3579 files
= (struct entry
*) tc
->dircache
;
3580 num_files
= tc
->filesindir
;
3590 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3592 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3594 if (callback(buf
, context
) != 0)
3600 /* let the other threads work */
3605 /* restore dirfilter */
3606 *(tc
->dirfilter
) = old_dirfilter
;