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"
88 #ifdef HAVE_LCD_BITMAP
96 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
97 #define PLAYLIST_CONTROL_FILE_VERSION 2
100 Each playlist index has a flag associated with it which identifies what
101 type of track it is. These flags are stored in the 3 high order bits of
104 NOTE: This limits the playlist file size to a max of 512M.
108 01 = Track was prepended into playlist
109 10 = Track was inserted into playlist
110 11 = Track was appended into playlist
115 #define PLAYLIST_SEEK_MASK 0x1FFFFFFF
116 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
117 #define PLAYLIST_QUEUE_MASK 0x20000000
119 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
120 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
121 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
123 #define PLAYLIST_QUEUED 0x20000000
125 #define PLAYLIST_DISPLAY_COUNT 10
127 static struct playlist_info current_playlist
;
128 static char now_playing
[MAX_PATH
+1];
130 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
131 static void new_playlist(struct playlist_info
* playlist
, char *dir
,
133 static void create_control(struct playlist_info
* playlist
);
134 static int check_control(struct playlist_info
* playlist
);
135 static void update_playlist_filename(struct playlist_info
* playlist
,
136 char *dir
, char *file
);
137 static int add_indices_to_playlist(struct playlist_info
* playlist
,
138 char* buffer
, int buflen
);
139 static int add_track_to_playlist(struct playlist_info
* playlist
,
140 char *filename
, int position
, bool queue
,
142 static int add_directory_to_playlist(struct playlist_info
* playlist
,
143 char *dirname
, int *position
, bool queue
,
144 int *count
, bool recurse
);
145 static int remove_track_from_playlist(struct playlist_info
* playlist
,
146 int position
, bool write
);
147 static int randomise_playlist(struct playlist_info
* playlist
,
148 unsigned int seed
, bool start_current
,
150 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
152 static int get_next_index(struct playlist_info
* playlist
, int steps
);
153 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
155 static int compare(const void* p1
, const void* p2
);
156 static int get_filename(struct playlist_info
* playlist
, int seek
,
157 bool control_file
, char *buf
, int buf_length
);
158 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
160 static void display_playlist_count(int count
, char *fmt
);
161 static void display_buffer_full(void);
162 static int flush_pending_control(struct playlist_info
* playlist
);
163 static int rotate_index(struct playlist_info
* playlist
, int index
);
166 * remove any files and indices associated with the playlist
168 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
170 playlist
->filename
[0] = '\0';
172 if(playlist
->fd
>= 0)
173 /* If there is an already open playlist, close it. */
177 if(playlist
->control_fd
>= 0)
178 close(playlist
->control_fd
);
179 playlist
->control_fd
= -1;
180 playlist
->control_created
= false;
182 playlist
->in_ram
= false;
184 if (playlist
->buffer
)
185 playlist
->buffer
[0] = 0;
187 playlist
->buffer_end_pos
= 0;
190 playlist
->first_index
= 0;
191 playlist
->amount
= 0;
192 playlist
->last_insert_pos
= -1;
194 playlist
->shuffle_modified
= false;
195 playlist
->deleted
= false;
196 playlist
->num_inserted_tracks
= 0;
197 playlist
->shuffle_flush
= false;
199 if (!resume
&& playlist
->current
)
201 /* start with fresh playlist control file when starting new
203 create_control(playlist
);
205 /* Reset resume settings */
206 global_settings
.resume_first_index
= 0;
207 global_settings
.resume_seed
= -1;
212 * Initialize a new playlist for viewing/editing/playing. dir is the
213 * directory where the playlist is located and file is the filename.
215 static void new_playlist(struct playlist_info
* playlist
, char *dir
,
218 empty_playlist(playlist
, false);
224 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
225 playlist
->in_ram
= true;
227 dir
= ""; /* empty playlist */
230 update_playlist_filename(playlist
, dir
, file
);
232 if (playlist
->control_fd
>= 0)
234 if (fprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
235 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
) > 0)
236 fsync(playlist
->control_fd
);
238 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
243 * create control file for playlist
245 static void create_control(struct playlist_info
* playlist
)
247 playlist
->control_fd
= creat(playlist
->control_filename
, 0000200);
248 if (playlist
->control_fd
< 0)
249 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
250 playlist
->control_created
= true;
254 * validate the control file. This may include creating/initializing it if
257 static int check_control(struct playlist_info
* playlist
)
259 if (!playlist
->control_created
)
261 create_control(playlist
);
263 if (playlist
->control_fd
>= 0)
265 char* dir
= playlist
->filename
;
266 char* file
= playlist
->filename
+playlist
->dirlen
;
267 char c
= playlist
->filename
[playlist
->dirlen
-1];
269 playlist
->filename
[playlist
->dirlen
-1] = '\0';
271 if (fprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
272 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
) > 0)
273 fsync(playlist
->control_fd
);
275 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
277 playlist
->filename
[playlist
->dirlen
-1] = c
;
281 if (playlist
->control_fd
< 0)
288 * store directory and name of playlist file
290 static void update_playlist_filename(struct playlist_info
* playlist
,
291 char *dir
, char *file
)
294 int dirlen
= strlen(dir
);
296 /* If the dir does not end in trailing slash, we use a separator.
297 Otherwise we don't. */
298 if('/' != dir
[dirlen
-1])
304 playlist
->dirlen
= dirlen
;
306 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
312 * calculate track offsets within a playlist file
314 static int add_indices_to_playlist(struct playlist_info
* playlist
,
315 char* buffer
, int buflen
)
319 unsigned int count
= 0;
323 if(-1 == playlist
->fd
)
324 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
326 return -1; /* failure */
328 #ifdef HAVE_LCD_BITMAP
329 if(global_settings
.statusbar
)
330 lcd_setmargins(0, STATUSBAR_HEIGHT
);
332 lcd_setmargins(0, 0);
335 splash(0, true, str(LANG_PLAYLIST_LOAD
));
339 /* use mp3 buffer for maximum load speed */
341 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
344 buflen
= (mp3end
- mp3buf
);
351 nread
= read(playlist
->fd
, buffer
, buflen
);
352 /* Terminate on EOF */
358 for(count
=0; count
< nread
; count
++,p
++) {
360 /* Are we on a new line? */
361 if((*p
== '\n') || (*p
== '\r'))
371 /* Store a new entry */
372 playlist
->indices
[ playlist
->amount
] = i
+count
;
374 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
375 display_buffer_full();
389 * Add track to playlist at specified position. There are three special
390 * positions that can be specified:
391 * PLAYLIST_PREPEND - Add track at beginning of playlist
392 * PLAYLIST_INSERT - Add track after current song. NOTE: If there
393 * are already inserted tracks then track is added
394 * to the end of the insertion list.
395 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
396 * matter what other tracks have been inserted.
397 * PLAYLIST_INSERT_LAST - Add track to end of playlist
399 static int add_track_to_playlist(struct playlist_info
* playlist
,
400 char *filename
, int position
, bool queue
,
403 int insert_position
= position
;
404 unsigned int flags
= PLAYLIST_INSERT_TYPE_INSERT
;
407 if (playlist
->amount
>= playlist
->max_playlist_size
)
409 display_buffer_full();
415 case PLAYLIST_PREPEND
:
416 insert_position
= playlist
->first_index
;
417 flags
= PLAYLIST_INSERT_TYPE_PREPEND
;
419 case PLAYLIST_INSERT
:
420 /* if there are already inserted tracks then add track to end of
421 insertion list else add after current playing track */
422 if (playlist
->last_insert_pos
>= 0 &&
423 playlist
->last_insert_pos
< playlist
->amount
&&
424 (playlist
->indices
[playlist
->last_insert_pos
]&
425 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
426 position
= insert_position
= playlist
->last_insert_pos
+1;
427 else if (playlist
->amount
> 0)
428 position
= insert_position
= playlist
->index
+ 1;
430 position
= insert_position
= 0;
432 playlist
->last_insert_pos
= position
;
434 case PLAYLIST_INSERT_FIRST
:
435 if (playlist
->amount
> 0)
436 position
= insert_position
= playlist
->index
+ 1;
438 position
= insert_position
= 0;
440 if (playlist
->last_insert_pos
< 0)
441 playlist
->last_insert_pos
= position
;
443 case PLAYLIST_INSERT_LAST
:
444 if (playlist
->first_index
> 0)
445 insert_position
= playlist
->first_index
;
447 insert_position
= playlist
->amount
;
449 flags
= PLAYLIST_INSERT_TYPE_APPEND
;
454 flags
|= PLAYLIST_QUEUED
;
456 /* shift indices so that track can be added */
457 for (i
=playlist
->amount
; i
>insert_position
; i
--)
458 playlist
->indices
[i
] = playlist
->indices
[i
-1];
460 /* update stored indices if needed */
461 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
)
464 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
465 position
!= PLAYLIST_PREPEND
)
467 playlist
->first_index
++;
469 if (seek_pos
< 0 && playlist
->current
)
471 global_settings
.resume_first_index
= playlist
->first_index
;
476 if (insert_position
< playlist
->last_insert_pos
||
477 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
478 playlist
->last_insert_pos
++;
480 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
484 if (flush_pending_control(playlist
) < 0)
487 mutex_lock(&playlist
->control_mutex
);
489 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
491 if (fprintf(playlist
->control_fd
, "%c:%d:%d:", (queue
?'Q':'A'),
492 position
, playlist
->last_insert_pos
) > 0)
494 /* save the position in file where track name is written */
495 seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
497 if (fprintf(playlist
->control_fd
, "%s\n", filename
) > 0)
502 mutex_unlock(&playlist
->control_mutex
);
506 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
511 playlist
->indices
[insert_position
] = flags
| seek_pos
;
514 playlist
->num_inserted_tracks
++;
516 return insert_position
;
520 * Insert directory into playlist. May be called recursively.
522 static int add_directory_to_playlist(struct playlist_info
* playlist
,
523 char *dirname
, int *position
, bool queue
,
524 int *count
, bool recurse
)
526 char buf
[MAX_PATH
+1];
530 bool buffer_full
= false;
532 int dirfilter
= SHOW_ALL
;
535 /* use the tree browser dircache to load files */
536 files
= load_and_sort_directory(dirname
, &dirfilter
, &num_files
,
541 splash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
545 /* we've overwritten the dircache so tree browser will need to be
550 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
552 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
554 for (i
=0; i
<num_files
; i
++)
557 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
558 if (button_get(false) == BUTTON_STOP
)
560 if (button_get(false) == BUTTON_OFF
)
567 if (files
[i
].attr
& ATTR_DIRECTORY
)
571 /* recursively add directories */
572 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
573 result
= add_directory_to_playlist(playlist
, buf
, position
,
574 queue
, count
, recurse
);
578 /* we now need to reload our current directory */
579 files
= load_and_sort_directory(dirname
, &dirfilter
, &num_files
,
590 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
594 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
596 insert_pos
= add_track_to_playlist(playlist
, buf
, *position
,
606 /* Make sure tracks are inserted in correct order if user requests
608 if (*position
== PLAYLIST_INSERT_FIRST
|| *position
>= 0)
609 *position
= insert_pos
+ 1;
611 if ((*count
%PLAYLIST_DISPLAY_COUNT
) == 0)
613 display_playlist_count(*count
, count_str
);
615 if (*count
== PLAYLIST_DISPLAY_COUNT
)
616 mpeg_flush_and_reload_tracks();
619 /* let the other threads work */
628 * remove track at specified position
630 static int remove_track_from_playlist(struct playlist_info
* playlist
,
631 int position
, bool write
)
636 if (playlist
->amount
<= 0)
639 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
641 /* shift indices now that track has been removed */
642 for (i
=position
; i
<playlist
->amount
; i
++)
643 playlist
->indices
[i
] = playlist
->indices
[i
+1];
648 playlist
->num_inserted_tracks
--;
650 playlist
->deleted
= true;
652 /* update stored indices if needed */
653 if (position
< playlist
->index
)
656 if (position
< playlist
->first_index
)
658 playlist
->first_index
--;
662 global_settings
.resume_first_index
= playlist
->first_index
;
667 if (position
<= playlist
->last_insert_pos
)
668 playlist
->last_insert_pos
--;
670 if (write
&& playlist
->control_fd
>= 0)
674 if (flush_pending_control(playlist
) < 0)
677 mutex_lock(&playlist
->control_mutex
);
679 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
681 if (fprintf(playlist
->control_fd
, "D:%d\n", position
) > 0)
683 fsync(playlist
->control_fd
);
688 mutex_unlock(&playlist
->control_mutex
);
692 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
701 * randomly rearrange the array of indices for the playlist. If start_current
702 * is true then update the index to the new index of the current playing track
704 static int randomise_playlist(struct playlist_info
* playlist
,
705 unsigned int seed
, bool start_current
,
711 unsigned int current
= playlist
->indices
[playlist
->index
];
713 /* seed 0 is used to identify sorted playlist for resume purposes */
717 /* seed with the given seed */
720 /* randomise entire indices list */
721 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
723 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
724 candidate
= rand() % (count
+ 1);
726 /* now swap the values at the 'count' and 'candidate' positions */
727 store
= playlist
->indices
[candidate
];
728 playlist
->indices
[candidate
] = playlist
->indices
[count
];
729 playlist
->indices
[count
] = store
;
733 find_and_set_playlist_index(playlist
, current
);
735 /* indices have been moved so last insert position is no longer valid */
736 playlist
->last_insert_pos
= -1;
738 playlist
->seed
= seed
;
739 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
740 playlist
->shuffle_modified
= true;
744 /* Don't write to disk immediately. Instead, save in settings and
745 only flush if playlist is modified (insertion/deletion) */
746 playlist
->shuffle_flush
= true;
747 global_settings
.resume_seed
= seed
;
755 * Sort the array of indices for the playlist. If start_current is true then
756 * set the index to the new index of the current song.
758 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
761 unsigned int current
= playlist
->indices
[playlist
->index
];
763 if (playlist
->amount
> 0)
764 qsort(playlist
->indices
, playlist
->amount
, sizeof(playlist
->indices
[0]),
768 find_and_set_playlist_index(playlist
, current
);
770 /* indices have been moved so last insert position is no longer valid */
771 playlist
->last_insert_pos
= -1;
773 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
774 playlist
->shuffle_modified
= false;
775 if (write
&& playlist
->control_fd
>= 0)
777 /* Don't write to disk immediately. Instead, save in settings and
778 only flush if playlist is modified (insertion/deletion) */
779 playlist
->shuffle_flush
= true;
780 global_settings
.resume_seed
= 0;
788 * returns the index of the track that is "steps" away from current playing
791 static int get_next_index(struct playlist_info
* playlist
, int steps
)
793 int current_index
= playlist
->index
;
796 if (playlist
->amount
<= 0)
799 switch (global_settings
.repeat_mode
)
803 current_index
= rotate_index(playlist
, current_index
);
805 next_index
= current_index
+steps
;
806 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
809 next_index
= (next_index
+playlist
->first_index
) %
816 next_index
= current_index
;
822 next_index
= (current_index
+steps
) % playlist
->amount
;
823 while (next_index
< 0)
824 next_index
+= playlist
->amount
;
826 if (steps
>= playlist
->amount
)
833 /* second time around so skip the queued files */
834 for (i
=0; i
<playlist
->amount
; i
++)
836 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
837 index
= (index
+1) % playlist
->amount
;
853 * Search for the seek track and set appropriate indices. Used after shuffle
854 * to make sure the current index is still pointing to correct track.
856 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
861 /* Set the index to the current song */
862 for (i
=0; i
<playlist
->amount
; i
++)
864 if (playlist
->indices
[i
] == seek
)
866 playlist
->index
= playlist
->first_index
= i
;
868 if (playlist
->current
)
870 global_settings
.resume_first_index
= i
;
880 * used to sort track indices. Sort order is as follows:
881 * 1. Prepended tracks (in prepend order)
882 * 2. Playlist/directory tracks (in playlist order)
883 * 3. Inserted/Appended tracks (in insert order)
885 static int compare(const void* p1
, const void* p2
)
887 unsigned int* e1
= (unsigned int*) p1
;
888 unsigned int* e2
= (unsigned int*) p2
;
889 unsigned int flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
890 unsigned int flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
892 if (flags1
== flags2
)
893 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
894 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
895 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
897 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
898 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
900 else if (flags1
&& flags2
)
901 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
907 * gets pathname for track at seek index
909 static int get_filename(struct playlist_info
* playlist
, int seek
,
910 bool control_file
, char *buf
, int buf_length
)
914 char tmp_buf
[MAX_PATH
+1];
915 char dir_buf
[MAX_PATH
+1];
917 if (buf_length
> MAX_PATH
+1)
918 buf_length
= MAX_PATH
+1;
920 if (playlist
->in_ram
&& !control_file
)
922 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
923 tmp_buf
[MAX_PATH
] = '\0';
924 max
= strlen(tmp_buf
) + 1;
929 fd
= playlist
->control_fd
;
932 if(-1 == playlist
->fd
)
933 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
941 mutex_lock(&playlist
->control_mutex
);
943 lseek(fd
, seek
, SEEK_SET
);
944 max
= read(fd
, tmp_buf
, buf_length
);
947 mutex_unlock(&playlist
->control_mutex
);
954 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
956 splash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
962 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
963 dir_buf
[playlist
->dirlen
-1] = 0;
965 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
969 * Returns absolute path of track
971 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
978 /* Zero-terminate the file name */
979 while((src
[i
] != '\n') &&
984 /* Now work back killing white space */
985 while((src
[i
-1] == ' ') ||
991 /* replace backslashes with forward slashes */
992 for ( j
=0; j
<i
; j
++ )
993 if ( src
[j
] == '\\' )
998 strncpy(dest
, src
, buf_length
);
1002 /* handle dos style drive letter */
1004 strncpy(dest
, &src
[2], buf_length
);
1005 else if ('.' == src
[0] && '.' == src
[1] && '/' == src
[2])
1007 /* handle relative paths */
1009 while(src
[i
] == '.' &&
1013 for (j
=0; j
<i
/3; j
++) {
1014 temp_ptr
= strrchr(dir
, '/');
1020 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1022 else if ( '.' == src
[0] && '/' == src
[1] ) {
1023 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1026 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1034 * Display splash message showing progress of playlist/directory insertion or
1037 static void display_playlist_count(int count
, char *fmt
)
1039 lcd_clear_display();
1041 #ifdef HAVE_LCD_BITMAP
1042 if(global_settings
.statusbar
)
1043 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1045 lcd_setmargins(0, 0);
1048 splash(0, true, fmt
, count
,
1049 #ifdef HAVE_PLAYER_KEYPAD
1050 str(LANG_STOP_ABORT
)
1058 * Display buffer full message
1060 static void display_buffer_full(void)
1062 splash(HZ
*2, true, "%s %s",
1063 str(LANG_PLAYINDICES_PLAYLIST
),
1064 str(LANG_PLAYINDICES_BUFFER
));
1068 * Flush any pending control commands to disk. Called when playlist is being
1069 * modified. Returns 0 on success and -1 on failure.
1071 static int flush_pending_control(struct playlist_info
* playlist
)
1075 if (playlist
->shuffle_flush
&& global_settings
.resume_seed
>= 0)
1077 /* pending shuffle */
1078 mutex_lock(&playlist
->control_mutex
);
1080 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
1082 if (global_settings
.resume_seed
== 0)
1083 result
= fprintf(playlist
->control_fd
, "U:%d\n",
1084 playlist
->first_index
);
1086 result
= fprintf(playlist
->control_fd
, "S:%d:%d\n",
1087 global_settings
.resume_seed
, playlist
->first_index
);
1091 fsync(playlist
->control_fd
);
1093 playlist
->shuffle_flush
= false;
1094 global_settings
.resume_seed
= -1;
1105 mutex_unlock(&playlist
->control_mutex
);
1109 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1118 * Rotate indices such that first_index is index 0
1120 static int rotate_index(struct playlist_info
* playlist
, int index
)
1122 index
-= playlist
->first_index
;
1124 index
+= playlist
->amount
;
1130 * Initialize playlist entries at startup
1132 void playlist_init(void)
1134 struct playlist_info
* playlist
= ¤t_playlist
;
1136 playlist
->current
= true;
1137 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1138 "%s", PLAYLIST_CONTROL_FILE
);
1140 playlist
->control_fd
= -1;
1141 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1142 playlist
->indices
= buffer_alloc(playlist
->max_playlist_size
* sizeof(int));
1143 playlist
->buffer_size
=
1144 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1145 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1146 mutex_init(&playlist
->control_mutex
);
1147 empty_playlist(playlist
, true);
1151 * Create new playlist
1153 int playlist_create(char *dir
, char *file
)
1155 struct playlist_info
* playlist
= ¤t_playlist
;
1157 new_playlist(playlist
, dir
, file
);
1160 /* load the playlist file */
1161 add_indices_to_playlist(playlist
, NULL
, 0);
1166 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1169 * Restore the playlist state based on control file commands. Called to
1170 * resume playback after shutdown.
1172 int playlist_resume(void)
1174 struct playlist_info
* playlist
= ¤t_playlist
;
1193 /* use mp3 buffer for maximum load speed */
1194 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1195 buflen
= (mp3end
- mp3buf
);
1198 empty_playlist(playlist
, true);
1200 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1201 if (playlist
->control_fd
< 0)
1203 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1206 playlist
->control_created
= true;
1208 /* read a small amount first to get the header */
1209 nread
= read(playlist
->control_fd
, buffer
,
1210 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1213 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1221 int current_command
= resume_comment
;
1222 int last_newline
= 0;
1224 bool newline
= true;
1225 bool exit_loop
= false;
1231 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1233 /* Are we on a new line? */
1234 if((*p
== '\n') || (*p
== '\r'))
1238 /* save last_newline in case we need to load more data */
1239 last_newline
= count
;
1241 switch (current_command
)
1243 case resume_playlist
:
1245 /* str1=version str2=dir str3=file */
1261 version
= atoi(str1
);
1263 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
1266 update_playlist_filename(playlist
, str2
, str3
);
1268 if (str3
[0] != '\0')
1270 /* NOTE: add_indices_to_playlist() overwrites the
1271 mp3buf so we need to reload control file
1273 add_indices_to_playlist(playlist
, NULL
, 0);
1275 else if (str2
[0] != '\0')
1277 playlist
->in_ram
= true;
1278 resume_directory(str2
);
1281 /* load the rest of the data */
1290 /* str1=position str2=last_position str3=file */
1291 int position
, last_position
;
1294 if (!str1
|| !str2
|| !str3
)
1301 position
= atoi(str1
);
1302 last_position
= atoi(str2
);
1304 queue
= (current_command
== resume_add
)?false:true;
1306 /* seek position is based on str3's position in
1308 if (add_track_to_playlist(playlist
, str3
, position
,
1309 queue
, total_read
+(str3
-buffer
)) < 0)
1312 playlist
->last_insert_pos
= last_position
;
1328 position
= atoi(str1
);
1330 if (remove_track_from_playlist(playlist
, position
,
1336 case resume_shuffle
:
1338 /* str1=seed str2=first_index */
1350 /* Always sort list before shuffling */
1351 sort_playlist(playlist
, false, false);
1355 playlist
->first_index
= atoi(str2
);
1357 if (randomise_playlist(playlist
, seed
, false,
1364 case resume_unshuffle
:
1366 /* str1=first_index */
1374 playlist
->first_index
= atoi(str1
);
1376 if (sort_playlist(playlist
, false, false) < 0)
1384 playlist
->last_insert_pos
= -1;
1387 case resume_comment
:
1394 /* to ignore any extra newlines */
1395 current_command
= resume_comment
;
1401 /* first non-comment line must always specify playlist */
1402 if (first
&& *p
!= 'P' && *p
!= '#')
1412 /* playlist can only be specified once */
1420 current_command
= resume_playlist
;
1423 current_command
= resume_add
;
1426 current_command
= resume_queue
;
1429 current_command
= resume_delete
;
1432 current_command
= resume_shuffle
;
1435 current_command
= resume_unshuffle
;
1438 current_command
= resume_reset
;
1441 current_command
= resume_comment
;
1454 else if(current_command
!= resume_comment
)
1456 /* all control file strings are separated with a colon.
1457 Replace the colon with 0 to get proper strings that can be
1458 used by commands above */
1464 if ((count
+1) < nread
)
1478 /* allow last string to contain colons */
1489 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
1493 if (!newline
|| (exit_loop
&& count
<nread
))
1495 /* We didn't end on a newline or we exited loop prematurely.
1496 Either way, re-read the remainder.
1497 NOTE: because of this, control file must always end with a
1499 count
= last_newline
;
1500 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
1503 total_read
+= count
;
1506 /* still looking for header */
1507 nread
= read(playlist
->control_fd
, buffer
,
1508 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1510 nread
= read(playlist
->control_fd
, buffer
, buflen
);
1512 /* Terminate on EOF */
1515 if (global_settings
.resume_seed
>= 0)
1517 /* Apply shuffle command saved in settings */
1518 if (global_settings
.resume_seed
== 0)
1519 sort_playlist(playlist
, false, true);
1523 sort_playlist(playlist
, false, false);
1525 randomise_playlist(playlist
, global_settings
.resume_seed
,
1529 playlist
->first_index
= global_settings
.resume_first_index
;
1540 * Add track to in_ram playlist. Used when playing directories.
1542 int playlist_add(char *filename
)
1544 struct playlist_info
* playlist
= ¤t_playlist
;
1545 int len
= strlen(filename
);
1547 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
1548 (playlist
->amount
>= playlist
->max_playlist_size
))
1550 display_buffer_full();
1554 playlist
->indices
[playlist
->amount
++] = playlist
->buffer_end_pos
;
1556 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
1557 playlist
->buffer_end_pos
+= len
;
1558 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
1563 /* shuffle newly created playlist using random seed. */
1564 int playlist_shuffle(int random_seed
, int start_index
)
1566 struct playlist_info
* playlist
= ¤t_playlist
;
1568 unsigned int seek_pos
= 0;
1569 bool start_current
= false;
1571 if (start_index
>= 0 && global_settings
.play_selected
)
1573 /* store the seek position before the shuffle */
1574 seek_pos
= playlist
->indices
[start_index
];
1575 playlist
->index
= global_settings
.resume_first_index
=
1576 playlist
->first_index
= start_index
;
1577 start_current
= true;
1580 splash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
1582 randomise_playlist(playlist
, random_seed
, start_current
, true);
1584 /* Flush shuffle command to disk */
1585 flush_pending_control(playlist
);
1587 return playlist
->index
;
1590 /* start playing current playlist at specified index/offset */
1591 int playlist_start(int start_index
, int offset
)
1593 struct playlist_info
* playlist
= ¤t_playlist
;
1595 playlist
->index
= start_index
;
1596 talk_buffer_steal(); /* will use the mp3 buffer */
1602 /* Returns false if 'steps' is out of bounds, else true */
1603 bool playlist_check(int steps
)
1605 struct playlist_info
* playlist
= ¤t_playlist
;
1606 int index
= get_next_index(playlist
, steps
);
1607 return (index
>= 0);
1610 /* get trackname of track that is "steps" away from current playing track.
1611 NULL is used to identify end of playlist */
1612 char* playlist_peek(int steps
)
1614 struct playlist_info
* playlist
= ¤t_playlist
;
1621 index
= get_next_index(playlist
, steps
);
1625 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1626 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1628 if (get_filename(playlist
, seek
, control_file
, now_playing
,
1632 temp_ptr
= now_playing
;
1634 if (!playlist
->in_ram
|| control_file
)
1636 /* remove bogus dirs from beginning of path
1637 (workaround for buggy playlist creation tools) */
1640 fd
= open(temp_ptr
, O_RDONLY
);
1647 temp_ptr
= strchr(temp_ptr
+1, '/');
1652 /* Even though this is an invalid file, we still need to pass a
1653 file name to the caller because NULL is used to indicate end
1663 * Update indices as track has changed
1665 int playlist_next(int steps
)
1667 struct playlist_info
* playlist
= ¤t_playlist
;
1670 if (steps
> 0 && global_settings
.repeat_mode
!= REPEAT_ONE
)
1674 /* We need to delete all the queued songs */
1675 for (i
=0, j
=steps
; i
<j
; i
++)
1677 index
= get_next_index(playlist
, i
);
1679 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1681 remove_track_from_playlist(playlist
, index
, true);
1682 steps
--; /* one less track */
1687 index
= get_next_index(playlist
, steps
);
1688 playlist
->index
= index
;
1690 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
1692 /* check to see if we've gone beyond the last inserted track */
1693 int cur
= rotate_index(playlist
, index
);
1694 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
1698 /* reset last inserted track */
1699 playlist
->last_insert_pos
= -1;
1701 if (playlist
->control_fd
>= 0)
1705 mutex_lock(&playlist
->control_mutex
);
1707 if (lseek(playlist
->control_fd
, 0, SEEK_END
) >= 0)
1709 if (fprintf(playlist
->control_fd
, "R\n") > 0)
1711 fsync(playlist
->control_fd
);
1716 mutex_unlock(&playlist
->control_mutex
);
1721 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1731 /* Get resume info for current playing song. If return value is -1 then
1732 settings shouldn't be saved. */
1733 int playlist_get_resume_info(int *resume_index
)
1735 struct playlist_info
* playlist
= ¤t_playlist
;
1737 *resume_index
= playlist
->index
;
1742 /* Returns index of current playing track for display purposes. This value
1743 should not be used for resume purposes as it doesn't represent the actual
1744 index into the playlist */
1745 int playlist_get_display_index(void)
1747 struct playlist_info
* playlist
= ¤t_playlist
;
1749 /* first_index should always be index 0 for display purposes */
1750 int index
= rotate_index(playlist
, playlist
->index
);
1755 /* returns number of tracks in current playlist */
1756 int playlist_amount(void)
1758 return playlist_amount_ex(NULL
);
1762 * Create a new playlist If playlist is not NULL then we're loading a
1763 * playlist off disk for viewing/editing. The index_buffer is used to store
1764 * playlist indices (required for and only used if !current playlist). The
1765 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
1767 int playlist_create_ex(struct playlist_info
* playlist
, char* dir
, char* file
,
1768 void* index_buffer
, int index_buffer_size
,
1769 void* temp_buffer
, int temp_buffer_size
)
1772 playlist
= ¤t_playlist
;
1775 /* Initialize playlist structure */
1776 int r
= rand() % 10;
1777 playlist
->current
= false;
1779 /* Use random name for control file */
1780 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1781 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
1783 playlist
->control_fd
= -1;
1787 int num_indices
= index_buffer_size
/ sizeof(int);
1789 if (num_indices
> global_settings
.max_files_in_playlist
)
1790 num_indices
= global_settings
.max_files_in_playlist
;
1792 playlist
->max_playlist_size
= num_indices
;
1793 playlist
->indices
= index_buffer
;
1797 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
1798 playlist
->indices
= current_playlist
.indices
;
1801 playlist
->buffer_size
= 0;
1802 playlist
->buffer
= NULL
;
1803 mutex_init(&playlist
->control_mutex
);
1806 new_playlist(playlist
, dir
, file
);
1809 /* load the playlist file */
1810 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
1816 * Set the specified playlist as the current.
1817 * NOTE: You will get undefined behaviour if something is already playing so
1818 * remember to stop before calling this. Also, this call will
1819 * effectively close your playlist, making it unusable.
1821 int playlist_set_current(struct playlist_info
* playlist
)
1823 if (!playlist
|| (check_control(playlist
) < 0))
1826 empty_playlist(¤t_playlist
, false);
1828 strncpy(current_playlist
.filename
, playlist
->filename
,
1829 sizeof(current_playlist
.filename
));
1831 current_playlist
.fd
= playlist
->fd
;
1833 close(playlist
->control_fd
);
1834 remove(current_playlist
.control_filename
);
1835 if (rename(playlist
->control_filename
,
1836 current_playlist
.control_filename
) < 0)
1838 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
1840 if (current_playlist
.control_fd
< 0)
1842 current_playlist
.control_created
= true;
1844 current_playlist
.dirlen
= playlist
->dirlen
;
1846 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
1847 memcpy(current_playlist
.indices
, playlist
->indices
,
1848 playlist
->max_playlist_size
*sizeof(int));
1850 current_playlist
.first_index
= playlist
->first_index
;
1851 current_playlist
.amount
= playlist
->amount
;
1852 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
1853 current_playlist
.seed
= playlist
->seed
;
1854 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
1855 current_playlist
.deleted
= playlist
->deleted
;
1856 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
1857 current_playlist
.shuffle_flush
= playlist
->shuffle_flush
;
1863 * Close files and delete control file for non-current playlist.
1865 void playlist_close(struct playlist_info
* playlist
)
1870 if (playlist
->fd
>= 0)
1871 close(playlist
->fd
);
1873 if (playlist
->control_fd
>= 0)
1874 close(playlist
->control_fd
);
1876 if (playlist
->control_created
)
1877 remove(playlist
->control_filename
);
1881 * Insert track into playlist at specified position (or one of the special
1882 * positions). Returns position where track was inserted or -1 if error.
1884 int playlist_insert_track(struct playlist_info
* playlist
, char *filename
,
1885 int position
, bool queue
)
1890 playlist
= ¤t_playlist
;
1892 if (check_control(playlist
) < 0)
1894 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1898 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
1902 fsync(playlist
->control_fd
);
1903 mpeg_flush_and_reload_tracks();
1910 * Insert all tracks from specified directory into playlist.
1912 int playlist_insert_directory(struct playlist_info
* playlist
, char *dirname
,
1913 int position
, bool queue
, bool recurse
)
1920 playlist
= ¤t_playlist
;
1922 if (check_control(playlist
) < 0)
1924 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1929 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
1931 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
1933 display_playlist_count(count
, count_str
);
1935 result
= add_directory_to_playlist(playlist
, dirname
, &position
, queue
,
1937 fsync(playlist
->control_fd
);
1939 display_playlist_count(count
, count_str
);
1940 mpeg_flush_and_reload_tracks();
1946 * Insert all tracks from specified playlist into dynamic playlist.
1948 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
1949 int position
, bool queue
)
1956 char temp_buf
[MAX_PATH
+1];
1957 char trackname
[MAX_PATH
+1];
1962 playlist
= ¤t_playlist
;
1964 if (check_control(playlist
) < 0)
1966 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1970 fd
= open(filename
, O_RDONLY
);
1973 splash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1977 /* we need the directory name for formatting purposes */
1980 temp_ptr
= strrchr(filename
+1,'/');
1987 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
1989 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
1991 display_playlist_count(count
, count_str
);
1993 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
1996 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
1997 if (button_get(false) == BUTTON_STOP
)
1999 if (button_get(false) == BUTTON_OFF
)
2003 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2007 /* we need to format so that relative paths are correctly
2009 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2016 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2025 /* Make sure tracks are inserted in correct order if user
2026 requests INSERT_FIRST */
2027 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
2028 position
= insert_pos
+ 1;
2032 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
2034 display_playlist_count(count
, count_str
);
2036 if (count
== PLAYLIST_DISPLAY_COUNT
)
2037 mpeg_flush_and_reload_tracks();
2041 /* let the other threads work */
2046 fsync(playlist
->control_fd
);
2051 display_playlist_count(count
, count_str
);
2052 mpeg_flush_and_reload_tracks();
2058 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2059 * we want to delete the current playing track.
2061 int playlist_delete(struct playlist_info
* playlist
, int index
)
2066 playlist
= ¤t_playlist
;
2068 if (check_control(playlist
) < 0)
2070 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2074 if (index
== PLAYLIST_DELETE_CURRENT
)
2075 index
= playlist
->index
;
2077 result
= remove_track_from_playlist(playlist
, index
, true);
2080 mpeg_flush_and_reload_tracks();
2086 * Move track at index to new_index. Tracks between the two are shifted
2087 * appropriately. Returns 0 on success and -1 on failure.
2089 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
2095 bool current
= false;
2097 char filename
[MAX_PATH
];
2100 playlist
= ¤t_playlist
;
2102 if (check_control(playlist
) < 0)
2104 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2108 if (index
== new_index
)
2111 if (index
== playlist
->index
)
2112 /* Moving the current track */
2115 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2116 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
2117 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2119 if (get_filename(playlist
, seek
, control_file
, filename
,
2120 sizeof(filename
)) < 0)
2123 /* Delete track from original position */
2124 result
= remove_track_from_playlist(playlist
, index
, true);
2128 /* We want to insert the track at the position that was specified by
2129 new_index. This may be different then new_index because of the
2130 shifting that occurred after the delete */
2131 r
= rotate_index(playlist
, new_index
);
2135 new_index
= PLAYLIST_PREPEND
;
2136 else if (r
== playlist
->amount
)
2138 new_index
= PLAYLIST_INSERT_LAST
;
2140 /* Calculate index of desired position */
2141 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
2143 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
2150 /* Moved the current track */
2153 case PLAYLIST_PREPEND
:
2154 playlist
->index
= playlist
->first_index
;
2156 case PLAYLIST_INSERT_LAST
:
2157 playlist
->index
= playlist
->first_index
- 1;
2158 if (playlist
->index
< 0)
2159 playlist
->index
+= playlist
->amount
;
2162 playlist
->index
= new_index
;
2167 fsync(playlist
->control_fd
);
2168 mpeg_flush_and_reload_tracks();
2175 /* shuffle currently playing playlist */
2176 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
2182 playlist
= ¤t_playlist
;
2184 check_control(playlist
);
2186 result
= randomise_playlist(playlist
, seed
, start_current
, true);
2189 mpeg_flush_and_reload_tracks();
2194 /* sort currently playing playlist */
2195 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
2200 playlist
= ¤t_playlist
;
2202 check_control(playlist
);
2204 result
= sort_playlist(playlist
, start_current
, true);
2207 mpeg_flush_and_reload_tracks();
2212 /* returns true if playlist has been modified */
2213 bool playlist_modified(struct playlist_info
* playlist
)
2216 playlist
= ¤t_playlist
;
2218 if (playlist
->shuffle_modified
||
2219 playlist
->deleted
||
2220 playlist
->num_inserted_tracks
> 0)
2226 /* returns index of first track in playlist */
2227 int playlist_get_first_index(struct playlist_info
* playlist
)
2230 playlist
= ¤t_playlist
;
2232 return playlist
->first_index
;
2235 /* returns shuffle seed of playlist */
2236 int playlist_get_seed(struct playlist_info
* playlist
)
2239 playlist
= ¤t_playlist
;
2241 return playlist
->seed
;
2244 /* returns number of tracks in playlist (includes queued/inserted tracks) */
2245 int playlist_amount_ex(struct playlist_info
* playlist
)
2248 playlist
= ¤t_playlist
;
2250 return playlist
->amount
;
2253 /* returns full path of playlist (minus extension) */
2254 char *playlist_name(struct playlist_info
* playlist
, char *buf
, int buf_size
)
2259 playlist
= ¤t_playlist
;
2261 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
2266 /* Remove extension */
2267 sep
= strrchr(buf
, '.');
2274 /* returns the playlist filename */
2275 char *playlist_get_name(struct playlist_info
* playlist
, char *buf
,
2279 playlist
= ¤t_playlist
;
2281 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
2289 /* Fills info structure with information about track at specified index.
2290 Returns 0 on success and -1 on failure */
2291 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
2292 struct playlist_track_info
* info
)
2298 playlist
= ¤t_playlist
;
2300 if (index
< 0 || index
>= playlist
->amount
)
2303 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2304 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2306 if (get_filename(playlist
, seek
, control_file
, info
->filename
,
2307 sizeof(info
->filename
)) < 0)
2314 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2315 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
2317 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
2320 info
->index
= index
;
2321 info
->display_index
= rotate_index(playlist
, index
) + 1;
2326 /* save the current dynamic playlist to specified file */
2327 int playlist_save(struct playlist_info
* playlist
, char *filename
)
2332 char tmp_buf
[MAX_PATH
+1];
2336 playlist
= ¤t_playlist
;
2338 if (playlist
->amount
<= 0)
2341 /* use current working directory as base for pathname */
2342 if (format_track_path(tmp_buf
, filename
, sizeof(tmp_buf
),
2343 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
2346 fd
= open(tmp_buf
, O_CREAT
|O_WRONLY
|O_TRUNC
);
2349 splash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2353 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
2355 index
= playlist
->first_index
;
2356 for (i
=0; i
<playlist
->amount
; i
++)
2363 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
2364 if (button_get(false) == BUTTON_STOP
)
2366 if (button_get(false) == BUTTON_OFF
)
2370 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2371 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
2372 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2374 /* Don't save queued files */
2377 if (get_filename(playlist
, seek
, control_file
, tmp_buf
,
2384 if (fprintf(fd
, "%s\n", tmp_buf
) < 0)
2386 splash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
2393 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
2394 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
2399 index
= (index
+1)%playlist
->amount
;
2402 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));