1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by wavey@wavey.org
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 Dynamic playlist design (based on design originally proposed by ricII)
23 There are two files associated with a dynamic playlist:
24 1. Playlist file : This file contains the initial songs in the playlist.
25 The file is created by the user and stored on the hard
26 drive. NOTE: If we are playing the contents of a
27 directory, there will be no playlist file.
28 2. Control file : This file is automatically created when a playlist is
29 started and contains all the commands done to it.
31 The first non-comment line in a control file must begin with
32 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
33 DIR is the directory where the playlist is located and FILE is the
34 playlist filename. For dirplay, FILE will be empty. An empty playlist
35 will have both entries as null.
37 Control file commands:
38 a. Add track (A:<position>:<last position>:<path to track>)
39 - Insert a track at the specified position in the current
40 playlist. Last position is used to specify where last insertion
42 b. Queue track (Q:<position>:<last position>:<path to track>)
43 - Queue a track at the specified position in the current
44 playlist. Queued tracks differ from added tracks in that they
45 are deleted from the playlist as soon as they are played and
46 they are not saved to disk as part of the playlist.
47 c. Delete track (D:<position>)
48 - Delete track from specified position in the current playlist.
49 d. Shuffle playlist (S:<seed>:<index>)
50 - Shuffle entire playlist with specified seed. The index
51 identifies the first index in the newly shuffled playlist
52 (needed for repeat mode).
53 e. Unshuffle playlist (U:<index>)
54 - Unshuffle entire playlist. The index identifies the first index
55 in the newly unshuffled playlist.
56 f. Reset last insert position (R)
57 - Needed so that insertions work properly after resume
60 The only resume info that needs to be saved is the current index in the
61 playlist and the position in the track. When resuming, all the commands
62 in the control file will be reapplied so that the playlist indices are
63 exactly the same as before shutdown. To avoid unnecessary disk
64 accesses, the shuffle mode settings are also saved in settings and only
65 flushed to disk when required.
83 #include "applimits.h"
93 #include "filetypes.h"
94 #ifdef HAVE_LCD_BITMAP
101 #include "rbunicode.h"
103 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
104 #define PLAYLIST_CONTROL_FILE_VERSION 2
107 Each playlist index has a flag associated with it which identifies what
108 type of track it is. These flags are stored in the 4 high order bits of
111 NOTE: This limits the playlist file size to a max of 256M.
115 01 = Track was prepended into playlist
116 10 = Track was inserted into playlist
117 11 = Track was appended into playlist
122 0 = Track entry is valid
123 1 = Track does not exist on disk and should be skipped
125 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
126 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
127 #define PLAYLIST_QUEUE_MASK 0x20000000
129 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
130 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
131 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
133 #define PLAYLIST_QUEUED 0x20000000
134 #define PLAYLIST_SKIPPED 0x10000000
136 #define PLAYLIST_DISPLAY_COUNT 10
138 struct directory_search_context
{
139 struct playlist_info
* playlist
;
145 static bool changing_dir
= false;
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
, char *subdir
);
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";
203 * remove any files and indices associated with the playlist
205 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
207 playlist
->filename
[0] = '\0';
208 playlist
->utf8
= true;
210 if(playlist
->fd
>= 0)
211 /* If there is an already open playlist, close it. */
215 if(playlist
->control_fd
>= 0)
216 close(playlist
->control_fd
);
217 playlist
->control_fd
= -1;
218 playlist
->control_created
= false;
220 playlist
->in_ram
= false;
222 if (playlist
->buffer
)
223 playlist
->buffer
[0] = 0;
225 playlist
->buffer_end_pos
= 0;
228 playlist
->first_index
= 0;
229 playlist
->amount
= 0;
230 playlist
->last_insert_pos
= -1;
232 playlist
->shuffle_modified
= false;
233 playlist
->deleted
= false;
234 playlist
->num_inserted_tracks
= 0;
235 playlist
->started
= false;
237 playlist
->num_cached
= 0;
238 playlist
->pending_control_sync
= false;
240 if (!resume
&& playlist
->current
)
242 /* start with fresh playlist control file when starting new
244 create_control(playlist
);
246 /* Reset resume settings */
247 global_status
.resume_first_index
= 0;
248 global_status
.resume_seed
= -1;
253 * Initialize a new playlist for viewing/editing/playing. dir is the
254 * directory where the playlist is located and file is the filename.
256 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
259 empty_playlist(playlist
, false);
265 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
266 playlist
->in_ram
= true;
268 dir
= ""; /* empty playlist */
271 update_playlist_filename(playlist
, dir
, file
);
273 if (playlist
->control_fd
>= 0)
275 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
276 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
277 sync_control(playlist
, false);
282 * create control file for playlist
284 static void create_control(struct playlist_info
* playlist
)
286 playlist
->control_fd
= open(playlist
->control_filename
,
287 O_CREAT
|O_RDWR
|O_TRUNC
);
288 if (playlist
->control_fd
< 0)
290 if (check_rockboxdir())
292 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
293 gui_syncsplash(HZ
*2, (unsigned char *)"%s (%d)",
294 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
295 playlist
->control_fd
);
297 playlist
->control_created
= false;
301 playlist
->control_created
= true;
306 * validate the control file. This may include creating/initializing it if
309 static int check_control(struct playlist_info
* playlist
)
311 if (!playlist
->control_created
)
313 create_control(playlist
);
315 if (playlist
->control_fd
>= 0)
317 char* dir
= playlist
->filename
;
318 char* file
= playlist
->filename
+playlist
->dirlen
;
319 char c
= playlist
->filename
[playlist
->dirlen
-1];
321 playlist
->filename
[playlist
->dirlen
-1] = '\0';
323 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
324 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
325 sync_control(playlist
, false);
326 playlist
->filename
[playlist
->dirlen
-1] = c
;
330 if (playlist
->control_fd
< 0)
337 * recreate the control file based on current playlist entries
339 static int recreate_control(struct playlist_info
* playlist
)
341 char temp_file
[MAX_PATH
+1];
346 if(playlist
->control_fd
>= 0)
348 char* dir
= playlist
->filename
;
349 char* file
= playlist
->filename
+playlist
->dirlen
;
350 char c
= playlist
->filename
[playlist
->dirlen
-1];
352 close(playlist
->control_fd
);
354 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
355 playlist
->control_filename
);
357 if (rename(playlist
->control_filename
, temp_file
) < 0)
360 temp_fd
= open(temp_file
, O_RDONLY
);
364 playlist
->control_fd
= open(playlist
->control_filename
,
365 O_CREAT
|O_RDWR
|O_TRUNC
);
366 if (playlist
->control_fd
< 0)
369 playlist
->filename
[playlist
->dirlen
-1] = '\0';
371 /* cannot call update_control() because of mutex */
372 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
373 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
375 playlist
->filename
[playlist
->dirlen
-1] = c
;
385 playlist
->shuffle_modified
= false;
386 playlist
->deleted
= false;
387 playlist
->num_inserted_tracks
= 0;
389 if (playlist
->current
)
391 global_status
.resume_seed
= -1;
395 for (i
=0; i
<playlist
->amount
; i
++)
397 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
399 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
400 char inserted_file
[MAX_PATH
+1];
402 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
404 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
406 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
407 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
410 /* save the position in file where name is written */
411 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
413 result
= fdprintf(playlist
->control_fd
, "%s\n",
416 playlist
->indices
[i
] =
417 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
423 playlist
->num_inserted_tracks
++;
429 fsync(playlist
->control_fd
);
438 * store directory and name of playlist file
440 static void update_playlist_filename(struct playlist_info
* playlist
,
441 const char *dir
, const char *file
)
444 int dirlen
= strlen(dir
);
445 int filelen
= strlen(file
);
447 /* Default to utf8 unless explicitly told otherwise. */
448 playlist
->utf8
= !(filelen
> 4 && strcasecmp(&file
[filelen
- 4], ".m3u") == 0);
450 /* If the dir does not end in trailing slash, we use a separator.
451 Otherwise we don't. */
452 if('/' != dir
[dirlen
-1])
458 playlist
->dirlen
= dirlen
;
460 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
461 "%s%s%s", dir
, sep
, file
);
465 * calculate track offsets within a playlist file
467 static int add_indices_to_playlist(struct playlist_info
* playlist
,
468 char* buffer
, size_t buflen
)
472 unsigned int count
= 0;
477 if(-1 == playlist
->fd
)
478 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
480 return -1; /* failure */
482 #ifdef HAVE_LCD_BITMAP
483 if(global_settings
.statusbar
)
484 lcd_setmargins(0, STATUSBAR_HEIGHT
);
486 lcd_setmargins(0, 0);
488 gui_syncsplash(0, ID2P(LANG_WAIT
));
492 /* use mp3 buffer for maximum load speed */
494 #if CONFIG_CODEC != SWCODEC
495 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
496 buflen
= (audiobufend
- audiobuf
);
497 buffer
= (char *)audiobuf
;
499 buffer
= (char *)audio_get_buffer(false, &buflen
);
507 nread
= read(playlist
->fd
, buffer
, buflen
);
508 /* Terminate on EOF */
512 p
= (unsigned char *)buffer
;
514 /* utf8 BOM at beginning of file? */
515 if(i
== 0 && nread
> 3
516 && *p
== 0xef && *(p
+1) == 0xbb && *(p
+2) == 0xbf) {
520 playlist
->utf8
= true; /* Override any earlier indication. */
523 for(count
=0; count
< nread
; count
++,p
++) {
525 /* Are we on a new line? */
526 if((*p
== '\n') || (*p
== '\r'))
536 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
537 display_buffer_full();
542 /* Store a new entry */
543 playlist
->indices
[ playlist
->amount
] = i
+count
;
545 if (playlist
->filenames
)
546 playlist
->filenames
[ playlist
->amount
] = NULL
;
558 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
565 * Removes all tracks, from the playlist, leaving the presently playing
568 int remove_all_tracks(struct playlist_info
*playlist
)
572 if (playlist
== NULL
)
573 playlist
= ¤t_playlist
;
575 while (playlist
->index
> 0)
576 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
579 while (playlist
->amount
> 1)
580 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
583 if (playlist
->amount
== 1) {
584 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
592 * Add track to playlist at specified position. There are five special
593 * positions that can be specified:
594 * PLAYLIST_PREPEND - Add track at beginning of playlist
595 * PLAYLIST_INSERT - Add track after current song. NOTE: If
596 * there are already inserted tracks then track
597 * is added to the end of the insertion list
598 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
599 * matter what other tracks have been inserted
600 * PLAYLIST_INSERT_LAST - Add track to end of playlist
601 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
602 * current playing track and end of playlist
603 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
604 * and inster this track at the end.
606 static int add_track_to_playlist(struct playlist_info
* playlist
,
607 const char *filename
, int position
,
608 bool queue
, int seek_pos
)
610 int insert_position
, orig_position
;
611 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
614 insert_position
= orig_position
= position
;
616 if (playlist
->amount
>= playlist
->max_playlist_size
)
618 display_buffer_full();
624 case PLAYLIST_PREPEND
:
625 position
= insert_position
= playlist
->first_index
;
627 case PLAYLIST_INSERT
:
628 /* if there are already inserted tracks then add track to end of
629 insertion list else add after current playing track */
630 if (playlist
->last_insert_pos
>= 0 &&
631 playlist
->last_insert_pos
< playlist
->amount
&&
632 (playlist
->indices
[playlist
->last_insert_pos
]&
633 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
634 position
= insert_position
= playlist
->last_insert_pos
+1;
635 else if (playlist
->amount
> 0)
636 position
= insert_position
= playlist
->index
+ 1;
638 position
= insert_position
= 0;
640 if (playlist
->started
)
641 playlist
->last_insert_pos
= position
;
643 case PLAYLIST_INSERT_FIRST
:
644 if (playlist
->amount
> 0)
645 position
= insert_position
= playlist
->index
+ 1;
647 position
= insert_position
= 0;
649 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
650 playlist
->last_insert_pos
= position
;
652 case PLAYLIST_INSERT_LAST
:
653 if (playlist
->first_index
> 0)
654 position
= insert_position
= playlist
->first_index
;
656 position
= insert_position
= playlist
->amount
;
658 case PLAYLIST_INSERT_SHUFFLED
:
660 if (playlist
->started
)
663 int n
= playlist
->amount
-
664 rotate_index(playlist
, playlist
->index
);
671 position
= playlist
->index
+ offset
+ 1;
672 if (position
>= playlist
->amount
)
673 position
-= playlist
->amount
;
675 insert_position
= position
;
678 position
= insert_position
= (rand() % (playlist
->amount
+1));
681 case PLAYLIST_REPLACE
:
682 if (remove_all_tracks(playlist
) < 0)
685 position
= insert_position
= playlist
->index
+ 1;
690 flags
|= PLAYLIST_QUEUED
;
692 /* shift indices so that track can be added */
693 for (i
=playlist
->amount
; i
>insert_position
; i
--)
695 playlist
->indices
[i
] = playlist
->indices
[i
-1];
697 if (playlist
->filenames
)
698 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
702 /* update stored indices if needed */
703 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
707 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
708 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
710 playlist
->first_index
++;
712 if (seek_pos
< 0 && playlist
->current
)
714 global_status
.resume_first_index
= playlist
->first_index
;
719 if (insert_position
< playlist
->last_insert_pos
||
720 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
721 playlist
->last_insert_pos
++;
723 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
725 int result
= update_control(playlist
,
726 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
727 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
733 playlist
->indices
[insert_position
] = flags
| seek_pos
;
736 if (playlist
->filenames
)
737 playlist
->filenames
[insert_position
] = NULL
;
741 playlist
->num_inserted_tracks
++;
743 return insert_position
;
747 * Callback for playlist_directory_tracksearch to insert track into
750 static int directory_search_callback(char* filename
, void* context
)
752 struct directory_search_context
* c
=
753 (struct directory_search_context
*) context
;
756 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
764 /* Make sure tracks are inserted in correct order if user requests
766 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
767 c
->position
= insert_pos
+ 1;
769 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
771 unsigned char* count_str
;
774 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
776 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
778 display_playlist_count(c
->count
, count_str
, false);
780 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
781 (audio_status() & AUDIO_STATUS_PLAY
) &&
782 c
->playlist
->started
)
783 audio_flush_and_reload_tracks();
790 * remove track at specified position
792 static int remove_track_from_playlist(struct playlist_info
* playlist
,
793 int position
, bool write
)
798 if (playlist
->amount
<= 0)
801 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
803 /* shift indices now that track has been removed */
804 for (i
=position
; i
<playlist
->amount
; i
++)
806 playlist
->indices
[i
] = playlist
->indices
[i
+1];
808 if (playlist
->filenames
)
809 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
816 playlist
->num_inserted_tracks
--;
818 playlist
->deleted
= true;
820 /* update stored indices if needed */
821 if (position
< playlist
->index
)
824 if (position
< playlist
->first_index
)
826 playlist
->first_index
--;
830 global_status
.resume_first_index
= playlist
->first_index
;
835 if (position
<= playlist
->last_insert_pos
)
836 playlist
->last_insert_pos
--;
838 if (write
&& playlist
->control_fd
>= 0)
840 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
841 position
, -1, NULL
, NULL
, NULL
);
846 sync_control(playlist
, false);
853 * randomly rearrange the array of indices for the playlist. If start_current
854 * is true then update the index to the new index of the current playing track
856 static int randomise_playlist(struct playlist_info
* playlist
,
857 unsigned int seed
, bool start_current
,
863 unsigned int current
= playlist
->indices
[playlist
->index
];
865 /* seed 0 is used to identify sorted playlist for resume purposes */
869 /* seed with the given seed */
872 /* randomise entire indices list */
873 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
875 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
876 candidate
= rand() % (count
+ 1);
878 /* now swap the values at the 'count' and 'candidate' positions */
879 store
= playlist
->indices
[candidate
];
880 playlist
->indices
[candidate
] = playlist
->indices
[count
];
881 playlist
->indices
[count
] = store
;
883 if (playlist
->filenames
)
885 store
= (long)playlist
->filenames
[candidate
];
886 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
887 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
893 find_and_set_playlist_index(playlist
, current
);
895 /* indices have been moved so last insert position is no longer valid */
896 playlist
->last_insert_pos
= -1;
898 playlist
->seed
= seed
;
899 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
900 playlist
->shuffle_modified
= true;
904 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
905 playlist
->first_index
, NULL
, NULL
, NULL
);
906 global_status
.resume_seed
= seed
;
914 * Sort the array of indices for the playlist. If start_current is true then
915 * set the index to the new index of the current song.
917 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
920 unsigned int current
= playlist
->indices
[playlist
->index
];
922 if (playlist
->amount
> 0)
923 qsort(playlist
->indices
, playlist
->amount
,
924 sizeof(playlist
->indices
[0]), compare
);
927 /** We need to re-check the song names from disk because qsort can't
928 * sort two arrays at once :/
929 * FIXME: Please implement a better way to do this. */
930 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
931 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
935 find_and_set_playlist_index(playlist
, current
);
937 /* indices have been moved so last insert position is no longer valid */
938 playlist
->last_insert_pos
= -1;
940 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
941 playlist
->shuffle_modified
= false;
942 if (write
&& playlist
->control_fd
>= 0)
944 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
945 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
946 global_status
.resume_seed
= 0;
953 /* Calculate how many steps we have to really step when skipping entries
956 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
958 int i
, count
, direction
;
960 int stepped_count
= 0;
973 index
= playlist
->index
;
978 index
+= playlist
->amount
;
979 if (index
>= playlist
->amount
)
980 index
-= playlist
->amount
;
982 /* Check if we found a bad entry. */
983 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
986 /* Are all entries bad? */
987 if (stepped_count
++ > playlist
->amount
)
994 } while (i
<= count
);
999 /* Marks the index of the track to be skipped that is "steps" away from
1000 * current playing track.
1002 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1006 if (playlist
== NULL
)
1007 playlist
= ¤t_playlist
;
1009 /* need to account for already skipped tracks */
1010 steps
= calculate_step_count(playlist
, steps
);
1012 index
= playlist
->index
+ steps
;
1014 index
+= playlist
->amount
;
1015 else if (index
>= playlist
->amount
)
1016 index
-= playlist
->amount
;
1018 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1022 * returns the index of the track that is "steps" away from current playing
1025 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1028 int current_index
= playlist
->index
;
1029 int next_index
= -1;
1031 if (playlist
->amount
<= 0)
1034 if (repeat_mode
== -1)
1035 repeat_mode
= global_settings
.repeat_mode
;
1037 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1038 repeat_mode
= REPEAT_ALL
;
1040 steps
= calculate_step_count(playlist
, steps
);
1041 switch (repeat_mode
)
1043 case REPEAT_SHUFFLE
:
1044 /* Treat repeat shuffle just like repeat off. At end of playlist,
1045 play will be resumed in playlist_next() */
1048 current_index
= rotate_index(playlist
, current_index
);
1049 next_index
= current_index
+steps
;
1050 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1053 next_index
= (next_index
+playlist
->first_index
) %
1060 #ifdef AB_REPEAT_ENABLE
1063 next_index
= current_index
;
1069 next_index
= (current_index
+steps
) % playlist
->amount
;
1070 while (next_index
< 0)
1071 next_index
+= playlist
->amount
;
1073 if (steps
>= playlist
->amount
)
1080 /* second time around so skip the queued files */
1081 for (i
=0; i
<playlist
->amount
; i
++)
1083 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1084 index
= (index
+1) % playlist
->amount
;
1096 /* No luck if the whole playlist was bad. */
1097 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1104 * Search for the seek track and set appropriate indices. Used after shuffle
1105 * to make sure the current index is still pointing to correct track.
1107 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1112 /* Set the index to the current song */
1113 for (i
=0; i
<playlist
->amount
; i
++)
1115 if (playlist
->indices
[i
] == seek
)
1117 playlist
->index
= playlist
->first_index
= i
;
1119 if (playlist
->current
)
1121 global_status
.resume_first_index
= i
;
1131 * used to sort track indices. Sort order is as follows:
1132 * 1. Prepended tracks (in prepend order)
1133 * 2. Playlist/directory tracks (in playlist order)
1134 * 3. Inserted/Appended tracks (in insert order)
1136 static int compare(const void* p1
, const void* p2
)
1138 unsigned long* e1
= (unsigned long*) p1
;
1139 unsigned long* e2
= (unsigned long*) p2
;
1140 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1141 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1143 if (flags1
== flags2
)
1144 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1145 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1146 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1148 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1149 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1151 else if (flags1
&& flags2
)
1152 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1157 #ifdef HAVE_DIRCACHE
1159 * Thread to update filename pointers to dircache on background
1160 * without affecting playlist load up performance. This thread also flushes
1161 * any pending control commands when the disk spins up.
1163 static void playlist_thread(void)
1166 bool dirty_pointers
= false;
1167 static char tmp
[MAX_PATH
+1];
1169 struct playlist_info
*playlist
;
1176 #ifndef HAVE_FLASH_STORAGE
1177 if (global_settings
.disk_spindown
> 1 &&
1178 global_settings
.disk_spindown
<= 5)
1179 sleep_time
= global_settings
.disk_spindown
- 1;
1184 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1188 case PLAYLIST_LOAD_POINTERS
:
1189 dirty_pointers
= true;
1192 /* Start the background scanning after either the disk spindown
1193 timeout or 5s, whichever is less */
1195 playlist
= ¤t_playlist
;
1197 if (playlist
->control_fd
>= 0
1199 && ata_disk_is_active()
1203 if (playlist
->num_cached
> 0)
1205 mutex_lock(&playlist
->control_mutex
);
1206 flush_cached_control(playlist
);
1207 mutex_unlock(&playlist
->control_mutex
);
1210 sync_control(playlist
, true);
1213 if (!dirty_pointers
)
1216 if (!dircache_is_enabled() || !playlist
->filenames
1217 || playlist
->amount
<= 0)
1220 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1223 for (index
= 0; index
< playlist
->amount
1224 && queue_empty(&playlist_queue
); index
++)
1226 /* Process only pointers that are not already loaded. */
1227 if (playlist
->filenames
[index
])
1230 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1231 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1233 /* Load the filename from playlist file. */
1234 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1238 /* Set the dircache entry pointer. */
1239 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1241 /* And be on background so user doesn't notice any delays. */
1245 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1248 dirty_pointers
= false;
1252 case SYS_USB_CONNECTED
:
1253 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1254 usb_wait_for_disconnect(&playlist_queue
);
1263 * gets pathname for track at seek index
1265 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1266 bool control_file
, char *buf
, int buf_length
)
1270 char tmp_buf
[MAX_PATH
+1];
1271 char dir_buf
[MAX_PATH
+1];
1273 if (buf_length
> MAX_PATH
+1)
1274 buf_length
= MAX_PATH
+1;
1276 #ifdef HAVE_DIRCACHE
1277 if (dircache_is_enabled() && playlist
->filenames
)
1279 if (playlist
->filenames
[index
] != NULL
)
1281 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1282 max
= strlen(tmp_buf
) + 1;
1289 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1291 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1292 tmp_buf
[MAX_PATH
] = '\0';
1293 max
= strlen(tmp_buf
) + 1;
1297 mutex_lock(&playlist
->control_mutex
);
1300 fd
= playlist
->control_fd
;
1303 if(-1 == playlist
->fd
)
1304 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1312 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1316 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1318 if ((max
> 0) && !playlist
->utf8
)
1324 while ((tmp_buf
[i
] != '\n') && (tmp_buf
[i
] != '\r')
1330 /* Now work back killing white space. */
1331 while ((i
> 0) && isspace(tmp_buf
[i
- 1]))
1336 /* Borrow dir_buf a little... */
1337 /* TODO: iso_decode can overflow dir_buf; it really
1338 * should take a dest size argument.
1340 end
= iso_decode(tmp_buf
, dir_buf
, -1, i
);
1342 strncpy(tmp_buf
, dir_buf
, sizeof(tmp_buf
));
1343 tmp_buf
[sizeof(tmp_buf
) - 1] = 0;
1344 max
= strlen(tmp_buf
);
1349 mutex_unlock(&playlist
->control_mutex
);
1354 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1356 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1362 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1363 dir_buf
[playlist
->dirlen
-1] = 0;
1365 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1368 static int get_next_directory(char *dir
){
1369 return get_next_dir(dir
,true,false);
1372 static int get_previous_directory(char *dir
){
1373 return get_next_dir(dir
,false,false);
1377 * search through all the directories (starting with the current) to find
1378 * one that has tracks to play
1380 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1382 struct playlist_info
* playlist
= ¤t_playlist
;
1384 int sort_dir
= global_settings
.sort_dir
;
1385 char *start_dir
= NULL
;
1387 struct tree_context
* tc
= tree_get_context();
1388 int dirfilter
= *(tc
->dirfilter
);
1390 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1392 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1393 char buffer
[MAX_PATH
];
1394 int folder_count
= 0,i
;
1395 srand(current_tick
);
1396 *(tc
->dirfilter
) = SHOW_MUSIC
;
1399 read(fd
,&folder_count
,sizeof(int));
1402 i
= rand()%folder_count
;
1403 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1404 read(fd
,buffer
,MAX_PATH
);
1405 if (check_subdir_for_music(buffer
,"") ==0)
1410 *(tc
->dirfilter
) = dirfilter
;
1415 /* not random folder advance */
1417 /* start with root */
1421 /* start with current directory */
1422 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1423 dir
[playlist
->dirlen
-1] = '\0';
1426 /* use the tree browser dircache to load files */
1427 *(tc
->dirfilter
) = SHOW_ALL
;
1429 /* sort in another direction if previous dir is requested */
1431 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1432 global_settings
.sort_dir
= 4;
1433 else if (global_settings
.sort_dir
== 1)
1434 global_settings
.sort_dir
= 2;
1435 else if (global_settings
.sort_dir
== 2)
1436 global_settings
.sort_dir
= 1;
1437 else if (global_settings
.sort_dir
== 4)
1438 global_settings
.sort_dir
= 0;
1443 struct entry
*files
;
1447 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1449 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1455 files
= (struct entry
*) tc
->dircache
;
1456 num_files
= tc
->filesindir
;
1458 for (i
=0; i
<num_files
; i
++)
1461 if (action_userabort(TIMEOUT_NOBLOCK
))
1468 if (files
[i
].attr
& ATTR_DIRECTORY
)
1472 result
= check_subdir_for_music(dir
, files
[i
].name
);
1479 else if (!strcmp(start_dir
, files
[i
].name
))
1486 /* move down to parent directory. current directory name is
1487 stored as the starting point for the search in parent */
1488 start_dir
= strrchr(dir
, '/');
1499 /* we've overwritten the dircache so tree browser will need to be
1503 /* restore dirfilter & sort_dir */
1504 *(tc
->dirfilter
) = dirfilter
;
1505 global_settings
.sort_dir
= sort_dir
;
1507 /* special case if nothing found: try start searching again from root */
1508 if (result
== -1 && !recursion
){
1509 result
= get_next_dir(dir
,is_forward
, true);
1516 * Checks if there are any music files in the dir or any of its
1517 * subdirectories. May be called recursively.
1519 static int check_subdir_for_music(char *dir
, char *subdir
)
1522 int dirlen
= strlen(dir
);
1525 struct entry
*files
;
1526 bool has_music
= false;
1527 bool has_subdir
= false;
1528 struct tree_context
* tc
= tree_get_context();
1530 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1532 if (ft_load(tc
, dir
) < 0)
1534 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1538 files
= (struct entry
*) tc
->dircache
;
1539 num_files
= tc
->filesindir
;
1541 for (i
=0; i
<num_files
; i
++)
1543 if (files
[i
].attr
& ATTR_DIRECTORY
)
1545 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1557 for (i
=0; i
<num_files
; i
++)
1559 if (action_userabort(TIMEOUT_NOBLOCK
))
1565 if (files
[i
].attr
& ATTR_DIRECTORY
)
1567 result
= check_subdir_for_music(dir
, files
[i
].name
);
1585 /* we now need to reload our current directory */
1586 if(ft_load(tc
, dir
) < 0)
1587 gui_syncsplash(HZ
*2,
1588 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1595 * Returns absolute path of track
1597 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1604 /* Zero-terminate the file name */
1605 while((src
[i
] != '\n') &&
1610 /* Now work back killing white space */
1611 while((src
[i
-1] == ' ') ||
1617 /* replace backslashes with forward slashes */
1618 for ( j
=0; j
<i
; j
++ )
1619 if ( src
[j
] == '\\' )
1624 strncpy(dest
, src
, buf_length
);
1628 /* handle dos style drive letter */
1630 strncpy(dest
, &src
[2], buf_length
);
1631 else if (!strncmp(src
, "../", 3))
1633 /* handle relative paths */
1635 while(!strncmp(&src
[i
], "../", 3))
1637 for (j
=0; j
<i
/3; j
++) {
1638 temp_ptr
= strrchr(dir
, '/');
1644 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1646 else if ( '.' == src
[0] && '/' == src
[1] ) {
1647 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1650 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1658 * Display splash message showing progress of playlist/directory insertion or
1661 static void display_playlist_count(int count
, const unsigned char *fmt
,
1664 static long talked_tick
= 0;
1665 long id
= P2ID(fmt
);
1666 if(talk_menus_enabled() && id
>=0)
1668 if(final
|| (count
&& (talked_tick
== 0
1669 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1671 talked_tick
= current_tick
;
1672 talk_number(count
, false);
1678 lcd_clear_display();
1680 #ifdef HAVE_LCD_BITMAP
1681 if(global_settings
.statusbar
)
1682 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1684 lcd_setmargins(0, 0);
1687 gui_syncsplash(0, fmt
, count
, str(LANG_OFF_ABORT
));
1691 * Display buffer full message
1693 static void display_buffer_full(void)
1695 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1699 * Flush any cached control commands to disk. Called when playlist is being
1700 * modified. Returns 0 on success and -1 on failure.
1702 static int flush_cached_control(struct playlist_info
* playlist
)
1707 if (!playlist
->num_cached
)
1710 lseek(playlist
->control_fd
, 0, SEEK_END
);
1712 for (i
=0; i
<playlist
->num_cached
; i
++)
1714 struct playlist_control_cache
* cache
=
1715 &(playlist
->control_cache
[i
]);
1717 switch (cache
->command
)
1719 case PLAYLIST_COMMAND_PLAYLIST
:
1720 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1721 cache
->i1
, cache
->s1
, cache
->s2
);
1723 case PLAYLIST_COMMAND_ADD
:
1724 case PLAYLIST_COMMAND_QUEUE
:
1725 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1726 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1727 cache
->i1
, cache
->i2
);
1730 /* save the position in file where name is written */
1731 int* seek_pos
= (int *)cache
->data
;
1732 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1733 result
= fdprintf(playlist
->control_fd
, "%s\n",
1737 case PLAYLIST_COMMAND_DELETE
:
1738 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1740 case PLAYLIST_COMMAND_SHUFFLE
:
1741 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1742 cache
->i1
, cache
->i2
);
1744 case PLAYLIST_COMMAND_UNSHUFFLE
:
1745 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1747 case PLAYLIST_COMMAND_RESET
:
1748 result
= fdprintf(playlist
->control_fd
, "R\n");
1760 if (global_status
.resume_seed
>= 0)
1762 global_status
.resume_seed
= -1;
1766 playlist
->num_cached
= 0;
1767 playlist
->pending_control_sync
= true;
1774 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1781 * Update control data with new command. Depending on the command, it may be
1782 * cached or flushed to disk.
1784 static int update_control(struct playlist_info
* playlist
,
1785 enum playlist_command command
, int i1
, int i2
,
1786 const char* s1
, const char* s2
, void* data
)
1789 struct playlist_control_cache
* cache
;
1792 mutex_lock(&playlist
->control_mutex
);
1794 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1796 cache
->command
= command
;
1805 case PLAYLIST_COMMAND_PLAYLIST
:
1806 case PLAYLIST_COMMAND_ADD
:
1807 case PLAYLIST_COMMAND_QUEUE
:
1808 #ifndef HAVE_DIRCACHE
1809 case PLAYLIST_COMMAND_DELETE
:
1810 case PLAYLIST_COMMAND_RESET
:
1814 case PLAYLIST_COMMAND_SHUFFLE
:
1815 case PLAYLIST_COMMAND_UNSHUFFLE
:
1817 /* only flush when needed */
1821 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1822 result
= flush_cached_control(playlist
);
1824 mutex_unlock(&playlist
->control_mutex
);
1830 * sync control file to disk
1832 static void sync_control(struct playlist_info
* playlist
, bool force
)
1834 #ifdef HAVE_DIRCACHE
1835 if (playlist
->started
&& force
)
1839 if (playlist
->started
)
1842 if (playlist
->pending_control_sync
)
1844 mutex_lock(&playlist
->control_mutex
);
1845 fsync(playlist
->control_fd
);
1846 playlist
->pending_control_sync
= false;
1847 mutex_unlock(&playlist
->control_mutex
);
1853 * Rotate indices such that first_index is index 0
1855 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1857 index
-= playlist
->first_index
;
1859 index
+= playlist
->amount
;
1865 * Initialize playlist entries at startup
1867 void playlist_init(void)
1869 struct playlist_info
* playlist
= ¤t_playlist
;
1871 playlist
->current
= true;
1872 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1873 "%s", PLAYLIST_CONTROL_FILE
);
1875 playlist
->control_fd
= -1;
1876 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1877 playlist
->indices
= buffer_alloc(
1878 playlist
->max_playlist_size
* sizeof(int));
1879 playlist
->buffer_size
=
1880 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1881 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1882 mutex_init(&playlist
->control_mutex
);
1883 empty_playlist(playlist
, true);
1885 #ifdef HAVE_DIRCACHE
1886 playlist
->filenames
= buffer_alloc(
1887 playlist
->max_playlist_size
* sizeof(int));
1888 memset(playlist
->filenames
, 0,
1889 playlist
->max_playlist_size
* sizeof(int));
1890 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1891 playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1892 IF_COP(, CPU
, false));
1893 queue_init(&playlist_queue
, true);
1898 * Clean playlist at shutdown
1900 void playlist_shutdown(void)
1902 struct playlist_info
* playlist
= ¤t_playlist
;
1904 if (playlist
->control_fd
>= 0)
1906 mutex_lock(&playlist
->control_mutex
);
1908 if (playlist
->num_cached
> 0)
1909 flush_cached_control(playlist
);
1911 close(playlist
->control_fd
);
1913 mutex_unlock(&playlist
->control_mutex
);
1918 * Create new playlist
1920 int playlist_create(const char *dir
, const char *file
)
1922 struct playlist_info
* playlist
= ¤t_playlist
;
1924 new_playlist(playlist
, dir
, file
);
1927 /* load the playlist file */
1928 add_indices_to_playlist(playlist
, NULL
, 0);
1933 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1936 * Restore the playlist state based on control file commands. Called to
1937 * resume playback after shutdown.
1939 int playlist_resume(void)
1941 struct playlist_info
* playlist
= ¤t_playlist
;
1946 int control_file_size
= 0;
1950 /* use mp3 buffer for maximum load speed */
1951 #if CONFIG_CODEC != SWCODEC
1952 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1953 buflen
= (audiobufend
- audiobuf
);
1954 buffer
= (char *)audiobuf
;
1956 buffer
= (char *)audio_get_buffer(false, &buflen
);
1959 empty_playlist(playlist
, true);
1961 gui_syncsplash(0, ID2P(LANG_WAIT
));
1962 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1963 if (playlist
->control_fd
< 0)
1965 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1968 playlist
->control_created
= true;
1970 control_file_size
= filesize(playlist
->control_fd
);
1971 if (control_file_size
<= 0)
1973 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1977 /* read a small amount first to get the header */
1978 nread
= read(playlist
->control_fd
, buffer
,
1979 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1982 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1986 playlist
->started
= true;
1992 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1993 int last_newline
= 0;
1995 bool newline
= true;
1996 bool exit_loop
= false;
2001 unsigned long last_tick
= current_tick
;
2003 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
2005 /* So a splash while we are loading. */
2006 if (current_tick
- last_tick
> HZ
/4)
2008 gui_syncsplash(0, str(LANG_LOADING_PERCENT
),
2009 (total_read
+count
)*100/control_file_size
,
2010 str(LANG_OFF_ABORT
));
2011 if (action_userabort(TIMEOUT_NOBLOCK
))
2014 * Not sure how to implement this, somebody more familiar
2015 * with the code, please fix this. */
2017 last_tick
= current_tick
;
2020 /* Are we on a new line? */
2021 if((*p
== '\n') || (*p
== '\r'))
2025 /* save last_newline in case we need to load more data */
2026 last_newline
= count
;
2028 switch (current_command
)
2030 case PLAYLIST_COMMAND_PLAYLIST
:
2032 /* str1=version str2=dir str3=file */
2048 version
= atoi(str1
);
2050 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2053 update_playlist_filename(playlist
, str2
, str3
);
2055 if (str3
[0] != '\0')
2057 /* NOTE: add_indices_to_playlist() overwrites the
2058 audiobuf so we need to reload control file
2060 add_indices_to_playlist(playlist
, NULL
, 0);
2062 else if (str2
[0] != '\0')
2064 playlist
->in_ram
= true;
2065 resume_directory(str2
);
2068 /* load the rest of the data */
2074 case PLAYLIST_COMMAND_ADD
:
2075 case PLAYLIST_COMMAND_QUEUE
:
2077 /* str1=position str2=last_position str3=file */
2078 int position
, last_position
;
2081 if (!str1
|| !str2
|| !str3
)
2088 position
= atoi(str1
);
2089 last_position
= atoi(str2
);
2091 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2094 /* seek position is based on str3's position in
2096 if (add_track_to_playlist(playlist
, str3
, position
,
2097 queue
, total_read
+(str3
-buffer
)) < 0)
2100 playlist
->last_insert_pos
= last_position
;
2104 case PLAYLIST_COMMAND_DELETE
:
2116 position
= atoi(str1
);
2118 if (remove_track_from_playlist(playlist
, position
,
2124 case PLAYLIST_COMMAND_SHUFFLE
:
2126 /* str1=seed str2=first_index */
2138 /* Always sort list before shuffling */
2139 sort_playlist(playlist
, false, false);
2143 playlist
->first_index
= atoi(str2
);
2145 if (randomise_playlist(playlist
, seed
, false,
2152 case PLAYLIST_COMMAND_UNSHUFFLE
:
2154 /* str1=first_index */
2162 playlist
->first_index
= atoi(str1
);
2164 if (sort_playlist(playlist
, false, false) < 0)
2170 case PLAYLIST_COMMAND_RESET
:
2172 playlist
->last_insert_pos
= -1;
2175 case PLAYLIST_COMMAND_COMMENT
:
2182 /* to ignore any extra newlines */
2183 current_command
= PLAYLIST_COMMAND_COMMENT
;
2189 /* first non-comment line must always specify playlist */
2190 if (first
&& *p
!= 'P' && *p
!= '#')
2200 /* playlist can only be specified once */
2208 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2211 current_command
= PLAYLIST_COMMAND_ADD
;
2214 current_command
= PLAYLIST_COMMAND_QUEUE
;
2217 current_command
= PLAYLIST_COMMAND_DELETE
;
2220 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2223 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2226 current_command
= PLAYLIST_COMMAND_RESET
;
2229 current_command
= PLAYLIST_COMMAND_COMMENT
;
2242 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2244 /* all control file strings are separated with a colon.
2245 Replace the colon with 0 to get proper strings that can be
2246 used by commands above */
2252 if ((count
+1) < nread
)
2266 /* allow last string to contain colons */
2277 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2281 if (!newline
|| (exit_loop
&& count
<nread
))
2283 if ((total_read
+ count
) >= control_file_size
)
2285 /* no newline at end of control file */
2286 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2290 /* We didn't end on a newline or we exited loop prematurely.
2291 Either way, re-read the remainder. */
2292 count
= last_newline
;
2293 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2296 total_read
+= count
;
2299 /* still looking for header */
2300 nread
= read(playlist
->control_fd
, buffer
,
2301 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2303 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2305 /* Terminate on EOF */
2308 if (global_status
.resume_seed
>= 0)
2310 /* Apply shuffle command saved in settings */
2311 if (global_status
.resume_seed
== 0)
2312 sort_playlist(playlist
, false, true);
2316 sort_playlist(playlist
, false, false);
2318 randomise_playlist(playlist
, global_status
.resume_seed
,
2323 playlist
->first_index
= global_status
.resume_first_index
;
2328 #ifdef HAVE_DIRCACHE
2329 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2336 * Add track to in_ram playlist. Used when playing directories.
2338 int playlist_add(const char *filename
)
2340 struct playlist_info
* playlist
= ¤t_playlist
;
2341 int len
= strlen(filename
);
2343 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2344 (playlist
->amount
>= playlist
->max_playlist_size
))
2346 display_buffer_full();
2350 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2351 #ifdef HAVE_DIRCACHE
2352 playlist
->filenames
[playlist
->amount
] = NULL
;
2356 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2357 playlist
->buffer_end_pos
+= len
;
2358 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2363 /* shuffle newly created playlist using random seed. */
2364 int playlist_shuffle(int random_seed
, int start_index
)
2366 struct playlist_info
* playlist
= ¤t_playlist
;
2368 unsigned int seek_pos
= 0;
2369 bool start_current
= false;
2371 if (start_index
>= 0 && global_settings
.play_selected
)
2373 /* store the seek position before the shuffle */
2374 seek_pos
= playlist
->indices
[start_index
];
2375 playlist
->index
= global_status
.resume_first_index
=
2376 playlist
->first_index
= start_index
;
2377 start_current
= true;
2380 cond_talk_ids(LANG_WAIT
);
2381 gui_syncsplash(0, str(LANG_PLAYLIST_SHUFFLE
));
2383 randomise_playlist(playlist
, random_seed
, start_current
, true);
2385 return playlist
->index
;
2388 /* start playing current playlist at specified index/offset */
2389 int playlist_start(int start_index
, int offset
)
2391 struct playlist_info
* playlist
= ¤t_playlist
;
2393 playlist
->index
= start_index
;
2395 #if CONFIG_CODEC != SWCODEC
2396 talk_buffer_steal(); /* will use the mp3 buffer */
2399 playlist
->started
= true;
2400 sync_control(playlist
, false);
2406 /* Returns false if 'steps' is out of bounds, else true */
2407 bool playlist_check(int steps
)
2409 struct playlist_info
* playlist
= ¤t_playlist
;
2411 /* always allow folder navigation */
2412 if (global_settings
.next_folder
&& playlist
->in_ram
)
2415 int index
= get_next_index(playlist
, steps
, -1);
2417 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2418 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2420 return (index
>= 0);
2423 /* get trackname of track that is "steps" away from current playing track.
2424 NULL is used to identify end of playlist */
2425 char* playlist_peek(int steps
)
2427 struct playlist_info
* playlist
= ¤t_playlist
;
2434 index
= get_next_index(playlist
, steps
, -1);
2438 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2439 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2441 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2445 temp_ptr
= now_playing
;
2447 if (!playlist
->in_ram
|| control_file
)
2449 /* remove bogus dirs from beginning of path
2450 (workaround for buggy playlist creation tools) */
2453 #ifdef HAVE_DIRCACHE
2454 if (dircache_is_enabled())
2456 if (dircache_get_entry_ptr(temp_ptr
))
2462 fd
= open(temp_ptr
, O_RDONLY
);
2470 temp_ptr
= strchr(temp_ptr
+1, '/');
2475 /* Even though this is an invalid file, we still need to pass a
2476 file name to the caller because NULL is used to indicate end
2486 * Update indices as track has changed
2488 int playlist_next(int steps
)
2490 struct playlist_info
* playlist
= ¤t_playlist
;
2494 #ifdef AB_REPEAT_ENABLE
2495 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2497 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2501 /* We need to delete all the queued songs */
2502 for (i
=0, j
=steps
; i
<j
; i
++)
2504 index
= get_next_index(playlist
, i
, -1);
2506 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2508 remove_track_from_playlist(playlist
, index
, true);
2509 steps
--; /* one less track */
2514 index
= get_next_index(playlist
, steps
, -1);
2518 /* end of playlist... or is it */
2519 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2520 playlist
->amount
> 1)
2522 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2523 playlist
->first_index
= global_status
.resume_first_index
= 0;
2524 sort_playlist(playlist
, false, false);
2525 randomise_playlist(playlist
, current_tick
, false, true);
2526 #if CONFIG_CODEC != SWCODEC
2527 playlist_start(0, 0);
2529 playlist
->index
= 0;
2532 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2534 char dir
[MAX_PATH
+1];
2536 changing_dir
= true;
2539 if (!get_next_directory(dir
))
2541 /* start playing next directory */
2542 if (playlist_create(dir
, NULL
) != -1)
2544 ft_build_playlist(tree_get_context(), 0);
2545 if (global_settings
.playlist_shuffle
)
2546 playlist_shuffle(current_tick
, -1);
2547 #if CONFIG_CODEC != SWCODEC
2548 playlist_start(0, 0);
2550 playlist
->index
= index
= 0;
2556 if (!get_previous_directory(dir
))
2558 /* start playing previous directory */
2559 if (playlist_create(dir
, NULL
) != -1)
2561 ft_build_playlist(tree_get_context(), 0);
2562 if (global_settings
.playlist_shuffle
)
2563 playlist_shuffle(current_tick
, -1);
2564 #if CONFIG_CODEC != SWCODEC
2565 playlist_start(current_playlist
.amount
-1, 0);
2567 playlist
->index
= index
= current_playlist
.amount
- 1;
2571 changing_dir
= false;
2577 playlist
->index
= index
;
2579 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2581 /* check to see if we've gone beyond the last inserted track */
2582 int cur
= rotate_index(playlist
, index
);
2583 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2587 /* reset last inserted track */
2588 playlist
->last_insert_pos
= -1;
2590 if (playlist
->control_fd
>= 0)
2592 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2593 -1, -1, NULL
, NULL
, NULL
);
2598 sync_control(playlist
, false);
2606 /* try playing next or previous folder */
2607 bool playlist_next_dir(int direction
)
2609 char dir
[MAX_PATH
+1];
2613 /* not to mess up real playlists */
2614 if(!current_playlist
.in_ram
)
2620 changing_dir
= true;
2622 res
= get_next_directory(dir
);
2624 res
= get_previous_directory(dir
);
2627 if (playlist_create(dir
, NULL
) != -1)
2629 ft_build_playlist(tree_get_context(), 0);
2630 if (global_settings
.playlist_shuffle
)
2631 playlist_shuffle(current_tick
, -1);
2632 #if (CONFIG_CODEC != SWCODEC)
2633 playlist_start(0,0);
2643 changing_dir
= false;
2648 /* Get resume info for current playing song. If return value is -1 then
2649 settings shouldn't be saved. */
2650 int playlist_get_resume_info(int *resume_index
)
2652 struct playlist_info
* playlist
= ¤t_playlist
;
2654 *resume_index
= playlist
->index
;
2659 /* Update resume info for current playing song. Returns -1 on error. */
2660 int playlist_update_resume_info(const struct mp3entry
* id3
)
2662 struct playlist_info
* playlist
= ¤t_playlist
;
2666 if (global_status
.resume_index
!= playlist
->index
||
2667 global_status
.resume_offset
!= id3
->offset
)
2669 global_status
.resume_index
= playlist
->index
;
2670 global_status
.resume_offset
= id3
->offset
;
2676 global_status
.resume_index
= -1;
2677 global_status
.resume_offset
= -1;
2684 /* Returns index of current playing track for display purposes. This value
2685 should not be used for resume purposes as it doesn't represent the actual
2686 index into the playlist */
2687 int playlist_get_display_index(void)
2689 struct playlist_info
* playlist
= ¤t_playlist
;
2691 /* first_index should always be index 0 for display purposes */
2692 int index
= rotate_index(playlist
, playlist
->index
);
2697 /* returns number of tracks in current playlist */
2698 int playlist_amount(void)
2700 return playlist_amount_ex(NULL
);
2704 * Create a new playlist If playlist is not NULL then we're loading a
2705 * playlist off disk for viewing/editing. The index_buffer is used to store
2706 * playlist indices (required for and only used if !current playlist). The
2707 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2709 int playlist_create_ex(struct playlist_info
* playlist
,
2710 const char* dir
, const char* file
,
2711 void* index_buffer
, int index_buffer_size
,
2712 void* temp_buffer
, int temp_buffer_size
)
2715 playlist
= ¤t_playlist
;
2718 /* Initialize playlist structure */
2719 int r
= rand() % 10;
2720 playlist
->current
= false;
2722 /* Use random name for control file */
2723 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2724 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2726 playlist
->control_fd
= -1;
2730 int num_indices
= index_buffer_size
/ sizeof(int);
2732 #ifdef HAVE_DIRCACHE
2735 if (num_indices
> global_settings
.max_files_in_playlist
)
2736 num_indices
= global_settings
.max_files_in_playlist
;
2738 playlist
->max_playlist_size
= num_indices
;
2739 playlist
->indices
= index_buffer
;
2740 #ifdef HAVE_DIRCACHE
2741 playlist
->filenames
= (const struct dircache_entry
**)
2742 &playlist
->indices
[num_indices
];
2747 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2748 playlist
->indices
= current_playlist
.indices
;
2749 #ifdef HAVE_DIRCACHE
2750 playlist
->filenames
= current_playlist
.filenames
;
2754 playlist
->buffer_size
= 0;
2755 playlist
->buffer
= NULL
;
2756 mutex_init(&playlist
->control_mutex
);
2759 new_playlist(playlist
, dir
, file
);
2762 /* load the playlist file */
2763 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2769 * Set the specified playlist as the current.
2770 * NOTE: You will get undefined behaviour if something is already playing so
2771 * remember to stop before calling this. Also, this call will
2772 * effectively close your playlist, making it unusable.
2774 int playlist_set_current(struct playlist_info
* playlist
)
2776 if (!playlist
|| (check_control(playlist
) < 0))
2779 empty_playlist(¤t_playlist
, false);
2781 strncpy(current_playlist
.filename
, playlist
->filename
,
2782 sizeof(current_playlist
.filename
));
2784 current_playlist
.utf8
= playlist
->utf8
;
2785 current_playlist
.fd
= playlist
->fd
;
2787 close(playlist
->control_fd
);
2788 close(current_playlist
.control_fd
);
2789 remove(current_playlist
.control_filename
);
2790 if (rename(playlist
->control_filename
,
2791 current_playlist
.control_filename
) < 0)
2793 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2795 if (current_playlist
.control_fd
< 0)
2797 current_playlist
.control_created
= true;
2799 current_playlist
.dirlen
= playlist
->dirlen
;
2801 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2803 memcpy(current_playlist
.indices
, playlist
->indices
,
2804 playlist
->max_playlist_size
*sizeof(int));
2805 #ifdef HAVE_DIRCACHE
2806 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2807 playlist
->max_playlist_size
*sizeof(int));
2811 current_playlist
.first_index
= playlist
->first_index
;
2812 current_playlist
.amount
= playlist
->amount
;
2813 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2814 current_playlist
.seed
= playlist
->seed
;
2815 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2816 current_playlist
.deleted
= playlist
->deleted
;
2817 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2819 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2820 sizeof(current_playlist
.control_cache
));
2821 current_playlist
.num_cached
= playlist
->num_cached
;
2822 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2828 * Close files and delete control file for non-current playlist.
2830 void playlist_close(struct playlist_info
* playlist
)
2835 if (playlist
->fd
>= 0)
2836 close(playlist
->fd
);
2838 if (playlist
->control_fd
>= 0)
2839 close(playlist
->control_fd
);
2841 if (playlist
->control_created
)
2842 remove(playlist
->control_filename
);
2845 void playlist_sync(struct playlist_info
* playlist
)
2848 playlist
= ¤t_playlist
;
2850 sync_control(playlist
, false);
2851 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2852 audio_flush_and_reload_tracks();
2854 #ifdef HAVE_DIRCACHE
2855 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2860 * Insert track into playlist at specified position (or one of the special
2861 * positions). Returns position where track was inserted or -1 if error.
2863 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2864 int position
, bool queue
, bool sync
)
2869 playlist
= ¤t_playlist
;
2871 if (check_control(playlist
) < 0)
2873 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2877 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2879 /* Check if we want manually sync later. For example when adding
2880 * bunch of files from tagcache, syncing after every file wouldn't be
2881 * a good thing to do. */
2882 if (sync
&& result
>= 0)
2883 playlist_sync(playlist
);
2889 * Insert all tracks from specified directory into playlist.
2891 int playlist_insert_directory(struct playlist_info
* playlist
,
2892 const char *dirname
, int position
, bool queue
,
2896 unsigned char *count_str
;
2897 struct directory_search_context context
;
2900 playlist
= ¤t_playlist
;
2902 if (check_control(playlist
) < 0)
2904 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2908 if (position
== PLAYLIST_REPLACE
)
2910 if (remove_all_tracks(playlist
) == 0)
2911 position
= PLAYLIST_INSERT_LAST
;
2917 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2919 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2921 display_playlist_count(0, count_str
, false);
2923 context
.playlist
= playlist
;
2924 context
.position
= position
;
2925 context
.queue
= queue
;
2930 result
= playlist_directory_tracksearch(dirname
, recurse
,
2931 directory_search_callback
, &context
);
2933 sync_control(playlist
, false);
2937 display_playlist_count(context
.count
, count_str
, true);
2939 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2940 audio_flush_and_reload_tracks();
2942 #ifdef HAVE_DIRCACHE
2943 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2950 * Insert all tracks from specified playlist into dynamic playlist.
2952 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2953 int position
, bool queue
)
2959 unsigned char *count_str
;
2960 char temp_buf
[MAX_PATH
+1];
2961 char trackname
[MAX_PATH
+1];
2966 playlist
= ¤t_playlist
;
2968 if (check_control(playlist
) < 0)
2970 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2974 fd
= open(filename
, O_RDONLY
);
2977 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2981 /* we need the directory name for formatting purposes */
2984 temp_ptr
= strrchr(filename
+1,'/');
2991 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2993 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2995 display_playlist_count(count
, count_str
, false);
2997 if (position
== PLAYLIST_REPLACE
)
2999 if (remove_all_tracks(playlist
) == 0)
3000 position
= PLAYLIST_INSERT_LAST
;
3006 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3009 if (action_userabort(TIMEOUT_NOBLOCK
))
3012 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3016 /* we need to format so that relative paths are correctly
3018 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3025 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3034 /* Make sure tracks are inserted in correct order if user
3035 requests INSERT_FIRST */
3036 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3037 position
= insert_pos
+ 1;
3041 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3043 display_playlist_count(count
, count_str
, false);
3045 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3046 (audio_status() & AUDIO_STATUS_PLAY
) &&
3048 audio_flush_and_reload_tracks();
3052 /* let the other threads work */
3061 sync_control(playlist
, false);
3065 display_playlist_count(count
, count_str
, true);
3067 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3068 audio_flush_and_reload_tracks();
3070 #ifdef HAVE_DIRCACHE
3071 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3078 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3079 * we want to delete the current playing track.
3081 int playlist_delete(struct playlist_info
* playlist
, int index
)
3086 playlist
= ¤t_playlist
;
3088 if (check_control(playlist
) < 0)
3090 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3094 if (index
== PLAYLIST_DELETE_CURRENT
)
3095 index
= playlist
->index
;
3097 result
= remove_track_from_playlist(playlist
, index
, true);
3099 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3101 audio_flush_and_reload_tracks();
3107 * Move track at index to new_index. Tracks between the two are shifted
3108 * appropriately. Returns 0 on success and -1 on failure.
3110 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3116 bool current
= false;
3118 char filename
[MAX_PATH
];
3121 playlist
= ¤t_playlist
;
3123 if (check_control(playlist
) < 0)
3125 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3129 if (index
== new_index
)
3132 if (index
== playlist
->index
)
3133 /* Moving the current track */
3136 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3137 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3138 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3140 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3141 sizeof(filename
)) < 0)
3144 /* Delete track from original position */
3145 result
= remove_track_from_playlist(playlist
, index
, true);
3149 /* We want to insert the track at the position that was specified by
3150 new_index. This may be different then new_index because of the
3151 shifting that occurred after the delete */
3152 r
= rotate_index(playlist
, new_index
);
3156 new_index
= PLAYLIST_PREPEND
;
3157 else if (r
== playlist
->amount
)
3159 new_index
= PLAYLIST_INSERT_LAST
;
3161 /* Calculate index of desired position */
3162 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3164 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3171 /* Moved the current track */
3174 case PLAYLIST_PREPEND
:
3175 playlist
->index
= playlist
->first_index
;
3177 case PLAYLIST_INSERT_LAST
:
3178 playlist
->index
= playlist
->first_index
- 1;
3179 if (playlist
->index
< 0)
3180 playlist
->index
+= playlist
->amount
;
3183 playlist
->index
= new_index
;
3188 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3189 audio_flush_and_reload_tracks();
3193 #ifdef HAVE_DIRCACHE
3194 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3200 /* shuffle currently playing playlist */
3201 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3207 playlist
= ¤t_playlist
;
3209 check_control(playlist
);
3211 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3213 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3215 audio_flush_and_reload_tracks();
3220 /* sort currently playing playlist */
3221 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3226 playlist
= ¤t_playlist
;
3228 check_control(playlist
);
3230 result
= sort_playlist(playlist
, start_current
, true);
3232 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3234 audio_flush_and_reload_tracks();
3239 /* returns true if playlist has been modified */
3240 bool playlist_modified(const struct playlist_info
* playlist
)
3243 playlist
= ¤t_playlist
;
3245 if (playlist
->shuffle_modified
||
3246 playlist
->deleted
||
3247 playlist
->num_inserted_tracks
> 0)
3253 /* returns index of first track in playlist */
3254 int playlist_get_first_index(const struct playlist_info
* playlist
)
3257 playlist
= ¤t_playlist
;
3259 return playlist
->first_index
;
3262 /* returns shuffle seed of playlist */
3263 int playlist_get_seed(const struct playlist_info
* playlist
)
3266 playlist
= ¤t_playlist
;
3268 return playlist
->seed
;
3271 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3272 int playlist_amount_ex(const struct playlist_info
* playlist
)
3275 playlist
= ¤t_playlist
;
3277 return playlist
->amount
;
3280 /* returns full path of playlist (minus extension) */
3281 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3287 playlist
= ¤t_playlist
;
3289 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3294 /* Remove extension */
3295 sep
= strrchr(buf
, '.');
3302 /* returns the playlist filename */
3303 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3307 playlist
= ¤t_playlist
;
3309 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3317 /* Fills info structure with information about track at specified index.
3318 Returns 0 on success and -1 on failure */
3319 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3320 struct playlist_track_info
* info
)
3326 playlist
= ¤t_playlist
;
3328 if (index
< 0 || index
>= playlist
->amount
)
3331 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3332 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3334 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3335 sizeof(info
->filename
)) < 0)
3342 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3343 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3345 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3349 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3350 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3352 info
->index
= index
;
3353 info
->display_index
= rotate_index(playlist
, index
) + 1;
3358 /* save the current dynamic playlist to specified file */
3359 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3364 char path
[MAX_PATH
+1];
3365 char tmp_buf
[MAX_PATH
+1];
3367 bool overwrite_current
= false;
3368 int* index_buf
= NULL
;
3371 playlist
= ¤t_playlist
;
3373 if (playlist
->amount
<= 0)
3376 /* use current working directory as base for pathname */
3377 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3378 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3381 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3383 /* Attempting to overwrite current playlist file.*/
3385 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3387 /* not enough buffer space to store updated indices */
3388 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3392 /* in_ram buffer is unused for m3u files so we'll use for storing
3394 index_buf
= (int*)playlist
->buffer
;
3396 /* use temporary pathname */
3397 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3398 overwrite_current
= true;
3401 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3404 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3408 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3412 index
= playlist
->first_index
;
3413 for (i
=0; i
<playlist
->amount
; i
++)
3420 if (action_userabort(TIMEOUT_NOBLOCK
))
3426 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3427 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3428 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3430 /* Don't save queued files */
3433 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3440 if (overwrite_current
)
3441 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3443 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3445 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3452 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3453 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3459 index
= (index
+1)%playlist
->amount
;
3462 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3466 if (overwrite_current
&& result
>= 0)
3470 mutex_lock(&playlist
->control_mutex
);
3472 /* Replace the current playlist with the new one and update indices */
3473 close(playlist
->fd
);
3474 if (remove(playlist
->filename
) >= 0)
3476 if (rename(path
, playlist
->filename
) >= 0)
3478 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3479 if (playlist
->fd
>= 0)
3481 index
= playlist
->first_index
;
3482 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3484 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3486 playlist
->indices
[index
] = index_buf
[count
];
3489 index
= (index
+1)%playlist
->amount
;
3492 /* we need to recreate control because inserted tracks are
3493 now part of the playlist and shuffle has been
3495 result
= recreate_control(playlist
);
3500 mutex_unlock(&playlist
->control_mutex
);
3510 * Search specified directory for tracks and notify via callback. May be
3511 * called recursively.
3513 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3514 int (*callback
)(char*, void*),
3517 char buf
[MAX_PATH
+1];
3521 struct entry
*files
;
3522 struct tree_context
* tc
= tree_get_context();
3523 int old_dirfilter
= *(tc
->dirfilter
);
3528 /* use the tree browser dircache to load files */
3529 *(tc
->dirfilter
) = SHOW_ALL
;
3531 if (ft_load(tc
, dirname
) < 0)
3533 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3534 *(tc
->dirfilter
) = old_dirfilter
;
3538 files
= (struct entry
*) tc
->dircache
;
3539 num_files
= tc
->filesindir
;
3541 /* we've overwritten the dircache so tree browser will need to be
3545 for (i
=0; i
<num_files
; i
++)
3548 if (action_userabort(TIMEOUT_NOBLOCK
))
3554 if (files
[i
].attr
& ATTR_DIRECTORY
)
3558 /* recursively add directories */
3559 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3560 result
= playlist_directory_tracksearch(buf
, recurse
,
3565 /* we now need to reload our current directory */
3566 if(ft_load(tc
, dirname
) < 0)
3572 files
= (struct entry
*) tc
->dircache
;
3573 num_files
= tc
->filesindir
;
3583 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3585 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3587 if (callback(buf
, context
) != 0)
3593 /* let the other threads work */
3598 /* restore dirfilter */
3599 *(tc
->dirfilter
) = old_dirfilter
;