D2: Enable plugin building (using initial keymaps from FS#8708 by Andreas Mueller...
[kugel-rb.git] / apps / playlist.c
blobb68a47e75ddc5c8c626a1b21837239f8cc9db8b3
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 "file.h"
74 #include "action.h"
75 #include "dir.h"
76 #include "sprintf.h"
77 #include "debug.h"
78 #include "audio.h"
79 #include "lcd.h"
80 #include "kernel.h"
81 #include "settings.h"
82 #include "status.h"
83 #include "applimits.h"
84 #include "screens.h"
85 #include "buffer.h"
86 #include "atoi.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 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 empty_playlist(playlist, false);
322 if (!file)
324 file = "";
326 if (dir && playlist->current) /* !current cannot be in_ram */
327 playlist->in_ram = true;
328 else
329 dir = ""; /* empty playlist */
332 update_playlist_filename(playlist, dir, file);
334 if (playlist->control_fd >= 0)
336 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
337 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
338 sync_control(playlist, false);
343 * create control file for playlist
345 static void create_control(struct playlist_info* playlist)
347 playlist->control_fd = open(playlist->control_filename,
348 O_CREAT|O_RDWR|O_TRUNC);
349 if (playlist->control_fd < 0)
351 if (check_rockboxdir())
353 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
354 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
355 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
356 playlist->control_fd);
358 playlist->control_created = false;
360 else
362 playlist->control_created = true;
367 * validate the control file. This may include creating/initializing it if
368 * necessary;
370 static int check_control(struct playlist_info* playlist)
372 if (!playlist->control_created)
374 create_control(playlist);
376 if (playlist->control_fd >= 0)
378 char* dir = playlist->filename;
379 char* file = playlist->filename+playlist->dirlen;
380 char c = playlist->filename[playlist->dirlen-1];
382 playlist->filename[playlist->dirlen-1] = '\0';
384 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
385 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
386 sync_control(playlist, false);
387 playlist->filename[playlist->dirlen-1] = c;
391 if (playlist->control_fd < 0)
392 return -1;
394 return 0;
398 * recreate the control file based on current playlist entries
400 static int recreate_control(struct playlist_info* playlist)
402 char temp_file[MAX_PATH+1];
403 int temp_fd = -1;
404 int i;
405 int result = 0;
407 if(playlist->control_fd >= 0)
409 char* dir = playlist->filename;
410 char* file = playlist->filename+playlist->dirlen;
411 char c = playlist->filename[playlist->dirlen-1];
413 close(playlist->control_fd);
415 snprintf(temp_file, sizeof(temp_file), "%s_temp",
416 playlist->control_filename);
418 if (rename(playlist->control_filename, temp_file) < 0)
419 return -1;
421 temp_fd = open(temp_file, O_RDONLY);
422 if (temp_fd < 0)
423 return -1;
425 playlist->control_fd = open(playlist->control_filename,
426 O_CREAT|O_RDWR|O_TRUNC);
427 if (playlist->control_fd < 0)
428 return -1;
430 playlist->filename[playlist->dirlen-1] = '\0';
432 /* cannot call update_control() because of mutex */
433 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
434 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
436 playlist->filename[playlist->dirlen-1] = c;
438 if (result < 0)
440 close(temp_fd);
441 return result;
445 playlist->seed = 0;
446 playlist->shuffle_modified = false;
447 playlist->deleted = false;
448 playlist->num_inserted_tracks = 0;
450 if (playlist->current)
452 global_status.resume_seed = -1;
453 status_save();
456 for (i=0; i<playlist->amount; i++)
458 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
460 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
461 char inserted_file[MAX_PATH+1];
463 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
464 SEEK_SET);
465 read_line(temp_fd, inserted_file, sizeof(inserted_file));
467 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
468 queue?'Q':'A', i, playlist->last_insert_pos);
469 if (result > 0)
471 /* save the position in file where name is written */
472 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
474 result = fdprintf(playlist->control_fd, "%s\n",
475 inserted_file);
477 playlist->indices[i] =
478 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
481 if (result < 0)
482 break;
484 playlist->num_inserted_tracks++;
488 close(temp_fd);
489 remove(temp_file);
490 fsync(playlist->control_fd);
492 if (result < 0)
493 return result;
495 return 0;
499 * store directory and name of playlist file
501 static void update_playlist_filename(struct playlist_info* playlist,
502 const char *dir, const char *file)
504 char *sep="";
505 int dirlen = strlen(dir);
507 playlist->utf8 = is_m3u8(file);
509 /* If the dir does not end in trailing slash, we use a separator.
510 Otherwise we don't. */
511 if('/' != dir[dirlen-1])
513 sep="/";
514 dirlen++;
517 playlist->dirlen = dirlen;
519 snprintf(playlist->filename, sizeof(playlist->filename),
520 "%s%s%s", dir, sep, file);
524 * calculate track offsets within a playlist file
526 static int add_indices_to_playlist(struct playlist_info* playlist,
527 char* buffer, size_t buflen)
529 unsigned int nread;
530 unsigned int i = 0;
531 unsigned int count = 0;
532 bool store_index;
533 unsigned char *p;
534 int result = 0;
536 if(-1 == playlist->fd)
537 playlist->fd = open(playlist->filename, O_RDONLY);
538 if(playlist->fd < 0)
539 return -1; /* failure */
541 #ifdef HAVE_LCD_BITMAP
542 if(global_settings.statusbar)
543 lcd_setmargins(0, STATUSBAR_HEIGHT);
544 else
545 lcd_setmargins(0, 0);
546 #endif
547 gui_syncsplash(0, ID2P(LANG_WAIT));
549 if (!buffer)
551 /* use mp3 buffer for maximum load speed */
552 audio_stop();
553 #if CONFIG_CODEC != SWCODEC
554 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
555 buflen = (audiobufend - audiobuf);
556 buffer = (char *)audiobuf;
557 #else
558 buffer = (char *)audio_get_buffer(false, &buflen);
559 #endif
562 store_index = true;
564 while(1)
566 nread = read(playlist->fd, buffer, buflen);
567 /* Terminate on EOF */
568 if(nread <= 0)
569 break;
571 p = (unsigned char *)buffer;
573 /* utf8 BOM at beginning of file? */
574 if(i == 0 && is_utf8_bom(p, nread)) {
575 nread -= BOM_SIZE;
576 p += BOM_SIZE;
577 i += BOM_SIZE;
578 playlist->utf8 = true; /* Override any earlier indication. */
581 for(count=0; count < nread; count++,p++) {
583 /* Are we on a new line? */
584 if((*p == '\n') || (*p == '\r'))
586 store_index = true;
588 else if(store_index)
590 store_index = false;
592 if(*p != '#')
594 if ( playlist->amount >= playlist->max_playlist_size ) {
595 display_buffer_full();
596 result = -1;
597 goto exit;
600 /* Store a new entry */
601 playlist->indices[ playlist->amount ] = i+count;
602 #ifdef HAVE_DIRCACHE
603 if (playlist->filenames)
604 playlist->filenames[ playlist->amount ] = NULL;
605 #endif
606 playlist->amount++;
611 i+= count;
614 exit:
615 #ifdef HAVE_DIRCACHE
616 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
617 #endif
619 return result;
623 * Utility function to create a new playlist, fill it with the next or
624 * previous directory, shuffle it if needed, and start playback.
625 * If play_last is true and direction zero or negative, start playing
626 * the last file in the directory, otherwise start playing the first.
628 static int create_and_play_dir(int direction, bool play_last)
630 char dir[MAX_PATH + 1];
631 int res;
632 int index = -1;
634 if(direction > 0)
635 res = get_next_directory(dir);
636 else
637 res = get_previous_directory(dir);
639 if (!res)
641 if (playlist_create(dir, NULL) != -1)
643 ft_build_playlist(tree_get_context(), 0);
645 if (global_settings.playlist_shuffle)
646 playlist_shuffle(current_tick, -1);
648 if (play_last && direction <= 0)
649 index = current_playlist.amount - 1;
650 else
651 index = 0;
653 #if (CONFIG_CODEC != SWCODEC)
654 playlist_start(index, 0);
655 #endif
658 /* we've overwritten the dircache when getting the next/previous dir,
659 so the tree browser context will need to be reloaded */
660 reload_directory();
663 return index;
667 * Removes all tracks, from the playlist, leaving the presently playing
668 * track queued.
670 int remove_all_tracks(struct playlist_info *playlist)
672 int result;
674 if (playlist == NULL)
675 playlist = &current_playlist;
677 while (playlist->index > 0)
678 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
679 return result;
681 while (playlist->amount > 1)
682 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
683 return result;
685 if (playlist->amount == 1) {
686 playlist->indices[0] |= PLAYLIST_QUEUED;
689 return 0;
694 * Add track to playlist at specified position. There are five special
695 * positions that can be specified:
696 * PLAYLIST_PREPEND - Add track at beginning of playlist
697 * PLAYLIST_INSERT - Add track after current song. NOTE: If
698 * there are already inserted tracks then track
699 * is added to the end of the insertion list
700 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
701 * matter what other tracks have been inserted
702 * PLAYLIST_INSERT_LAST - Add track to end of playlist
703 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
704 * current playing track and end of playlist
705 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
706 * and inster this track at the end.
708 static int add_track_to_playlist(struct playlist_info* playlist,
709 const char *filename, int position,
710 bool queue, int seek_pos)
712 int insert_position, orig_position;
713 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
714 int i;
716 insert_position = orig_position = position;
718 if (playlist->amount >= playlist->max_playlist_size)
720 display_buffer_full();
721 return -1;
724 switch (position)
726 case PLAYLIST_PREPEND:
727 position = insert_position = playlist->first_index;
728 break;
729 case PLAYLIST_INSERT:
730 /* if there are already inserted tracks then add track to end of
731 insertion list else add after current playing track */
732 if (playlist->last_insert_pos >= 0 &&
733 playlist->last_insert_pos < playlist->amount &&
734 (playlist->indices[playlist->last_insert_pos]&
735 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
736 position = insert_position = playlist->last_insert_pos+1;
737 else if (playlist->amount > 0)
738 position = insert_position = playlist->index + 1;
739 else
740 position = insert_position = 0;
742 if (playlist->started)
743 playlist->last_insert_pos = position;
744 break;
745 case PLAYLIST_INSERT_FIRST:
746 if (playlist->amount > 0)
747 position = insert_position = playlist->index + 1;
748 else
749 position = insert_position = 0;
751 if (playlist->last_insert_pos < 0 && playlist->started)
752 playlist->last_insert_pos = position;
753 break;
754 case PLAYLIST_INSERT_LAST:
755 if (playlist->first_index > 0)
756 position = insert_position = playlist->first_index;
757 else
758 position = insert_position = playlist->amount;
759 break;
760 case PLAYLIST_INSERT_SHUFFLED:
762 if (playlist->started)
764 int offset;
765 int n = playlist->amount -
766 rotate_index(playlist, playlist->index);
768 if (n > 0)
769 offset = rand() % n;
770 else
771 offset = 0;
773 position = playlist->index + offset + 1;
774 if (position >= playlist->amount)
775 position -= playlist->amount;
777 insert_position = position;
779 else
780 position = insert_position = (rand() % (playlist->amount+1));
781 break;
783 case PLAYLIST_REPLACE:
784 if (remove_all_tracks(playlist) < 0)
785 return -1;
787 position = insert_position = playlist->index + 1;
788 break;
791 if (queue)
792 flags |= PLAYLIST_QUEUED;
794 /* shift indices so that track can be added */
795 for (i=playlist->amount; i>insert_position; i--)
797 playlist->indices[i] = playlist->indices[i-1];
798 #ifdef HAVE_DIRCACHE
799 if (playlist->filenames)
800 playlist->filenames[i] = playlist->filenames[i-1];
801 #endif
804 /* update stored indices if needed */
805 if (playlist->amount > 0 && insert_position <= playlist->index &&
806 playlist->started)
807 playlist->index++;
809 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
810 orig_position != PLAYLIST_PREPEND && playlist->started)
812 playlist->first_index++;
814 if (seek_pos < 0 && playlist->current)
816 global_status.resume_first_index = playlist->first_index;
817 status_save();
821 if (insert_position < playlist->last_insert_pos ||
822 (insert_position == playlist->last_insert_pos && position < 0))
823 playlist->last_insert_pos++;
825 if (seek_pos < 0 && playlist->control_fd >= 0)
827 int result = update_control(playlist,
828 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
829 playlist->last_insert_pos, filename, NULL, &seek_pos);
831 if (result < 0)
832 return result;
835 playlist->indices[insert_position] = flags | seek_pos;
837 #ifdef HAVE_DIRCACHE
838 if (playlist->filenames)
839 playlist->filenames[insert_position] = NULL;
840 #endif
842 playlist->amount++;
843 playlist->num_inserted_tracks++;
845 return insert_position;
849 * Callback for playlist_directory_tracksearch to insert track into
850 * playlist.
852 static int directory_search_callback(char* filename, void* context)
854 struct directory_search_context* c =
855 (struct directory_search_context*) context;
856 int insert_pos;
858 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
859 c->queue, -1);
861 if (insert_pos < 0)
862 return -1;
864 (c->count)++;
866 /* Make sure tracks are inserted in correct order if user requests
867 INSERT_FIRST */
868 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
869 c->position = insert_pos + 1;
871 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
873 unsigned char* count_str;
875 if (c->queue)
876 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
877 else
878 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
880 display_playlist_count(c->count, count_str, false);
882 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
883 (audio_status() & AUDIO_STATUS_PLAY) &&
884 c->playlist->started)
885 audio_flush_and_reload_tracks();
888 return 0;
892 * remove track at specified position
894 static int remove_track_from_playlist(struct playlist_info* playlist,
895 int position, bool write)
897 int i;
898 bool inserted;
900 if (playlist->amount <= 0)
901 return -1;
903 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
905 /* shift indices now that track has been removed */
906 for (i=position; i<playlist->amount; i++)
908 playlist->indices[i] = playlist->indices[i+1];
909 #ifdef HAVE_DIRCACHE
910 if (playlist->filenames)
911 playlist->filenames[i] = playlist->filenames[i+1];
912 #endif
915 playlist->amount--;
917 if (inserted)
918 playlist->num_inserted_tracks--;
919 else
920 playlist->deleted = true;
922 /* update stored indices if needed */
923 if (position < playlist->index)
924 playlist->index--;
926 if (position < playlist->first_index)
928 playlist->first_index--;
930 if (write)
932 global_status.resume_first_index = playlist->first_index;
933 status_save();
937 if (position <= playlist->last_insert_pos)
938 playlist->last_insert_pos--;
940 if (write && playlist->control_fd >= 0)
942 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
943 position, -1, NULL, NULL, NULL);
945 if (result < 0)
946 return result;
948 sync_control(playlist, false);
951 return 0;
955 * randomly rearrange the array of indices for the playlist. If start_current
956 * is true then update the index to the new index of the current playing track
958 static int randomise_playlist(struct playlist_info* playlist,
959 unsigned int seed, bool start_current,
960 bool write)
962 int count;
963 int candidate;
964 long store;
965 unsigned int current = playlist->indices[playlist->index];
967 /* seed 0 is used to identify sorted playlist for resume purposes */
968 if (seed == 0)
969 seed = 1;
971 /* seed with the given seed */
972 srand(seed);
974 /* randomise entire indices list */
975 for(count = playlist->amount - 1; count >= 0; count--)
977 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
978 candidate = rand() % (count + 1);
980 /* now swap the values at the 'count' and 'candidate' positions */
981 store = playlist->indices[candidate];
982 playlist->indices[candidate] = playlist->indices[count];
983 playlist->indices[count] = store;
984 #ifdef HAVE_DIRCACHE
985 if (playlist->filenames)
987 store = (long)playlist->filenames[candidate];
988 playlist->filenames[candidate] = playlist->filenames[count];
989 playlist->filenames[count] = (struct dircache_entry *)store;
991 #endif
994 if (start_current)
995 find_and_set_playlist_index(playlist, current);
997 /* indices have been moved so last insert position is no longer valid */
998 playlist->last_insert_pos = -1;
1000 playlist->seed = seed;
1001 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
1002 playlist->shuffle_modified = true;
1004 if (write)
1006 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
1007 playlist->first_index, NULL, NULL, NULL);
1008 global_status.resume_seed = seed;
1009 status_save();
1012 return 0;
1016 * Sort the array of indices for the playlist. If start_current is true then
1017 * set the index to the new index of the current song.
1019 static int sort_playlist(struct playlist_info* playlist, bool start_current,
1020 bool write)
1022 unsigned int current = playlist->indices[playlist->index];
1024 if (playlist->amount > 0)
1025 qsort(playlist->indices, playlist->amount,
1026 sizeof(playlist->indices[0]), compare);
1028 #ifdef HAVE_DIRCACHE
1029 /** We need to re-check the song names from disk because qsort can't
1030 * sort two arrays at once :/
1031 * FIXME: Please implement a better way to do this. */
1032 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
1033 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1034 #endif
1036 if (start_current)
1037 find_and_set_playlist_index(playlist, current);
1039 /* indices have been moved so last insert position is no longer valid */
1040 playlist->last_insert_pos = -1;
1042 if (!playlist->num_inserted_tracks && !playlist->deleted)
1043 playlist->shuffle_modified = false;
1044 if (write && playlist->control_fd >= 0)
1046 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1047 playlist->first_index, -1, NULL, NULL, NULL);
1048 global_status.resume_seed = 0;
1049 status_save();
1052 return 0;
1055 /* Calculate how many steps we have to really step when skipping entries
1056 * marked as bad.
1058 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1060 int i, count, direction;
1061 int index;
1062 int stepped_count = 0;
1064 if (steps < 0)
1066 direction = -1;
1067 count = -steps;
1069 else
1071 direction = 1;
1072 count = steps;
1075 index = playlist->index;
1076 i = 0;
1077 do {
1078 /* Boundary check */
1079 if (index < 0)
1080 index += playlist->amount;
1081 if (index >= playlist->amount)
1082 index -= playlist->amount;
1084 /* Check if we found a bad entry. */
1085 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1087 steps += direction;
1088 /* Are all entries bad? */
1089 if (stepped_count++ > playlist->amount)
1090 break ;
1092 else
1093 i++;
1095 index += direction;
1096 } while (i <= count);
1098 return steps;
1101 /* Marks the index of the track to be skipped that is "steps" away from
1102 * current playing track.
1104 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1106 int index;
1108 if (playlist == NULL)
1109 playlist = &current_playlist;
1111 /* need to account for already skipped tracks */
1112 steps = calculate_step_count(playlist, steps);
1114 index = playlist->index + steps;
1115 if (index < 0)
1116 index += playlist->amount;
1117 else if (index >= playlist->amount)
1118 index -= playlist->amount;
1120 playlist->indices[index] |= PLAYLIST_SKIPPED;
1124 * returns the index of the track that is "steps" away from current playing
1125 * track.
1127 static int get_next_index(const struct playlist_info* playlist, int steps,
1128 int repeat_mode)
1130 int current_index = playlist->index;
1131 int next_index = -1;
1133 if (playlist->amount <= 0)
1134 return -1;
1136 if (repeat_mode == -1)
1137 repeat_mode = global_settings.repeat_mode;
1139 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1140 repeat_mode = REPEAT_ALL;
1142 steps = calculate_step_count(playlist, steps);
1143 switch (repeat_mode)
1145 case REPEAT_SHUFFLE:
1146 /* Treat repeat shuffle just like repeat off. At end of playlist,
1147 play will be resumed in playlist_next() */
1148 case REPEAT_OFF:
1150 current_index = rotate_index(playlist, current_index);
1151 next_index = current_index+steps;
1152 if ((next_index < 0) || (next_index >= playlist->amount))
1153 next_index = -1;
1154 else
1155 next_index = (next_index+playlist->first_index) %
1156 playlist->amount;
1158 break;
1161 case REPEAT_ONE:
1162 #ifdef AB_REPEAT_ENABLE
1163 case REPEAT_AB:
1164 #endif
1165 next_index = current_index;
1166 break;
1168 case REPEAT_ALL:
1169 default:
1171 next_index = (current_index+steps) % playlist->amount;
1172 while (next_index < 0)
1173 next_index += playlist->amount;
1175 if (steps >= playlist->amount)
1177 int i, index;
1179 index = next_index;
1180 next_index = -1;
1182 /* second time around so skip the queued files */
1183 for (i=0; i<playlist->amount; i++)
1185 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1186 index = (index+1) % playlist->amount;
1187 else
1189 next_index = index;
1190 break;
1194 break;
1198 /* No luck if the whole playlist was bad. */
1199 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1200 return -1;
1202 return next_index;
1206 * Search for the seek track and set appropriate indices. Used after shuffle
1207 * to make sure the current index is still pointing to correct track.
1209 static void find_and_set_playlist_index(struct playlist_info* playlist,
1210 unsigned int seek)
1212 int i;
1214 /* Set the index to the current song */
1215 for (i=0; i<playlist->amount; i++)
1217 if (playlist->indices[i] == seek)
1219 playlist->index = playlist->first_index = i;
1221 if (playlist->current)
1223 global_status.resume_first_index = i;
1224 status_save();
1227 break;
1233 * used to sort track indices. Sort order is as follows:
1234 * 1. Prepended tracks (in prepend order)
1235 * 2. Playlist/directory tracks (in playlist order)
1236 * 3. Inserted/Appended tracks (in insert order)
1238 static int compare(const void* p1, const void* p2)
1240 unsigned long* e1 = (unsigned long*) p1;
1241 unsigned long* e2 = (unsigned long*) p2;
1242 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1243 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1245 if (flags1 == flags2)
1246 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1247 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1248 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1249 return -1;
1250 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1251 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1252 return 1;
1253 else if (flags1 && flags2)
1254 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1255 else
1256 return *e1 - *e2;
1259 #ifdef HAVE_DIRCACHE
1261 * Thread to update filename pointers to dircache on background
1262 * without affecting playlist load up performance. This thread also flushes
1263 * any pending control commands when the disk spins up.
1265 static void playlist_thread(void)
1267 struct queue_event ev;
1268 bool dirty_pointers = false;
1269 static char tmp[MAX_PATH+1];
1271 struct playlist_info *playlist;
1272 int index;
1273 int seek;
1274 bool control_file;
1276 int sleep_time = 5;
1278 #ifndef HAVE_FLASH_STORAGE
1279 if (global_settings.disk_spindown > 1 &&
1280 global_settings.disk_spindown <= 5)
1281 sleep_time = global_settings.disk_spindown - 1;
1282 #endif
1284 while (1)
1286 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1288 switch (ev.id)
1290 case PLAYLIST_LOAD_POINTERS:
1291 dirty_pointers = true;
1292 break ;
1294 /* Start the background scanning after either the disk spindown
1295 timeout or 5s, whichever is less */
1296 case SYS_TIMEOUT:
1297 playlist = &current_playlist;
1299 if (playlist->control_fd >= 0
1300 # ifndef SIMULATOR
1301 && ata_disk_is_active()
1302 # endif
1305 if (playlist->num_cached > 0)
1307 mutex_lock(&playlist->control_mutex);
1308 flush_cached_control(playlist);
1309 mutex_unlock(&playlist->control_mutex);
1312 sync_control(playlist, true);
1315 if (!dirty_pointers)
1316 break ;
1318 if (!dircache_is_enabled() || !playlist->filenames
1319 || playlist->amount <= 0)
1320 break ;
1322 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1323 cpu_boost(true);
1324 #endif
1325 for (index = 0; index < playlist->amount
1326 && queue_empty(&playlist_queue); index++)
1328 /* Process only pointers that are not already loaded. */
1329 if (playlist->filenames[index])
1330 continue ;
1332 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1333 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1335 /* Load the filename from playlist file. */
1336 if (get_filename(playlist, index, seek, control_file, tmp,
1337 sizeof(tmp)) < 0)
1338 break ;
1340 /* Set the dircache entry pointer. */
1341 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1343 /* And be on background so user doesn't notice any delays. */
1344 yield();
1347 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1348 cpu_boost(false);
1349 #endif
1350 dirty_pointers = false;
1351 break ;
1353 #ifndef SIMULATOR
1354 case SYS_USB_CONNECTED:
1355 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1356 usb_wait_for_disconnect(&playlist_queue);
1357 break ;
1358 #endif
1362 #endif
1365 * gets pathname for track at seek index
1367 static int get_filename(struct playlist_info* playlist, int index, int seek,
1368 bool control_file, char *buf, int buf_length)
1370 int fd;
1371 int max = -1;
1372 char tmp_buf[MAX_PATH+1];
1373 char dir_buf[MAX_PATH+1];
1374 bool utf8 = playlist->utf8;
1376 if (buf_length > MAX_PATH+1)
1377 buf_length = MAX_PATH+1;
1379 #ifdef HAVE_DIRCACHE
1380 if (dircache_is_enabled() && playlist->filenames)
1382 if (playlist->filenames[index] != NULL)
1384 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1385 max = strlen(tmp_buf) + 1;
1388 #else
1389 (void)index;
1390 #endif
1392 if (playlist->in_ram && !control_file && max < 0)
1394 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1395 tmp_buf[MAX_PATH] = '\0';
1396 max = strlen(tmp_buf) + 1;
1398 else if (max < 0)
1400 mutex_lock(&playlist->control_mutex);
1402 if (control_file)
1404 fd = playlist->control_fd;
1405 utf8 = true;
1407 else
1409 if(-1 == playlist->fd)
1410 playlist->fd = open(playlist->filename, O_RDONLY);
1412 fd = playlist->fd;
1415 if(-1 != fd)
1418 if (lseek(fd, seek, SEEK_SET) != seek)
1419 max = -1;
1420 else
1422 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1424 if ((max > 0) && !utf8)
1426 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1427 * be as large as tmp_buf.
1429 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1434 mutex_unlock(&playlist->control_mutex);
1436 if (max < 0)
1438 if (control_file)
1439 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1440 else
1441 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1443 return max;
1447 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1448 dir_buf[playlist->dirlen-1] = 0;
1450 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1453 static int get_next_directory(char *dir){
1454 return get_next_dir(dir,true,false);
1457 static int get_previous_directory(char *dir){
1458 return get_next_dir(dir,false,false);
1462 * search through all the directories (starting with the current) to find
1463 * one that has tracks to play
1465 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1467 struct playlist_info* playlist = &current_playlist;
1468 int result = -1;
1469 int sort_dir = global_settings.sort_dir;
1470 char *start_dir = NULL;
1471 bool exit = false;
1472 struct tree_context* tc = tree_get_context();
1473 int dirfilter = *(tc->dirfilter);
1475 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1477 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1478 char buffer[MAX_PATH];
1479 int folder_count = 0,i;
1480 srand(current_tick);
1481 *(tc->dirfilter) = SHOW_MUSIC;
1482 if (fd >= 0)
1484 read(fd,&folder_count,sizeof(int));
1485 while (!exit)
1487 i = rand()%folder_count;
1488 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1489 read(fd,buffer,MAX_PATH);
1490 if (check_subdir_for_music(buffer,"") ==0)
1491 exit = true;
1493 strcpy(dir,buffer);
1494 close(fd);
1495 *(tc->dirfilter) = dirfilter;
1496 reload_directory();
1497 return 0;
1500 /* not random folder advance */
1501 if (recursion){
1502 /* start with root */
1503 dir[0] = '\0';
1505 else{
1506 /* start with current directory */
1507 strncpy(dir, playlist->filename, playlist->dirlen-1);
1508 dir[playlist->dirlen-1] = '\0';
1511 /* use the tree browser dircache to load files */
1512 *(tc->dirfilter) = SHOW_ALL;
1514 /* sort in another direction if previous dir is requested */
1515 if(!is_forward){
1516 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1517 global_settings.sort_dir = 4;
1518 else if (global_settings.sort_dir == 1)
1519 global_settings.sort_dir = 2;
1520 else if (global_settings.sort_dir == 2)
1521 global_settings.sort_dir = 1;
1522 else if (global_settings.sort_dir == 4)
1523 global_settings.sort_dir = 0;
1526 while (!exit)
1528 struct entry *files;
1529 int num_files = 0;
1530 int i;
1532 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1534 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1535 exit = true;
1536 result = -1;
1537 break;
1540 files = (struct entry*) tc->dircache;
1541 num_files = tc->filesindir;
1543 for (i=0; i<num_files; i++)
1545 /* user abort */
1546 if (action_userabort(TIMEOUT_NOBLOCK))
1548 result = -1;
1549 exit = true;
1550 break;
1553 if (files[i].attr & ATTR_DIRECTORY)
1555 if (!start_dir)
1557 result = check_subdir_for_music(dir, files[i].name);
1558 if (result != -1)
1560 exit = true;
1561 break;
1564 else if (!strcmp(start_dir, files[i].name))
1565 start_dir = NULL;
1569 if (!exit)
1571 /* move down to parent directory. current directory name is
1572 stored as the starting point for the search in parent */
1573 start_dir = strrchr(dir, '/');
1574 if (start_dir)
1576 *start_dir = '\0';
1577 start_dir++;
1579 else
1580 break;
1584 /* restore dirfilter & sort_dir */
1585 *(tc->dirfilter) = dirfilter;
1586 global_settings.sort_dir = sort_dir;
1588 /* special case if nothing found: try start searching again from root */
1589 if (result == -1 && !recursion){
1590 result = get_next_dir(dir,is_forward, true);
1593 return result;
1597 * Checks if there are any music files in the dir or any of its
1598 * subdirectories. May be called recursively.
1600 static int check_subdir_for_music(char *dir, char *subdir)
1602 int result = -1;
1603 int dirlen = strlen(dir);
1604 int num_files = 0;
1605 int i;
1606 struct entry *files;
1607 bool has_music = false;
1608 bool has_subdir = false;
1609 struct tree_context* tc = tree_get_context();
1611 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1613 if (ft_load(tc, dir) < 0)
1615 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1616 return -2;
1619 files = (struct entry*) tc->dircache;
1620 num_files = tc->filesindir;
1622 for (i=0; i<num_files; i++)
1624 if (files[i].attr & ATTR_DIRECTORY)
1625 has_subdir = true;
1626 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1628 has_music = true;
1629 break;
1633 if (has_music)
1634 return 0;
1636 if (has_subdir)
1638 for (i=0; i<num_files; i++)
1640 if (action_userabort(TIMEOUT_NOBLOCK))
1642 result = -2;
1643 break;
1646 if (files[i].attr & ATTR_DIRECTORY)
1648 result = check_subdir_for_music(dir, files[i].name);
1649 if (!result)
1650 break;
1655 if (result < 0)
1657 if (dirlen)
1659 dir[dirlen] = '\0';
1661 else
1663 strcpy(dir, "/");
1666 /* we now need to reload our current directory */
1667 if(ft_load(tc, dir) < 0)
1668 gui_syncsplash(HZ*2,
1669 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1672 return result;
1676 * Returns absolute path of track
1678 static int format_track_path(char *dest, char *src, int buf_length, int max,
1679 char *dir)
1681 int i = 0;
1682 int j;
1683 char *temp_ptr;
1685 /* Zero-terminate the file name */
1686 while((src[i] != '\n') &&
1687 (src[i] != '\r') &&
1688 (i < max))
1689 i++;
1691 /* Now work back killing white space */
1692 while((src[i-1] == ' ') ||
1693 (src[i-1] == '\t'))
1694 i--;
1696 src[i]=0;
1698 /* replace backslashes with forward slashes */
1699 for ( j=0; j<i; j++ )
1700 if ( src[j] == '\\' )
1701 src[j] = '/';
1703 if('/' == src[0])
1705 strncpy(dest, src, buf_length);
1707 else
1709 /* handle dos style drive letter */
1710 if (':' == src[1])
1711 strncpy(dest, &src[2], buf_length);
1712 else if (!strncmp(src, "../", 3))
1714 /* handle relative paths */
1715 i=3;
1716 while(!strncmp(&src[i], "../", 3))
1717 i += 3;
1718 for (j=0; j<i/3; j++) {
1719 temp_ptr = strrchr(dir, '/');
1720 if (temp_ptr)
1721 *temp_ptr = '\0';
1722 else
1723 break;
1725 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1727 else if ( '.' == src[0] && '/' == src[1] ) {
1728 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1730 else {
1731 snprintf(dest, buf_length, "%s/%s", dir, src);
1735 return 0;
1739 * Display splash message showing progress of playlist/directory insertion or
1740 * save.
1742 static void display_playlist_count(int count, const unsigned char *fmt,
1743 bool final)
1745 static long talked_tick = 0;
1746 long id = P2ID(fmt);
1747 if(global_settings.talk_menu && id>=0)
1749 if(final || (count && (talked_tick == 0
1750 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1752 talked_tick = current_tick;
1753 talk_number(count, false);
1754 talk_id(id, true);
1757 fmt = P2STR(fmt);
1759 lcd_clear_display();
1761 #ifdef HAVE_LCD_BITMAP
1762 if(global_settings.statusbar)
1763 lcd_setmargins(0, STATUSBAR_HEIGHT);
1764 else
1765 lcd_setmargins(0, 0);
1766 #endif
1768 gui_syncsplash(0, fmt, count, str(LANG_OFF_ABORT));
1772 * Display buffer full message
1774 static void display_buffer_full(void)
1776 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1780 * Flush any cached control commands to disk. Called when playlist is being
1781 * modified. Returns 0 on success and -1 on failure.
1783 static int flush_cached_control(struct playlist_info* playlist)
1785 int result = 0;
1786 int i;
1788 if (!playlist->num_cached)
1789 return 0;
1791 lseek(playlist->control_fd, 0, SEEK_END);
1793 for (i=0; i<playlist->num_cached; i++)
1795 struct playlist_control_cache* cache =
1796 &(playlist->control_cache[i]);
1798 switch (cache->command)
1800 case PLAYLIST_COMMAND_PLAYLIST:
1801 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1802 cache->i1, cache->s1, cache->s2);
1803 break;
1804 case PLAYLIST_COMMAND_ADD:
1805 case PLAYLIST_COMMAND_QUEUE:
1806 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1807 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1808 cache->i1, cache->i2);
1809 if (result > 0)
1811 /* save the position in file where name is written */
1812 int* seek_pos = (int *)cache->data;
1813 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1814 result = fdprintf(playlist->control_fd, "%s\n",
1815 cache->s1);
1817 break;
1818 case PLAYLIST_COMMAND_DELETE:
1819 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1820 break;
1821 case PLAYLIST_COMMAND_SHUFFLE:
1822 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1823 cache->i1, cache->i2);
1824 break;
1825 case PLAYLIST_COMMAND_UNSHUFFLE:
1826 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1827 break;
1828 case PLAYLIST_COMMAND_RESET:
1829 result = fdprintf(playlist->control_fd, "R\n");
1830 break;
1831 default:
1832 break;
1835 if (result <= 0)
1836 break;
1839 if (result > 0)
1841 if (global_status.resume_seed >= 0)
1843 global_status.resume_seed = -1;
1844 status_save();
1847 playlist->num_cached = 0;
1848 playlist->pending_control_sync = true;
1850 result = 0;
1852 else
1854 result = -1;
1855 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1858 return result;
1862 * Update control data with new command. Depending on the command, it may be
1863 * cached or flushed to disk.
1865 static int update_control(struct playlist_info* playlist,
1866 enum playlist_command command, int i1, int i2,
1867 const char* s1, const char* s2, void* data)
1869 int result = 0;
1870 struct playlist_control_cache* cache;
1871 bool flush = false;
1873 mutex_lock(&playlist->control_mutex);
1875 cache = &(playlist->control_cache[playlist->num_cached++]);
1877 cache->command = command;
1878 cache->i1 = i1;
1879 cache->i2 = i2;
1880 cache->s1 = s1;
1881 cache->s2 = s2;
1882 cache->data = data;
1884 switch (command)
1886 case PLAYLIST_COMMAND_PLAYLIST:
1887 case PLAYLIST_COMMAND_ADD:
1888 case PLAYLIST_COMMAND_QUEUE:
1889 #ifndef HAVE_DIRCACHE
1890 case PLAYLIST_COMMAND_DELETE:
1891 case PLAYLIST_COMMAND_RESET:
1892 #endif
1893 flush = true;
1894 break;
1895 case PLAYLIST_COMMAND_SHUFFLE:
1896 case PLAYLIST_COMMAND_UNSHUFFLE:
1897 default:
1898 /* only flush when needed */
1899 break;
1902 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1903 result = flush_cached_control(playlist);
1905 mutex_unlock(&playlist->control_mutex);
1907 return result;
1911 * sync control file to disk
1913 static void sync_control(struct playlist_info* playlist, bool force)
1915 #ifdef HAVE_DIRCACHE
1916 if (playlist->started && force)
1917 #else
1918 (void) force;
1920 if (playlist->started)
1921 #endif
1923 if (playlist->pending_control_sync)
1925 mutex_lock(&playlist->control_mutex);
1926 fsync(playlist->control_fd);
1927 playlist->pending_control_sync = false;
1928 mutex_unlock(&playlist->control_mutex);
1934 * Rotate indices such that first_index is index 0
1936 static int rotate_index(const struct playlist_info* playlist, int index)
1938 index -= playlist->first_index;
1939 if (index < 0)
1940 index += playlist->amount;
1942 return index;
1946 * Initialize playlist entries at startup
1948 void playlist_init(void)
1950 struct playlist_info* playlist = &current_playlist;
1952 playlist->current = true;
1953 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1954 "%s", PLAYLIST_CONTROL_FILE);
1955 playlist->fd = -1;
1956 playlist->control_fd = -1;
1957 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1958 playlist->indices = buffer_alloc(
1959 playlist->max_playlist_size * sizeof(int));
1960 playlist->buffer_size =
1961 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1962 playlist->buffer = buffer_alloc(playlist->buffer_size);
1963 mutex_init(&playlist->control_mutex);
1964 empty_playlist(playlist, true);
1966 #ifdef HAVE_DIRCACHE
1967 playlist->filenames = buffer_alloc(
1968 playlist->max_playlist_size * sizeof(int));
1969 memset(playlist->filenames, 0,
1970 playlist->max_playlist_size * sizeof(int));
1971 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1972 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1973 IF_COP(, CPU));
1974 queue_init(&playlist_queue, true);
1975 #endif
1979 * Clean playlist at shutdown
1981 void playlist_shutdown(void)
1983 struct playlist_info* playlist = &current_playlist;
1985 if (playlist->control_fd >= 0)
1987 mutex_lock(&playlist->control_mutex);
1989 if (playlist->num_cached > 0)
1990 flush_cached_control(playlist);
1992 close(playlist->control_fd);
1994 mutex_unlock(&playlist->control_mutex);
1999 * Create new playlist
2001 int playlist_create(const char *dir, const char *file)
2003 struct playlist_info* playlist = &current_playlist;
2005 new_playlist(playlist, dir, file);
2007 if (file)
2008 /* load the playlist file */
2009 add_indices_to_playlist(playlist, NULL, 0);
2011 return 0;
2014 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2017 * Restore the playlist state based on control file commands. Called to
2018 * resume playback after shutdown.
2020 int playlist_resume(void)
2022 struct playlist_info* playlist = &current_playlist;
2023 char *buffer;
2024 size_t buflen;
2025 int nread;
2026 int total_read = 0;
2027 int control_file_size = 0;
2028 bool first = true;
2029 bool sorted = true;
2031 /* use mp3 buffer for maximum load speed */
2032 #if CONFIG_CODEC != SWCODEC
2033 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2034 buflen = (audiobufend - audiobuf);
2035 buffer = (char *)audiobuf;
2036 #else
2037 buffer = (char *)audio_get_buffer(false, &buflen);
2038 #endif
2040 empty_playlist(playlist, true);
2042 gui_syncsplash(0, ID2P(LANG_WAIT));
2043 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2044 if (playlist->control_fd < 0)
2046 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2047 return -1;
2049 playlist->control_created = true;
2051 control_file_size = filesize(playlist->control_fd);
2052 if (control_file_size <= 0)
2054 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2055 return -1;
2058 /* read a small amount first to get the header */
2059 nread = read(playlist->control_fd, buffer,
2060 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2061 if(nread <= 0)
2063 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2064 return -1;
2067 playlist->started = true;
2069 while (1)
2071 int result = 0;
2072 int count;
2073 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2074 int last_newline = 0;
2075 int str_count = -1;
2076 bool newline = true;
2077 bool exit_loop = false;
2078 char *p = buffer;
2079 char *str1 = NULL;
2080 char *str2 = NULL;
2081 char *str3 = NULL;
2082 unsigned long last_tick = current_tick;
2084 for(count=0; count<nread && !exit_loop; count++,p++)
2086 /* So a splash while we are loading. */
2087 if (current_tick - last_tick > HZ/4)
2089 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
2090 (total_read+count)*100/control_file_size,
2091 str(LANG_OFF_ABORT));
2092 if (action_userabort(TIMEOUT_NOBLOCK))
2094 /* FIXME:
2095 * Not sure how to implement this, somebody more familiar
2096 * with the code, please fix this. */
2098 last_tick = current_tick;
2101 /* Are we on a new line? */
2102 if((*p == '\n') || (*p == '\r'))
2104 *p = '\0';
2106 /* save last_newline in case we need to load more data */
2107 last_newline = count;
2109 switch (current_command)
2111 case PLAYLIST_COMMAND_PLAYLIST:
2113 /* str1=version str2=dir str3=file */
2114 int version;
2116 if (!str1)
2118 result = -1;
2119 exit_loop = true;
2120 break;
2123 if (!str2)
2124 str2 = "";
2126 if (!str3)
2127 str3 = "";
2129 version = atoi(str1);
2131 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2132 return -1;
2134 update_playlist_filename(playlist, str2, str3);
2136 if (str3[0] != '\0')
2138 /* NOTE: add_indices_to_playlist() overwrites the
2139 audiobuf so we need to reload control file
2140 data */
2141 add_indices_to_playlist(playlist, NULL, 0);
2143 else if (str2[0] != '\0')
2145 playlist->in_ram = true;
2146 resume_directory(str2);
2149 /* load the rest of the data */
2150 first = false;
2151 exit_loop = true;
2153 break;
2155 case PLAYLIST_COMMAND_ADD:
2156 case PLAYLIST_COMMAND_QUEUE:
2158 /* str1=position str2=last_position str3=file */
2159 int position, last_position;
2160 bool queue;
2162 if (!str1 || !str2 || !str3)
2164 result = -1;
2165 exit_loop = true;
2166 break;
2169 position = atoi(str1);
2170 last_position = atoi(str2);
2172 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2173 false:true;
2175 /* seek position is based on str3's position in
2176 buffer */
2177 if (add_track_to_playlist(playlist, str3, position,
2178 queue, total_read+(str3-buffer)) < 0)
2179 return -1;
2181 playlist->last_insert_pos = last_position;
2183 break;
2185 case PLAYLIST_COMMAND_DELETE:
2187 /* str1=position */
2188 int position;
2190 if (!str1)
2192 result = -1;
2193 exit_loop = true;
2194 break;
2197 position = atoi(str1);
2199 if (remove_track_from_playlist(playlist, position,
2200 false) < 0)
2201 return -1;
2203 break;
2205 case PLAYLIST_COMMAND_SHUFFLE:
2207 /* str1=seed str2=first_index */
2208 int seed;
2210 if (!str1 || !str2)
2212 result = -1;
2213 exit_loop = true;
2214 break;
2217 if (!sorted)
2219 /* Always sort list before shuffling */
2220 sort_playlist(playlist, false, false);
2223 seed = atoi(str1);
2224 playlist->first_index = atoi(str2);
2226 if (randomise_playlist(playlist, seed, false,
2227 false) < 0)
2228 return -1;
2230 sorted = false;
2231 break;
2233 case PLAYLIST_COMMAND_UNSHUFFLE:
2235 /* str1=first_index */
2236 if (!str1)
2238 result = -1;
2239 exit_loop = true;
2240 break;
2243 playlist->first_index = atoi(str1);
2245 if (sort_playlist(playlist, false, false) < 0)
2246 return -1;
2248 sorted = true;
2249 break;
2251 case PLAYLIST_COMMAND_RESET:
2253 playlist->last_insert_pos = -1;
2254 break;
2256 case PLAYLIST_COMMAND_COMMENT:
2257 default:
2258 break;
2261 newline = true;
2263 /* to ignore any extra newlines */
2264 current_command = PLAYLIST_COMMAND_COMMENT;
2266 else if(newline)
2268 newline = false;
2270 /* first non-comment line must always specify playlist */
2271 if (first && *p != 'P' && *p != '#')
2273 result = -1;
2274 exit_loop = true;
2275 break;
2278 switch (*p)
2280 case 'P':
2281 /* playlist can only be specified once */
2282 if (!first)
2284 result = -1;
2285 exit_loop = true;
2286 break;
2289 current_command = PLAYLIST_COMMAND_PLAYLIST;
2290 break;
2291 case 'A':
2292 current_command = PLAYLIST_COMMAND_ADD;
2293 break;
2294 case 'Q':
2295 current_command = PLAYLIST_COMMAND_QUEUE;
2296 break;
2297 case 'D':
2298 current_command = PLAYLIST_COMMAND_DELETE;
2299 break;
2300 case 'S':
2301 current_command = PLAYLIST_COMMAND_SHUFFLE;
2302 break;
2303 case 'U':
2304 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2305 break;
2306 case 'R':
2307 current_command = PLAYLIST_COMMAND_RESET;
2308 break;
2309 case '#':
2310 current_command = PLAYLIST_COMMAND_COMMENT;
2311 break;
2312 default:
2313 result = -1;
2314 exit_loop = true;
2315 break;
2318 str_count = -1;
2319 str1 = NULL;
2320 str2 = NULL;
2321 str3 = NULL;
2323 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2325 /* all control file strings are separated with a colon.
2326 Replace the colon with 0 to get proper strings that can be
2327 used by commands above */
2328 if (*p == ':')
2330 *p = '\0';
2331 str_count++;
2333 if ((count+1) < nread)
2335 switch (str_count)
2337 case 0:
2338 str1 = p+1;
2339 break;
2340 case 1:
2341 str2 = p+1;
2342 break;
2343 case 2:
2344 str3 = p+1;
2345 break;
2346 default:
2347 /* allow last string to contain colons */
2348 *p = ':';
2349 break;
2356 if (result < 0)
2358 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2359 return result;
2362 if (!newline || (exit_loop && count<nread))
2364 if ((total_read + count) >= control_file_size)
2366 /* no newline at end of control file */
2367 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2368 return -1;
2371 /* We didn't end on a newline or we exited loop prematurely.
2372 Either way, re-read the remainder. */
2373 count = last_newline;
2374 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2377 total_read += count;
2379 if (first)
2380 /* still looking for header */
2381 nread = read(playlist->control_fd, buffer,
2382 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2383 else
2384 nread = read(playlist->control_fd, buffer, buflen);
2386 /* Terminate on EOF */
2387 if(nread <= 0)
2389 if (global_status.resume_seed >= 0)
2391 /* Apply shuffle command saved in settings */
2392 if (global_status.resume_seed == 0)
2393 sort_playlist(playlist, false, true);
2394 else
2396 if (!sorted)
2397 sort_playlist(playlist, false, false);
2399 randomise_playlist(playlist, global_status.resume_seed,
2400 false, true);
2404 playlist->first_index = global_status.resume_first_index;
2405 break;
2409 #ifdef HAVE_DIRCACHE
2410 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2411 #endif
2413 return 0;
2417 * Add track to in_ram playlist. Used when playing directories.
2419 int playlist_add(const char *filename)
2421 struct playlist_info* playlist = &current_playlist;
2422 int len = strlen(filename);
2424 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2425 (playlist->amount >= playlist->max_playlist_size))
2427 display_buffer_full();
2428 return -1;
2431 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2432 #ifdef HAVE_DIRCACHE
2433 playlist->filenames[playlist->amount] = NULL;
2434 #endif
2435 playlist->amount++;
2437 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2438 playlist->buffer_end_pos += len;
2439 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2441 return 0;
2444 /* shuffle newly created playlist using random seed. */
2445 int playlist_shuffle(int random_seed, int start_index)
2447 struct playlist_info* playlist = &current_playlist;
2449 unsigned int seek_pos = 0;
2450 bool start_current = false;
2452 if (start_index >= 0 && global_settings.play_selected)
2454 /* store the seek position before the shuffle */
2455 seek_pos = playlist->indices[start_index];
2456 playlist->index = global_status.resume_first_index =
2457 playlist->first_index = start_index;
2458 start_current = true;
2461 randomise_playlist(playlist, random_seed, start_current, true);
2463 return playlist->index;
2466 /* start playing current playlist at specified index/offset */
2467 int playlist_start(int start_index, int offset)
2469 struct playlist_info* playlist = &current_playlist;
2471 /* Cancel FM radio selection as previous music. For cases where we start
2472 playback without going to the WPS, such as playlist insert.. or
2473 playlist catalog. */
2474 previous_music_is_wps();
2476 playlist->index = start_index;
2478 #if CONFIG_CODEC != SWCODEC
2479 talk_buffer_steal(); /* will use the mp3 buffer */
2480 #endif
2482 playlist->started = true;
2483 sync_control(playlist, false);
2484 audio_play(offset);
2486 return 0;
2489 /* Returns false if 'steps' is out of bounds, else true */
2490 bool playlist_check(int steps)
2492 struct playlist_info* playlist = &current_playlist;
2494 /* always allow folder navigation */
2495 if (global_settings.next_folder && playlist->in_ram)
2496 return true;
2498 int index = get_next_index(playlist, steps, -1);
2500 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2501 index = get_next_index(playlist, steps, REPEAT_ALL);
2503 return (index >= 0);
2506 /* get trackname of track that is "steps" away from current playing track.
2507 NULL is used to identify end of playlist */
2508 char* playlist_peek(int steps)
2510 struct playlist_info* playlist = &current_playlist;
2511 int seek;
2512 int fd;
2513 char *temp_ptr;
2514 int index;
2515 bool control_file;
2517 index = get_next_index(playlist, steps, -1);
2518 if (index < 0)
2519 return NULL;
2521 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2522 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2524 if (get_filename(playlist, index, seek, control_file, now_playing,
2525 MAX_PATH+1) < 0)
2526 return NULL;
2528 temp_ptr = now_playing;
2530 if (!playlist->in_ram || control_file)
2532 /* remove bogus dirs from beginning of path
2533 (workaround for buggy playlist creation tools) */
2534 while (temp_ptr)
2536 #ifdef HAVE_DIRCACHE
2537 if (dircache_is_enabled())
2539 if (dircache_get_entry_ptr(temp_ptr))
2540 break;
2542 else
2543 #endif
2545 fd = open(temp_ptr, O_RDONLY);
2546 if (fd >= 0)
2548 close(fd);
2549 break;
2553 temp_ptr = strchr(temp_ptr+1, '/');
2556 if (!temp_ptr)
2558 /* Even though this is an invalid file, we still need to pass a
2559 file name to the caller because NULL is used to indicate end
2560 of playlist */
2561 return now_playing;
2565 return temp_ptr;
2569 * Update indices as track has changed
2571 int playlist_next(int steps)
2573 struct playlist_info* playlist = &current_playlist;
2574 int index;
2576 if ( (steps > 0)
2577 #ifdef AB_REPEAT_ENABLE
2578 && (global_settings.repeat_mode != REPEAT_AB)
2579 #endif
2580 && (global_settings.repeat_mode != REPEAT_ONE) )
2582 int i, j;
2584 /* We need to delete all the queued songs */
2585 for (i=0, j=steps; i<j; i++)
2587 index = get_next_index(playlist, i, -1);
2589 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2591 remove_track_from_playlist(playlist, index, true);
2592 steps--; /* one less track */
2597 index = get_next_index(playlist, steps, -1);
2599 if (index < 0)
2601 /* end of playlist... or is it */
2602 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2603 playlist->amount > 1)
2605 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2606 playlist->first_index = global_status.resume_first_index = 0;
2607 sort_playlist(playlist, false, false);
2608 randomise_playlist(playlist, current_tick, false, true);
2609 #if CONFIG_CODEC != SWCODEC
2610 playlist_start(0, 0);
2611 #endif
2612 playlist->index = 0;
2613 index = 0;
2615 else if (playlist->in_ram && global_settings.next_folder)
2617 index = create_and_play_dir(steps, true);
2619 if (index >= 0)
2621 playlist->index = index;
2625 return index;
2628 playlist->index = index;
2630 if (playlist->last_insert_pos >= 0 && steps > 0)
2632 /* check to see if we've gone beyond the last inserted track */
2633 int cur = rotate_index(playlist, index);
2634 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2636 if (cur > last_pos)
2638 /* reset last inserted track */
2639 playlist->last_insert_pos = -1;
2641 if (playlist->control_fd >= 0)
2643 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2644 -1, -1, NULL, NULL, NULL);
2646 if (result < 0)
2647 return result;
2649 sync_control(playlist, false);
2654 return index;
2657 /* try playing next or previous folder */
2658 bool playlist_next_dir(int direction)
2660 /* not to mess up real playlists */
2661 if(!current_playlist.in_ram)
2662 return false;
2664 return create_and_play_dir(direction, false) >= 0;
2667 /* Get resume info for current playing song. If return value is -1 then
2668 settings shouldn't be saved. */
2669 int playlist_get_resume_info(int *resume_index)
2671 struct playlist_info* playlist = &current_playlist;
2673 *resume_index = playlist->index;
2675 return 0;
2678 /* Update resume info for current playing song. Returns -1 on error. */
2679 int playlist_update_resume_info(const struct mp3entry* id3)
2681 struct playlist_info* playlist = &current_playlist;
2683 if (id3)
2685 if (global_status.resume_index != playlist->index ||
2686 global_status.resume_offset != id3->offset)
2688 global_status.resume_index = playlist->index;
2689 global_status.resume_offset = id3->offset;
2690 status_save();
2693 else
2695 global_status.resume_index = -1;
2696 global_status.resume_offset = -1;
2697 status_save();
2700 return 0;
2703 /* Returns index of current playing track for display purposes. This value
2704 should not be used for resume purposes as it doesn't represent the actual
2705 index into the playlist */
2706 int playlist_get_display_index(void)
2708 struct playlist_info* playlist = &current_playlist;
2710 /* first_index should always be index 0 for display purposes */
2711 int index = rotate_index(playlist, playlist->index);
2713 return (index+1);
2716 /* returns number of tracks in current playlist */
2717 int playlist_amount(void)
2719 return playlist_amount_ex(NULL);
2723 * Create a new playlist If playlist is not NULL then we're loading a
2724 * playlist off disk for viewing/editing. The index_buffer is used to store
2725 * playlist indices (required for and only used if !current playlist). The
2726 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2728 int playlist_create_ex(struct playlist_info* playlist,
2729 const char* dir, const char* file,
2730 void* index_buffer, int index_buffer_size,
2731 void* temp_buffer, int temp_buffer_size)
2733 if (!playlist)
2734 playlist = &current_playlist;
2735 else
2737 /* Initialize playlist structure */
2738 int r = rand() % 10;
2739 playlist->current = false;
2741 /* Use random name for control file */
2742 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2743 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2744 playlist->fd = -1;
2745 playlist->control_fd = -1;
2747 if (index_buffer)
2749 int num_indices = index_buffer_size / sizeof(int);
2751 #ifdef HAVE_DIRCACHE
2752 num_indices /= 2;
2753 #endif
2754 if (num_indices > global_settings.max_files_in_playlist)
2755 num_indices = global_settings.max_files_in_playlist;
2757 playlist->max_playlist_size = num_indices;
2758 playlist->indices = index_buffer;
2759 #ifdef HAVE_DIRCACHE
2760 playlist->filenames = (const struct dircache_entry **)
2761 &playlist->indices[num_indices];
2762 #endif
2764 else
2766 playlist->max_playlist_size = current_playlist.max_playlist_size;
2767 playlist->indices = current_playlist.indices;
2768 #ifdef HAVE_DIRCACHE
2769 playlist->filenames = current_playlist.filenames;
2770 #endif
2773 playlist->buffer_size = 0;
2774 playlist->buffer = NULL;
2775 mutex_init(&playlist->control_mutex);
2778 new_playlist(playlist, dir, file);
2780 if (file)
2781 /* load the playlist file */
2782 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2784 return 0;
2788 * Set the specified playlist as the current.
2789 * NOTE: You will get undefined behaviour if something is already playing so
2790 * remember to stop before calling this. Also, this call will
2791 * effectively close your playlist, making it unusable.
2793 int playlist_set_current(struct playlist_info* playlist)
2795 if (!playlist || (check_control(playlist) < 0))
2796 return -1;
2798 empty_playlist(&current_playlist, false);
2800 strncpy(current_playlist.filename, playlist->filename,
2801 sizeof(current_playlist.filename));
2803 current_playlist.utf8 = playlist->utf8;
2804 current_playlist.fd = playlist->fd;
2806 close(playlist->control_fd);
2807 close(current_playlist.control_fd);
2808 remove(current_playlist.control_filename);
2809 if (rename(playlist->control_filename,
2810 current_playlist.control_filename) < 0)
2811 return -1;
2812 current_playlist.control_fd = open(current_playlist.control_filename,
2813 O_RDWR);
2814 if (current_playlist.control_fd < 0)
2815 return -1;
2816 current_playlist.control_created = true;
2818 current_playlist.dirlen = playlist->dirlen;
2820 if (playlist->indices && playlist->indices != current_playlist.indices)
2822 memcpy(current_playlist.indices, playlist->indices,
2823 playlist->max_playlist_size*sizeof(int));
2824 #ifdef HAVE_DIRCACHE
2825 memcpy(current_playlist.filenames, playlist->filenames,
2826 playlist->max_playlist_size*sizeof(int));
2827 #endif
2830 current_playlist.first_index = playlist->first_index;
2831 current_playlist.amount = playlist->amount;
2832 current_playlist.last_insert_pos = playlist->last_insert_pos;
2833 current_playlist.seed = playlist->seed;
2834 current_playlist.shuffle_modified = playlist->shuffle_modified;
2835 current_playlist.deleted = playlist->deleted;
2836 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2838 memcpy(current_playlist.control_cache, playlist->control_cache,
2839 sizeof(current_playlist.control_cache));
2840 current_playlist.num_cached = playlist->num_cached;
2841 current_playlist.pending_control_sync = playlist->pending_control_sync;
2843 return 0;
2847 * Close files and delete control file for non-current playlist.
2849 void playlist_close(struct playlist_info* playlist)
2851 if (!playlist)
2852 return;
2854 if (playlist->fd >= 0)
2855 close(playlist->fd);
2857 if (playlist->control_fd >= 0)
2858 close(playlist->control_fd);
2860 if (playlist->control_created)
2861 remove(playlist->control_filename);
2864 void playlist_sync(struct playlist_info* playlist)
2866 if (!playlist)
2867 playlist = &current_playlist;
2869 sync_control(playlist, false);
2870 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2871 audio_flush_and_reload_tracks();
2873 #ifdef HAVE_DIRCACHE
2874 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2875 #endif
2879 * Insert track into playlist at specified position (or one of the special
2880 * positions). Returns position where track was inserted or -1 if error.
2882 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2883 int position, bool queue, bool sync)
2885 int result;
2887 if (!playlist)
2888 playlist = &current_playlist;
2890 if (check_control(playlist) < 0)
2892 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2893 return -1;
2896 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2898 /* Check if we want manually sync later. For example when adding
2899 * bunch of files from tagcache, syncing after every file wouldn't be
2900 * a good thing to do. */
2901 if (sync && result >= 0)
2902 playlist_sync(playlist);
2904 return result;
2908 * Insert all tracks from specified directory into playlist.
2910 int playlist_insert_directory(struct playlist_info* playlist,
2911 const char *dirname, int position, bool queue,
2912 bool recurse)
2914 int result;
2915 unsigned char *count_str;
2916 struct directory_search_context context;
2918 if (!playlist)
2919 playlist = &current_playlist;
2921 if (check_control(playlist) < 0)
2923 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2924 return -1;
2927 if (position == PLAYLIST_REPLACE)
2929 if (remove_all_tracks(playlist) == 0)
2930 position = PLAYLIST_INSERT_LAST;
2931 else
2932 return -1;
2935 if (queue)
2936 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2937 else
2938 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2940 display_playlist_count(0, count_str, false);
2942 context.playlist = playlist;
2943 context.position = position;
2944 context.queue = queue;
2945 context.count = 0;
2947 cpu_boost(true);
2949 result = playlist_directory_tracksearch(dirname, recurse,
2950 directory_search_callback, &context);
2952 sync_control(playlist, false);
2954 cpu_boost(false);
2956 display_playlist_count(context.count, count_str, true);
2958 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2959 audio_flush_and_reload_tracks();
2961 #ifdef HAVE_DIRCACHE
2962 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2963 #endif
2965 return result;
2969 * Insert all tracks from specified playlist into dynamic playlist.
2971 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2972 int position, bool queue)
2974 int fd;
2975 int max;
2976 char *temp_ptr;
2977 char *dir;
2978 unsigned char *count_str;
2979 char temp_buf[MAX_PATH+1];
2980 char trackname[MAX_PATH+1];
2981 int count = 0;
2982 int result = 0;
2983 bool utf8 = is_m3u8(filename);
2985 if (!playlist)
2986 playlist = &current_playlist;
2988 if (check_control(playlist) < 0)
2990 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2991 return -1;
2994 fd = open(filename, O_RDONLY);
2995 if (fd < 0)
2997 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2998 return -1;
3001 /* we need the directory name for formatting purposes */
3002 dir = filename;
3004 temp_ptr = strrchr(filename+1,'/');
3005 if (temp_ptr)
3006 *temp_ptr = 0;
3007 else
3008 dir = "/";
3010 if (queue)
3011 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
3012 else
3013 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
3015 display_playlist_count(count, count_str, false);
3017 if (position == PLAYLIST_REPLACE)
3019 if (remove_all_tracks(playlist) == 0)
3020 position = PLAYLIST_INSERT_LAST;
3021 else return -1;
3024 cpu_boost(true);
3026 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3028 /* user abort */
3029 if (action_userabort(TIMEOUT_NOBLOCK))
3030 break;
3032 if (count == 0 && is_utf8_bom(temp_buf, max))
3034 max -= BOM_SIZE;
3035 memmove(temp_buf, temp_buf + BOM_SIZE, max);
3038 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3040 int insert_pos;
3042 if (!utf8)
3044 /* Use trackname as a temporay buffer. Note that trackname must
3045 * be as large as temp_buf.
3047 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3050 /* we need to format so that relative paths are correctly
3051 handled */
3052 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3053 dir) < 0)
3055 result = -1;
3056 break;
3059 insert_pos = add_track_to_playlist(playlist, trackname, position,
3060 queue, -1);
3062 if (insert_pos < 0)
3064 result = -1;
3065 break;
3068 /* Make sure tracks are inserted in correct order if user
3069 requests INSERT_FIRST */
3070 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3071 position = insert_pos + 1;
3073 count++;
3075 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3077 display_playlist_count(count, count_str, false);
3079 if (count == PLAYLIST_DISPLAY_COUNT &&
3080 (audio_status() & AUDIO_STATUS_PLAY) &&
3081 playlist->started)
3082 audio_flush_and_reload_tracks();
3086 /* let the other threads work */
3087 yield();
3090 close(fd);
3092 if (temp_ptr)
3093 *temp_ptr = '/';
3095 sync_control(playlist, false);
3097 cpu_boost(false);
3099 display_playlist_count(count, count_str, true);
3101 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3102 audio_flush_and_reload_tracks();
3104 #ifdef HAVE_DIRCACHE
3105 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3106 #endif
3108 return result;
3112 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3113 * we want to delete the current playing track.
3115 int playlist_delete(struct playlist_info* playlist, int index)
3117 int result = 0;
3119 if (!playlist)
3120 playlist = &current_playlist;
3122 if (check_control(playlist) < 0)
3124 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3125 return -1;
3128 if (index == PLAYLIST_DELETE_CURRENT)
3129 index = playlist->index;
3131 result = remove_track_from_playlist(playlist, index, true);
3133 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3134 playlist->started)
3135 audio_flush_and_reload_tracks();
3137 return result;
3141 * Move track at index to new_index. Tracks between the two are shifted
3142 * appropriately. Returns 0 on success and -1 on failure.
3144 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3146 int result;
3147 int seek;
3148 bool control_file;
3149 bool queue;
3150 bool current = false;
3151 int r;
3152 char filename[MAX_PATH];
3154 if (!playlist)
3155 playlist = &current_playlist;
3157 if (check_control(playlist) < 0)
3159 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3160 return -1;
3163 if (index == new_index)
3164 return -1;
3166 if (index == playlist->index)
3167 /* Moving the current track */
3168 current = true;
3170 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3171 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3172 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3174 if (get_filename(playlist, index, seek, control_file, filename,
3175 sizeof(filename)) < 0)
3176 return -1;
3178 /* Delete track from original position */
3179 result = remove_track_from_playlist(playlist, index, true);
3181 if (result != -1)
3183 /* We want to insert the track at the position that was specified by
3184 new_index. This may be different then new_index because of the
3185 shifting that occurred after the delete */
3186 r = rotate_index(playlist, new_index);
3188 if (r == 0)
3189 /* First index */
3190 new_index = PLAYLIST_PREPEND;
3191 else if (r == playlist->amount)
3192 /* Append */
3193 new_index = PLAYLIST_INSERT_LAST;
3194 else
3195 /* Calculate index of desired position */
3196 new_index = (r+playlist->first_index)%playlist->amount;
3198 result = add_track_to_playlist(playlist, filename, new_index, queue,
3199 -1);
3201 if (result != -1)
3203 if (current)
3205 /* Moved the current track */
3206 switch (new_index)
3208 case PLAYLIST_PREPEND:
3209 playlist->index = playlist->first_index;
3210 break;
3211 case PLAYLIST_INSERT_LAST:
3212 playlist->index = playlist->first_index - 1;
3213 if (playlist->index < 0)
3214 playlist->index += playlist->amount;
3215 break;
3216 default:
3217 playlist->index = new_index;
3218 break;
3222 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3223 audio_flush_and_reload_tracks();
3227 #ifdef HAVE_DIRCACHE
3228 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3229 #endif
3231 return result;
3234 /* shuffle currently playing playlist */
3235 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3236 bool start_current)
3238 int result;
3240 if (!playlist)
3241 playlist = &current_playlist;
3243 check_control(playlist);
3245 result = randomise_playlist(playlist, seed, start_current, true);
3247 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3248 playlist->started)
3249 audio_flush_and_reload_tracks();
3251 return result;
3254 /* sort currently playing playlist */
3255 int playlist_sort(struct playlist_info* playlist, bool start_current)
3257 int result;
3259 if (!playlist)
3260 playlist = &current_playlist;
3262 check_control(playlist);
3264 result = sort_playlist(playlist, start_current, true);
3266 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3267 playlist->started)
3268 audio_flush_and_reload_tracks();
3270 return result;
3273 /* returns true if playlist has been modified */
3274 bool playlist_modified(const struct playlist_info* playlist)
3276 if (!playlist)
3277 playlist = &current_playlist;
3279 if (playlist->shuffle_modified ||
3280 playlist->deleted ||
3281 playlist->num_inserted_tracks > 0)
3282 return true;
3284 return false;
3287 /* returns index of first track in playlist */
3288 int playlist_get_first_index(const struct playlist_info* playlist)
3290 if (!playlist)
3291 playlist = &current_playlist;
3293 return playlist->first_index;
3296 /* returns shuffle seed of playlist */
3297 int playlist_get_seed(const struct playlist_info* playlist)
3299 if (!playlist)
3300 playlist = &current_playlist;
3302 return playlist->seed;
3305 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3306 int playlist_amount_ex(const struct playlist_info* playlist)
3308 if (!playlist)
3309 playlist = &current_playlist;
3311 return playlist->amount;
3314 /* returns full path of playlist (minus extension) */
3315 char *playlist_name(const struct playlist_info* playlist, char *buf,
3316 int buf_size)
3318 char *sep;
3320 if (!playlist)
3321 playlist = &current_playlist;
3323 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3325 if (!buf[0])
3326 return NULL;
3328 /* Remove extension */
3329 sep = strrchr(buf, '.');
3330 if (sep)
3331 *sep = 0;
3333 return buf;
3336 /* returns the playlist filename */
3337 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3338 int buf_size)
3340 if (!playlist)
3341 playlist = &current_playlist;
3343 snprintf(buf, buf_size, "%s", playlist->filename);
3345 if (!buf[0])
3346 return NULL;
3348 return buf;
3351 /* Fills info structure with information about track at specified index.
3352 Returns 0 on success and -1 on failure */
3353 int playlist_get_track_info(struct playlist_info* playlist, int index,
3354 struct playlist_track_info* info)
3356 int seek;
3357 bool control_file;
3359 if (!playlist)
3360 playlist = &current_playlist;
3362 if (index < 0 || index >= playlist->amount)
3363 return -1;
3365 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3366 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3368 if (get_filename(playlist, index, seek, control_file, info->filename,
3369 sizeof(info->filename)) < 0)
3370 return -1;
3372 info->attr = 0;
3374 if (control_file)
3376 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3377 info->attr |= PLAYLIST_ATTR_QUEUED;
3378 else
3379 info->attr |= PLAYLIST_ATTR_INSERTED;
3383 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3384 info->attr |= PLAYLIST_ATTR_SKIPPED;
3386 info->index = index;
3387 info->display_index = rotate_index(playlist, index) + 1;
3389 return 0;
3392 /* save the current dynamic playlist to specified file */
3393 int playlist_save(struct playlist_info* playlist, char *filename)
3395 int fd;
3396 int i, index;
3397 int count = 0;
3398 char path[MAX_PATH+1];
3399 char tmp_buf[MAX_PATH+1];
3400 int result = 0;
3401 bool overwrite_current = false;
3402 int* index_buf = NULL;
3404 if (!playlist)
3405 playlist = &current_playlist;
3407 if (playlist->amount <= 0)
3408 return -1;
3410 /* use current working directory as base for pathname */
3411 if (format_track_path(path, filename, sizeof(tmp_buf),
3412 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3413 return -1;
3415 if (!strncmp(playlist->filename, path, strlen(path)))
3417 /* Attempting to overwrite current playlist file.*/
3419 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3421 /* not enough buffer space to store updated indices */
3422 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3423 return -1;
3426 /* in_ram buffer is unused for m3u files so we'll use for storing
3427 updated indices */
3428 index_buf = (int*)playlist->buffer;
3430 /* use temporary pathname */
3431 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3432 overwrite_current = true;
3435 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3436 if (fd < 0)
3438 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3439 return -1;
3442 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3444 cpu_boost(true);
3446 index = playlist->first_index;
3447 for (i=0; i<playlist->amount; i++)
3449 bool control_file;
3450 bool queue;
3451 int seek;
3453 /* user abort */
3454 if (action_userabort(TIMEOUT_NOBLOCK))
3456 result = -1;
3457 break;
3460 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3461 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3462 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3464 /* Don't save queued files */
3465 if (!queue)
3467 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3468 MAX_PATH+1) < 0)
3470 result = -1;
3471 break;
3474 if (overwrite_current)
3475 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3477 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3479 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3480 result = -1;
3481 break;
3484 count++;
3486 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3487 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3488 false);
3490 yield();
3493 index = (index+1)%playlist->amount;
3496 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3498 close(fd);
3500 if (overwrite_current && result >= 0)
3502 result = -1;
3504 mutex_lock(&playlist->control_mutex);
3506 /* Replace the current playlist with the new one and update indices */
3507 close(playlist->fd);
3508 if (remove(playlist->filename) >= 0)
3510 if (rename(path, playlist->filename) >= 0)
3512 playlist->fd = open(playlist->filename, O_RDONLY);
3513 if (playlist->fd >= 0)
3515 index = playlist->first_index;
3516 for (i=0, count=0; i<playlist->amount; i++)
3518 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3520 playlist->indices[index] = index_buf[count];
3521 count++;
3523 index = (index+1)%playlist->amount;
3526 /* we need to recreate control because inserted tracks are
3527 now part of the playlist and shuffle has been
3528 invalidated */
3529 result = recreate_control(playlist);
3534 mutex_unlock(&playlist->control_mutex);
3538 cpu_boost(false);
3540 return result;
3544 * Search specified directory for tracks and notify via callback. May be
3545 * called recursively.
3547 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3548 int (*callback)(char*, void*),
3549 void* context)
3551 char buf[MAX_PATH+1];
3552 int result = 0;
3553 int num_files = 0;
3554 int i;
3555 struct entry *files;
3556 struct tree_context* tc = tree_get_context();
3557 int old_dirfilter = *(tc->dirfilter);
3559 if (!callback)
3560 return -1;
3562 /* use the tree browser dircache to load files */
3563 *(tc->dirfilter) = SHOW_ALL;
3565 if (ft_load(tc, dirname) < 0)
3567 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3568 *(tc->dirfilter) = old_dirfilter;
3569 return -1;
3572 files = (struct entry*) tc->dircache;
3573 num_files = tc->filesindir;
3575 /* we've overwritten the dircache so tree browser will need to be
3576 reloaded */
3577 reload_directory();
3579 for (i=0; i<num_files; i++)
3581 /* user abort */
3582 if (action_userabort(TIMEOUT_NOBLOCK))
3584 result = -1;
3585 break;
3588 if (files[i].attr & ATTR_DIRECTORY)
3590 if (recurse)
3592 /* recursively add directories */
3593 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3594 result = playlist_directory_tracksearch(buf, recurse,
3595 callback, context);
3596 if (result < 0)
3597 break;
3599 /* we now need to reload our current directory */
3600 if(ft_load(tc, dirname) < 0)
3602 result = -1;
3603 break;
3606 files = (struct entry*) tc->dircache;
3607 num_files = tc->filesindir;
3608 if (!num_files)
3610 result = -1;
3611 break;
3614 else
3615 continue;
3617 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3619 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3621 if (callback(buf, context) != 0)
3623 result = -1;
3624 break;
3627 /* let the other threads work */
3628 yield();
3632 /* restore dirfilter */
3633 *(tc->dirfilter) = old_dirfilter;
3635 return result;