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.
82 #include "applimits.h"
93 #ifdef HAVE_LCD_BITMAP
102 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
103 #define PLAYLIST_CONTROL_FILE_VERSION 2
106 Each playlist index has a flag associated with it which identifies what
107 type of track it is. These flags are stored in the 4 high order bits of
110 NOTE: This limits the playlist file size to a max of 256M.
114 01 = Track was prepended into playlist
115 10 = Track was inserted into playlist
116 11 = Track was appended into playlist
121 0 = Track entry is valid
122 1 = Track does not exist on disk and should be skipped
124 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
125 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
126 #define PLAYLIST_QUEUE_MASK 0x20000000
128 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
129 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
130 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
132 #define PLAYLIST_QUEUED 0x20000000
133 #define PLAYLIST_SKIPPED 0x10000000
135 #define PLAYLIST_DISPLAY_COUNT 10
137 struct directory_search_context
{
138 struct playlist_info
* playlist
;
144 static bool changing_dir
= false;
146 static struct playlist_info current_playlist
;
147 static char now_playing
[MAX_PATH
+1];
149 static void empty_playlist(struct playlist_info
* playlist
, bool resume
);
150 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
152 static void create_control(struct playlist_info
* playlist
);
153 static int check_control(struct playlist_info
* playlist
);
154 static int recreate_control(struct playlist_info
* playlist
);
155 static void update_playlist_filename(struct playlist_info
* playlist
,
156 const char *dir
, const char *file
);
157 static int add_indices_to_playlist(struct playlist_info
* playlist
,
158 char* buffer
, int buflen
);
159 static int add_track_to_playlist(struct playlist_info
* playlist
,
160 const char *filename
, int position
,
161 bool queue
, int seek_pos
);
162 static int directory_search_callback(char* filename
, void* context
);
163 static int remove_track_from_playlist(struct playlist_info
* playlist
,
164 int position
, bool write
);
165 static int randomise_playlist(struct playlist_info
* playlist
,
166 unsigned int seed
, bool start_current
,
168 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
170 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
172 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
174 static int compare(const void* p1
, const void* p2
);
175 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
176 bool control_file
, char *buf
, int buf_length
);
177 static int get_next_directory(char *dir
);
178 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
);
179 static int get_previous_directory(char *dir
);
180 static int check_subdir_for_music(char *dir
, char *subdir
);
181 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
183 static void display_playlist_count(int count
, const unsigned char *fmt
);
184 static void display_buffer_full(void);
185 static int flush_cached_control(struct playlist_info
* playlist
);
186 static int update_control(struct playlist_info
* playlist
,
187 enum playlist_command command
, int i1
, int i2
,
188 const char* s1
, const char* s2
, void* data
);
189 static void sync_control(struct playlist_info
* playlist
, bool force
);
190 static int rotate_index(const struct playlist_info
* playlist
, int index
);
193 #define PLAYLIST_LOAD_POINTERS 1
195 static struct event_queue playlist_queue
;
196 static long playlist_stack
[(DEFAULT_STACK_SIZE
+ 0x400)/sizeof(long)];
197 static const char playlist_thread_name
[] = "playlist cachectrl";
201 * remove any files and indices associated with the playlist
203 static void empty_playlist(struct playlist_info
* playlist
, bool resume
)
205 playlist
->filename
[0] = '\0';
207 if(playlist
->fd
>= 0)
208 /* If there is an already open playlist, close it. */
212 if(playlist
->control_fd
>= 0)
213 close(playlist
->control_fd
);
214 playlist
->control_fd
= -1;
215 playlist
->control_created
= false;
217 playlist
->in_ram
= false;
219 if (playlist
->buffer
)
220 playlist
->buffer
[0] = 0;
222 playlist
->buffer_end_pos
= 0;
225 playlist
->first_index
= 0;
226 playlist
->amount
= 0;
227 playlist
->last_insert_pos
= -1;
229 playlist
->shuffle_modified
= false;
230 playlist
->deleted
= false;
231 playlist
->num_inserted_tracks
= 0;
232 playlist
->started
= false;
234 playlist
->num_cached
= 0;
235 playlist
->pending_control_sync
= false;
237 if (!resume
&& playlist
->current
)
239 /* start with fresh playlist control file when starting new
241 create_control(playlist
);
243 /* Reset resume settings */
244 global_settings
.resume_first_index
= 0;
245 global_settings
.resume_seed
= -1;
250 * Initialize a new playlist for viewing/editing/playing. dir is the
251 * directory where the playlist is located and file is the filename.
253 static void new_playlist(struct playlist_info
* playlist
, const char *dir
,
256 empty_playlist(playlist
, false);
262 if (dir
&& playlist
->current
) /* !current cannot be in_ram */
263 playlist
->in_ram
= true;
265 dir
= ""; /* empty playlist */
268 update_playlist_filename(playlist
, dir
, file
);
270 if (playlist
->control_fd
>= 0)
272 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
273 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
274 sync_control(playlist
, false);
279 * create control file for playlist
281 static void create_control(struct playlist_info
* playlist
)
283 playlist
->control_fd
= open(playlist
->control_filename
,
284 O_CREAT
|O_RDWR
|O_TRUNC
);
285 if (playlist
->control_fd
< 0)
287 if (check_rockboxdir())
289 gui_syncsplash(HZ
*2, true, (unsigned char *)"%s (%d)",
290 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
),
291 playlist
->control_fd
);
293 playlist
->control_created
= false;
297 playlist
->control_created
= true;
302 * validate the control file. This may include creating/initializing it if
305 static int check_control(struct playlist_info
* playlist
)
307 if (!playlist
->control_created
)
309 create_control(playlist
);
311 if (playlist
->control_fd
>= 0)
313 char* dir
= playlist
->filename
;
314 char* file
= playlist
->filename
+playlist
->dirlen
;
315 char c
= playlist
->filename
[playlist
->dirlen
-1];
317 playlist
->filename
[playlist
->dirlen
-1] = '\0';
319 update_control(playlist
, PLAYLIST_COMMAND_PLAYLIST
,
320 PLAYLIST_CONTROL_FILE_VERSION
, -1, dir
, file
, NULL
);
321 sync_control(playlist
, false);
322 playlist
->filename
[playlist
->dirlen
-1] = c
;
326 if (playlist
->control_fd
< 0)
333 * recreate the control file based on current playlist entries
335 static int recreate_control(struct playlist_info
* playlist
)
337 char temp_file
[MAX_PATH
+1];
342 if(playlist
->control_fd
>= 0)
344 char* dir
= playlist
->filename
;
345 char* file
= playlist
->filename
+playlist
->dirlen
;
346 char c
= playlist
->filename
[playlist
->dirlen
-1];
348 close(playlist
->control_fd
);
350 snprintf(temp_file
, sizeof(temp_file
), "%s_temp",
351 playlist
->control_filename
);
353 if (rename(playlist
->control_filename
, temp_file
) < 0)
356 temp_fd
= open(temp_file
, O_RDONLY
);
360 playlist
->control_fd
= open(playlist
->control_filename
,
361 O_CREAT
|O_RDWR
|O_TRUNC
);
362 if (playlist
->control_fd
< 0)
365 playlist
->filename
[playlist
->dirlen
-1] = '\0';
367 /* cannot call update_control() because of mutex */
368 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
369 PLAYLIST_CONTROL_FILE_VERSION
, dir
, file
);
371 playlist
->filename
[playlist
->dirlen
-1] = c
;
381 playlist
->shuffle_modified
= false;
382 playlist
->deleted
= false;
383 playlist
->num_inserted_tracks
= 0;
385 if (playlist
->current
)
387 global_settings
.resume_seed
= -1;
391 for (i
=0; i
<playlist
->amount
; i
++)
393 if (playlist
->indices
[i
] & PLAYLIST_INSERT_TYPE_MASK
)
395 bool queue
= playlist
->indices
[i
] & PLAYLIST_QUEUE_MASK
;
396 char inserted_file
[MAX_PATH
+1];
398 lseek(temp_fd
, playlist
->indices
[i
] & PLAYLIST_SEEK_MASK
,
400 read_line(temp_fd
, inserted_file
, sizeof(inserted_file
));
402 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
403 queue
?'Q':'A', i
, playlist
->last_insert_pos
);
406 /* save the position in file where name is written */
407 int seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
409 result
= fdprintf(playlist
->control_fd
, "%s\n",
412 playlist
->indices
[i
] =
413 (playlist
->indices
[i
] & ~PLAYLIST_SEEK_MASK
) | seek_pos
;
419 playlist
->num_inserted_tracks
++;
425 fsync(playlist
->control_fd
);
434 * store directory and name of playlist file
436 static void update_playlist_filename(struct playlist_info
* playlist
,
437 const char *dir
, const char *file
)
440 int dirlen
= strlen(dir
);
442 /* If the dir does not end in trailing slash, we use a separator.
443 Otherwise we don't. */
444 if('/' != dir
[dirlen
-1])
450 playlist
->dirlen
= dirlen
;
452 snprintf(playlist
->filename
, sizeof(playlist
->filename
),
453 "%s%s%s", dir
, sep
, file
);
457 * calculate track offsets within a playlist file
459 static int add_indices_to_playlist(struct playlist_info
* playlist
,
460 char* buffer
, int buflen
)
464 unsigned int count
= 0;
469 if(-1 == playlist
->fd
)
470 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
472 return -1; /* failure */
474 #ifdef HAVE_LCD_BITMAP
475 if(global_settings
.statusbar
)
476 lcd_setmargins(0, STATUSBAR_HEIGHT
);
478 lcd_setmargins(0, 0);
481 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD
));
485 /* use mp3 buffer for maximum load speed */
487 #if CONFIG_CODEC != SWCODEC
488 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
489 buflen
= (audiobufend
- audiobuf
);
490 buffer
= (char *)audiobuf
;
492 buflen
= (audiobufend
- audiobuf
- talk_get_bufsize());
493 buffer
= (char *)&audiobuf
[talk_get_bufsize()];
501 nread
= read(playlist
->fd
, buffer
, buflen
);
502 /* Terminate on EOF */
506 p
= (unsigned char *)buffer
;
508 /* utf8 BOM at beginning of file? */
509 if(i
== 0 && nread
> 3
510 && *p
== 0xef && *(p
+1) == 0xbb && *(p
+2) == 0xbf) {
516 for(count
=0; count
< nread
; count
++,p
++) {
518 /* Are we on a new line? */
519 if((*p
== '\n') || (*p
== '\r'))
529 if ( playlist
->amount
>= playlist
->max_playlist_size
) {
530 display_buffer_full();
535 /* Store a new entry */
536 playlist
->indices
[ playlist
->amount
] = i
+count
;
538 if (playlist
->filenames
)
539 playlist
->filenames
[ playlist
->amount
] = NULL
;
551 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
558 * Add track to playlist at specified position. There are five special
559 * positions that can be specified:
560 * PLAYLIST_PREPEND - Add track at beginning of playlist
561 * PLAYLIST_INSERT - Add track after current song. NOTE: If
562 * there are already inserted tracks then track
563 * is added to the end of the insertion list
564 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
565 * matter what other tracks have been inserted
566 * PLAYLIST_INSERT_LAST - Add track to end of playlist
567 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
568 * current playing track and end of playlist
570 static int add_track_to_playlist(struct playlist_info
* playlist
,
571 const char *filename
, int position
,
572 bool queue
, int seek_pos
)
574 int insert_position
, orig_position
;
575 unsigned long flags
= PLAYLIST_INSERT_TYPE_INSERT
;
578 insert_position
= orig_position
= position
;
580 if (playlist
->amount
>= playlist
->max_playlist_size
)
582 display_buffer_full();
588 case PLAYLIST_PREPEND
:
589 position
= insert_position
= playlist
->first_index
;
591 case PLAYLIST_INSERT
:
592 /* if there are already inserted tracks then add track to end of
593 insertion list else add after current playing track */
594 if (playlist
->last_insert_pos
>= 0 &&
595 playlist
->last_insert_pos
< playlist
->amount
&&
596 (playlist
->indices
[playlist
->last_insert_pos
]&
597 PLAYLIST_INSERT_TYPE_MASK
) == PLAYLIST_INSERT_TYPE_INSERT
)
598 position
= insert_position
= playlist
->last_insert_pos
+1;
599 else if (playlist
->amount
> 0)
600 position
= insert_position
= playlist
->index
+ 1;
602 position
= insert_position
= 0;
604 if (playlist
->started
)
605 playlist
->last_insert_pos
= position
;
607 case PLAYLIST_INSERT_FIRST
:
608 if (playlist
->amount
> 0)
609 position
= insert_position
= playlist
->index
+ 1;
611 position
= insert_position
= 0;
613 if (playlist
->last_insert_pos
< 0 && playlist
->started
)
614 playlist
->last_insert_pos
= position
;
616 case PLAYLIST_INSERT_LAST
:
617 if (playlist
->first_index
> 0)
618 position
= insert_position
= playlist
->first_index
;
620 position
= insert_position
= playlist
->amount
;
622 case PLAYLIST_INSERT_SHUFFLED
:
624 if (playlist
->started
)
627 int n
= playlist
->amount
-
628 rotate_index(playlist
, playlist
->index
);
635 position
= playlist
->index
+ offset
+ 1;
636 if (position
>= playlist
->amount
)
637 position
-= playlist
->amount
;
639 insert_position
= position
;
642 position
= insert_position
= (rand() % (playlist
->amount
+1));
648 flags
|= PLAYLIST_QUEUED
;
650 /* shift indices so that track can be added */
651 for (i
=playlist
->amount
; i
>insert_position
; i
--)
653 playlist
->indices
[i
] = playlist
->indices
[i
-1];
655 if (playlist
->filenames
)
656 playlist
->filenames
[i
] = playlist
->filenames
[i
-1];
660 /* update stored indices if needed */
661 if (playlist
->amount
> 0 && insert_position
<= playlist
->index
&&
665 if (playlist
->amount
> 0 && insert_position
<= playlist
->first_index
&&
666 orig_position
!= PLAYLIST_PREPEND
&& playlist
->started
)
668 playlist
->first_index
++;
670 if (seek_pos
< 0 && playlist
->current
)
672 global_settings
.resume_first_index
= playlist
->first_index
;
677 if (insert_position
< playlist
->last_insert_pos
||
678 (insert_position
== playlist
->last_insert_pos
&& position
< 0))
679 playlist
->last_insert_pos
++;
681 if (seek_pos
< 0 && playlist
->control_fd
>= 0)
683 int result
= update_control(playlist
,
684 (queue
?PLAYLIST_COMMAND_QUEUE
:PLAYLIST_COMMAND_ADD
), position
,
685 playlist
->last_insert_pos
, filename
, NULL
, &seek_pos
);
691 playlist
->indices
[insert_position
] = flags
| seek_pos
;
694 if (playlist
->filenames
)
695 playlist
->filenames
[insert_position
] = NULL
;
699 playlist
->num_inserted_tracks
++;
701 return insert_position
;
705 * Callback for playlist_directory_tracksearch to insert track into
708 static int directory_search_callback(char* filename
, void* context
)
710 struct directory_search_context
* c
=
711 (struct directory_search_context
*) context
;
714 insert_pos
= add_track_to_playlist(c
->playlist
, filename
, c
->position
,
722 /* Make sure tracks are inserted in correct order if user requests
724 if (c
->position
== PLAYLIST_INSERT_FIRST
|| c
->position
>= 0)
725 c
->position
= insert_pos
+ 1;
727 if (((c
->count
)%PLAYLIST_DISPLAY_COUNT
) == 0)
729 unsigned char* count_str
;
732 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
734 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
736 display_playlist_count(c
->count
, count_str
);
738 if ((c
->count
) == PLAYLIST_DISPLAY_COUNT
&&
739 (audio_status() & AUDIO_STATUS_PLAY
) &&
740 c
->playlist
->started
)
741 audio_flush_and_reload_tracks();
748 * remove track at specified position
750 static int remove_track_from_playlist(struct playlist_info
* playlist
,
751 int position
, bool write
)
756 if (playlist
->amount
<= 0)
759 inserted
= playlist
->indices
[position
] & PLAYLIST_INSERT_TYPE_MASK
;
761 /* shift indices now that track has been removed */
762 for (i
=position
; i
<playlist
->amount
; i
++)
764 playlist
->indices
[i
] = playlist
->indices
[i
+1];
766 if (playlist
->filenames
)
767 playlist
->filenames
[i
] = playlist
->filenames
[i
+1];
774 playlist
->num_inserted_tracks
--;
776 playlist
->deleted
= true;
778 /* update stored indices if needed */
779 if (position
< playlist
->index
)
782 if (position
< playlist
->first_index
)
784 playlist
->first_index
--;
788 global_settings
.resume_first_index
= playlist
->first_index
;
793 if (position
<= playlist
->last_insert_pos
)
794 playlist
->last_insert_pos
--;
796 if (write
&& playlist
->control_fd
>= 0)
798 int result
= update_control(playlist
, PLAYLIST_COMMAND_DELETE
,
799 position
, -1, NULL
, NULL
, NULL
);
804 sync_control(playlist
, false);
811 * randomly rearrange the array of indices for the playlist. If start_current
812 * is true then update the index to the new index of the current playing track
814 static int randomise_playlist(struct playlist_info
* playlist
,
815 unsigned int seed
, bool start_current
,
821 unsigned int current
= playlist
->indices
[playlist
->index
];
823 /* seed 0 is used to identify sorted playlist for resume purposes */
827 /* seed with the given seed */
830 /* randomise entire indices list */
831 for(count
= playlist
->amount
- 1; count
>= 0; count
--)
833 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
834 candidate
= rand() % (count
+ 1);
836 /* now swap the values at the 'count' and 'candidate' positions */
837 store
= playlist
->indices
[candidate
];
838 playlist
->indices
[candidate
] = playlist
->indices
[count
];
839 playlist
->indices
[count
] = store
;
841 if (playlist
->filenames
)
843 store
= (long)playlist
->filenames
[candidate
];
844 playlist
->filenames
[candidate
] = playlist
->filenames
[count
];
845 playlist
->filenames
[count
] = (struct dircache_entry
*)store
;
851 find_and_set_playlist_index(playlist
, current
);
853 /* indices have been moved so last insert position is no longer valid */
854 playlist
->last_insert_pos
= -1;
856 playlist
->seed
= seed
;
857 if (playlist
->num_inserted_tracks
> 0 || playlist
->deleted
)
858 playlist
->shuffle_modified
= true;
862 update_control(playlist
, PLAYLIST_COMMAND_SHUFFLE
, seed
,
863 playlist
->first_index
, NULL
, NULL
, NULL
);
864 global_settings
.resume_seed
= seed
;
872 * Sort the array of indices for the playlist. If start_current is true then
873 * set the index to the new index of the current song.
875 static int sort_playlist(struct playlist_info
* playlist
, bool start_current
,
878 unsigned int current
= playlist
->indices
[playlist
->index
];
880 if (playlist
->amount
> 0)
881 qsort(playlist
->indices
, playlist
->amount
,
882 sizeof(playlist
->indices
[0]), compare
);
885 /** We need to re-check the song names from disk because qsort can't
886 * sort two arrays at once :/
887 * FIXME: Please implement a better way to do this. */
888 memset(playlist
->filenames
, 0, playlist
->max_playlist_size
* sizeof(int));
889 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
893 find_and_set_playlist_index(playlist
, current
);
895 /* indices have been moved so last insert position is no longer valid */
896 playlist
->last_insert_pos
= -1;
898 if (!playlist
->num_inserted_tracks
&& !playlist
->deleted
)
899 playlist
->shuffle_modified
= false;
900 if (write
&& playlist
->control_fd
>= 0)
902 update_control(playlist
, PLAYLIST_COMMAND_UNSHUFFLE
,
903 playlist
->first_index
, -1, NULL
, NULL
, NULL
);
904 global_settings
.resume_seed
= 0;
911 /* Calculate how many steps we have to really step when skipping entries
914 static int calculate_step_count(const struct playlist_info
*playlist
, int steps
)
916 int i
, count
, direction
;
918 int stepped_count
= 0;
931 index
= playlist
->index
;
936 index
+= playlist
->amount
;
937 if (index
>= playlist
->amount
)
938 index
-= playlist
->amount
;
940 /* Check if we found a bad entry. */
941 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
944 /* Are all entries bad? */
945 if (stepped_count
++ > playlist
->amount
)
952 } while (i
<= count
);
957 /* Marks the index of the track to be skipped that is "steps" away from
958 * current playing track.
960 void playlist_skip_entry(struct playlist_info
*playlist
, int steps
)
964 if (playlist
== NULL
)
965 playlist
= ¤t_playlist
;
967 /* need to account for already skipped tracks */
968 steps
= calculate_step_count(playlist
, steps
);
970 index
= playlist
->index
+ steps
;
972 index
+= playlist
->amount
;
973 else if (index
>= playlist
->amount
)
974 index
-= playlist
->amount
;
976 playlist
->indices
[index
] |= PLAYLIST_SKIPPED
;
980 * returns the index of the track that is "steps" away from current playing
983 static int get_next_index(const struct playlist_info
* playlist
, int steps
,
986 int current_index
= playlist
->index
;
989 if (playlist
->amount
<= 0)
992 if (repeat_mode
== -1)
993 repeat_mode
= global_settings
.repeat_mode
;
995 if (repeat_mode
== REPEAT_SHUFFLE
&&
996 (!global_settings
.playlist_shuffle
|| playlist
->amount
<= 1))
997 repeat_mode
= REPEAT_ALL
;
999 steps
= calculate_step_count(playlist
, steps
);
1000 switch (repeat_mode
)
1002 case REPEAT_SHUFFLE
:
1003 /* Treat repeat shuffle just like repeat off. At end of playlist,
1004 play will be resumed in playlist_next() */
1007 current_index
= rotate_index(playlist
, current_index
);
1008 next_index
= current_index
+steps
;
1009 if ((next_index
< 0) || (next_index
>= playlist
->amount
))
1012 next_index
= (next_index
+playlist
->first_index
) %
1019 #if (AB_REPEAT_ENABLE == 1)
1022 next_index
= current_index
;
1028 next_index
= (current_index
+steps
) % playlist
->amount
;
1029 while (next_index
< 0)
1030 next_index
+= playlist
->amount
;
1032 if (steps
>= playlist
->amount
)
1039 /* second time around so skip the queued files */
1040 for (i
=0; i
<playlist
->amount
; i
++)
1042 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
1043 index
= (index
+1) % playlist
->amount
;
1055 /* No luck if the whole playlist was bad. */
1056 if (playlist
->indices
[next_index
] & PLAYLIST_SKIPPED
)
1063 * Search for the seek track and set appropriate indices. Used after shuffle
1064 * to make sure the current index is still pointing to correct track.
1066 static void find_and_set_playlist_index(struct playlist_info
* playlist
,
1071 /* Set the index to the current song */
1072 for (i
=0; i
<playlist
->amount
; i
++)
1074 if (playlist
->indices
[i
] == seek
)
1076 playlist
->index
= playlist
->first_index
= i
;
1078 if (playlist
->current
)
1080 global_settings
.resume_first_index
= i
;
1090 * used to sort track indices. Sort order is as follows:
1091 * 1. Prepended tracks (in prepend order)
1092 * 2. Playlist/directory tracks (in playlist order)
1093 * 3. Inserted/Appended tracks (in insert order)
1095 static int compare(const void* p1
, const void* p2
)
1097 unsigned long* e1
= (unsigned long*) p1
;
1098 unsigned long* e2
= (unsigned long*) p2
;
1099 unsigned long flags1
= *e1
& PLAYLIST_INSERT_TYPE_MASK
;
1100 unsigned long flags2
= *e2
& PLAYLIST_INSERT_TYPE_MASK
;
1102 if (flags1
== flags2
)
1103 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1104 else if (flags1
== PLAYLIST_INSERT_TYPE_PREPEND
||
1105 flags2
== PLAYLIST_INSERT_TYPE_APPEND
)
1107 else if (flags1
== PLAYLIST_INSERT_TYPE_APPEND
||
1108 flags2
== PLAYLIST_INSERT_TYPE_PREPEND
)
1110 else if (flags1
&& flags2
)
1111 return (*e1
& PLAYLIST_SEEK_MASK
) - (*e2
& PLAYLIST_SEEK_MASK
);
1116 #ifdef HAVE_DIRCACHE
1118 * Thread to update filename pointers to dircache on background
1119 * without affecting playlist load up performance. This thread also flushes
1120 * any pending control commands when the disk spins up.
1122 static void playlist_thread(void)
1125 bool dirty_pointers
= false;
1126 static char tmp
[MAX_PATH
+1];
1128 struct playlist_info
*playlist
;
1135 if (global_settings
.disk_spindown
> 1 &&
1136 global_settings
.disk_spindown
<= 5)
1137 sleep_time
= global_settings
.disk_spindown
- 1;
1141 queue_wait_w_tmo(&playlist_queue
, &ev
, HZ
*sleep_time
);
1145 case PLAYLIST_LOAD_POINTERS
:
1146 dirty_pointers
= true;
1149 /* Start the background scanning after either the disk spindown
1150 timeout or 5s, whichever is less */
1152 playlist
= ¤t_playlist
;
1154 if (playlist
->control_fd
>= 0
1156 && ata_disk_is_active()
1160 if (playlist
->num_cached
> 0)
1162 mutex_lock(&playlist
->control_mutex
);
1163 flush_cached_control(playlist
);
1164 mutex_unlock(&playlist
->control_mutex
);
1167 sync_control(playlist
, true);
1170 if (!dirty_pointers
)
1173 if (!dircache_is_enabled() || !playlist
->filenames
1174 || playlist
->amount
<= 0)
1177 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1180 for (index
= 0; index
< playlist
->amount
1181 && queue_empty(&playlist_queue
); index
++)
1183 /* Process only pointers that are not already loaded. */
1184 if (playlist
->filenames
[index
])
1187 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
1188 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
1190 /* Load the filename from playlist file. */
1191 if (get_filename(playlist
, index
, seek
, control_file
, tmp
,
1195 /* Set the dircache entry pointer. */
1196 playlist
->filenames
[index
] = dircache_get_entry_ptr(tmp
);
1198 /* And be on background so user doesn't notice any delays. */
1202 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1205 dirty_pointers
= false;
1209 case SYS_USB_CONNECTED
:
1210 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1211 usb_wait_for_disconnect(&playlist_queue
);
1220 * gets pathname for track at seek index
1222 static int get_filename(struct playlist_info
* playlist
, int index
, int seek
,
1223 bool control_file
, char *buf
, int buf_length
)
1227 char tmp_buf
[MAX_PATH
+1];
1228 char dir_buf
[MAX_PATH
+1];
1230 if (buf_length
> MAX_PATH
+1)
1231 buf_length
= MAX_PATH
+1;
1233 #ifdef HAVE_DIRCACHE
1234 if (dircache_is_enabled() && playlist
->filenames
)
1236 if (playlist
->filenames
[index
] != NULL
)
1238 dircache_copy_path(playlist
->filenames
[index
], tmp_buf
, sizeof(tmp_buf
)-1);
1239 max
= strlen(tmp_buf
) + 1;
1246 if (playlist
->in_ram
&& !control_file
&& max
< 0)
1248 strncpy(tmp_buf
, &playlist
->buffer
[seek
], sizeof(tmp_buf
));
1249 tmp_buf
[MAX_PATH
] = '\0';
1250 max
= strlen(tmp_buf
) + 1;
1254 mutex_lock(&playlist
->control_mutex
);
1257 fd
= playlist
->control_fd
;
1260 if(-1 == playlist
->fd
)
1261 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
1269 if (lseek(fd
, seek
, SEEK_SET
) != seek
)
1272 max
= read(fd
, tmp_buf
, buf_length
);
1275 mutex_unlock(&playlist
->control_mutex
);
1280 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1282 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
1288 strncpy(dir_buf
, playlist
->filename
, playlist
->dirlen
-1);
1289 dir_buf
[playlist
->dirlen
-1] = 0;
1291 return (format_track_path(buf
, tmp_buf
, buf_length
, max
, dir_buf
));
1294 static int get_next_directory(char *dir
){
1295 return get_next_dir(dir
,true,false);
1298 static int get_previous_directory(char *dir
){
1299 return get_next_dir(dir
,false,false);
1303 * search through all the directories (starting with the current) to find
1304 * one that has tracks to play
1306 static int get_next_dir(char *dir
, bool is_forward
, bool recursion
)
1308 struct playlist_info
* playlist
= ¤t_playlist
;
1310 int sort_dir
= global_settings
.sort_dir
;
1311 char *start_dir
= NULL
;
1313 struct tree_context
* tc
= tree_get_context();
1314 int dirfilter
= *(tc
->dirfilter
);
1316 if (global_settings
.next_folder
== FOLDER_ADVANCE_RANDOM
)
1318 int fd
= open(ROCKBOX_DIR
"/folder_advance_list.dat",O_RDONLY
);
1319 char buffer
[MAX_PATH
];
1320 int folder_count
= 0,i
;
1321 srand(current_tick
);
1324 read(fd
,&folder_count
,sizeof(int));
1327 i
= rand()%folder_count
;
1328 lseek(fd
,sizeof(int) + (MAX_PATH
*i
),SEEK_SET
);
1329 read(fd
,buffer
,MAX_PATH
);
1330 if (check_subdir_for_music(buffer
,"") ==0)
1338 /* not random folder advance */
1340 /* start with root */
1344 /* start with current directory */
1345 strncpy(dir
, playlist
->filename
, playlist
->dirlen
-1);
1346 dir
[playlist
->dirlen
-1] = '\0';
1349 /* use the tree browser dircache to load files */
1350 *(tc
->dirfilter
) = SHOW_ALL
;
1352 /* sort in another direction if previous dir is requested */
1354 if ((global_settings
.sort_dir
== 0) || (global_settings
.sort_dir
== 3))
1355 global_settings
.sort_dir
= 4;
1356 else if (global_settings
.sort_dir
== 1)
1357 global_settings
.sort_dir
= 2;
1358 else if (global_settings
.sort_dir
== 2)
1359 global_settings
.sort_dir
= 1;
1360 else if (global_settings
.sort_dir
== 4)
1361 global_settings
.sort_dir
= 0;
1366 struct entry
*files
;
1370 if (ft_load(tc
, (dir
[0]=='\0')?"/":dir
) < 0)
1372 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1378 files
= (struct entry
*) tc
->dircache
;
1379 num_files
= tc
->filesindir
;
1381 for (i
=0; i
<num_files
; i
++)
1384 if (action_userabort(TIMEOUT_NOBLOCK
))
1391 if (files
[i
].attr
& ATTR_DIRECTORY
)
1395 result
= check_subdir_for_music(dir
, files
[i
].name
);
1402 else if (!strcmp(start_dir
, files
[i
].name
))
1409 /* move down to parent directory. current directory name is
1410 stored as the starting point for the search in parent */
1411 start_dir
= strrchr(dir
, '/');
1422 /* we've overwritten the dircache so tree browser will need to be
1426 /* restore dirfilter & sort_dir */
1427 *(tc
->dirfilter
) = dirfilter
;
1428 global_settings
.sort_dir
= sort_dir
;
1430 /* special case if nothing found: try start searching again from root */
1431 if (result
== -1 && !recursion
){
1432 result
= get_next_dir(dir
,is_forward
, true);
1439 * Checks if there are any music files in the dir or any of its
1440 * subdirectories. May be called recursively.
1442 static int check_subdir_for_music(char *dir
, char *subdir
)
1445 int dirlen
= strlen(dir
);
1448 struct entry
*files
;
1449 bool has_music
= false;
1450 bool has_subdir
= false;
1451 struct tree_context
* tc
= tree_get_context();
1453 snprintf(dir
+dirlen
, MAX_PATH
-dirlen
, "/%s", subdir
);
1455 if (ft_load(tc
, dir
) < 0)
1457 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1461 files
= (struct entry
*) tc
->dircache
;
1462 num_files
= tc
->filesindir
;
1464 for (i
=0; i
<num_files
; i
++)
1466 if (files
[i
].attr
& ATTR_DIRECTORY
)
1468 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
1480 for (i
=0; i
<num_files
; i
++)
1482 if (action_userabort(TIMEOUT_NOBLOCK
))
1488 if (files
[i
].attr
& ATTR_DIRECTORY
)
1490 result
= check_subdir_for_music(dir
, files
[i
].name
);
1508 /* we now need to reload our current directory */
1509 if(ft_load(tc
, dir
) < 0)
1510 gui_syncsplash(HZ
*2, true,
1511 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
1518 * Returns absolute path of track
1520 static int format_track_path(char *dest
, char *src
, int buf_length
, int max
,
1527 /* Zero-terminate the file name */
1528 while((src
[i
] != '\n') &&
1533 /* Now work back killing white space */
1534 while((src
[i
-1] == ' ') ||
1540 /* replace backslashes with forward slashes */
1541 for ( j
=0; j
<i
; j
++ )
1542 if ( src
[j
] == '\\' )
1547 strncpy(dest
, src
, buf_length
);
1551 /* handle dos style drive letter */
1553 strncpy(dest
, &src
[2], buf_length
);
1554 else if (!strncmp(src
, "../", 3))
1556 /* handle relative paths */
1558 while(!strncmp(&src
[i
], "../", 3))
1560 for (j
=0; j
<i
/3; j
++) {
1561 temp_ptr
= strrchr(dir
, '/');
1567 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[i
]);
1569 else if ( '.' == src
[0] && '/' == src
[1] ) {
1570 snprintf(dest
, buf_length
, "%s/%s", dir
, &src
[2]);
1573 snprintf(dest
, buf_length
, "%s/%s", dir
, src
);
1581 * Display splash message showing progress of playlist/directory insertion or
1584 static void display_playlist_count(int count
, const unsigned char *fmt
)
1586 lcd_clear_display();
1588 #ifdef HAVE_LCD_BITMAP
1589 if(global_settings
.statusbar
)
1590 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1592 lcd_setmargins(0, 0);
1595 gui_syncsplash(0, true, fmt
, count
,
1596 #if CONFIG_KEYPAD == PLAYER_PAD
1597 str(LANG_STOP_ABORT
)
1605 * Display buffer full message
1607 static void display_buffer_full(void)
1609 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_BUFFER_FULL
));
1613 * Flush any cached control commands to disk. Called when playlist is being
1614 * modified. Returns 0 on success and -1 on failure.
1616 static int flush_cached_control(struct playlist_info
* playlist
)
1621 if (!playlist
->num_cached
)
1624 lseek(playlist
->control_fd
, 0, SEEK_END
);
1626 for (i
=0; i
<playlist
->num_cached
; i
++)
1628 struct playlist_control_cache
* cache
=
1629 &(playlist
->control_cache
[i
]);
1631 switch (cache
->command
)
1633 case PLAYLIST_COMMAND_PLAYLIST
:
1634 result
= fdprintf(playlist
->control_fd
, "P:%d:%s:%s\n",
1635 cache
->i1
, cache
->s1
, cache
->s2
);
1637 case PLAYLIST_COMMAND_ADD
:
1638 case PLAYLIST_COMMAND_QUEUE
:
1639 result
= fdprintf(playlist
->control_fd
, "%c:%d:%d:",
1640 (cache
->command
== PLAYLIST_COMMAND_ADD
)?'A':'Q',
1641 cache
->i1
, cache
->i2
);
1644 /* save the position in file where name is written */
1645 int* seek_pos
= (int *)cache
->data
;
1646 *seek_pos
= lseek(playlist
->control_fd
, 0, SEEK_CUR
);
1647 result
= fdprintf(playlist
->control_fd
, "%s\n",
1651 case PLAYLIST_COMMAND_DELETE
:
1652 result
= fdprintf(playlist
->control_fd
, "D:%d\n", cache
->i1
);
1654 case PLAYLIST_COMMAND_SHUFFLE
:
1655 result
= fdprintf(playlist
->control_fd
, "S:%d:%d\n",
1656 cache
->i1
, cache
->i2
);
1658 case PLAYLIST_COMMAND_UNSHUFFLE
:
1659 result
= fdprintf(playlist
->control_fd
, "U:%d\n", cache
->i1
);
1661 case PLAYLIST_COMMAND_RESET
:
1662 result
= fdprintf(playlist
->control_fd
, "R\n");
1674 if (global_settings
.resume_seed
>= 0)
1676 global_settings
.resume_seed
= -1;
1680 playlist
->num_cached
= 0;
1681 playlist
->pending_control_sync
= true;
1688 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR
));
1695 * Update control data with new command. Depending on the command, it may be
1696 * cached or flushed to disk.
1698 static int update_control(struct playlist_info
* playlist
,
1699 enum playlist_command command
, int i1
, int i2
,
1700 const char* s1
, const char* s2
, void* data
)
1703 struct playlist_control_cache
* cache
;
1706 mutex_lock(&playlist
->control_mutex
);
1708 cache
= &(playlist
->control_cache
[playlist
->num_cached
++]);
1710 cache
->command
= command
;
1719 case PLAYLIST_COMMAND_PLAYLIST
:
1720 case PLAYLIST_COMMAND_ADD
:
1721 case PLAYLIST_COMMAND_QUEUE
:
1722 #ifndef HAVE_DIRCACHE
1723 case PLAYLIST_COMMAND_DELETE
:
1724 case PLAYLIST_COMMAND_RESET
:
1728 case PLAYLIST_COMMAND_SHUFFLE
:
1729 case PLAYLIST_COMMAND_UNSHUFFLE
:
1731 /* only flush when needed */
1735 if (flush
|| playlist
->num_cached
== PLAYLIST_MAX_CACHE
)
1736 result
= flush_cached_control(playlist
);
1738 mutex_unlock(&playlist
->control_mutex
);
1744 * sync control file to disk
1746 static void sync_control(struct playlist_info
* playlist
, bool force
)
1748 #ifdef HAVE_DIRCACHE
1749 if (playlist
->started
&& force
)
1753 if (playlist
->started
)
1756 if (playlist
->pending_control_sync
)
1758 mutex_lock(&playlist
->control_mutex
);
1759 fsync(playlist
->control_fd
);
1760 playlist
->pending_control_sync
= false;
1761 mutex_unlock(&playlist
->control_mutex
);
1767 * Rotate indices such that first_index is index 0
1769 static int rotate_index(const struct playlist_info
* playlist
, int index
)
1771 index
-= playlist
->first_index
;
1773 index
+= playlist
->amount
;
1779 * Initialize playlist entries at startup
1781 void playlist_init(void)
1783 struct playlist_info
* playlist
= ¤t_playlist
;
1785 playlist
->current
= true;
1786 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
1787 "%s", PLAYLIST_CONTROL_FILE
);
1789 playlist
->control_fd
= -1;
1790 playlist
->max_playlist_size
= global_settings
.max_files_in_playlist
;
1791 playlist
->indices
= buffer_alloc(
1792 playlist
->max_playlist_size
* sizeof(int));
1793 playlist
->buffer_size
=
1794 AVERAGE_FILENAME_LENGTH
* global_settings
.max_files_in_dir
;
1795 playlist
->buffer
= buffer_alloc(playlist
->buffer_size
);
1796 mutex_init(&playlist
->control_mutex
);
1797 empty_playlist(playlist
, true);
1799 #ifdef HAVE_DIRCACHE
1800 playlist
->filenames
= buffer_alloc(
1801 playlist
->max_playlist_size
* sizeof(int));
1802 memset(playlist
->filenames
, 0,
1803 playlist
->max_playlist_size
* sizeof(int));
1804 create_thread(playlist_thread
, playlist_stack
, sizeof(playlist_stack
),
1805 playlist_thread_name
IF_PRIO(, PRIORITY_BACKGROUND
));
1806 queue_init(&playlist_queue
, true);
1811 * Clean playlist at shutdown
1813 void playlist_shutdown(void)
1815 struct playlist_info
* playlist
= ¤t_playlist
;
1817 if (playlist
->control_fd
>= 0)
1819 mutex_lock(&playlist
->control_mutex
);
1821 if (playlist
->num_cached
> 0)
1822 flush_cached_control(playlist
);
1824 close(playlist
->control_fd
);
1826 mutex_unlock(&playlist
->control_mutex
);
1831 * Create new playlist
1833 int playlist_create(const char *dir
, const char *file
)
1835 struct playlist_info
* playlist
= ¤t_playlist
;
1837 new_playlist(playlist
, dir
, file
);
1840 /* load the playlist file */
1841 add_indices_to_playlist(playlist
, NULL
, 0);
1846 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1849 * Restore the playlist state based on control file commands. Called to
1850 * resume playback after shutdown.
1852 int playlist_resume(void)
1854 struct playlist_info
* playlist
= ¤t_playlist
;
1859 int control_file_size
= 0;
1863 /* use mp3 buffer for maximum load speed */
1864 #if CONFIG_CODEC != SWCODEC
1865 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1866 buflen
= (audiobufend
- audiobuf
);
1867 buffer
= (char *)audiobuf
;
1869 buflen
= (audiobufend
- audiobuf
- talk_get_bufsize());
1870 buffer
= (char *)&audiobuf
[talk_get_bufsize()];
1873 empty_playlist(playlist
, true);
1875 gui_syncsplash(0, true, str(LANG_WAIT
));
1876 playlist
->control_fd
= open(playlist
->control_filename
, O_RDWR
);
1877 if (playlist
->control_fd
< 0)
1879 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1882 playlist
->control_created
= true;
1884 control_file_size
= filesize(playlist
->control_fd
);
1885 if (control_file_size
<= 0)
1887 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1891 /* read a small amount first to get the header */
1892 nread
= read(playlist
->control_fd
, buffer
,
1893 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
1896 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
1900 playlist
->started
= true;
1906 enum playlist_command current_command
= PLAYLIST_COMMAND_COMMENT
;
1907 int last_newline
= 0;
1909 bool newline
= true;
1910 bool exit_loop
= false;
1915 unsigned long last_tick
= current_tick
;
1917 for(count
=0; count
<nread
&& !exit_loop
; count
++,p
++)
1919 /* So a splash while we are loading. */
1920 if (current_tick
- last_tick
> HZ
/4)
1922 gui_syncsplash(0, true, str(LANG_LOADING_PERCENT
),
1923 (total_read
+count
)*100/control_file_size
,
1924 #if CONFIG_KEYPAD == PLAYER_PAD
1925 str(LANG_STOP_ABORT
)
1930 if (action_userabort(TIMEOUT_NOBLOCK
))
1933 * Not sure how to implement this, somebody more familiar
1934 * with the code, please fix this. */
1936 last_tick
= current_tick
;
1939 /* Are we on a new line? */
1940 if((*p
== '\n') || (*p
== '\r'))
1944 /* save last_newline in case we need to load more data */
1945 last_newline
= count
;
1947 switch (current_command
)
1949 case PLAYLIST_COMMAND_PLAYLIST
:
1951 /* str1=version str2=dir str3=file */
1967 version
= atoi(str1
);
1969 if (version
!= PLAYLIST_CONTROL_FILE_VERSION
)
1972 update_playlist_filename(playlist
, str2
, str3
);
1974 if (str3
[0] != '\0')
1976 /* NOTE: add_indices_to_playlist() overwrites the
1977 audiobuf so we need to reload control file
1979 add_indices_to_playlist(playlist
, NULL
, 0);
1981 else if (str2
[0] != '\0')
1983 playlist
->in_ram
= true;
1984 resume_directory(str2
);
1987 /* load the rest of the data */
1993 case PLAYLIST_COMMAND_ADD
:
1994 case PLAYLIST_COMMAND_QUEUE
:
1996 /* str1=position str2=last_position str3=file */
1997 int position
, last_position
;
2000 if (!str1
|| !str2
|| !str3
)
2007 position
= atoi(str1
);
2008 last_position
= atoi(str2
);
2010 queue
= (current_command
== PLAYLIST_COMMAND_ADD
)?
2013 /* seek position is based on str3's position in
2015 if (add_track_to_playlist(playlist
, str3
, position
,
2016 queue
, total_read
+(str3
-buffer
)) < 0)
2019 playlist
->last_insert_pos
= last_position
;
2023 case PLAYLIST_COMMAND_DELETE
:
2035 position
= atoi(str1
);
2037 if (remove_track_from_playlist(playlist
, position
,
2043 case PLAYLIST_COMMAND_SHUFFLE
:
2045 /* str1=seed str2=first_index */
2057 /* Always sort list before shuffling */
2058 sort_playlist(playlist
, false, false);
2062 playlist
->first_index
= atoi(str2
);
2064 if (randomise_playlist(playlist
, seed
, false,
2071 case PLAYLIST_COMMAND_UNSHUFFLE
:
2073 /* str1=first_index */
2081 playlist
->first_index
= atoi(str1
);
2083 if (sort_playlist(playlist
, false, false) < 0)
2089 case PLAYLIST_COMMAND_RESET
:
2091 playlist
->last_insert_pos
= -1;
2094 case PLAYLIST_COMMAND_COMMENT
:
2101 /* to ignore any extra newlines */
2102 current_command
= PLAYLIST_COMMAND_COMMENT
;
2108 /* first non-comment line must always specify playlist */
2109 if (first
&& *p
!= 'P' && *p
!= '#')
2119 /* playlist can only be specified once */
2127 current_command
= PLAYLIST_COMMAND_PLAYLIST
;
2130 current_command
= PLAYLIST_COMMAND_ADD
;
2133 current_command
= PLAYLIST_COMMAND_QUEUE
;
2136 current_command
= PLAYLIST_COMMAND_DELETE
;
2139 current_command
= PLAYLIST_COMMAND_SHUFFLE
;
2142 current_command
= PLAYLIST_COMMAND_UNSHUFFLE
;
2145 current_command
= PLAYLIST_COMMAND_RESET
;
2148 current_command
= PLAYLIST_COMMAND_COMMENT
;
2161 else if(current_command
!= PLAYLIST_COMMAND_COMMENT
)
2163 /* all control file strings are separated with a colon.
2164 Replace the colon with 0 to get proper strings that can be
2165 used by commands above */
2171 if ((count
+1) < nread
)
2185 /* allow last string to contain colons */
2196 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2200 if (!newline
|| (exit_loop
&& count
<nread
))
2202 if ((total_read
+ count
) >= control_file_size
)
2204 /* no newline at end of control file */
2205 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_INVALID
));
2209 /* We didn't end on a newline or we exited loop prematurely.
2210 Either way, re-read the remainder. */
2211 count
= last_newline
;
2212 lseek(playlist
->control_fd
, total_read
+count
, SEEK_SET
);
2215 total_read
+= count
;
2218 /* still looking for header */
2219 nread
= read(playlist
->control_fd
, buffer
,
2220 PLAYLIST_COMMAND_SIZE
<buflen
?PLAYLIST_COMMAND_SIZE
:buflen
);
2222 nread
= read(playlist
->control_fd
, buffer
, buflen
);
2224 /* Terminate on EOF */
2227 if (global_settings
.resume_seed
>= 0)
2229 /* Apply shuffle command saved in settings */
2230 if (global_settings
.resume_seed
== 0)
2231 sort_playlist(playlist
, false, true);
2235 sort_playlist(playlist
, false, false);
2237 randomise_playlist(playlist
, global_settings
.resume_seed
,
2242 playlist
->first_index
= global_settings
.resume_first_index
;
2247 #ifdef HAVE_DIRCACHE
2248 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2255 * Add track to in_ram playlist. Used when playing directories.
2257 int playlist_add(const char *filename
)
2259 struct playlist_info
* playlist
= ¤t_playlist
;
2260 int len
= strlen(filename
);
2262 if((len
+1 > playlist
->buffer_size
- playlist
->buffer_end_pos
) ||
2263 (playlist
->amount
>= playlist
->max_playlist_size
))
2265 display_buffer_full();
2269 playlist
->indices
[playlist
->amount
] = playlist
->buffer_end_pos
;
2270 #ifdef HAVE_DIRCACHE
2271 playlist
->filenames
[playlist
->amount
] = NULL
;
2275 strcpy(&playlist
->buffer
[playlist
->buffer_end_pos
], filename
);
2276 playlist
->buffer_end_pos
+= len
;
2277 playlist
->buffer
[playlist
->buffer_end_pos
++] = '\0';
2282 /* shuffle newly created playlist using random seed. */
2283 int playlist_shuffle(int random_seed
, int start_index
)
2285 struct playlist_info
* playlist
= ¤t_playlist
;
2287 unsigned int seek_pos
= 0;
2288 bool start_current
= false;
2290 if (start_index
>= 0 && global_settings
.play_selected
)
2292 /* store the seek position before the shuffle */
2293 seek_pos
= playlist
->indices
[start_index
];
2294 playlist
->index
= global_settings
.resume_first_index
=
2295 playlist
->first_index
= start_index
;
2296 start_current
= true;
2299 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE
));
2301 randomise_playlist(playlist
, random_seed
, start_current
, true);
2303 return playlist
->index
;
2306 /* start playing current playlist at specified index/offset */
2307 int playlist_start(int start_index
, int offset
)
2309 struct playlist_info
* playlist
= ¤t_playlist
;
2311 playlist
->index
= start_index
;
2313 #if CONFIG_CODEC != SWCODEC
2314 talk_buffer_steal(); /* will use the mp3 buffer */
2317 playlist
->started
= true;
2318 sync_control(playlist
, false);
2324 /* Returns false if 'steps' is out of bounds, else true */
2325 bool playlist_check(int steps
)
2327 struct playlist_info
* playlist
= ¤t_playlist
;
2329 /* always allow folder navigation */
2330 if (global_settings
.next_folder
&& playlist
->in_ram
)
2333 int index
= get_next_index(playlist
, steps
, -1);
2335 if (index
< 0 && steps
>= 0 && global_settings
.repeat_mode
== REPEAT_SHUFFLE
)
2336 index
= get_next_index(playlist
, steps
, REPEAT_ALL
);
2338 return (index
>= 0);
2341 /* get trackname of track that is "steps" away from current playing track.
2342 NULL is used to identify end of playlist */
2343 char* playlist_peek(int steps
)
2345 struct playlist_info
* playlist
= ¤t_playlist
;
2352 index
= get_next_index(playlist
, steps
, -1);
2356 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
2357 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
2359 if (get_filename(playlist
, index
, seek
, control_file
, now_playing
,
2363 temp_ptr
= now_playing
;
2365 if (!playlist
->in_ram
|| control_file
)
2367 /* remove bogus dirs from beginning of path
2368 (workaround for buggy playlist creation tools) */
2371 #ifdef HAVE_DIRCACHE
2372 if (dircache_is_enabled())
2374 if (dircache_get_entry_ptr(temp_ptr
))
2380 fd
= open(temp_ptr
, O_RDONLY
);
2388 temp_ptr
= strchr(temp_ptr
+1, '/');
2393 /* Even though this is an invalid file, we still need to pass a
2394 file name to the caller because NULL is used to indicate end
2404 * Update indices as track has changed
2406 int playlist_next(int steps
)
2408 struct playlist_info
* playlist
= ¤t_playlist
;
2412 #if (AB_REPEAT_ENABLE == 1)
2413 && (global_settings
.repeat_mode
!= REPEAT_AB
)
2415 && (global_settings
.repeat_mode
!= REPEAT_ONE
) )
2419 /* We need to delete all the queued songs */
2420 for (i
=0, j
=steps
; i
<j
; i
++)
2422 index
= get_next_index(playlist
, i
, -1);
2424 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
2426 remove_track_from_playlist(playlist
, index
, true);
2427 steps
--; /* one less track */
2432 index
= get_next_index(playlist
, steps
, -1);
2436 /* end of playlist... or is it */
2437 if (global_settings
.repeat_mode
== REPEAT_SHUFFLE
&&
2438 global_settings
.playlist_shuffle
&&
2439 playlist
->amount
> 1)
2441 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2442 playlist
->first_index
= global_settings
.resume_first_index
= 0;
2443 sort_playlist(playlist
, false, false);
2444 randomise_playlist(playlist
, current_tick
, false, true);
2445 #if CONFIG_CODEC != SWCODEC
2446 playlist_start(0, 0);
2448 playlist
->index
= 0;
2451 else if (playlist
->in_ram
&& global_settings
.next_folder
)
2453 char dir
[MAX_PATH
+1];
2455 changing_dir
= true;
2458 if (!get_next_directory(dir
))
2460 /* start playing next directory */
2461 if (playlist_create(dir
, NULL
) != -1)
2463 ft_build_playlist(tree_get_context(), 0);
2464 if (global_settings
.playlist_shuffle
)
2465 playlist_shuffle(current_tick
, -1);
2466 #if CONFIG_CODEC != SWCODEC
2467 playlist_start(0, 0);
2469 playlist
->index
= index
= 0;
2475 if (!get_previous_directory(dir
))
2477 /* start playing previous directory */
2478 if (playlist_create(dir
, NULL
) != -1)
2480 ft_build_playlist(tree_get_context(), 0);
2481 if (global_settings
.playlist_shuffle
)
2482 playlist_shuffle(current_tick
, -1);
2483 #if CONFIG_CODEC != SWCODEC
2484 playlist_start(current_playlist
.amount
-1, 0);
2486 playlist
->index
= index
= current_playlist
.amount
- 1;
2490 changing_dir
= false;
2496 playlist
->index
= index
;
2498 if (playlist
->last_insert_pos
>= 0 && steps
> 0)
2500 /* check to see if we've gone beyond the last inserted track */
2501 int cur
= rotate_index(playlist
, index
);
2502 int last_pos
= rotate_index(playlist
, playlist
->last_insert_pos
);
2506 /* reset last inserted track */
2507 playlist
->last_insert_pos
= -1;
2509 if (playlist
->control_fd
>= 0)
2511 int result
= update_control(playlist
, PLAYLIST_COMMAND_RESET
,
2512 -1, -1, NULL
, NULL
, NULL
);
2517 sync_control(playlist
, false);
2525 /* try playing next or previous folder */
2526 bool playlist_next_dir(int direction
)
2528 char dir
[MAX_PATH
+1];
2532 /* not to mess up real playlists */
2533 if(!current_playlist
.in_ram
)
2539 changing_dir
= true;
2541 res
= get_next_directory(dir
);
2543 res
= get_previous_directory(dir
);
2546 if (playlist_create(dir
, NULL
) != -1)
2548 ft_build_playlist(tree_get_context(), 0);
2549 if (global_settings
.playlist_shuffle
)
2550 playlist_shuffle(current_tick
, -1);
2551 #if (CONFIG_CODEC != SWCODEC)
2552 playlist_start(0,0);
2562 changing_dir
= false;
2567 /* Get resume info for current playing song. If return value is -1 then
2568 settings shouldn't be saved. */
2569 int playlist_get_resume_info(int *resume_index
)
2571 struct playlist_info
* playlist
= ¤t_playlist
;
2573 *resume_index
= playlist
->index
;
2578 /* Update resume info for current playing song. Returns -1 on error. */
2579 int playlist_update_resume_info(const struct mp3entry
* id3
)
2581 struct playlist_info
* playlist
= ¤t_playlist
;
2585 if (global_settings
.resume_index
!= playlist
->index
||
2586 global_settings
.resume_offset
!= id3
->offset
)
2588 global_settings
.resume_index
= playlist
->index
;
2589 global_settings
.resume_offset
= id3
->offset
;
2595 global_settings
.resume_index
= -1;
2596 global_settings
.resume_offset
= -1;
2603 /* Returns index of current playing track for display purposes. This value
2604 should not be used for resume purposes as it doesn't represent the actual
2605 index into the playlist */
2606 int playlist_get_display_index(void)
2608 struct playlist_info
* playlist
= ¤t_playlist
;
2610 /* first_index should always be index 0 for display purposes */
2611 int index
= rotate_index(playlist
, playlist
->index
);
2616 /* returns number of tracks in current playlist */
2617 int playlist_amount(void)
2619 return playlist_amount_ex(NULL
);
2623 * Create a new playlist If playlist is not NULL then we're loading a
2624 * playlist off disk for viewing/editing. The index_buffer is used to store
2625 * playlist indices (required for and only used if !current playlist). The
2626 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2628 int playlist_create_ex(struct playlist_info
* playlist
,
2629 const char* dir
, const char* file
,
2630 void* index_buffer
, int index_buffer_size
,
2631 void* temp_buffer
, int temp_buffer_size
)
2634 playlist
= ¤t_playlist
;
2637 /* Initialize playlist structure */
2638 int r
= rand() % 10;
2639 playlist
->current
= false;
2641 /* Use random name for control file */
2642 snprintf(playlist
->control_filename
, sizeof(playlist
->control_filename
),
2643 "%s.%d", PLAYLIST_CONTROL_FILE
, r
);
2645 playlist
->control_fd
= -1;
2649 int num_indices
= index_buffer_size
/ sizeof(int);
2651 #ifdef HAVE_DIRCACHE
2654 if (num_indices
> global_settings
.max_files_in_playlist
)
2655 num_indices
= global_settings
.max_files_in_playlist
;
2657 playlist
->max_playlist_size
= num_indices
;
2658 playlist
->indices
= index_buffer
;
2659 #ifdef HAVE_DIRCACHE
2660 playlist
->filenames
= (const struct dircache_entry
**)
2661 &playlist
->indices
[num_indices
];
2666 playlist
->max_playlist_size
= current_playlist
.max_playlist_size
;
2667 playlist
->indices
= current_playlist
.indices
;
2668 #ifdef HAVE_DIRCACHE
2669 playlist
->filenames
= current_playlist
.filenames
;
2673 playlist
->buffer_size
= 0;
2674 playlist
->buffer
= NULL
;
2675 mutex_init(&playlist
->control_mutex
);
2678 new_playlist(playlist
, dir
, file
);
2681 /* load the playlist file */
2682 add_indices_to_playlist(playlist
, temp_buffer
, temp_buffer_size
);
2688 * Set the specified playlist as the current.
2689 * NOTE: You will get undefined behaviour if something is already playing so
2690 * remember to stop before calling this. Also, this call will
2691 * effectively close your playlist, making it unusable.
2693 int playlist_set_current(struct playlist_info
* playlist
)
2695 if (!playlist
|| (check_control(playlist
) < 0))
2698 empty_playlist(¤t_playlist
, false);
2700 strncpy(current_playlist
.filename
, playlist
->filename
,
2701 sizeof(current_playlist
.filename
));
2703 current_playlist
.fd
= playlist
->fd
;
2705 close(playlist
->control_fd
);
2706 close(current_playlist
.control_fd
);
2707 remove(current_playlist
.control_filename
);
2708 if (rename(playlist
->control_filename
,
2709 current_playlist
.control_filename
) < 0)
2711 current_playlist
.control_fd
= open(current_playlist
.control_filename
,
2713 if (current_playlist
.control_fd
< 0)
2715 current_playlist
.control_created
= true;
2717 current_playlist
.dirlen
= playlist
->dirlen
;
2719 if (playlist
->indices
&& playlist
->indices
!= current_playlist
.indices
)
2721 memcpy(current_playlist
.indices
, playlist
->indices
,
2722 playlist
->max_playlist_size
*sizeof(int));
2723 #ifdef HAVE_DIRCACHE
2724 memcpy(current_playlist
.filenames
, playlist
->filenames
,
2725 playlist
->max_playlist_size
*sizeof(int));
2729 current_playlist
.first_index
= playlist
->first_index
;
2730 current_playlist
.amount
= playlist
->amount
;
2731 current_playlist
.last_insert_pos
= playlist
->last_insert_pos
;
2732 current_playlist
.seed
= playlist
->seed
;
2733 current_playlist
.shuffle_modified
= playlist
->shuffle_modified
;
2734 current_playlist
.deleted
= playlist
->deleted
;
2735 current_playlist
.num_inserted_tracks
= playlist
->num_inserted_tracks
;
2737 memcpy(current_playlist
.control_cache
, playlist
->control_cache
,
2738 sizeof(current_playlist
.control_cache
));
2739 current_playlist
.num_cached
= playlist
->num_cached
;
2740 current_playlist
.pending_control_sync
= playlist
->pending_control_sync
;
2746 * Close files and delete control file for non-current playlist.
2748 void playlist_close(struct playlist_info
* playlist
)
2753 if (playlist
->fd
>= 0)
2754 close(playlist
->fd
);
2756 if (playlist
->control_fd
>= 0)
2757 close(playlist
->control_fd
);
2759 if (playlist
->control_created
)
2760 remove(playlist
->control_filename
);
2763 void playlist_sync(struct playlist_info
* playlist
)
2766 playlist
= ¤t_playlist
;
2768 sync_control(playlist
, false);
2769 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2770 audio_flush_and_reload_tracks();
2772 #ifdef HAVE_DIRCACHE
2773 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2778 * Insert track into playlist at specified position (or one of the special
2779 * positions). Returns position where track was inserted or -1 if error.
2781 int playlist_insert_track(struct playlist_info
* playlist
, const char *filename
,
2782 int position
, bool queue
, bool sync
)
2787 playlist
= ¤t_playlist
;
2789 if (check_control(playlist
) < 0)
2791 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2795 result
= add_track_to_playlist(playlist
, filename
, position
, queue
, -1);
2797 /* Check if we want manually sync later. For example when adding
2798 * bunch of files from tagcache, syncing after every file wouldn't be
2799 * a good thing to do. */
2800 if (sync
&& result
>= 0)
2801 playlist_sync(playlist
);
2807 * Insert all tracks from specified directory into playlist.
2809 int playlist_insert_directory(struct playlist_info
* playlist
,
2810 const char *dirname
, int position
, bool queue
,
2814 unsigned char *count_str
;
2815 struct directory_search_context context
;
2818 playlist
= ¤t_playlist
;
2820 if (check_control(playlist
) < 0)
2822 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2827 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2829 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2831 display_playlist_count(0, count_str
);
2833 context
.playlist
= playlist
;
2834 context
.position
= position
;
2835 context
.queue
= queue
;
2838 cpu_boost_id(true, CPUBOOSTID_PLAYLIST
);
2840 result
= playlist_directory_tracksearch(dirname
, recurse
,
2841 directory_search_callback
, &context
);
2843 sync_control(playlist
, false);
2845 cpu_boost_id(false, CPUBOOSTID_PLAYLIST
);
2847 display_playlist_count(context
.count
, count_str
);
2849 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2850 audio_flush_and_reload_tracks();
2852 #ifdef HAVE_DIRCACHE
2853 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2860 * Insert all tracks from specified playlist into dynamic playlist.
2862 int playlist_insert_playlist(struct playlist_info
* playlist
, char *filename
,
2863 int position
, bool queue
)
2869 unsigned char *count_str
;
2870 char temp_buf
[MAX_PATH
+1];
2871 char trackname
[MAX_PATH
+1];
2876 playlist
= ¤t_playlist
;
2878 if (check_control(playlist
) < 0)
2880 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2884 fd
= open(filename
, O_RDONLY
);
2887 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
2891 /* we need the directory name for formatting purposes */
2894 temp_ptr
= strrchr(filename
+1,'/');
2901 count_str
= str(LANG_PLAYLIST_QUEUE_COUNT
);
2903 count_str
= str(LANG_PLAYLIST_INSERT_COUNT
);
2905 display_playlist_count(count
, count_str
);
2907 cpu_boost_id(true, CPUBOOSTID_PLAYLIST
);
2909 while ((max
= read_line(fd
, temp_buf
, sizeof(temp_buf
))) > 0)
2912 if (action_userabort(TIMEOUT_NOBLOCK
))
2915 if (temp_buf
[0] != '#' && temp_buf
[0] != '\0')
2919 /* we need to format so that relative paths are correctly
2921 if (format_track_path(trackname
, temp_buf
, sizeof(trackname
), max
,
2928 insert_pos
= add_track_to_playlist(playlist
, trackname
, position
,
2937 /* Make sure tracks are inserted in correct order if user
2938 requests INSERT_FIRST */
2939 if (position
== PLAYLIST_INSERT_FIRST
|| position
>= 0)
2940 position
= insert_pos
+ 1;
2944 if ((count
%PLAYLIST_DISPLAY_COUNT
) == 0)
2946 display_playlist_count(count
, count_str
);
2948 if (count
== PLAYLIST_DISPLAY_COUNT
&&
2949 (audio_status() & AUDIO_STATUS_PLAY
) &&
2951 audio_flush_and_reload_tracks();
2955 /* let the other threads work */
2964 sync_control(playlist
, false);
2966 cpu_boost_id(false, CPUBOOSTID_PLAYLIST
);
2968 display_playlist_count(count
, count_str
);
2970 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
2971 audio_flush_and_reload_tracks();
2973 #ifdef HAVE_DIRCACHE
2974 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
2981 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2982 * we want to delete the current playing track.
2984 int playlist_delete(struct playlist_info
* playlist
, int index
)
2989 playlist
= ¤t_playlist
;
2991 if (check_control(playlist
) < 0)
2993 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
2997 if (index
== PLAYLIST_DELETE_CURRENT
)
2998 index
= playlist
->index
;
3000 result
= remove_track_from_playlist(playlist
, index
, true);
3002 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3004 audio_flush_and_reload_tracks();
3010 * Move track at index to new_index. Tracks between the two are shifted
3011 * appropriately. Returns 0 on success and -1 on failure.
3013 int playlist_move(struct playlist_info
* playlist
, int index
, int new_index
)
3019 bool current
= false;
3021 char filename
[MAX_PATH
];
3024 playlist
= ¤t_playlist
;
3026 if (check_control(playlist
) < 0)
3028 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR
));
3032 if (index
== new_index
)
3035 if (index
== playlist
->index
)
3036 /* Moving the current track */
3039 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3040 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3041 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3043 if (get_filename(playlist
, index
, seek
, control_file
, filename
,
3044 sizeof(filename
)) < 0)
3047 /* Delete track from original position */
3048 result
= remove_track_from_playlist(playlist
, index
, true);
3052 /* We want to insert the track at the position that was specified by
3053 new_index. This may be different then new_index because of the
3054 shifting that occurred after the delete */
3055 r
= rotate_index(playlist
, new_index
);
3059 new_index
= PLAYLIST_PREPEND
;
3060 else if (r
== playlist
->amount
)
3062 new_index
= PLAYLIST_INSERT_LAST
;
3064 /* Calculate index of desired position */
3065 new_index
= (r
+playlist
->first_index
)%playlist
->amount
;
3067 result
= add_track_to_playlist(playlist
, filename
, new_index
, queue
,
3074 /* Moved the current track */
3077 case PLAYLIST_PREPEND
:
3078 playlist
->index
= playlist
->first_index
;
3080 case PLAYLIST_INSERT_LAST
:
3081 playlist
->index
= playlist
->first_index
- 1;
3082 if (playlist
->index
< 0)
3083 playlist
->index
+= playlist
->amount
;
3086 playlist
->index
= new_index
;
3091 if ((audio_status() & AUDIO_STATUS_PLAY
) && playlist
->started
)
3092 audio_flush_and_reload_tracks();
3096 #ifdef HAVE_DIRCACHE
3097 queue_post(&playlist_queue
, PLAYLIST_LOAD_POINTERS
, 0);
3103 /* shuffle currently playing playlist */
3104 int playlist_randomise(struct playlist_info
* playlist
, unsigned int seed
,
3110 playlist
= ¤t_playlist
;
3112 check_control(playlist
);
3114 result
= randomise_playlist(playlist
, seed
, start_current
, true);
3116 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3118 audio_flush_and_reload_tracks();
3123 /* sort currently playing playlist */
3124 int playlist_sort(struct playlist_info
* playlist
, bool start_current
)
3129 playlist
= ¤t_playlist
;
3131 check_control(playlist
);
3133 result
= sort_playlist(playlist
, start_current
, true);
3135 if (result
!= -1 && (audio_status() & AUDIO_STATUS_PLAY
) &&
3137 audio_flush_and_reload_tracks();
3142 /* returns true if playlist has been modified */
3143 bool playlist_modified(const struct playlist_info
* playlist
)
3146 playlist
= ¤t_playlist
;
3148 if (playlist
->shuffle_modified
||
3149 playlist
->deleted
||
3150 playlist
->num_inserted_tracks
> 0)
3156 /* returns index of first track in playlist */
3157 int playlist_get_first_index(const struct playlist_info
* playlist
)
3160 playlist
= ¤t_playlist
;
3162 return playlist
->first_index
;
3165 /* returns shuffle seed of playlist */
3166 int playlist_get_seed(const struct playlist_info
* playlist
)
3169 playlist
= ¤t_playlist
;
3171 return playlist
->seed
;
3174 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3175 int playlist_amount_ex(const struct playlist_info
* playlist
)
3178 playlist
= ¤t_playlist
;
3180 return playlist
->amount
;
3183 /* returns full path of playlist (minus extension) */
3184 char *playlist_name(const struct playlist_info
* playlist
, char *buf
,
3190 playlist
= ¤t_playlist
;
3192 snprintf(buf
, buf_size
, "%s", playlist
->filename
+playlist
->dirlen
);
3197 /* Remove extension */
3198 sep
= strrchr(buf
, '.');
3205 /* returns the playlist filename */
3206 char *playlist_get_name(const struct playlist_info
* playlist
, char *buf
,
3210 playlist
= ¤t_playlist
;
3212 snprintf(buf
, buf_size
, "%s", playlist
->filename
);
3220 /* Fills info structure with information about track at specified index.
3221 Returns 0 on success and -1 on failure */
3222 int playlist_get_track_info(struct playlist_info
* playlist
, int index
,
3223 struct playlist_track_info
* info
)
3229 playlist
= ¤t_playlist
;
3231 if (index
< 0 || index
>= playlist
->amount
)
3234 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3235 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3237 if (get_filename(playlist
, index
, seek
, control_file
, info
->filename
,
3238 sizeof(info
->filename
)) < 0)
3245 if (playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
)
3246 info
->attr
|= PLAYLIST_ATTR_QUEUED
;
3248 info
->attr
|= PLAYLIST_ATTR_INSERTED
;
3252 if (playlist
->indices
[index
] & PLAYLIST_SKIPPED
)
3253 info
->attr
|= PLAYLIST_ATTR_SKIPPED
;
3255 info
->index
= index
;
3256 info
->display_index
= rotate_index(playlist
, index
) + 1;
3261 /* save the current dynamic playlist to specified file */
3262 int playlist_save(struct playlist_info
* playlist
, char *filename
)
3267 char path
[MAX_PATH
+1];
3268 char tmp_buf
[MAX_PATH
+1];
3270 bool overwrite_current
= false;
3271 int* index_buf
= NULL
;
3274 playlist
= ¤t_playlist
;
3276 if (playlist
->amount
<= 0)
3279 /* use current working directory as base for pathname */
3280 if (format_track_path(path
, filename
, sizeof(tmp_buf
),
3281 strlen(filename
)+1, getcwd(NULL
, -1)) < 0)
3284 if (!strncmp(playlist
->filename
, path
, strlen(path
)))
3286 /* Attempting to overwrite current playlist file.*/
3288 if (playlist
->buffer_size
< (int)(playlist
->amount
* sizeof(int)))
3290 /* not enough buffer space to store updated indices */
3291 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3295 /* in_ram buffer is unused for m3u files so we'll use for storing
3297 index_buf
= (int*)playlist
->buffer
;
3299 /* use temporary pathname */
3300 snprintf(path
, sizeof(path
), "%s_temp", playlist
->filename
);
3301 overwrite_current
= true;
3304 fd
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
);
3307 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3311 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3313 cpu_boost_id(true, CPUBOOSTID_PLAYLIST
);
3315 index
= playlist
->first_index
;
3316 for (i
=0; i
<playlist
->amount
; i
++)
3323 if (action_userabort(TIMEOUT_NOBLOCK
))
3329 control_file
= playlist
->indices
[index
] & PLAYLIST_INSERT_TYPE_MASK
;
3330 queue
= playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
;
3331 seek
= playlist
->indices
[index
] & PLAYLIST_SEEK_MASK
;
3333 /* Don't save queued files */
3336 if (get_filename(playlist
, index
, seek
, control_file
, tmp_buf
,
3343 if (overwrite_current
)
3344 index_buf
[count
] = lseek(fd
, 0, SEEK_CUR
);
3346 if (fdprintf(fd
, "%s\n", tmp_buf
) < 0)
3348 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_ACCESS_ERROR
));
3355 if ((count
% PLAYLIST_DISPLAY_COUNT
) == 0)
3356 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3361 index
= (index
+1)%playlist
->amount
;
3364 display_playlist_count(count
, str(LANG_PLAYLIST_SAVE_COUNT
));
3368 if (overwrite_current
&& result
>= 0)
3372 mutex_lock(&playlist
->control_mutex
);
3374 /* Replace the current playlist with the new one and update indices */
3375 close(playlist
->fd
);
3376 if (remove(playlist
->filename
) >= 0)
3378 if (rename(path
, playlist
->filename
) >= 0)
3380 playlist
->fd
= open(playlist
->filename
, O_RDONLY
);
3381 if (playlist
->fd
>= 0)
3383 index
= playlist
->first_index
;
3384 for (i
=0, count
=0; i
<playlist
->amount
; i
++)
3386 if (!(playlist
->indices
[index
] & PLAYLIST_QUEUE_MASK
))
3388 playlist
->indices
[index
] = index_buf
[count
];
3391 index
= (index
+1)%playlist
->amount
;
3394 /* we need to recreate control because inserted tracks are
3395 now part of the playlist and shuffle has been
3397 result
= recreate_control(playlist
);
3402 mutex_unlock(&playlist
->control_mutex
);
3406 cpu_boost_id(false, CPUBOOSTID_PLAYLIST
);
3412 * Search specified directory for tracks and notify via callback. May be
3413 * called recursively.
3415 int playlist_directory_tracksearch(const char* dirname
, bool recurse
,
3416 int (*callback
)(char*, void*),
3419 char buf
[MAX_PATH
+1];
3423 struct entry
*files
;
3424 struct tree_context
* tc
= tree_get_context();
3425 int old_dirfilter
= *(tc
->dirfilter
);
3430 /* use the tree browser dircache to load files */
3431 *(tc
->dirfilter
) = SHOW_ALL
;
3433 if (ft_load(tc
, dirname
) < 0)
3435 gui_syncsplash(HZ
*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
));
3436 *(tc
->dirfilter
) = old_dirfilter
;
3440 files
= (struct entry
*) tc
->dircache
;
3441 num_files
= tc
->filesindir
;
3443 /* we've overwritten the dircache so tree browser will need to be
3447 for (i
=0; i
<num_files
; i
++)
3450 if (action_userabort(TIMEOUT_NOBLOCK
))
3456 if (files
[i
].attr
& ATTR_DIRECTORY
)
3460 /* recursively add directories */
3461 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3462 result
= playlist_directory_tracksearch(buf
, recurse
,
3467 /* we now need to reload our current directory */
3468 if(ft_load(tc
, dirname
) < 0)
3474 files
= (struct entry
*) tc
->dircache
;
3475 num_files
= tc
->filesindir
;
3485 else if ((files
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
3487 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, files
[i
].name
);
3489 if (callback(buf
, context
) != 0)
3495 /* let the other threads work */
3500 /* restore dirfilter */
3501 *(tc
->dirfilter
) = old_dirfilter
;