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.
81 #include "applimits.h"
92 #ifdef HAVE_LCD_BITMAP
101 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
102 #define PLAYLIST_CONTROL_FILE_VERSION 2
105 Each playlist index has a flag associated with it which identifies what
106 type of track it is. These flags are stored in the 4 high order bits of
109 NOTE: This limits the playlist file size to a max of 256M.
113 01 = Track was prepended into playlist
114 10 = Track was inserted into playlist
115 11 = Track was appended into playlist
120 0 = Track entry is valid
121 1 = Track does not exist on disk and should be skipped
123 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
124 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
125 #define PLAYLIST_QUEUE_MASK 0x20000000
127 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
128 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
129 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
131 #define PLAYLIST_QUEUED 0x20000000
132 #define PLAYLIST_SKIPPED 0x10000000
134 #define PLAYLIST_DISPLAY_COUNT 10
136 static bool changing_dir
= false;
138 static struct playlist_info current_playlist
;
139 static char now_playing
[MAX_PATH
+1];
141 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
142 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
144 static void create_control(struct playlist_info
* playlist
);
145 static int check_control(struct playlist_info
* playlist
);
146 static int recreate_control(struct playlist_info
* playlist
);
147 static void update_playlist_filename(struct playlist_info
* playlist
,
148 const char *dir
, const char *file
);
149 static int add_indices_to_playlist(struct playlist_info
* playlist
,
150 char* buffer
, int buflen
);
151 static int add_track_to_playlist(struct playlist_info
* playlist
,
152 const char *filename
, int position
,
153 bool queue
, int seek_pos
);
154 static int add_directory_to_playlist(struct playlist_info
* playlist
,
155 const char *dirname
, int *position
,
156 bool queue
, int *count
, bool recurse
);
157 static int remove_track_from_playlist(struct playlist_info
* playlist
,
158 int position
, bool write
);
159 static int randomise_playlist(struct playlist_info
* playlist
,
160 unsigned int seed
, bool start_current
,
162 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
164 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
166 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
168 static int compare(const void* p1
, const void* p2
);
169 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
170 bool control_file
, char *buf
, int buf_length
);
171 static int get_next_directory(char *dir
);
172 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
173 static int get_previous_directory(char *dir
);
174 static int check_subdir_for_music(char *dir
, char *subdir
);
175 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
177 static void display_playlist_count(int count
, const unsigned char *fmt
);
178 static void display_buffer_full(void);
179 static int flush_cached_control(struct playlist_info
* playlist
);
180 static int update_control(struct playlist_info
* playlist
,
181 enum playlist_command command
, int i1
, int i2
,
182 const char* s1
, const char* s2
, void* data
);
183 static void sync_control(struct playlist_info
* playlist
, bool force
);
184 static int rotate_index(const struct playlist_info
* playlist
, int index
);
187 #define PLAYLIST_LOAD_POINTERS 1
189 static struct event_queue playlist_queue
;
190 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x400)/sizeof(long)];
191 static const char playlist_thread_name
[] = "playlist cachectrl";
195 * remove any files and indices associated with the playlist
197 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
199 playlist
->filename
[0] = '\0';
201 if(playlist
->fd
>= 0)
202 /* If there is an already open playlist, close it. */
206 if(playlist
->control_fd
>= 0)
207 close(playlist
->control_fd
);
208 playlist
->control_fd
= -1;
209 playlist
->control_created
= false;
211 playlist
->in_ram
= false;
213 if (playlist
->buffer
)
214 playlist
->buffer
[0] = 0;
216 playlist
->buffer_end_pos
= 0;
219 playlist
->first_index
= 0;
220 playlist
->amount
= 0;
221 playlist
->last_insert_pos
= -1;
223 playlist
->shuffle_modified
= false;
224 playlist
->deleted
= false;
225 playlist
->num_inserted_tracks
= 0;
227 playlist
->num_cached
= 0;
228 playlist
->pending_control_sync
= false;
230 if (!resume
&& playlist
->current
)
232 /* start with fresh playlist control file when starting new
234 create_control(playlist
);
236 /* Reset resume settings */
237 global_settings
.resume_first_index
= 0;
238 global_settings
.resume_seed
= -1;
243 * Initialize a new playlist for viewing/editing/playing. dir is the
244 * directory where the playlist is located and file is the filename.
246 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
249 empty_playlist(playlist
, false);
255 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
256 playlist
->in_ram
= true;
258 dir
= ""; /* empty playlist */
261 update_playlist_filename(playlist
, dir
, file
);
263 if (playlist
->control_fd
>= 0)
265 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
266 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
267 sync_control(playlist
, false);
272 * create control file for playlist
274 static void create_control(struct playlist_info
* playlist
)
276 playlist
->control_fd
= open(playlist
->control_filename
,
277 O_CREAT
|O_RDWR
|O_TRUNC
);
278 if (playlist
->control_fd
< 0)
280 if (check_rockboxdir())
282 gui_syncsplash(HZ
*2, true, (unsigned char *)"%s (%d)",
283 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
284 playlist
->control_fd
);
286 playlist
->control_created
= false;
290 playlist
->control_created
= true;
295 * validate the control file. This may include creating/initializing it if
298 static int check_control(struct playlist_info
* playlist
)
300 if (!playlist
->control_created
)
302 create_control(playlist
);
304 if (playlist
->control_fd
>= 0)
306 char* dir
= playlist
->filename
;
307 char* file
= playlist
->filename
+playlist
->dirlen
;
308 char c
= playlist
->filename
[playlist
->dirlen
-1];
310 playlist
->filename
[playlist
->dirlen
-1] = '\0';
312 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
313 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
314 sync_control(playlist
, false);
315 playlist
->filename
[playlist
->dirlen
-1] = c
;
319 if (playlist
->control_fd
< 0)
326 * recreate the control file based on current playlist entries
328 static int recreate_control(struct playlist_info
* playlist
)
330 char temp_file
[MAX_PATH
+1];
335 if(playlist
->control_fd
>= 0)
337 char* dir
= playlist
->filename
;
338 char* file
= playlist
->filename
+playlist
->dirlen
;
339 char c
= playlist
->filename
[playlist
->dirlen
-1];
341 close(playlist
->control_fd
);
343 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
344 playlist
->control_filename
);
346 if (rename(playlist
->control_filename
, temp_file
) < 0)
349 temp_fd
= open(temp_file
, O_RDONLY
);
353 playlist
->control_fd
= open(playlist
->control_filename
,
354 O_CREAT
|O_RDWR
|O_TRUNC
);
355 if (playlist
->control_fd
< 0)
358 playlist
->filename
[playlist
->dirlen
-1] = '\0';
360 /* cannot call update_control() because of mutex */
361 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
362 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
364 playlist
->filename
[playlist
->dirlen
-1] = c
;
374 playlist
->shuffle_modified
= false;
375 playlist
->deleted
= false;
376 playlist
->num_inserted_tracks
= 0;
378 if (playlist
->current
)
380 global_settings
.resume_seed
= -1;
384 for (i
=0; i
<playlist
->amount
; i
++)
386 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
388 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
389 char inserted_file
[MAX_PATH
+1];
391 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
393 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
395 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
396 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
399 /* save the position in file where name is written */
400 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
402 result
= fdprintf(playlist
->control_fd
, "%s\n",
405 playlist
->indices
[i
] =
406 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
412 playlist
->num_inserted_tracks
++;
418 fsync(playlist
->control_fd
);
427 * store directory and name of playlist file
429 static void update_playlist_filename(struct playlist_info
* playlist
,
430 const char *dir
, const char *file
)
433 int dirlen
= strlen(dir
);
435 /* If the dir does not end in trailing slash, we use a separator.
436 Otherwise we don't. */
437 if('/' != dir
[dirlen
-1])
443 playlist
->dirlen
= dirlen
;
445 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
446 "%s%s%s", dir
, sep
, file
);
450 * calculate track offsets within a playlist file
452 static int add_indices_to_playlist(struct playlist_info
* playlist
,
453 char* buffer
, int buflen
)
457 unsigned int count
= 0;
461 if(-1 == playlist
->fd
)
462 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
464 return -1; /* failure */
466 #ifdef HAVE_LCD_BITMAP
467 if(global_settings
.statusbar
)
468 lcd_setmargins(0, STATUSBAR_HEIGHT
);
470 lcd_setmargins(0, 0);
473 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD
));
477 /* use mp3 buffer for maximum load speed */
479 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
481 buffer
= (char *)audiobuf
;
482 buflen
= (audiobufend
- audiobuf
);
489 nread
= read(playlist
->fd
, buffer
, buflen
);
490 /* Terminate on EOF */
494 p
= (unsigned char *)buffer
;
496 for(count
=0; count
< nread
; count
++,p
++) {
498 /* Are we on a new line? */
499 if((*p
== '\n') || (*p
== '\r'))
509 /* Store a new entry */
510 playlist
->indices
[ playlist
->amount
] = i
+count
;
512 if (playlist
->filenames
)
513 playlist
->filenames
[ playlist
->amount
] = NULL
;
515 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
516 display_buffer_full();
528 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
535 * Add track to playlist at specified position. There are five special
536 * positions that can be specified:
537 * PLAYLIST_PREPEND - Add track at beginning of playlist
538 * PLAYLIST_INSERT - Add track after current song. NOTE: If
539 * there are already inserted tracks then track
540 * is added to the end of the insertion list
541 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
542 * matter what other tracks have been inserted
543 * PLAYLIST_INSERT_LAST - Add track to end of playlist
544 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
545 * current playing track and end of playlist
547 static int add_track_to_playlist(struct playlist_info
* playlist
,
548 const char *filename
, int position
,
549 bool queue
, int seek_pos
)
551 int insert_position
= position
;
552 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
555 if (playlist
->amount
>= playlist
->max_playlist_size
)
557 display_buffer_full();
563 case PLAYLIST_PREPEND
:
564 insert_position
= playlist
->first_index
;
565 flags
= PLAYLIST_INSERT_TYPE_PREPEND
;
567 case PLAYLIST_INSERT
:
568 /* if there are already inserted tracks then add track to end of
569 insertion list else add after current playing track */
570 if (playlist
->last_insert_pos
>= 0 &&
571 playlist
->last_insert_pos
< playlist
->amount
&&
572 (playlist
->indices
[playlist
->last_insert_pos
]&
573 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
574 position
= insert_position
= playlist
->last_insert_pos
+1;
575 else if (playlist
->amount
> 0)
576 position
= insert_position
= playlist
->index
+ 1;
578 position
= insert_position
= 0;
580 playlist
->last_insert_pos
= position
;
582 case PLAYLIST_INSERT_FIRST
:
583 if (playlist
->amount
> 0)
584 position
= insert_position
= playlist
->index
+ 1;
586 position
= insert_position
= 0;
588 if (playlist
->last_insert_pos
< 0)
589 playlist
->last_insert_pos
= position
;
591 case PLAYLIST_INSERT_LAST
:
592 if (playlist
->first_index
> 0)
593 insert_position
= playlist
->first_index
;
595 insert_position
= playlist
->amount
;
597 flags
= PLAYLIST_INSERT_TYPE_APPEND
;
599 case PLAYLIST_INSERT_SHUFFLED
:
602 int n
= playlist
->amount
-
603 rotate_index(playlist
, playlist
->index
);
610 position
= playlist
->index
+ offset
+ 1;
611 if (position
>= playlist
->amount
)
612 position
-= playlist
->amount
;
614 insert_position
= position
;
620 flags
|= PLAYLIST_QUEUED
;
622 /* shift indices so that track can be added */
623 for (i
=playlist
->amount
; i
>insert_position
; i
--)
625 playlist
->indices
[i
] = playlist
->indices
[i
-1];
627 if (playlist
->filenames
)
628 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
632 /* update stored indices if needed */
633 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
)
636 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
637 position
!= PLAYLIST_PREPEND
)
639 playlist
->first_index
++;
641 if (seek_pos
< 0 && playlist
->current
)
643 global_settings
.resume_first_index
= playlist
->first_index
;
648 if (insert_position
< playlist
->last_insert_pos
||
649 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
650 playlist
->last_insert_pos
++;
652 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
654 int result
= update_control(playlist
,
655 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
656 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
662 playlist
->indices
[insert_position
] = flags
| seek_pos
;
665 if (playlist
->filenames
)
666 playlist
->filenames
[insert_position
] = NULL
;
670 playlist
->num_inserted_tracks
++;
672 return insert_position
;
676 * Insert directory into playlist. May be called recursively.
678 static int add_directory_to_playlist(struct playlist_info
* playlist
,
679 const char *dirname
, int *position
,
680 bool queue
, int *count
, bool recurse
)
682 char buf
[MAX_PATH
+1];
683 unsigned char *count_str
;
687 int dirfilter
= global_settings
.dirfilter
;
689 struct tree_context
* tc
= tree_get_context();
691 /* use the tree browser dircache to load files */
692 global_settings
.dirfilter
= SHOW_ALL
;
694 if (ft_load(tc
, dirname
) < 0)
696 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
697 global_settings
.dirfilter
= dirfilter
;
701 files
= (struct entry
*) tc
->dircache
;
702 num_files
= tc
->filesindir
;
704 /* we've overwritten the dircache so tree browser will need to be
709 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
711 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
713 for (i
=0; i
<num_files
; i
++)
716 if (button_get(false) == SETTINGS_CANCEL
)
722 if (files
[i
].attr
& ATTR_DIRECTORY
)
726 /* recursively add directories */
727 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
728 result
= add_directory_to_playlist(playlist
, buf
, position
,
729 queue
, count
, recurse
);
733 /* we now need to reload our current directory */
734 if(ft_load(tc
, dirname
) < 0)
740 files
= (struct entry
*) tc
->dircache
;
741 num_files
= tc
->filesindir
;
751 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
755 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
757 insert_pos
= add_track_to_playlist(playlist
, buf
, *position
,
767 /* Make sure tracks are inserted in correct order if user requests
769 if (*position
== PLAYLIST_INSERT_FIRST
|| *position
>= 0)
770 *position
= insert_pos
+ 1;
772 if ((*count
%PLAYLIST_DISPLAY_COUNT
) == 0)
774 display_playlist_count(*count
, count_str
);
776 if (*count
== PLAYLIST_DISPLAY_COUNT
&&
777 (audio_status() & AUDIO_STATUS_PLAY
))
778 audio_flush_and_reload_tracks();
781 /* let the other threads work */
786 /* restore dirfilter */
787 global_settings
.dirfilter
= dirfilter
;
793 * remove track at specified position
795 static int remove_track_from_playlist(struct playlist_info
* playlist
,
796 int position
, bool write
)
801 if (playlist
->amount
<= 0)
804 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
806 /* shift indices now that track has been removed */
807 for (i
=position
; i
<playlist
->amount
; i
++)
809 playlist
->indices
[i
] = playlist
->indices
[i
+1];
811 if (playlist
->filenames
)
812 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
819 playlist
->num_inserted_tracks
--;
821 playlist
->deleted
= true;
823 /* update stored indices if needed */
824 if (position
< playlist
->index
)
827 if (position
< playlist
->first_index
)
829 playlist
->first_index
--;
833 global_settings
.resume_first_index
= playlist
->first_index
;
838 if (position
<= playlist
->last_insert_pos
)
839 playlist
->last_insert_pos
--;
841 if (write
&& playlist
->control_fd
>= 0)
843 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
844 position
, -1, NULL
, NULL
, NULL
);
849 sync_control(playlist
, false);
856 * randomly rearrange the array of indices for the playlist. If start_current
857 * is true then update the index to the new index of the current playing track
859 static int randomise_playlist(struct playlist_info
* playlist
,
860 unsigned int seed
, bool start_current
,
866 unsigned int current
= playlist
->indices
[playlist
->index
];
868 /* seed 0 is used to identify sorted playlist for resume purposes */
872 /* seed with the given seed */
875 /* randomise entire indices list */
876 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
878 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
879 candidate
= rand() % (count
+ 1);
881 /* now swap the values at the 'count' and 'candidate' positions */
882 store
= playlist
->indices
[candidate
];
883 playlist
->indices
[candidate
] = playlist
->indices
[count
];
884 playlist
->indices
[count
] = store
;
886 if (playlist
->filenames
)
888 store
= (int)playlist
->filenames
[candidate
];
889 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
890 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
896 find_and_set_playlist_index(playlist
, current
);
898 /* indices have been moved so last insert position is no longer valid */
899 playlist
->last_insert_pos
= -1;
901 playlist
->seed
= seed
;
902 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
903 playlist
->shuffle_modified
= true;
907 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
908 playlist
->first_index
, NULL
, NULL
, NULL
);
909 global_settings
.resume_seed
= seed
;
917 * Sort the array of indices for the playlist. If start_current is true then
918 * set the index to the new index of the current song.
920 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
923 unsigned int current
= playlist
->indices
[playlist
->index
];
925 if (playlist
->amount
> 0)
926 qsort(playlist
->indices
, playlist
->amount
,
927 sizeof(playlist
->indices
[0]), compare
);
930 /** We need to re-check the song names from disk because qsort can't
931 * sort two arrays at once :/
932 * FIXME: Please implement a better way to do this. */
933 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
937 find_and_set_playlist_index(playlist
, current
);
939 /* indices have been moved so last insert position is no longer valid */
940 playlist
->last_insert_pos
= -1;
942 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
943 playlist
->shuffle_modified
= false;
944 if (write
&& playlist
->control_fd
>= 0)
946 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
947 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
948 global_settings
.resume_seed
= 0;
955 /* Calculate how many steps we have to really step when skipping entries
958 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
960 int i
, count
, direction
;
962 int stepped_count
= 0;
975 index
= playlist
->index
;
980 index
+= playlist
->amount
;
981 if (index
>= playlist
->amount
)
982 index
-= playlist
->amount
;
984 /* Check if we found a bad entry. */
985 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
988 /* Are all entries bad? */
989 if (stepped_count
++ > playlist
->amount
)
996 } while (i
<= count
);
1001 /* Marks the index of the track to be skipped that is "steps" away from
1002 * current playing track.
1004 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
1008 if (playlist
== NULL
)
1009 playlist
= ¤t_playlist
;
1011 index
= rotate_index(playlist
, playlist
->index
);
1012 /* We should also skip already skipped entries before the entry to be skipepd. */
1013 index
+= calculate_step_count(playlist
, steps
);
1014 if (index
< 0 || index
>= playlist
->amount
)
1017 index
= (index
+playlist
->first_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
&&
1038 (!global_settings
.playlist_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 #if (AB_REPEAT_ENABLE == 1)
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_settings
.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)
1167 bool dirty_pointers
= false;
1168 static char tmp
[MAX_PATH
+1];
1170 struct playlist_info
*playlist
;
1177 if (global_settings
.disk_spindown
> 1 &&
1178 global_settings
.disk_spindown
<= 5)
1179 sleep_time
= global_settings
.disk_spindown
- 1;
1183 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1187 case PLAYLIST_LOAD_POINTERS
:
1188 dirty_pointers
= true;
1191 /* Start the background scanning after either the disk spindown
1192 timeout or 5s, whichever is less */
1194 playlist
= ¤t_playlist
;
1196 if (playlist
->control_fd
>= 0 && ata_disk_is_active())
1198 if (playlist
->num_cached
> 0)
1199 flush_cached_control(playlist
);
1201 sync_control(playlist
, true);
1204 if (!dirty_pointers
)
1207 if (!dircache_is_enabled() || !playlist
->filenames
1208 || playlist
->amount
<= 0)
1211 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1214 for (index
= 0; index
< playlist
->amount
1215 && queue_empty(&playlist_queue
); index
++)
1217 /* Process only pointers that are not already loaded. */
1218 if (playlist
->filenames
[index
])
1221 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1222 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1224 /* Load the filename from playlist file. */
1225 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1229 /* Set the dircache entry pointer. */
1230 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1232 /* And be on background so user doesn't notice any delays. */
1236 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1239 dirty_pointers
= false;
1243 case SYS_USB_CONNECTED
:
1244 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1245 usb_wait_for_disconnect(&playlist_queue
);
1254 * gets pathname for track at seek index
1256 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1257 bool control_file
, char *buf
, int buf_length
)
1261 char tmp_buf
[MAX_PATH
+1];
1262 char dir_buf
[MAX_PATH
+1];
1264 if (buf_length
> MAX_PATH
+1)
1265 buf_length
= MAX_PATH
+1;
1267 #ifdef HAVE_DIRCACHE
1268 if (dircache_is_enabled() && playlist
->filenames
)
1270 if (playlist
->filenames
[index
] != NULL
)
1272 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1273 max
= strlen(tmp_buf
) + 1;
1280 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1282 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1283 tmp_buf
[MAX_PATH
] = '\0';
1284 max
= strlen(tmp_buf
) + 1;
1288 mutex_lock(&playlist
->control_mutex
);
1291 fd
= playlist
->control_fd
;
1294 if(-1 == playlist
->fd
)
1295 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1303 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1306 max
= read(fd
, tmp_buf
, buf_length
);
1309 mutex_unlock(&playlist
->control_mutex
);
1314 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1316 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1322 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1323 dir_buf
[playlist
->dirlen
-1] = 0;
1325 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1328 static int get_next_directory(char *dir
){
1329 return get_next_dir(dir
,true,false);
1332 static int get_previous_directory(char *dir
){
1333 return get_next_dir(dir
,false,false);
1337 * search through all the directories (starting with the current) to find
1338 * one that has tracks to play
1340 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1342 struct playlist_info
* playlist
= ¤t_playlist
;
1344 int dirfilter
= global_settings
.dirfilter
;
1345 int sort_dir
= global_settings
.sort_dir
;
1346 char *start_dir
= NULL
;
1348 struct tree_context
* tc
= tree_get_context();
1351 /* start with root */
1355 /* start with current directory */
1356 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1357 dir
[playlist
->dirlen
-1] = '\0';
1360 /* use the tree browser dircache to load files */
1361 global_settings
.dirfilter
= SHOW_ALL
;
1363 /* sort in another direction if previous dir is requested */
1365 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1366 global_settings
.sort_dir
= 4;
1367 else if (global_settings
.sort_dir
== 1)
1368 global_settings
.sort_dir
= 2;
1369 else if (global_settings
.sort_dir
== 2)
1370 global_settings
.sort_dir
= 1;
1371 else if (global_settings
.sort_dir
== 4)
1372 global_settings
.sort_dir
= 0;
1377 struct entry
*files
;
1381 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1383 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1389 files
= (struct entry
*) tc
->dircache
;
1390 num_files
= tc
->filesindir
;
1392 for (i
=0; i
<num_files
; i
++)
1395 if (button_get(false) == SETTINGS_CANCEL
)
1402 if (files
[i
].attr
& ATTR_DIRECTORY
)
1406 result
= check_subdir_for_music(dir
, files
[i
].name
);
1413 else if (!strcmp(start_dir
, files
[i
].name
))
1420 /* move down to parent directory. current directory name is
1421 stored as the starting point for the search in parent */
1422 start_dir
= strrchr(dir
, '/');
1433 /* we've overwritten the dircache so tree browser will need to be
1437 /* restore dirfilter & sort_dir */
1438 global_settings
.dirfilter
= dirfilter
;
1439 global_settings
.sort_dir
= sort_dir
;
1441 /* special case if nothing found: try start searching again from root */
1442 if (result
== -1 && !recursion
){
1443 result
= get_next_dir(dir
,is_forward
, true);
1450 * Checks if there are any music files in the dir or any of its
1451 * subdirectories. May be called recursively.
1453 static int check_subdir_for_music(char *dir
, char *subdir
)
1456 int dirlen
= strlen(dir
);
1459 struct entry
*files
;
1460 bool has_music
= false;
1461 bool has_subdir
= false;
1462 struct tree_context
* tc
= tree_get_context();
1464 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1466 if (ft_load(tc
, dir
) < 0)
1468 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1472 files
= (struct entry
*) tc
->dircache
;
1473 num_files
= tc
->filesindir
;
1475 for (i
=0; i
<num_files
; i
++)
1477 if (files
[i
].attr
& ATTR_DIRECTORY
)
1479 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
1491 for (i
=0; i
<num_files
; i
++)
1493 if (button_get(false) == SETTINGS_CANCEL
)
1499 if (files
[i
].attr
& ATTR_DIRECTORY
)
1501 result
= check_subdir_for_music(dir
, files
[i
].name
);
1519 /* we now need to reload our current directory */
1520 if(ft_load(tc
, dir
) < 0)
1521 gui_syncsplash(HZ
*2, true,
1522 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1529 * Returns absolute path of track
1531 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1538 /* Zero-terminate the file name */
1539 while((src
[i
] != '\n') &&
1544 /* Now work back killing white space */
1545 while((src
[i
-1] == ' ') ||
1551 /* replace backslashes with forward slashes */
1552 for ( j
=0; j
<i
; j
++ )
1553 if ( src
[j
] == '\\' )
1558 strncpy(dest
, src
, buf_length
);
1562 /* handle dos style drive letter */
1564 strncpy(dest
, &src
[2], buf_length
);
1565 else if (!strncmp(src
, "../", 3))
1567 /* handle relative paths */
1569 while(!strncmp(&src
[i
], "../", 3))
1571 for (j
=0; j
<i
/3; j
++) {
1572 temp_ptr
= strrchr(dir
, '/');
1578 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1580 else if ( '.' == src
[0] && '/' == src
[1] ) {
1581 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1584 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1592 * Display splash message showing progress of playlist/directory insertion or
1595 static void display_playlist_count(int count
, const unsigned char *fmt
)
1597 lcd_clear_display();
1599 #ifdef HAVE_LCD_BITMAP
1600 if(global_settings
.statusbar
)
1601 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1603 lcd_setmargins(0, 0);
1606 gui_syncsplash(0, true, fmt
, count
,
1607 #if CONFIG_KEYPAD == PLAYER_PAD
1608 str(LANG_STOP_ABORT
)
1616 * Display buffer full message
1618 static void display_buffer_full(void)
1620 gui_syncsplash(HZ
*2, true, (unsigned char *)"%s %s",
1621 str(LANG_PLAYINDICES_PLAYLIST
),
1622 str(LANG_PLAYINDICES_BUFFER
));
1626 * Flush any cached control commands to disk. Called when playlist is being
1627 * modified. Returns 0 on success and -1 on failure.
1629 static int flush_cached_control(struct playlist_info
* playlist
)
1634 lseek(playlist
->control_fd
, 0, SEEK_END
);
1636 for (i
=0; i
<playlist
->num_cached
; i
++)
1638 struct playlist_control_cache
* cache
=
1639 &(playlist
->control_cache
[i
]);
1641 switch (cache
->command
)
1643 case PLAYLIST_COMMAND_PLAYLIST
:
1644 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1645 cache
->i1
, cache
->s1
, cache
->s2
);
1647 case PLAYLIST_COMMAND_ADD
:
1648 case PLAYLIST_COMMAND_QUEUE
:
1649 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1650 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1651 cache
->i1
, cache
->i2
);
1654 /* save the position in file where name is written */
1655 int* seek_pos
= (int *)cache
->data
;
1656 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1657 result
= fdprintf(playlist
->control_fd
, "%s\n",
1661 case PLAYLIST_COMMAND_DELETE
:
1662 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1664 case PLAYLIST_COMMAND_SHUFFLE
:
1665 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1666 cache
->i1
, cache
->i2
);
1668 case PLAYLIST_COMMAND_UNSHUFFLE
:
1669 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1671 case PLAYLIST_COMMAND_RESET
:
1672 result
= fdprintf(playlist
->control_fd
, "R\n");
1684 if (global_settings
.resume_seed
>= 0)
1686 global_settings
.resume_seed
= -1;
1690 playlist
->num_cached
= 0;
1691 playlist
->pending_control_sync
= true;
1700 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1708 * Update control data with new command. Depending on the command, it may be
1709 * cached or flushed to disk.
1711 static int update_control(struct playlist_info
* playlist
,
1712 enum playlist_command command
, int i1
, int i2
,
1713 const char* s1
, const char* s2
, void* data
)
1716 struct playlist_control_cache
* cache
;
1719 mutex_lock(&playlist
->control_mutex
);
1721 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1723 cache
->command
= command
;
1732 case PLAYLIST_COMMAND_PLAYLIST
:
1733 case PLAYLIST_COMMAND_ADD
:
1734 case PLAYLIST_COMMAND_QUEUE
:
1735 #ifndef HAVE_DIRCACHE
1736 case PLAYLIST_COMMAND_DELETE
:
1737 case PLAYLIST_COMMAND_RESET
:
1741 case PLAYLIST_COMMAND_SHUFFLE
:
1742 case PLAYLIST_COMMAND_UNSHUFFLE
:
1744 /* only flush when needed */
1748 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1749 result
= flush_cached_control(playlist
);
1751 mutex_unlock(&playlist
->control_mutex
);
1757 * sync control file to disk
1759 static void sync_control(struct playlist_info
* playlist
, bool force
)
1762 #ifdef HAVE_DIRCACHE
1766 if (playlist
->pending_control_sync
)
1768 mutex_lock(&playlist
->control_mutex
);
1769 fsync(playlist
->control_fd
);
1770 playlist
->pending_control_sync
= false;
1771 mutex_unlock(&playlist
->control_mutex
);
1777 * Rotate indices such that first_index is index 0
1779 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1781 index
-= playlist
->first_index
;
1783 index
+= playlist
->amount
;
1789 * Initialize playlist entries at startup
1791 void playlist_init(void)
1793 struct playlist_info
* playlist
= ¤t_playlist
;
1795 playlist
->current
= true;
1796 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1797 "%s", PLAYLIST_CONTROL_FILE
);
1799 playlist
->control_fd
= -1;
1800 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1801 playlist
->indices
= buffer_alloc(
1802 playlist
->max_playlist_size
* sizeof(int));
1803 playlist
->buffer_size
=
1804 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1805 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1806 mutex_init(&playlist
->control_mutex
);
1807 empty_playlist(playlist
, true);
1809 #ifdef HAVE_DIRCACHE
1810 playlist
->filenames
= buffer_alloc(
1811 playlist
->max_playlist_size
* sizeof(int));
1812 memset(playlist
->filenames
, 0,
1813 playlist
->max_playlist_size
* sizeof(int));
1814 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1815 playlist_thread_name
);
1816 queue_init(&playlist_queue
);
1821 * Clean playlist at shutdown
1823 void playlist_shutdown(void)
1825 struct playlist_info
* playlist
= ¤t_playlist
;
1827 if (playlist
->control_fd
>= 0)
1829 mutex_lock(&playlist
->control_mutex
);
1831 if (playlist
->num_cached
> 0)
1832 flush_cached_control(playlist
);
1834 close(playlist
->control_fd
);
1836 mutex_unlock(&playlist
->control_mutex
);
1841 * Create new playlist
1843 int playlist_create(const char *dir
, const char *file
)
1845 struct playlist_info
* playlist
= ¤t_playlist
;
1847 new_playlist(playlist
, dir
, file
);
1850 /* load the playlist file */
1851 add_indices_to_playlist(playlist
, NULL
, 0);
1856 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1859 * Restore the playlist state based on control file commands. Called to
1860 * resume playback after shutdown.
1862 int playlist_resume(void)
1864 struct playlist_info
* playlist
= ¤t_playlist
;
1869 int control_file_size
= 0;
1873 /* use mp3 buffer for maximum load speed */
1874 #if CONFIG_CODEC != SWCODEC
1875 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1876 buflen
= (audiobufend
- audiobuf
);
1877 buffer
= (char *)audiobuf
;
1879 buflen
= (audiobufend
- audiobuf
- talk_get_bufsize());
1880 buffer
= (char *)&audiobuf
[talk_get_bufsize()];
1883 empty_playlist(playlist
, true);
1885 gui_syncsplash(0, true, str(LANG_WAIT
));
1886 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1887 if (playlist
->control_fd
< 0)
1889 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1892 playlist
->control_created
= true;
1894 control_file_size
= filesize(playlist
->control_fd
);
1895 if (control_file_size
<= 0)
1897 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1901 /* read a small amount first to get the header */
1902 nread
= read(playlist
->control_fd
, buffer
,
1903 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1906 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1914 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1915 int last_newline
= 0;
1917 bool newline
= true;
1918 bool exit_loop
= false;
1924 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1926 /* Are we on a new line? */
1927 if((*p
== '\n') || (*p
== '\r'))
1931 /* save last_newline in case we need to load more data */
1932 last_newline
= count
;
1934 switch (current_command
)
1936 case PLAYLIST_COMMAND_PLAYLIST
:
1938 /* str1=version str2=dir str3=file */
1954 version
= atoi(str1
);
1956 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
1959 update_playlist_filename(playlist
, str2
, str3
);
1961 if (str3
[0] != '\0')
1963 /* NOTE: add_indices_to_playlist() overwrites the
1964 audiobuf so we need to reload control file
1966 add_indices_to_playlist(playlist
, NULL
, 0);
1968 else if (str2
[0] != '\0')
1970 playlist
->in_ram
= true;
1971 resume_directory(str2
);
1974 /* load the rest of the data */
1980 case PLAYLIST_COMMAND_ADD
:
1981 case PLAYLIST_COMMAND_QUEUE
:
1983 /* str1=position str2=last_position str3=file */
1984 int position
, last_position
;
1987 if (!str1
|| !str2
|| !str3
)
1994 position
= atoi(str1
);
1995 last_position
= atoi(str2
);
1997 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2000 /* seek position is based on str3's position in
2002 if (add_track_to_playlist(playlist
, str3
, position
,
2003 queue
, total_read
+(str3
-buffer
)) < 0)
2006 playlist
->last_insert_pos
= last_position
;
2010 case PLAYLIST_COMMAND_DELETE
:
2022 position
= atoi(str1
);
2024 if (remove_track_from_playlist(playlist
, position
,
2030 case PLAYLIST_COMMAND_SHUFFLE
:
2032 /* str1=seed str2=first_index */
2044 /* Always sort list before shuffling */
2045 sort_playlist(playlist
, false, false);
2049 playlist
->first_index
= atoi(str2
);
2051 if (randomise_playlist(playlist
, seed
, false,
2058 case PLAYLIST_COMMAND_UNSHUFFLE
:
2060 /* str1=first_index */
2068 playlist
->first_index
= atoi(str1
);
2070 if (sort_playlist(playlist
, false, false) < 0)
2076 case PLAYLIST_COMMAND_RESET
:
2078 playlist
->last_insert_pos
= -1;
2081 case PLAYLIST_COMMAND_COMMENT
:
2088 /* to ignore any extra newlines */
2089 current_command
= PLAYLIST_COMMAND_COMMENT
;
2095 /* first non-comment line must always specify playlist */
2096 if (first
&& *p
!= 'P' && *p
!= '#')
2106 /* playlist can only be specified once */
2114 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2117 current_command
= PLAYLIST_COMMAND_ADD
;
2120 current_command
= PLAYLIST_COMMAND_QUEUE
;
2123 current_command
= PLAYLIST_COMMAND_DELETE
;
2126 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2129 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2132 current_command
= PLAYLIST_COMMAND_RESET
;
2135 current_command
= PLAYLIST_COMMAND_COMMENT
;
2148 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2150 /* all control file strings are separated with a colon.
2151 Replace the colon with 0 to get proper strings that can be
2152 used by commands above */
2158 if ((count
+1) < nread
)
2172 /* allow last string to contain colons */
2183 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2187 if (!newline
|| (exit_loop
&& count
<nread
))
2189 if ((total_read
+ count
) >= control_file_size
)
2191 /* no newline at end of control file */
2192 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2196 /* We didn't end on a newline or we exited loop prematurely.
2197 Either way, re-read the remainder. */
2198 count
= last_newline
;
2199 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2202 total_read
+= count
;
2205 /* still looking for header */
2206 nread
= read(playlist
->control_fd
, buffer
,
2207 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2209 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2211 /* Terminate on EOF */
2214 if (global_settings
.resume_seed
>= 0)
2216 /* Apply shuffle command saved in settings */
2217 if (global_settings
.resume_seed
== 0)
2218 sort_playlist(playlist
, false, true);
2222 sort_playlist(playlist
, false, false);
2224 randomise_playlist(playlist
, global_settings
.resume_seed
,
2228 playlist
->first_index
= global_settings
.resume_first_index
;
2235 #ifdef HAVE_DIRCACHE
2236 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2243 * Add track to in_ram playlist. Used when playing directories.
2245 int playlist_add(const char *filename
)
2247 struct playlist_info
* playlist
= ¤t_playlist
;
2248 int len
= strlen(filename
);
2250 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2251 (playlist
->amount
>= playlist
->max_playlist_size
))
2253 display_buffer_full();
2257 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2258 #ifdef HAVE_DIRCACHE
2259 playlist
->filenames
[playlist
->amount
] = NULL
;
2263 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2264 playlist
->buffer_end_pos
+= len
;
2265 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2270 /* shuffle newly created playlist using random seed. */
2271 int playlist_shuffle(int random_seed
, int start_index
)
2273 struct playlist_info
* playlist
= ¤t_playlist
;
2275 unsigned int seek_pos
= 0;
2276 bool start_current
= false;
2278 if (start_index
>= 0 && global_settings
.play_selected
)
2280 /* store the seek position before the shuffle */
2281 seek_pos
= playlist
->indices
[start_index
];
2282 playlist
->index
= global_settings
.resume_first_index
=
2283 playlist
->first_index
= start_index
;
2284 start_current
= true;
2287 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
2289 randomise_playlist(playlist
, random_seed
, start_current
, true);
2291 return playlist
->index
;
2294 /* start playing current playlist at specified index/offset */
2295 int playlist_start(int start_index
, int offset
)
2297 struct playlist_info
* playlist
= ¤t_playlist
;
2299 playlist
->index
= start_index
;
2300 #if CONFIG_CODEC != SWCODEC
2301 talk_buffer_steal(); /* will use the mp3 buffer */
2308 /* Returns false if 'steps' is out of bounds, else true */
2309 bool playlist_check(int steps
)
2311 struct playlist_info
* playlist
= ¤t_playlist
;
2313 /* always allow folder navigation */
2314 if (global_settings
.next_folder
&& playlist
->in_ram
)
2317 int index
= get_next_index(playlist
, steps
, -1);
2319 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2320 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2322 return (index
>= 0);
2325 /* get trackname of track that is "steps" away from current playing track.
2326 NULL is used to identify end of playlist */
2327 char* playlist_peek(int steps
)
2329 struct playlist_info
* playlist
= ¤t_playlist
;
2336 index
= get_next_index(playlist
, steps
, -1);
2340 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2341 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2343 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2347 temp_ptr
= now_playing
;
2349 if (!playlist
->in_ram
|| control_file
)
2351 /* remove bogus dirs from beginning of path
2352 (workaround for buggy playlist creation tools) */
2355 #ifdef HAVE_DIRCACHE
2356 if (dircache_is_enabled())
2358 if (dircache_get_entry_ptr(temp_ptr
))
2364 fd
= open(temp_ptr
, O_RDONLY
);
2372 temp_ptr
= strchr(temp_ptr
+1, '/');
2377 /* Even though this is an invalid file, we still need to pass a
2378 file name to the caller because NULL is used to indicate end
2388 * Update indices as track has changed
2390 int playlist_next(int steps
)
2392 struct playlist_info
* playlist
= ¤t_playlist
;
2396 #if (AB_REPEAT_ENABLE == 1)
2397 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2399 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2403 /* We need to delete all the queued songs */
2404 for (i
=0, j
=steps
; i
<j
; i
++)
2406 index
= get_next_index(playlist
, i
, -1);
2408 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2410 remove_track_from_playlist(playlist
, index
, true);
2411 steps
--; /* one less track */
2416 index
= get_next_index(playlist
, steps
, -1);
2420 /* end of playlist... or is it */
2421 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2422 global_settings
.playlist_shuffle
&&
2423 playlist
->amount
> 1)
2425 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2426 playlist
->first_index
= global_settings
.resume_first_index
= 0;
2427 sort_playlist(playlist
, false, false);
2428 randomise_playlist(playlist
, current_tick
, false, true);
2429 playlist_start(0, 0);
2432 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2434 char dir
[MAX_PATH
+1];
2436 changing_dir
= true;
2439 if (!get_next_directory(dir
))
2441 /* start playing next directory */
2442 if (playlist_create(dir
, NULL
) != -1)
2444 ft_build_playlist(tree_get_context(), 0);
2445 if (global_settings
.playlist_shuffle
)
2446 playlist_shuffle(current_tick
, -1);
2447 #if CONFIG_CODEC != SWCODEC
2448 playlist_start(0, 0);
2450 playlist
->index
= 0;
2457 if (!get_previous_directory(dir
))
2459 /* start playing previous directory */
2460 if (playlist_create(dir
, NULL
) != -1)
2462 ft_build_playlist(tree_get_context(), 0);
2463 if (global_settings
.playlist_shuffle
)
2464 playlist_shuffle(current_tick
, -1);
2465 playlist_start(current_playlist
.amount
-1,0);
2466 index
= current_playlist
.amount
-1;
2470 changing_dir
= false;
2476 playlist
->index
= index
;
2478 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2480 /* check to see if we've gone beyond the last inserted track */
2481 int cur
= rotate_index(playlist
, index
);
2482 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2486 /* reset last inserted track */
2487 playlist
->last_insert_pos
= -1;
2489 if (playlist
->control_fd
>= 0)
2491 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2492 -1, -1, NULL
, NULL
, NULL
);
2497 sync_control(playlist
, false);
2505 /* try playing next or previous folder */
2506 bool playlist_next_dir(int direction
)
2508 char dir
[MAX_PATH
+1];
2512 /* not to mess up real playlists */
2513 if(!current_playlist
.in_ram
)
2519 changing_dir
= true;
2521 res
= get_next_directory(dir
);
2523 res
= get_previous_directory(dir
);
2526 if (playlist_create(dir
, NULL
) != -1)
2528 ft_build_playlist(tree_get_context(), 0);
2529 if (global_settings
.playlist_shuffle
)
2530 playlist_shuffle(current_tick
, -1);
2531 playlist_start(0,0);
2540 changing_dir
= false;
2545 /* Get resume info for current playing song. If return value is -1 then
2546 settings shouldn't be saved. */
2547 int playlist_get_resume_info(int *resume_index
)
2549 struct playlist_info
* playlist
= ¤t_playlist
;
2551 *resume_index
= playlist
->index
;
2556 /* Update resume info for current playing song. Returns -1 on error. */
2557 int playlist_update_resume_info(const struct mp3entry
* id3
)
2559 struct playlist_info
* playlist
= ¤t_playlist
;
2563 if (global_settings
.resume_index
!= playlist
->index
||
2564 global_settings
.resume_offset
!= id3
->offset
)
2566 global_settings
.resume_index
= playlist
->index
;
2567 global_settings
.resume_offset
= id3
->offset
;
2573 global_settings
.resume_index
= -1;
2574 global_settings
.resume_offset
= -1;
2581 /* Returns index of current playing track for display purposes. This value
2582 should not be used for resume purposes as it doesn't represent the actual
2583 index into the playlist */
2584 int playlist_get_display_index(void)
2586 struct playlist_info
* playlist
= ¤t_playlist
;
2588 /* first_index should always be index 0 for display purposes */
2589 int index
= rotate_index(playlist
, playlist
->index
);
2594 /* returns number of tracks in current playlist */
2595 int playlist_amount(void)
2597 return playlist_amount_ex(NULL
);
2601 * Create a new playlist If playlist is not NULL then we're loading a
2602 * playlist off disk for viewing/editing. The index_buffer is used to store
2603 * playlist indices (required for and only used if !current playlist). The
2604 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2606 int playlist_create_ex(struct playlist_info
* playlist
,
2607 const char* dir
, const char* file
,
2608 void* index_buffer
, int index_buffer_size
,
2609 void* temp_buffer
, int temp_buffer_size
)
2612 playlist
= ¤t_playlist
;
2615 /* Initialize playlist structure */
2616 int r
= rand() % 10;
2617 playlist
->current
= false;
2619 /* Use random name for control file */
2620 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2621 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2623 playlist
->control_fd
= -1;
2627 int num_indices
= index_buffer_size
/ sizeof(int);
2629 #ifdef HAVE_DIRCACHE
2632 if (num_indices
> global_settings
.max_files_in_playlist
)
2633 num_indices
= global_settings
.max_files_in_playlist
;
2635 playlist
->max_playlist_size
= num_indices
;
2636 playlist
->indices
= index_buffer
;
2637 #ifdef HAVE_DIRCACHE
2638 playlist
->filenames
= (const struct dircache_entry
**)
2639 &playlist
->indices
[num_indices
];
2644 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2645 playlist
->indices
= current_playlist
.indices
;
2646 #ifdef HAVE_DIRCACHE
2647 playlist
->filenames
= current_playlist
.filenames
;
2651 playlist
->buffer_size
= 0;
2652 playlist
->buffer
= NULL
;
2653 mutex_init(&playlist
->control_mutex
);
2656 new_playlist(playlist
, dir
, file
);
2659 /* load the playlist file */
2660 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2666 * Set the specified playlist as the current.
2667 * NOTE: You will get undefined behaviour if something is already playing so
2668 * remember to stop before calling this. Also, this call will
2669 * effectively close your playlist, making it unusable.
2671 int playlist_set_current(struct playlist_info
* playlist
)
2673 if (!playlist
|| (check_control(playlist
) < 0))
2676 empty_playlist(¤t_playlist
, false);
2678 strncpy(current_playlist
.filename
, playlist
->filename
,
2679 sizeof(current_playlist
.filename
));
2681 current_playlist
.fd
= playlist
->fd
;
2683 close(playlist
->control_fd
);
2684 remove(current_playlist
.control_filename
);
2685 if (rename(playlist
->control_filename
,
2686 current_playlist
.control_filename
) < 0)
2688 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2690 if (current_playlist
.control_fd
< 0)
2692 current_playlist
.control_created
= true;
2694 current_playlist
.dirlen
= playlist
->dirlen
;
2696 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2698 memcpy(current_playlist
.indices
, playlist
->indices
,
2699 playlist
->max_playlist_size
*sizeof(int));
2700 #ifdef HAVE_DIRCACHE
2701 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2702 playlist
->max_playlist_size
*sizeof(int));
2706 current_playlist
.first_index
= playlist
->first_index
;
2707 current_playlist
.amount
= playlist
->amount
;
2708 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2709 current_playlist
.seed
= playlist
->seed
;
2710 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2711 current_playlist
.deleted
= playlist
->deleted
;
2712 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2714 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2715 sizeof(current_playlist
.control_cache
));
2716 current_playlist
.num_cached
= playlist
->num_cached
;
2717 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2723 * Close files and delete control file for non-current playlist.
2725 void playlist_close(struct playlist_info
* playlist
)
2730 if (playlist
->fd
>= 0)
2731 close(playlist
->fd
);
2733 if (playlist
->control_fd
>= 0)
2734 close(playlist
->control_fd
);
2736 if (playlist
->control_created
)
2737 remove(playlist
->control_filename
);
2741 * Insert track into playlist at specified position (or one of the special
2742 * positions). Returns position where track was inserted or -1 if error.
2744 int playlist_insert_track(struct playlist_info
* playlist
,
2745 const char *filename
, int position
, bool queue
)
2750 playlist
= ¤t_playlist
;
2752 if (check_control(playlist
) < 0)
2754 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2758 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2762 sync_control(playlist
, false);
2763 if (audio_status() & AUDIO_STATUS_PLAY
)
2764 audio_flush_and_reload_tracks();
2767 #ifdef HAVE_DIRCACHE
2768 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2775 * Insert all tracks from specified directory into playlist.
2777 int playlist_insert_directory(struct playlist_info
* playlist
,
2778 const char *dirname
, int position
, bool queue
,
2783 unsigned char *count_str
;
2786 playlist
= ¤t_playlist
;
2788 if (check_control(playlist
) < 0)
2790 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2795 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2797 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2799 display_playlist_count(count
, count_str
);
2801 result
= add_directory_to_playlist(playlist
, dirname
, &position
, queue
,
2804 sync_control(playlist
, false);
2806 display_playlist_count(count
, count_str
);
2808 if (audio_status() & AUDIO_STATUS_PLAY
)
2809 audio_flush_and_reload_tracks();
2811 #ifdef HAVE_DIRCACHE
2812 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2819 * Insert all tracks from specified playlist into dynamic playlist.
2821 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2822 int position
, bool queue
)
2828 unsigned char *count_str
;
2829 char temp_buf
[MAX_PATH
+1];
2830 char trackname
[MAX_PATH
+1];
2835 playlist
= ¤t_playlist
;
2837 if (check_control(playlist
) < 0)
2839 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2843 fd
= open(filename
, O_RDONLY
);
2846 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2850 /* we need the directory name for formatting purposes */
2853 temp_ptr
= strrchr(filename
+1,'/');
2860 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2862 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2864 display_playlist_count(count
, count_str
);
2866 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2869 if (button_get(false) == SETTINGS_CANCEL
)
2872 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2876 /* we need to format so that relative paths are correctly
2878 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2885 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2894 /* Make sure tracks are inserted in correct order if user
2895 requests INSERT_FIRST */
2896 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
2897 position
= insert_pos
+ 1;
2901 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
2903 display_playlist_count(count
, count_str
);
2905 if (count
== PLAYLIST_DISPLAY_COUNT
&&
2906 (audio_status() & AUDIO_STATUS_PLAY
))
2907 audio_flush_and_reload_tracks();
2911 /* let the other threads work */
2920 sync_control(playlist
, false);
2922 display_playlist_count(count
, count_str
);
2924 if (audio_status() & AUDIO_STATUS_PLAY
)
2925 audio_flush_and_reload_tracks();
2927 #ifdef HAVE_DIRCACHE
2928 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2935 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2936 * we want to delete the current playing track.
2938 int playlist_delete(struct playlist_info
* playlist
, int index
)
2943 playlist
= ¤t_playlist
;
2945 if (check_control(playlist
) < 0)
2947 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2951 if (index
== PLAYLIST_DELETE_CURRENT
)
2952 index
= playlist
->index
;
2954 result
= remove_track_from_playlist(playlist
, index
, true);
2956 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
2957 audio_flush_and_reload_tracks();
2963 * Move track at index to new_index. Tracks between the two are shifted
2964 * appropriately. Returns 0 on success and -1 on failure.
2966 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
2972 bool current
= false;
2974 char filename
[MAX_PATH
];
2977 playlist
= ¤t_playlist
;
2979 if (check_control(playlist
) < 0)
2981 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2985 if (index
== new_index
)
2988 if (index
== playlist
->index
)
2989 /* Moving the current track */
2992 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2993 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
2994 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2996 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
2997 sizeof(filename
)) < 0)
3000 /* Delete track from original position */
3001 result
= remove_track_from_playlist(playlist
, index
, true);
3005 /* We want to insert the track at the position that was specified by
3006 new_index. This may be different then new_index because of the
3007 shifting that occurred after the delete */
3008 r
= rotate_index(playlist
, new_index
);
3012 new_index
= PLAYLIST_PREPEND
;
3013 else if (r
== playlist
->amount
)
3015 new_index
= PLAYLIST_INSERT_LAST
;
3017 /* Calculate index of desired position */
3018 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3020 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3027 /* Moved the current track */
3030 case PLAYLIST_PREPEND
:
3031 playlist
->index
= playlist
->first_index
;
3033 case PLAYLIST_INSERT_LAST
:
3034 playlist
->index
= playlist
->first_index
- 1;
3035 if (playlist
->index
< 0)
3036 playlist
->index
+= playlist
->amount
;
3039 playlist
->index
= new_index
;
3044 if (audio_status() & AUDIO_STATUS_PLAY
)
3045 audio_flush_and_reload_tracks();
3049 #ifdef HAVE_DIRCACHE
3050 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3056 /* shuffle currently playing playlist */
3057 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3063 playlist
= ¤t_playlist
;
3065 check_control(playlist
);
3067 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3069 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
3070 audio_flush_and_reload_tracks();
3075 /* sort currently playing playlist */
3076 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3081 playlist
= ¤t_playlist
;
3083 check_control(playlist
);
3085 result
= sort_playlist(playlist
, start_current
, true);
3087 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
))
3088 audio_flush_and_reload_tracks();
3093 /* returns true if playlist has been modified */
3094 bool playlist_modified(const struct playlist_info
* playlist
)
3097 playlist
= ¤t_playlist
;
3099 if (playlist
->shuffle_modified
||
3100 playlist
->deleted
||
3101 playlist
->num_inserted_tracks
> 0)
3107 /* returns index of first track in playlist */
3108 int playlist_get_first_index(const struct playlist_info
* playlist
)
3111 playlist
= ¤t_playlist
;
3113 return playlist
->first_index
;
3116 /* returns shuffle seed of playlist */
3117 int playlist_get_seed(const struct playlist_info
* playlist
)
3120 playlist
= ¤t_playlist
;
3122 return playlist
->seed
;
3125 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3126 int playlist_amount_ex(const struct playlist_info
* playlist
)
3129 playlist
= ¤t_playlist
;
3131 return playlist
->amount
;
3134 /* returns full path of playlist (minus extension) */
3135 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3141 playlist
= ¤t_playlist
;
3143 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3148 /* Remove extension */
3149 sep
= strrchr(buf
, '.');
3156 /* returns the playlist filename */
3157 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3161 playlist
= ¤t_playlist
;
3163 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3171 /* Fills info structure with information about track at specified index.
3172 Returns 0 on success and -1 on failure */
3173 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3174 struct playlist_track_info
* info
)
3180 playlist
= ¤t_playlist
;
3182 if (index
< 0 || index
>= playlist
->amount
)
3185 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3186 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3188 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3189 sizeof(info
->filename
)) < 0)
3196 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3197 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3199 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3203 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3204 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3206 info
->index
= index
;
3207 info
->display_index
= rotate_index(playlist
, index
) + 1;
3212 /* save the current dynamic playlist to specified file */
3213 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3218 char path
[MAX_PATH
+1];
3219 char tmp_buf
[MAX_PATH
+1];
3221 bool overwrite_current
= false;
3222 int* index_buf
= NULL
;
3225 playlist
= ¤t_playlist
;
3227 if (playlist
->amount
<= 0)
3230 /* use current working directory as base for pathname */
3231 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3232 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3235 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3237 /* Attempting to overwrite current playlist file.*/
3239 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3241 /* not enough buffer space to store updated indices */
3242 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3246 /* in_ram buffer is unused for m3u files so we'll use for storing
3248 index_buf
= (int*)playlist
->buffer
;
3250 /* use temporary pathname */
3251 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3252 overwrite_current
= true;
3255 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3258 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3262 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3264 index
= playlist
->first_index
;
3265 for (i
=0; i
<playlist
->amount
; i
++)
3272 if (button_get(false) == SETTINGS_CANCEL
)
3278 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3279 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3280 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3282 /* Don't save queued files */
3285 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3292 if (overwrite_current
)
3293 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3295 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3297 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3304 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3305 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3310 index
= (index
+1)%playlist
->amount
;
3313 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3317 if (overwrite_current
&& result
>= 0)
3321 mutex_lock(&playlist
->control_mutex
);
3323 /* Replace the current playlist with the new one and update indices */
3324 close(playlist
->fd
);
3325 if (remove(playlist
->filename
) >= 0)
3327 if (rename(path
, playlist
->filename
) >= 0)
3329 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3330 if (playlist
->fd
>= 0)
3332 index
= playlist
->first_index
;
3333 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3335 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3337 playlist
->indices
[index
] = index_buf
[count
];
3340 index
= (index
+1)%playlist
->amount
;
3343 /* we need to recreate control because inserted tracks are
3344 now part of the playlist and shuffle has been
3346 result
= recreate_control(playlist
);
3351 mutex_unlock(&playlist
->control_mutex
);