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"
102 #include "root_menu.h"
104 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
105 #define PLAYLIST_CONTROL_FILE_VERSION 2
108 Each playlist index has a flag associated with it which identifies what
109 type of track it is. These flags are stored in the 4 high order bits of
112 NOTE: This limits the playlist file size to a max of 256M.
116 01 = Track was prepended into playlist
117 10 = Track was inserted into playlist
118 11 = Track was appended into playlist
123 0 = Track entry is valid
124 1 = Track does not exist on disk and should be skipped
126 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
127 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
128 #define PLAYLIST_QUEUE_MASK 0x20000000
130 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
131 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
132 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
134 #define PLAYLIST_QUEUED 0x20000000
135 #define PLAYLIST_SKIPPED 0x10000000
137 #define PLAYLIST_DISPLAY_COUNT 10
139 struct directory_search_context
{
140 struct playlist_info
* playlist
;
146 static bool changing_dir
= false;
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
);
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";
204 * remove any files and indices associated with the playlist
206 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
208 playlist
->filename
[0] = '\0';
209 playlist
->utf8
= true;
211 if(playlist
->fd
>= 0)
212 /* If there is an already open playlist, close it. */
216 if(playlist
->control_fd
>= 0)
217 close(playlist
->control_fd
);
218 playlist
->control_fd
= -1;
219 playlist
->control_created
= false;
221 playlist
->in_ram
= false;
223 if (playlist
->buffer
)
224 playlist
->buffer
[0] = 0;
226 playlist
->buffer_end_pos
= 0;
229 playlist
->first_index
= 0;
230 playlist
->amount
= 0;
231 playlist
->last_insert_pos
= -1;
233 playlist
->shuffle_modified
= false;
234 playlist
->deleted
= false;
235 playlist
->num_inserted_tracks
= 0;
236 playlist
->started
= false;
238 playlist
->num_cached
= 0;
239 playlist
->pending_control_sync
= false;
241 if (!resume
&& playlist
->current
)
243 /* start with fresh playlist control file when starting new
245 create_control(playlist
);
247 /* Reset resume settings */
248 global_status
.resume_first_index
= 0;
249 global_status
.resume_seed
= -1;
254 * Initialize a new playlist for viewing/editing/playing. dir is the
255 * directory where the playlist is located and file is the filename.
257 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
260 empty_playlist(playlist
, false);
266 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
267 playlist
->in_ram
= true;
269 dir
= ""; /* empty playlist */
272 update_playlist_filename(playlist
, dir
, file
);
274 if (playlist
->control_fd
>= 0)
276 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
277 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
278 sync_control(playlist
, false);
283 * create control file for playlist
285 static void create_control(struct playlist_info
* playlist
)
287 playlist
->control_fd
= open(playlist
->control_filename
,
288 O_CREAT
|O_RDWR
|O_TRUNC
);
289 if (playlist
->control_fd
< 0)
291 if (check_rockboxdir())
293 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
);
294 gui_syncsplash(HZ
*2, (unsigned char *)"%s (%d)",
295 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
296 playlist
->control_fd
);
298 playlist
->control_created
= false;
302 playlist
->control_created
= true;
307 * validate the control file. This may include creating/initializing it if
310 static int check_control(struct playlist_info
* playlist
)
312 if (!playlist
->control_created
)
314 create_control(playlist
);
316 if (playlist
->control_fd
>= 0)
318 char* dir
= playlist
->filename
;
319 char* file
= playlist
->filename
+playlist
->dirlen
;
320 char c
= playlist
->filename
[playlist
->dirlen
-1];
322 playlist
->filename
[playlist
->dirlen
-1] = '\0';
324 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
325 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
326 sync_control(playlist
, false);
327 playlist
->filename
[playlist
->dirlen
-1] = c
;
331 if (playlist
->control_fd
< 0)
338 * recreate the control file based on current playlist entries
340 static int recreate_control(struct playlist_info
* playlist
)
342 char temp_file
[MAX_PATH
+1];
347 if(playlist
->control_fd
>= 0)
349 char* dir
= playlist
->filename
;
350 char* file
= playlist
->filename
+playlist
->dirlen
;
351 char c
= playlist
->filename
[playlist
->dirlen
-1];
353 close(playlist
->control_fd
);
355 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
356 playlist
->control_filename
);
358 if (rename(playlist
->control_filename
, temp_file
) < 0)
361 temp_fd
= open(temp_file
, O_RDONLY
);
365 playlist
->control_fd
= open(playlist
->control_filename
,
366 O_CREAT
|O_RDWR
|O_TRUNC
);
367 if (playlist
->control_fd
< 0)
370 playlist
->filename
[playlist
->dirlen
-1] = '\0';
372 /* cannot call update_control() because of mutex */
373 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
374 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
376 playlist
->filename
[playlist
->dirlen
-1] = c
;
386 playlist
->shuffle_modified
= false;
387 playlist
->deleted
= false;
388 playlist
->num_inserted_tracks
= 0;
390 if (playlist
->current
)
392 global_status
.resume_seed
= -1;
396 for (i
=0; i
<playlist
->amount
; i
++)
398 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
400 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
401 char inserted_file
[MAX_PATH
+1];
403 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
405 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
407 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
408 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
411 /* save the position in file where name is written */
412 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
414 result
= fdprintf(playlist
->control_fd
, "%s\n",
417 playlist
->indices
[i
] =
418 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
424 playlist
->num_inserted_tracks
++;
430 fsync(playlist
->control_fd
);
439 * store directory and name of playlist file
441 static void update_playlist_filename(struct playlist_info
* playlist
,
442 const char *dir
, const char *file
)
445 int dirlen
= strlen(dir
);
446 int filelen
= strlen(file
);
448 /* Default to utf8 unless explicitly told otherwise. */
449 playlist
->utf8
= !(filelen
> 4 && strcasecmp(&file
[filelen
- 4], ".m3u") == 0);
451 /* If the dir does not end in trailing slash, we use a separator.
452 Otherwise we don't. */
453 if('/' != dir
[dirlen
-1])
459 playlist
->dirlen
= dirlen
;
461 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
462 "%s%s%s", dir
, sep
, file
);
466 * calculate track offsets within a playlist file
468 static int add_indices_to_playlist(struct playlist_info
* playlist
,
469 char* buffer
, size_t buflen
)
473 unsigned int count
= 0;
478 if(-1 == playlist
->fd
)
479 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
481 return -1; /* failure */
483 #ifdef HAVE_LCD_BITMAP
484 if(global_settings
.statusbar
)
485 lcd_setmargins(0, STATUSBAR_HEIGHT
);
487 lcd_setmargins(0, 0);
489 gui_syncsplash(0, ID2P(LANG_WAIT
));
493 /* use mp3 buffer for maximum load speed */
495 #if CONFIG_CODEC != SWCODEC
496 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
497 buflen
= (audiobufend
- audiobuf
);
498 buffer
= (char *)audiobuf
;
500 buffer
= (char *)audio_get_buffer(false, &buflen
);
508 nread
= read(playlist
->fd
, buffer
, buflen
);
509 /* Terminate on EOF */
513 p
= (unsigned char *)buffer
;
515 /* utf8 BOM at beginning of file? */
516 if(i
== 0 && nread
> 3
517 && *p
== 0xef && *(p
+1) == 0xbb && *(p
+2) == 0xbf) {
521 playlist
->utf8
= true; /* Override any earlier indication. */
524 for(count
=0; count
< nread
; count
++,p
++) {
526 /* Are we on a new line? */
527 if((*p
== '\n') || (*p
== '\r'))
537 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
538 display_buffer_full();
543 /* Store a new entry */
544 playlist
->indices
[ playlist
->amount
] = i
+count
;
546 if (playlist
->filenames
)
547 playlist
->filenames
[ playlist
->amount
] = NULL
;
559 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
566 * Removes all tracks, from the playlist, leaving the presently playing
569 int remove_all_tracks(struct playlist_info
*playlist
)
573 if (playlist
== NULL
)
574 playlist
= ¤t_playlist
;
576 while (playlist
->index
> 0)
577 if ((result
= remove_track_from_playlist(playlist
, 0, true)) < 0)
580 while (playlist
->amount
> 1)
581 if ((result
= remove_track_from_playlist(playlist
, 1, true)) < 0)
584 if (playlist
->amount
== 1) {
585 playlist
->indices
[0] |= PLAYLIST_QUEUED
;
593 * Add track to playlist at specified position. There are five special
594 * positions that can be specified:
595 * PLAYLIST_PREPEND - Add track at beginning of playlist
596 * PLAYLIST_INSERT - Add track after current song. NOTE: If
597 * there are already inserted tracks then track
598 * is added to the end of the insertion list
599 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
600 * matter what other tracks have been inserted
601 * PLAYLIST_INSERT_LAST - Add track to end of playlist
602 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
603 * current playing track and end of playlist
604 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
605 * and inster this track at the end.
607 static int add_track_to_playlist(struct playlist_info
* playlist
,
608 const char *filename
, int position
,
609 bool queue
, int seek_pos
)
611 int insert_position
, orig_position
;
612 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
615 insert_position
= orig_position
= position
;
617 if (playlist
->amount
>= playlist
->max_playlist_size
)
619 display_buffer_full();
625 case PLAYLIST_PREPEND
:
626 position
= insert_position
= playlist
->first_index
;
628 case PLAYLIST_INSERT
:
629 /* if there are already inserted tracks then add track to end of
630 insertion list else add after current playing track */
631 if (playlist
->last_insert_pos
>= 0 &&
632 playlist
->last_insert_pos
< playlist
->amount
&&
633 (playlist
->indices
[playlist
->last_insert_pos
]&
634 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
635 position
= insert_position
= playlist
->last_insert_pos
+1;
636 else if (playlist
->amount
> 0)
637 position
= insert_position
= playlist
->index
+ 1;
639 position
= insert_position
= 0;
641 if (playlist
->started
)
642 playlist
->last_insert_pos
= position
;
644 case PLAYLIST_INSERT_FIRST
:
645 if (playlist
->amount
> 0)
646 position
= insert_position
= playlist
->index
+ 1;
648 position
= insert_position
= 0;
650 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
651 playlist
->last_insert_pos
= position
;
653 case PLAYLIST_INSERT_LAST
:
654 if (playlist
->first_index
> 0)
655 position
= insert_position
= playlist
->first_index
;
657 position
= insert_position
= playlist
->amount
;
659 case PLAYLIST_INSERT_SHUFFLED
:
661 if (playlist
->started
)
664 int n
= playlist
->amount
-
665 rotate_index(playlist
, playlist
->index
);
672 position
= playlist
->index
+ offset
+ 1;
673 if (position
>= playlist
->amount
)
674 position
-= playlist
->amount
;
676 insert_position
= position
;
679 position
= insert_position
= (rand() % (playlist
->amount
+1));
682 case PLAYLIST_REPLACE
:
683 if (remove_all_tracks(playlist
) < 0)
686 position
= insert_position
= playlist
->index
+ 1;
691 flags
|= PLAYLIST_QUEUED
;
693 /* shift indices so that track can be added */
694 for (i
=playlist
->amount
; i
>insert_position
; i
--)
696 playlist
->indices
[i
] = playlist
->indices
[i
-1];
698 if (playlist
->filenames
)
699 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
703 /* update stored indices if needed */
704 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
708 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
709 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
711 playlist
->first_index
++;
713 if (seek_pos
< 0 && playlist
->current
)
715 global_status
.resume_first_index
= playlist
->first_index
;
720 if (insert_position
< playlist
->last_insert_pos
||
721 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
722 playlist
->last_insert_pos
++;
724 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
726 int result
= update_control(playlist
,
727 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
728 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
734 playlist
->indices
[insert_position
] = flags
| seek_pos
;
737 if (playlist
->filenames
)
738 playlist
->filenames
[insert_position
] = NULL
;
742 playlist
->num_inserted_tracks
++;
744 return insert_position
;
748 * Callback for playlist_directory_tracksearch to insert track into
751 static int directory_search_callback(char* filename
, void* context
)
753 struct directory_search_context
* c
=
754 (struct directory_search_context
*) context
;
757 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
765 /* Make sure tracks are inserted in correct order if user requests
767 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
768 c
->position
= insert_pos
+ 1;
770 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
772 unsigned char* count_str
;
775 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
777 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
779 display_playlist_count(c
->count
, count_str
, false);
781 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
782 (audio_status() & AUDIO_STATUS_PLAY
) &&
783 c
->playlist
->started
)
784 audio_flush_and_reload_tracks();
791 * remove track at specified position
793 static int remove_track_from_playlist(struct playlist_info
* playlist
,
794 int position
, bool write
)
799 if (playlist
->amount
<= 0)
802 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
804 /* shift indices now that track has been removed */
805 for (i
=position
; i
<playlist
->amount
; i
++)
807 playlist
->indices
[i
] = playlist
->indices
[i
+1];
809 if (playlist
->filenames
)
810 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
817 playlist
->num_inserted_tracks
--;
819 playlist
->deleted
= true;
821 /* update stored indices if needed */
822 if (position
< playlist
->index
)
825 if (position
< playlist
->first_index
)
827 playlist
->first_index
--;
831 global_status
.resume_first_index
= playlist
->first_index
;
836 if (position
<= playlist
->last_insert_pos
)
837 playlist
->last_insert_pos
--;
839 if (write
&& playlist
->control_fd
>= 0)
841 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
842 position
, -1, NULL
, NULL
, NULL
);
847 sync_control(playlist
, false);
854 * randomly rearrange the array of indices for the playlist. If start_current
855 * is true then update the index to the new index of the current playing track
857 static int randomise_playlist(struct playlist_info
* playlist
,
858 unsigned int seed
, bool start_current
,
864 unsigned int current
= playlist
->indices
[playlist
->index
];
866 /* seed 0 is used to identify sorted playlist for resume purposes */
870 /* seed with the given seed */
873 /* randomise entire indices list */
874 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
876 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
877 candidate
= rand() % (count
+ 1);
879 /* now swap the values at the 'count' and 'candidate' positions */
880 store
= playlist
->indices
[candidate
];
881 playlist
->indices
[candidate
] = playlist
->indices
[count
];
882 playlist
->indices
[count
] = store
;
884 if (playlist
->filenames
)
886 store
= (long)playlist
->filenames
[candidate
];
887 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
888 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
894 find_and_set_playlist_index(playlist
, current
);
896 /* indices have been moved so last insert position is no longer valid */
897 playlist
->last_insert_pos
= -1;
899 playlist
->seed
= seed
;
900 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
901 playlist
->shuffle_modified
= true;
905 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
906 playlist
->first_index
, NULL
, NULL
, NULL
);
907 global_status
.resume_seed
= seed
;
915 * Sort the array of indices for the playlist. If start_current is true then
916 * set the index to the new index of the current song.
918 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
921 unsigned int current
= playlist
->indices
[playlist
->index
];
923 if (playlist
->amount
> 0)
924 qsort(playlist
->indices
, playlist
->amount
,
925 sizeof(playlist
->indices
[0]), compare
);
928 /** We need to re-check the song names from disk because qsort can't
929 * sort two arrays at once :/
930 * FIXME: Please implement a better way to do this. */
931 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
932 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
936 find_and_set_playlist_index(playlist
, current
);
938 /* indices have been moved so last insert position is no longer valid */
939 playlist
->last_insert_pos
= -1;
941 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
942 playlist
->shuffle_modified
= false;
943 if (write
&& playlist
->control_fd
>= 0)
945 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
946 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
947 global_status
.resume_seed
= 0;
954 /* Calculate how many steps we have to really step when skipping entries
957 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
959 int i
, count
, direction
;
961 int stepped_count
= 0;
974 index
= playlist
->index
;
979 index
+= playlist
->amount
;
980 if (index
>= playlist
->amount
)
981 index
-= playlist
->amount
;
983 /* Check if we found a bad entry. */
984 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
987 /* Are all entries bad? */
988 if (stepped_count
++ > playlist
->amount
)
995 } while (i
<= count
);
1000 /* Marks the index of the track to be skipped that is "steps" away from
1001 * current playing track.
1003 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1007 if (playlist
== NULL
)
1008 playlist
= ¤t_playlist
;
1010 /* need to account for already skipped tracks */
1011 steps
= calculate_step_count(playlist
, steps
);
1013 index
= playlist
->index
+ steps
;
1015 index
+= playlist
->amount
;
1016 else if (index
>= playlist
->amount
)
1017 index
-= playlist
->amount
;
1019 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
1023 * returns the index of the track that is "steps" away from current playing
1026 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
1029 int current_index
= playlist
->index
;
1030 int next_index
= -1;
1032 if (playlist
->amount
<= 0)
1035 if (repeat_mode
== -1)
1036 repeat_mode
= global_settings
.repeat_mode
;
1038 if (repeat_mode
== REPEAT_SHUFFLE
&& playlist
->amount
<= 1)
1039 repeat_mode
= REPEAT_ALL
;
1041 steps
= calculate_step_count(playlist
, steps
);
1042 switch (repeat_mode
)
1044 case REPEAT_SHUFFLE
:
1045 /* Treat repeat shuffle just like repeat off. At end of playlist,
1046 play will be resumed in playlist_next() */
1049 current_index
= rotate_index(playlist
, current_index
);
1050 next_index
= current_index
+steps
;
1051 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1054 next_index
= (next_index
+playlist
->first_index
) %
1061 #ifdef AB_REPEAT_ENABLE
1064 next_index
= current_index
;
1070 next_index
= (current_index
+steps
) % playlist
->amount
;
1071 while (next_index
< 0)
1072 next_index
+= playlist
->amount
;
1074 if (steps
>= playlist
->amount
)
1081 /* second time around so skip the queued files */
1082 for (i
=0; i
<playlist
->amount
; i
++)
1084 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1085 index
= (index
+1) % playlist
->amount
;
1097 /* No luck if the whole playlist was bad. */
1098 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1105 * Search for the seek track and set appropriate indices. Used after shuffle
1106 * to make sure the current index is still pointing to correct track.
1108 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1113 /* Set the index to the current song */
1114 for (i
=0; i
<playlist
->amount
; i
++)
1116 if (playlist
->indices
[i
] == seek
)
1118 playlist
->index
= playlist
->first_index
= i
;
1120 if (playlist
->current
)
1122 global_status
.resume_first_index
= i
;
1132 * used to sort track indices. Sort order is as follows:
1133 * 1. Prepended tracks (in prepend order)
1134 * 2. Playlist/directory tracks (in playlist order)
1135 * 3. Inserted/Appended tracks (in insert order)
1137 static int compare(const void* p1
, const void* p2
)
1139 unsigned long* e1
= (unsigned long*) p1
;
1140 unsigned long* e2
= (unsigned long*) p2
;
1141 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1142 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1144 if (flags1
== flags2
)
1145 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1146 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1147 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1149 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1150 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1152 else if (flags1
&& flags2
)
1153 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1158 #ifdef HAVE_DIRCACHE
1160 * Thread to update filename pointers to dircache on background
1161 * without affecting playlist load up performance. This thread also flushes
1162 * any pending control commands when the disk spins up.
1164 static void playlist_thread(void)
1166 struct queue_event ev
;
1167 bool dirty_pointers
= false;
1168 static char tmp
[MAX_PATH
+1];
1170 struct playlist_info
*playlist
;
1177 #ifndef HAVE_FLASH_STORAGE
1178 if (global_settings
.disk_spindown
> 1 &&
1179 global_settings
.disk_spindown
<= 5)
1180 sleep_time
= global_settings
.disk_spindown
- 1;
1185 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1189 case PLAYLIST_LOAD_POINTERS
:
1190 dirty_pointers
= true;
1193 /* Start the background scanning after either the disk spindown
1194 timeout or 5s, whichever is less */
1196 playlist
= ¤t_playlist
;
1198 if (playlist
->control_fd
>= 0
1200 && ata_disk_is_active()
1204 if (playlist
->num_cached
> 0)
1206 mutex_lock(&playlist
->control_mutex
);
1207 flush_cached_control(playlist
);
1208 mutex_unlock(&playlist
->control_mutex
);
1211 sync_control(playlist
, true);
1214 if (!dirty_pointers
)
1217 if (!dircache_is_enabled() || !playlist
->filenames
1218 || playlist
->amount
<= 0)
1221 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1224 for (index
= 0; index
< playlist
->amount
1225 && queue_empty(&playlist_queue
); index
++)
1227 /* Process only pointers that are not already loaded. */
1228 if (playlist
->filenames
[index
])
1231 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1232 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1234 /* Load the filename from playlist file. */
1235 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1239 /* Set the dircache entry pointer. */
1240 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1242 /* And be on background so user doesn't notice any delays. */
1246 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1249 dirty_pointers
= false;
1253 case SYS_USB_CONNECTED
:
1254 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1255 usb_wait_for_disconnect(&playlist_queue
);
1264 * gets pathname for track at seek index
1266 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1267 bool control_file
, char *buf
, int buf_length
)
1271 char tmp_buf
[MAX_PATH
+1];
1272 char dir_buf
[MAX_PATH
+1];
1274 if (buf_length
> MAX_PATH
+1)
1275 buf_length
= MAX_PATH
+1;
1277 #ifdef HAVE_DIRCACHE
1278 if (dircache_is_enabled() && playlist
->filenames
)
1280 if (playlist
->filenames
[index
] != NULL
)
1282 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1283 max
= strlen(tmp_buf
) + 1;
1290 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1292 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1293 tmp_buf
[MAX_PATH
] = '\0';
1294 max
= strlen(tmp_buf
) + 1;
1298 mutex_lock(&playlist
->control_mutex
);
1301 fd
= playlist
->control_fd
;
1304 if(-1 == playlist
->fd
)
1305 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1313 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1317 max
= read(fd
, tmp_buf
, MIN((size_t) buf_length
, sizeof(tmp_buf
)));
1319 if ((max
> 0) && !playlist
->utf8
)
1325 while ((tmp_buf
[i
] != '\n') && (tmp_buf
[i
] != '\r')
1331 /* Now work back killing white space. */
1332 while ((i
> 0) && isspace(tmp_buf
[i
- 1]))
1337 /* Borrow dir_buf a little... */
1338 /* TODO: iso_decode can overflow dir_buf; it really
1339 * should take a dest size argument.
1341 end
= iso_decode(tmp_buf
, dir_buf
, -1, i
);
1343 strncpy(tmp_buf
, dir_buf
, sizeof(tmp_buf
));
1344 tmp_buf
[sizeof(tmp_buf
) - 1] = 0;
1345 max
= strlen(tmp_buf
);
1350 mutex_unlock(&playlist
->control_mutex
);
1355 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1357 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
1363 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1364 dir_buf
[playlist
->dirlen
-1] = 0;
1366 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1369 static int get_next_directory(char *dir
){
1370 return get_next_dir(dir
,true,false);
1373 static int get_previous_directory(char *dir
){
1374 return get_next_dir(dir
,false,false);
1378 * search through all the directories (starting with the current) to find
1379 * one that has tracks to play
1381 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1383 struct playlist_info
* playlist
= ¤t_playlist
;
1385 int sort_dir
= global_settings
.sort_dir
;
1386 char *start_dir
= NULL
;
1388 struct tree_context
* tc
= tree_get_context();
1389 int dirfilter
= *(tc
->dirfilter
);
1391 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1393 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1394 char buffer
[MAX_PATH
];
1395 int folder_count
= 0,i
;
1396 srand(current_tick
);
1397 *(tc
->dirfilter
) = SHOW_MUSIC
;
1400 read(fd
,&folder_count
,sizeof(int));
1403 i
= rand()%folder_count
;
1404 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1405 read(fd
,buffer
,MAX_PATH
);
1406 if (check_subdir_for_music(buffer
,"") ==0)
1411 *(tc
->dirfilter
) = dirfilter
;
1416 /* not random folder advance */
1418 /* start with root */
1422 /* start with current directory */
1423 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1424 dir
[playlist
->dirlen
-1] = '\0';
1427 /* use the tree browser dircache to load files */
1428 *(tc
->dirfilter
) = SHOW_ALL
;
1430 /* sort in another direction if previous dir is requested */
1432 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1433 global_settings
.sort_dir
= 4;
1434 else if (global_settings
.sort_dir
== 1)
1435 global_settings
.sort_dir
= 2;
1436 else if (global_settings
.sort_dir
== 2)
1437 global_settings
.sort_dir
= 1;
1438 else if (global_settings
.sort_dir
== 4)
1439 global_settings
.sort_dir
= 0;
1444 struct entry
*files
;
1448 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1450 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1456 files
= (struct entry
*) tc
->dircache
;
1457 num_files
= tc
->filesindir
;
1459 for (i
=0; i
<num_files
; i
++)
1462 if (action_userabort(TIMEOUT_NOBLOCK
))
1469 if (files
[i
].attr
& ATTR_DIRECTORY
)
1473 result
= check_subdir_for_music(dir
, files
[i
].name
);
1480 else if (!strcmp(start_dir
, files
[i
].name
))
1487 /* move down to parent directory. current directory name is
1488 stored as the starting point for the search in parent */
1489 start_dir
= strrchr(dir
, '/');
1500 /* we've overwritten the dircache so tree browser will need to be
1504 /* restore dirfilter & sort_dir */
1505 *(tc
->dirfilter
) = dirfilter
;
1506 global_settings
.sort_dir
= sort_dir
;
1508 /* special case if nothing found: try start searching again from root */
1509 if (result
== -1 && !recursion
){
1510 result
= get_next_dir(dir
,is_forward
, true);
1517 * Checks if there are any music files in the dir or any of its
1518 * subdirectories. May be called recursively.
1520 static int check_subdir_for_music(char *dir
, char *subdir
)
1523 int dirlen
= strlen(dir
);
1526 struct entry
*files
;
1527 bool has_music
= false;
1528 bool has_subdir
= false;
1529 struct tree_context
* tc
= tree_get_context();
1531 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1533 if (ft_load(tc
, dir
) < 0)
1535 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1539 files
= (struct entry
*) tc
->dircache
;
1540 num_files
= tc
->filesindir
;
1542 for (i
=0; i
<num_files
; i
++)
1544 if (files
[i
].attr
& ATTR_DIRECTORY
)
1546 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
1558 for (i
=0; i
<num_files
; i
++)
1560 if (action_userabort(TIMEOUT_NOBLOCK
))
1566 if (files
[i
].attr
& ATTR_DIRECTORY
)
1568 result
= check_subdir_for_music(dir
, files
[i
].name
);
1586 /* we now need to reload our current directory */
1587 if(ft_load(tc
, dir
) < 0)
1588 gui_syncsplash(HZ
*2,
1589 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1596 * Returns absolute path of track
1598 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1605 /* Zero-terminate the file name */
1606 while((src
[i
] != '\n') &&
1611 /* Now work back killing white space */
1612 while((src
[i
-1] == ' ') ||
1618 /* replace backslashes with forward slashes */
1619 for ( j
=0; j
<i
; j
++ )
1620 if ( src
[j
] == '\\' )
1625 strncpy(dest
, src
, buf_length
);
1629 /* handle dos style drive letter */
1631 strncpy(dest
, &src
[2], buf_length
);
1632 else if (!strncmp(src
, "../", 3))
1634 /* handle relative paths */
1636 while(!strncmp(&src
[i
], "../", 3))
1638 for (j
=0; j
<i
/3; j
++) {
1639 temp_ptr
= strrchr(dir
, '/');
1645 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1647 else if ( '.' == src
[0] && '/' == src
[1] ) {
1648 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1651 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1659 * Display splash message showing progress of playlist/directory insertion or
1662 static void display_playlist_count(int count
, const unsigned char *fmt
,
1665 static long talked_tick
= 0;
1666 long id
= P2ID(fmt
);
1667 if(global_settings
.talk_menu
&& id
>=0)
1669 if(final
|| (count
&& (talked_tick
== 0
1670 || TIME_AFTER(current_tick
, talked_tick
+5*HZ
))))
1672 talked_tick
= current_tick
;
1673 talk_number(count
, false);
1679 lcd_clear_display();
1681 #ifdef HAVE_LCD_BITMAP
1682 if(global_settings
.statusbar
)
1683 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1685 lcd_setmargins(0, 0);
1688 gui_syncsplash(0, fmt
, count
, str(LANG_OFF_ABORT
));
1692 * Display buffer full message
1694 static void display_buffer_full(void)
1696 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_BUFFER_FULL
));
1700 * Flush any cached control commands to disk. Called when playlist is being
1701 * modified. Returns 0 on success and -1 on failure.
1703 static int flush_cached_control(struct playlist_info
* playlist
)
1708 if (!playlist
->num_cached
)
1711 lseek(playlist
->control_fd
, 0, SEEK_END
);
1713 for (i
=0; i
<playlist
->num_cached
; i
++)
1715 struct playlist_control_cache
* cache
=
1716 &(playlist
->control_cache
[i
]);
1718 switch (cache
->command
)
1720 case PLAYLIST_COMMAND_PLAYLIST
:
1721 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1722 cache
->i1
, cache
->s1
, cache
->s2
);
1724 case PLAYLIST_COMMAND_ADD
:
1725 case PLAYLIST_COMMAND_QUEUE
:
1726 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1727 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1728 cache
->i1
, cache
->i2
);
1731 /* save the position in file where name is written */
1732 int* seek_pos
= (int *)cache
->data
;
1733 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1734 result
= fdprintf(playlist
->control_fd
, "%s\n",
1738 case PLAYLIST_COMMAND_DELETE
:
1739 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1741 case PLAYLIST_COMMAND_SHUFFLE
:
1742 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1743 cache
->i1
, cache
->i2
);
1745 case PLAYLIST_COMMAND_UNSHUFFLE
:
1746 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1748 case PLAYLIST_COMMAND_RESET
:
1749 result
= fdprintf(playlist
->control_fd
, "R\n");
1761 if (global_status
.resume_seed
>= 0)
1763 global_status
.resume_seed
= -1;
1767 playlist
->num_cached
= 0;
1768 playlist
->pending_control_sync
= true;
1775 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1782 * Update control data with new command. Depending on the command, it may be
1783 * cached or flushed to disk.
1785 static int update_control(struct playlist_info
* playlist
,
1786 enum playlist_command command
, int i1
, int i2
,
1787 const char* s1
, const char* s2
, void* data
)
1790 struct playlist_control_cache
* cache
;
1793 mutex_lock(&playlist
->control_mutex
);
1795 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1797 cache
->command
= command
;
1806 case PLAYLIST_COMMAND_PLAYLIST
:
1807 case PLAYLIST_COMMAND_ADD
:
1808 case PLAYLIST_COMMAND_QUEUE
:
1809 #ifndef HAVE_DIRCACHE
1810 case PLAYLIST_COMMAND_DELETE
:
1811 case PLAYLIST_COMMAND_RESET
:
1815 case PLAYLIST_COMMAND_SHUFFLE
:
1816 case PLAYLIST_COMMAND_UNSHUFFLE
:
1818 /* only flush when needed */
1822 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1823 result
= flush_cached_control(playlist
);
1825 mutex_unlock(&playlist
->control_mutex
);
1831 * sync control file to disk
1833 static void sync_control(struct playlist_info
* playlist
, bool force
)
1835 #ifdef HAVE_DIRCACHE
1836 if (playlist
->started
&& force
)
1840 if (playlist
->started
)
1843 if (playlist
->pending_control_sync
)
1845 mutex_lock(&playlist
->control_mutex
);
1846 fsync(playlist
->control_fd
);
1847 playlist
->pending_control_sync
= false;
1848 mutex_unlock(&playlist
->control_mutex
);
1854 * Rotate indices such that first_index is index 0
1856 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1858 index
-= playlist
->first_index
;
1860 index
+= playlist
->amount
;
1866 * Initialize playlist entries at startup
1868 void playlist_init(void)
1870 struct playlist_info
* playlist
= ¤t_playlist
;
1872 playlist
->current
= true;
1873 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1874 "%s", PLAYLIST_CONTROL_FILE
);
1876 playlist
->control_fd
= -1;
1877 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1878 playlist
->indices
= buffer_alloc(
1879 playlist
->max_playlist_size
* sizeof(int));
1880 playlist
->buffer_size
=
1881 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1882 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1883 mutex_init(&playlist
->control_mutex
);
1884 empty_playlist(playlist
, true);
1886 #ifdef HAVE_DIRCACHE
1887 playlist
->filenames
= buffer_alloc(
1888 playlist
->max_playlist_size
* sizeof(int));
1889 memset(playlist
->filenames
, 0,
1890 playlist
->max_playlist_size
* sizeof(int));
1891 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1892 0, playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
)
1894 queue_init(&playlist_queue
, true);
1899 * Clean playlist at shutdown
1901 void playlist_shutdown(void)
1903 struct playlist_info
* playlist
= ¤t_playlist
;
1905 if (playlist
->control_fd
>= 0)
1907 mutex_lock(&playlist
->control_mutex
);
1909 if (playlist
->num_cached
> 0)
1910 flush_cached_control(playlist
);
1912 close(playlist
->control_fd
);
1914 mutex_unlock(&playlist
->control_mutex
);
1919 * Create new playlist
1921 int playlist_create(const char *dir
, const char *file
)
1923 struct playlist_info
* playlist
= ¤t_playlist
;
1925 new_playlist(playlist
, dir
, file
);
1928 /* load the playlist file */
1929 add_indices_to_playlist(playlist
, NULL
, 0);
1934 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1937 * Restore the playlist state based on control file commands. Called to
1938 * resume playback after shutdown.
1940 int playlist_resume(void)
1942 struct playlist_info
* playlist
= ¤t_playlist
;
1947 int control_file_size
= 0;
1951 /* use mp3 buffer for maximum load speed */
1952 #if CONFIG_CODEC != SWCODEC
1953 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1954 buflen
= (audiobufend
- audiobuf
);
1955 buffer
= (char *)audiobuf
;
1957 buffer
= (char *)audio_get_buffer(false, &buflen
);
1960 empty_playlist(playlist
, true);
1962 gui_syncsplash(0, ID2P(LANG_WAIT
));
1963 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1964 if (playlist
->control_fd
< 0)
1966 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1969 playlist
->control_created
= true;
1971 control_file_size
= filesize(playlist
->control_fd
);
1972 if (control_file_size
<= 0)
1974 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1978 /* read a small amount first to get the header */
1979 nread
= read(playlist
->control_fd
, buffer
,
1980 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1983 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1987 playlist
->started
= true;
1993 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1994 int last_newline
= 0;
1996 bool newline
= true;
1997 bool exit_loop
= false;
2002 unsigned long last_tick
= current_tick
;
2004 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
2006 /* So a splash while we are loading. */
2007 if (current_tick
- last_tick
> HZ
/4)
2009 gui_syncsplash(0, str(LANG_LOADING_PERCENT
),
2010 (total_read
+count
)*100/control_file_size
,
2011 str(LANG_OFF_ABORT
));
2012 if (action_userabort(TIMEOUT_NOBLOCK
))
2015 * Not sure how to implement this, somebody more familiar
2016 * with the code, please fix this. */
2018 last_tick
= current_tick
;
2021 /* Are we on a new line? */
2022 if((*p
== '\n') || (*p
== '\r'))
2026 /* save last_newline in case we need to load more data */
2027 last_newline
= count
;
2029 switch (current_command
)
2031 case PLAYLIST_COMMAND_PLAYLIST
:
2033 /* str1=version str2=dir str3=file */
2049 version
= atoi(str1
);
2051 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
2054 update_playlist_filename(playlist
, str2
, str3
);
2056 if (str3
[0] != '\0')
2058 /* NOTE: add_indices_to_playlist() overwrites the
2059 audiobuf so we need to reload control file
2061 add_indices_to_playlist(playlist
, NULL
, 0);
2063 else if (str2
[0] != '\0')
2065 playlist
->in_ram
= true;
2066 resume_directory(str2
);
2069 /* load the rest of the data */
2075 case PLAYLIST_COMMAND_ADD
:
2076 case PLAYLIST_COMMAND_QUEUE
:
2078 /* str1=position str2=last_position str3=file */
2079 int position
, last_position
;
2082 if (!str1
|| !str2
|| !str3
)
2089 position
= atoi(str1
);
2090 last_position
= atoi(str2
);
2092 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2095 /* seek position is based on str3's position in
2097 if (add_track_to_playlist(playlist
, str3
, position
,
2098 queue
, total_read
+(str3
-buffer
)) < 0)
2101 playlist
->last_insert_pos
= last_position
;
2105 case PLAYLIST_COMMAND_DELETE
:
2117 position
= atoi(str1
);
2119 if (remove_track_from_playlist(playlist
, position
,
2125 case PLAYLIST_COMMAND_SHUFFLE
:
2127 /* str1=seed str2=first_index */
2139 /* Always sort list before shuffling */
2140 sort_playlist(playlist
, false, false);
2144 playlist
->first_index
= atoi(str2
);
2146 if (randomise_playlist(playlist
, seed
, false,
2153 case PLAYLIST_COMMAND_UNSHUFFLE
:
2155 /* str1=first_index */
2163 playlist
->first_index
= atoi(str1
);
2165 if (sort_playlist(playlist
, false, false) < 0)
2171 case PLAYLIST_COMMAND_RESET
:
2173 playlist
->last_insert_pos
= -1;
2176 case PLAYLIST_COMMAND_COMMENT
:
2183 /* to ignore any extra newlines */
2184 current_command
= PLAYLIST_COMMAND_COMMENT
;
2190 /* first non-comment line must always specify playlist */
2191 if (first
&& *p
!= 'P' && *p
!= '#')
2201 /* playlist can only be specified once */
2209 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2212 current_command
= PLAYLIST_COMMAND_ADD
;
2215 current_command
= PLAYLIST_COMMAND_QUEUE
;
2218 current_command
= PLAYLIST_COMMAND_DELETE
;
2221 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2224 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2227 current_command
= PLAYLIST_COMMAND_RESET
;
2230 current_command
= PLAYLIST_COMMAND_COMMENT
;
2243 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2245 /* all control file strings are separated with a colon.
2246 Replace the colon with 0 to get proper strings that can be
2247 used by commands above */
2253 if ((count
+1) < nread
)
2267 /* allow last string to contain colons */
2278 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2282 if (!newline
|| (exit_loop
&& count
<nread
))
2284 if ((total_read
+ count
) >= control_file_size
)
2286 /* no newline at end of control file */
2287 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID
));
2291 /* We didn't end on a newline or we exited loop prematurely.
2292 Either way, re-read the remainder. */
2293 count
= last_newline
;
2294 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2297 total_read
+= count
;
2300 /* still looking for header */
2301 nread
= read(playlist
->control_fd
, buffer
,
2302 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2304 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2306 /* Terminate on EOF */
2309 if (global_status
.resume_seed
>= 0)
2311 /* Apply shuffle command saved in settings */
2312 if (global_status
.resume_seed
== 0)
2313 sort_playlist(playlist
, false, true);
2317 sort_playlist(playlist
, false, false);
2319 randomise_playlist(playlist
, global_status
.resume_seed
,
2324 playlist
->first_index
= global_status
.resume_first_index
;
2329 #ifdef HAVE_DIRCACHE
2330 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2337 * Add track to in_ram playlist. Used when playing directories.
2339 int playlist_add(const char *filename
)
2341 struct playlist_info
* playlist
= ¤t_playlist
;
2342 int len
= strlen(filename
);
2344 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2345 (playlist
->amount
>= playlist
->max_playlist_size
))
2347 display_buffer_full();
2351 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2352 #ifdef HAVE_DIRCACHE
2353 playlist
->filenames
[playlist
->amount
] = NULL
;
2357 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2358 playlist
->buffer_end_pos
+= len
;
2359 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2364 /* shuffle newly created playlist using random seed. */
2365 int playlist_shuffle(int random_seed
, int start_index
)
2367 struct playlist_info
* playlist
= ¤t_playlist
;
2369 unsigned int seek_pos
= 0;
2370 bool start_current
= false;
2372 if (start_index
>= 0 && global_settings
.play_selected
)
2374 /* store the seek position before the shuffle */
2375 seek_pos
= playlist
->indices
[start_index
];
2376 playlist
->index
= global_status
.resume_first_index
=
2377 playlist
->first_index
= start_index
;
2378 start_current
= true;
2381 randomise_playlist(playlist
, random_seed
, start_current
, true);
2383 return playlist
->index
;
2386 /* start playing current playlist at specified index/offset */
2387 int playlist_start(int start_index
, int offset
)
2389 struct playlist_info
* playlist
= ¤t_playlist
;
2391 /* Cancel FM radio selection as previous music. For cases where we start
2392 playback without going to the WPS, such as playlist insert.. or
2393 playlist catalog. */
2394 previous_music_is_wps();
2396 playlist
->index
= start_index
;
2398 #if CONFIG_CODEC != SWCODEC
2399 talk_buffer_steal(); /* will use the mp3 buffer */
2402 playlist
->started
= true;
2403 sync_control(playlist
, false);
2409 /* Returns false if 'steps' is out of bounds, else true */
2410 bool playlist_check(int steps
)
2412 struct playlist_info
* playlist
= ¤t_playlist
;
2414 /* always allow folder navigation */
2415 if (global_settings
.next_folder
&& playlist
->in_ram
)
2418 int index
= get_next_index(playlist
, steps
, -1);
2420 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2421 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2423 return (index
>= 0);
2426 /* get trackname of track that is "steps" away from current playing track.
2427 NULL is used to identify end of playlist */
2428 char* playlist_peek(int steps
)
2430 struct playlist_info
* playlist
= ¤t_playlist
;
2437 index
= get_next_index(playlist
, steps
, -1);
2441 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2442 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2444 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2448 temp_ptr
= now_playing
;
2450 if (!playlist
->in_ram
|| control_file
)
2452 /* remove bogus dirs from beginning of path
2453 (workaround for buggy playlist creation tools) */
2456 #ifdef HAVE_DIRCACHE
2457 if (dircache_is_enabled())
2459 if (dircache_get_entry_ptr(temp_ptr
))
2465 fd
= open(temp_ptr
, O_RDONLY
);
2473 temp_ptr
= strchr(temp_ptr
+1, '/');
2478 /* Even though this is an invalid file, we still need to pass a
2479 file name to the caller because NULL is used to indicate end
2489 * Update indices as track has changed
2491 int playlist_next(int steps
)
2493 struct playlist_info
* playlist
= ¤t_playlist
;
2497 #ifdef AB_REPEAT_ENABLE
2498 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2500 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2504 /* We need to delete all the queued songs */
2505 for (i
=0, j
=steps
; i
<j
; i
++)
2507 index
= get_next_index(playlist
, i
, -1);
2509 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2511 remove_track_from_playlist(playlist
, index
, true);
2512 steps
--; /* one less track */
2517 index
= get_next_index(playlist
, steps
, -1);
2521 /* end of playlist... or is it */
2522 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2523 playlist
->amount
> 1)
2525 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2526 playlist
->first_index
= global_status
.resume_first_index
= 0;
2527 sort_playlist(playlist
, false, false);
2528 randomise_playlist(playlist
, current_tick
, false, true);
2529 #if CONFIG_CODEC != SWCODEC
2530 playlist_start(0, 0);
2532 playlist
->index
= 0;
2535 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2537 char dir
[MAX_PATH
+1];
2539 changing_dir
= true;
2542 if (!get_next_directory(dir
))
2544 /* start playing next directory */
2545 if (playlist_create(dir
, NULL
) != -1)
2547 ft_build_playlist(tree_get_context(), 0);
2548 if (global_settings
.playlist_shuffle
)
2549 playlist_shuffle(current_tick
, -1);
2550 #if CONFIG_CODEC != SWCODEC
2551 playlist_start(0, 0);
2553 playlist
->index
= index
= 0;
2559 if (!get_previous_directory(dir
))
2561 /* start playing previous directory */
2562 if (playlist_create(dir
, NULL
) != -1)
2564 ft_build_playlist(tree_get_context(), 0);
2565 if (global_settings
.playlist_shuffle
)
2566 playlist_shuffle(current_tick
, -1);
2567 #if CONFIG_CODEC != SWCODEC
2568 playlist_start(current_playlist
.amount
-1, 0);
2570 playlist
->index
= index
= current_playlist
.amount
- 1;
2574 changing_dir
= false;
2580 playlist
->index
= index
;
2582 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2584 /* check to see if we've gone beyond the last inserted track */
2585 int cur
= rotate_index(playlist
, index
);
2586 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2590 /* reset last inserted track */
2591 playlist
->last_insert_pos
= -1;
2593 if (playlist
->control_fd
>= 0)
2595 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2596 -1, -1, NULL
, NULL
, NULL
);
2601 sync_control(playlist
, false);
2609 /* try playing next or previous folder */
2610 bool playlist_next_dir(int direction
)
2612 char dir
[MAX_PATH
+1];
2616 /* not to mess up real playlists */
2617 if(!current_playlist
.in_ram
)
2623 changing_dir
= true;
2625 res
= get_next_directory(dir
);
2627 res
= get_previous_directory(dir
);
2630 if (playlist_create(dir
, NULL
) != -1)
2632 ft_build_playlist(tree_get_context(), 0);
2633 if (global_settings
.playlist_shuffle
)
2634 playlist_shuffle(current_tick
, -1);
2635 #if (CONFIG_CODEC != SWCODEC)
2636 playlist_start(0,0);
2646 changing_dir
= false;
2651 /* Get resume info for current playing song. If return value is -1 then
2652 settings shouldn't be saved. */
2653 int playlist_get_resume_info(int *resume_index
)
2655 struct playlist_info
* playlist
= ¤t_playlist
;
2657 *resume_index
= playlist
->index
;
2662 /* Update resume info for current playing song. Returns -1 on error. */
2663 int playlist_update_resume_info(const struct mp3entry
* id3
)
2665 struct playlist_info
* playlist
= ¤t_playlist
;
2669 if (global_status
.resume_index
!= playlist
->index
||
2670 global_status
.resume_offset
!= id3
->offset
)
2672 global_status
.resume_index
= playlist
->index
;
2673 global_status
.resume_offset
= id3
->offset
;
2679 global_status
.resume_index
= -1;
2680 global_status
.resume_offset
= -1;
2687 /* Returns index of current playing track for display purposes. This value
2688 should not be used for resume purposes as it doesn't represent the actual
2689 index into the playlist */
2690 int playlist_get_display_index(void)
2692 struct playlist_info
* playlist
= ¤t_playlist
;
2694 /* first_index should always be index 0 for display purposes */
2695 int index
= rotate_index(playlist
, playlist
->index
);
2700 /* returns number of tracks in current playlist */
2701 int playlist_amount(void)
2703 return playlist_amount_ex(NULL
);
2707 * Create a new playlist If playlist is not NULL then we're loading a
2708 * playlist off disk for viewing/editing. The index_buffer is used to store
2709 * playlist indices (required for and only used if !current playlist). The
2710 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2712 int playlist_create_ex(struct playlist_info
* playlist
,
2713 const char* dir
, const char* file
,
2714 void* index_buffer
, int index_buffer_size
,
2715 void* temp_buffer
, int temp_buffer_size
)
2718 playlist
= ¤t_playlist
;
2721 /* Initialize playlist structure */
2722 int r
= rand() % 10;
2723 playlist
->current
= false;
2725 /* Use random name for control file */
2726 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2727 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2729 playlist
->control_fd
= -1;
2733 int num_indices
= index_buffer_size
/ sizeof(int);
2735 #ifdef HAVE_DIRCACHE
2738 if (num_indices
> global_settings
.max_files_in_playlist
)
2739 num_indices
= global_settings
.max_files_in_playlist
;
2741 playlist
->max_playlist_size
= num_indices
;
2742 playlist
->indices
= index_buffer
;
2743 #ifdef HAVE_DIRCACHE
2744 playlist
->filenames
= (const struct dircache_entry
**)
2745 &playlist
->indices
[num_indices
];
2750 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2751 playlist
->indices
= current_playlist
.indices
;
2752 #ifdef HAVE_DIRCACHE
2753 playlist
->filenames
= current_playlist
.filenames
;
2757 playlist
->buffer_size
= 0;
2758 playlist
->buffer
= NULL
;
2759 mutex_init(&playlist
->control_mutex
);
2762 new_playlist(playlist
, dir
, file
);
2765 /* load the playlist file */
2766 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2772 * Set the specified playlist as the current.
2773 * NOTE: You will get undefined behaviour if something is already playing so
2774 * remember to stop before calling this. Also, this call will
2775 * effectively close your playlist, making it unusable.
2777 int playlist_set_current(struct playlist_info
* playlist
)
2779 if (!playlist
|| (check_control(playlist
) < 0))
2782 empty_playlist(¤t_playlist
, false);
2784 strncpy(current_playlist
.filename
, playlist
->filename
,
2785 sizeof(current_playlist
.filename
));
2787 current_playlist
.utf8
= playlist
->utf8
;
2788 current_playlist
.fd
= playlist
->fd
;
2790 close(playlist
->control_fd
);
2791 close(current_playlist
.control_fd
);
2792 remove(current_playlist
.control_filename
);
2793 if (rename(playlist
->control_filename
,
2794 current_playlist
.control_filename
) < 0)
2796 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2798 if (current_playlist
.control_fd
< 0)
2800 current_playlist
.control_created
= true;
2802 current_playlist
.dirlen
= playlist
->dirlen
;
2804 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2806 memcpy(current_playlist
.indices
, playlist
->indices
,
2807 playlist
->max_playlist_size
*sizeof(int));
2808 #ifdef HAVE_DIRCACHE
2809 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2810 playlist
->max_playlist_size
*sizeof(int));
2814 current_playlist
.first_index
= playlist
->first_index
;
2815 current_playlist
.amount
= playlist
->amount
;
2816 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2817 current_playlist
.seed
= playlist
->seed
;
2818 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2819 current_playlist
.deleted
= playlist
->deleted
;
2820 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2822 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2823 sizeof(current_playlist
.control_cache
));
2824 current_playlist
.num_cached
= playlist
->num_cached
;
2825 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2831 * Close files and delete control file for non-current playlist.
2833 void playlist_close(struct playlist_info
* playlist
)
2838 if (playlist
->fd
>= 0)
2839 close(playlist
->fd
);
2841 if (playlist
->control_fd
>= 0)
2842 close(playlist
->control_fd
);
2844 if (playlist
->control_created
)
2845 remove(playlist
->control_filename
);
2848 void playlist_sync(struct playlist_info
* playlist
)
2851 playlist
= ¤t_playlist
;
2853 sync_control(playlist
, false);
2854 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2855 audio_flush_and_reload_tracks();
2857 #ifdef HAVE_DIRCACHE
2858 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2863 * Insert track into playlist at specified position (or one of the special
2864 * positions). Returns position where track was inserted or -1 if error.
2866 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2867 int position
, bool queue
, bool sync
)
2872 playlist
= ¤t_playlist
;
2874 if (check_control(playlist
) < 0)
2876 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2880 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2882 /* Check if we want manually sync later. For example when adding
2883 * bunch of files from tagcache, syncing after every file wouldn't be
2884 * a good thing to do. */
2885 if (sync
&& result
>= 0)
2886 playlist_sync(playlist
);
2892 * Insert all tracks from specified directory into playlist.
2894 int playlist_insert_directory(struct playlist_info
* playlist
,
2895 const char *dirname
, int position
, bool queue
,
2899 unsigned char *count_str
;
2900 struct directory_search_context context
;
2903 playlist
= ¤t_playlist
;
2905 if (check_control(playlist
) < 0)
2907 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2911 if (position
== PLAYLIST_REPLACE
)
2913 if (remove_all_tracks(playlist
) == 0)
2914 position
= PLAYLIST_INSERT_LAST
;
2920 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2922 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2924 display_playlist_count(0, count_str
, false);
2926 context
.playlist
= playlist
;
2927 context
.position
= position
;
2928 context
.queue
= queue
;
2933 result
= playlist_directory_tracksearch(dirname
, recurse
,
2934 directory_search_callback
, &context
);
2936 sync_control(playlist
, false);
2940 display_playlist_count(context
.count
, count_str
, true);
2942 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2943 audio_flush_and_reload_tracks();
2945 #ifdef HAVE_DIRCACHE
2946 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2953 * Insert all tracks from specified playlist into dynamic playlist.
2955 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2956 int position
, bool queue
)
2962 unsigned char *count_str
;
2963 char temp_buf
[MAX_PATH
+1];
2964 char trackname
[MAX_PATH
+1];
2969 playlist
= ¤t_playlist
;
2971 if (check_control(playlist
) < 0)
2973 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2977 fd
= open(filename
, O_RDONLY
);
2980 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
2984 /* we need the directory name for formatting purposes */
2987 temp_ptr
= strrchr(filename
+1,'/');
2994 count_str
= ID2P(LANG_PLAYLIST_QUEUE_COUNT
);
2996 count_str
= ID2P(LANG_PLAYLIST_INSERT_COUNT
);
2998 display_playlist_count(count
, count_str
, false);
3000 if (position
== PLAYLIST_REPLACE
)
3002 if (remove_all_tracks(playlist
) == 0)
3003 position
= PLAYLIST_INSERT_LAST
;
3009 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
3012 if (action_userabort(TIMEOUT_NOBLOCK
))
3015 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
3019 /* we need to format so that relative paths are correctly
3021 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
3028 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
3037 /* Make sure tracks are inserted in correct order if user
3038 requests INSERT_FIRST */
3039 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
3040 position
= insert_pos
+ 1;
3044 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
3046 display_playlist_count(count
, count_str
, false);
3048 if (count
== PLAYLIST_DISPLAY_COUNT
&&
3049 (audio_status() & AUDIO_STATUS_PLAY
) &&
3051 audio_flush_and_reload_tracks();
3055 /* let the other threads work */
3064 sync_control(playlist
, false);
3068 display_playlist_count(count
, count_str
, true);
3070 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3071 audio_flush_and_reload_tracks();
3073 #ifdef HAVE_DIRCACHE
3074 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3081 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3082 * we want to delete the current playing track.
3084 int playlist_delete(struct playlist_info
* playlist
, int index
)
3089 playlist
= ¤t_playlist
;
3091 if (check_control(playlist
) < 0)
3093 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3097 if (index
== PLAYLIST_DELETE_CURRENT
)
3098 index
= playlist
->index
;
3100 result
= remove_track_from_playlist(playlist
, index
, true);
3102 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3104 audio_flush_and_reload_tracks();
3110 * Move track at index to new_index. Tracks between the two are shifted
3111 * appropriately. Returns 0 on success and -1 on failure.
3113 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3119 bool current
= false;
3121 char filename
[MAX_PATH
];
3124 playlist
= ¤t_playlist
;
3126 if (check_control(playlist
) < 0)
3128 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3132 if (index
== new_index
)
3135 if (index
== playlist
->index
)
3136 /* Moving the current track */
3139 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3140 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3141 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3143 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3144 sizeof(filename
)) < 0)
3147 /* Delete track from original position */
3148 result
= remove_track_from_playlist(playlist
, index
, true);
3152 /* We want to insert the track at the position that was specified by
3153 new_index. This may be different then new_index because of the
3154 shifting that occurred after the delete */
3155 r
= rotate_index(playlist
, new_index
);
3159 new_index
= PLAYLIST_PREPEND
;
3160 else if (r
== playlist
->amount
)
3162 new_index
= PLAYLIST_INSERT_LAST
;
3164 /* Calculate index of desired position */
3165 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3167 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3174 /* Moved the current track */
3177 case PLAYLIST_PREPEND
:
3178 playlist
->index
= playlist
->first_index
;
3180 case PLAYLIST_INSERT_LAST
:
3181 playlist
->index
= playlist
->first_index
- 1;
3182 if (playlist
->index
< 0)
3183 playlist
->index
+= playlist
->amount
;
3186 playlist
->index
= new_index
;
3191 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3192 audio_flush_and_reload_tracks();
3196 #ifdef HAVE_DIRCACHE
3197 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3203 /* shuffle currently playing playlist */
3204 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3210 playlist
= ¤t_playlist
;
3212 check_control(playlist
);
3214 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3216 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3218 audio_flush_and_reload_tracks();
3223 /* sort currently playing playlist */
3224 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3229 playlist
= ¤t_playlist
;
3231 check_control(playlist
);
3233 result
= sort_playlist(playlist
, start_current
, true);
3235 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3237 audio_flush_and_reload_tracks();
3242 /* returns true if playlist has been modified */
3243 bool playlist_modified(const struct playlist_info
* playlist
)
3246 playlist
= ¤t_playlist
;
3248 if (playlist
->shuffle_modified
||
3249 playlist
->deleted
||
3250 playlist
->num_inserted_tracks
> 0)
3256 /* returns index of first track in playlist */
3257 int playlist_get_first_index(const struct playlist_info
* playlist
)
3260 playlist
= ¤t_playlist
;
3262 return playlist
->first_index
;
3265 /* returns shuffle seed of playlist */
3266 int playlist_get_seed(const struct playlist_info
* playlist
)
3269 playlist
= ¤t_playlist
;
3271 return playlist
->seed
;
3274 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3275 int playlist_amount_ex(const struct playlist_info
* playlist
)
3278 playlist
= ¤t_playlist
;
3280 return playlist
->amount
;
3283 /* returns full path of playlist (minus extension) */
3284 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3290 playlist
= ¤t_playlist
;
3292 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3297 /* Remove extension */
3298 sep
= strrchr(buf
, '.');
3305 /* returns the playlist filename */
3306 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3310 playlist
= ¤t_playlist
;
3312 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3320 /* Fills info structure with information about track at specified index.
3321 Returns 0 on success and -1 on failure */
3322 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3323 struct playlist_track_info
* info
)
3329 playlist
= ¤t_playlist
;
3331 if (index
< 0 || index
>= playlist
->amount
)
3334 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3335 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3337 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3338 sizeof(info
->filename
)) < 0)
3345 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3346 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3348 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3352 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3353 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3355 info
->index
= index
;
3356 info
->display_index
= rotate_index(playlist
, index
) + 1;
3361 /* save the current dynamic playlist to specified file */
3362 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3367 char path
[MAX_PATH
+1];
3368 char tmp_buf
[MAX_PATH
+1];
3370 bool overwrite_current
= false;
3371 int* index_buf
= NULL
;
3374 playlist
= ¤t_playlist
;
3376 if (playlist
->amount
<= 0)
3379 /* use current working directory as base for pathname */
3380 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3381 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3384 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3386 /* Attempting to overwrite current playlist file.*/
3388 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3390 /* not enough buffer space to store updated indices */
3391 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3395 /* in_ram buffer is unused for m3u files so we'll use for storing
3397 index_buf
= (int*)playlist
->buffer
;
3399 /* use temporary pathname */
3400 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3401 overwrite_current
= true;
3404 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3407 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3411 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), false);
3415 index
= playlist
->first_index
;
3416 for (i
=0; i
<playlist
->amount
; i
++)
3423 if (action_userabort(TIMEOUT_NOBLOCK
))
3429 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3430 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3431 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3433 /* Don't save queued files */
3436 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3443 if (overwrite_current
)
3444 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3446 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3448 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR
));
3455 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3456 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
),
3462 index
= (index
+1)%playlist
->amount
;
3465 display_playlist_count(count
, ID2P(LANG_PLAYLIST_SAVE_COUNT
), true);
3469 if (overwrite_current
&& result
>= 0)
3473 mutex_lock(&playlist
->control_mutex
);
3475 /* Replace the current playlist with the new one and update indices */
3476 close(playlist
->fd
);
3477 if (remove(playlist
->filename
) >= 0)
3479 if (rename(path
, playlist
->filename
) >= 0)
3481 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3482 if (playlist
->fd
>= 0)
3484 index
= playlist
->first_index
;
3485 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3487 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3489 playlist
->indices
[index
] = index_buf
[count
];
3492 index
= (index
+1)%playlist
->amount
;
3495 /* we need to recreate control because inserted tracks are
3496 now part of the playlist and shuffle has been
3498 result
= recreate_control(playlist
);
3503 mutex_unlock(&playlist
->control_mutex
);
3513 * Search specified directory for tracks and notify via callback. May be
3514 * called recursively.
3516 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3517 int (*callback
)(char*, void*),
3520 char buf
[MAX_PATH
+1];
3524 struct entry
*files
;
3525 struct tree_context
* tc
= tree_get_context();
3526 int old_dirfilter
= *(tc
->dirfilter
);
3531 /* use the tree browser dircache to load files */
3532 *(tc
->dirfilter
) = SHOW_ALL
;
3534 if (ft_load(tc
, dirname
) < 0)
3536 gui_syncsplash(HZ
*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3537 *(tc
->dirfilter
) = old_dirfilter
;
3541 files
= (struct entry
*) tc
->dircache
;
3542 num_files
= tc
->filesindir
;
3544 /* we've overwritten the dircache so tree browser will need to be
3548 for (i
=0; i
<num_files
; i
++)
3551 if (action_userabort(TIMEOUT_NOBLOCK
))
3557 if (files
[i
].attr
& ATTR_DIRECTORY
)
3561 /* recursively add directories */
3562 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3563 result
= playlist_directory_tracksearch(buf
, recurse
,
3568 /* we now need to reload our current directory */
3569 if(ft_load(tc
, dirname
) < 0)
3575 files
= (struct entry
*) tc
->dircache
;
3576 num_files
= tc
->filesindir
;
3586 else if ((files
[i
].attr
& FILE_ATTR_MASK
) == FILE_ATTR_AUDIO
)
3588 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3590 if (callback(buf
, context
) != 0)
3596 /* let the other threads work */
3601 /* restore dirfilter */
3602 *(tc
->dirfilter
) = old_dirfilter
;