Let qmake generate an install Makefile target to install the binary. Doesn't handle...
[Rockbox.git] / apps / playlist.c
blob30fd0f11e966a47280b844756012010d073c14c7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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
41 occurred.
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
59 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.
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <ctype.h>
72 #include "playlist.h"
73 #include "ata_idle_notify.h"
74 #include "file.h"
75 #include "action.h"
76 #include "dir.h"
77 #include "sprintf.h"
78 #include "debug.h"
79 #include "audio.h"
80 #include "lcd.h"
81 #include "kernel.h"
82 #include "settings.h"
83 #include "status.h"
84 #include "applimits.h"
85 #include "screens.h"
86 #include "buffer.h"
87 #include "misc.h"
88 #include "button.h"
89 #include "filetree.h"
90 #include "abrepeat.h"
91 #include "thread.h"
92 #include "usb.h"
93 #include "filetypes.h"
94 #ifdef HAVE_LCD_BITMAP
95 #include "icons.h"
96 #endif
98 #include "lang.h"
99 #include "talk.h"
100 #include "splash.h"
101 #include "rbunicode.h"
102 #include "root_menu.h"
104 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
105 #define PLAYLIST_CONTROL_FILE_VERSION 2
108 Each playlist index has a flag associated with it which identifies what
109 type of track it is. These flags are stored in the 4 high order bits of
110 the index.
112 NOTE: This limits the playlist file size to a max of 256M.
114 Bits 31-30:
115 00 = Playlist track
116 01 = Track was prepended into playlist
117 10 = Track was inserted into playlist
118 11 = Track was appended into playlist
119 Bit 29:
120 0 = Added track
121 1 = Queued track
122 Bit 28:
123 0 = Track entry is valid
124 1 = Track does not exist on disk and should be skipped
126 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
127 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
128 #define PLAYLIST_QUEUE_MASK 0x20000000
130 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
131 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
132 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
134 #define PLAYLIST_QUEUED 0x20000000
135 #define PLAYLIST_SKIPPED 0x10000000
137 #define PLAYLIST_DISPLAY_COUNT 10
139 struct directory_search_context {
140 struct playlist_info* playlist;
141 int position;
142 bool queue;
143 int count;
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,
151 const char *file);
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, size_t 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,
167 bool write);
168 static int sort_playlist(struct playlist_info* playlist, bool start_current,
169 bool write);
170 static int get_next_index(const struct playlist_info* playlist, int steps,
171 int repeat_mode);
172 static void find_and_set_playlist_index(struct playlist_info* playlist,
173 unsigned int seek);
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,
182 const char *dir);
183 static void display_playlist_count(int count, const unsigned char *fmt,
184 bool final);
185 static void display_buffer_full(void);
186 static int flush_cached_control(struct playlist_info* playlist);
187 static int update_control(struct playlist_info* playlist,
188 enum playlist_command command, int i1, int i2,
189 const char* s1, const char* s2, void* data);
190 static void sync_control(struct playlist_info* playlist, bool force);
191 static int rotate_index(const struct playlist_info* playlist, int index);
193 #ifdef HAVE_DIRCACHE
194 #define PLAYLIST_LOAD_POINTERS 1
196 static struct event_queue playlist_queue;
197 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
198 static const char playlist_thread_name[] = "playlist cachectrl";
199 #endif
201 #define BOM "\xef\xbb\xbf"
202 #define BOM_SIZE 3
204 /* Check if the filename suggests M3U or M3U8 format. */
205 static bool is_m3u8(const char* filename)
207 int len = strlen(filename);
209 /* Default to M3U8 unless explicitly told otherwise. */
210 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
213 /* Check if a strings starts with an UTF-8 byte-order mark. */
214 static bool is_utf8_bom(const char* str, int len)
216 return len >= BOM_SIZE && memcmp(str, BOM, BOM_SIZE) == 0;
219 /* Convert a filename in an M3U playlist to UTF-8.
221 * buf - the filename to convert; can contain more than one line from the
222 * playlist.
223 * buf_len - amount of buf that is used.
224 * buf_max - total size of buf.
225 * temp - temporary conversion buffer, at least buf_max bytes.
227 * Returns the length of the converted filename.
229 static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
231 int i = 0;
232 char* dest;
234 /* Locate EOL. */
235 while ((buf[i] != '\n') && (buf[i] != '\r') && (i < buf_len))
237 i++;
240 /* Work back killing white space. */
241 while ((i > 0) && isspace(buf[i - 1]))
243 i--;
246 buf_len = i;
247 dest = temp;
249 /* Convert char by char, so as to not overflow temp (iso_decode should
250 * preferably handle this). No more than 4 bytes should be generated for
251 * each input char.
253 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
255 dest = iso_decode(&buf[i], dest, -1, 1);
258 *dest = 0;
259 strcpy(buf, temp);
260 return dest - temp;
264 * remove any files and indices associated with the playlist
266 static void empty_playlist(struct playlist_info* playlist, bool resume)
268 playlist->filename[0] = '\0';
269 playlist->utf8 = true;
271 if(playlist->fd >= 0)
272 /* If there is an already open playlist, close it. */
273 close(playlist->fd);
274 playlist->fd = -1;
276 if(playlist->control_fd >= 0)
277 close(playlist->control_fd);
278 playlist->control_fd = -1;
279 playlist->control_created = false;
281 playlist->in_ram = false;
283 if (playlist->buffer)
284 playlist->buffer[0] = 0;
286 playlist->buffer_end_pos = 0;
288 playlist->index = 0;
289 playlist->first_index = 0;
290 playlist->amount = 0;
291 playlist->last_insert_pos = -1;
292 playlist->seed = 0;
293 playlist->shuffle_modified = false;
294 playlist->deleted = false;
295 playlist->num_inserted_tracks = 0;
296 playlist->started = false;
298 playlist->num_cached = 0;
299 playlist->pending_control_sync = false;
301 if (!resume && playlist->current)
303 /* start with fresh playlist control file when starting new
304 playlist */
305 create_control(playlist);
307 /* Reset resume settings */
308 global_status.resume_first_index = 0;
309 global_status.resume_seed = -1;
314 * Initialize a new playlist for viewing/editing/playing. dir is the
315 * directory where the playlist is located and file is the filename.
317 static void new_playlist(struct playlist_info* playlist, const char *dir,
318 const char *file)
320 const char *fileused = file;
321 const char *dirused = dir;
322 empty_playlist(playlist, false);
324 if (!fileused)
326 fileused = "";
328 if (dirused && playlist->current) /* !current cannot be in_ram */
329 playlist->in_ram = true;
330 else
331 dirused = ""; /* empty playlist */
334 update_playlist_filename(playlist, dirused, fileused);
336 if (playlist->control_fd >= 0)
338 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
339 PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
340 sync_control(playlist, false);
345 * create control file for playlist
347 static void create_control(struct playlist_info* playlist)
349 playlist->control_fd = open(playlist->control_filename,
350 O_CREAT|O_RDWR|O_TRUNC);
351 if (playlist->control_fd < 0)
353 if (check_rockboxdir())
355 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
356 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
357 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
358 playlist->control_fd);
360 playlist->control_created = false;
362 else
364 playlist->control_created = true;
369 * validate the control file. This may include creating/initializing it if
370 * necessary;
372 static int check_control(struct playlist_info* playlist)
374 if (!playlist->control_created)
376 create_control(playlist);
378 if (playlist->control_fd >= 0)
380 char* dir = playlist->filename;
381 char* file = playlist->filename+playlist->dirlen;
382 char c = playlist->filename[playlist->dirlen-1];
384 playlist->filename[playlist->dirlen-1] = '\0';
386 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
387 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
388 sync_control(playlist, false);
389 playlist->filename[playlist->dirlen-1] = c;
393 if (playlist->control_fd < 0)
394 return -1;
396 return 0;
400 * recreate the control file based on current playlist entries
402 static int recreate_control(struct playlist_info* playlist)
404 char temp_file[MAX_PATH+1];
405 int temp_fd = -1;
406 int i;
407 int result = 0;
409 if(playlist->control_fd >= 0)
411 char* dir = playlist->filename;
412 char* file = playlist->filename+playlist->dirlen;
413 char c = playlist->filename[playlist->dirlen-1];
415 close(playlist->control_fd);
417 snprintf(temp_file, sizeof(temp_file), "%s_temp",
418 playlist->control_filename);
420 if (rename(playlist->control_filename, temp_file) < 0)
421 return -1;
423 temp_fd = open(temp_file, O_RDONLY);
424 if (temp_fd < 0)
425 return -1;
427 playlist->control_fd = open(playlist->control_filename,
428 O_CREAT|O_RDWR|O_TRUNC);
429 if (playlist->control_fd < 0)
430 return -1;
432 playlist->filename[playlist->dirlen-1] = '\0';
434 /* cannot call update_control() because of mutex */
435 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
436 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
438 playlist->filename[playlist->dirlen-1] = c;
440 if (result < 0)
442 close(temp_fd);
443 return result;
447 playlist->seed = 0;
448 playlist->shuffle_modified = false;
449 playlist->deleted = false;
450 playlist->num_inserted_tracks = 0;
452 if (playlist->current)
454 global_status.resume_seed = -1;
455 status_save();
458 for (i=0; i<playlist->amount; i++)
460 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
462 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
463 char inserted_file[MAX_PATH+1];
465 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
466 SEEK_SET);
467 read_line(temp_fd, inserted_file, sizeof(inserted_file));
469 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
470 queue?'Q':'A', i, playlist->last_insert_pos);
471 if (result > 0)
473 /* save the position in file where name is written */
474 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
476 result = fdprintf(playlist->control_fd, "%s\n",
477 inserted_file);
479 playlist->indices[i] =
480 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
483 if (result < 0)
484 break;
486 playlist->num_inserted_tracks++;
490 close(temp_fd);
491 remove(temp_file);
492 fsync(playlist->control_fd);
494 if (result < 0)
495 return result;
497 return 0;
501 * store directory and name of playlist file
503 static void update_playlist_filename(struct playlist_info* playlist,
504 const char *dir, const char *file)
506 char *sep="";
507 int dirlen = strlen(dir);
509 playlist->utf8 = is_m3u8(file);
511 /* If the dir does not end in trailing slash, we use a separator.
512 Otherwise we don't. */
513 if('/' != dir[dirlen-1])
515 sep="/";
516 dirlen++;
519 playlist->dirlen = dirlen;
521 snprintf(playlist->filename, sizeof(playlist->filename),
522 "%s%s%s", dir, sep, file);
526 * calculate track offsets within a playlist file
528 static int add_indices_to_playlist(struct playlist_info* playlist,
529 char* buffer, size_t buflen)
531 unsigned int nread;
532 unsigned int i = 0;
533 unsigned int count = 0;
534 bool store_index;
535 unsigned char *p;
536 int result = 0;
538 if(-1 == playlist->fd)
539 playlist->fd = open(playlist->filename, O_RDONLY);
540 if(playlist->fd < 0)
541 return -1; /* failure */
543 gui_syncsplash(0, ID2P(LANG_WAIT));
545 if (!buffer)
547 /* use mp3 buffer for maximum load speed */
548 audio_stop();
549 #if CONFIG_CODEC != SWCODEC
550 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
551 buflen = (audiobufend - audiobuf);
552 buffer = (char *)audiobuf;
553 #else
554 buffer = (char *)audio_get_buffer(false, &buflen);
555 #endif
558 store_index = true;
560 while(1)
562 nread = read(playlist->fd, buffer, buflen);
563 /* Terminate on EOF */
564 if(nread <= 0)
565 break;
567 p = (unsigned char *)buffer;
569 /* utf8 BOM at beginning of file? */
570 if(i == 0 && is_utf8_bom(p, nread)) {
571 nread -= BOM_SIZE;
572 p += BOM_SIZE;
573 i += BOM_SIZE;
574 playlist->utf8 = true; /* Override any earlier indication. */
577 for(count=0; count < nread; count++,p++) {
579 /* Are we on a new line? */
580 if((*p == '\n') || (*p == '\r'))
582 store_index = true;
584 else if(store_index)
586 store_index = false;
588 if(*p != '#')
590 if ( playlist->amount >= playlist->max_playlist_size ) {
591 display_buffer_full();
592 result = -1;
593 goto exit;
596 /* Store a new entry */
597 playlist->indices[ playlist->amount ] = i+count;
598 #ifdef HAVE_DIRCACHE
599 if (playlist->filenames)
600 playlist->filenames[ playlist->amount ] = NULL;
601 #endif
602 playlist->amount++;
607 i+= count;
610 exit:
611 #ifdef HAVE_DIRCACHE
612 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
613 #endif
615 return result;
619 * Utility function to create a new playlist, fill it with the next or
620 * previous directory, shuffle it if needed, and start playback.
621 * If play_last is true and direction zero or negative, start playing
622 * the last file in the directory, otherwise start playing the first.
624 static int create_and_play_dir(int direction, bool play_last)
626 char dir[MAX_PATH + 1];
627 int res;
628 int index = -1;
630 if(direction > 0)
631 res = get_next_directory(dir);
632 else
633 res = get_previous_directory(dir);
635 if (!res)
637 if (playlist_create(dir, NULL) != -1)
639 ft_build_playlist(tree_get_context(), 0);
641 if (global_settings.playlist_shuffle)
642 playlist_shuffle(current_tick, -1);
644 if (play_last && direction <= 0)
645 index = current_playlist.amount - 1;
646 else
647 index = 0;
649 #if (CONFIG_CODEC != SWCODEC)
650 playlist_start(index, 0);
651 #endif
654 /* we've overwritten the dircache when getting the next/previous dir,
655 so the tree browser context will need to be reloaded */
656 reload_directory();
659 return index;
663 * Removes all tracks, from the playlist, leaving the presently playing
664 * track queued.
666 int playlist_remove_all_tracks(struct playlist_info *playlist)
668 int result;
670 if (playlist == NULL)
671 playlist = &current_playlist;
673 while (playlist->index > 0)
674 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
675 return result;
677 while (playlist->amount > 1)
678 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
679 return result;
681 if (playlist->amount == 1) {
682 playlist->indices[0] |= PLAYLIST_QUEUED;
685 return 0;
690 * Add track to playlist at specified position. There are five special
691 * positions that can be specified:
692 * PLAYLIST_PREPEND - Add track at beginning of playlist
693 * PLAYLIST_INSERT - Add track after current song. NOTE: If
694 * there are already inserted tracks then track
695 * is added to the end of the insertion list
696 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
697 * matter what other tracks have been inserted
698 * PLAYLIST_INSERT_LAST - Add track to end of playlist
699 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
700 * current playing track and end of playlist
701 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
702 * and inster this track at the end.
704 static int add_track_to_playlist(struct playlist_info* playlist,
705 const char *filename, int position,
706 bool queue, int seek_pos)
708 int insert_position, orig_position;
709 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
710 int i;
712 insert_position = orig_position = position;
714 if (playlist->amount >= playlist->max_playlist_size)
716 display_buffer_full();
717 return -1;
720 switch (position)
722 case PLAYLIST_PREPEND:
723 position = insert_position = playlist->first_index;
724 break;
725 case PLAYLIST_INSERT:
726 /* if there are already inserted tracks then add track to end of
727 insertion list else add after current playing track */
728 if (playlist->last_insert_pos >= 0 &&
729 playlist->last_insert_pos < playlist->amount &&
730 (playlist->indices[playlist->last_insert_pos]&
731 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
732 position = insert_position = playlist->last_insert_pos+1;
733 else if (playlist->amount > 0)
734 position = insert_position = playlist->index + 1;
735 else
736 position = insert_position = 0;
738 if (playlist->started)
739 playlist->last_insert_pos = position;
740 break;
741 case PLAYLIST_INSERT_FIRST:
742 if (playlist->amount > 0)
743 position = insert_position = playlist->index + 1;
744 else
745 position = insert_position = 0;
747 if (playlist->last_insert_pos < 0 && playlist->started)
748 playlist->last_insert_pos = position;
749 break;
750 case PLAYLIST_INSERT_LAST:
751 if (playlist->first_index > 0)
752 position = insert_position = playlist->first_index;
753 else
754 position = insert_position = playlist->amount;
755 break;
756 case PLAYLIST_INSERT_SHUFFLED:
758 if (playlist->started)
760 int offset;
761 int n = playlist->amount -
762 rotate_index(playlist, playlist->index);
764 if (n > 0)
765 offset = rand() % n;
766 else
767 offset = 0;
769 position = playlist->index + offset + 1;
770 if (position >= playlist->amount)
771 position -= playlist->amount;
773 insert_position = position;
775 else
776 position = insert_position = (rand() % (playlist->amount+1));
777 break;
779 case PLAYLIST_REPLACE:
780 if (playlist_remove_all_tracks(playlist) < 0)
781 return -1;
783 position = insert_position = playlist->index + 1;
784 break;
787 if (queue)
788 flags |= PLAYLIST_QUEUED;
790 /* shift indices so that track can be added */
791 for (i=playlist->amount; i>insert_position; i--)
793 playlist->indices[i] = playlist->indices[i-1];
794 #ifdef HAVE_DIRCACHE
795 if (playlist->filenames)
796 playlist->filenames[i] = playlist->filenames[i-1];
797 #endif
800 /* update stored indices if needed */
801 if (playlist->amount > 0 && insert_position <= playlist->index &&
802 playlist->started)
803 playlist->index++;
805 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
806 orig_position != PLAYLIST_PREPEND && playlist->started)
808 playlist->first_index++;
810 if (seek_pos < 0 && playlist->current)
812 global_status.resume_first_index = playlist->first_index;
813 status_save();
817 if (insert_position < playlist->last_insert_pos ||
818 (insert_position == playlist->last_insert_pos && position < 0))
819 playlist->last_insert_pos++;
821 if (seek_pos < 0 && playlist->control_fd >= 0)
823 int result = update_control(playlist,
824 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
825 playlist->last_insert_pos, filename, NULL, &seek_pos);
827 if (result < 0)
828 return result;
831 playlist->indices[insert_position] = flags | seek_pos;
833 #ifdef HAVE_DIRCACHE
834 if (playlist->filenames)
835 playlist->filenames[insert_position] = NULL;
836 #endif
838 playlist->amount++;
839 playlist->num_inserted_tracks++;
841 return insert_position;
845 * Callback for playlist_directory_tracksearch to insert track into
846 * playlist.
848 static int directory_search_callback(char* filename, void* context)
850 struct directory_search_context* c =
851 (struct directory_search_context*) context;
852 int insert_pos;
854 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
855 c->queue, -1);
857 if (insert_pos < 0)
858 return -1;
860 (c->count)++;
862 /* Make sure tracks are inserted in correct order if user requests
863 INSERT_FIRST */
864 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
865 c->position = insert_pos + 1;
867 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
869 unsigned char* count_str;
871 if (c->queue)
872 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
873 else
874 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
876 display_playlist_count(c->count, count_str, false);
878 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
879 (audio_status() & AUDIO_STATUS_PLAY) &&
880 c->playlist->started)
881 audio_flush_and_reload_tracks();
884 return 0;
888 * remove track at specified position
890 static int remove_track_from_playlist(struct playlist_info* playlist,
891 int position, bool write)
893 int i;
894 bool inserted;
896 if (playlist->amount <= 0)
897 return -1;
899 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
901 /* shift indices now that track has been removed */
902 for (i=position; i<playlist->amount; i++)
904 playlist->indices[i] = playlist->indices[i+1];
905 #ifdef HAVE_DIRCACHE
906 if (playlist->filenames)
907 playlist->filenames[i] = playlist->filenames[i+1];
908 #endif
911 playlist->amount--;
913 if (inserted)
914 playlist->num_inserted_tracks--;
915 else
916 playlist->deleted = true;
918 /* update stored indices if needed */
919 if (position < playlist->index)
920 playlist->index--;
922 if (position < playlist->first_index)
924 playlist->first_index--;
926 if (write)
928 global_status.resume_first_index = playlist->first_index;
929 status_save();
933 if (position <= playlist->last_insert_pos)
934 playlist->last_insert_pos--;
936 if (write && playlist->control_fd >= 0)
938 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
939 position, -1, NULL, NULL, NULL);
941 if (result < 0)
942 return result;
944 sync_control(playlist, false);
947 return 0;
951 * randomly rearrange the array of indices for the playlist. If start_current
952 * is true then update the index to the new index of the current playing track
954 static int randomise_playlist(struct playlist_info* playlist,
955 unsigned int seed, bool start_current,
956 bool write)
958 int count;
959 int candidate;
960 long store;
961 unsigned int current = playlist->indices[playlist->index];
963 /* seed 0 is used to identify sorted playlist for resume purposes */
964 if (seed == 0)
965 seed = 1;
967 /* seed with the given seed */
968 srand(seed);
970 /* randomise entire indices list */
971 for(count = playlist->amount - 1; count >= 0; count--)
973 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
974 candidate = rand() % (count + 1);
976 /* now swap the values at the 'count' and 'candidate' positions */
977 store = playlist->indices[candidate];
978 playlist->indices[candidate] = playlist->indices[count];
979 playlist->indices[count] = store;
980 #ifdef HAVE_DIRCACHE
981 if (playlist->filenames)
983 store = (long)playlist->filenames[candidate];
984 playlist->filenames[candidate] = playlist->filenames[count];
985 playlist->filenames[count] = (struct dircache_entry *)store;
987 #endif
990 if (start_current)
991 find_and_set_playlist_index(playlist, current);
993 /* indices have been moved so last insert position is no longer valid */
994 playlist->last_insert_pos = -1;
996 playlist->seed = seed;
997 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
998 playlist->shuffle_modified = true;
1000 if (write)
1002 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
1003 playlist->first_index, NULL, NULL, NULL);
1004 global_status.resume_seed = seed;
1005 status_save();
1008 return 0;
1012 * Sort the array of indices for the playlist. If start_current is true then
1013 * set the index to the new index of the current song.
1015 static int sort_playlist(struct playlist_info* playlist, bool start_current,
1016 bool write)
1018 unsigned int current = playlist->indices[playlist->index];
1020 if (playlist->amount > 0)
1021 qsort(playlist->indices, playlist->amount,
1022 sizeof(playlist->indices[0]), compare);
1024 #ifdef HAVE_DIRCACHE
1025 /** We need to re-check the song names from disk because qsort can't
1026 * sort two arrays at once :/
1027 * FIXME: Please implement a better way to do this. */
1028 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
1029 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1030 #endif
1032 if (start_current)
1033 find_and_set_playlist_index(playlist, current);
1035 /* indices have been moved so last insert position is no longer valid */
1036 playlist->last_insert_pos = -1;
1038 if (!playlist->num_inserted_tracks && !playlist->deleted)
1039 playlist->shuffle_modified = false;
1040 if (write && playlist->control_fd >= 0)
1042 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1043 playlist->first_index, -1, NULL, NULL, NULL);
1044 global_status.resume_seed = 0;
1045 status_save();
1048 return 0;
1051 /* Calculate how many steps we have to really step when skipping entries
1052 * marked as bad.
1054 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1056 int i, count, direction;
1057 int index;
1058 int stepped_count = 0;
1060 if (steps < 0)
1062 direction = -1;
1063 count = -steps;
1065 else
1067 direction = 1;
1068 count = steps;
1071 index = playlist->index;
1072 i = 0;
1073 do {
1074 /* Boundary check */
1075 if (index < 0)
1076 index += playlist->amount;
1077 if (index >= playlist->amount)
1078 index -= playlist->amount;
1080 /* Check if we found a bad entry. */
1081 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1083 steps += direction;
1084 /* Are all entries bad? */
1085 if (stepped_count++ > playlist->amount)
1086 break ;
1088 else
1089 i++;
1091 index += direction;
1092 } while (i <= count);
1094 return steps;
1097 /* Marks the index of the track to be skipped that is "steps" away from
1098 * current playing track.
1100 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1102 int index;
1104 if (playlist == NULL)
1105 playlist = &current_playlist;
1107 /* need to account for already skipped tracks */
1108 steps = calculate_step_count(playlist, steps);
1110 index = playlist->index + steps;
1111 if (index < 0)
1112 index += playlist->amount;
1113 else if (index >= playlist->amount)
1114 index -= playlist->amount;
1116 playlist->indices[index] |= PLAYLIST_SKIPPED;
1120 * returns the index of the track that is "steps" away from current playing
1121 * track.
1123 static int get_next_index(const struct playlist_info* playlist, int steps,
1124 int repeat_mode)
1126 int current_index = playlist->index;
1127 int next_index = -1;
1129 if (playlist->amount <= 0)
1130 return -1;
1132 if (repeat_mode == -1)
1133 repeat_mode = global_settings.repeat_mode;
1135 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1136 repeat_mode = REPEAT_ALL;
1138 steps = calculate_step_count(playlist, steps);
1139 switch (repeat_mode)
1141 case REPEAT_SHUFFLE:
1142 /* Treat repeat shuffle just like repeat off. At end of playlist,
1143 play will be resumed in playlist_next() */
1144 case REPEAT_OFF:
1146 current_index = rotate_index(playlist, current_index);
1147 next_index = current_index+steps;
1148 if ((next_index < 0) || (next_index >= playlist->amount))
1149 next_index = -1;
1150 else
1151 next_index = (next_index+playlist->first_index) %
1152 playlist->amount;
1154 break;
1157 case REPEAT_ONE:
1158 #ifdef AB_REPEAT_ENABLE
1159 case REPEAT_AB:
1160 #endif
1161 next_index = current_index;
1162 break;
1164 case REPEAT_ALL:
1165 default:
1167 next_index = (current_index+steps) % playlist->amount;
1168 while (next_index < 0)
1169 next_index += playlist->amount;
1171 if (steps >= playlist->amount)
1173 int i, index;
1175 index = next_index;
1176 next_index = -1;
1178 /* second time around so skip the queued files */
1179 for (i=0; i<playlist->amount; i++)
1181 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1182 index = (index+1) % playlist->amount;
1183 else
1185 next_index = index;
1186 break;
1190 break;
1194 /* No luck if the whole playlist was bad. */
1195 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1196 return -1;
1198 return next_index;
1202 * Search for the seek track and set appropriate indices. Used after shuffle
1203 * to make sure the current index is still pointing to correct track.
1205 static void find_and_set_playlist_index(struct playlist_info* playlist,
1206 unsigned int seek)
1208 int i;
1210 /* Set the index to the current song */
1211 for (i=0; i<playlist->amount; i++)
1213 if (playlist->indices[i] == seek)
1215 playlist->index = playlist->first_index = i;
1217 if (playlist->current)
1219 global_status.resume_first_index = i;
1220 status_save();
1223 break;
1229 * used to sort track indices. Sort order is as follows:
1230 * 1. Prepended tracks (in prepend order)
1231 * 2. Playlist/directory tracks (in playlist order)
1232 * 3. Inserted/Appended tracks (in insert order)
1234 static int compare(const void* p1, const void* p2)
1236 unsigned long* e1 = (unsigned long*) p1;
1237 unsigned long* e2 = (unsigned long*) p2;
1238 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1239 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1241 if (flags1 == flags2)
1242 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1243 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1244 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1245 return -1;
1246 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1247 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1248 return 1;
1249 else if (flags1 && flags2)
1250 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1251 else
1252 return *e1 - *e2;
1255 #ifdef HAVE_DIRCACHE
1257 * Thread to update filename pointers to dircache on background
1258 * without affecting playlist load up performance. This thread also flushes
1259 * any pending control commands when the disk spins up.
1261 static bool playlist_flush_callback(void)
1263 struct playlist_info *playlist;
1264 playlist = &current_playlist;
1265 if (playlist->control_fd >= 0)
1267 if (playlist->num_cached > 0)
1269 mutex_lock(&playlist->control_mutex);
1270 flush_cached_control(playlist);
1271 mutex_unlock(&playlist->control_mutex);
1273 sync_control(playlist, true);
1275 return true;
1278 static void playlist_thread(void)
1280 struct queue_event ev;
1281 bool dirty_pointers = false;
1282 static char tmp[MAX_PATH+1];
1284 struct playlist_info *playlist;
1285 int index;
1286 int seek;
1287 bool control_file;
1289 int sleep_time = 5;
1291 #ifndef HAVE_FLASH_STORAGE
1292 if (global_settings.disk_spindown > 1 &&
1293 global_settings.disk_spindown <= 5)
1294 sleep_time = global_settings.disk_spindown - 1;
1295 #endif
1297 while (1)
1299 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1301 switch (ev.id)
1303 case PLAYLIST_LOAD_POINTERS:
1304 dirty_pointers = true;
1305 break ;
1307 /* Start the background scanning after either the disk spindown
1308 timeout or 5s, whichever is less */
1309 case SYS_TIMEOUT:
1310 playlist = &current_playlist;
1311 if (playlist->control_fd >= 0)
1313 if (playlist->num_cached > 0)
1314 register_ata_idle_func(playlist_flush_callback);
1317 if (!dirty_pointers)
1318 break ;
1320 if (!dircache_is_enabled() || !playlist->filenames
1321 || playlist->amount <= 0)
1322 break ;
1324 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1325 cpu_boost(true);
1326 #endif
1327 for (index = 0; index < playlist->amount
1328 && queue_empty(&playlist_queue); index++)
1330 /* Process only pointers that are not already loaded. */
1331 if (playlist->filenames[index])
1332 continue ;
1334 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1335 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1337 /* Load the filename from playlist file. */
1338 if (get_filename(playlist, index, seek, control_file, tmp,
1339 sizeof(tmp)) < 0)
1340 break ;
1342 /* Set the dircache entry pointer. */
1343 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1345 /* And be on background so user doesn't notice any delays. */
1346 yield();
1349 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1350 cpu_boost(false);
1351 #endif
1352 dirty_pointers = false;
1353 break ;
1355 #ifndef SIMULATOR
1356 case SYS_USB_CONNECTED:
1357 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1358 usb_wait_for_disconnect(&playlist_queue);
1359 break ;
1360 #endif
1364 #endif
1367 * gets pathname for track at seek index
1369 static int get_filename(struct playlist_info* playlist, int index, int seek,
1370 bool control_file, char *buf, int buf_length)
1372 int fd;
1373 int max = -1;
1374 char tmp_buf[MAX_PATH+1];
1375 char dir_buf[MAX_PATH+1];
1376 bool utf8 = playlist->utf8;
1378 if (buf_length > MAX_PATH+1)
1379 buf_length = MAX_PATH+1;
1381 #ifdef HAVE_DIRCACHE
1382 if (dircache_is_enabled() && playlist->filenames)
1384 if (playlist->filenames[index] != NULL)
1386 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1387 max = strlen(tmp_buf) + 1;
1390 #else
1391 (void)index;
1392 #endif
1394 if (playlist->in_ram && !control_file && max < 0)
1396 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1397 tmp_buf[MAX_PATH] = '\0';
1398 max = strlen(tmp_buf) + 1;
1400 else if (max < 0)
1402 mutex_lock(&playlist->control_mutex);
1404 if (control_file)
1406 fd = playlist->control_fd;
1407 utf8 = true;
1409 else
1411 if(-1 == playlist->fd)
1412 playlist->fd = open(playlist->filename, O_RDONLY);
1414 fd = playlist->fd;
1417 if(-1 != fd)
1420 if (lseek(fd, seek, SEEK_SET) != seek)
1421 max = -1;
1422 else
1424 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1426 if ((max > 0) && !utf8)
1428 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1429 * be as large as tmp_buf.
1431 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1436 mutex_unlock(&playlist->control_mutex);
1438 if (max < 0)
1440 if (control_file)
1441 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1442 else
1443 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1445 return max;
1449 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1450 dir_buf[playlist->dirlen-1] = 0;
1452 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1455 static int get_next_directory(char *dir){
1456 return get_next_dir(dir,true,false);
1459 static int get_previous_directory(char *dir){
1460 return get_next_dir(dir,false,false);
1464 * search through all the directories (starting with the current) to find
1465 * one that has tracks to play
1467 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1469 struct playlist_info* playlist = &current_playlist;
1470 int result = -1;
1471 int sort_dir = global_settings.sort_dir;
1472 char *start_dir = NULL;
1473 bool exit = false;
1474 struct tree_context* tc = tree_get_context();
1475 int dirfilter = *(tc->dirfilter);
1477 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1479 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1480 char buffer[MAX_PATH];
1481 int folder_count = 0,i;
1482 srand(current_tick);
1483 *(tc->dirfilter) = SHOW_MUSIC;
1484 if (fd >= 0)
1486 read(fd,&folder_count,sizeof(int));
1487 while (!exit)
1489 i = rand()%folder_count;
1490 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1491 read(fd,buffer,MAX_PATH);
1492 if (check_subdir_for_music(buffer,"") ==0)
1493 exit = true;
1495 strcpy(dir,buffer);
1496 close(fd);
1497 *(tc->dirfilter) = dirfilter;
1498 reload_directory();
1499 return 0;
1502 /* not random folder advance */
1503 if (recursion){
1504 /* start with root */
1505 dir[0] = '\0';
1507 else{
1508 /* start with current directory */
1509 strncpy(dir, playlist->filename, playlist->dirlen-1);
1510 dir[playlist->dirlen-1] = '\0';
1513 /* use the tree browser dircache to load files */
1514 *(tc->dirfilter) = SHOW_ALL;
1516 /* sort in another direction if previous dir is requested */
1517 if(!is_forward){
1518 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1519 global_settings.sort_dir = 4;
1520 else if (global_settings.sort_dir == 1)
1521 global_settings.sort_dir = 2;
1522 else if (global_settings.sort_dir == 2)
1523 global_settings.sort_dir = 1;
1524 else if (global_settings.sort_dir == 4)
1525 global_settings.sort_dir = 0;
1528 while (!exit)
1530 struct entry *files;
1531 int num_files = 0;
1532 int i;
1534 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1536 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1537 exit = true;
1538 result = -1;
1539 break;
1542 files = (struct entry*) tc->dircache;
1543 num_files = tc->filesindir;
1545 for (i=0; i<num_files; i++)
1547 /* user abort */
1548 if (action_userabort(TIMEOUT_NOBLOCK))
1550 result = -1;
1551 exit = true;
1552 break;
1555 if (files[i].attr & ATTR_DIRECTORY)
1557 if (!start_dir)
1559 result = check_subdir_for_music(dir, files[i].name);
1560 if (result != -1)
1562 exit = true;
1563 break;
1566 else if (!strcmp(start_dir, files[i].name))
1567 start_dir = NULL;
1571 if (!exit)
1573 /* move down to parent directory. current directory name is
1574 stored as the starting point for the search in parent */
1575 start_dir = strrchr(dir, '/');
1576 if (start_dir)
1578 *start_dir = '\0';
1579 start_dir++;
1581 else
1582 break;
1586 /* restore dirfilter & sort_dir */
1587 *(tc->dirfilter) = dirfilter;
1588 global_settings.sort_dir = sort_dir;
1590 /* special case if nothing found: try start searching again from root */
1591 if (result == -1 && !recursion){
1592 result = get_next_dir(dir,is_forward, true);
1595 return result;
1599 * Checks if there are any music files in the dir or any of its
1600 * subdirectories. May be called recursively.
1602 static int check_subdir_for_music(char *dir, char *subdir)
1604 int result = -1;
1605 int dirlen = strlen(dir);
1606 int num_files = 0;
1607 int i;
1608 struct entry *files;
1609 bool has_music = false;
1610 bool has_subdir = false;
1611 struct tree_context* tc = tree_get_context();
1613 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1615 if (ft_load(tc, dir) < 0)
1617 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1618 return -2;
1621 files = (struct entry*) tc->dircache;
1622 num_files = tc->filesindir;
1624 for (i=0; i<num_files; i++)
1626 if (files[i].attr & ATTR_DIRECTORY)
1627 has_subdir = true;
1628 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1630 has_music = true;
1631 break;
1635 if (has_music)
1636 return 0;
1638 if (has_subdir)
1640 for (i=0; i<num_files; i++)
1642 if (action_userabort(TIMEOUT_NOBLOCK))
1644 result = -2;
1645 break;
1648 if (files[i].attr & ATTR_DIRECTORY)
1650 result = check_subdir_for_music(dir, files[i].name);
1651 if (!result)
1652 break;
1657 if (result < 0)
1659 if (dirlen)
1661 dir[dirlen] = '\0';
1663 else
1665 strcpy(dir, "/");
1668 /* we now need to reload our current directory */
1669 if(ft_load(tc, dir) < 0)
1670 gui_syncsplash(HZ*2,
1671 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1674 return result;
1678 * Returns absolute path of track
1680 static int format_track_path(char *dest, char *src, int buf_length, int max,
1681 const char *dir)
1683 int i = 0;
1684 int j;
1685 char *temp_ptr;
1687 /* Zero-terminate the file name */
1688 while((src[i] != '\n') &&
1689 (src[i] != '\r') &&
1690 (i < max))
1691 i++;
1693 /* Now work back killing white space */
1694 while((src[i-1] == ' ') ||
1695 (src[i-1] == '\t'))
1696 i--;
1698 src[i]=0;
1700 /* replace backslashes with forward slashes */
1701 for ( j=0; j<i; j++ )
1702 if ( src[j] == '\\' )
1703 src[j] = '/';
1705 if('/' == src[0])
1707 strncpy(dest, src, buf_length);
1709 else
1711 /* handle dos style drive letter */
1712 if (':' == src[1])
1713 strncpy(dest, &src[2], buf_length);
1714 else if (!strncmp(src, "../", 3))
1716 /* handle relative paths */
1717 i=3;
1718 while(!strncmp(&src[i], "../", 3))
1719 i += 3;
1720 for (j=0; j<i/3; j++) {
1721 temp_ptr = strrchr(dir, '/');
1722 if (temp_ptr)
1723 *temp_ptr = '\0';
1724 else
1725 break;
1727 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1729 else if ( '.' == src[0] && '/' == src[1] ) {
1730 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1732 else {
1733 snprintf(dest, buf_length, "%s/%s", dir, src);
1737 return 0;
1741 * Display splash message showing progress of playlist/directory insertion or
1742 * save.
1744 static void display_playlist_count(int count, const unsigned char *fmt,
1745 bool final)
1747 static long talked_tick = 0;
1748 long id = P2ID(fmt);
1749 if(global_settings.talk_menu && id>=0)
1751 if(final || (count && (talked_tick == 0
1752 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1754 talked_tick = current_tick;
1755 talk_number(count, false);
1756 talk_id(id, true);
1759 fmt = P2STR(fmt);
1761 lcd_clear_display();
1763 #ifdef HAVE_LCD_BITMAP
1764 if(global_settings.statusbar)
1765 lcd_setmargins(0, STATUSBAR_HEIGHT);
1766 else
1767 lcd_setmargins(0, 0);
1768 #endif
1770 gui_syncsplash(0, fmt, count, str(LANG_OFF_ABORT));
1774 * Display buffer full message
1776 static void display_buffer_full(void)
1778 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1782 * Flush any cached control commands to disk. Called when playlist is being
1783 * modified. Returns 0 on success and -1 on failure.
1785 static int flush_cached_control(struct playlist_info* playlist)
1787 int result = 0;
1788 int i;
1790 if (!playlist->num_cached)
1791 return 0;
1793 lseek(playlist->control_fd, 0, SEEK_END);
1795 for (i=0; i<playlist->num_cached; i++)
1797 struct playlist_control_cache* cache =
1798 &(playlist->control_cache[i]);
1800 switch (cache->command)
1802 case PLAYLIST_COMMAND_PLAYLIST:
1803 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1804 cache->i1, cache->s1, cache->s2);
1805 break;
1806 case PLAYLIST_COMMAND_ADD:
1807 case PLAYLIST_COMMAND_QUEUE:
1808 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1809 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1810 cache->i1, cache->i2);
1811 if (result > 0)
1813 /* save the position in file where name is written */
1814 int* seek_pos = (int *)cache->data;
1815 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1816 result = fdprintf(playlist->control_fd, "%s\n",
1817 cache->s1);
1819 break;
1820 case PLAYLIST_COMMAND_DELETE:
1821 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1822 break;
1823 case PLAYLIST_COMMAND_SHUFFLE:
1824 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1825 cache->i1, cache->i2);
1826 break;
1827 case PLAYLIST_COMMAND_UNSHUFFLE:
1828 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1829 break;
1830 case PLAYLIST_COMMAND_RESET:
1831 result = fdprintf(playlist->control_fd, "R\n");
1832 break;
1833 default:
1834 break;
1837 if (result <= 0)
1838 break;
1841 if (result > 0)
1843 if (global_status.resume_seed >= 0)
1845 global_status.resume_seed = -1;
1846 status_save();
1849 playlist->num_cached = 0;
1850 playlist->pending_control_sync = true;
1852 result = 0;
1854 else
1856 result = -1;
1857 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1860 return result;
1864 * Update control data with new command. Depending on the command, it may be
1865 * cached or flushed to disk.
1867 static int update_control(struct playlist_info* playlist,
1868 enum playlist_command command, int i1, int i2,
1869 const char* s1, const char* s2, void* data)
1871 int result = 0;
1872 struct playlist_control_cache* cache;
1873 bool flush = false;
1875 mutex_lock(&playlist->control_mutex);
1877 cache = &(playlist->control_cache[playlist->num_cached++]);
1879 cache->command = command;
1880 cache->i1 = i1;
1881 cache->i2 = i2;
1882 cache->s1 = s1;
1883 cache->s2 = s2;
1884 cache->data = data;
1886 switch (command)
1888 case PLAYLIST_COMMAND_PLAYLIST:
1889 case PLAYLIST_COMMAND_ADD:
1890 case PLAYLIST_COMMAND_QUEUE:
1891 #ifndef HAVE_DIRCACHE
1892 case PLAYLIST_COMMAND_DELETE:
1893 case PLAYLIST_COMMAND_RESET:
1894 #endif
1895 flush = true;
1896 break;
1897 case PLAYLIST_COMMAND_SHUFFLE:
1898 case PLAYLIST_COMMAND_UNSHUFFLE:
1899 default:
1900 /* only flush when needed */
1901 break;
1904 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1905 result = flush_cached_control(playlist);
1907 mutex_unlock(&playlist->control_mutex);
1909 return result;
1913 * sync control file to disk
1915 static void sync_control(struct playlist_info* playlist, bool force)
1917 #ifdef HAVE_DIRCACHE
1918 if (playlist->started && force)
1919 #else
1920 (void) force;
1922 if (playlist->started)
1923 #endif
1925 if (playlist->pending_control_sync)
1927 mutex_lock(&playlist->control_mutex);
1928 fsync(playlist->control_fd);
1929 playlist->pending_control_sync = false;
1930 mutex_unlock(&playlist->control_mutex);
1936 * Rotate indices such that first_index is index 0
1938 static int rotate_index(const struct playlist_info* playlist, int index)
1940 index -= playlist->first_index;
1941 if (index < 0)
1942 index += playlist->amount;
1944 return index;
1948 * Initialize playlist entries at startup
1950 void playlist_init(void)
1952 struct playlist_info* playlist = &current_playlist;
1954 playlist->current = true;
1955 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1956 "%s", PLAYLIST_CONTROL_FILE);
1957 playlist->fd = -1;
1958 playlist->control_fd = -1;
1959 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1960 playlist->indices = buffer_alloc(
1961 playlist->max_playlist_size * sizeof(int));
1962 playlist->buffer_size =
1963 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1964 playlist->buffer = buffer_alloc(playlist->buffer_size);
1965 mutex_init(&playlist->control_mutex);
1966 empty_playlist(playlist, true);
1968 #ifdef HAVE_DIRCACHE
1969 playlist->filenames = buffer_alloc(
1970 playlist->max_playlist_size * sizeof(int));
1971 memset(playlist->filenames, 0,
1972 playlist->max_playlist_size * sizeof(int));
1973 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1974 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1975 IF_COP(, CPU));
1976 queue_init(&playlist_queue, true);
1977 #endif
1981 * Clean playlist at shutdown
1983 void playlist_shutdown(void)
1985 struct playlist_info* playlist = &current_playlist;
1987 if (playlist->control_fd >= 0)
1989 mutex_lock(&playlist->control_mutex);
1991 if (playlist->num_cached > 0)
1992 flush_cached_control(playlist);
1994 close(playlist->control_fd);
1996 mutex_unlock(&playlist->control_mutex);
2001 * Create new playlist
2003 int playlist_create(const char *dir, const char *file)
2005 struct playlist_info* playlist = &current_playlist;
2007 new_playlist(playlist, dir, file);
2009 if (file)
2010 /* load the playlist file */
2011 add_indices_to_playlist(playlist, NULL, 0);
2013 return 0;
2016 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2019 * Restore the playlist state based on control file commands. Called to
2020 * resume playback after shutdown.
2022 int playlist_resume(void)
2024 struct playlist_info* playlist = &current_playlist;
2025 char *buffer;
2026 size_t buflen;
2027 int nread;
2028 int total_read = 0;
2029 int control_file_size = 0;
2030 bool first = true;
2031 bool sorted = true;
2033 /* use mp3 buffer for maximum load speed */
2034 #if CONFIG_CODEC != SWCODEC
2035 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2036 buflen = (audiobufend - audiobuf);
2037 buffer = (char *)audiobuf;
2038 #else
2039 buffer = (char *)audio_get_buffer(false, &buflen);
2040 #endif
2042 empty_playlist(playlist, true);
2044 gui_syncsplash(0, ID2P(LANG_WAIT));
2045 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2046 if (playlist->control_fd < 0)
2048 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2049 return -1;
2051 playlist->control_created = true;
2053 control_file_size = filesize(playlist->control_fd);
2054 if (control_file_size <= 0)
2056 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2057 return -1;
2060 /* read a small amount first to get the header */
2061 nread = read(playlist->control_fd, buffer,
2062 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2063 if(nread <= 0)
2065 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2066 return -1;
2069 playlist->started = true;
2071 while (1)
2073 int result = 0;
2074 int count;
2075 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2076 int last_newline = 0;
2077 int str_count = -1;
2078 bool newline = true;
2079 bool exit_loop = false;
2080 char *p = buffer;
2081 char *str1 = NULL;
2082 char *str2 = NULL;
2083 char *str3 = NULL;
2084 unsigned long last_tick = current_tick;
2086 for(count=0; count<nread && !exit_loop; count++,p++)
2088 /* So a splash while we are loading. */
2089 if (current_tick - last_tick > HZ/4)
2091 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
2092 (total_read+count)*100/control_file_size,
2093 str(LANG_OFF_ABORT));
2094 if (action_userabort(TIMEOUT_NOBLOCK))
2096 /* FIXME:
2097 * Not sure how to implement this, somebody more familiar
2098 * with the code, please fix this. */
2100 last_tick = current_tick;
2103 /* Are we on a new line? */
2104 if((*p == '\n') || (*p == '\r'))
2106 *p = '\0';
2108 /* save last_newline in case we need to load more data */
2109 last_newline = count;
2111 switch (current_command)
2113 case PLAYLIST_COMMAND_PLAYLIST:
2115 /* str1=version str2=dir str3=file */
2116 int version;
2118 if (!str1)
2120 result = -1;
2121 exit_loop = true;
2122 break;
2125 if (!str2)
2126 str2 = "";
2128 if (!str3)
2129 str3 = "";
2131 version = atoi(str1);
2133 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2134 return -1;
2136 update_playlist_filename(playlist, str2, str3);
2138 if (str3[0] != '\0')
2140 /* NOTE: add_indices_to_playlist() overwrites the
2141 audiobuf so we need to reload control file
2142 data */
2143 add_indices_to_playlist(playlist, NULL, 0);
2145 else if (str2[0] != '\0')
2147 playlist->in_ram = true;
2148 resume_directory(str2);
2151 /* load the rest of the data */
2152 first = false;
2153 exit_loop = true;
2155 break;
2157 case PLAYLIST_COMMAND_ADD:
2158 case PLAYLIST_COMMAND_QUEUE:
2160 /* str1=position str2=last_position str3=file */
2161 int position, last_position;
2162 bool queue;
2164 if (!str1 || !str2 || !str3)
2166 result = -1;
2167 exit_loop = true;
2168 break;
2171 position = atoi(str1);
2172 last_position = atoi(str2);
2174 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2175 false:true;
2177 /* seek position is based on str3's position in
2178 buffer */
2179 if (add_track_to_playlist(playlist, str3, position,
2180 queue, total_read+(str3-buffer)) < 0)
2181 return -1;
2183 playlist->last_insert_pos = last_position;
2185 break;
2187 case PLAYLIST_COMMAND_DELETE:
2189 /* str1=position */
2190 int position;
2192 if (!str1)
2194 result = -1;
2195 exit_loop = true;
2196 break;
2199 position = atoi(str1);
2201 if (remove_track_from_playlist(playlist, position,
2202 false) < 0)
2203 return -1;
2205 break;
2207 case PLAYLIST_COMMAND_SHUFFLE:
2209 /* str1=seed str2=first_index */
2210 int seed;
2212 if (!str1 || !str2)
2214 result = -1;
2215 exit_loop = true;
2216 break;
2219 if (!sorted)
2221 /* Always sort list before shuffling */
2222 sort_playlist(playlist, false, false);
2225 seed = atoi(str1);
2226 playlist->first_index = atoi(str2);
2228 if (randomise_playlist(playlist, seed, false,
2229 false) < 0)
2230 return -1;
2232 sorted = false;
2233 break;
2235 case PLAYLIST_COMMAND_UNSHUFFLE:
2237 /* str1=first_index */
2238 if (!str1)
2240 result = -1;
2241 exit_loop = true;
2242 break;
2245 playlist->first_index = atoi(str1);
2247 if (sort_playlist(playlist, false, false) < 0)
2248 return -1;
2250 sorted = true;
2251 break;
2253 case PLAYLIST_COMMAND_RESET:
2255 playlist->last_insert_pos = -1;
2256 break;
2258 case PLAYLIST_COMMAND_COMMENT:
2259 default:
2260 break;
2263 newline = true;
2265 /* to ignore any extra newlines */
2266 current_command = PLAYLIST_COMMAND_COMMENT;
2268 else if(newline)
2270 newline = false;
2272 /* first non-comment line must always specify playlist */
2273 if (first && *p != 'P' && *p != '#')
2275 result = -1;
2276 exit_loop = true;
2277 break;
2280 switch (*p)
2282 case 'P':
2283 /* playlist can only be specified once */
2284 if (!first)
2286 result = -1;
2287 exit_loop = true;
2288 break;
2291 current_command = PLAYLIST_COMMAND_PLAYLIST;
2292 break;
2293 case 'A':
2294 current_command = PLAYLIST_COMMAND_ADD;
2295 break;
2296 case 'Q':
2297 current_command = PLAYLIST_COMMAND_QUEUE;
2298 break;
2299 case 'D':
2300 current_command = PLAYLIST_COMMAND_DELETE;
2301 break;
2302 case 'S':
2303 current_command = PLAYLIST_COMMAND_SHUFFLE;
2304 break;
2305 case 'U':
2306 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2307 break;
2308 case 'R':
2309 current_command = PLAYLIST_COMMAND_RESET;
2310 break;
2311 case '#':
2312 current_command = PLAYLIST_COMMAND_COMMENT;
2313 break;
2314 default:
2315 result = -1;
2316 exit_loop = true;
2317 break;
2320 str_count = -1;
2321 str1 = NULL;
2322 str2 = NULL;
2323 str3 = NULL;
2325 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2327 /* all control file strings are separated with a colon.
2328 Replace the colon with 0 to get proper strings that can be
2329 used by commands above */
2330 if (*p == ':')
2332 *p = '\0';
2333 str_count++;
2335 if ((count+1) < nread)
2337 switch (str_count)
2339 case 0:
2340 str1 = p+1;
2341 break;
2342 case 1:
2343 str2 = p+1;
2344 break;
2345 case 2:
2346 str3 = p+1;
2347 break;
2348 default:
2349 /* allow last string to contain colons */
2350 *p = ':';
2351 break;
2358 if (result < 0)
2360 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2361 return result;
2364 if (!newline || (exit_loop && count<nread))
2366 if ((total_read + count) >= control_file_size)
2368 /* no newline at end of control file */
2369 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2370 return -1;
2373 /* We didn't end on a newline or we exited loop prematurely.
2374 Either way, re-read the remainder. */
2375 count = last_newline;
2376 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2379 total_read += count;
2381 if (first)
2382 /* still looking for header */
2383 nread = read(playlist->control_fd, buffer,
2384 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2385 else
2386 nread = read(playlist->control_fd, buffer, buflen);
2388 /* Terminate on EOF */
2389 if(nread <= 0)
2391 if (global_status.resume_seed >= 0)
2393 /* Apply shuffle command saved in settings */
2394 if (global_status.resume_seed == 0)
2395 sort_playlist(playlist, false, true);
2396 else
2398 if (!sorted)
2399 sort_playlist(playlist, false, false);
2401 randomise_playlist(playlist, global_status.resume_seed,
2402 false, true);
2406 playlist->first_index = global_status.resume_first_index;
2407 break;
2411 #ifdef HAVE_DIRCACHE
2412 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2413 #endif
2415 return 0;
2419 * Add track to in_ram playlist. Used when playing directories.
2421 int playlist_add(const char *filename)
2423 struct playlist_info* playlist = &current_playlist;
2424 int len = strlen(filename);
2426 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2427 (playlist->amount >= playlist->max_playlist_size))
2429 display_buffer_full();
2430 return -1;
2433 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2434 #ifdef HAVE_DIRCACHE
2435 playlist->filenames[playlist->amount] = NULL;
2436 #endif
2437 playlist->amount++;
2439 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2440 playlist->buffer_end_pos += len;
2441 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2443 return 0;
2446 /* shuffle newly created playlist using random seed. */
2447 int playlist_shuffle(int random_seed, int start_index)
2449 struct playlist_info* playlist = &current_playlist;
2451 unsigned int seek_pos = 0;
2452 bool start_current = false;
2454 if (start_index >= 0 && global_settings.play_selected)
2456 /* store the seek position before the shuffle */
2457 seek_pos = playlist->indices[start_index];
2458 playlist->index = global_status.resume_first_index =
2459 playlist->first_index = start_index;
2460 start_current = true;
2463 randomise_playlist(playlist, random_seed, start_current, true);
2465 return playlist->index;
2468 /* start playing current playlist at specified index/offset */
2469 int playlist_start(int start_index, int offset)
2471 struct playlist_info* playlist = &current_playlist;
2473 /* Cancel FM radio selection as previous music. For cases where we start
2474 playback without going to the WPS, such as playlist insert.. or
2475 playlist catalog. */
2476 previous_music_is_wps();
2478 playlist->index = start_index;
2480 #if CONFIG_CODEC != SWCODEC
2481 talk_buffer_steal(); /* will use the mp3 buffer */
2482 #endif
2484 playlist->started = true;
2485 sync_control(playlist, false);
2486 audio_play(offset);
2488 return 0;
2491 /* Returns false if 'steps' is out of bounds, else true */
2492 bool playlist_check(int steps)
2494 struct playlist_info* playlist = &current_playlist;
2496 /* always allow folder navigation */
2497 if (global_settings.next_folder && playlist->in_ram)
2498 return true;
2500 int index = get_next_index(playlist, steps, -1);
2502 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2503 index = get_next_index(playlist, steps, REPEAT_ALL);
2505 return (index >= 0);
2508 /* get trackname of track that is "steps" away from current playing track.
2509 NULL is used to identify end of playlist */
2510 char* playlist_peek(int steps)
2512 struct playlist_info* playlist = &current_playlist;
2513 int seek;
2514 char *temp_ptr;
2515 int index;
2516 bool control_file;
2518 index = get_next_index(playlist, steps, -1);
2519 if (index < 0)
2520 return NULL;
2522 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2523 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2525 if (get_filename(playlist, index, seek, control_file, now_playing,
2526 MAX_PATH+1) < 0)
2527 return NULL;
2529 temp_ptr = now_playing;
2531 if (!playlist->in_ram || control_file)
2533 /* remove bogus dirs from beginning of path
2534 (workaround for buggy playlist creation tools) */
2535 while (temp_ptr)
2537 if (file_exists(temp_ptr))
2538 break;
2540 temp_ptr = strchr(temp_ptr+1, '/');
2543 if (!temp_ptr)
2545 /* Even though this is an invalid file, we still need to pass a
2546 file name to the caller because NULL is used to indicate end
2547 of playlist */
2548 return now_playing;
2552 return temp_ptr;
2556 * Update indices as track has changed
2558 int playlist_next(int steps)
2560 struct playlist_info* playlist = &current_playlist;
2561 int index;
2563 if ( (steps > 0)
2564 #ifdef AB_REPEAT_ENABLE
2565 && (global_settings.repeat_mode != REPEAT_AB)
2566 #endif
2567 && (global_settings.repeat_mode != REPEAT_ONE) )
2569 int i, j;
2571 /* We need to delete all the queued songs */
2572 for (i=0, j=steps; i<j; i++)
2574 index = get_next_index(playlist, i, -1);
2576 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2578 remove_track_from_playlist(playlist, index, true);
2579 steps--; /* one less track */
2584 index = get_next_index(playlist, steps, -1);
2586 if (index < 0)
2588 /* end of playlist... or is it */
2589 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2590 playlist->amount > 1)
2592 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2593 playlist->first_index = global_status.resume_first_index = 0;
2594 sort_playlist(playlist, false, false);
2595 randomise_playlist(playlist, current_tick, false, true);
2596 #if CONFIG_CODEC != SWCODEC
2597 playlist_start(0, 0);
2598 #endif
2599 playlist->index = 0;
2600 index = 0;
2602 else if (playlist->in_ram && global_settings.next_folder)
2604 index = create_and_play_dir(steps, true);
2606 if (index >= 0)
2608 playlist->index = index;
2612 return index;
2615 playlist->index = index;
2617 if (playlist->last_insert_pos >= 0 && steps > 0)
2619 /* check to see if we've gone beyond the last inserted track */
2620 int cur = rotate_index(playlist, index);
2621 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2623 if (cur > last_pos)
2625 /* reset last inserted track */
2626 playlist->last_insert_pos = -1;
2628 if (playlist->control_fd >= 0)
2630 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2631 -1, -1, NULL, NULL, NULL);
2633 if (result < 0)
2634 return result;
2636 sync_control(playlist, false);
2641 return index;
2644 /* try playing next or previous folder */
2645 bool playlist_next_dir(int direction)
2647 /* not to mess up real playlists */
2648 if(!current_playlist.in_ram)
2649 return false;
2651 return create_and_play_dir(direction, false) >= 0;
2654 /* Get resume info for current playing song. If return value is -1 then
2655 settings shouldn't be saved. */
2656 int playlist_get_resume_info(int *resume_index)
2658 struct playlist_info* playlist = &current_playlist;
2660 *resume_index = playlist->index;
2662 return 0;
2665 /* Update resume info for current playing song. Returns -1 on error. */
2666 int playlist_update_resume_info(const struct mp3entry* id3)
2668 struct playlist_info* playlist = &current_playlist;
2670 if (id3)
2672 if (global_status.resume_index != playlist->index ||
2673 global_status.resume_offset != id3->offset)
2675 global_status.resume_index = playlist->index;
2676 global_status.resume_offset = id3->offset;
2677 status_save();
2680 else
2682 global_status.resume_index = -1;
2683 global_status.resume_offset = -1;
2684 status_save();
2687 return 0;
2690 /* Returns index of current playing track for display purposes. This value
2691 should not be used for resume purposes as it doesn't represent the actual
2692 index into the playlist */
2693 int playlist_get_display_index(void)
2695 struct playlist_info* playlist = &current_playlist;
2697 /* first_index should always be index 0 for display purposes */
2698 int index = rotate_index(playlist, playlist->index);
2700 return (index+1);
2703 /* returns number of tracks in current playlist */
2704 int playlist_amount(void)
2706 return playlist_amount_ex(NULL);
2710 * Create a new playlist If playlist is not NULL then we're loading a
2711 * playlist off disk for viewing/editing. The index_buffer is used to store
2712 * playlist indices (required for and only used if !current playlist). The
2713 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2715 int playlist_create_ex(struct playlist_info* playlist,
2716 const char* dir, const char* file,
2717 void* index_buffer, int index_buffer_size,
2718 void* temp_buffer, int temp_buffer_size)
2720 if (!playlist)
2721 playlist = &current_playlist;
2722 else
2724 /* Initialize playlist structure */
2725 int r = rand() % 10;
2726 playlist->current = false;
2728 /* Use random name for control file */
2729 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2730 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2731 playlist->fd = -1;
2732 playlist->control_fd = -1;
2734 if (index_buffer)
2736 int num_indices = index_buffer_size / sizeof(int);
2738 #ifdef HAVE_DIRCACHE
2739 num_indices /= 2;
2740 #endif
2741 if (num_indices > global_settings.max_files_in_playlist)
2742 num_indices = global_settings.max_files_in_playlist;
2744 playlist->max_playlist_size = num_indices;
2745 playlist->indices = index_buffer;
2746 #ifdef HAVE_DIRCACHE
2747 playlist->filenames = (const struct dircache_entry **)
2748 &playlist->indices[num_indices];
2749 #endif
2751 else
2753 playlist->max_playlist_size = current_playlist.max_playlist_size;
2754 playlist->indices = current_playlist.indices;
2755 #ifdef HAVE_DIRCACHE
2756 playlist->filenames = current_playlist.filenames;
2757 #endif
2760 playlist->buffer_size = 0;
2761 playlist->buffer = NULL;
2762 mutex_init(&playlist->control_mutex);
2765 new_playlist(playlist, dir, file);
2767 if (file)
2768 /* load the playlist file */
2769 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2771 return 0;
2775 * Set the specified playlist as the current.
2776 * NOTE: You will get undefined behaviour if something is already playing so
2777 * remember to stop before calling this. Also, this call will
2778 * effectively close your playlist, making it unusable.
2780 int playlist_set_current(struct playlist_info* playlist)
2782 if (!playlist || (check_control(playlist) < 0))
2783 return -1;
2785 empty_playlist(&current_playlist, false);
2787 strncpy(current_playlist.filename, playlist->filename,
2788 sizeof(current_playlist.filename));
2790 current_playlist.utf8 = playlist->utf8;
2791 current_playlist.fd = playlist->fd;
2793 close(playlist->control_fd);
2794 close(current_playlist.control_fd);
2795 remove(current_playlist.control_filename);
2796 if (rename(playlist->control_filename,
2797 current_playlist.control_filename) < 0)
2798 return -1;
2799 current_playlist.control_fd = open(current_playlist.control_filename,
2800 O_RDWR);
2801 if (current_playlist.control_fd < 0)
2802 return -1;
2803 current_playlist.control_created = true;
2805 current_playlist.dirlen = playlist->dirlen;
2807 if (playlist->indices && playlist->indices != current_playlist.indices)
2809 memcpy(current_playlist.indices, playlist->indices,
2810 playlist->max_playlist_size*sizeof(int));
2811 #ifdef HAVE_DIRCACHE
2812 memcpy(current_playlist.filenames, playlist->filenames,
2813 playlist->max_playlist_size*sizeof(int));
2814 #endif
2817 current_playlist.first_index = playlist->first_index;
2818 current_playlist.amount = playlist->amount;
2819 current_playlist.last_insert_pos = playlist->last_insert_pos;
2820 current_playlist.seed = playlist->seed;
2821 current_playlist.shuffle_modified = playlist->shuffle_modified;
2822 current_playlist.deleted = playlist->deleted;
2823 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2825 memcpy(current_playlist.control_cache, playlist->control_cache,
2826 sizeof(current_playlist.control_cache));
2827 current_playlist.num_cached = playlist->num_cached;
2828 current_playlist.pending_control_sync = playlist->pending_control_sync;
2830 return 0;
2834 * Close files and delete control file for non-current playlist.
2836 void playlist_close(struct playlist_info* playlist)
2838 if (!playlist)
2839 return;
2841 if (playlist->fd >= 0)
2842 close(playlist->fd);
2844 if (playlist->control_fd >= 0)
2845 close(playlist->control_fd);
2847 if (playlist->control_created)
2848 remove(playlist->control_filename);
2851 void playlist_sync(struct playlist_info* playlist)
2853 if (!playlist)
2854 playlist = &current_playlist;
2856 sync_control(playlist, false);
2857 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2858 audio_flush_and_reload_tracks();
2860 #ifdef HAVE_DIRCACHE
2861 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2862 #endif
2866 * Insert track into playlist at specified position (or one of the special
2867 * positions). Returns position where track was inserted or -1 if error.
2869 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2870 int position, bool queue, bool sync)
2872 int result;
2874 if (!playlist)
2875 playlist = &current_playlist;
2877 if (check_control(playlist) < 0)
2879 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2880 return -1;
2883 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2885 /* Check if we want manually sync later. For example when adding
2886 * bunch of files from tagcache, syncing after every file wouldn't be
2887 * a good thing to do. */
2888 if (sync && result >= 0)
2889 playlist_sync(playlist);
2891 return result;
2895 * Insert all tracks from specified directory into playlist.
2897 int playlist_insert_directory(struct playlist_info* playlist,
2898 const char *dirname, int position, bool queue,
2899 bool recurse)
2901 int result;
2902 unsigned char *count_str;
2903 struct directory_search_context context;
2905 if (!playlist)
2906 playlist = &current_playlist;
2908 if (check_control(playlist) < 0)
2910 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2911 return -1;
2914 if (position == PLAYLIST_REPLACE)
2916 if (playlist_remove_all_tracks(playlist) == 0)
2917 position = PLAYLIST_INSERT_LAST;
2918 else
2919 return -1;
2922 if (queue)
2923 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2924 else
2925 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2927 display_playlist_count(0, count_str, false);
2929 context.playlist = playlist;
2930 context.position = position;
2931 context.queue = queue;
2932 context.count = 0;
2934 cpu_boost(true);
2936 result = playlist_directory_tracksearch(dirname, recurse,
2937 directory_search_callback, &context);
2939 sync_control(playlist, false);
2941 cpu_boost(false);
2943 display_playlist_count(context.count, count_str, true);
2945 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2946 audio_flush_and_reload_tracks();
2948 #ifdef HAVE_DIRCACHE
2949 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2950 #endif
2952 return result;
2956 * Insert all tracks from specified playlist into dynamic playlist.
2958 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
2959 int position, bool queue)
2961 int fd;
2962 int max;
2963 char *temp_ptr;
2964 const char *dir;
2965 unsigned char *count_str;
2966 char temp_buf[MAX_PATH+1];
2967 char trackname[MAX_PATH+1];
2968 int count = 0;
2969 int result = 0;
2970 bool utf8 = is_m3u8(filename);
2972 if (!playlist)
2973 playlist = &current_playlist;
2975 if (check_control(playlist) < 0)
2977 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2978 return -1;
2981 fd = open(filename, O_RDONLY);
2982 if (fd < 0)
2984 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2985 return -1;
2988 /* we need the directory name for formatting purposes */
2989 dir = filename;
2991 temp_ptr = strrchr(filename+1,'/');
2992 if (temp_ptr)
2993 *temp_ptr = 0;
2994 else
2995 dir = "/";
2997 if (queue)
2998 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2999 else
3000 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
3002 display_playlist_count(count, count_str, false);
3004 if (position == PLAYLIST_REPLACE)
3006 if (playlist_remove_all_tracks(playlist) == 0)
3007 position = PLAYLIST_INSERT_LAST;
3008 else return -1;
3011 cpu_boost(true);
3013 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3015 /* user abort */
3016 if (action_userabort(TIMEOUT_NOBLOCK))
3017 break;
3019 if (count == 0 && is_utf8_bom(temp_buf, max))
3021 max -= BOM_SIZE;
3022 memmove(temp_buf, temp_buf + BOM_SIZE, max);
3025 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3027 int insert_pos;
3029 if (!utf8)
3031 /* Use trackname as a temporay buffer. Note that trackname must
3032 * be as large as temp_buf.
3034 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3037 /* we need to format so that relative paths are correctly
3038 handled */
3039 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3040 dir) < 0)
3042 result = -1;
3043 break;
3046 insert_pos = add_track_to_playlist(playlist, trackname, position,
3047 queue, -1);
3049 if (insert_pos < 0)
3051 result = -1;
3052 break;
3055 /* Make sure tracks are inserted in correct order if user
3056 requests INSERT_FIRST */
3057 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3058 position = insert_pos + 1;
3060 count++;
3062 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3064 display_playlist_count(count, count_str, false);
3066 if (count == PLAYLIST_DISPLAY_COUNT &&
3067 (audio_status() & AUDIO_STATUS_PLAY) &&
3068 playlist->started)
3069 audio_flush_and_reload_tracks();
3073 /* let the other threads work */
3074 yield();
3077 close(fd);
3079 if (temp_ptr)
3080 *temp_ptr = '/';
3082 sync_control(playlist, false);
3084 cpu_boost(false);
3086 display_playlist_count(count, count_str, true);
3088 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3089 audio_flush_and_reload_tracks();
3091 #ifdef HAVE_DIRCACHE
3092 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3093 #endif
3095 return result;
3099 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3100 * we want to delete the current playing track.
3102 int playlist_delete(struct playlist_info* playlist, int index)
3104 int result = 0;
3106 if (!playlist)
3107 playlist = &current_playlist;
3109 if (check_control(playlist) < 0)
3111 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3112 return -1;
3115 if (index == PLAYLIST_DELETE_CURRENT)
3116 index = playlist->index;
3118 result = remove_track_from_playlist(playlist, index, true);
3120 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3121 playlist->started)
3122 audio_flush_and_reload_tracks();
3124 return result;
3128 * Move track at index to new_index. Tracks between the two are shifted
3129 * appropriately. Returns 0 on success and -1 on failure.
3131 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3133 int result;
3134 int seek;
3135 bool control_file;
3136 bool queue;
3137 bool current = false;
3138 int r;
3139 char filename[MAX_PATH];
3141 if (!playlist)
3142 playlist = &current_playlist;
3144 if (check_control(playlist) < 0)
3146 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3147 return -1;
3150 if (index == new_index)
3151 return -1;
3153 if (index == playlist->index)
3154 /* Moving the current track */
3155 current = true;
3157 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3158 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3159 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3161 if (get_filename(playlist, index, seek, control_file, filename,
3162 sizeof(filename)) < 0)
3163 return -1;
3165 /* Delete track from original position */
3166 result = remove_track_from_playlist(playlist, index, true);
3168 if (result != -1)
3170 /* We want to insert the track at the position that was specified by
3171 new_index. This may be different then new_index because of the
3172 shifting that occurred after the delete */
3173 r = rotate_index(playlist, new_index);
3175 if (r == 0)
3176 /* First index */
3177 new_index = PLAYLIST_PREPEND;
3178 else if (r == playlist->amount)
3179 /* Append */
3180 new_index = PLAYLIST_INSERT_LAST;
3181 else
3182 /* Calculate index of desired position */
3183 new_index = (r+playlist->first_index)%playlist->amount;
3185 result = add_track_to_playlist(playlist, filename, new_index, queue,
3186 -1);
3188 if (result != -1)
3190 if (current)
3192 /* Moved the current track */
3193 switch (new_index)
3195 case PLAYLIST_PREPEND:
3196 playlist->index = playlist->first_index;
3197 break;
3198 case PLAYLIST_INSERT_LAST:
3199 playlist->index = playlist->first_index - 1;
3200 if (playlist->index < 0)
3201 playlist->index += playlist->amount;
3202 break;
3203 default:
3204 playlist->index = new_index;
3205 break;
3209 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3210 audio_flush_and_reload_tracks();
3214 #ifdef HAVE_DIRCACHE
3215 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3216 #endif
3218 return result;
3221 /* shuffle currently playing playlist */
3222 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3223 bool start_current)
3225 int result;
3227 if (!playlist)
3228 playlist = &current_playlist;
3230 check_control(playlist);
3232 result = randomise_playlist(playlist, seed, start_current, true);
3234 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3235 playlist->started)
3236 audio_flush_and_reload_tracks();
3238 return result;
3241 /* sort currently playing playlist */
3242 int playlist_sort(struct playlist_info* playlist, bool start_current)
3244 int result;
3246 if (!playlist)
3247 playlist = &current_playlist;
3249 check_control(playlist);
3251 result = sort_playlist(playlist, start_current, true);
3253 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3254 playlist->started)
3255 audio_flush_and_reload_tracks();
3257 return result;
3260 /* returns true if playlist has been modified */
3261 bool playlist_modified(const struct playlist_info* playlist)
3263 if (!playlist)
3264 playlist = &current_playlist;
3266 if (playlist->shuffle_modified ||
3267 playlist->deleted ||
3268 playlist->num_inserted_tracks > 0)
3269 return true;
3271 return false;
3274 /* returns index of first track in playlist */
3275 int playlist_get_first_index(const struct playlist_info* playlist)
3277 if (!playlist)
3278 playlist = &current_playlist;
3280 return playlist->first_index;
3283 /* returns shuffle seed of playlist */
3284 int playlist_get_seed(const struct playlist_info* playlist)
3286 if (!playlist)
3287 playlist = &current_playlist;
3289 return playlist->seed;
3292 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3293 int playlist_amount_ex(const struct playlist_info* playlist)
3295 if (!playlist)
3296 playlist = &current_playlist;
3298 return playlist->amount;
3301 /* returns full path of playlist (minus extension) */
3302 char *playlist_name(const struct playlist_info* playlist, char *buf,
3303 int buf_size)
3305 char *sep;
3307 if (!playlist)
3308 playlist = &current_playlist;
3310 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3312 if (!buf[0])
3313 return NULL;
3315 /* Remove extension */
3316 sep = strrchr(buf, '.');
3317 if (sep)
3318 *sep = 0;
3320 return buf;
3323 /* returns the playlist filename */
3324 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3325 int buf_size)
3327 if (!playlist)
3328 playlist = &current_playlist;
3330 snprintf(buf, buf_size, "%s", playlist->filename);
3332 if (!buf[0])
3333 return NULL;
3335 return buf;
3338 /* Fills info structure with information about track at specified index.
3339 Returns 0 on success and -1 on failure */
3340 int playlist_get_track_info(struct playlist_info* playlist, int index,
3341 struct playlist_track_info* info)
3343 int seek;
3344 bool control_file;
3346 if (!playlist)
3347 playlist = &current_playlist;
3349 if (index < 0 || index >= playlist->amount)
3350 return -1;
3352 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3353 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3355 if (get_filename(playlist, index, seek, control_file, info->filename,
3356 sizeof(info->filename)) < 0)
3357 return -1;
3359 info->attr = 0;
3361 if (control_file)
3363 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3364 info->attr |= PLAYLIST_ATTR_QUEUED;
3365 else
3366 info->attr |= PLAYLIST_ATTR_INSERTED;
3370 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3371 info->attr |= PLAYLIST_ATTR_SKIPPED;
3373 info->index = index;
3374 info->display_index = rotate_index(playlist, index) + 1;
3376 return 0;
3379 /* save the current dynamic playlist to specified file */
3380 int playlist_save(struct playlist_info* playlist, char *filename)
3382 int fd;
3383 int i, index;
3384 int count = 0;
3385 char path[MAX_PATH+1];
3386 char tmp_buf[MAX_PATH+1];
3387 int result = 0;
3388 bool overwrite_current = false;
3389 int* index_buf = NULL;
3391 if (!playlist)
3392 playlist = &current_playlist;
3394 if (playlist->amount <= 0)
3395 return -1;
3397 /* use current working directory as base for pathname */
3398 if (format_track_path(path, filename, sizeof(tmp_buf),
3399 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3400 return -1;
3402 if (!strncmp(playlist->filename, path, strlen(path)))
3404 /* Attempting to overwrite current playlist file.*/
3406 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3408 /* not enough buffer space to store updated indices */
3409 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3410 return -1;
3413 /* in_ram buffer is unused for m3u files so we'll use for storing
3414 updated indices */
3415 index_buf = (int*)playlist->buffer;
3417 /* use temporary pathname */
3418 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3419 overwrite_current = true;
3422 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3423 if (fd < 0)
3425 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3426 return -1;
3429 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3431 cpu_boost(true);
3433 index = playlist->first_index;
3434 for (i=0; i<playlist->amount; i++)
3436 bool control_file;
3437 bool queue;
3438 int seek;
3440 /* user abort */
3441 if (action_userabort(TIMEOUT_NOBLOCK))
3443 result = -1;
3444 break;
3447 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3448 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3449 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3451 /* Don't save queued files */
3452 if (!queue)
3454 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3455 MAX_PATH+1) < 0)
3457 result = -1;
3458 break;
3461 if (overwrite_current)
3462 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3464 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3466 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3467 result = -1;
3468 break;
3471 count++;
3473 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3474 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3475 false);
3477 yield();
3480 index = (index+1)%playlist->amount;
3483 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3485 close(fd);
3487 if (overwrite_current && result >= 0)
3489 result = -1;
3491 mutex_lock(&playlist->control_mutex);
3493 /* Replace the current playlist with the new one and update indices */
3494 close(playlist->fd);
3495 if (remove(playlist->filename) >= 0)
3497 if (rename(path, playlist->filename) >= 0)
3499 playlist->fd = open(playlist->filename, O_RDONLY);
3500 if (playlist->fd >= 0)
3502 index = playlist->first_index;
3503 for (i=0, count=0; i<playlist->amount; i++)
3505 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3507 playlist->indices[index] = index_buf[count];
3508 count++;
3510 index = (index+1)%playlist->amount;
3513 /* we need to recreate control because inserted tracks are
3514 now part of the playlist and shuffle has been
3515 invalidated */
3516 result = recreate_control(playlist);
3521 mutex_unlock(&playlist->control_mutex);
3525 cpu_boost(false);
3527 return result;
3531 * Search specified directory for tracks and notify via callback. May be
3532 * called recursively.
3534 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3535 int (*callback)(char*, void*),
3536 void* context)
3538 char buf[MAX_PATH+1];
3539 int result = 0;
3540 int num_files = 0;
3541 int i;
3542 struct entry *files;
3543 struct tree_context* tc = tree_get_context();
3544 int old_dirfilter = *(tc->dirfilter);
3546 if (!callback)
3547 return -1;
3549 /* use the tree browser dircache to load files */
3550 *(tc->dirfilter) = SHOW_ALL;
3552 if (ft_load(tc, dirname) < 0)
3554 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3555 *(tc->dirfilter) = old_dirfilter;
3556 return -1;
3559 files = (struct entry*) tc->dircache;
3560 num_files = tc->filesindir;
3562 /* we've overwritten the dircache so tree browser will need to be
3563 reloaded */
3564 reload_directory();
3566 for (i=0; i<num_files; i++)
3568 /* user abort */
3569 if (action_userabort(TIMEOUT_NOBLOCK))
3571 result = -1;
3572 break;
3575 if (files[i].attr & ATTR_DIRECTORY)
3577 if (recurse)
3579 /* recursively add directories */
3580 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3581 result = playlist_directory_tracksearch(buf, recurse,
3582 callback, context);
3583 if (result < 0)
3584 break;
3586 /* we now need to reload our current directory */
3587 if(ft_load(tc, dirname) < 0)
3589 result = -1;
3590 break;
3593 files = (struct entry*) tc->dircache;
3594 num_files = tc->filesindir;
3595 if (!num_files)
3597 result = -1;
3598 break;
3601 else
3602 continue;
3604 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3606 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3608 if (callback(buf, context) != 0)
3610 result = -1;
3611 break;
3614 /* let the other threads work */
3615 yield();
3619 /* restore dirfilter */
3620 *(tc->dirfilter) = old_dirfilter;
3622 return result;